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
// 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);
}

View file

@ -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