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
|
// 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);
|
||||||
|
}
|
||||||
|
|
293
src/SSD1306.h
293
src/SSD1306.h
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue