SSD1306 OLED display driver  1.8.2
This library is developed to control SSD1306/SSD1331/SSD1351/IL9163/PCD8554 RGB i2c/spi LED displays
ssd1306_generic.c
1 /*
2  MIT License
3 
4  Copyright (c) 2016-2019, Alexey Dynda
5 
6  Permission is hereby granted, free of charge, to any person obtaining a copy
7  of this software and associated documentation files (the "Software"), to deal
8  in the Software without restriction, including without limitation the rights
9  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  copies of the Software, and to permit persons to whom the Software is
11  furnished to do so, subject to the following conditions:
12 
13  The above copyright notice and this permission notice shall be included in all
14  copies or substantial portions of the Software.
15 
16  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  SOFTWARE.
23 */
24 
28 
29 #include "ssd1306.h"
30 #include "ssd1306_fonts.h"
31 #include "lcd/lcd_common.h"
32 #include "intf/i2c/ssd1306_i2c.h"
33 #include "intf/spi/ssd1306_spi.h"
34 #include "intf/ssd1306_interface.h"
35 #include "ssd1306_hal/io.h"
36 #include "nano_gfx_types.h"
37 
38 enum
39 {
40  SSD1306_OLD_FIXED_FORMAT = 0x00,
41  SSD1306_NEW_FIXED_FORMAT = 0x01,
42  SSD1306_NEW_FORMAT = 0x02,
43  SSD1306_SQUIX_FORMAT = 0x03,
44 };
45 
46 uint16_t ssd1306_color = 0xFFFF;
47 lcduint_t ssd1306_cursorX = 0;
48 lcduint_t ssd1306_cursorY = 0;
49 SFixedFontInfo s_fixedFont = {}; //{ { 0 }, 0 };
50 #ifdef CONFIG_SSD1306_UNICODE_ENABLE
51 uint8_t g_ssd1306_unicode = 1;
52 #endif
53 static void (*s_ssd1306_getCharBitmap)(uint16_t unicode, SCharInfo *info) = NULL;
54 
55 static const uint8_t *ssd1306_getCharGlyph(char ch);
56 static const uint8_t *ssd1306_getU16CharGlyph(uint16_t unicode);
57 
59 {
60  return ssd1306_lcd.height;
61 }
62 
64 {
65  return ssd1306_lcd.width;
66 }
67 
68 void ssd1306_setCursor(lcdint_t x, lcdint_t y)
69 {
70  ssd1306_cursorX = x;
71  ssd1306_cursorY = y;
72 }
73 
74 static const uint8_t * ssd1306_readUnicodeRecord(SUnicodeBlockRecord *r, const uint8_t *p)
75 {
76  r->start_code =( pgm_read_byte(&p[0]) << 8) | (pgm_read_byte(&p[1]));
77  r->count = pgm_read_byte(&p[2]);
78  return (r->count > 0) ? (&p[3]): NULL;
79 }
80 
81 
82 void ssd1306_setSecondaryFont(const uint8_t * progmemUnicode)
83 {
84 #ifdef CONFIG_SSD1306_UNICODE_ENABLE
85  s_fixedFont.secondary_table = progmemUnicode;
86  if (s_fixedFont.secondary_table != NULL)
87  {
88  s_fixedFont.secondary_table += sizeof(SFontHeaderRecord);
89  }
90 #endif
91 }
92 
93 void ssd1306_getCharBitmap(uint16_t unicode, SCharInfo *info)
94 {
95  return s_ssd1306_getCharBitmap( unicode, info );
96 }
97 
98 uint16_t ssd1306_unicode16FromUtf8(uint8_t ch)
99 {
100 #ifdef CONFIG_SSD1306_UNICODE_ENABLE
101  static uint16_t unicode = 0;
102  ch &= 0x000000FF;
103  if (!unicode)
104  {
105  if ( ch >= 0xc0 )
106  {
107  unicode = ch;
109  }
110  return ch;
111  }
112  uint16_t code = ((unicode & 0x1f) << 6) | (ch & 0x3f);
113  unicode = 0;
114  return code;
115 #else
116  return ch;
117 #endif
118 }
119 
121 {
122 #ifdef CONFIG_SSD1306_UNICODE_ENABLE
123  g_ssd1306_unicode = 1;
124 #endif
125 }
126 
128 {
129 #ifdef CONFIG_SSD1306_UNICODE_ENABLE
130  g_ssd1306_unicode = 0;
131 #endif
132 }
133 
137 
141 
142 static const uint8_t *ssd1306_getCharGlyph(char ch)
143 {
144  return &s_fixedFont.primary_table[ (ch - s_fixedFont.h.ascii_offset) *
145  s_fixedFont.glyph_size +
146  (s_fixedFont.h.type == 0x01 ? sizeof(SUnicodeBlockRecord) : 0) ];
147 }
148 
149 #ifdef CONFIG_SSD1306_UNICODE_ENABLE
150 static const uint8_t *ssd1306_searchCharGlyph(const uint8_t * unicode_table, uint16_t unicode)
151 {
153  const uint8_t *data = unicode_table;
154  // looking for required unicode table
155  while (1)
156  {
157  ssd1306_readUnicodeRecord( &r, data );
158  if (r.count == 0)
159  {
160  break;
161  }
162  data += sizeof(SUnicodeBlockRecord);
163  if ( ( unicode >= r.start_code) && ( unicode < (r.start_code + r.count) ) )
164  {
165  break;
166  }
167  data += r.count * s_fixedFont.glyph_size;
168  }
169  if (r.count == 0)
170  {
171  // Sorry, no glyph found for the specified character
172  return NULL;
173  }
174  return &data[ (unicode - r.start_code) * s_fixedFont.glyph_size ];
175 }
176 #endif
177 
178 static const uint8_t *ssd1306_getU16CharGlyph(uint16_t unicode)
179 {
180 #ifdef CONFIG_SSD1306_UNICODE_ENABLE
181  const uint8_t * glyph = NULL;
182  if (g_ssd1306_unicode)
183  {
184  if ((unicode < 128) && (s_fixedFont.h.type == 0x00) && (s_fixedFont.primary_table != NULL))
185  {
186  return ssd1306_getCharGlyph(unicode);
187  }
188  if (s_fixedFont.primary_table)
189  {
190  glyph = ssd1306_searchCharGlyph( s_fixedFont.primary_table, unicode );
191  }
192  if (!glyph && s_fixedFont.secondary_table)
193  {
194  glyph = ssd1306_searchCharGlyph( s_fixedFont.secondary_table, unicode );
195  }
196  if (!glyph)
197  {
198  return ssd1306_getCharGlyph( s_fixedFont.h.ascii_offset );
199  }
200  return glyph;
201  }
202  else
203 #endif
204  {
205  return ssd1306_getCharGlyph(unicode);
206  }
207 }
208 
209 static void __ssd1306_oldFormatGetBitmap(uint16_t unicode, SCharInfo *info)
210 {
211  if (info)
212  {
213  info->width = s_fixedFont.h.width;
214  info->height = s_fixedFont.h.height;
215  info->spacing = 0;
216  info->glyph = ssd1306_getU16CharGlyph( unicode );
217  }
218 }
219 
220 void ssd1306_setFixedFont(const uint8_t * progmemFont)
221 {
222  s_fixedFont.h.type = pgm_read_byte( &progmemFont[0] );
223  s_fixedFont.h.width = pgm_read_byte(&progmemFont[1]);
224  s_fixedFont.h.height = pgm_read_byte(&progmemFont[2]);
225  s_fixedFont.h.ascii_offset = pgm_read_byte(&progmemFont[3]);
226  s_fixedFont.primary_table = progmemFont + 4;
227  s_ssd1306_getCharBitmap = __ssd1306_oldFormatGetBitmap;
228  s_fixedFont.pages = (s_fixedFont.h.height + 7) >> 3;
229  s_fixedFont.glyph_size = s_fixedFont.pages * s_fixedFont.h.width;
230 #ifdef CONFIG_SSD1306_UNICODE_ENABLE
231  s_fixedFont.secondary_table = NULL;
232 #endif
233 }
234 
235 void ssd1306_setFixedFont_oldStyle(const uint8_t * progmemFont)
236 {
237  s_fixedFont.h.type = pgm_read_byte( &progmemFont[0] );
238  s_fixedFont.h.width = pgm_read_byte(&progmemFont[1]);
239  s_fixedFont.h.height = pgm_read_byte(&progmemFont[2]);
240  s_fixedFont.h.ascii_offset = pgm_read_byte(&progmemFont[3]);
241  s_fixedFont.primary_table = progmemFont + 4;
242  s_fixedFont.pages = (s_fixedFont.h.height + 7) >> 3;
243  s_fixedFont.glyph_size = s_fixedFont.pages * s_fixedFont.h.width;
244 }
245 
249 
250 static void __ssd1306_newFormatGetBitmap(uint16_t unicode, SCharInfo *info)
251 {
252  if (info)
253  {
254 #ifdef CONFIG_SSD1306_UNICODE_ENABLE
255  uint8_t table_index = 0;
256 #endif
257  const uint8_t *data = s_fixedFont.primary_table;
258  while (data)
259  {
261  data = ssd1306_readUnicodeRecord( &r, data );
262  if (!data)
263  {
264 #ifdef CONFIG_SSD1306_UNICODE_ENABLE
265  if ( table_index == 0 )
266  {
267  table_index++;
268  data = s_fixedFont.secondary_table;
269  continue;
270  }
271 #endif
272  break;
273  }
274  /* Check that unicode in the section being processed */
275  if ( ( unicode < r.start_code) || ( unicode >= (r.start_code + r.count) ) )
276  {
277  // skip jump table
278  data += r.count * 4;
279  // skip block bitmap data
280  data += ((pgm_read_byte(&data[0]) << 8) | (pgm_read_byte(&data[1]))) + 2;
281  continue;
282  }
283  /* At this point data points to jump table (offset|offset|bytes|width) */
284  unicode -= r.start_code;
285  data += unicode * 4;
286  uint16_t offset = (pgm_read_byte(&data[0]) << 8) | (pgm_read_byte(&data[1]));
287  uint8_t glyph_width = pgm_read_byte(&data[2]);
288  uint8_t glyph_height = pgm_read_byte(&data[3]);
289  info->width = glyph_width;
290  info->height = glyph_height;
291  info->spacing = glyph_width ? 1 : (s_fixedFont.h.width >> 1);
292  info->glyph = data + (r.count - unicode) * 4 + 2 + offset;
293  break;
294  }
295  if (!info->glyph)
296  {
297  info->width = 0;
298  info->height = 0;
299  info->spacing = s_fixedFont.h.width >> 1;
300  info->glyph = s_fixedFont.primary_table;
301  }
302  }
303 }
304 
305 void ssd1306_setFreeFont(const uint8_t * progmemFont)
306 {
307  s_fixedFont.h.type = pgm_read_byte( &progmemFont[0] );
308  s_fixedFont.h.width = pgm_read_byte(&progmemFont[1]);
309  s_fixedFont.h.height = pgm_read_byte(&progmemFont[2]);
310  s_fixedFont.h.ascii_offset = pgm_read_byte(&progmemFont[3]);
311  s_fixedFont.primary_table = progmemFont + 4;
312  s_ssd1306_getCharBitmap = __ssd1306_newFormatGetBitmap;
313  s_fixedFont.pages = (s_fixedFont.h.height + 7) >> 3;
314 #ifdef CONFIG_SSD1306_UNICODE_ENABLE
315  s_fixedFont.secondary_table = NULL;
316 #endif
317 }
318 
322 
323 static void __ssd1306_squixFormatGetBitmap(uint16_t unicode, SCharInfo *info)
324 {
325  if (info)
326  {
327  const uint8_t *data = s_fixedFont.primary_table;
328  /* Check that unicode in the section being processed */
329  if ( !data || ( unicode < s_fixedFont.h.ascii_offset) || ( unicode >= (s_fixedFont.h.ascii_offset + s_fixedFont.count) ) )
330  {
331  info->width = 0;
332  info->height = 0;
333  info->spacing = s_fixedFont.h.width >> 1;
334  info->glyph = s_fixedFont.primary_table;
335  return;
336  }
337  /* At this point data points to jump table (offset|offset|bytes|width) */
338  const uint8_t * bitmap_data = data + (uint16_t)s_fixedFont.count * 4;
339  unicode -= s_fixedFont.h.ascii_offset;
340  data += (unicode * 4);
341  uint16_t offset = (pgm_read_byte(&data[0]) << 8) | pgm_read_byte(&data[1]);
342  uint8_t glyph_bytes = pgm_read_byte(&data[2]);
343 // uint8_t width = pgm_read_byte(&data[3]);
344  info->width = glyph_bytes; //(glyph_bytes + s_fixedFont.pages - 1) / s_fixedFont.pages;
345  info->height = s_fixedFont.h.height / 2;
346  info->spacing = 1;
347 // uint8_t index=0;
348  info->glyph = bitmap_data;
349  if ( offset != 0xFFFF )
350  {
351  info->glyph += offset;
352  }
353  }
354 }
355 
356 void ssd1306_setSquixFont(const uint8_t * progmemFont)
357 {
358  s_fixedFont.h.type = SSD1306_SQUIX_FORMAT;
359  s_fixedFont.h.width = pgm_read_byte(&progmemFont[0]);
360  s_fixedFont.h.height = pgm_read_byte(&progmemFont[1]);
361  s_fixedFont.h.ascii_offset = pgm_read_byte(&progmemFont[2]);
362  s_fixedFont.count = pgm_read_byte(&progmemFont[3]);
363  s_fixedFont.primary_table = progmemFont + 4;
364  s_ssd1306_getCharBitmap = __ssd1306_squixFormatGetBitmap;
365  s_fixedFont.pages = (s_fixedFont.h.height + 7) >> 3;
366  s_fixedFont.glyph_size = s_fixedFont.pages * s_fixedFont.h.width;
367 #ifdef CONFIG_SSD1306_UNICODE_ENABLE
368  s_fixedFont.secondary_table = NULL;
369 #endif
370 }
371 
372 lcduint_t ssd1306_getTextSize(const char *text, lcduint_t *height)
373 {
374  lcduint_t width = 0;
375  while (*text)
376  {
377  if (*text == '\r' || *text == '\n')
378  {
379  text++;
380  break;
381  }
382  uint16_t unicode = ssd1306_unicode16FromUtf8(*text);
383  if (unicode == SSD1306_MORE_CHARS_REQUIRED)
384  {
385  text++;
386  continue;
387  }
388  SCharInfo char_info;
389  ssd1306_getCharBitmap(unicode, &char_info);
390  width += char_info.width + char_info.spacing;
391  if ( height ) *height = char_info.height;
392  text++;
393  }
394  return width;
395 }
void ssd1306_setSecondaryFont(const uint8_t *progmemUnicode)
lcduint_t ssd1306_displayWidth()
uint8_t height
char height in pixels
const uint8_t * primary_table
font chars bits
uint16_t start_code
unicode start code
uint8_t width
width in pixels
uint8_t type
font type: 0 - Fixed Font
void ssd1306_setFixedFont(const uint8_t *progmemFont)
uint8_t height
height in pixels
uint8_t ascii_offset
ascii offset
void ssd1306_setFreeFont(const uint8_t *progmemFont)
ssd1306_lcd_t ssd1306_lcd
Definition: lcd_common.c:33
void ssd1306_enableAsciiMode(void)
uint8_t count
count of unicode chars in block
lcduint_t height
Definition: lcd_common.h:97
uint8_t width
char width in pixels
const uint8_t * secondary_table
font chars bits
void ssd1306_setCursor(lcdint_t x, lcdint_t y)
Sets cursor position for text mode print functions.
SFixedFontInfo s_fixedFont
SFontHeaderRecord h
record, containing information on font
lcduint_t ssd1306_getTextSize(const char *text, lcduint_t *height)
const uint8_t * glyph
char data, located in progmem.
lcduint_t ssd1306_displayHeight()
uint8_t spacing
additional spaces after char in pixels
void ssd1306_getCharBitmap(uint16_t unicode, SCharInfo *info)
returns char data for currently set (active) font.
lcduint_t width
Definition: lcd_common.h:94
#define SSD1306_MORE_CHARS_REQUIRED
uint8_t glyph_size
glyph size in bytes
uint8_t pages
height in pages (each page height is 8-pixels)
uint8_t count
count of characters
void ssd1306_enableUtf8Mode(void)