HCesp/SSD1306.cpp

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