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 m_i2c = i2c;
139 m_i2cAddress = address;
140 m_resetGPIO = resetGPIO;
141}
142
143
144void SSD1306Controller::begin()
145{
146 auto i2c = new I2C;
147 i2c->begin(GPIO_NUM_4, GPIO_NUM_15);
148 begin(i2c);
149}
150
151
152void SSD1306Controller::end()
153{
154 if (m_updateTaskHandle)
155 vTaskDelete(m_updateTaskHandle);
156 m_updateTaskHandle = nullptr;
157
158 free(m_screenBuffer);
159 m_screenBuffer = nullptr;
160}
161
162
163void SSD1306Controller::setResolution(char const * modeline, int viewPortWidth, int viewPortHeight, bool doubleBuffered)
164{
165 char label[32];
166 int pos = 0, swidth, sheight;
167 int count = sscanf(modeline, "\"%[^\"]\" %d %d %n", label, &swidth, &sheight, &pos);
168 if (count != 3 || pos == 0)
169 return; // invalid modeline
170
171 m_screenWidth = swidth;
172 m_screenHeight = sheight;
173 m_screenCol = 0;
174 m_screenRow = 0;
175
176 // inform base class about screen size
177 setScreenSize(m_screenWidth, m_screenHeight);
178
179 setDoubleBuffered(doubleBuffered);
180
181 m_viewPortWidth = viewPortWidth < 0 ? m_screenWidth : viewPortWidth;
182 m_viewPortHeight = viewPortHeight < 0 ? m_screenHeight : viewPortHeight;
183
184 resetPaintState();
185
186 SSD1306_hardReset();
187
188 if (!SSD1306_softReset())
189 return;
190
191 allocScreenBuffer();
192
193 // setup update task
194 xTaskCreate(&updateTaskFunc, "", SSD1306_UPDATETASK_STACK, this, SSD1306_UPDATETASK_PRIORITY, &m_updateTaskHandle);
195
196 // allows updateTaskFunc() to run
197 m_updateTaskFuncSuspended = 0;
198}
199
200
201void SSD1306Controller::setScreenCol(int value)
202{
203 if (value != m_screenCol) {
204 m_screenCol = iclamp(value, 0, m_viewPortWidth - m_screenWidth);
205 sendRefresh();
206 }
207}
208
209
210void SSD1306Controller::setScreenRow(int value)
211{
212 if (value != m_screenRow) {
213 m_screenRow = iclamp(value, 0, m_viewPortHeight - m_screenHeight);
214 sendRefresh();
215 }
216}
217
218
219void SSD1306Controller::sendRefresh()
220{
221 Primitive p(PrimitiveCmd::Refresh, Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
222 addPrimitive(p);
223}
224
225
226bool SSD1306Controller::SSD1306_sendData(uint8_t * buf, int count, uint8_t ctrl)
227{
228 int bufSize = m_i2c->getMaxBufferLength();
229 uint8_t sbuf[bufSize];
230 sbuf[0] = ctrl;
231 while (count > 0) {
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))
235 return false;
236 count -= bToSend;
237 buf += bToSend;
238 ets_delay_us(2); // delay 2uS (standing to SSD1306 doc should be 1.3uS, before new transmission can start)
239 }
240 return true;
241}
242
243
244bool SSD1306Controller::SSD1306_sendCmd(uint8_t c)
245{
246 return SSD1306_sendData(&c, 1, 0x00);
247}
248
249
250bool SSD1306Controller::SSD1306_sendCmd(uint8_t c1, uint8_t c2)
251{
252 uint8_t buf[2] = { c1, c2 };
253 return SSD1306_sendData(buf, 2, 0x00);
254}
255
256
257bool SSD1306Controller::SSD1306_sendCmd(uint8_t c1, uint8_t c2, uint8_t c3)
258{
259 uint8_t buf[3] = { c1, c2, c3 };
260 return SSD1306_sendData(buf, 3, 0x00);
261}
262
263
264// hard reset SSD1306
265void SSD1306Controller::SSD1306_hardReset()
266{
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);
274 }
275}
276
277
278// soft reset SSD1306
279bool SSD1306Controller::SSD1306_softReset()
280{
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); // 0x14 = SWITCHCAPVCC, 0x10 = EXTERNALVCC
287 SSD1306_sendCmd(SSD1306_MEMORYMODE, 0b100); // 0b100 = page addressing mode
288 setupOrientation();
289 if (m_screenHeight == 64) {
290 SSD1306_sendCmd(SSD1306_SETCOMPINS, 0x12);
291 SSD1306_sendCmd(SSD1306_SETCONTRAST, 0xCF); // max: 0xCF = SWITCHCAPVCC, 0x9F = EXTERNALVCC
292 } else if (m_screenHeight == 32) {
293 SSD1306_sendCmd(SSD1306_SETCOMPINS, 0x02);
294 SSD1306_sendCmd(SSD1306_SETCONTRAST, 0x8F);
295 }
296 SSD1306_sendCmd(SSD1306_SETPRECHARGE, 0xF1); // 0xF1 = SWITCHCAPVCC, 0x22 = EXTERNALVCC
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);
302}
303
304
305void SSD1306Controller::setupOrientation()
306{
307 switch (m_orientation) {
308 case SSD1306Orientation::Normal:
309 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x1);
310 SSD1306_sendCmd(SSD1306_COMSCANDEC);
311 break;
312 case SSD1306Orientation::ReverseHorizontal:
313 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x0);
314 SSD1306_sendCmd(SSD1306_COMSCANDEC);
315 break;
316 case SSD1306Orientation::ReverseVertical:
317 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x1);
318 SSD1306_sendCmd(SSD1306_COMSCANINC);
319 break;
320 case SSD1306Orientation::Rotate180:
321 SSD1306_sendCmd(SSD1306_SEGREMAP | 0x0);
322 SSD1306_sendCmd(SSD1306_COMSCANINC);
323 break;
324 }
325}
326
327
328void SSD1306Controller::setOrientation(SSD1306Orientation value)
329{
330 m_orientation = value;
331 setupOrientation();
332 sendRefresh();
333}
334
335
336void SSD1306Controller::SSD1306_sendScreenBuffer(Rect updateRect)
337{
338 // align visible screen row to page (steps of 8 rows)
339 const int screenRow = m_screenRow & ~7;
340
341 // visible area
342 const Rect scrRect = Rect(m_screenCol, screenRow, m_screenCol + m_screenWidth - 1, screenRow + m_screenHeight - 1);
343
344 // align rectangle to update to pages (0, 8, 16...)
345 updateRect.Y1 &= ~7;
346 updateRect.Y2 = (updateRect.Y2 + 7) & ~7;
347
348 // does the visible area intersect with area to update?
349 if (scrRect.intersects(updateRect)) {
350
351 // intersection between visible area and rectangle to update
352 Rect r = updateRect.intersection(scrRect);
353
354 // horizontal screen update limits
355 const int screenX1 = r.X1 - m_screenCol;
356 const int screenX2 = r.X2 - m_screenCol;
357
358 // send one page (8 rows) at the time
359 for (int y = r.Y1; y <= r.Y2; y += 8) {
360 int screenY = y - screenRow;
361 if (screenY >= 0) {
362 int page = screenY >> 3;
363 if (!(SSD1306_sendCmd(SSD1306_PAGEADDR, page, page) && SSD1306_sendCmd(SSD1306_COLUMNADDR, screenX1, screenX2)))
364 break; // address selection failed, try with next page
365 SSD1306_sendData(m_screenBuffer + r.X1 + (y >> 3) * m_viewPortWidth, r.width(), 0x40);
366 }
367 }
368
369 }
370}
371
372
374{
375 SSD1306_sendCmd(value ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY);
376}
377
378
379void SSD1306Controller::allocScreenBuffer()
380{
381 m_screenBuffer = (uint8_t*) malloc(m_viewPortWidth * m_viewPortHeight / 8);
382 memset(m_screenBuffer, 0, m_viewPortWidth * m_viewPortHeight / 8);
383}
384
385
386void SSD1306Controller::updateTaskFunc(void * pvParameters)
387{
388 SSD1306Controller * ctrl = (SSD1306Controller*) pvParameters;
389
390 while (true) {
391
392 ctrl->waitForPrimitives();
393
394 // primitive processing blocked?
395 if (ctrl->m_updateTaskFuncSuspended > 0)
396 ulTaskNotifyTake(true, portMAX_DELAY); // yes, wait for a notify
397
398 ctrl->m_updateTaskRunning = true;
399
400 Rect updateRect = Rect(SHRT_MAX, SHRT_MAX, SHRT_MIN, SHRT_MIN);
401
402 int64_t startTime = ctrl->backgroundPrimitiveTimeoutEnabled() ? esp_timer_get_time() : 0;
403 do {
404
405 Primitive prim;
406 if (ctrl->getPrimitive(&prim) == false)
407 break;
408
409 ctrl->execPrimitive(prim, updateRect, false);
410
411 if (ctrl->m_updateTaskFuncSuspended > 0)
412 break;
413
414 } while (!ctrl->backgroundPrimitiveTimeoutEnabled() || (startTime + SSD1306_BACKGROUND_PRIMITIVE_TIMEOUT > esp_timer_get_time()));
415
416 ctrl->showSprites(updateRect);
417
418 ctrl->m_updateTaskRunning = false;
419
420 if (!ctrl->isDoubleBuffered())
421 ctrl->SSD1306_sendScreenBuffer(updateRect);
422 }
423}
424
425
426
427void SSD1306Controller::suspendBackgroundPrimitiveExecution()
428{
429 ++m_updateTaskFuncSuspended;
430 while (m_updateTaskRunning)
431 taskYIELD();
432}
433
434
435void SSD1306Controller::resumeBackgroundPrimitiveExecution()
436{
437 m_updateTaskFuncSuspended = tmax(0, m_updateTaskFuncSuspended - 1);
438 if (m_updateTaskFuncSuspended == 0)
439 xTaskNotifyGive(m_updateTaskHandle); // resume updateTaskFunc()
440}
441
442
443void SSD1306Controller::setPixelAt(PixelDesc const & pixelDesc, Rect & updateRect)
444{
445 genericSetPixelAt(pixelDesc, updateRect,
446 [&] (RGB888 const & color) { return preparePixel(color); },
447 [&] (int X, int Y, uint8_t pattern) { SSD1306_SETPIXELCOLOR(X, Y, pattern); }
448 );
449}
450
451
452// coordinates are absolute values (not relative to origin)
453// line clipped on current absolute clipping rectangle
454void SSD1306Controller::absDrawLine(int X1, int Y1, int X2, int Y2, RGB888 color)
455{
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); }
462 );
463}
464
465
466// parameters not checked
467void SSD1306Controller::rawFillRow(int y, int x1, int x2, uint8_t pattern)
468{
469 if (pattern) {
470 for (; x1 <= x2; ++x1)
471 SSD1306_SETPIXEL(x1, y);
472 } else {
473 for (; x1 <= x2; ++x1)
474 SSD1306_CLEARPIXEL(x1, y);
475 }
476}
477
478
479// parameters not checked
480void SSD1306Controller::rawFillRow(int y, int x1, int x2, RGB888 color)
481{
482 rawFillRow(y, x1, x2, preparePixel(color));
483}
484
485
486// parameters not checked
487void SSD1306Controller::rawInvertRow(int y, int x1, int x2)
488{
489 for (; x1 <= x2; ++x1)
490 SSD1306_INVERTPIXEL(x1, y);
491}
492
493
494void SSD1306Controller::drawEllipse(Size const & size, Rect & updateRect)
495{
496 genericDrawEllipse(size, updateRect,
497 [&] (RGB888 const & color) { return preparePixel(color); },
498 [&] (int X, int Y, uint8_t pattern) { SSD1306_SETPIXELCOLOR(X, Y, pattern); }
499 );
500}
501
502
503void SSD1306Controller::clear(Rect & updateRect)
504{
505 hideSprites(updateRect);
506 uint8_t pattern = preparePixel(getActualBrushColor());
507 memset(m_screenBuffer, (pattern ? 255 : 0), m_viewPortWidth * m_viewPortHeight / 8);
508}
509
510
511void SSD1306Controller::VScroll(int scroll, Rect & updateRect)
512{
513 genericVScroll(scroll, updateRect,
514 [&] (int x1, int x2, int srcY, int dstY) { rawCopyRow(x1, x2, srcY, dstY); }, // rawCopyRow
515 [&] (int y, int x1, int x2, RGB888 color) { rawFillRow(y, x1, x2, color); } // rawFillRow
516 );
517}
518
519
520void SSD1306Controller::HScroll(int scroll, Rect & updateRect)
521{
522 genericHScroll(scroll, updateRect,
523 [&] (RGB888 const & color) { return preparePixel(color); }, // preparePixel
524 [&] (int y) { return y; }, // rawGetRow
525 [&] (int y, int x) { return SSD1306_GETPIXEL(x, y); }, // rawGetPixelInRow
526 [&] (int y, int x, int pattern) { SSD1306_SETPIXELCOLOR(x, y, pattern); } // rawSetPixelInRow
527 );
528}
529
530
531void SSD1306Controller::drawGlyph(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect)
532{
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); }
537 );
538}
539
540
541void SSD1306Controller::rawCopyRow(int x1, int x2, int srcY, int dstY)
542{
543 for (; x1 <= x2; ++x1) {
544 uint8_t c = SSD1306_GETPIXEL(x1, srcY);
545 SSD1306_SETPIXELCOLOR(x1, dstY, c);
546 }
547}
548
549
550void SSD1306Controller::invertRect(Rect const & rect, Rect & updateRect)
551{
552 genericInvertRect(rect, updateRect,
553 [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); }
554 );
555}
556
557
558void SSD1306Controller::swapFGBG(Rect const & rect, Rect & updateRect)
559{
560 invertRect(rect, updateRect);
561}
562
563
564// supports overlapping of source and dest rectangles
565void SSD1306Controller::copyRect(Rect const & source, Rect & updateRect)
566{
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); }
571 );
572}
573
574
575// no bounds check is done!
576void SSD1306Controller::readScreen(Rect const & rect, RGB888 * destBuf)
577{
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);
581}
582
583
584void SSD1306Controller::rawDrawBitmap_Native(int destX, int destY, Bitmap const * bitmap, int X1, int Y1, int XCount, int YCount)
585{
586 genericRawDrawBitmap_Native(destX, destY, (uint8_t*) bitmap->data, bitmap->width, X1, Y1, XCount, YCount,
587 [&] (int y) { return y; }, // rawGetRow
588 [&] (int y, int x, uint8_t src) { SSD1306_SETPIXELCOLOR(x, y, src); } // rawSetPixelInRow
589 );
590}
591
592
593void SSD1306Controller::rawDrawBitmap_Mask(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
594{
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; }, // rawGetRow
598 [&] (int y, int x) { return SSD1306_GETPIXEL(x, y); }, // rawGetPixelInRow
599 [&] (int y, int x) { SSD1306_SETPIXELCOLOR(x, y, foregroundColor); } // rawSetPixelInRow
600 );
601}
602
603
604void SSD1306Controller::rawDrawBitmap_RGBA2222(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
605{
606 genericRawDrawBitmap_RGBA2222(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
607 [&] (int y) { return y; }, // rawGetRow
608 [&] (int y, int x) { return SSD1306_GETPIXEL(x, y); }, // rawGetPixelInRow
609 [&] (int y, int x, uint8_t src) { SSD1306_SETPIXELCOLOR(x, y, RGBA2222toMono(src)); } // rawSetPixelInRow
610 );
611}
612
613
614void SSD1306Controller::rawDrawBitmap_RGBA8888(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
615{
616 genericRawDrawBitmap_RGBA8888(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
617 [&] (int y) { return y; }, // rawGetRow
618 [&] (int y, int x) { return SSD1306_GETPIXEL(x, y); }, // rawGetPixelInRow
619 [&] (int y, int x, RGBA8888 const & src) { SSD1306_SETPIXELCOLOR(x, y, RGBA8888toMono(src)); } // rawSetPixelInRow
620 );
621}
622
623
624void SSD1306Controller::swapBuffers()
625{
626 // nothing to do, we just send current view port to the device
627 SSD1306_sendScreenBuffer(Rect(0, 0, getViewPortWidth() - 1, getViewPortHeight() - 1));
628}
629
630
631
632
633} // end of namespace
634
635
636
637#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:245
int16_t Y2
Definition: fabutils.h:248
int16_t X2
Definition: fabutils.h:247
int16_t Y1
Definition: fabutils.h:246
Represents a rectangle.
Definition: fabutils.h:244