28 #include "freertos/FreeRTOS.h" 29 #include "freertos/task.h" 37 #define SSD1306_I2C_TIMEOUT 100 // ms 38 #define SSD1306_I2C_FREQUENCY 400000 40 #define SSD1306_UPDATETASK_STACK 1024 41 #define SSD1306_UPDATETASK_PRIORITY 5 43 #define SSD1306_BACKGROUND_PRIMITIVE_TIMEOUT 10000 // uS 46 #define SSD1306_SETLOWCOLUMN 0x00 47 #define SSD1306_SETHIGHCOLUMN 0x10 48 #define SSD1306_MEMORYMODE 0x20 49 #define SSD1306_COLUMNADDR 0x21 50 #define SSD1306_PAGEADDR 0x22 51 #define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26 52 #define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27 53 #define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 54 #define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A 55 #define SSD1306_DEACTIVATE_SCROLL 0x2E 56 #define SSD1306_ACTIVATE_SCROLL 0x2F 57 #define SSD1306_SETSTARTLINE 0x40 58 #define SSD1306_SETCONTRAST 0x81 59 #define SSD1306_CHARGEPUMP 0x8D 60 #define SSD1306_SEGREMAP 0xA0 61 #define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3 62 #define SSD1306_DISPLAYALLON_RESUME 0xA4 63 #define SSD1306_DISPLAYALLON 0xA5 64 #define SSD1306_NORMALDISPLAY 0xA6 65 #define SSD1306_INVERTDISPLAY 0xA7 66 #define SSD1306_SETMULTIPLEX 0xA8 67 #define SSD1306_DISPLAYOFF 0xAE 68 #define SSD1306_DISPLAYON 0xAF 69 #define SSD1306_COMSCANINC 0xC0 70 #define SSD1306_COMSCANDEC 0xC8 71 #define SSD1306_SETDISPLAYOFFSET 0xD3 72 #define SSD1306_SETDISPLAYCLOCKDIV 0xD5 73 #define SSD1306_SETPRECHARGE 0xD9 74 #define SSD1306_SETCOMPINS 0xDA 75 #define SSD1306_SETVCOMDETECT 0xDB 78 #define SSD1306_SETPIXEL(x, y) (m_screenBuffer[(x) + ((y) >> 3) * m_viewPortWidth] |= (1 << ((y) & 7))) 79 #define SSD1306_CLEARPIXEL(x, y) (m_screenBuffer[(x) + ((y) >> 3) * m_viewPortWidth] &= ~(1 << ((y) & 7))) 80 #define SSD1306_INVERTPIXEL(x, y) (m_screenBuffer[(x) + ((y) >> 3) * m_viewPortWidth] ^= (1 << ((y) & 7))) 82 #define SSD1306_SETPIXELCOLOR(x, y, color) { if ((color)) SSD1306_SETPIXEL((x), (y)); else SSD1306_CLEARPIXEL((x), (y)); } 84 #define SSD1306_GETPIXEL(x, y) ((m_screenBuffer[(x) + ((y) >> 3) * m_viewPortWidth] >> ((y) & 7)) & 1) 90 inline uint8_t RGB888toMono(RGB888
const & rgb)
92 return rgb.R > 0 || rgb.G > 0 || rgb.B > 0 ? 1 : 0;
96 inline uint8_t RGBA2222toMono(uint8_t rgba2222)
98 return (rgba2222 & 0x3f) ? 1 : 0;
102 inline uint8_t RGBA8888toMono(
RGBA8888 const & rgba)
104 return rgba.R > 0 || rgba.G > 0 || rgba.B > 0 ? 1 : 0;
108 inline uint8_t preparePixel(RGB888
const & rgb)
110 return RGB888toMono(rgb);
115 SSD1306Controller::SSD1306Controller()
117 m_screenBuffer(nullptr),
118 m_updateTaskHandle(nullptr),
119 m_updateTaskRunning(false),
125 SSD1306Controller::~SSD1306Controller()
134 m_i2cAddress = address;
135 m_resetGPIO = resetGPIO;
142 i2c->
begin(GPIO_NUM_4, GPIO_NUM_15);
147 void SSD1306Controller::end()
149 if (m_updateTaskHandle)
150 vTaskDelete(m_updateTaskHandle);
151 m_updateTaskHandle =
nullptr;
153 free(m_screenBuffer);
154 m_screenBuffer =
nullptr;
161 int pos = 0, swidth, sheight;
162 int count = sscanf(modeline,
"\"%[^\"]\" %d %d %n", label, &swidth, &sheight, &pos);
163 if (count != 3 || pos == 0)
166 m_screenWidth = swidth;
167 m_screenHeight = sheight;
172 setScreenSize(m_screenWidth, m_screenHeight);
174 setDoubleBuffered(doubleBuffered);
176 m_viewPortWidth = viewPortWidth < 0 ? m_screenWidth : viewPortWidth;
177 m_viewPortHeight = viewPortHeight < 0 ? m_screenHeight : viewPortHeight;
183 if (!SSD1306_softReset())
189 xTaskCreate(&updateTaskFunc,
"", SSD1306_UPDATETASK_STACK,
this, SSD1306_UPDATETASK_PRIORITY, &m_updateTaskHandle);
192 m_updateTaskFuncSuspended = 0;
198 if (value != m_screenCol) {
199 m_screenCol = iclamp(value, 0, m_viewPortWidth - m_screenWidth);
207 if (value != m_screenRow) {
208 m_screenRow = iclamp(value, 0, m_viewPortHeight - m_screenHeight);
214 void SSD1306Controller::sendRefresh()
216 Primitive p(PrimitiveCmd::Refresh,
Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
221 bool SSD1306Controller::SSD1306_sendData(uint8_t * buf,
int count, uint8_t ctrl)
224 uint8_t sbuf[bufSize];
227 int bToSend = imin(bufSize - 1, count);
228 memcpy(&sbuf[1], buf, bToSend);
229 if (!m_i2c->
write(m_i2cAddress, sbuf, bToSend + 1, SSD1306_I2C_FREQUENCY, SSD1306_I2C_TIMEOUT))
239 bool SSD1306Controller::SSD1306_sendCmd(uint8_t c)
241 return SSD1306_sendData(&c, 1, 0x00);
245 bool SSD1306Controller::SSD1306_sendCmd(uint8_t c1, uint8_t c2)
247 uint8_t buf[2] = { c1, c2 };
248 return SSD1306_sendData(buf, 2, 0x00);
252 bool SSD1306Controller::SSD1306_sendCmd(uint8_t c1, uint8_t c2, uint8_t c3)
254 uint8_t buf[3] = { c1, c2, c3 };
255 return SSD1306_sendData(buf, 3, 0x00);
260 void SSD1306Controller::SSD1306_hardReset()
262 if (m_resetGPIO != GPIO_UNUSED) {
263 configureGPIO(m_resetGPIO, GPIO_MODE_OUTPUT);
264 gpio_set_level(m_resetGPIO, 1);
265 vTaskDelay(1 / portTICK_PERIOD_MS);
266 gpio_set_level(m_resetGPIO, 0);
267 vTaskDelay(10 / portTICK_PERIOD_MS);
268 gpio_set_level(m_resetGPIO, 1);
274 bool SSD1306Controller::SSD1306_softReset()
276 SSD1306_sendCmd(SSD1306_DISPLAYOFF);
277 SSD1306_sendCmd(SSD1306_SETDISPLAYCLOCKDIV, 0x80);
278 SSD1306_sendCmd(SSD1306_SETMULTIPLEX, m_screenHeight - 1);
279 SSD1306_sendCmd(SSD1306_SETDISPLAYOFFSET, 0);
280 SSD1306_sendCmd(SSD1306_SETSTARTLINE);
281 SSD1306_sendCmd(SSD1306_CHARGEPUMP, 0x14);
282 SSD1306_sendCmd(SSD1306_MEMORYMODE, 0b100);
284 if (m_screenHeight == 64) {
285 SSD1306_sendCmd(SSD1306_SETCOMPINS, 0x12);
286 SSD1306_sendCmd(SSD1306_SETCONTRAST, 0xCF);
287 }
else if (m_screenHeight == 32) {
288 SSD1306_sendCmd(SSD1306_SETCOMPINS, 0x02);
289 SSD1306_sendCmd(SSD1306_SETCONTRAST, 0x8F);
291 SSD1306_sendCmd(SSD1306_SETPRECHARGE, 0xF1);
292 SSD1306_sendCmd(SSD1306_SETVCOMDETECT, 0x40);
293 SSD1306_sendCmd(SSD1306_DISPLAYALLON_RESUME);
294 SSD1306_sendCmd(SSD1306_NORMALDISPLAY);
295 SSD1306_sendCmd(SSD1306_DEACTIVATE_SCROLL);
296 return SSD1306_sendCmd(SSD1306_DISPLAYON);
300 void SSD1306Controller::setupOrientation()
302 switch (m_orientation) {
304 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x1);
305 SSD1306_sendCmd(SSD1306_COMSCANDEC);
308 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x0);
309 SSD1306_sendCmd(SSD1306_COMSCANDEC);
312 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x1);
313 SSD1306_sendCmd(SSD1306_COMSCANINC);
316 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x0);
317 SSD1306_sendCmd(SSD1306_COMSCANINC);
325 m_orientation = value;
331 void SSD1306Controller::SSD1306_sendScreenBuffer(
Rect updateRect)
341 updateRect.
Y2 = (updateRect.
Y2 + 7) & ~7;
344 if (scrRect.intersects(updateRect)) {
347 Rect r = updateRect.intersection(scrRect);
350 const int screenX1 = r.
X1 - m_screenCol;
351 const int screenX2 = r.
X2 - m_screenCol;
354 for (
int y = r.
Y1; y <= r.
Y2; y += 8) {
357 int page = screenY >> 3;
358 if (!(SSD1306_sendCmd(SSD1306_PAGEADDR, page, page) && SSD1306_sendCmd(SSD1306_COLUMNADDR, screenX1, screenX2)))
360 SSD1306_sendData(m_screenBuffer + r.
X1 + (y >> 3) * m_viewPortWidth, r.width(), 0x40);
370 SSD1306_sendCmd(value ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY);
374 void SSD1306Controller::allocScreenBuffer()
376 m_screenBuffer = (uint8_t*) malloc(m_viewPortWidth * m_viewPortHeight / 8);
377 memset(m_screenBuffer, 0, m_viewPortWidth * m_viewPortHeight / 8);
381 void SSD1306Controller::updateTaskFunc(
void * pvParameters)
383 SSD1306Controller * ctrl = (SSD1306Controller*) pvParameters;
387 ctrl->waitForPrimitives();
390 if (ctrl->m_updateTaskFuncSuspended > 0)
391 ulTaskNotifyTake(
true, portMAX_DELAY);
393 ctrl->m_updateTaskRunning =
true;
395 Rect updateRect = Rect(SHRT_MAX, SHRT_MAX, SHRT_MIN, SHRT_MIN);
397 int64_t startTime = ctrl->backgroundPrimitiveTimeoutEnabled() ? esp_timer_get_time() : 0;
401 if (ctrl->getPrimitive(&prim) ==
false)
404 ctrl->execPrimitive(prim, updateRect,
false);
406 if (ctrl->m_updateTaskFuncSuspended > 0)
409 }
while (!ctrl->backgroundPrimitiveTimeoutEnabled() || (startTime + SSD1306_BACKGROUND_PRIMITIVE_TIMEOUT > esp_timer_get_time()));
411 ctrl->showSprites(updateRect);
413 ctrl->m_updateTaskRunning =
false;
415 if (!ctrl->isDoubleBuffered())
416 ctrl->SSD1306_sendScreenBuffer(updateRect);
424 ++m_updateTaskFuncSuspended;
425 while (m_updateTaskRunning)
432 m_updateTaskFuncSuspended = tmax(0, m_updateTaskFuncSuspended - 1);
433 if (m_updateTaskFuncSuspended == 0)
434 xTaskNotifyGive(m_updateTaskHandle);
438 void SSD1306Controller::setPixelAt(PixelDesc
const & pixelDesc,
Rect & updateRect)
440 genericSetPixelAt(pixelDesc, updateRect,
441 [&] (
RGB888 const & color) {
return preparePixel(color); },
442 [&] (
int X,
int Y, uint8_t pattern) { SSD1306_SETPIXELCOLOR(
X,
Y, pattern); }
449 void SSD1306Controller::absDrawLine(
int X1,
int Y1,
int X2,
int Y2, RGB888 color)
451 genericAbsDrawLine(
X1,
Y1,
X2,
Y2, color,
452 [&] (RGB888
const & color) {
return preparePixel(color); },
453 [&] (
int Y,
int X1,
int X2, uint8_t pattern) { rawFillRow(
Y,
X1,
X2, pattern); },
454 [&] (
int Y,
int X1,
int X2) { rawInvertRow(
Y,
X1,
X2); },
455 [&] (
int X,
int Y, uint8_t pattern) { SSD1306_SETPIXELCOLOR(
X,
Y, pattern); },
456 [&] (
int X,
int Y) { SSD1306_INVERTPIXEL(
X,
Y); }
462 void SSD1306Controller::rawFillRow(
int y,
int x1,
int x2, uint8_t pattern)
465 for (; x1 <= x2; ++x1)
466 SSD1306_SETPIXEL(x1, y);
468 for (; x1 <= x2; ++x1)
469 SSD1306_CLEARPIXEL(x1, y);
475 void SSD1306Controller::rawFillRow(
int y,
int x1,
int x2, RGB888 color)
477 rawFillRow(y, x1, x2, preparePixel(color));
482 void SSD1306Controller::rawInvertRow(
int y,
int x1,
int x2)
484 for (; x1 <= x2; ++x1)
485 SSD1306_INVERTPIXEL(x1, y);
489 void SSD1306Controller::drawEllipse(Size
const & size, Rect & updateRect)
491 genericDrawEllipse(size, updateRect,
492 [&] (RGB888
const & color) {
return preparePixel(color); },
493 [&] (
int X,
int Y, uint8_t pattern) { SSD1306_SETPIXELCOLOR(
X,
Y, pattern); }
498 void SSD1306Controller::clear(Rect & updateRect)
500 hideSprites(updateRect);
501 uint8_t pattern = preparePixel(getActualBrushColor());
502 memset(m_screenBuffer, (pattern ? 255 : 0), m_viewPortWidth * m_viewPortHeight / 8);
506 void SSD1306Controller::VScroll(
int scroll, Rect & updateRect)
508 genericVScroll(scroll, updateRect,
509 [&] (
int x1,
int x2,
int srcY,
int dstY) { rawCopyRow(x1, x2, srcY, dstY); },
510 [&] (
int y,
int x1,
int x2, RGB888 color) { rawFillRow(y, x1, x2, color); }
515 void SSD1306Controller::HScroll(
int scroll, Rect & updateRect)
517 genericHScroll(scroll, updateRect,
518 [&] (RGB888
const & color) {
return preparePixel(color); },
519 [&] (
int y) {
return y; },
520 [&] (
int y,
int x) {
return SSD1306_GETPIXEL(x, y); },
521 [&] (
int y,
int x,
int pattern) { SSD1306_SETPIXELCOLOR(x, y, pattern); }
526 void SSD1306Controller::drawGlyph(Glyph
const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect)
528 genericDrawGlyph(glyph, glyphOptions, penColor, brushColor, updateRect,
529 [&] (RGB888
const & color) {
return preparePixel(color); },
530 [&] (
int y) {
return y; },
531 [&] (
int y,
int x, uint8_t pattern) { SSD1306_SETPIXELCOLOR(x, y, pattern); }
536 void SSD1306Controller::rawCopyRow(
int x1,
int x2,
int srcY,
int dstY)
538 for (; x1 <= x2; ++x1) {
539 uint8_t c = SSD1306_GETPIXEL(x1, srcY);
540 SSD1306_SETPIXELCOLOR(x1, dstY, c);
545 void SSD1306Controller::invertRect(Rect
const & rect, Rect & updateRect)
547 genericInvertRect(rect, updateRect,
548 [&] (
int Y,
int X1,
int X2) { rawInvertRow(
Y,
X1,
X2); }
553 void SSD1306Controller::swapFGBG(Rect
const & rect, Rect & updateRect)
555 invertRect(rect, updateRect);
560 void SSD1306Controller::copyRect(Rect
const & source, Rect & updateRect)
562 genericCopyRect(source, updateRect,
563 [&] (
int y) {
return y; },
564 [&] (
int y,
int x) {
return SSD1306_GETPIXEL(x, y); },
565 [&] (
int y,
int x, uint8_t pattern) { SSD1306_SETPIXELCOLOR(x, y, pattern); }
571 void SSD1306Controller::readScreen(Rect
const & rect, RGB888 * destBuf)
573 for (
int y = rect.Y1; y <= rect.Y2; ++y)
574 for (
int x = rect.X1; x <= rect.X2; ++x, ++destBuf)
575 *destBuf = SSD1306_GETPIXEL(x, y) ? RGB888(255, 255, 255) : RGB888(0, 0, 0);
579 void SSD1306Controller::rawDrawBitmap_Native(
int destX,
int destY, Bitmap
const * bitmap,
int X1,
int Y1,
int XCount,
int YCount)
581 genericRawDrawBitmap_Native(destX, destY, (uint8_t*) bitmap->data, bitmap->width,
X1,
Y1, XCount, YCount,
582 [&] (
int y) { return y; },
583 [&] (
int y,
int x, uint8_t src) { SSD1306_SETPIXELCOLOR(x, y, src); }
588 void SSD1306Controller::rawDrawBitmap_Mask(
int destX,
int destY, Bitmap
const * bitmap,
void * saveBackground,
int X1,
int Y1,
int XCount,
int YCount)
590 uint8_t foregroundColor = RGB888toMono(bitmap->foregroundColor);
591 genericRawDrawBitmap_Mask(destX, destY, bitmap, (uint8_t*)saveBackground,
X1,
Y1, XCount, YCount,
592 [&] (
int y) {
return y; },
593 [&] (
int y,
int x) {
return SSD1306_GETPIXEL(x, y); },
594 [&] (
int y,
int x) { SSD1306_SETPIXELCOLOR(x, y, foregroundColor); }
599 void SSD1306Controller::rawDrawBitmap_RGBA2222(
int destX,
int destY, Bitmap
const * bitmap,
void * saveBackground,
int X1,
int Y1,
int XCount,
int YCount)
601 genericRawDrawBitmap_RGBA2222(destX, destY, bitmap, (uint8_t*)saveBackground,
X1,
Y1, XCount, YCount,
602 [&] (
int y) {
return y; },
603 [&] (
int y,
int x) {
return SSD1306_GETPIXEL(x, y); },
604 [&] (
int y,
int x, uint8_t src) { SSD1306_SETPIXELCOLOR(x, y, RGBA2222toMono(src)); }
609 void SSD1306Controller::rawDrawBitmap_RGBA8888(
int destX,
int destY, Bitmap
const * bitmap,
void * saveBackground,
int X1,
int Y1,
int XCount,
int YCount)
611 genericRawDrawBitmap_RGBA8888(destX, destY, bitmap, (uint8_t*)saveBackground,
X1,
Y1, XCount, YCount,
612 [&] (
int y) {
return y; },
613 [&] (
int y,
int x) {
return SSD1306_GETPIXEL(x, y); },
614 [&] (
int y,
int x,
RGBA8888 const & src) { SSD1306_SETPIXELCOLOR(x, y, RGBA8888toMono(src)); }
619 void SSD1306Controller::swapBuffers()
632 #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.