32 #include "freertos/FreeRTOS.h" 33 #include "freertos/task.h" 39 #pragma GCC optimize ("O2") 42 #define SSD1306_I2C_TIMEOUT 100 // ms 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 // uS 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) 95 inline uint8_t RGB888toMono(RGB888
const & rgb)
97 return rgb.R > 0 || rgb.G > 0 || rgb.B > 0 ? 1 : 0;
101 inline uint8_t RGBA2222toMono(uint8_t rgba2222)
103 return (rgba2222 & 0x3f) ? 1 : 0;
107 inline uint8_t RGBA8888toMono(
RGBA8888 const & rgba)
109 return rgba.R > 0 || rgba.G > 0 || rgba.B > 0 ? 1 : 0;
113 inline uint8_t preparePixel(RGB888
const & rgb)
115 return RGB888toMono(rgb);
120 SSD1306Controller::SSD1306Controller()
122 m_screenBuffer(nullptr),
123 m_updateTaskHandle(nullptr),
124 m_updateTaskRunning(false),
130 SSD1306Controller::~SSD1306Controller()
139 m_i2cAddress = address;
140 m_resetGPIO = resetGPIO;
147 i2c->
begin(GPIO_NUM_4, GPIO_NUM_15);
152 void SSD1306Controller::end()
154 if (m_updateTaskHandle)
155 vTaskDelete(m_updateTaskHandle);
156 m_updateTaskHandle =
nullptr;
158 free(m_screenBuffer);
159 m_screenBuffer =
nullptr;
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;
203 if (value != m_screenCol) {
204 m_screenCol = iclamp(value, 0, m_viewPortWidth - m_screenWidth);
212 if (value != m_screenRow) {
213 m_screenRow = iclamp(value, 0, m_viewPortHeight - m_screenHeight);
219 void SSD1306Controller::sendRefresh()
221 Primitive p(PrimitiveCmd::Refresh,
Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
226 bool SSD1306Controller::SSD1306_sendData(uint8_t * buf,
int count, uint8_t ctrl)
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))
244 bool SSD1306Controller::SSD1306_sendCmd(uint8_t c)
246 return SSD1306_sendData(&c, 1, 0x00);
250 bool SSD1306Controller::SSD1306_sendCmd(uint8_t c1, uint8_t c2)
252 uint8_t buf[2] = { c1, c2 };
253 return SSD1306_sendData(buf, 2, 0x00);
257 bool 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);
265 void 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);
279 bool 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);
305 void SSD1306Controller::setupOrientation()
307 switch (m_orientation) {
309 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x1);
310 SSD1306_sendCmd(SSD1306_COMSCANDEC);
313 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x0);
314 SSD1306_sendCmd(SSD1306_COMSCANDEC);
317 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x1);
318 SSD1306_sendCmd(SSD1306_COMSCANINC);
321 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x0);
322 SSD1306_sendCmd(SSD1306_COMSCANINC);
330 m_orientation = value;
336 void SSD1306Controller::SSD1306_sendScreenBuffer(
Rect updateRect)
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) {
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);
379 void SSD1306Controller::allocScreenBuffer()
381 m_screenBuffer = (uint8_t*) malloc(m_viewPortWidth * m_viewPortHeight / 8);
382 memset(m_screenBuffer, 0, m_viewPortWidth * m_viewPortHeight / 8);
386 void 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);
429 ++m_updateTaskFuncSuspended;
430 while (m_updateTaskRunning)
437 m_updateTaskFuncSuspended = tmax(0, m_updateTaskFuncSuspended - 1);
438 if (m_updateTaskFuncSuspended == 0)
439 xTaskNotifyGive(m_updateTaskHandle);
443 void 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); }
454 void 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); }
467 void 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);
480 void SSD1306Controller::rawFillRow(
int y,
int x1,
int x2, RGB888 color)
482 rawFillRow(y, x1, x2, preparePixel(color));
487 void SSD1306Controller::rawInvertRow(
int y,
int x1,
int x2)
489 for (; x1 <= x2; ++x1)
490 SSD1306_INVERTPIXEL(x1, y);
494 void 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); }
503 void 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);
511 void 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); }
520 void 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); }
531 void 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); }
541 void 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);
550 void SSD1306Controller::invertRect(Rect
const & rect, Rect & updateRect)
552 genericInvertRect(rect, updateRect,
553 [&] (
int Y,
int X1,
int X2) { rawInvertRow(
Y,
X1,
X2); }
558 void SSD1306Controller::swapFGBG(Rect
const & rect, Rect & updateRect)
560 invertRect(rect, updateRect);
565 void 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); }
576 void 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);
584 void 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); }
593 void 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); }
604 void 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)); }
614 void 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)); }
624 void SSD1306Controller::swapBuffers()
637 #endif // #ifdef ARDUINO
Represents a 24 bit RGB color.
void invert(bool value)
Inverts display colors.
void begin()
Initializes SSD1306.
void setOrientation(SSD1306Orientation value)
Set display orientation and rotation.
int getMaxBufferLength()
Returns maximum size of read and write buffers.
void setResolution(char const *modeline, int viewPortWidth=-1, int viewPortHeight=-1, bool doubleBuffered=false)
Sets SSD1306 resolution and viewport size.
int getViewPortHeight()
Determines vertical size of the viewport.
bool begin(gpio_num_t SDAGPIO, gpio_num_t SCLGPIO)
Initializes I2C instance associating GPIOs to I2C signals.
void setScreenCol(int value)
Set initial left column of the viewport.
I2C class allows multiple tasks to communicate with I2C devices, serializing read/write jobs...
void resumeBackgroundPrimitiveExecution()
Resumes drawings after suspendBackgroundPrimitiveExecution().
This file contains some utility classes and functions.
SSD1306Orientation
This enum defines SSD1306 orientation.
void setScreenRow(int value)
Set initial top row of the viewport.
int getViewPortWidth()
Determines horizontal size of the viewport.
void suspendBackgroundPrimitiveExecution()
Suspends drawings.
bool write(int address, uint8_t *buffer, int size, int frequency=100000, int timeOutMS=50)
Sends a buffer to I2C bus.
This file contains fabgl::SSD1306Controller definition.
int screenRow()
Gets initial top row of the viewport.