Merge pull request #120 from gbouille/SSD1306_improvment
A little improvment on SSD refresh
This commit is contained in:
commit
dba374593e
2 changed files with 479 additions and 455 deletions
641
src/SSD1306.cpp
641
src/SSD1306.cpp
|
@ -1,309 +1,332 @@
|
|||
// Pi1541 - A Commodore 1541 disk drive emulator
|
||||
// Copyright(C) 2018 Stephen White
|
||||
//
|
||||
// This file is part of Pi1541.
|
||||
//
|
||||
// Pi1541 is free software : you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Pi1541 is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "SSD1306.h"
|
||||
#include "debug.h"
|
||||
#include <string.h>
|
||||
#include "Petscii.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "xga_font_data.h"
|
||||
}
|
||||
|
||||
extern unsigned char* CBMFont;
|
||||
|
||||
SSD1306::SSD1306(int BSCMaster, u8 address, unsigned width, unsigned height, int flip, LCD_MODEL type)
|
||||
: BSCMaster(BSCMaster)
|
||||
, address(address)
|
||||
, type(type)
|
||||
, flip(flip)
|
||||
, contrast(127)
|
||||
, width(width)
|
||||
, height(height)
|
||||
{
|
||||
sizeof_frame = width*height/8;
|
||||
frame = (unsigned char *)malloc(sizeof_frame);
|
||||
oldFrame = (unsigned char *)malloc(sizeof_frame);
|
||||
RPI_I2CInit(BSCMaster, 1);
|
||||
InitHardware();
|
||||
}
|
||||
|
||||
void SSD1306::InitHardware()
|
||||
{
|
||||
// SSD1306 data sheet configuration flow
|
||||
SendCommand(SSD1306_CMD_DISPLAY_OFF); // 0xAE
|
||||
|
||||
SendCommand(SSD1306_CMD_MULTIPLEX_RATIO); // 0xA8
|
||||
SendCommand(height-1); // SSD1306_LCDHEIGHT - 1
|
||||
|
||||
SendCommand(SSD1306_CMD_SET_DISPLAY_OFFSET); // 0xD3 Vertical scroll position
|
||||
SendCommand(0x00); // no Offset
|
||||
|
||||
SendCommand(SSD1306_CMD_SET_START_LINE | 0x0); // 0x40
|
||||
|
||||
if (flip) {
|
||||
SendCommand(0xA0); // No Segment Re-Map
|
||||
SendCommand(0xC0); // No COM Output Scan Direction
|
||||
} else {
|
||||
SendCommand(0xA1); // Set Segment Re-Map (horizontal flip)
|
||||
SendCommand(0xC8); // Set COM Output Scan Direction (vertical flip)
|
||||
}
|
||||
|
||||
SendCommand(SSD1306_CMD_SET_COM_PINS); // 0xDA Layout and direction
|
||||
if (type == LCD_1306_128x32)
|
||||
SendCommand(0x02);
|
||||
else
|
||||
SendCommand(0x12);
|
||||
|
||||
SetContrast(GetContrast());
|
||||
|
||||
SendCommand(SSD1306_CMD_TEST_DISPLAY_OFF); // 0xA4 - DONT force entire display on
|
||||
|
||||
SendCommand(SSD1306_CMD_NORMAL_DISPLAY); // 0xA6 = non inverted
|
||||
|
||||
SendCommand(SSD1306_CMD_SET_PRE_CHARGE_PERIOD); // 0xD9
|
||||
SendCommand(0xF1);
|
||||
|
||||
SendCommand(SSD1306_CMD_SET_VCOMH_DESELECT_LEVEL); // 0xDB
|
||||
SendCommand(0x40);
|
||||
|
||||
SendCommand(SSD1306_CMD_SET_DISPLAY_CLOCK_DIVIDE_RATIO); // 0xD5
|
||||
SendCommand(0x80); // upper nibble is rate, lower nibble is divisor
|
||||
|
||||
SendCommand(SSD1306_ENABLE_CHARGE_PUMP); // 0x8D Enable charge pump regulator
|
||||
SendCommand(0x14); // external = 0x10 internal = 0x14
|
||||
|
||||
SendCommand(SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE); // 0x20 Set Memory Addressing Mode
|
||||
SendCommand(0x10); // 10 - Page Addressing Mode for SH1106 compatibility
|
||||
|
||||
Home();
|
||||
|
||||
if (type != LCD_1106_128x64)
|
||||
SendCommand(SSD1306_CMD_DEACTIVATE_SCROLL); // 0x2E
|
||||
}
|
||||
|
||||
void SSD1306::SendCommand(u8 command)
|
||||
{
|
||||
char buffer[2];
|
||||
|
||||
buffer[0] = SSD1306_CONTROL_REG;
|
||||
buffer[1] = command;
|
||||
|
||||
RPI_I2CWrite(BSCMaster, address, buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
void SSD1306::SendData(u8 data)
|
||||
{
|
||||
char buffer[2];
|
||||
|
||||
buffer[0] = SSD1306_DATA_REG;
|
||||
buffer[1] = data;
|
||||
|
||||
RPI_I2CWrite(BSCMaster, address, buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
void SSD1306::Home()
|
||||
{
|
||||
SetDataPointer(0, 0);
|
||||
}
|
||||
|
||||
void SSD1306::SetDataPointer(u8 page, u8 col)
|
||||
{
|
||||
if (col > width-1) { col = width-1; }
|
||||
if (page > height/8-1) { page = height/8-1; }
|
||||
|
||||
if (type == LCD_1106_128x64)
|
||||
col += 2; // sh1106 uses columns 2..129
|
||||
|
||||
SendCommand(SSD1306_CMD_SET_PAGE | page); // 0xB0 page address
|
||||
SendCommand(SSD1306_CMD_SET_COLUMN_LOW | (col & 0xf)); // 0x00 column address lower bits
|
||||
SendCommand(SSD1306_CMD_SET_COLUMN_HIGH | (col >> 4)); // 0x10 column address upper bits
|
||||
}
|
||||
|
||||
void SSD1306::RefreshScreen()
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < height/8; i++)
|
||||
{
|
||||
RefreshPage(i);
|
||||
}
|
||||
}
|
||||
|
||||
// assumes a text row is 8 bit high
|
||||
void SSD1306::RefreshTextRows(u32 start, u32 amountOfRows)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
//start <<= 1;
|
||||
//amountOfRows <<= 1;
|
||||
for (i = start; i < start+amountOfRows; i++)
|
||||
{
|
||||
RefreshPage(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Some very basic optimisation is implemented.
|
||||
// it scans the page to work out the first (new_start) and last (new_end) changed bytes
|
||||
// Only update that window on the OLED
|
||||
// If someone is keen, a smarter algorithm could work out a series of ranges to update
|
||||
void SSD1306::RefreshPage(u32 page)
|
||||
{
|
||||
if (page >= height/8)
|
||||
return;
|
||||
|
||||
// x32 displays use lower half (pages 2 and 3)
|
||||
if (type == LCD_1306_128x32)
|
||||
{
|
||||
page = page+4; // 0,1,2,3 -> 4,5,6,7
|
||||
page = page%4; // and wrap it so 4,5 -> 0,1
|
||||
}
|
||||
|
||||
int i;
|
||||
int start = page*width;
|
||||
int end = start + width;
|
||||
|
||||
int new_start = -1;
|
||||
int new_end = -1;
|
||||
for (i = start; i < end; i++)
|
||||
{
|
||||
if (oldFrame[i] ^ frame[i])
|
||||
{
|
||||
if (new_start == -1)
|
||||
new_start = i;
|
||||
new_end = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_start >= 0)
|
||||
{
|
||||
SetDataPointer(page, new_start-start);
|
||||
for (i = new_start; i <= new_end; i++)
|
||||
{
|
||||
SendData(frame[i]);
|
||||
oldFrame[i] = frame[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSD1306::ClearScreen()
|
||||
{
|
||||
memset(frame, 0, sizeof_frame);
|
||||
memset(oldFrame, 0xff, sizeof_frame); // to force update
|
||||
RefreshScreen();
|
||||
}
|
||||
|
||||
void SSD1306::DisplayOn()
|
||||
{
|
||||
SendCommand(SSD1306_CMD_DISPLAY_ON); // 0xAF
|
||||
}
|
||||
|
||||
void SSD1306::DisplayOff()
|
||||
{
|
||||
SendCommand(SSD1306_CMD_DISPLAY_OFF); // 0xAE
|
||||
}
|
||||
|
||||
void SSD1306::SetContrast(u8 value)
|
||||
{
|
||||
contrast = value;
|
||||
SendCommand(SSD1306_CMD_SET_CONTRAST_CONTROL);
|
||||
SendCommand(value);
|
||||
if (type != LCD_1106_128x64) // dont fiddle vcomdeselect on 1106 displays
|
||||
SetVCOMDeselect( value >> 8);
|
||||
}
|
||||
|
||||
void SSD1306::SetVCOMDeselect(u8 value)
|
||||
{
|
||||
SendCommand(SSD1306_CMD_SET_VCOMH_DESELECT_LEVEL);
|
||||
SendCommand( (value & 7) << 4 );
|
||||
}
|
||||
|
||||
void SSD1306::PlotText(bool useCBMFont, bool petscii, int x, int y, char* str, bool inverse)
|
||||
{
|
||||
// assumes 16 character width
|
||||
int i;
|
||||
i = 0;
|
||||
while (str[i] && x < 16)
|
||||
{
|
||||
PlotCharacter(useCBMFont, petscii, x++, y, str[i++], inverse);
|
||||
}
|
||||
}
|
||||
|
||||
// Pg 143 - 145. Transposing an 8x8 bit matrix. Hacker's Delight
|
||||
void transpose8(unsigned char* B, const unsigned char* A, bool inverse)
|
||||
{
|
||||
unsigned x, y, t;
|
||||
x = (A[7] << 24) | (A[6] << 16) | (A[5] << 8) | A[4];
|
||||
y = (A[3] << 24) | (A[2] << 16) | (A[1] << 8) | A[0];
|
||||
if (inverse)
|
||||
{
|
||||
x = ~x;
|
||||
y = ~y;
|
||||
}
|
||||
|
||||
t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
|
||||
t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
|
||||
|
||||
t = (x ^ (x >> 14)) & 0x0000CCCC; x = x ^ t ^ (t << 14);
|
||||
t = (y ^ (y >> 14)) & 0x0000CCCC; y = y ^ t ^ (t << 14);
|
||||
|
||||
t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
|
||||
y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
|
||||
x = t;
|
||||
|
||||
|
||||
B[0] = x >> 24; B[1] = x >> 16; B[2] = x >> 8; B[3] = x;
|
||||
B[4] = y >> 24; B[5] = y >> 16; B[6] = y >> 8; B[7] = y;
|
||||
}
|
||||
|
||||
void SSD1306::PlotCharacter(bool useCBMFont, bool petscii, int x, int y, char c, bool inverse)
|
||||
{
|
||||
unsigned char a[8], b[8];
|
||||
if (useCBMFont && CBMFont)
|
||||
{
|
||||
if (! petscii)
|
||||
c = ascii2petscii(c);
|
||||
c = petscii2screen(c);
|
||||
transpose8(a, CBMFont + ((c+256) * 8), inverse); // 256 byte shift to use the maj/min bank
|
||||
memcpy(frame + (y * 128) + (x * 8), a, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
transpose8(a, avpriv_vga16_font + (c * 16), inverse);
|
||||
transpose8(b, avpriv_vga16_font + (c * 16) + 8, inverse);
|
||||
memcpy(frame + (y * 256) + (x * 8), a, 8);
|
||||
memcpy(frame + (y * 256) + (x * 8) + 128, b, 8);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SSD1306::PlotPixel(int x, int y, int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 1: frame[x+ (y/8)*width] |= (1 << (y&7)); break;
|
||||
case 0: frame[x+ (y/8)*width] &= ~(1 << (y&7)); break;
|
||||
case -1: frame[x+ (y/8)*width] ^= (1 << (y&7)); break;
|
||||
}
|
||||
}
|
||||
|
||||
// expects source in ssd1306 native vertical byte format
|
||||
void SSD1306::PlotImage(const unsigned char * source)
|
||||
{
|
||||
memcpy (frame, source, SSD1306_128x64_BYTES);
|
||||
}
|
||||
// Pi1541 - A Commodore 1541 disk drive emulator
|
||||
// Copyright(C) 2018 Stephen White
|
||||
//
|
||||
// This file is part of Pi1541.
|
||||
//
|
||||
// Pi1541 is free software : you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Pi1541 is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "SSD1306.h"
|
||||
#include "debug.h"
|
||||
#include <string.h>
|
||||
#include "Petscii.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "xga_font_data.h"
|
||||
}
|
||||
|
||||
extern unsigned char* CBMFont;
|
||||
|
||||
SSD1306::SSD1306(int BSCMaster, u8 address, unsigned width, unsigned height, int flip, LCD_MODEL type)
|
||||
: BSCMaster(BSCMaster)
|
||||
, address(address)
|
||||
, type(type)
|
||||
, flip(flip)
|
||||
, contrast(127)
|
||||
, width(width)
|
||||
, height(height)
|
||||
{
|
||||
sizeof_frame = width*height/8;
|
||||
frame = (unsigned char *)malloc(sizeof_frame);
|
||||
oldFrame = (unsigned char *)malloc(sizeof_frame);
|
||||
RPI_I2CInit(BSCMaster, 1);
|
||||
InitHardware();
|
||||
}
|
||||
|
||||
void SSD1306::InitHardware()
|
||||
{
|
||||
// SSD1306 data sheet configuration flow
|
||||
SendCommand(SSD1306_CMD_DISPLAY_OFF); // 0xAE
|
||||
|
||||
SendCommand(SSD1306_CMD_MULTIPLEX_RATIO); // 0xA8
|
||||
SendCommand(height-1); // SSD1306_LCDHEIGHT - 1
|
||||
|
||||
SendCommand(SSD1306_CMD_SET_DISPLAY_OFFSET); // 0xD3 Vertical scroll position
|
||||
SendCommand(0x00); // no Offset
|
||||
|
||||
SendCommand(SSD1306_CMD_SET_START_LINE | 0x0); // 0x40
|
||||
|
||||
if (flip) {
|
||||
SendCommand(0xA0); // No Segment Re-Map
|
||||
SendCommand(0xC0); // No COM Output Scan Direction
|
||||
} else {
|
||||
SendCommand(0xA1); // Set Segment Re-Map (horizontal flip)
|
||||
SendCommand(0xC8); // Set COM Output Scan Direction (vertical flip)
|
||||
}
|
||||
|
||||
SendCommand(SSD1306_CMD_SET_COM_PINS); // 0xDA Layout and direction
|
||||
if (type == LCD_1306_128x32)
|
||||
SendCommand(0x02);
|
||||
else
|
||||
SendCommand(0x12);
|
||||
|
||||
SetContrast(GetContrast());
|
||||
|
||||
SendCommand(SSD1306_CMD_TEST_DISPLAY_OFF); // 0xA4 - DONT force entire display on
|
||||
|
||||
SendCommand(SSD1306_CMD_NORMAL_DISPLAY); // 0xA6 = non inverted
|
||||
|
||||
SendCommand(SSD1306_CMD_SET_PRE_CHARGE_PERIOD); // 0xD9
|
||||
SendCommand(0xF1);
|
||||
|
||||
SendCommand(SSD1306_CMD_SET_VCOMH_DESELECT_LEVEL); // 0xDB
|
||||
SendCommand(0x40);
|
||||
|
||||
SendCommand(SSD1306_CMD_SET_DISPLAY_CLOCK_DIVIDE_RATIO); // 0xD5
|
||||
SendCommand(0x80); // upper nibble is rate, lower nibble is divisor
|
||||
|
||||
SendCommand(SSD1306_ENABLE_CHARGE_PUMP); // 0x8D Enable charge pump regulator
|
||||
SendCommand(0x14); // external = 0x10 internal = 0x14
|
||||
|
||||
SendCommand(SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE); // 0x20 Set Memory Addressing Mode
|
||||
SendCommand(0x10); // 10 - Page Addressing Mode for SH1106 compatibility
|
||||
|
||||
Home();
|
||||
|
||||
if (type != LCD_1106_128x64)
|
||||
SendCommand(SSD1306_CMD_DEACTIVATE_SCROLL); // 0x2E
|
||||
}
|
||||
|
||||
void SSD1306::SendCommand(u8 command)
|
||||
{
|
||||
char buffer[2];
|
||||
|
||||
buffer[0] = SSD1306_CONTROL_REG;
|
||||
buffer[1] = command;
|
||||
|
||||
RPI_I2CWrite(BSCMaster, address, buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
void SSD1306::SendData(u8 data)
|
||||
{
|
||||
char buffer[2];
|
||||
|
||||
buffer[0] = SSD1306_DATA_REG;
|
||||
buffer[1] = data;
|
||||
|
||||
RPI_I2CWrite(BSCMaster, address, buffer, sizeof(buffer));
|
||||
}
|
||||
|
||||
//We can send up to 16 bytes to the i2c bus
|
||||
void SSD1306::SendDataLong(void* data, u8 length)
|
||||
{
|
||||
char buffer[15];
|
||||
|
||||
buffer[0] = SSD1306_DATA_REG;
|
||||
memcpy(&buffer[1], data, length);
|
||||
|
||||
RPI_I2CWrite(BSCMaster, address, buffer, ++length);
|
||||
}
|
||||
|
||||
void SSD1306::Home()
|
||||
{
|
||||
SetDataPointer(0, 0);
|
||||
}
|
||||
|
||||
void SSD1306::SetDataPointer(u8 page, u8 col)
|
||||
{
|
||||
if (col > width-1) { col = width-1; }
|
||||
if (page > height/8-1) { page = height/8-1; }
|
||||
|
||||
if (type == LCD_1106_128x64)
|
||||
col += 2; // sh1106 uses columns 2..129
|
||||
|
||||
SendCommand(SSD1306_CMD_SET_PAGE | page); // 0xB0 page address
|
||||
SendCommand(SSD1306_CMD_SET_COLUMN_LOW | (col & 0xf)); // 0x00 column address lower bits
|
||||
SendCommand(SSD1306_CMD_SET_COLUMN_HIGH | (col >> 4)); // 0x10 column address upper bits
|
||||
}
|
||||
|
||||
void SSD1306::RefreshScreen()
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < height/8; i++)
|
||||
{
|
||||
RefreshPage(i);
|
||||
}
|
||||
}
|
||||
|
||||
// assumes a text row is 8 bit high
|
||||
void SSD1306::RefreshTextRows(u32 start, u32 amountOfRows)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
//start <<= 1;
|
||||
//amountOfRows <<= 1;
|
||||
for (i = start; i < start+amountOfRows; i++)
|
||||
{
|
||||
RefreshPage(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Some very basic optimisation is implemented.
|
||||
// it scans the page to work out the first (new_start) and last (new_end) changed bytes
|
||||
// Only update that window on the OLED
|
||||
// If someone is keen, a smarter algorithm could work out a series of ranges to update
|
||||
void SSD1306::RefreshPage(u32 page)
|
||||
{
|
||||
if (page >= height/8)
|
||||
return;
|
||||
|
||||
// x32 displays use lower half (pages 2 and 3)
|
||||
if (type == LCD_1306_128x32)
|
||||
{
|
||||
page = page+4; // 0,1,2,3 -> 4,5,6,7
|
||||
page = page%4; // and wrap it so 4,5 -> 0,1
|
||||
}
|
||||
|
||||
int i;
|
||||
int start = page*width;
|
||||
int end = start + width;
|
||||
|
||||
int new_start = -1;
|
||||
int new_end = -1;
|
||||
for (i = start; i < end; i++)
|
||||
{
|
||||
if (oldFrame[i] ^ frame[i])
|
||||
{
|
||||
if (new_start == -1)
|
||||
new_start = i;
|
||||
new_end = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_start >= 0)
|
||||
{
|
||||
SetDataPointer(page, new_start-start);
|
||||
new_end++;
|
||||
while (new_start < new_end)
|
||||
{
|
||||
i = (new_end-new_start < 15) ? new_end-new_start : 15;
|
||||
if (i == 1)
|
||||
{
|
||||
SendData(frame[new_start]);
|
||||
oldFrame[new_start] = frame[new_start];
|
||||
}
|
||||
else // Use the ability to send up to 16 byte in a row
|
||||
// (1 for command and 15 for data)
|
||||
{
|
||||
SendDataLong(&frame[new_start],i);
|
||||
memcpy(&oldFrame[new_start], &frame[new_start],i);
|
||||
}
|
||||
new_start += i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSD1306::ClearScreen()
|
||||
{
|
||||
memset(frame, 0, sizeof_frame);
|
||||
//memset(oldFrame, 0xff, sizeof_frame); // to force update
|
||||
RefreshScreen();
|
||||
}
|
||||
|
||||
void SSD1306::DisplayOn()
|
||||
{
|
||||
SendCommand(SSD1306_CMD_DISPLAY_ON); // 0xAF
|
||||
}
|
||||
|
||||
void SSD1306::DisplayOff()
|
||||
{
|
||||
SendCommand(SSD1306_CMD_DISPLAY_OFF); // 0xAE
|
||||
}
|
||||
|
||||
void SSD1306::SetContrast(u8 value)
|
||||
{
|
||||
contrast = value;
|
||||
SendCommand(SSD1306_CMD_SET_CONTRAST_CONTROL);
|
||||
SendCommand(value);
|
||||
if (type != LCD_1106_128x64) // dont fiddle vcomdeselect on 1106 displays
|
||||
SetVCOMDeselect( value >> 8);
|
||||
}
|
||||
|
||||
void SSD1306::SetVCOMDeselect(u8 value)
|
||||
{
|
||||
SendCommand(SSD1306_CMD_SET_VCOMH_DESELECT_LEVEL);
|
||||
SendCommand( (value & 7) << 4 );
|
||||
}
|
||||
|
||||
void SSD1306::PlotText(bool useCBMFont, bool petscii, int x, int y, char* str, bool inverse)
|
||||
{
|
||||
// assumes 16 character width
|
||||
int i;
|
||||
i = 0;
|
||||
while (str[i] && x < 16)
|
||||
{
|
||||
PlotCharacter(useCBMFont, petscii, x++, y, str[i++], inverse);
|
||||
}
|
||||
}
|
||||
|
||||
// Pg 143 - 145. Transposing an 8x8 bit matrix. Hacker's Delight
|
||||
void transpose8(unsigned char* B, const unsigned char* A, bool inverse)
|
||||
{
|
||||
unsigned x, y, t;
|
||||
x = (A[7] << 24) | (A[6] << 16) | (A[5] << 8) | A[4];
|
||||
y = (A[3] << 24) | (A[2] << 16) | (A[1] << 8) | A[0];
|
||||
if (inverse)
|
||||
{
|
||||
x = ~x;
|
||||
y = ~y;
|
||||
}
|
||||
|
||||
t = (x ^ (x >> 7)) & 0x00AA00AA; x = x ^ t ^ (t << 7);
|
||||
t = (y ^ (y >> 7)) & 0x00AA00AA; y = y ^ t ^ (t << 7);
|
||||
|
||||
t = (x ^ (x >> 14)) & 0x0000CCCC; x = x ^ t ^ (t << 14);
|
||||
t = (y ^ (y >> 14)) & 0x0000CCCC; y = y ^ t ^ (t << 14);
|
||||
|
||||
t = (x & 0xF0F0F0F0) | ((y >> 4) & 0x0F0F0F0F);
|
||||
y = ((x << 4) & 0xF0F0F0F0) | (y & 0x0F0F0F0F);
|
||||
x = t;
|
||||
|
||||
|
||||
B[0] = x >> 24; B[1] = x >> 16; B[2] = x >> 8; B[3] = x;
|
||||
B[4] = y >> 24; B[5] = y >> 16; B[6] = y >> 8; B[7] = y;
|
||||
}
|
||||
|
||||
void SSD1306::PlotCharacter(bool useCBMFont, bool petscii, int x, int y, char c, bool inverse)
|
||||
{
|
||||
unsigned char a[8], b[8];
|
||||
if (useCBMFont && CBMFont)
|
||||
{
|
||||
if (! petscii)
|
||||
c = ascii2petscii(c);
|
||||
c = petscii2screen(c);
|
||||
transpose8(a, CBMFont + ((c+256) * 8), inverse); // 256 byte shift to use the maj/min bank
|
||||
memcpy(frame + (y * 128) + (x * 8), a, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
transpose8(a, avpriv_vga16_font + (c * 16), inverse);
|
||||
transpose8(b, avpriv_vga16_font + (c * 16) + 8, inverse);
|
||||
memcpy(frame + (y * 256) + (x * 8), a, 8);
|
||||
memcpy(frame + (y * 256) + (x * 8) + 128, b, 8);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SSD1306::PlotPixel(int x, int y, int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 1: frame[x+ (y/8)*width] |= (1 << (y&7)); break;
|
||||
case 0: frame[x+ (y/8)*width] &= ~(1 << (y&7)); break;
|
||||
case -1: frame[x+ (y/8)*width] ^= (1 << (y&7)); break;
|
||||
}
|
||||
}
|
||||
|
||||
// expects source in ssd1306 native vertical byte format
|
||||
void SSD1306::PlotImage(const unsigned char * source)
|
||||
{
|
||||
memcpy (frame, source, SSD1306_128x64_BYTES);
|
||||
}
|
||||
|
|
293
src/SSD1306.h
293
src/SSD1306.h
|
@ -1,146 +1,147 @@
|
|||
// Pi1541 - A Commodore 1541 disk drive emulator
|
||||
// Copyright(C) 2018 Stephen White
|
||||
//
|
||||
// This file is part of Pi1541.
|
||||
//
|
||||
// Pi1541 is free software : you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Pi1541 is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef SSD1306_H
|
||||
#define SSD1306_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "types.h"
|
||||
extern "C"
|
||||
{
|
||||
#include "rpi-i2c.h"
|
||||
}
|
||||
|
||||
// 8 pages * (128 columns * 8 bits)
|
||||
//0 127 0 127
|
||||
//0 127 0 127
|
||||
//0 127 0 127
|
||||
//________________________________________________________
|
||||
//7777777 | 7
|
||||
//6666666 | 6
|
||||
//5555555 | 5
|
||||
//4444444 PG(ROW)0 | PG(ROW)1 4
|
||||
//3333333 | 3
|
||||
//2222222 | 2
|
||||
//1111111 | 1
|
||||
//0000000 | 0
|
||||
//________________________________________________________
|
||||
//7777777 | 7
|
||||
//6666666 | 6
|
||||
//5555555 | 5
|
||||
//4444444 PG(ROW)2 | PG(ROW)3 4
|
||||
//3333333 | 3
|
||||
//2222222 | 2
|
||||
//1111111 | 1
|
||||
//0000000 | 0
|
||||
//________________________________________________________
|
||||
//7777777 | 7
|
||||
//6666666 | 6
|
||||
//5555555 | 5
|
||||
//4444444 PG(ROW)4 | PG(ROW)5 4
|
||||
//3333333 | 3
|
||||
//2222222 | 2
|
||||
//1111111 | 1
|
||||
//0000000 | 0
|
||||
//________________________________________________________
|
||||
//7777777 | 7
|
||||
//6666666 | 6
|
||||
//5555555 | 5
|
||||
//4444444 PG(ROW)6 | PG(ROW)7 4
|
||||
//3333333 | 3
|
||||
//2222222 | 2
|
||||
//1111111 | 1
|
||||
//0000000 | 0
|
||||
//________________________________________________________
|
||||
|
||||
#define SSD1306_128x64_BYTES ((128 * 64) / 8)
|
||||
|
||||
class SSD1306
|
||||
{
|
||||
public:
|
||||
// 128x32 0x3C
|
||||
// 128x64 0x3D or 0x3C (if SA0 is grounded)
|
||||
SSD1306(int BSCMaster = 1, u8 address = 0x3C, unsigned width = 128, unsigned height = 64, int flip = 0, LCD_MODEL type=LCD_UNKNOWN);
|
||||
|
||||
void PlotCharacter(bool useCBMFont, bool petscii, int x, int y, char ascii, bool inverse);
|
||||
void PlotText(bool useCBMFont, bool petscii, int x, int y, char* str, bool inverse);
|
||||
|
||||
void InitHardware();
|
||||
void DisplayOn();
|
||||
void DisplayOff();
|
||||
void SetContrast(u8 value);
|
||||
u8 GetContrast() { return contrast; }
|
||||
void SetVCOMDeselect(u8 value);
|
||||
|
||||
void ClearScreen();
|
||||
void RefreshScreen();
|
||||
void RefreshPage(u32 page);
|
||||
void RefreshTextRows(u32 start, u32 amountOfRows);
|
||||
void SetDisplayWindow(u8 x1, u8 y1, u8 x2, u8 y2);
|
||||
void PlotPixel(int x, int y, int c);
|
||||
void PlotImage(const unsigned char * source);
|
||||
|
||||
protected:
|
||||
void SendCommand(u8 command);
|
||||
void SendData(u8 data);
|
||||
|
||||
void Home();
|
||||
void SetDataPointer(u8 row, u8 col);
|
||||
|
||||
// unsigned char frame[SSD1306_128x64_BYTES];
|
||||
// unsigned char oldFrame[SSD1306_128x64_BYTES];
|
||||
unsigned char * frame;
|
||||
unsigned char * oldFrame;
|
||||
unsigned sizeof_frame;
|
||||
|
||||
int BSCMaster;
|
||||
u8 address;
|
||||
int type;
|
||||
int flip;
|
||||
int contrast;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#define SSD1306_CMD_SET_COLUMN_LOW 0x00
|
||||
#define SSD1306_CMD_SET_COLUMN_HIGH 0x10
|
||||
#define SSD1306_CMD_SET_PAGE 0xB0
|
||||
#define SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE 0x20
|
||||
#define SSD1306_CMD_SET_COLUMN_ADDRESS 0x21
|
||||
#define SSD1306_CMD_SET_PAGE_ADDRESS 0x22
|
||||
#define SSD1306_CMD_DEACTIVATE_SCROLL 0x2E
|
||||
#define SSD1306_CMD_ACTIVATE_SCROLL 0x2F
|
||||
#define SSD1306_CMD_SET_CONTRAST_CONTROL 0x81 // Set Contrast Control for BANK0
|
||||
#define SSD1306_ENABLE_CHARGE_PUMP 0x8D
|
||||
#define SSD1306_CMD_TEST_DISPLAY_OFF 0xA4
|
||||
#define SSD1306_CMD_TEST_DISPLAY_ON 0xA5
|
||||
#define SSD1306_CMD_NORMAL_DISPLAY 0xA6 // 1 = on pixel
|
||||
#define SSD1306_CMD_INVERT_DISPLAY 0xA7 // 0 = on pixel
|
||||
#define SSD1306_CMD_DISPLAY_OFF 0xAE
|
||||
#define SSD1306_CMD_DISPLAY_ON 0xAF
|
||||
#define SSD1306_CMD_MULTIPLEX_RATIO 0xA8
|
||||
#define SSD1306_CMD_SET_START_LINE 0x40
|
||||
#define SSD1306_CMD_SET_DISPLAY_OFFSET 0xD3
|
||||
#define SSD1306_CMD_SET_DISPLAY_CLOCK_DIVIDE_RATIO 0xD5
|
||||
#define SSD1306_CMD_SET_PRE_CHARGE_PERIOD 0xD9
|
||||
#define SSD1306_CMD_SET_COM_PINS 0xDA
|
||||
#define SSD1306_CMD_SET_VCOMH_DESELECT_LEVEL 0xDB
|
||||
#define SSD1306_CONTROL_REG 0x00
|
||||
#define SSD1306_DATA_REG 0x40
|
||||
// Pi1541 - A Commodore 1541 disk drive emulator
|
||||
// Copyright(C) 2018 Stephen White
|
||||
//
|
||||
// This file is part of Pi1541.
|
||||
//
|
||||
// Pi1541 is free software : you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Pi1541 is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef SSD1306_H
|
||||
#define SSD1306_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "types.h"
|
||||
extern "C"
|
||||
{
|
||||
#include "rpi-i2c.h"
|
||||
}
|
||||
|
||||
// 8 pages * (128 columns * 8 bits)
|
||||
//0 127 0 127
|
||||
//0 127 0 127
|
||||
//0 127 0 127
|
||||
//________________________________________________________
|
||||
//7777777 | 7
|
||||
//6666666 | 6
|
||||
//5555555 | 5
|
||||
//4444444 PG(ROW)0 | PG(ROW)1 4
|
||||
//3333333 | 3
|
||||
//2222222 | 2
|
||||
//1111111 | 1
|
||||
//0000000 | 0
|
||||
//________________________________________________________
|
||||
//7777777 | 7
|
||||
//6666666 | 6
|
||||
//5555555 | 5
|
||||
//4444444 PG(ROW)2 | PG(ROW)3 4
|
||||
//3333333 | 3
|
||||
//2222222 | 2
|
||||
//1111111 | 1
|
||||
//0000000 | 0
|
||||
//________________________________________________________
|
||||
//7777777 | 7
|
||||
//6666666 | 6
|
||||
//5555555 | 5
|
||||
//4444444 PG(ROW)4 | PG(ROW)5 4
|
||||
//3333333 | 3
|
||||
//2222222 | 2
|
||||
//1111111 | 1
|
||||
//0000000 | 0
|
||||
//________________________________________________________
|
||||
//7777777 | 7
|
||||
//6666666 | 6
|
||||
//5555555 | 5
|
||||
//4444444 PG(ROW)6 | PG(ROW)7 4
|
||||
//3333333 | 3
|
||||
//2222222 | 2
|
||||
//1111111 | 1
|
||||
//0000000 | 0
|
||||
//________________________________________________________
|
||||
|
||||
#define SSD1306_128x64_BYTES ((128 * 64) / 8)
|
||||
|
||||
class SSD1306
|
||||
{
|
||||
public:
|
||||
// 128x32 0x3C
|
||||
// 128x64 0x3D or 0x3C (if SA0 is grounded)
|
||||
SSD1306(int BSCMaster = 1, u8 address = 0x3C, unsigned width = 128, unsigned height = 64, int flip = 0, LCD_MODEL type=LCD_UNKNOWN);
|
||||
|
||||
void PlotCharacter(bool useCBMFont, bool petscii, int x, int y, char ascii, bool inverse);
|
||||
void PlotText(bool useCBMFont, bool petscii, int x, int y, char* str, bool inverse);
|
||||
|
||||
void InitHardware();
|
||||
void DisplayOn();
|
||||
void DisplayOff();
|
||||
void SetContrast(u8 value);
|
||||
u8 GetContrast() { return contrast; }
|
||||
void SetVCOMDeselect(u8 value);
|
||||
|
||||
void ClearScreen();
|
||||
void RefreshScreen();
|
||||
void RefreshPage(u32 page);
|
||||
void RefreshTextRows(u32 start, u32 amountOfRows);
|
||||
void SetDisplayWindow(u8 x1, u8 y1, u8 x2, u8 y2);
|
||||
void PlotPixel(int x, int y, int c);
|
||||
void PlotImage(const unsigned char * source);
|
||||
|
||||
protected:
|
||||
void SendCommand(u8 command);
|
||||
void SendData(u8 data);
|
||||
void SendDataLong(void* data, u8 length);
|
||||
|
||||
void Home();
|
||||
void SetDataPointer(u8 row, u8 col);
|
||||
|
||||
// unsigned char frame[SSD1306_128x64_BYTES];
|
||||
// unsigned char oldFrame[SSD1306_128x64_BYTES];
|
||||
unsigned char * frame;
|
||||
unsigned char * oldFrame;
|
||||
unsigned sizeof_frame;
|
||||
|
||||
int BSCMaster;
|
||||
u8 address;
|
||||
int type;
|
||||
int flip;
|
||||
int contrast;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#define SSD1306_CMD_SET_COLUMN_LOW 0x00
|
||||
#define SSD1306_CMD_SET_COLUMN_HIGH 0x10
|
||||
#define SSD1306_CMD_SET_PAGE 0xB0
|
||||
#define SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE 0x20
|
||||
#define SSD1306_CMD_SET_COLUMN_ADDRESS 0x21
|
||||
#define SSD1306_CMD_SET_PAGE_ADDRESS 0x22
|
||||
#define SSD1306_CMD_DEACTIVATE_SCROLL 0x2E
|
||||
#define SSD1306_CMD_ACTIVATE_SCROLL 0x2F
|
||||
#define SSD1306_CMD_SET_CONTRAST_CONTROL 0x81 // Set Contrast Control for BANK0
|
||||
#define SSD1306_ENABLE_CHARGE_PUMP 0x8D
|
||||
#define SSD1306_CMD_TEST_DISPLAY_OFF 0xA4
|
||||
#define SSD1306_CMD_TEST_DISPLAY_ON 0xA5
|
||||
#define SSD1306_CMD_NORMAL_DISPLAY 0xA6 // 1 = on pixel
|
||||
#define SSD1306_CMD_INVERT_DISPLAY 0xA7 // 0 = on pixel
|
||||
#define SSD1306_CMD_DISPLAY_OFF 0xAE
|
||||
#define SSD1306_CMD_DISPLAY_ON 0xAF
|
||||
#define SSD1306_CMD_MULTIPLEX_RATIO 0xA8
|
||||
#define SSD1306_CMD_SET_START_LINE 0x40
|
||||
#define SSD1306_CMD_SET_DISPLAY_OFFSET 0xD3
|
||||
#define SSD1306_CMD_SET_DISPLAY_CLOCK_DIVIDE_RATIO 0xD5
|
||||
#define SSD1306_CMD_SET_PRE_CHARGE_PERIOD 0xD9
|
||||
#define SSD1306_CMD_SET_COM_PINS 0xDA
|
||||
#define SSD1306_CMD_SET_VCOMH_DESELECT_LEVEL 0xDB
|
||||
#define SSD1306_CONTROL_REG 0x00
|
||||
#define SSD1306_DATA_REG 0x40
|
||||
|
|
Loading…
Reference in a new issue