226 lines
7.2 KiB
C++
226 lines
7.2 KiB
C++
#include "HermitCrab.h"
|
|
#include "SSD1306.h"
|
|
|
|
//#define DISPLAY_WIDTH SCREEN_WIDTH
|
|
//#define DISPLAY_HEIGHT SCREEN_HEIGHT
|
|
|
|
|
|
#define WIRE_WRITE Wire.write
|
|
#define SETWIRECLOCK Wire.setClock(400000UL) ///< Set before I2C transfer
|
|
#define RESWIRECLOCK Wire.setClock(100000UL) ///< Restore after I2C xfer
|
|
#define TRANSACTION_START SETWIRECLOCK
|
|
#define TRANSACTION_END RESWIRECLOCK
|
|
#define WIRE_MAX I2C_BUFFER_LENGTH
|
|
|
|
|
|
// Constructor for SSD1306 class
|
|
//SSD1306::SSD1306(Adafruit_SSD1306 &display)
|
|
SSD1306::SSD1306()
|
|
: Adafruit_GFX(SCREEN_WIDTH, SCREEN_HEIGHT)
|
|
, i2caddr(DISPLAY_I2C_ADDRESS)
|
|
{}
|
|
|
|
void SSD1306::dim(bool dim) {
|
|
// the range of contrast to too small to be really useful
|
|
// it is useful to dim the display
|
|
TRANSACTION_START;
|
|
ssd1306_command1(SSD1306_SETCONTRAST);
|
|
ssd1306_command1(dim ? 0 : contrast);
|
|
TRANSACTION_END;
|
|
}
|
|
|
|
void SSD1306::setContrast(uint8_t con) {
|
|
// the range of contrast to too small to be really useful
|
|
// it is useful to dim the display
|
|
TRANSACTION_START;
|
|
ssd1306_command1(SSD1306_SETCONTRAST);
|
|
ssd1306_command1(100 * con / contrast);
|
|
TRANSACTION_END;
|
|
}
|
|
|
|
bool SSD1306::begin(uint8_t vcs, uint8_t addr) {
|
|
vccstate = vcs;
|
|
i2caddr = addr;
|
|
|
|
TRANSACTION_START;
|
|
|
|
// Init sequence
|
|
static const uint8_t init1[] = {SSD1306_DISPLAYOFF, // 0xAE
|
|
SSD1306_SETDISPLAYCLOCKDIV, // 0xD5
|
|
0x80, // the suggested ratio 0x80
|
|
SSD1306_SETMULTIPLEX,
|
|
SCREEN_HEIGHT - 1}; // 0xA8
|
|
ssd1306_commandList(init1, sizeof(init1));
|
|
|
|
static const uint8_t init2[] = {SSD1306_SETDISPLAYOFFSET, // 0xD3
|
|
0x0, // no offset
|
|
SSD1306_SETSTARTLINE | 0x0, // line #0
|
|
SSD1306_CHARGEPUMP}; // 0x8D
|
|
ssd1306_commandList(init2, sizeof(init2));
|
|
|
|
ssd1306_command1((vccstate == SSD1306_EXTERNALVCC) ? 0x10 : 0x14);
|
|
|
|
static const uint8_t init3[] = {SSD1306_MEMORYMODE, // 0x20
|
|
0x00, // 0x0 act like ks0108
|
|
SSD1306_SEGREMAP | 0x1,
|
|
SSD1306_COMSCANDEC};
|
|
ssd1306_commandList(init3, sizeof(init3));
|
|
|
|
uint8_t comPins = 0x02;
|
|
contrast = 0x8F;
|
|
|
|
if ((WIDTH == 128) && (HEIGHT == 32)) {
|
|
comPins = 0x02;
|
|
contrast = 0x8F;
|
|
} else if ((WIDTH == 128) && (HEIGHT == 64)) {
|
|
comPins = 0x12;
|
|
contrast = (vccstate == SSD1306_EXTERNALVCC) ? 0x9F : 0xCF;
|
|
} else if ((WIDTH == 96) && (HEIGHT == 16)) {
|
|
comPins = 0x2; // ada x12
|
|
contrast = (vccstate == SSD1306_EXTERNALVCC) ? 0x10 : 0xAF;
|
|
} else {
|
|
// Other screen varieties -- TBD
|
|
}
|
|
|
|
ssd1306_command1(SSD1306_SETCOMPINS);
|
|
ssd1306_command1(comPins);
|
|
ssd1306_command1(SSD1306_SETCONTRAST);
|
|
ssd1306_command1(contrast);
|
|
|
|
ssd1306_command1(SSD1306_SETPRECHARGE); // 0xd9
|
|
ssd1306_command1((vccstate == SSD1306_EXTERNALVCC) ? 0x22 : 0xF1);
|
|
static const uint8_t init5[] = {
|
|
SSD1306_SETVCOMDETECT, // 0xDB
|
|
0x40,
|
|
SSD1306_DISPLAYALLON_RESUME, // 0xA4
|
|
SSD1306_NORMALDISPLAY, // 0xA6
|
|
SSD1306_DEACTIVATE_SCROLL,
|
|
SSD1306_DISPLAYON}; // Main screen turn on
|
|
ssd1306_commandList(init5, sizeof(init5));
|
|
|
|
TRANSACTION_END;
|
|
clearDisplayBuffer();
|
|
return true;
|
|
}
|
|
|
|
MY_IRAM_ATTR void SSD1306::updateScreen() {
|
|
TRANSACTION_START;
|
|
uint8_t dlist1[6];
|
|
dlist1[0] = SSD1306_PAGEADDR;
|
|
dlist1[1] = 0;
|
|
dlist1[2] = (height() / 8) - 1;
|
|
dlist1[3] = SSD1306_COLUMNADDR;
|
|
dlist1[4] = 0;
|
|
dlist1[5] = width() - 1;
|
|
ssd1306_commandList(dlist1, sizeof(dlist1));
|
|
|
|
uint16_t count = height() * width() / 8;
|
|
uint8_t *ptr = getBuffer();
|
|
|
|
Wire.beginTransmission(i2caddr);
|
|
WIRE_WRITE((uint8_t)0x40);
|
|
uint16_t bytesOut = 1;
|
|
// Loop through each page in the range
|
|
while (count--) {
|
|
if (bytesOut >= WIRE_MAX) {
|
|
Wire.endTransmission();
|
|
Wire.beginTransmission(i2caddr);
|
|
WIRE_WRITE((uint8_t)0x40);
|
|
bytesOut = 1;
|
|
}
|
|
WIRE_WRITE(*ptr++);
|
|
bytesOut++;
|
|
}
|
|
Wire.endTransmission(); // End transmission for the page
|
|
|
|
TRANSACTION_END;
|
|
}
|
|
|
|
MY_IRAM_ATTR void SSD1306::updateRegion(uint8_t pageStart, uint8_t pageEnd, uint8_t colStart = 0, uint8_t colEnd = 127) {
|
|
TRANSACTION_START;
|
|
uint8_t dlist1[6];
|
|
dlist1[0] = SSD1306_PAGEADDR;
|
|
dlist1[1] = pageStart;
|
|
dlist1[2] = pageEnd;
|
|
dlist1[3] = SSD1306_COLUMNADDR;
|
|
dlist1[4] = colStart;
|
|
dlist1[5] = colEnd;
|
|
ssd1306_commandList(dlist1, sizeof(dlist1));
|
|
|
|
uint16_t count = (colEnd - colStart + 1) * (pageEnd - pageStart + 1);
|
|
uint8_t *ptr = getBuffer() + pageStart * SCREEN_WIDTH + colStart;
|
|
uint16_t cols = colEnd - colStart + 1;
|
|
uint16_t offset = SCREEN_WIDTH - cols;
|
|
|
|
Wire.beginTransmission(i2caddr);
|
|
WIRE_WRITE((uint8_t)0x40);
|
|
uint16_t bytesOut = 1;
|
|
// Loop through each page in the range
|
|
for (uint8_t page = pageStart; page <= pageEnd; page++) {
|
|
// Send column data for the current page
|
|
for (uint8_t col = 0; col < cols; col++) {
|
|
if (bytesOut >= WIRE_MAX) {
|
|
Wire.endTransmission();
|
|
Wire.beginTransmission(i2caddr);
|
|
WIRE_WRITE((uint8_t)0x40);
|
|
bytesOut = 1;
|
|
}
|
|
WIRE_WRITE(*ptr++);
|
|
bytesOut++;
|
|
}
|
|
ptr += offset;
|
|
}
|
|
Wire.endTransmission(); // End transmission for the page
|
|
TRANSACTION_END;
|
|
}
|
|
|
|
MY_IRAM_ATTR void SSD1306::ssd1306_hscroll(uint8_t dir, uint8_t pageStart, uint8_t pageEnd, uint8_t offset, uint8_t interval) {
|
|
ssd1306_command1(dir ? SSD1306_RIGHT_HORIZONTAL_SCROLL : SSD1306_LEFT_HORIZONTAL_SCROLL);
|
|
ssd1306_command1(0x00); // Dummy
|
|
ssd1306_command1(pageStart);
|
|
ssd1306_command1(interval);
|
|
ssd1306_command1(pageEnd);
|
|
ssd1306_command1(offset);
|
|
ssd1306_command1(SSD1306_ACTIVATE_SCROLL);
|
|
}
|
|
|
|
MY_IRAM_ATTR void SSD1306::drawPixel(int16_t x, int16_t y, uint16_t color) {
|
|
if ((x >= 0) && (x < width()) && (y >= 0) && (y < height())) {
|
|
switch (color) {
|
|
case SSD1306_WHITE:
|
|
buffer[x + (y / 8) * WIDTH] |= (1 << (y & 7));
|
|
break;
|
|
case SSD1306_BLACK:
|
|
buffer[x + (y / 8) * WIDTH] &= ~(1 << (y & 7));
|
|
break;
|
|
case SSD1306_INVERSE:
|
|
buffer[x + (y / 8) * WIDTH] ^= (1 << (y & 7));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
MY_IRAM_ATTR void SSD1306::ssd1306_commandList(const uint8_t *c, uint8_t n) {
|
|
Wire.beginTransmission(i2caddr);
|
|
WIRE_WRITE((uint8_t)0x00); // Co = 0, D/C = 0
|
|
uint16_t bytesOut = 1;
|
|
while (n--) {
|
|
if (bytesOut >= WIRE_MAX) {
|
|
Wire.endTransmission();
|
|
Wire.beginTransmission(i2caddr);
|
|
WIRE_WRITE((uint8_t)0x00); // Co = 0, D/C = 0
|
|
bytesOut = 1;
|
|
}
|
|
WIRE_WRITE(*c++);
|
|
bytesOut++;
|
|
}
|
|
Wire.endTransmission();
|
|
}
|
|
|
|
MY_IRAM_ATTR void SSD1306::ssd1306_command1(uint8_t c) {
|
|
Wire.beginTransmission(i2caddr);
|
|
WIRE_WRITE((uint8_t)0x00); // Co = 0, D/C = 0
|
|
WIRE_WRITE(c);
|
|
Wire.endTransmission();
|
|
}
|