PololuOLED library
PololuSH1106Main.h
Go to the documentation of this file.
1 // Copyright (C) Pololu Corporation. See www.pololu.com for details.
2 
4 
5 #pragma once
6 
7 #include "PololuOLEDHelpers.h"
8 
9 #define SH1106_SET_COLUMN_ADDR_LOW 0x00
10 #define SH1106_SET_COLUMN_ADDR_HIGH 0x10
11 #define SH1106_SET_CONTRAST 0x81
12 #define SH1106_SET_SEGMENT_REMAP 0xA0
13 #define SH1106_SET_INVERT_DISPLAY 0xA6
14 #define SH1106_SET_DISPLAY_ON 0xAE
15 #define SH1106_SET_PAGE_ADDR 0xB0
16 #define SH1106_SET_COM_SCAN_DIR 0xC0
17 
109 template<class C> class PololuSH1106Main : public Print
110 {
111 public:
112 
114  {
115  memset(textBuffer, ' ', sizeof(textBuffer));
116  setLayout8x2();
117  }
118 
120 private:
121 
122  void clearDisplayRam()
123  {
124  core.sh1106TransferStart();
125  core.sh1106CommandMode();
126  core.sh1106Write(SH1106_SET_COLUMN_ADDR_LOW | 2);
127  for (uint8_t page = 0; page < 8; page++)
128  {
129  core.sh1106CommandMode();
130  core.sh1106Write(SH1106_SET_PAGE_ADDR | page);
131  core.sh1106Write(SH1106_SET_COLUMN_ADDR_HIGH | 0);
132  core.sh1106DataMode();
133  for (uint8_t i = 0; i < 128; i++)
134  {
135  core.sh1106Write(0);
136  }
137  }
138  core.sh1106TransferEnd();
139  clearDisplayRamOnNextDisplay = false;
140  }
141 
142  void configureDefault()
143  {
144  core.sh1106TransferStart();
145  core.sh1106CommandMode();
146  core.sh1106Write(SH1106_SET_SEGMENT_REMAP | 1); // flip horizontally
147  core.sh1106Write(SH1106_SET_COM_SCAN_DIR | 8); // flip vertically
148  core.sh1106Write(SH1106_SET_CONTRAST);
149  core.sh1106Write(0xFF); // maximum brightness
150  core.sh1106Write(SH1106_SET_DISPLAY_ON | 1);
151  core.sh1106TransferEnd();
152  }
153 
155 
156 private:
157 
158  void init2()
159  {
160  core.initPins();
161  core.reset();
162  clearDisplayRam();
163  configureDefault();
164  initialized = true;
165  }
166 
167 public:
168 
176  void init() { if (!initialized) { init2(); } }
177 
189  {
190  init2();
191  }
192 
195  void invert()
196  {
197  init();
198  core.sh1106TransferStart();
199  core.sh1106CommandMode();
200  core.sh1106Write(SH1106_SET_INVERT_DISPLAY | 1);
201  core.sh1106TransferEnd();
202  }
203 
205  void noInvert()
206  {
207  init();
208  core.sh1106TransferStart();
209  core.sh1106CommandMode();
210  core.sh1106Write(SH1106_SET_INVERT_DISPLAY | 0);
211  core.sh1106TransferEnd();
212  }
213 
215  void rotate180()
216  {
217  init();
218  core.sh1106TransferStart();
219  core.sh1106CommandMode();
220  core.sh1106Write(SH1106_SET_SEGMENT_REMAP);
221  core.sh1106Write(SH1106_SET_COM_SCAN_DIR);
222  core.sh1106TransferEnd();
223  }
224 
232  void noRotate()
233  {
234  init();
235  core.sh1106TransferStart();
236  core.sh1106CommandMode();
237  core.sh1106Write(SH1106_SET_SEGMENT_REMAP | 1);
238  core.sh1106Write(SH1106_SET_COM_SCAN_DIR | 8);
239  core.sh1106TransferEnd();
240  }
241 
246  void setContrast(uint8_t contrast)
247  {
248  init();
249  core.sh1106TransferStart();
250  core.sh1106CommandMode();
251  core.sh1106Write(SH1106_SET_CONTRAST);
252  core.sh1106Write(contrast);
253  core.sh1106TransferEnd();
254  }
255 
259  {
260  graphicsBuffer = nullptr;
261  displayFunction = &PololuSH1106Main::display8x2Text;
262  displayPartialFunction = &PololuSH1106Main::display8x2TextPartial;
263  clearDisplayRamOnNextDisplay = true;
264  }
265 
270  void setLayout8x2WithGraphics(const uint8_t * graphics)
271  {
272  graphicsBuffer = graphics;
273  displayFunction = &PololuSH1106Main::display8x2TextAndGraphics;
274  displayPartialFunction = &PololuSH1106Main::display8x2TextAndGraphicsPartial;
275  clearDisplayRamOnNextDisplay = true;
276  }
277 
285  {
286  graphicsBuffer = nullptr;
287  displayFunction = &PololuSH1106Main::display11x4Text;
288  displayPartialFunction = &PololuSH1106Main::display11x4TextPartial;
289  clearDisplayRamOnNextDisplay = true;
290  }
291 
300  void setLayout11x4WithGraphics(const uint8_t * graphics)
301  {
302  graphicsBuffer = graphics;
303  displayFunction = &PololuSH1106Main::display11x4TextAndGraphics;
304  displayPartialFunction = &PololuSH1106Main::display11x4TextAndGraphicsPartial;
305  clearDisplayRamOnNextDisplay = true;
306  }
307 
311  {
312  graphicsBuffer = nullptr;
313  displayFunction = &PololuSH1106Main::display21x8Text;
314  displayPartialFunction = &PololuSH1106Main::display21x8TextPartial;
315  clearDisplayRamOnNextDisplay = true;
316  }
317 
322  void setLayout21x8WithGraphics(const uint8_t * graphics)
323  {
324  graphicsBuffer = graphics;
325  displayFunction = &PololuSH1106Main::display21x8TextAndGraphics;
326  displayPartialFunction = &PololuSH1106Main::display21x8TextAndGraphicsPartial;
327  clearDisplayRamOnNextDisplay = true;
328  }
329 
331 
332 private:
333 
334  uint8_t getGlyphColumn(uint8_t glyph, uint8_t pixelX)
335  {
336  if (glyph >= 0x20)
337  {
338  return pgm_read_byte(&pololuOledFont[glyph - 0x20][pixelX]);
339  }
340  else if (glyph < 8)
341  {
342  return customChars[glyph][pixelX];
343  }
344  else
345  {
346  return 0;
347  }
348  }
349 
350  void writePageGraphics(uint8_t page)
351  {
352  core.sh1106CommandMode();
353  core.sh1106Write(SH1106_SET_PAGE_ADDR | page);
354  core.sh1106Write(SH1106_SET_COLUMN_ADDR_HIGH | 0);
355  core.sh1106Write(SH1106_SET_COLUMN_ADDR_LOW | 2);
356  core.sh1106DataMode();
357  const uint8_t * g = graphicsBuffer + page * 128;
358  for (uint8_t x = 0; x < 128; x++) { core.sh1106Write(*g++); }
359  }
360 
361  void writeSegmentUpperText(uint8_t page, uint8_t columnAddr,
362  const uint8_t * text, uint8_t textLength)
363  {
364  core.sh1106CommandMode();
365  core.sh1106Write(SH1106_SET_PAGE_ADDR | page);
366  core.sh1106Write(SH1106_SET_COLUMN_ADDR_HIGH | (columnAddr >> 4));
367  core.sh1106Write(SH1106_SET_COLUMN_ADDR_LOW | (columnAddr & 0xF));
368  core.sh1106DataMode();
369  for (uint8_t i = 0; i < textLength; i++)
370  {
371  uint8_t glyph = *text++;
372  for (uint8_t pixelX = 0; pixelX < 5; pixelX++)
373  {
374  uint8_t column = PololuOLEDHelpers::repeatBits(
375  getGlyphColumn(glyph, pixelX) & 0xF);
376  core.sh1106Write(column);
377  core.sh1106Write(column);
378  }
379  core.sh1106Write(0);
380  core.sh1106Write(0);
381  }
382  }
383 
384  void writeSegmentLowerText(uint8_t page, uint8_t columnAddr,
385  const uint8_t * text, uint8_t textLength)
386  {
387  core.sh1106CommandMode();
388  core.sh1106Write(SH1106_SET_PAGE_ADDR | page);
389  core.sh1106Write(SH1106_SET_COLUMN_ADDR_HIGH | (columnAddr >> 4));
390  core.sh1106Write(SH1106_SET_COLUMN_ADDR_LOW | (columnAddr & 0xF));
391  core.sh1106DataMode();
392  for (uint8_t i = 0; i < textLength; i++)
393  {
394  uint8_t glyph = *text++;
395  for (uint8_t pixelX = 0; pixelX < 5; pixelX++)
396  {
397  uint8_t column = PololuOLEDHelpers::repeatBits(
398  getGlyphColumn(glyph, pixelX) >> 4);
399  core.sh1106Write(column);
400  core.sh1106Write(column);
401  }
402  core.sh1106Write(0);
403  core.sh1106Write(0);
404  }
405  }
406 
407  void writeSegmentUpperTextAndGraphics(uint8_t page, uint8_t columnAddr,
408  const uint8_t * text, uint8_t textLength)
409  {
410  core.sh1106CommandMode();
411  core.sh1106Write(SH1106_SET_PAGE_ADDR | page);
412  core.sh1106Write(SH1106_SET_COLUMN_ADDR_HIGH | (columnAddr >> 4));
413  core.sh1106Write(SH1106_SET_COLUMN_ADDR_LOW | (columnAddr & 0xF));
414  core.sh1106DataMode();
415  const uint8_t * g = graphicsBuffer + page * 128 + (columnAddr - 2);
416  for (uint8_t i = 0; i < textLength; i++)
417  {
418  uint8_t glyph = *text++;
419  for (uint8_t pixelX = 0; pixelX < 5; pixelX++)
420  {
421  uint8_t column = PololuOLEDHelpers::repeatBits(
422  getGlyphColumn(glyph, pixelX) & 0xF);
423  core.sh1106Write(column ^ *g++);
424  core.sh1106Write(column ^ *g++);
425  }
426  core.sh1106Write(*g++);
427  core.sh1106Write(*g++);
428  }
429  }
430 
431  void writeSegmentLowerTextAndGraphics(uint8_t page, uint8_t columnAddr,
432  const uint8_t * text, uint8_t textLength)
433  {
434  core.sh1106CommandMode();
435  core.sh1106Write(SH1106_SET_PAGE_ADDR | page);
436  core.sh1106Write(SH1106_SET_COLUMN_ADDR_HIGH | (columnAddr >> 4));
437  core.sh1106Write(SH1106_SET_COLUMN_ADDR_LOW | (columnAddr & 0xF));
438  core.sh1106DataMode();
439  const uint8_t * g = graphicsBuffer + page * 128 + (columnAddr - 2);
440  for (uint8_t i = 0; i < textLength; i++)
441  {
442  uint8_t glyph = *text++;
443  // Note the extra check for g < graphicsBuffer + 1023, which avoids
444  // reading past the end of the graphics array in 11x4 mode.
445  for (uint8_t pixelX = 0; pixelX < 5 && g < graphicsBuffer + 1023; pixelX++)
446  {
447  uint8_t column = PololuOLEDHelpers::repeatBits(
448  getGlyphColumn(glyph, pixelX) >> 4);
449  core.sh1106Write(column ^ *g++);
450  core.sh1106Write(column ^ *g++);
451  }
452  if(g < graphicsBuffer + 1023)
453  {
454  core.sh1106Write(*g++);
455  core.sh1106Write(*g++);
456  }
457  }
458  }
459 
460  void writePageUpperTextAndGraphics(uint8_t page, const uint8_t * text,
461  uint8_t leftMargin, uint8_t textLength)
462  {
463  core.sh1106CommandMode();
464  core.sh1106Write(SH1106_SET_PAGE_ADDR | page);
465  core.sh1106Write(SH1106_SET_COLUMN_ADDR_HIGH | 0);
466  core.sh1106Write(SH1106_SET_COLUMN_ADDR_LOW | 2);
467  core.sh1106DataMode();
468  const uint8_t * g = graphicsBuffer + page * 128;
469  for (uint8_t i = 0; i < leftMargin; i++) { core.sh1106Write(*g++); }
470  for (uint8_t textX = 0; textX < textLength; textX++)
471  {
472  uint8_t glyph = *text++;
473  for (uint8_t pixelX = 0; pixelX < 5; pixelX++)
474  {
475  uint8_t column = PololuOLEDHelpers::repeatBits(
476  getGlyphColumn(glyph, pixelX) & 0xF);
477  core.sh1106Write(column ^ *g++);
478  core.sh1106Write(column ^ *g++);
479  }
480  core.sh1106Write(*g++);
481  core.sh1106Write(*g++);
482  }
483  for (uint8_t x = leftMargin + textLength * 12; x < 128; x++)
484  {
485  core.sh1106Write(*g++);
486  }
487  }
488 
489  void writePageLowerTextAndGraphics(uint8_t page, const uint8_t * text,
490  uint8_t leftMargin, uint8_t textLength)
491  {
492  core.sh1106CommandMode();
493  core.sh1106Write(SH1106_SET_PAGE_ADDR | page);
494  core.sh1106Write(SH1106_SET_COLUMN_ADDR_HIGH | 0);
495  core.sh1106Write(SH1106_SET_COLUMN_ADDR_LOW | 2);
496  core.sh1106DataMode();
497  const uint8_t * g = graphicsBuffer + page * 128;
498  for (uint8_t i = 0; i < leftMargin; i++) { core.sh1106Write(*g++); }
499  for (uint8_t textX = 0; textX < textLength; textX++)
500  {
501  uint8_t glyph = *text++;
502  // Note the extra check for g < graphicsBuffer + 1023, which avoids
503  // reading past the end of the graphics array in 11x4 mode.
504  for (uint8_t pixelX = 0; pixelX < 5 && g < graphicsBuffer + 1023; pixelX++)
505  {
506  uint8_t column = PololuOLEDHelpers::repeatBits(
507  getGlyphColumn(glyph, pixelX) >> 4);
508  core.sh1106Write(column ^ *g++);
509  core.sh1106Write(column ^ *g++);
510  }
511  if(g < graphicsBuffer + 1023)
512  {
513  core.sh1106Write(*g++);
514  core.sh1106Write(*g++);
515  }
516  }
517  for (uint8_t x = leftMargin + textLength * 12; x < 128; x++)
518  {
519  core.sh1106Write(*g++);
520  }
521  }
522 
523  void writeSegmentText(uint8_t page, uint8_t columnAddr,
524  const uint8_t * text, uint8_t textLength)
525  {
526  core.sh1106CommandMode();
527  core.sh1106Write(SH1106_SET_PAGE_ADDR | page);
528  core.sh1106Write(SH1106_SET_COLUMN_ADDR_HIGH | (columnAddr >> 4));
529  core.sh1106Write(SH1106_SET_COLUMN_ADDR_LOW | (columnAddr & 0xF));
530  core.sh1106DataMode();
531  for (uint8_t i = 0; i < textLength; i++)
532  {
533  uint8_t glyph = *text++;
534  for (uint8_t pixelX = 0; pixelX < 5; pixelX++)
535  {
536  core.sh1106Write(getGlyphColumn(glyph, pixelX));
537  }
538  core.sh1106Write(0);
539  }
540  }
541 
542  void writeSegmentTextAndGraphics(uint8_t page, uint8_t columnAddr,
543  const uint8_t * text, uint8_t textLength)
544  {
545  core.sh1106CommandMode();
546  core.sh1106Write(SH1106_SET_PAGE_ADDR | page);
547  core.sh1106Write(SH1106_SET_COLUMN_ADDR_HIGH | (columnAddr >> 4));
548  core.sh1106Write(SH1106_SET_COLUMN_ADDR_LOW | (columnAddr & 0xF));
549  core.sh1106DataMode();
550  const uint8_t * g = graphicsBuffer + page * 128 + (columnAddr - 2);
551  for (uint8_t i = 0; i < textLength; i++)
552  {
553  uint8_t glyph = *text++;
554  for (uint8_t pixelX = 0; pixelX < 5; pixelX++)
555  {
556  core.sh1106Write(getGlyphColumn(glyph, pixelX) ^ *g++);
557  }
558  core.sh1106Write(*g++);
559  }
560  }
561 
562  void writePageTextAndGraphics(uint8_t page, const uint8_t * text,
563  uint8_t leftMargin, uint8_t textLength)
564  {
565  core.sh1106CommandMode();
566  core.sh1106Write(SH1106_SET_PAGE_ADDR | page);
567  core.sh1106Write(SH1106_SET_COLUMN_ADDR_HIGH | 0);
568  core.sh1106Write(SH1106_SET_COLUMN_ADDR_LOW | 2);
569  core.sh1106DataMode();
570  const uint8_t * g = graphicsBuffer + page * 128;
571  for (uint8_t i = 0; i < leftMargin; i++) { core.sh1106Write(*g++); }
572  for (uint8_t textX = 0; textX < textLength; textX++)
573  {
574  uint8_t glyph = *text++;
575  for (uint8_t pixelX = 0; pixelX < 5; pixelX++)
576  {
577  core.sh1106Write(getGlyphColumn(glyph, pixelX) ^ *g++);
578  }
579  core.sh1106Write(*g++);
580  }
581  for (uint8_t x = leftMargin + textLength * 6; x < 128; x++)
582  {
583  core.sh1106Write(*g++);
584  }
585  }
586 
587 
589  // Character size: 10x16
590  // Character horizontal margin: 2
591  // Screen left and right margins: (128 - 8*10 - 7*2)/2 = 17
592  // Line 0: pages 2 and 3
593  // Line 1: pages 5 and 6
594 
595  void display8x2TextPartial(uint8_t x, uint8_t y, uint8_t width)
596  {
597  if (x >= 8 || y >= 2) { return; }
598  if (width > (uint8_t)(8 - x)) { width = 8 - x; }
599  if (width == 0) { return; }
600 
601  const uint8_t page = 2 + y * 3;
602  const uint8_t columnAddr = 2 + 17 + x * 12;
603  const uint8_t * const text = getLinePointer(y) + x;
604 
605  core.sh1106TransferStart();
606  writeSegmentUpperText(page, columnAddr, text, width);
607  writeSegmentLowerText(page + 1, columnAddr, text, width);
608  core.sh1106TransferEnd();
609  }
610 
611  void display8x2Text()
612  {
613  core.sh1106TransferStart();
614  writeSegmentUpperText(2, 2 + 17, getLinePointer(0), 8);
615  writeSegmentLowerText(3, 2 + 17, getLinePointer(0), 8);
616  writeSegmentUpperText(5, 2 + 17, getLinePointer(1), 8);
617  writeSegmentLowerText(6, 2 + 17, getLinePointer(1), 8);
618  core.sh1106TransferEnd();
619  }
620 
621  void display8x2TextAndGraphicsPartial(uint8_t x, uint8_t y, uint8_t width)
622  {
623  if (x >= 8 || y >= 2) { return; }
624  if (width > (uint8_t)(8 - x)) { width = 8 - x; }
625  if (width == 0) { return; }
626 
627  const uint8_t page = 2 + y * 3;
628  const uint8_t columnAddr = 2 + 17 + x * 12;
629  uint8_t * const text = getLinePointer(y) + x;
630 
631  core.sh1106TransferStart();
632  writeSegmentUpperTextAndGraphics(page, columnAddr, text, width);
633  writeSegmentLowerTextAndGraphics(page + 1, columnAddr, text, width);
634  core.sh1106TransferEnd();
635  }
636 
637  void display8x2TextAndGraphics()
638  {
639  core.sh1106TransferStart();
640  writePageGraphics(0);
641  writePageGraphics(1);
642  writePageUpperTextAndGraphics(2, getLinePointer(0), 17, 8);
643  writePageLowerTextAndGraphics(3, getLinePointer(0), 17, 8);
644  writePageGraphics(4);
645  writePageUpperTextAndGraphics(5, getLinePointer(1), 17, 8);
646  writePageLowerTextAndGraphics(6, getLinePointer(1), 17, 8);
647  writePageGraphics(7);
648  core.sh1106TransferEnd();
649  }
650 
652  // Character size: 10x16
653  // Character horizontal margin: 2
654  // Screen left margin: 0
655  // Screen right margin: -2 (or 10 if you don't use the last column; off center: more room for graphics)
656  // Line 0: pages 0 and 1
657  // Line 1: pages 2 and 3
658  // Line 3: pages 4 and 5
659  // Line 4: pages 6 and 7
660 
661  void display11x4TextPartial(uint8_t x, uint8_t y, uint8_t width)
662  {
663  if (x >= 11 || y >= 4) { return; }
664  if (width > (uint8_t)(11 - x)) { width = 11 - x; }
665  if (width == 0) { return; }
666 
667  const uint8_t page = y * 2;
668  const uint8_t columnAddr = 2 + x * 12;
669  const uint8_t * const textStart = getLinePointer(y) + x;
670 
671  core.sh1106TransferStart();
672  writeSegmentUpperText(page, columnAddr, textStart, width);
673  writeSegmentLowerText(page + 1, columnAddr, textStart, width);
674  core.sh1106TransferEnd();
675  }
676 
677  void display11x4Text()
678  {
679  core.sh1106TransferStart();
680  writeSegmentUpperText(0, 2, getLinePointer(0), 11);
681  writeSegmentLowerText(1, 2, getLinePointer(0), 11);
682  writeSegmentUpperText(2, 2, getLinePointer(1), 11);
683  writeSegmentLowerText(3, 2, getLinePointer(1), 11);
684  writeSegmentUpperText(4, 2, getLinePointer(2), 11);
685  writeSegmentLowerText(5, 2, getLinePointer(2), 11);
686  writeSegmentUpperText(6, 2, getLinePointer(3), 11);
687  writeSegmentLowerText(7, 2, getLinePointer(3), 11);
688  core.sh1106TransferEnd();
689  }
690 
691  void display11x4TextAndGraphicsPartial(uint8_t x, uint8_t y, uint8_t width)
692  {
693  if (x >= 11 || y >= 4) { return; }
694  if (width > (uint8_t)(10 - x)) { width = 11 - x; }
695  if (width == 0) { return; }
696 
697  const uint8_t page = y * 2;
698  const uint8_t columnAddr = 2 + x * 12;
699  uint8_t * const text = getLinePointer(y) + x;
700 
701  core.sh1106TransferStart();
702  writeSegmentUpperTextAndGraphics(page, columnAddr, text, width);
703  writeSegmentLowerTextAndGraphics(page + 1, columnAddr, text, width);
704  core.sh1106TransferEnd();
705  }
706 
707  void display11x4TextAndGraphics()
708  {
709  core.sh1106TransferStart();
710  writePageUpperTextAndGraphics(0, getLinePointer(0), 0, 11);
711  writePageLowerTextAndGraphics(1, getLinePointer(0), 0, 11);
712  writePageUpperTextAndGraphics(2, getLinePointer(1), 0, 11);
713  writePageLowerTextAndGraphics(3, getLinePointer(1), 0, 11);
714  writePageUpperTextAndGraphics(4, getLinePointer(2), 0, 11);
715  writePageLowerTextAndGraphics(5, getLinePointer(2), 0, 11);
716  writePageUpperTextAndGraphics(6, getLinePointer(3), 0, 11);
717  writePageLowerTextAndGraphics(7, getLinePointer(3), 0, 11);
718  core.sh1106TransferEnd();
719  }
720 
722  // Character size: 5x8
723  // Character horizontal margin: 1
724  // Screen left margin: 0
725  // Screen right margin: 3 (off center: more room for graphics)
726  // Line number = Page number
727 
728  void display21x8TextPartial(uint8_t x, uint8_t y, uint8_t width)
729  {
730  if (x >= 21 || y >= 8) { return; }
731  if (width > (uint8_t)(21 - x)) { width = 21 - x; }
732  if (width == 0) { return; }
733 
734  const uint8_t columnAddr = 2 + x * 6;
735  const uint8_t * const textStart = getLinePointer(y) + x;
736 
737  core.sh1106TransferStart();
738  writeSegmentText(y, columnAddr, textStart, width);
739  core.sh1106TransferEnd();
740  }
741 
742  void display21x8Text()
743  {
744  core.sh1106TransferStart();
745  writeSegmentText(0, 2, getLinePointer(0), 21);
746  writeSegmentText(1, 2, getLinePointer(1), 21);
747  writeSegmentText(2, 2, getLinePointer(2), 21);
748  writeSegmentText(3, 2, getLinePointer(3), 21);
749  writeSegmentText(4, 2, getLinePointer(4), 21);
750  writeSegmentText(5, 2, getLinePointer(5), 21);
751  writeSegmentText(6, 2, getLinePointer(6), 21);
752  writeSegmentText(7, 2, getLinePointer(7), 21);
753  core.sh1106TransferEnd();
754  }
755 
756  void display21x8TextAndGraphicsPartial(uint8_t x, uint8_t y, uint8_t width)
757  {
758  if (x >= 21 || y >= 8) { return; }
759  if (width > (uint8_t)(21 - x)) { width = 21 - x; }
760  if (width == 0) { return; }
761 
762  const uint8_t columnAddr = 2 + x * 6;
763  uint8_t * const text = getLinePointer(y) + x;
764 
765  core.sh1106TransferStart();
766  writeSegmentTextAndGraphics(y, columnAddr, text, width);
767  core.sh1106TransferEnd();
768  }
769 
770  void display21x8TextAndGraphics()
771  {
772  core.sh1106TransferStart();
773  writePageTextAndGraphics(0, getLinePointer(0), 0, 21);
774  writePageTextAndGraphics(1, getLinePointer(1), 0, 21);
775  writePageTextAndGraphics(2, getLinePointer(2), 0, 21);
776  writePageTextAndGraphics(3, getLinePointer(3), 0, 21);
777  writePageTextAndGraphics(4, getLinePointer(4), 0, 21);
778  writePageTextAndGraphics(5, getLinePointer(5), 0, 21);
779  writePageTextAndGraphics(6, getLinePointer(6), 0, 21);
780  writePageTextAndGraphics(7, getLinePointer(7), 0, 21);
781  core.sh1106TransferEnd();
782  }
783 
784 public:
785 
794  void display()
795  {
796  init();
797  if (clearDisplayRamOnNextDisplay) { clearDisplayRam(); }
798  ((*this).*(displayFunction))();
799  disableAutoDisplay = false;
800  }
801 
822  void displayPartial(uint8_t x, uint8_t y, uint8_t width)
823  {
824  init();
825  if (clearDisplayRamOnNextDisplay) { clearDisplayRam(); }
826  ((*this).*(displayPartialFunction))(x, y, width);
827  }
828 
829 
831 
840  {
841  disableAutoDisplay = true;
842  }
843 
862  uint8_t * getLinePointer(uint8_t line)
863  {
864  return textBuffer + line * textBufferWidth;
865  }
866 
874  void gotoXY(uint8_t x, uint8_t y)
875  {
876  textCursorX = x;
877  textCursorY = y;
878  }
879 
881  uint8_t getX() { return textCursorX; }
882 
884  uint8_t getY() { return textCursorY; }
885 
888  {
889  memmove(textBuffer, textBuffer + textBufferWidth, textBufferWidth * (textBufferHeight - 1));
890  memset(textBuffer + textBufferWidth * (textBufferHeight - 1), ' ', textBufferWidth);
891  if (!disableAutoDisplay) { display(); }
892  }
893 
901  void clear()
902  {
903  memset(textBuffer, ' ', sizeof(textBuffer));
904  gotoXY(0, 0);
905  if (!disableAutoDisplay) { display(); }
906  }
907 
926  size_t write(const uint8_t * buffer, size_t size) override
927  {
928  if (textCursorY >= textBufferHeight) { return 0; }
929  if (textCursorX >= textBufferWidth) { return 0; }
930  if (size > (uint8_t)(textBufferWidth - textCursorX))
931  {
932  size = textBufferWidth - textCursorX;
933  }
934 
935  memcpy(getLinePointer(textCursorY) + textCursorX, buffer, size);
936 
937  if (!disableAutoDisplay)
938  {
939  displayPartial(textCursorX, textCursorY, size);
940  }
941 
942  textCursorX += size;
943  return size;
944  }
945 
950  size_t write(uint8_t d) override
951  {
952  if (textCursorY >= textBufferHeight) { return 0; }
953  if (textCursorX >= textBufferWidth) { return 0; }
954 
955  *(getLinePointer(textCursorY) + textCursorX) = d;
956 
957  if (!disableAutoDisplay)
958  {
959  displayPartial(textCursorX, textCursorY, 1);
960  }
961 
962  textCursorX++;
963  return 1;
964  }
965 
969  void loadCustomCharacterFromRam(const uint8_t * picture, uint8_t number)
970  {
971  uint8_t * columns = customChars[number];
972  for (uint8_t i = 0; i < 5; i++)
973  {
974  columns[i] = 0;
975  }
976  for (uint8_t i = 0; i < 8; i++)
977  {
978  uint8_t row = picture[i];
979  uint8_t mask = 1 << i;
980  if (row & (1 << 0)) { columns[4] |= mask; }
981  if (row & (1 << 1)) { columns[3] |= mask; }
982  if (row & (1 << 2)) { columns[2] |= mask; }
983  if (row & (1 << 3)) { columns[1] |= mask; }
984  if (row & (1 << 4)) { columns[0] |= mask; }
985  }
986  }
987 
991  void loadCustomCharacter(const uint8_t * picture, uint8_t number)
992  {
993  uint8_t ram_picture[8];
994  for (uint8_t i = 0; i < 8; i++)
995  {
996  ram_picture[i] = pgm_read_byte(picture + i);
997  }
998  loadCustomCharacterFromRam(ram_picture, number);
999  }
1000 
1005  void loadCustomCharacter(const char * picture, uint8_t number) {
1006  loadCustomCharacter((const uint8_t *)picture, number);
1007  }
1008 
1010 
1012  C core;
1013 
1014 private:
1015 
1016  bool initialized;
1017 
1018  bool clearDisplayRamOnNextDisplay;
1019 
1020  bool disableAutoDisplay;
1021 
1022  // We use the display routines through these member function pointers and are
1023  // careful about where we refer to them so that the routines for unused
1024  // layouts do not take up program space.
1025  void (PololuSH1106Main::*displayFunction)();
1026  void (PololuSH1106Main::*displayPartialFunction)(uint8_t, uint8_t, uint8_t);
1027 
1028  static const uint8_t textBufferWidth = 21, textBufferHeight = 8;
1029 
1030  uint8_t textBuffer[textBufferHeight * textBufferWidth];
1031  uint8_t textCursorX;
1032  uint8_t textCursorY;
1033  uint8_t customChars[8][5];
1034 
1035  const uint8_t * graphicsBuffer;
1036 };
const PROGMEM uint8_t pololuOledFont[][5]
This array defines the appearance of characters 32 through 255.
This class makes it easy to display text and graphics on a 128x64 SH1106 OLED.
void loadCustomCharacterFromRam(const uint8_t *picture, uint8_t number)
Defines a custom character from RAM.
void invert()
Configures the OLED to invert all the pixels, resulting in black-on-white text.
void rotate180()
Configures the OLED to rotate its display 180 degrees from normal.
void setLayout8x2WithGraphics(const uint8_t *graphics)
Configures this library to use a layout with 8 columns and 2 rows of text, XORed with a graphics buff...
uint8_t getY()
Gets the Y coordinate of the text cursor.
void noRotate()
Configures the OLED to use the default orientation.
void gotoXY(uint8_t x, uint8_t y)
Changes the location of the text cursor.
void noAutoDisplay()
Turns off auto display mode.
void loadCustomCharacter(const uint8_t *picture, uint8_t number)
Defines a custom character.
void setLayout21x8WithGraphics(const uint8_t *graphics)
Configures this library to use a layout with 21 columns and 8 rows of text, XORed with a graphics buf...
void clear()
Clears the text and resets the text cursor to the upper left.
void setLayout11x4()
Configures this library to use a layout with 11 columns and 4 rows of text.
void setLayout8x2()
Configures this library to use its default layout, which allows for 8 columns and 2 rows of text.
void reinitialize()
Reinitializes the OLED and its settings.
void loadCustomCharacter(const char *picture, uint8_t number)
Defines a custom character.
void display()
Writes all of the text/graphics to the OLED.
void init()
Initializes the OLED if it has not already been initialized.
void setLayout21x8()
Configures this library to use a layout with 21 columns and 8 rows of text.
size_t write(uint8_t d) override
Writes a single character of text.
C core
This object handles all low-level communication with the SH1106.
uint8_t * getLinePointer(uint8_t line)
Gets a pointer to a line of text in this library's text buffer.
void displayPartial(uint8_t x, uint8_t y, uint8_t width)
Writes a certain region of text/graphics to the OLED.
void scrollDisplayUp()
Moves all the text up one row. (Does not change the cursor position.)
uint8_t getX()
Gets the X coordinate of the text cursor.
void noInvert()
Configures the OLED to not invert its pixels (the default).
size_t write(const uint8_t *buffer, size_t size) override
Writes a string of text.
void setContrast(uint8_t contrast)
Sets the contrast (i.e. brightness) of the OLED.
void setLayout11x4WithGraphics(const uint8_t *graphics)
Configures this library to use a layout with 11 columns and 4 rows of text, XORed with a graphics buf...