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-2021 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. Feel free to use FabGL in free software and hardware:
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 
92 namespace fabgl {
93 
94 
95 inline uint8_t RGB888toMono(RGB888 const & rgb)
96 {
97  return rgb.R > 0 || rgb.G > 0 || rgb.B > 0 ? 1 : 0;
98 }
99 
100 
101 inline uint8_t RGBA2222toMono(uint8_t rgba2222)
102 {
103  return (rgba2222 & 0x3f) ? 1 : 0;
104 }
105 
106 
107 inline uint8_t RGBA8888toMono(RGBA8888 const & rgba)
108 {
109  return rgba.R > 0 || rgba.G > 0 || rgba.B > 0 ? 1 : 0;
110 }
111 
112 
113 inline uint8_t preparePixel(RGB888 const & rgb)
114 {
115  return RGB888toMono(rgb);
116 }
117 
118 
119 
120 SSD1306Controller::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 
130 SSD1306Controller::~SSD1306Controller()
131 {
132  end();
133 }
134 
135 
136 void 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 
145 {
146  auto i2c = new I2C;
147  i2c->begin(GPIO_NUM_4, GPIO_NUM_15);
148  begin(i2c);
149 }
150 
151 
152 void 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 
163 void 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 
202 {
203  if (value != m_screenCol) {
204  m_screenCol = iclamp(value, 0, m_viewPortWidth - m_screenWidth);
205  sendRefresh();
206  }
207 }
208 
209 
211 {
212  if (value != m_screenRow) {
213  m_screenRow = iclamp(value, 0, m_viewPortHeight - m_screenHeight);
214  sendRefresh();
215  }
216 }
217 
218 
219 void SSD1306Controller::sendRefresh()
220 {
221  Primitive p(PrimitiveCmd::Refresh, Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
222  addPrimitive(p);
223 }
224 
225 
226 bool 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 
244 bool SSD1306Controller::SSD1306_sendCmd(uint8_t c)
245 {
246  return SSD1306_sendData(&c, 1, 0x00);
247 }
248 
249 
250 bool 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 
257 bool 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
265 void 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
279 bool 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 
305 void SSD1306Controller::setupOrientation()
306 {
307  switch (m_orientation) {
309  SSD1306_sendCmd(SSD1306_SEGREMAP | 0x1);
310  SSD1306_sendCmd(SSD1306_COMSCANDEC);
311  break;
313  SSD1306_sendCmd(SSD1306_SEGREMAP | 0x0);
314  SSD1306_sendCmd(SSD1306_COMSCANDEC);
315  break;
317  SSD1306_sendCmd(SSD1306_SEGREMAP | 0x1);
318  SSD1306_sendCmd(SSD1306_COMSCANINC);
319  break;
321  SSD1306_sendCmd(SSD1306_SEGREMAP | 0x0);
322  SSD1306_sendCmd(SSD1306_COMSCANINC);
323  break;
324  }
325 }
326 
327 
329 {
330  m_orientation = value;
331  setupOrientation();
332  sendRefresh();
333 }
334 
335 
336 void 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 
379 void 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 
386 void 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 
428 {
429  ++m_updateTaskFuncSuspended;
430  while (m_updateTaskRunning)
431  taskYIELD();
432 }
433 
434 
436 {
437  m_updateTaskFuncSuspended = tmax(0, m_updateTaskFuncSuspended - 1);
438  if (m_updateTaskFuncSuspended == 0)
439  xTaskNotifyGive(m_updateTaskHandle); // resume updateTaskFunc()
440 }
441 
442 
443 void 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
454 void 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
467 void 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
480 void 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
487 void SSD1306Controller::rawInvertRow(int y, int x1, int x2)
488 {
489  for (; x1 <= x2; ++x1)
490  SSD1306_INVERTPIXEL(x1, y);
491 }
492 
493 
494 void 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 
503 void 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 
511 void 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 
520 void 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 
531 void 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 
541 void 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 
550 void 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 
558 void SSD1306Controller::swapFGBG(Rect const & rect, Rect & updateRect)
559 {
560  invertRect(rect, updateRect);
561 }
562 
563 
564 // supports overlapping of source and dest rectangles
565 void 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!
576 void 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 
584 void 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 
593 void 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 
604 void 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 
614 void 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 
624 void 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
int16_t X2
Definition: fabutils.h:179
Represents a 24 bit RGB color.
int16_t Y1
Definition: fabutils.h:227
void invert(bool value)
Inverts display colors.
void begin()
Initializes SSD1306.
int16_t Y2
Definition: fabutils.h:180
void setOrientation(SSD1306Orientation value)
Set display orientation and rotation.
int getMaxBufferLength()
Returns maximum size of read and write buffers.
Definition: tsi2c.h:143
void setResolution(char const *modeline, int viewPortWidth=-1, int viewPortHeight=-1, bool doubleBuffered=false)
Sets SSD1306 resolution and viewport size.
int16_t Y1
Definition: fabutils.h:178
int getViewPortHeight()
Determines vertical size of the viewport.
int16_t Y
bool begin(gpio_num_t SDAGPIO, gpio_num_t SCLGPIO)
Initializes I2C instance associating GPIOs to I2C signals.
Definition: tsi2c.cpp:68
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...
Definition: tsi2c.h:80
int16_t X1
Definition: fabutils.h:177
int16_t X1
Definition: fabutils.h:226
void resumeBackgroundPrimitiveExecution()
Resumes drawings after suspendBackgroundPrimitiveExecution().
This file contains some utility classes and functions.
Definition: canvas.cpp:36
SSD1306Orientation
This enum defines SSD1306 orientation.
int16_t Y2
Definition: fabutils.h:229
void setScreenRow(int value)
Set initial top row of the viewport.
Represents a rectangle.
Definition: fabutils.h:225
int getViewPortWidth()
Determines horizontal size of the viewport.
int16_t X
void suspendBackgroundPrimitiveExecution()
Suspends drawings.
int16_t X2
Definition: fabutils.h:228
bool write(int address, uint8_t *buffer, int size, int frequency=100000, int timeOutMS=50)
Sends a buffer to I2C bus.
Definition: tsi2c.cpp:105
This file contains fabgl::SSD1306Controller definition.
int screenRow()
Gets initial top row of the viewport.