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)
139 m_i2cAddress = address;
140 m_resetGPIO = resetGPIO;
144void SSD1306Controller::begin()
147 i2c->
begin(GPIO_NUM_4, GPIO_NUM_15);
152void SSD1306Controller::end()
154 if (m_updateTaskHandle)
155 vTaskDelete(m_updateTaskHandle);
156 m_updateTaskHandle =
nullptr;
158 free(m_screenBuffer);
159 m_screenBuffer =
nullptr;
163void SSD1306Controller::setResolution(
char const * modeline,
int viewPortWidth,
int viewPortHeight,
bool doubleBuffered)
166 int pos = 0, swidth, sheight;
167 int count = sscanf(modeline,
"\"%[^\"]\" %d %d %n", label, &swidth, &sheight, &pos);
168 if (count != 3 || pos == 0)
171 m_screenWidth = swidth;
172 m_screenHeight = sheight;
177 setScreenSize(m_screenWidth, m_screenHeight);
179 setDoubleBuffered(doubleBuffered);
181 m_viewPortWidth = viewPortWidth < 0 ? m_screenWidth : viewPortWidth;
182 m_viewPortHeight = viewPortHeight < 0 ? m_screenHeight : viewPortHeight;
188 if (!SSD1306_softReset())
194 xTaskCreate(&updateTaskFunc,
"", SSD1306_UPDATETASK_STACK,
this, SSD1306_UPDATETASK_PRIORITY, &m_updateTaskHandle);
197 m_updateTaskFuncSuspended = 0;
201void SSD1306Controller::setScreenCol(
int value)
203 if (value != m_screenCol) {
204 m_screenCol = iclamp(value, 0, m_viewPortWidth - m_screenWidth);
210void SSD1306Controller::setScreenRow(
int value)
212 if (value != m_screenRow) {
213 m_screenRow = iclamp(value, 0, m_viewPortHeight - m_screenHeight);
219void SSD1306Controller::sendRefresh()
221 Primitive p(PrimitiveCmd::Refresh,
Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
226bool SSD1306Controller::SSD1306_sendData(uint8_t * buf,
int count, uint8_t ctrl)
228 int bufSize = m_i2c->getMaxBufferLength();
229 uint8_t sbuf[bufSize];
232 int bToSend = imin(bufSize - 1, count);
233 memcpy(&sbuf[1], buf, bToSend);
234 if (!m_i2c->write(m_i2cAddress, sbuf, bToSend + 1, SSD1306_I2C_FREQUENCY, SSD1306_I2C_TIMEOUT))
244bool SSD1306Controller::SSD1306_sendCmd(uint8_t c)
246 return SSD1306_sendData(&c, 1, 0x00);
250bool SSD1306Controller::SSD1306_sendCmd(uint8_t c1, uint8_t c2)
252 uint8_t buf[2] = { c1, c2 };
253 return SSD1306_sendData(buf, 2, 0x00);
257bool SSD1306Controller::SSD1306_sendCmd(uint8_t c1, uint8_t c2, uint8_t c3)
259 uint8_t buf[3] = { c1, c2, c3 };
260 return SSD1306_sendData(buf, 3, 0x00);
265void SSD1306Controller::SSD1306_hardReset()
267 if (m_resetGPIO != GPIO_UNUSED) {
268 configureGPIO(m_resetGPIO, GPIO_MODE_OUTPUT);
269 gpio_set_level(m_resetGPIO, 1);
270 vTaskDelay(1 / portTICK_PERIOD_MS);
271 gpio_set_level(m_resetGPIO, 0);
272 vTaskDelay(10 / portTICK_PERIOD_MS);
273 gpio_set_level(m_resetGPIO, 1);
279bool SSD1306Controller::SSD1306_softReset()
281 SSD1306_sendCmd(SSD1306_DISPLAYOFF);
282 SSD1306_sendCmd(SSD1306_SETDISPLAYCLOCKDIV, 0x80);
283 SSD1306_sendCmd(SSD1306_SETMULTIPLEX, m_screenHeight - 1);
284 SSD1306_sendCmd(SSD1306_SETDISPLAYOFFSET, 0);
285 SSD1306_sendCmd(SSD1306_SETSTARTLINE);
286 SSD1306_sendCmd(SSD1306_CHARGEPUMP, 0x14);
287 SSD1306_sendCmd(SSD1306_MEMORYMODE, 0b100);
289 if (m_screenHeight == 64) {
290 SSD1306_sendCmd(SSD1306_SETCOMPINS, 0x12);
291 SSD1306_sendCmd(SSD1306_SETCONTRAST, 0xCF);
292 }
else if (m_screenHeight == 32) {
293 SSD1306_sendCmd(SSD1306_SETCOMPINS, 0x02);
294 SSD1306_sendCmd(SSD1306_SETCONTRAST, 0x8F);
296 SSD1306_sendCmd(SSD1306_SETPRECHARGE, 0xF1);
297 SSD1306_sendCmd(SSD1306_SETVCOMDETECT, 0x40);
298 SSD1306_sendCmd(SSD1306_DISPLAYALLON_RESUME);
299 SSD1306_sendCmd(SSD1306_NORMALDISPLAY);
300 SSD1306_sendCmd(SSD1306_DEACTIVATE_SCROLL);
301 return SSD1306_sendCmd(SSD1306_DISPLAYON);
305void SSD1306Controller::setupOrientation()
307 switch (m_orientation) {
308 case SSD1306Orientation::Normal:
309 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x1);
310 SSD1306_sendCmd(SSD1306_COMSCANDEC);
312 case SSD1306Orientation::ReverseHorizontal:
313 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x0);
314 SSD1306_sendCmd(SSD1306_COMSCANDEC);
316 case SSD1306Orientation::ReverseVertical:
317 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x1);
318 SSD1306_sendCmd(SSD1306_COMSCANINC);
320 case SSD1306Orientation::Rotate180:
321 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x0);
322 SSD1306_sendCmd(SSD1306_COMSCANINC);
330 m_orientation = value;
336void SSD1306Controller::SSD1306_sendScreenBuffer(
Rect updateRect)
339 const int screenRow = m_screenRow & ~7;
342 const Rect scrRect =
Rect(m_screenCol, screenRow, m_screenCol + m_screenWidth - 1, screenRow + m_screenHeight - 1);
346 updateRect.
Y2 = (updateRect.
Y2 + 7) & ~7;
349 if (scrRect.intersects(updateRect)) {
352 Rect r = updateRect.intersection(scrRect);
355 const int screenX1 = r.
X1 - m_screenCol;
356 const int screenX2 = r.
X2 - m_screenCol;
359 for (
int y = r.
Y1; y <= r.
Y2; y += 8) {
360 int screenY = y - screenRow;
362 int page = screenY >> 3;
363 if (!(SSD1306_sendCmd(SSD1306_PAGEADDR, page, page) && SSD1306_sendCmd(SSD1306_COLUMNADDR, screenX1, screenX2)))
365 SSD1306_sendData(m_screenBuffer + r.
X1 + (y >> 3) * m_viewPortWidth, r.width(), 0x40);
375 SSD1306_sendCmd(value ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY);
379void SSD1306Controller::allocScreenBuffer()
381 m_screenBuffer = (uint8_t*) malloc(m_viewPortWidth * m_viewPortHeight / 8);
382 memset(m_screenBuffer, 0, m_viewPortWidth * m_viewPortHeight / 8);
386void SSD1306Controller::updateTaskFunc(
void * pvParameters)
388 SSD1306Controller * ctrl = (SSD1306Controller*) pvParameters;
392 ctrl->waitForPrimitives();
395 if (ctrl->m_updateTaskFuncSuspended > 0)
396 ulTaskNotifyTake(
true, portMAX_DELAY);
398 ctrl->m_updateTaskRunning =
true;
400 Rect updateRect = Rect(SHRT_MAX, SHRT_MAX, SHRT_MIN, SHRT_MIN);
402 int64_t startTime = ctrl->backgroundPrimitiveTimeoutEnabled() ? esp_timer_get_time() : 0;
406 if (ctrl->getPrimitive(&prim) ==
false)
409 ctrl->execPrimitive(prim, updateRect,
false);
411 if (ctrl->m_updateTaskFuncSuspended > 0)
414 }
while (!ctrl->backgroundPrimitiveTimeoutEnabled() || (startTime + SSD1306_BACKGROUND_PRIMITIVE_TIMEOUT > esp_timer_get_time()));
416 ctrl->showSprites(updateRect);
418 ctrl->m_updateTaskRunning =
false;
420 if (!ctrl->isDoubleBuffered())
421 ctrl->SSD1306_sendScreenBuffer(updateRect);
427void SSD1306Controller::suspendBackgroundPrimitiveExecution()
429 ++m_updateTaskFuncSuspended;
430 while (m_updateTaskRunning)
435void SSD1306Controller::resumeBackgroundPrimitiveExecution()
437 m_updateTaskFuncSuspended = tmax(0, m_updateTaskFuncSuspended - 1);
438 if (m_updateTaskFuncSuspended == 0)
439 xTaskNotifyGive(m_updateTaskHandle);
443void SSD1306Controller::setPixelAt(PixelDesc
const & pixelDesc,
Rect & updateRect)
445 genericSetPixelAt(pixelDesc, updateRect,
446 [&] (
RGB888 const & color) {
return preparePixel(color); },
447 [&] (
int X,
int Y, uint8_t pattern) { SSD1306_SETPIXELCOLOR(
X,
Y, pattern); }
454void SSD1306Controller::absDrawLine(
int X1,
int Y1,
int X2,
int Y2, RGB888 color)
456 genericAbsDrawLine(
X1,
Y1,
X2,
Y2, color,
457 [&] (RGB888
const & color) {
return preparePixel(color); },
458 [&] (
int Y,
int X1,
int X2, uint8_t pattern) { rawFillRow(
Y,
X1,
X2, pattern); },
459 [&] (
int Y,
int X1,
int X2) { rawInvertRow(
Y,
X1,
X2); },
460 [&] (
int X,
int Y, uint8_t pattern) { SSD1306_SETPIXELCOLOR(
X,
Y, pattern); },
461 [&] (
int X,
int Y) { SSD1306_INVERTPIXEL(
X,
Y); }
467void SSD1306Controller::rawFillRow(
int y,
int x1,
int x2, uint8_t pattern)
470 for (; x1 <= x2; ++x1)
471 SSD1306_SETPIXEL(x1, y);
473 for (; x1 <= x2; ++x1)
474 SSD1306_CLEARPIXEL(x1, y);
480void SSD1306Controller::rawFillRow(
int y,
int x1,
int x2, RGB888 color)
482 rawFillRow(y, x1, x2, preparePixel(color));
487void SSD1306Controller::rawInvertRow(
int y,
int x1,
int x2)
489 for (; x1 <= x2; ++x1)
490 SSD1306_INVERTPIXEL(x1, y);
494void SSD1306Controller::drawEllipse(Size
const & size, Rect & updateRect)
496 genericDrawEllipse(size, updateRect,
497 [&] (RGB888
const & color) {
return preparePixel(color); },
498 [&] (
int X,
int Y, uint8_t pattern) { SSD1306_SETPIXELCOLOR(
X,
Y, pattern); }
503void SSD1306Controller::clear(Rect & updateRect)
505 hideSprites(updateRect);
506 uint8_t pattern = preparePixel(getActualBrushColor());
507 memset(m_screenBuffer, (pattern ? 255 : 0), m_viewPortWidth * m_viewPortHeight / 8);
511void SSD1306Controller::VScroll(
int scroll, Rect & updateRect)
513 genericVScroll(scroll, updateRect,
514 [&] (
int x1,
int x2,
int srcY,
int dstY) { rawCopyRow(x1, x2, srcY, dstY); },
515 [&] (
int y,
int x1,
int x2, RGB888 color) { rawFillRow(y, x1, x2, color); }
520void SSD1306Controller::HScroll(
int scroll, Rect & updateRect)
522 genericHScroll(scroll, updateRect,
523 [&] (RGB888
const & color) {
return preparePixel(color); },
524 [&] (
int y) {
return y; },
525 [&] (
int y,
int x) {
return SSD1306_GETPIXEL(x, y); },
526 [&] (
int y,
int x,
int pattern) { SSD1306_SETPIXELCOLOR(x, y, pattern); }
531void SSD1306Controller::drawGlyph(Glyph
const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect)
533 genericDrawGlyph(glyph, glyphOptions, penColor, brushColor, updateRect,
534 [&] (RGB888
const & color) {
return preparePixel(color); },
535 [&] (
int y) {
return y; },
536 [&] (
int y,
int x, uint8_t pattern) { SSD1306_SETPIXELCOLOR(x, y, pattern); }
541void SSD1306Controller::rawCopyRow(
int x1,
int x2,
int srcY,
int dstY)
543 for (; x1 <= x2; ++x1) {
544 uint8_t c = SSD1306_GETPIXEL(x1, srcY);
545 SSD1306_SETPIXELCOLOR(x1, dstY, c);
550void SSD1306Controller::invertRect(Rect
const & rect, Rect & updateRect)
552 genericInvertRect(rect, updateRect,
553 [&] (
int Y,
int X1,
int X2) { rawInvertRow(
Y,
X1,
X2); }
560 invertRect(rect, updateRect);
565void SSD1306Controller::copyRect(Rect
const & source, Rect & updateRect)
567 genericCopyRect(source, updateRect,
568 [&] (
int y) {
return y; },
569 [&] (
int y,
int x) {
return SSD1306_GETPIXEL(x, y); },
570 [&] (
int y,
int x, uint8_t pattern) { SSD1306_SETPIXELCOLOR(x, y, pattern); }
576void SSD1306Controller::readScreen(Rect
const & rect, RGB888 * destBuf)
578 for (
int y = rect.Y1; y <= rect.Y2; ++y)
579 for (
int x = rect.X1; x <= rect.X2; ++x, ++destBuf)
580 *destBuf = SSD1306_GETPIXEL(x, y) ? RGB888(255, 255, 255) : RGB888(0, 0, 0);
584void SSD1306Controller::rawDrawBitmap_Native(
int destX,
int destY, Bitmap
const * bitmap,
int X1,
int Y1,
int XCount,
int YCount)
586 genericRawDrawBitmap_Native(destX, destY, (uint8_t*) bitmap->data, bitmap->width,
X1,
Y1, XCount, YCount,
587 [&] (
int y) { return y; },
588 [&] (
int y,
int x, uint8_t src) { SSD1306_SETPIXELCOLOR(x, y, src); }
593void SSD1306Controller::rawDrawBitmap_Mask(
int destX,
int destY, Bitmap
const * bitmap,
void * saveBackground,
int X1,
int Y1,
int XCount,
int YCount)
595 uint8_t foregroundColor = RGB888toMono(bitmap->foregroundColor);
596 genericRawDrawBitmap_Mask(destX, destY, bitmap, (uint8_t*)saveBackground,
X1,
Y1, XCount, YCount,
597 [&] (
int y) {
return y; },
598 [&] (
int y,
int x) {
return SSD1306_GETPIXEL(x, y); },
599 [&] (
int y,
int x) { SSD1306_SETPIXELCOLOR(x, y, foregroundColor); }
604void SSD1306Controller::rawDrawBitmap_RGBA2222(
int destX,
int destY, Bitmap
const * bitmap,
void * saveBackground,
int X1,
int Y1,
int XCount,
int YCount)
606 genericRawDrawBitmap_RGBA2222(destX, destY, bitmap, (uint8_t*)saveBackground,
X1,
Y1, XCount, YCount,
607 [&] (
int y) {
return y; },
608 [&] (
int y,
int x) {
return SSD1306_GETPIXEL(x, y); },
609 [&] (
int y,
int x, uint8_t src) { SSD1306_SETPIXELCOLOR(x, y, RGBA2222toMono(src)); }
614void SSD1306Controller::rawDrawBitmap_RGBA8888(
int destX,
int destY, Bitmap
const * bitmap,
void * saveBackground,
int X1,
int Y1,
int XCount,
int YCount)
616 genericRawDrawBitmap_RGBA8888(destX, destY, bitmap, (uint8_t*)saveBackground,
X1,
Y1, XCount, YCount,
617 [&] (
int y) {
return y; },
618 [&] (
int y,
int x) {
return SSD1306_GETPIXEL(x, y); },
619 [&] (
int y,
int x,
RGBA8888 const & src) { SSD1306_SETPIXELCOLOR(x, y, RGBA8888toMono(src)); }
624void SSD1306Controller::swapBuffers()
627 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.