SSD1306 OLED display driver  1.8.2
This library is developed to control SSD1306/SSD1331/SSD1351/IL9163/PCD8554 RGB i2c/spi LED displays
lcd_il9163.c
1 /*
2  MIT License
3 
4  Copyright (c) 2018-2020, 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 
25 #include "lcd_il9163.h"
26 #include "lcd_common.h"
27 #include "intf/ssd1306_interface.h"
28 #include "intf/spi/ssd1306_spi.h"
29 #include "ssd1306_hal/io.h"
30 #include "nano_gfx_types.h"
31 #ifdef SDL_EMULATION
32 #include "sdl_core.h"
33 #endif
34 
35 #define CMD_ARG 0xFF
36 #define CMD_DELAY 0xFF
37 
38 extern uint16_t ssd1306_color;
39 extern uint32_t s_ssd1306_spi_clock;
40 
41 static uint8_t s_rotation = 0x00;
42 static uint8_t s_rgb_bit = 0b00001000;
43 static lcdint_t s_offset_x = 0;
44 static lcdint_t s_offset_y = 0;
45 
46 static const PROGMEM uint8_t s_oled128x128_initData[] =
47 {
48 #ifdef SDL_EMULATION
49  SDL_LCD_IL9163,
50  0x00,
51 #endif
52 // 0x01, // sw reset. not needed, we do hardware reset
53  0x11, // exit sleep mode
54  0x3A, CMD_ARG, 0x05, // set 16-bit pixel format
55  0x26, CMD_ARG, 0x04, // set gamma curve: valid values 1, 2, 4, 8
56 // 0xF2, CMD_ARG, 0x01, // enable gamma adjustment, 0 - to disable
57 // 0xE0, CMD_ARG, 0x3F, CMD_ARG, 0x25, CMD_ARG, 0x1C,
58 // CMD_ARG, 0x1E, CMD_ARG, 0x20, CMD_ARG, 0x12,
59 // CMD_ARG, 0x2A, CMD_ARG, 0x90, CMD_ARG, 0x24,
60 // CMD_ARG, 0x11, CMD_ARG, 0x00, CMD_ARG, 0x00,
61 // CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x00, // positive gamma correction
62 // 0xE1, CMD_ARG, 0x20, CMD_ARG, 0x20, CMD_ARG, 0x20,
63 // CMD_ARG, 0x20, CMD_ARG, 0x05, CMD_ARG, 0x00,
64 // CMD_ARG, 0x15, CMD_ARG, 0xA7, CMD_ARG, 0x3D,
65 // CMD_ARG, 0x18, CMD_ARG, 0x25, CMD_ARG, 0x2A,
66 // CMD_ARG, 0x2B, CMD_ARG, 0x2B, CMD_ARG, 0x3A, // negative gamma correction
67 // 0xB1, CMD_ARG, 0x08, CMD_ARG, 0x08, // frame rate control 1, use by default
68 // 0xB4, CMD_ARG, 0x07, // display inversion, use by default
69  0xC0, CMD_ARG, 0x0A, CMD_ARG, 0x02, // power control 1
70  0xC1, CMD_ARG, 0x02, // power control 2
71  0xC5, CMD_ARG, 0x50, CMD_ARG, 0x5B, // vcom control 1
72  0xC7, CMD_ARG, 0x40, // vcom offset
73 // 0x2A, CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x7F, // set column address, not needed. set by direct API
74 // 0x2B, CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x9F, // set page address, not needed. set by direct API
75  0x36, CMD_ARG, 0b10001100, // enable fake "vertical addressing" mode (for il9163_setBlock() )
76  0x29, // display on
77 };
78 
79 static const PROGMEM uint8_t s_oled128x160_initData[] =
80 {
81 #ifdef SDL_EMULATION
82  SDL_LCD_ST7735, 0x00,
83  0b00000011, 0x00,
84 #endif
85  0x01, CMD_DELAY, 150, // SWRESET sw reset. not needed, we do hardware reset
86  0x11, CMD_DELAY, 255, // SLPOUT exit sleep mode
87  0xB1, 0x03, 0x01, 0x2C, 0x2D, // FRMCTR1 frame rate control 1, use by default
88  0xB2, 0x03, 0x01, 0x2C, 0x2D, // FRMCTR2, Frame Rate Control (In Idle mode/ 8-colors)
89  0xB3, 0x06, // FRMCTR3 (B3h): Frame Rate Control (In Partial mode/ full colors)
90  0x01, 0x2C, 0x2D,
91  0x01, 0x2C, 0x2D,
92  0xB4, 0x01, 0x07, // INVCTR display inversion, use by default
93  0xB6, 0x02, 0x15, 0x02, // DISSET5
94  0xC0, 0x03, 0xA2, 0x02, 0x84, // PWCTR1 power control 1
95  0xC1, 0x01, 0xC5, // PWCTR2 power control 2
96  0xC2, 0x02, 0x0A, 0x00, // PWCTR3 power control 3
97  0xC3, 0x02, 0x8A, 0x2A, // PWCTR4 (C3h): Power Control 4 (in Idle mode/ 8-colors)
98  0xC4, 0x02, 0x8A, 0xEE, // PWCTR5 (C4h): Power Control 5 (in Partial mode/ full-colors)
99  0xC5, 0x01, 0x0E, // VMCTR vcom control 1
100  0x20, 0x00, // INVOFF (20h): Display Inversion Off
101 // 0xFC, 0x02, 0x11, 0x15, // PWCTR6
102 
103  0x36, 0x01, 0b00100000, // MADCTL // enable fake "vertical addressing" mode (for il9163_setBlock() )
104  0x3A, 0x01, 0x05, // COLMOD set 16-bit pixel format
105 
106 // 0x26, 1, 0x08, // GAMSET set gamma curve: valid values 1, 2, 4, 8
107 // 0xF2, 1, 0x01, // enable gamma adjustment, 0 - to disable
108  0xE0, 0x10, // GMCTRP1 positive gamma correction
109  0x0F, 0x1A, 0x0F, 0x18,
110  0x2F, 0x28, 0x20, 0x22,
111  0x1F, 0x1B, 0x23, 0x37,
112  0x00, 0x07, 0x02, 0x10,
113  0xE1, 0x10, // GMCTRN1 negative gamma correction
114  0x0F, 0x1B, 0x0F, 0x17,
115  0x33, 0x2C, 0x29, 0x2E,
116  0x30, 0x30, 0x39, 0x3F,
117  0x00, 0x07, 0x03, 0x10,
118 // 0xC7, 1, 0x40, // vcom offset
119 // 0x2A, CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x7F, // set column address, not needed. set by direct API
120 // 0x2B, CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x9F, // set page address, not needed. set by direct API
121  0x29, CMD_DELAY, 100, // DISPON display on
122  0x13, CMD_DELAY, 10, // NORON
123 };
124 
125 static uint8_t s_column;
126 static uint8_t s_page;
127 
128 static void il9163_setBlock(lcduint_t x, lcduint_t y, lcduint_t w)
129 {
130  uint8_t rx = w ? (x + w - 1) : (ssd1306_lcd.width - 1);
131  s_column = x;
132  s_page = y;
135  ssd1306_intf.send(0x2B);
136  ssd1306_spiDataMode(1); // According to datasheet all args must be passed in data mode
137  ssd1306_intf.send(0);
138  ssd1306_intf.send(x + s_offset_x);
139  ssd1306_intf.send(0);
140  ssd1306_intf.send((rx < ssd1306_lcd.width ? rx : (ssd1306_lcd.width - 1)) + s_offset_x);
142  ssd1306_intf.send(0x2A);
143  ssd1306_spiDataMode(1); // According to datasheet all args must be passed in data mode
144  ssd1306_intf.send(0);
145  ssd1306_intf.send((y<<3) + s_offset_y);
146  ssd1306_intf.send(0);
147  ssd1306_intf.send((((y<<3) + 7) < ssd1306_lcd.height ? ((y<<3) + 7) : (ssd1306_lcd.height - 1)) + s_offset_y);
149  ssd1306_intf.send(0x2C);
151 }
152 
153 static void il9163_setBlock2(lcduint_t x, lcduint_t y, lcduint_t w)
154 {
155  uint8_t rx = w ? (x + w - 1) : (ssd1306_lcd.width - 1);
158  ssd1306_intf.send(0x2A);
159  ssd1306_spiDataMode(1); // According to datasheet all args must be passed in data mode
160  ssd1306_intf.send(0);
161  ssd1306_intf.send(x + s_offset_x);
162  ssd1306_intf.send(0);
163  ssd1306_intf.send((rx < ssd1306_lcd.width ? rx : (ssd1306_lcd.width - 1)) + s_offset_x);
165  ssd1306_intf.send(0x2B);
166  ssd1306_spiDataMode(1); // According to datasheet all args must be passed in data mode
167  ssd1306_intf.send(0);
168  ssd1306_intf.send(y + s_offset_y);
169  ssd1306_intf.send(0);
170  ssd1306_intf.send(ssd1306_lcd.height - 1 + s_offset_y);
172  ssd1306_intf.send(0x2C);
174 }
175 
176 void il9163_setOffset(lcdint_t x, lcdint_t y)
177 {
178  s_offset_x = x;
179  s_offset_y = y;
180 }
181 
182 static void il9163_nextPage(void)
183 {
184  ssd1306_intf.stop();
185  ssd1306_lcd.set_block(s_column,s_page+1,0);
186 }
187 
188 static void il9163_nextPage2(void)
189 {
190 }
191 
193 {
196  ssd1306_intf.send( 0x36 );
198  ssd1306_intf.send( ( mode ? 0b00100000 : 0b00000000 ) | s_rgb_bit );
199  ssd1306_intf.stop();
200  if (mode == LCD_MODE_SSD1306_COMPAT )
201  {
202  ssd1306_lcd.set_block = il9163_setBlock;
203  ssd1306_lcd.next_page = il9163_nextPage;
204  }
205  else if ( mode == LCD_MODE_NORMAL )
206  {
207  ssd1306_lcd.set_block = il9163_setBlock2;
208  ssd1306_lcd.next_page = il9163_nextPage2;
209  }
210  s_rotation = mode ? 0x00 : 0x04;
211 }
212 
213 static void il9163_sendPixels(uint8_t data)
214 {
215  for (uint8_t i=8; i>0; i--)
216  {
217  if ( data & 0x01 )
218  {
219  ssd1306_intf.send( (uint8_t)(ssd1306_color>>8) );
220  ssd1306_intf.send( (uint8_t)(ssd1306_color) );
221  }
222  else
223  {
224  ssd1306_intf.send( 0B00000000 );
225  ssd1306_intf.send( 0B00000000 );
226  }
227  data >>= 1;
228  }
229 }
230 
231 static void il9163_sendPixelsBuffer(const uint8_t *buffer, uint16_t len)
232 {
233  while(len--)
234  {
235  il9163_sendPixels(*buffer);
236  buffer++;
237  }
238 }
239 
240 static void il9163_sendPixel8(uint8_t data)
241 {
242  uint16_t color = RGB8_TO_RGB16(data);
243  ssd1306_intf.send( color >> 8 );
244  ssd1306_intf.send( color & 0xFF );
245 }
246 
247 static void il9163_sendPixel16(uint16_t color)
248 {
249  ssd1306_intf.send( color >> 8 );
250  ssd1306_intf.send( color & 0xFF );
251 }
252 
254 {
256  ssd1306_lcd.height = 128;
257  ssd1306_lcd.width = 128;
258  s_rgb_bit = 0b00001000; // set BGR mode mapping
259  ssd1306_lcd.set_block = il9163_setBlock;
260  ssd1306_lcd.next_page = il9163_nextPage;
261  ssd1306_lcd.send_pixels1 = il9163_sendPixels;
262  ssd1306_lcd.send_pixels_buffer1 = il9163_sendPixelsBuffer;
263  ssd1306_lcd.send_pixels8 = il9163_sendPixel8;
264  ssd1306_lcd.send_pixels16 = il9163_sendPixel16;
266  ssd1306_configureSpiDisplay(s_oled128x128_initData, sizeof(s_oled128x128_initData));
267 }
268 
269 void il9163_128x128_spi_init(int8_t rstPin, int8_t cesPin, int8_t dcPin)
270 {
271  if (rstPin >=0)
272  {
273  pinMode(rstPin, OUTPUT);
274  digitalWrite(rstPin, HIGH);
275  /* Wait at least 1ms after VCC is up for LCD */
276  delay(1);
277  /* Perform reset operation of LCD display */
278  digitalWrite(rstPin, LOW);
279  delay(20);
280  digitalWrite(rstPin, HIGH);
281  }
282  /* ssd1351 cannot work faster than at 4MHz per datasheet */
283  s_ssd1306_spi_clock = 8000000;
284  ssd1306_spiInit(cesPin, dcPin);
286 }
287 
288 void il9163_setRotation(uint8_t rotation)
289 {
290  uint8_t ram_mode;
291  if ((rotation^s_rotation) & 0x01)
292  {
293  uint16_t t = ssd1306_lcd.width;
295  ssd1306_lcd.height = t;
296  }
297  s_rotation = (rotation & 0x03) | (s_rotation & 0x04);
300  ssd1306_intf.send(0x28);
301  ssd1306_intf.send(0x36);
303  switch (s_rotation)
304  {
305  case 0:
306  ram_mode = 0b00000000;
307  break;
308  case 1: // 90 degree CW
309  ram_mode = 0b01000000;
310  break;
311  case 2: // 180 degree CW
312  ram_mode = 0b11100000;
313  break;
314  case 3: // 270 degree CW
315  ram_mode = 0b10000000;
316  break;
317  case 4:
318  ram_mode = 0b00000000;
319  break;
320  case 5: // 90 degree CW
321  ram_mode = 0b01100000;
322  break;
323  case 6: // 180 degree CW
324  ram_mode = 0b11000000;
325  break;
326  default: // 270 degree CW
327  ram_mode = 0b10100000;
328  break;
329  }
330  s_offset_x = ((s_rotation & 0x03) == 3 ? 32 : 0);
331  s_offset_y = ((s_rotation & 0x03) == 2 ? 32 : 0);
332  ssd1306_intf.send( ram_mode | s_rgb_bit );
334  ssd1306_intf.send(0x29);
335  ssd1306_intf.stop();
336 }
337 
339 // ST7735 support
341 
342 static void st7735_setBlock(lcduint_t x, lcduint_t y, lcduint_t w)
343 {
344  uint8_t rx = w ? (x + w - 1) : (ssd1306_lcd.width - 1);
345  s_column = x;
346  s_page = y;
349  ssd1306_intf.send(0x2B);
350  ssd1306_spiDataMode(1); // According to datasheet all args must be passed in data mode
351  ssd1306_intf.send(0);
352  ssd1306_intf.send(x);
353  ssd1306_intf.send(0);
354  ssd1306_intf.send((rx < ssd1306_lcd.width ? rx : (ssd1306_lcd.width - 1)));
356  ssd1306_intf.send(0x2A);
357  ssd1306_spiDataMode(1); // According to datasheet all args must be passed in data mode
358  ssd1306_intf.send(0);
359  ssd1306_intf.send((y<<3));
360  ssd1306_intf.send(0);
361  ssd1306_intf.send((((y<<3) + 7) < ssd1306_lcd.height ? ((y<<3) + 7) : (ssd1306_lcd.height - 1)));
363  ssd1306_intf.send(0x2C);
365 }
366 
367 static void st7735_setBlock2(lcduint_t x, lcduint_t y, lcduint_t w)
368 {
369  uint8_t rx = w ? (x + w - 1) : (ssd1306_lcd.width - 1);
372  ssd1306_intf.send(0x2A);
373  ssd1306_spiDataMode(1); // According to datasheet all args must be passed in data mode
374  ssd1306_intf.send(0);
375  ssd1306_intf.send(x);
376  ssd1306_intf.send(0);
377  ssd1306_intf.send( rx < ssd1306_lcd.width ? rx : (ssd1306_lcd.width - 1) );
379  ssd1306_intf.send(0x2B);
380  ssd1306_spiDataMode(1); // According to datasheet all args must be passed in data mode
381  ssd1306_intf.send(0);
382  ssd1306_intf.send(y);
383  ssd1306_intf.send(0);
386  ssd1306_intf.send(0x2C);
388 }
389 
390 static void st7735_setMode(lcd_mode_t mode)
391 {
392  if (mode == LCD_MODE_SSD1306_COMPAT )
393  {
394  ssd1306_lcd.set_block = st7735_setBlock;
395  ssd1306_lcd.next_page = il9163_nextPage;
396  }
397  else if ( mode == LCD_MODE_NORMAL )
398  {
399  ssd1306_lcd.set_block = st7735_setBlock2;
400  ssd1306_lcd.next_page = il9163_nextPage2;
401  }
402  s_rotation = mode ? (s_rotation & 0x03) : (s_rotation | 0x04);
403  il9163_setRotation( s_rotation & 0x03 );
404 }
405 
407 {
409  ssd1306_lcd.width = 128;
410  ssd1306_lcd.height = 160;
411  s_rgb_bit = 0b00000000; // set RGB mode mapping
412  ssd1306_lcd.set_block = st7735_setBlock;
413  ssd1306_lcd.next_page = il9163_nextPage;
414  ssd1306_lcd.send_pixels1 = il9163_sendPixels;
415  ssd1306_lcd.send_pixels_buffer1 = il9163_sendPixelsBuffer;
416  ssd1306_lcd.send_pixels8 = il9163_sendPixel8;
417  ssd1306_lcd.send_pixels16 = il9163_sendPixel16;
418  ssd1306_lcd.set_mode = st7735_setMode;
419  ssd1306_configureSpiDisplay2(s_oled128x160_initData, sizeof(s_oled128x160_initData));
420 }
421 
422 void st7735_128x160_spi_init(int8_t rstPin, int8_t cesPin, int8_t dcPin)
423 {
424  if (rstPin >=0)
425  {
426  ssd1306_resetController( rstPin, 20 );
427  /* Give 120ms display to initialize */
428  delay(120);
429  }
430  /* ssd1351 cannot work faster than at 4MHz per datasheet */
431  s_ssd1306_spi_clock = 8000000;
432  ssd1306_spiInit(cesPin, dcPin);
434 }
void st7735_128x160_init(void)
Inits 128x160 RGB OLED display (based on st7735 controller).
Definition: lcd_il9163.c:406
void ssd1306_configureSpiDisplay2(const uint8_t *config, uint8_t configSize)
Sends configuration being passed to lcd display spi controller.
Definition: lcd_common.c:75
void ssd1306_configureSpiDisplay(const uint8_t *config, uint8_t configSize)
Sends configuration being passed to lcd display spi controller.
Definition: lcd_common.c:53
void st7735_128x160_spi_init(int8_t rstPin, int8_t cesPin, int8_t dcPin)
Inits 128x160 RGB TFT display over spi (based on st7735 controller).
Definition: lcd_il9163.c:422
void(* send)(uint8_t data)
uint32_t s_ssd1306_spi_clock
Definition: ssd1306_spi.c:35
void(* send_pixels16)(uint16_t data)
Sends RGB pixel encoded in 5-6-5 format to OLED driver. Sends RGB pixel encoded in 5-6-5 format to OL...
Definition: lcd_common.h:149
void(* set_block)(lcduint_t x, lcduint_t y, lcduint_t w)
Sets block in RAM of lcd display controller to write data to.
Definition: lcd_common.h:114
void(* send_pixels8)(uint8_t data)
Sends RGB pixel encoded in 3-3-2 format to OLED driver. Sends RGB pixel encoded in 3-3-2 format to OL...
Definition: lcd_common.h:142
void ssd1306_spiDataMode(uint8_t mode)
Definition: ssd1306_spi.c:50
lcd_mode_t
Definition: lcd_common.h:69
void(* send_pixels_buffer1)(const uint8_t *buffer, uint16_t len)
Definition: lcd_common.h:135
void ssd1306_resetController(int8_t rstPin, uint8_t delayMs)
Does hardware reset for oled controller.
Definition: lcd_common.c:139
void(* send_pixels1)(uint8_t data)
Definition: lcd_common.h:128
void il9163_setRotation(uint8_t rotation)
Sets screen orientation (rotation)
Definition: lcd_il9163.c:288
void il9163_128x128_init(void)
Inits 128x128 RGB OLED display (based on il9163 controller).
Definition: lcd_il9163.c:253
void ssd1306_spiInit(int8_t cesPin, int8_t dcPin)
Definition: ssd1306_spi.c:37
ssd1306_lcd_t ssd1306_lcd
Definition: lcd_common.c:33
void il9163_setOffset(lcdint_t x, lcdint_t y)
Sets screen offset (refer to datasheet of your display)
Definition: lcd_il9163.c:176
ssd1306_interface_t ssd1306_intf
lcduint_t height
Definition: lcd_common.h:97
void il9163_128x128_spi_init(int8_t rstPin, int8_t cesPin, int8_t dcPin)
Inits 128x128 RGB TFT display over spi (based on il9163 controller).
Definition: lcd_il9163.c:269
#define RGB8_TO_RGB16(c)
void(* next_page)(void)
Definition: lcd_common.h:122
lcduint_t width
Definition: lcd_common.h:94
lcd_type_t type
Definition: lcd_common.h:91
void il9163_setMode(lcd_mode_t mode)
Sets GDRAM autoincrement mode.
Definition: lcd_il9163.c:192
void(* set_mode)(lcd_mode_t mode)
Sets library display mode for direct draw functions.
Definition: lcd_common.h:164