FabGL
ESP32 Display Controller and Graphics Library
SSD1306Controller.cpp
1/*
2 Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - <http://www.fabgl.com>
3 Copyright (c) 2019-2022 Fabrizio Di Vittorio.
4 All rights reserved.
5
6
7* Please contact fdivitto2013@gmail.com if you need a commercial license.
8
9
10* This library and related software is available under GPL v3.
11
12 FabGL is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 FabGL is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with FabGL. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26
27#ifdef ARDUINO
28
29
30#include <string.h>
31
32#include "freertos/FreeRTOS.h"
33#include "freertos/task.h"
34
35#include "fabutils.h"
36#include "SSD1306Controller.h"
37
38
39#pragma GCC optimize ("O2")
40
41
42#define SSD1306_I2C_TIMEOUT 100 // ms
43#define SSD1306_I2C_FREQUENCY 400000
44
45#define SSD1306_UPDATETASK_STACK 1024
46#define SSD1306_UPDATETASK_PRIORITY 5
47
48#define SSD1306_BACKGROUND_PRIMITIVE_TIMEOUT 10000 // uS
49
50
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
81
82
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)))
86
87#define SSD1306_SETPIXELCOLOR(x, y, color) { if ((color)) SSD1306_SETPIXEL((x), (y)); else SSD1306_CLEARPIXEL((x), (y)); }
88
89#define SSD1306_GETPIXEL(x, y) ((m_screenBuffer[(x) + ((y) >> 3) * m_viewPortWidth] >> ((y) & 7)) & 1)
90
91
92namespace fabgl {
93
94
95inline uint8_t RGB888toMono(RGB888 const & rgb)
96{
97 return rgb.R > 0 || rgb.G > 0 || rgb.B > 0 ? 1 : 0;
98}
99
100
101inline uint8_t RGBA2222toMono(uint8_t rgba2222)
102{
103 return (rgba2222 & 0x3f) ? 1 : 0;
104}
105
106
107inline uint8_t RGBA8888toMono(RGBA8888 const & rgba)
108{
109 return rgba.R > 0 || rgba.G > 0 || rgba.B > 0 ? 1 : 0;
110}
111
112
113inline uint8_t preparePixel(RGB888 const & rgb)
114{
115 return RGB888toMono(rgb);
116}
117
118
119
120SSD1306Controller::SSD1306Controller()
121 : m_i2c(nullptr),
122 m_screenBuffer(nullptr),
123 m_updateTaskHandle(nullptr),
124 m_updateTaskRunning(false),
125 m_orientation(SSD1306Orientation::Normal)
126{
127}
128
129
130SSD1306Controller::~SSD1306Controller()
131{
132 end();
133}
134
135
136void SSD1306Controller::begin(I2C * i2c, int address, gpio_num_t resetGPIO)
137{
138 CurrentVideoMode::set(VideoMode::I2C);
139 m_i2c = i2c;
140 m_i2cAddress = address;
141 m_resetGPIO = resetGPIO;
142}
143
144
145void SSD1306Controller::begin()
146{
147 auto i2c = new I2C;
148 i2c->begin(GPIO_NUM_4, GPIO_NUM_15);
149 begin(i2c);
150}
151
152
153void SSD1306Controller::end()
154{
155 if (m_updateTaskHandle)
156 vTaskDelete(m_updateTaskHandle);
157 m_updateTaskHandle = nullptr;
158
159 free(m_screenBuffer);
160 m_screenBuffer = nullptr;
161}
162
163
164void SSD1306Controller::setResolution(char const * modeline, int viewPortWidth, int viewPortHeight, bool doubleBuffered)
165{
166 char label[32];
167 int pos = 0, swidth, sheight;
168 int count = sscanf(modeline, "\"%[^\"]\" %d %d %n", label, &swidth, &sheight, &pos);
169 if (count != 3 || pos == 0)
170 return; // invalid modeline
171
172 m_screenWidth = swidth;
173 m_screenHeight = sheight;
174 m_screenCol = 0;
175 m_screenRow = 0;
176
177 // inform base class about screen size
178 setScreenSize(m_screenWidth, m_screenHeight);
179
180 setDoubleBuffered(doubleBuffered);
181
182 m_viewPortWidth = viewPortWidth < 0 ? m_screenWidth : viewPortWidth;
183 m_viewPortHeight = viewPortHeight < 0 ? m_screenHeight : viewPortHeight;
184
185 resetPaintState();
186
187 SSD1306_hardReset();
188
189 if (!SSD1306_softReset())
190 return;
191
192 allocScreenBuffer();
193
194 // setup update task
195 xTaskCreate(&updateTaskFunc, "", SSD1306_UPDATETASK_STACK, this, SSD1306_UPDATETASK_PRIORITY, &m_updateTaskHandle);
196
197 // allows updateTaskFunc() to run
198 m_updateTaskFuncSuspended = 0;
199}
200
201
202void SSD1306Controller::setScreenCol(int value)
203{
204 if (value != m_screenCol) {
205 m_screenCol = iclamp(value, 0, m_viewPortWidth - m_screenWidth);
206 sendRefresh();
207 }
208}
209
210
211void SSD1306Controller::setScreenRow(int value)
212{
213 if (value != m_screenRow) {
214 m_screenRow = iclamp(value, 0, m_viewPortHeight - m_screenHeight);
215 sendRefresh();
216 }
217}
218
219
220void SSD1306Controller::sendRefresh()
221{
222 Primitive p(PrimitiveCmd::Refresh, Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
223 addPrimitive(p);
224}
225
226
227bool SSD1306Controller::SSD1306_sendData(uint8_t * buf, int count, uint8_t ctrl)
228{
229 int bufSize = m_i2c->getMaxBufferLength();
230 uint8_t sbuf[bufSize];
231 sbuf[0] = ctrl;
232 while (count > 0) {
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))
236 return false;
237 count -= bToSend;
238 buf += bToSend;
239 ets_delay_us(2); // delay 2uS (standing to SSD1306 doc should be 1.3uS, before new transmission can start)
240 }
241 return true;
242}
243
244
245bool SSD1306Controller::SSD1306_sendCmd(uint8_t c)
246{
247 return SSD1306_sendData(&c, 1, 0x00);
248}
249
250
251bool SSD1306Controller::SSD1306_sendCmd(uint8_t c1, uint8_t c2)
252{
253 uint8_t buf[2] = { c1, c2 };
254 return SSD1306_sendData(buf, 2, 0x00);
255}
256
257
258bool SSD1306Controller::SSD1306_sendCmd(uint8_t c1, uint8_t c2, uint8_t c3)
259{
260 uint8_t buf[3] = { c1, c2, c3 };
261 return SSD1306_sendData(buf, 3, 0x00);
262}
263
264
265// hard reset SSD1306
266void SSD1306Controller::SSD1306_hardReset()
267{
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);
275 }
276}
277
278
279// soft reset SSD1306
280bool SSD1306Controller::SSD1306_softReset()
281{
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); // 0x14 = SWITCHCAPVCC, 0x10 = EXTERNALVCC
288 SSD1306_sendCmd(SSD1306_MEMORYMODE, 0b100); // 0b100 = page addressing mode
289 setupOrientation();
290 if (m_screenHeight == 64) {
291 SSD1306_sendCmd(SSD1306_SETCOMPINS, 0x12);
292 SSD1306_sendCmd(SSD1306_SETCONTRAST, 0xCF); // max: 0xCF = SWITCHCAPVCC, 0x9F = EXTERNALVCC
293 } else if (m_screenHeight == 32) {
294 SSD1306_sendCmd(SSD1306_SETCOMPINS, 0x02);
295 SSD1306_sendCmd(SSD1306_SETCONTRAST, 0x8F);
296 }
297 SSD1306_sendCmd(SSD1306_SETPRECHARGE, 0xF1); // 0xF1 = SWITCHCAPVCC, 0x22 = EXTERNALVCC
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);
303}
304
305
306void SSD1306Controller::setupOrientation()
307{
308 switch (m_orientation) {
309 case SSD1306Orientation::Normal:
310 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x1);
311 SSD1306_sendCmd(SSD1306_COMSCANDEC);
312 break;
313 case SSD1306Orientation::ReverseHorizontal:
314 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x0);
315 SSD1306_sendCmd(SSD1306_COMSCANDEC);
316 break;
317 case SSD1306Orientation::ReverseVertical:
318 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x1);
319 SSD1306_sendCmd(SSD1306_COMSCANINC);
320 break;
321 case SSD1306Orientation::Rotate180:
322 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x0);
323 SSD1306_sendCmd(SSD1306_COMSCANINC);
324 break;
325 }
326}
327
328
329void SSD1306Controller::setOrientation(SSD1306Orientation value)
330{
331 m_orientation = value;
332 setupOrientation();
333 sendRefresh();
334}
335
336
337void SSD1306Controller::SSD1306_sendScreenBuffer(Rect updateRect)
338{
339 // align visible screen row to page (steps of 8 rows)
340 const int screenRow = m_screenRow & ~7;
341
342 // visible area
343 const Rect scrRect = Rect(m_screenCol, screenRow, m_screenCol + m_screenWidth - 1, screenRow + m_screenHeight - 1);
344
345 // align rectangle to update to pages (0, 8, 16...)
346 updateRect.Y1 &= ~7;
347 updateRect.Y2 = (updateRect.Y2 + 7) & ~7;
348
349 // does the visible area intersect with area to update?
350 if (scrRect.intersects(updateRect)) {
351
352 // intersection between visible area and rectangle to update
353 Rect r = updateRect.intersection(scrRect);
354
355 // horizontal screen update limits
356 const int screenX1 = r.X1 - m_screenCol;
357 const int screenX2 = r.X2 - m_screenCol;
358
359 // send one page (8 rows) at the time
360 for (int y = r.Y1; y <= r.Y2; y += 8) {
361 int screenY = y - screenRow;
362 if (screenY >= 0) {
363 int page = screenY >> 3;
364 if (!(SSD1306_sendCmd(SSD1306_PAGEADDR, page, page) && SSD1306_sendCmd(SSD1306_COLUMNADDR, screenX1, screenX2)))
365 break; // address selection failed, try with next page
366 SSD1306_sendData(m_screenBuffer + r.X1 + (y >> 3) * m_viewPortWidth, r.width(), 0x40);
367 }
368 }
369
370 }
371}
372
373
375{
376 SSD1306_sendCmd(value ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY);
377}
378
379
380void SSD1306Controller::allocScreenBuffer()
381{
382 m_screenBuffer = (uint8_t*) malloc(m_viewPortWidth * m_viewPortHeight / 8);
383 memset(m_screenBuffer, 0, m_viewPortWidth * m_viewPortHeight / 8);
384}
385
386
387void SSD1306Controller::updateTaskFunc(void * pvParameters)
388{
389 SSD1306Controller * ctrl = (SSD1306Controller*) pvParameters;
390
391 while (true) {
392
393 ctrl->waitForPrimitives();
394
395 // primitive processing blocked?
396 if (ctrl->m_updateTaskFuncSuspended > 0)
397 ulTaskNotifyTake(true, portMAX_DELAY); // yes, wait for a notify
398
399 ctrl->m_updateTaskRunning = true;
400
401 Rect updateRect = Rect(SHRT_MAX, SHRT_MAX, SHRT_MIN, SHRT_MIN);
402
403 int64_t startTime = ctrl->backgroundPrimitiveTimeoutEnabled() ? esp_timer_get_time() : 0;
404 do {
405
406 Primitive prim;
407 if (ctrl->getPrimitive(&prim) == false)
408 break;
409
410 ctrl->execPrimitive(prim, updateRect, false);
411
412 if (ctrl->m_updateTaskFuncSuspended > 0)
413 break;
414
415 } while (!ctrl->backgroundPrimitiveTimeoutEnabled() || (startTime + SSD1306_BACKGROUND_PRIMITIVE_TIMEOUT > esp_timer_get_time()));
416
417 ctrl->showSprites(updateRect);
418
419 ctrl->m_updateTaskRunning = false;
420
421 if (!ctrl->isDoubleBuffered())
422 ctrl->SSD1306_sendScreenBuffer(updateRect);
423 }
424}
425
426
427
428void SSD1306Controller::suspendBackgroundPrimitiveExecution()
429{
430 ++m_updateTaskFuncSuspended;
431 while (m_updateTaskRunning)
432 taskYIELD();
433}
434
435
436void SSD1306Controller::resumeBackgroundPrimitiveExecution()
437{
438 m_updateTaskFuncSuspended = tmax(0, m_updateTaskFuncSuspended - 1);
439 if (m_updateTaskFuncSuspended == 0)
440 xTaskNotifyGive(m_updateTaskHandle); // resume updateTaskFunc()
441}
442
443
444void SSD1306Controller::setPixelAt(PixelDesc const & pixelDesc, Rect & updateRect)
445{
446 genericSetPixelAt(pixelDesc, updateRect,
447 [&] (RGB888 const & color) { return preparePixel(color); },
448 [&] (int X, int Y, uint8_t pattern) { SSD1306_SETPIXELCOLOR(X, Y, pattern); }
449 );
450}
451
452
453// coordinates are absolute values (not relative to origin)
454// line clipped on current absolute clipping rectangle
455void SSD1306Controller::absDrawLine(int X1, int Y1, int X2, int Y2, RGB888 color)
456{
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); }
463 );
464}
465
466
467// parameters not checked
468void SSD1306Controller::rawFillRow(int y, int x1, int x2, uint8_t pattern)
469{
470 if (pattern) {
471 for (; x1 <= x2; ++x1)
472 SSD1306_SETPIXEL(x1, y);
473 } else {
474 for (; x1 <= x2; ++x1)
475 SSD1306_CLEARPIXEL(x1, y);
476 }
477}
478
479
480// parameters not checked
481void SSD1306Controller::rawFillRow(int y, int x1, int x2, RGB888 color)
482{
483 rawFillRow(y, x1, x2, preparePixel(color));
484}
485
486
487// parameters not checked
488void SSD1306Controller::rawInvertRow(int y, int x1, int x2)
489{
490 for (; x1 <= x2; ++x1)
491 SSD1306_INVERTPIXEL(x1, y);
492}
493
494
495void SSD1306Controller::drawEllipse(Size const & size, Rect & updateRect)
496{
497 genericDrawEllipse(size, updateRect,
498 [&] (RGB888 const & color) { return preparePixel(color); },
499 [&] (int X, int Y, uint8_t pattern) { SSD1306_SETPIXELCOLOR(X, Y, pattern); }
500 );
501}
502
503
504void SSD1306Controller::clear(Rect & updateRect)
505{
506 hideSprites(updateRect);
507 uint8_t pattern = preparePixel(getActualBrushColor());
508 memset(m_screenBuffer, (pattern ? 255 : 0), m_viewPortWidth * m_viewPortHeight / 8);
509}
510
511
512void SSD1306Controller::VScroll(int scroll, Rect & updateRect)
513{
514 genericVScroll(scroll, updateRect,
515 [&] (int x1, int x2, int srcY, int dstY) { rawCopyRow(x1, x2, srcY, dstY); }, // rawCopyRow
516 [&] (int y, int x1, int x2, RGB888 color) { rawFillRow(y, x1, x2, color); } // rawFillRow
517 );
518}
519
520
521void SSD1306Controller::HScroll(int scroll, Rect & updateRect)
522{
523 genericHScroll(scroll, updateRect,
524 [&] (RGB888 const & color) { return preparePixel(color); }, // preparePixel
525 [&] (int y) { return y; }, // rawGetRow
526 [&] (int y, int x) { return SSD1306_GETPIXEL(x, y); }, // rawGetPixelInRow
527 [&] (int y, int x, int pattern) { SSD1306_SETPIXELCOLOR(x, y, pattern); } // rawSetPixelInRow
528 );
529}
530
531
532void SSD1306Controller::drawGlyph(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect)
533{
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); }
538 );
539}
540
541
542void SSD1306Controller::rawCopyRow(int x1, int x2, int srcY, int dstY)
543{
544 for (; x1 <= x2; ++x1) {
545 uint8_t c = SSD1306_GETPIXEL(x1, srcY);
546 SSD1306_SETPIXELCOLOR(x1, dstY, c);
547 }
548}
549
550
551void SSD1306Controller::invertRect(Rect const & rect, Rect & updateRect)
552{
553 genericInvertRect(rect, updateRect,
554 [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); }
555 );
556}
557
558
559void SSD1306Controller::swapFGBG(Rect const & rect, Rect & updateRect)
560{
561 invertRect(rect, updateRect);
562}
563
564
565// supports overlapping of source and dest rectangles
566void SSD1306Controller::copyRect(Rect const & source, Rect & updateRect)
567{
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); }
572 );
573}
574
575
576// no bounds check is done!
577void SSD1306Controller::readScreen(Rect const & rect, RGB888 * destBuf)
578{
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);
582}
583
584
585void SSD1306Controller::rawDrawBitmap_Native(int destX, int destY, Bitmap const * bitmap, int X1, int Y1, int XCount, int YCount)
586{
587 genericRawDrawBitmap_Native(destX, destY, (uint8_t*) bitmap->data, bitmap->width, X1, Y1, XCount, YCount,
588 [&] (int y) { return y; }, // rawGetRow
589 [&] (int y, int x, uint8_t src) { SSD1306_SETPIXELCOLOR(x, y, src); } // rawSetPixelInRow
590 );
591}
592
593
594void SSD1306Controller::rawDrawBitmap_Mask(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
595{
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; }, // rawGetRow
599 [&] (int y, int x) { return SSD1306_GETPIXEL(x, y); }, // rawGetPixelInRow
600 [&] (int y, int x) { SSD1306_SETPIXELCOLOR(x, y, foregroundColor); } // rawSetPixelInRow
601 );
602}
603
604
605void SSD1306Controller::rawDrawBitmap_RGBA2222(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
606{
607 genericRawDrawBitmap_RGBA2222(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
608 [&] (int y) { return y; }, // rawGetRow
609 [&] (int y, int x) { return SSD1306_GETPIXEL(x, y); }, // rawGetPixelInRow
610 [&] (int y, int x, uint8_t src) { SSD1306_SETPIXELCOLOR(x, y, RGBA2222toMono(src)); } // rawSetPixelInRow
611 );
612}
613
614
615void SSD1306Controller::rawDrawBitmap_RGBA8888(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
616{
617 genericRawDrawBitmap_RGBA8888(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
618 [&] (int y) { return y; }, // rawGetRow
619 [&] (int y, int x) { return SSD1306_GETPIXEL(x, y); }, // rawGetPixelInRow
620 [&] (int y, int x, RGBA8888 const & src) { SSD1306_SETPIXELCOLOR(x, y, RGBA8888toMono(src)); } // rawSetPixelInRow
621 );
622}
623
624
625void SSD1306Controller::swapBuffers()
626{
627 // nothing to do, we just send current view port to the device
628 SSD1306_sendScreenBuffer(Rect(0, 0, getViewPortWidth() - 1, getViewPortHeight() - 1));
629}
630
631
632
633
634} // end of namespace
635
636
637
638#endif // #ifdef ARDUINO
This file contains fabgl::SSD1306Controller definition.
bool begin(gpio_num_t SDAGPIO, gpio_num_t SCLGPIO)
Initializes I2C instance associating GPIOs to I2C signals.
Definition: tsi2c.cpp:72
I2C class allows multiple tasks to communicate with I2C devices, serializing read/write jobs.
Definition: tsi2c.h:85
int16_t X
uint8_t swapFGBG
int16_t Y
uint16_t invert
int16_t X1
Definition: fabutils.h:0
int16_t Y2
Definition: fabutils.h:3
int16_t X2
Definition: fabutils.h:2
int16_t Y1
Definition: fabutils.h:1
This file contains some utility classes and functions.
SSD1306Orientation
This enum defines SSD1306 orientation.
Represents a 24 bit RGB color.
int16_t X1
Definition: fabutils.h:249
int16_t Y2
Definition: fabutils.h:252
int16_t X2
Definition: fabutils.h:251
int16_t Y1
Definition: fabutils.h:250
Represents a rectangle.
Definition: fabutils.h:248