A little improvment on SSD refresh by using the ability to send up to

15 bytes in one single data command.
Should increase the speed to refresh the screen.
This commit is contained in:
gbouille 2018-12-31 18:02:14 +01:00
parent 2979c69791
commit 800972fa3e
2 changed files with 479 additions and 455 deletions

View File

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

View File

@ -1,146 +1,147 @@
// Pi1541 - A Commodore 1541 disk drive emulator // Pi1541 - A Commodore 1541 disk drive emulator
// Copyright(C) 2018 Stephen White // Copyright(C) 2018 Stephen White
// //
// This file is part of Pi1541. // This file is part of Pi1541.
// //
// Pi1541 is free software : you can redistribute it and/or modify // Pi1541 is free software : you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// Pi1541 is distributed in the hope that it will be useful, // Pi1541 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>. // along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
#ifndef SSD1306_H #ifndef SSD1306_H
#define SSD1306_H #define SSD1306_H
#include <stdlib.h> #include <stdlib.h>
#include "types.h" #include "types.h"
extern "C" extern "C"
{ {
#include "rpi-i2c.h" #include "rpi-i2c.h"
} }
// 8 pages * (128 columns * 8 bits) // 8 pages * (128 columns * 8 bits)
//0 127 0 127 //0 127 0 127
//0 127 0 127 //0 127 0 127
//0 127 0 127 //0 127 0 127
//________________________________________________________ //________________________________________________________
//7777777 | 7 //7777777 | 7
//6666666 | 6 //6666666 | 6
//5555555 | 5 //5555555 | 5
//4444444 PG(ROW)0 | PG(ROW)1 4 //4444444 PG(ROW)0 | PG(ROW)1 4
//3333333 | 3 //3333333 | 3
//2222222 | 2 //2222222 | 2
//1111111 | 1 //1111111 | 1
//0000000 | 0 //0000000 | 0
//________________________________________________________ //________________________________________________________
//7777777 | 7 //7777777 | 7
//6666666 | 6 //6666666 | 6
//5555555 | 5 //5555555 | 5
//4444444 PG(ROW)2 | PG(ROW)3 4 //4444444 PG(ROW)2 | PG(ROW)3 4
//3333333 | 3 //3333333 | 3
//2222222 | 2 //2222222 | 2
//1111111 | 1 //1111111 | 1
//0000000 | 0 //0000000 | 0
//________________________________________________________ //________________________________________________________
//7777777 | 7 //7777777 | 7
//6666666 | 6 //6666666 | 6
//5555555 | 5 //5555555 | 5
//4444444 PG(ROW)4 | PG(ROW)5 4 //4444444 PG(ROW)4 | PG(ROW)5 4
//3333333 | 3 //3333333 | 3
//2222222 | 2 //2222222 | 2
//1111111 | 1 //1111111 | 1
//0000000 | 0 //0000000 | 0
//________________________________________________________ //________________________________________________________
//7777777 | 7 //7777777 | 7
//6666666 | 6 //6666666 | 6
//5555555 | 5 //5555555 | 5
//4444444 PG(ROW)6 | PG(ROW)7 4 //4444444 PG(ROW)6 | PG(ROW)7 4
//3333333 | 3 //3333333 | 3
//2222222 | 2 //2222222 | 2
//1111111 | 1 //1111111 | 1
//0000000 | 0 //0000000 | 0
//________________________________________________________ //________________________________________________________
#define SSD1306_128x64_BYTES ((128 * 64) / 8) #define SSD1306_128x64_BYTES ((128 * 64) / 8)
class SSD1306 class SSD1306
{ {
public: public:
// 128x32 0x3C // 128x32 0x3C
// 128x64 0x3D or 0x3C (if SA0 is grounded) // 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); 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 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 PlotText(bool useCBMFont, bool petscii, int x, int y, char* str, bool inverse);
void InitHardware(); void InitHardware();
void DisplayOn(); void DisplayOn();
void DisplayOff(); void DisplayOff();
void SetContrast(u8 value); void SetContrast(u8 value);
u8 GetContrast() { return contrast; } u8 GetContrast() { return contrast; }
void SetVCOMDeselect(u8 value); void SetVCOMDeselect(u8 value);
void ClearScreen(); void ClearScreen();
void RefreshScreen(); void RefreshScreen();
void RefreshPage(u32 page); void RefreshPage(u32 page);
void RefreshTextRows(u32 start, u32 amountOfRows); void RefreshTextRows(u32 start, u32 amountOfRows);
void SetDisplayWindow(u8 x1, u8 y1, u8 x2, u8 y2); void SetDisplayWindow(u8 x1, u8 y1, u8 x2, u8 y2);
void PlotPixel(int x, int y, int c); void PlotPixel(int x, int y, int c);
void PlotImage(const unsigned char * source); void PlotImage(const unsigned char * source);
protected: protected:
void SendCommand(u8 command); void SendCommand(u8 command);
void SendData(u8 data); void SendData(u8 data);
void SendDataLong(void* data, u8 length);
void Home();
void SetDataPointer(u8 row, u8 col); void Home();
void SetDataPointer(u8 row, u8 col);
// unsigned char frame[SSD1306_128x64_BYTES];
// unsigned char oldFrame[SSD1306_128x64_BYTES]; // unsigned char frame[SSD1306_128x64_BYTES];
unsigned char * frame; // unsigned char oldFrame[SSD1306_128x64_BYTES];
unsigned char * oldFrame; unsigned char * frame;
unsigned sizeof_frame; unsigned char * oldFrame;
unsigned sizeof_frame;
int BSCMaster;
u8 address; int BSCMaster;
int type; u8 address;
int flip; int type;
int contrast; int flip;
unsigned width; int contrast;
unsigned height; unsigned width;
}; unsigned height;
#endif };
#endif
#define SSD1306_CMD_SET_COLUMN_LOW 0x00
#define SSD1306_CMD_SET_COLUMN_HIGH 0x10 #define SSD1306_CMD_SET_COLUMN_LOW 0x00
#define SSD1306_CMD_SET_PAGE 0xB0 #define SSD1306_CMD_SET_COLUMN_HIGH 0x10
#define SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE 0x20 #define SSD1306_CMD_SET_PAGE 0xB0
#define SSD1306_CMD_SET_COLUMN_ADDRESS 0x21 #define SSD1306_CMD_SET_MEMORY_ADDRESSING_MODE 0x20
#define SSD1306_CMD_SET_PAGE_ADDRESS 0x22 #define SSD1306_CMD_SET_COLUMN_ADDRESS 0x21
#define SSD1306_CMD_DEACTIVATE_SCROLL 0x2E #define SSD1306_CMD_SET_PAGE_ADDRESS 0x22
#define SSD1306_CMD_ACTIVATE_SCROLL 0x2F #define SSD1306_CMD_DEACTIVATE_SCROLL 0x2E
#define SSD1306_CMD_SET_CONTRAST_CONTROL 0x81 // Set Contrast Control for BANK0 #define SSD1306_CMD_ACTIVATE_SCROLL 0x2F
#define SSD1306_ENABLE_CHARGE_PUMP 0x8D #define SSD1306_CMD_SET_CONTRAST_CONTROL 0x81 // Set Contrast Control for BANK0
#define SSD1306_CMD_TEST_DISPLAY_OFF 0xA4 #define SSD1306_ENABLE_CHARGE_PUMP 0x8D
#define SSD1306_CMD_TEST_DISPLAY_ON 0xA5 #define SSD1306_CMD_TEST_DISPLAY_OFF 0xA4
#define SSD1306_CMD_NORMAL_DISPLAY 0xA6 // 1 = on pixel #define SSD1306_CMD_TEST_DISPLAY_ON 0xA5
#define SSD1306_CMD_INVERT_DISPLAY 0xA7 // 0 = on pixel #define SSD1306_CMD_NORMAL_DISPLAY 0xA6 // 1 = on pixel
#define SSD1306_CMD_DISPLAY_OFF 0xAE #define SSD1306_CMD_INVERT_DISPLAY 0xA7 // 0 = on pixel
#define SSD1306_CMD_DISPLAY_ON 0xAF #define SSD1306_CMD_DISPLAY_OFF 0xAE
#define SSD1306_CMD_MULTIPLEX_RATIO 0xA8 #define SSD1306_CMD_DISPLAY_ON 0xAF
#define SSD1306_CMD_SET_START_LINE 0x40 #define SSD1306_CMD_MULTIPLEX_RATIO 0xA8
#define SSD1306_CMD_SET_DISPLAY_OFFSET 0xD3 #define SSD1306_CMD_SET_START_LINE 0x40
#define SSD1306_CMD_SET_DISPLAY_CLOCK_DIVIDE_RATIO 0xD5 #define SSD1306_CMD_SET_DISPLAY_OFFSET 0xD3
#define SSD1306_CMD_SET_PRE_CHARGE_PERIOD 0xD9 #define SSD1306_CMD_SET_DISPLAY_CLOCK_DIVIDE_RATIO 0xD5
#define SSD1306_CMD_SET_COM_PINS 0xDA #define SSD1306_CMD_SET_PRE_CHARGE_PERIOD 0xD9
#define SSD1306_CMD_SET_VCOMH_DESELECT_LEVEL 0xDB #define SSD1306_CMD_SET_COM_PINS 0xDA
#define SSD1306_CONTROL_REG 0x00 #define SSD1306_CMD_SET_VCOMH_DESELECT_LEVEL 0xDB
#define SSD1306_DATA_REG 0x40 #define SSD1306_CONTROL_REG 0x00
#define SSD1306_DATA_REG 0x40