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