32#include "freertos/FreeRTOS.h"
33#include "freertos/task.h"
39#pragma GCC optimize ("O2")
42#define SSD1306_I2C_TIMEOUT 100
43#define SSD1306_I2C_FREQUENCY 400000
45#define SSD1306_UPDATETASK_STACK 1024
46#define SSD1306_UPDATETASK_PRIORITY 5
48#define SSD1306_BACKGROUND_PRIMITIVE_TIMEOUT 10000
51#define SSD1306_SETLOWCOLUMN 0x00
52#define SSD1306_SETHIGHCOLUMN 0x10
53#define SSD1306_MEMORYMODE 0x20
54#define SSD1306_COLUMNADDR 0x21
55#define SSD1306_PAGEADDR 0x22
56#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26
57#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27
58#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29
59#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A
60#define SSD1306_DEACTIVATE_SCROLL 0x2E
61#define SSD1306_ACTIVATE_SCROLL 0x2F
62#define SSD1306_SETSTARTLINE 0x40
63#define SSD1306_SETCONTRAST 0x81
64#define SSD1306_CHARGEPUMP 0x8D
65#define SSD1306_SEGREMAP 0xA0
66#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3
67#define SSD1306_DISPLAYALLON_RESUME 0xA4
68#define SSD1306_DISPLAYALLON 0xA5
69#define SSD1306_NORMALDISPLAY 0xA6
70#define SSD1306_INVERTDISPLAY 0xA7
71#define SSD1306_SETMULTIPLEX 0xA8
72#define SSD1306_DISPLAYOFF 0xAE
73#define SSD1306_DISPLAYON 0xAF
74#define SSD1306_COMSCANINC 0xC0
75#define SSD1306_COMSCANDEC 0xC8
76#define SSD1306_SETDISPLAYOFFSET 0xD3
77#define SSD1306_SETDISPLAYCLOCKDIV 0xD5
78#define SSD1306_SETPRECHARGE 0xD9
79#define SSD1306_SETCOMPINS 0xDA
80#define SSD1306_SETVCOMDETECT 0xDB
83#define SSD1306_SETPIXEL(x, y) (m_screenBuffer[(x) + ((y) >> 3) * m_viewPortWidth] |= (1 << ((y) & 7)))
84#define SSD1306_CLEARPIXEL(x, y) (m_screenBuffer[(x) + ((y) >> 3) * m_viewPortWidth] &= ~(1 << ((y) & 7)))
85#define SSD1306_INVERTPIXEL(x, y) (m_screenBuffer[(x) + ((y) >> 3) * m_viewPortWidth] ^= (1 << ((y) & 7)))
87#define SSD1306_SETPIXELCOLOR(x, y, color) { if ((color)) SSD1306_SETPIXEL((x), (y)); else SSD1306_CLEARPIXEL((x), (y)); }
89#define SSD1306_GETPIXEL(x, y) ((m_screenBuffer[(x) + ((y) >> 3) * m_viewPortWidth] >> ((y) & 7)) & 1)
95inline uint8_t RGB888toMono(RGB888
const & rgb)
97 return rgb.R > 0 || rgb.G > 0 || rgb.B > 0 ? 1 : 0;
101inline uint8_t RGBA2222toMono(uint8_t rgba2222)
103 return (rgba2222 & 0x3f) ? 1 : 0;
107inline uint8_t RGBA8888toMono(RGBA8888
const & rgba)
109 return rgba.R > 0 || rgba.G > 0 || rgba.B > 0 ? 1 : 0;
113inline uint8_t preparePixel(RGB888
const & rgb)
115 return RGB888toMono(rgb);
120SSD1306Controller::SSD1306Controller()
122 m_screenBuffer(nullptr),
123 m_updateTaskHandle(nullptr),
124 m_updateTaskRunning(false),
130SSD1306Controller::~SSD1306Controller()
136void SSD1306Controller::begin(
I2C * i2c,
int address, gpio_num_t resetGPIO)
138 CurrentVideoMode::set(VideoMode::I2C);
140 m_i2cAddress = address;
141 m_resetGPIO = resetGPIO;
145void SSD1306Controller::begin()
148 i2c->
begin(GPIO_NUM_4, GPIO_NUM_15);
153void SSD1306Controller::end()
155 if (m_updateTaskHandle)
156 vTaskDelete(m_updateTaskHandle);
157 m_updateTaskHandle =
nullptr;
159 free(m_screenBuffer);
160 m_screenBuffer =
nullptr;
164void SSD1306Controller::setResolution(
char const * modeline,
int viewPortWidth,
int viewPortHeight,
bool doubleBuffered)
167 int pos = 0, swidth, sheight;
168 int count = sscanf(modeline,
"\"%[^\"]\" %d %d %n", label, &swidth, &sheight, &pos);
169 if (count != 3 || pos == 0)
172 m_screenWidth = swidth;
173 m_screenHeight = sheight;
178 setScreenSize(m_screenWidth, m_screenHeight);
180 setDoubleBuffered(doubleBuffered);
182 m_viewPortWidth = viewPortWidth < 0 ? m_screenWidth : viewPortWidth;
183 m_viewPortHeight = viewPortHeight < 0 ? m_screenHeight : viewPortHeight;
189 if (!SSD1306_softReset())
195 xTaskCreate(&updateTaskFunc,
"", SSD1306_UPDATETASK_STACK,
this, SSD1306_UPDATETASK_PRIORITY, &m_updateTaskHandle);
198 m_updateTaskFuncSuspended = 0;
202void SSD1306Controller::setScreenCol(
int value)
204 if (value != m_screenCol) {
205 m_screenCol = iclamp(value, 0, m_viewPortWidth - m_screenWidth);
211void SSD1306Controller::setScreenRow(
int value)
213 if (value != m_screenRow) {
214 m_screenRow = iclamp(value, 0, m_viewPortHeight - m_screenHeight);
220void SSD1306Controller::sendRefresh()
222 Primitive p(PrimitiveCmd::Refresh,
Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
227bool SSD1306Controller::SSD1306_sendData(uint8_t * buf,
int count, uint8_t ctrl)
229 int bufSize = m_i2c->getMaxBufferLength();
230 uint8_t sbuf[bufSize];
233 int bToSend = imin(bufSize - 1, count);
234 memcpy(&sbuf[1], buf, bToSend);
235 if (!m_i2c->write(m_i2cAddress, sbuf, bToSend + 1, SSD1306_I2C_FREQUENCY, SSD1306_I2C_TIMEOUT))
245bool SSD1306Controller::SSD1306_sendCmd(uint8_t c)
247 return SSD1306_sendData(&c, 1, 0x00);
251bool SSD1306Controller::SSD1306_sendCmd(uint8_t c1, uint8_t c2)
253 uint8_t buf[2] = { c1, c2 };
254 return SSD1306_sendData(buf, 2, 0x00);
258bool SSD1306Controller::SSD1306_sendCmd(uint8_t c1, uint8_t c2, uint8_t c3)
260 uint8_t buf[3] = { c1, c2, c3 };
261 return SSD1306_sendData(buf, 3, 0x00);
266void SSD1306Controller::SSD1306_hardReset()
268 if (m_resetGPIO != GPIO_UNUSED) {
269 configureGPIO(m_resetGPIO, GPIO_MODE_OUTPUT);
270 gpio_set_level(m_resetGPIO, 1);
271 vTaskDelay(1 / portTICK_PERIOD_MS);
272 gpio_set_level(m_resetGPIO, 0);
273 vTaskDelay(10 / portTICK_PERIOD_MS);
274 gpio_set_level(m_resetGPIO, 1);
280bool SSD1306Controller::SSD1306_softReset()
282 SSD1306_sendCmd(SSD1306_DISPLAYOFF);
283 SSD1306_sendCmd(SSD1306_SETDISPLAYCLOCKDIV, 0x80);
284 SSD1306_sendCmd(SSD1306_SETMULTIPLEX, m_screenHeight - 1);
285 SSD1306_sendCmd(SSD1306_SETDISPLAYOFFSET, 0);
286 SSD1306_sendCmd(SSD1306_SETSTARTLINE);
287 SSD1306_sendCmd(SSD1306_CHARGEPUMP, 0x14);
288 SSD1306_sendCmd(SSD1306_MEMORYMODE, 0b100);
290 if (m_screenHeight == 64) {
291 SSD1306_sendCmd(SSD1306_SETCOMPINS, 0x12);
292 SSD1306_sendCmd(SSD1306_SETCONTRAST, 0xCF);
293 }
else if (m_screenHeight == 32) {
294 SSD1306_sendCmd(SSD1306_SETCOMPINS, 0x02);
295 SSD1306_sendCmd(SSD1306_SETCONTRAST, 0x8F);
297 SSD1306_sendCmd(SSD1306_SETPRECHARGE, 0xF1);
298 SSD1306_sendCmd(SSD1306_SETVCOMDETECT, 0x40);
299 SSD1306_sendCmd(SSD1306_DISPLAYALLON_RESUME);
300 SSD1306_sendCmd(SSD1306_NORMALDISPLAY);
301 SSD1306_sendCmd(SSD1306_DEACTIVATE_SCROLL);
302 return SSD1306_sendCmd(SSD1306_DISPLAYON);
306void SSD1306Controller::setupOrientation()
308 switch (m_orientation) {
309 case SSD1306Orientation::Normal:
310 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x1);
311 SSD1306_sendCmd(SSD1306_COMSCANDEC);
313 case SSD1306Orientation::ReverseHorizontal:
314 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x0);
315 SSD1306_sendCmd(SSD1306_COMSCANDEC);
317 case SSD1306Orientation::ReverseVertical:
318 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x1);
319 SSD1306_sendCmd(SSD1306_COMSCANINC);
321 case SSD1306Orientation::Rotate180:
322 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x0);
323 SSD1306_sendCmd(SSD1306_COMSCANINC);
331 m_orientation = value;
337void SSD1306Controller::SSD1306_sendScreenBuffer(
Rect updateRect)
340 const int screenRow = m_screenRow & ~7;
343 const Rect scrRect =
Rect(m_screenCol, screenRow, m_screenCol + m_screenWidth - 1, screenRow + m_screenHeight - 1);
347 updateRect.
Y2 = (updateRect.
Y2 + 7) & ~7;
350 if (scrRect.intersects(updateRect)) {
353 Rect r = updateRect.intersection(scrRect);
356 const int screenX1 = r.
X1 - m_screenCol;
357 const int screenX2 = r.
X2 - m_screenCol;
360 for (
int y = r.
Y1; y <= r.
Y2; y += 8) {
361 int screenY = y - screenRow;
363 int page = screenY >> 3;
364 if (!(SSD1306_sendCmd(SSD1306_PAGEADDR, page, page) && SSD1306_sendCmd(SSD1306_COLUMNADDR, screenX1, screenX2)))
366 SSD1306_sendData(m_screenBuffer + r.
X1 + (y >> 3) * m_viewPortWidth, r.width(), 0x40);
376 SSD1306_sendCmd(value ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY);
380void SSD1306Controller::allocScreenBuffer()
382 m_screenBuffer = (uint8_t*) malloc(m_viewPortWidth * m_viewPortHeight / 8);
383 memset(m_screenBuffer, 0, m_viewPortWidth * m_viewPortHeight / 8);
387void SSD1306Controller::updateTaskFunc(
void * pvParameters)
389 SSD1306Controller * ctrl = (SSD1306Controller*) pvParameters;
393 ctrl->waitForPrimitives();
396 if (ctrl->m_updateTaskFuncSuspended > 0)
397 ulTaskNotifyTake(
true, portMAX_DELAY);
399 ctrl->m_updateTaskRunning =
true;
401 Rect updateRect = Rect(SHRT_MAX, SHRT_MAX, SHRT_MIN, SHRT_MIN);
403 int64_t startTime = ctrl->backgroundPrimitiveTimeoutEnabled() ? esp_timer_get_time() : 0;
407 if (ctrl->getPrimitive(&prim) ==
false)
410 ctrl->execPrimitive(prim, updateRect,
false);
412 if (ctrl->m_updateTaskFuncSuspended > 0)
415 }
while (!ctrl->backgroundPrimitiveTimeoutEnabled() || (startTime + SSD1306_BACKGROUND_PRIMITIVE_TIMEOUT > esp_timer_get_time()));
417 ctrl->showSprites(updateRect);
419 ctrl->m_updateTaskRunning =
false;
421 if (!ctrl->isDoubleBuffered())
422 ctrl->SSD1306_sendScreenBuffer(updateRect);
428void SSD1306Controller::suspendBackgroundPrimitiveExecution()
430 ++m_updateTaskFuncSuspended;
431 while (m_updateTaskRunning)
436void SSD1306Controller::resumeBackgroundPrimitiveExecution()
438 m_updateTaskFuncSuspended = tmax(0, m_updateTaskFuncSuspended - 1);
439 if (m_updateTaskFuncSuspended == 0)
440 xTaskNotifyGive(m_updateTaskHandle);
444void SSD1306Controller::setPixelAt(PixelDesc
const & pixelDesc,
Rect & updateRect)
446 genericSetPixelAt(pixelDesc, updateRect,
447 [&] (
RGB888 const & color) {
return preparePixel(color); },
448 [&] (
int X,
int Y, uint8_t pattern) { SSD1306_SETPIXELCOLOR(
X,
Y, pattern); }
455void SSD1306Controller::absDrawLine(
int X1,
int Y1,
int X2,
int Y2, RGB888 color)
457 genericAbsDrawLine(
X1,
Y1,
X2,
Y2, color,
458 [&] (RGB888
const & color) {
return preparePixel(color); },
459 [&] (
int Y,
int X1,
int X2, uint8_t pattern) { rawFillRow(
Y,
X1,
X2, pattern); },
460 [&] (
int Y,
int X1,
int X2) { rawInvertRow(
Y,
X1,
X2); },
461 [&] (
int X,
int Y, uint8_t pattern) { SSD1306_SETPIXELCOLOR(
X,
Y, pattern); },
462 [&] (
int X,
int Y) { SSD1306_INVERTPIXEL(
X,
Y); }
468void SSD1306Controller::rawFillRow(
int y,
int x1,
int x2, uint8_t pattern)
471 for (; x1 <= x2; ++x1)
472 SSD1306_SETPIXEL(x1, y);
474 for (; x1 <= x2; ++x1)
475 SSD1306_CLEARPIXEL(x1, y);
481void SSD1306Controller::rawFillRow(
int y,
int x1,
int x2, RGB888 color)
483 rawFillRow(y, x1, x2, preparePixel(color));
488void SSD1306Controller::rawInvertRow(
int y,
int x1,
int x2)
490 for (; x1 <= x2; ++x1)
491 SSD1306_INVERTPIXEL(x1, y);
495void SSD1306Controller::drawEllipse(Size
const & size, Rect & updateRect)
497 genericDrawEllipse(size, updateRect,
498 [&] (RGB888
const & color) {
return preparePixel(color); },
499 [&] (
int X,
int Y, uint8_t pattern) { SSD1306_SETPIXELCOLOR(
X,
Y, pattern); }
504void SSD1306Controller::clear(Rect & updateRect)
506 hideSprites(updateRect);
507 uint8_t pattern = preparePixel(getActualBrushColor());
508 memset(m_screenBuffer, (pattern ? 255 : 0), m_viewPortWidth * m_viewPortHeight / 8);
512void SSD1306Controller::VScroll(
int scroll, Rect & updateRect)
514 genericVScroll(scroll, updateRect,
515 [&] (
int x1,
int x2,
int srcY,
int dstY) { rawCopyRow(x1, x2, srcY, dstY); },
516 [&] (
int y,
int x1,
int x2, RGB888 color) { rawFillRow(y, x1, x2, color); }
521void SSD1306Controller::HScroll(
int scroll, Rect & updateRect)
523 genericHScroll(scroll, updateRect,
524 [&] (RGB888
const & color) {
return preparePixel(color); },
525 [&] (
int y) {
return y; },
526 [&] (
int y,
int x) {
return SSD1306_GETPIXEL(x, y); },
527 [&] (
int y,
int x,
int pattern) { SSD1306_SETPIXELCOLOR(x, y, pattern); }
532void SSD1306Controller::drawGlyph(Glyph
const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect)
534 genericDrawGlyph(glyph, glyphOptions, penColor, brushColor, updateRect,
535 [&] (RGB888
const & color) {
return preparePixel(color); },
536 [&] (
int y) {
return y; },
537 [&] (
int y,
int x, uint8_t pattern) { SSD1306_SETPIXELCOLOR(x, y, pattern); }
542void SSD1306Controller::rawCopyRow(
int x1,
int x2,
int srcY,
int dstY)
544 for (; x1 <= x2; ++x1) {
545 uint8_t c = SSD1306_GETPIXEL(x1, srcY);
546 SSD1306_SETPIXELCOLOR(x1, dstY, c);
551void SSD1306Controller::invertRect(Rect
const & rect, Rect & updateRect)
553 genericInvertRect(rect, updateRect,
554 [&] (
int Y,
int X1,
int X2) { rawInvertRow(
Y,
X1,
X2); }
561 invertRect(rect, updateRect);
566void SSD1306Controller::copyRect(Rect
const & source, Rect & updateRect)
568 genericCopyRect(source, updateRect,
569 [&] (
int y) {
return y; },
570 [&] (
int y,
int x) {
return SSD1306_GETPIXEL(x, y); },
571 [&] (
int y,
int x, uint8_t pattern) { SSD1306_SETPIXELCOLOR(x, y, pattern); }
577void SSD1306Controller::readScreen(Rect
const & rect, RGB888 * destBuf)
579 for (
int y = rect.Y1; y <= rect.Y2; ++y)
580 for (
int x = rect.X1; x <= rect.X2; ++x, ++destBuf)
581 *destBuf = SSD1306_GETPIXEL(x, y) ? RGB888(255, 255, 255) : RGB888(0, 0, 0);
585void SSD1306Controller::rawDrawBitmap_Native(
int destX,
int destY, Bitmap
const * bitmap,
int X1,
int Y1,
int XCount,
int YCount)
587 genericRawDrawBitmap_Native(destX, destY, (uint8_t*) bitmap->data, bitmap->width,
X1,
Y1, XCount, YCount,
588 [&] (
int y) { return y; },
589 [&] (
int y,
int x, uint8_t src) { SSD1306_SETPIXELCOLOR(x, y, src); }
594void SSD1306Controller::rawDrawBitmap_Mask(
int destX,
int destY, Bitmap
const * bitmap,
void * saveBackground,
int X1,
int Y1,
int XCount,
int YCount)
596 uint8_t foregroundColor = RGB888toMono(bitmap->foregroundColor);
597 genericRawDrawBitmap_Mask(destX, destY, bitmap, (uint8_t*)saveBackground,
X1,
Y1, XCount, YCount,
598 [&] (
int y) {
return y; },
599 [&] (
int y,
int x) {
return SSD1306_GETPIXEL(x, y); },
600 [&] (
int y,
int x) { SSD1306_SETPIXELCOLOR(x, y, foregroundColor); }
605void SSD1306Controller::rawDrawBitmap_RGBA2222(
int destX,
int destY, Bitmap
const * bitmap,
void * saveBackground,
int X1,
int Y1,
int XCount,
int YCount)
607 genericRawDrawBitmap_RGBA2222(destX, destY, bitmap, (uint8_t*)saveBackground,
X1,
Y1, XCount, YCount,
608 [&] (
int y) {
return y; },
609 [&] (
int y,
int x) {
return SSD1306_GETPIXEL(x, y); },
610 [&] (
int y,
int x, uint8_t src) { SSD1306_SETPIXELCOLOR(x, y, RGBA2222toMono(src)); }
615void SSD1306Controller::rawDrawBitmap_RGBA8888(
int destX,
int destY, Bitmap
const * bitmap,
void * saveBackground,
int X1,
int Y1,
int XCount,
int YCount)
617 genericRawDrawBitmap_RGBA8888(destX, destY, bitmap, (uint8_t*)saveBackground,
X1,
Y1, XCount, YCount,
618 [&] (
int y) {
return y; },
619 [&] (
int y,
int x) {
return SSD1306_GETPIXEL(x, y); },
620 [&] (
int y,
int x,
RGBA8888 const & src) { SSD1306_SETPIXELCOLOR(x, y, RGBA8888toMono(src)); }
625void SSD1306Controller::swapBuffers()
628 SSD1306_sendScreenBuffer(Rect(0, 0, getViewPortWidth() - 1, getViewPortHeight() - 1));
This file contains fabgl::SSD1306Controller definition.
bool begin(gpio_num_t SDAGPIO, gpio_num_t SCLGPIO)
Initializes I2C instance associating GPIOs to I2C signals.
I2C class allows multiple tasks to communicate with I2C devices, serializing read/write jobs.
This file contains some utility classes and functions.
SSD1306Orientation
This enum defines SSD1306 orientation.
Represents a 24 bit RGB color.