From 800972fa3e4b81f29c9fb1f30271b690a6aa83c3 Mon Sep 17 00:00:00 2001 From: gbouille <45919242+gbouille@users.noreply.github.com> Date: Mon, 31 Dec 2018 18:02:14 +0100 Subject: [PATCH] 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. --- src/SSD1306.cpp | 641 +++++++++++++++++++++++++----------------------- src/SSD1306.h | 293 +++++++++++----------- 2 files changed, 479 insertions(+), 455 deletions(-) diff --git a/src/SSD1306.cpp b/src/SSD1306.cpp index c96fc33..89931bd 100644 --- a/src/SSD1306.cpp +++ b/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 . - -#include "SSD1306.h" -#include "debug.h" -#include -#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 . + +#include "SSD1306.h" +#include "debug.h" +#include +#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); +} diff --git a/src/SSD1306.h b/src/SSD1306.h index b1ffb62..07d1293 100644 --- a/src/SSD1306.h +++ b/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 . - -#ifndef SSD1306_H -#define SSD1306_H - -#include -#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 . + +#ifndef SSD1306_H +#define SSD1306_H + +#include +#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