SSD1306 OLED display driver  1.7.17
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 
37 extern uint16_t ssd1306_color;
38 extern uint32_t s_ssd1306_spi_clock;
39 
40 static uint8_t s_rotation = 0x00;
41 static uint8_t s_rgb_bit = 0b00001000;
42 
43 static const PROGMEM uint8_t s_oled128x128_initData[] =
44 {
45 #ifdef SDL_EMULATION
46  SDL_LCD_IL9163,
47  0x00,
48 #endif
49 // 0x01, // sw reset. not needed, we do hardware reset
50  0x11, // exit sleep mode
51  0x3A, CMD_ARG, 0x05, // set 16-bit pixel format
52  0x26, CMD_ARG, 0x04, // set gamma curve: valid values 1, 2, 4, 8
53 // 0xF2, CMD_ARG, 0x01, // enable gamma adjustment, 0 - to disable
54 // 0xE0, CMD_ARG, 0x3F, CMD_ARG, 0x25, CMD_ARG, 0x1C,
55 // CMD_ARG, 0x1E, CMD_ARG, 0x20, CMD_ARG, 0x12,
56 // CMD_ARG, 0x2A, CMD_ARG, 0x90, CMD_ARG, 0x24,
57 // CMD_ARG, 0x11, CMD_ARG, 0x00, CMD_ARG, 0x00,
58 // CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x00, // positive gamma correction
59 // 0xE1, CMD_ARG, 0x20, CMD_ARG, 0x20, CMD_ARG, 0x20,
60 // CMD_ARG, 0x20, CMD_ARG, 0x05, CMD_ARG, 0x00,
61 // CMD_ARG, 0x15, CMD_ARG, 0xA7, CMD_ARG, 0x3D,
62 // CMD_ARG, 0x18, CMD_ARG, 0x25, CMD_ARG, 0x2A,
63 // CMD_ARG, 0x2B, CMD_ARG, 0x2B, CMD_ARG, 0x3A, // negative gamma correction
64 // 0xB1, CMD_ARG, 0x08, CMD_ARG, 0x08, // frame rate control 1, use by default
65 // 0xB4, CMD_ARG, 0x07, // display inversion, use by default
66  0xC0, CMD_ARG, 0x0A, CMD_ARG, 0x02, // power control 1
67  0xC1, CMD_ARG, 0x02, // power control 2
68  0xC5, CMD_ARG, 0x50, CMD_ARG, 0x5B, // vcom control 1
69  0xC7, CMD_ARG, 0x40, // vcom offset
70 // 0x2A, CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x7F, // set column address, not needed. set by direct API
71 // 0x2B, CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x9F, // set page address, not needed. set by direct API
72  0x36, CMD_ARG, 0b10001100, // enable fake "vertical addressing" mode (for il9163_setBlock() )
73  0x29, // display on
74 };
75 
76 static const PROGMEM uint8_t s_oled128x160_initData[] =
77 {
78 #ifdef SDL_EMULATION
79  SDL_LCD_ST7735,
80  0x00,
81 #endif
82 // 0x01, // sw reset. not needed, we do hardware reset
83  0x11, // exit sleep mode
84 // 0x28, // display off
85  0x3A, CMD_ARG, 0x05, // set 16-bit pixel format
86  0x26, CMD_ARG, 0x04, // set gamma curve: valid values 1, 2, 4, 8
87  0xF2, CMD_ARG, 0x01, // enable gamma adjustment, 0 - to disable
88  0xE0, CMD_ARG, 0x3F, CMD_ARG, 0x25, CMD_ARG, 0x1C,
89  CMD_ARG, 0x1E, CMD_ARG, 0x20, CMD_ARG, 0x12,
90  CMD_ARG, 0x2A, CMD_ARG, 0x90, CMD_ARG, 0x24,
91  CMD_ARG, 0x11, CMD_ARG, 0x00, CMD_ARG, 0x00,
92  CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x00, // positive gamma correction
93  0xE1, CMD_ARG, 0x20, CMD_ARG, 0x20, CMD_ARG, 0x20,
94  CMD_ARG, 0x20, CMD_ARG, 0x05, CMD_ARG, 0x00,
95  CMD_ARG, 0x15, CMD_ARG, 0xA7, CMD_ARG, 0x3D,
96  CMD_ARG, 0x18, CMD_ARG, 0x25, CMD_ARG, 0x2A,
97  CMD_ARG, 0x2B, CMD_ARG, 0x2B, CMD_ARG, 0x3A, // negative gamma correction
98  0xB1, CMD_ARG, 0x08, CMD_ARG, 0x08, // frame rate control 1, use by default
99  0xB4, CMD_ARG, 0x07, // display inversion, use by default
100  0xC0, CMD_ARG, 0x0A, CMD_ARG, 0x02, // power control 1
101  0xC1, CMD_ARG, 0x02, // power control 2
102  0xC5, CMD_ARG, 0x50, CMD_ARG, 0x5B, // vcom control 1
103  0xC7, CMD_ARG, 0x40, // vcom offset
104 // 0x2A, CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x7F, // set column address, not needed. set by direct API
105 // 0x2B, CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x00, CMD_ARG, 0x9F, // set page address, not needed. set by direct API
106  0x36, CMD_ARG, 0b00100000, // enable fake "vertical addressing" mode (for il9163_setBlock() )
107  0x29, // display on
108 };
109 
110 static uint8_t s_column;
111 static uint8_t s_page;
112 
113 static void il9163_setBlock(lcduint_t x, lcduint_t y, lcduint_t w)
114 {
115  uint8_t rx = w ? (x + w - 1) : (ssd1306_lcd.width - 1);
116  s_column = x;
117  s_page = y;
120  ssd1306_intf.send(0x2B);
121  ssd1306_spiDataMode(1); // According to datasheet all args must be passed in data mode
122  ssd1306_intf.send(0);
123  ssd1306_intf.send(x + (s_rotation == 3 ? 32 : 0));
124  ssd1306_intf.send(0);
125  ssd1306_intf.send((rx < ssd1306_lcd.width ? rx : (ssd1306_lcd.width - 1))
126  + (s_rotation == 3 ? 32 : 0));
128  ssd1306_intf.send(0x2A);
129  ssd1306_spiDataMode(1); // According to datasheet all args must be passed in data mode
130  ssd1306_intf.send(0);
131  ssd1306_intf.send((y<<3) + (s_rotation == 2 ? 32: 0));
132  ssd1306_intf.send(0);
133  ssd1306_intf.send((((y<<3) + 7) < ssd1306_lcd.height ? ((y<<3) + 7) : (ssd1306_lcd.height - 1))
134  + (s_rotation == 2 ? 32: 0));
136  ssd1306_intf.send(0x2C);
138 }
139 
140 static void il9163_setBlock2(lcduint_t x, lcduint_t y, lcduint_t w)
141 {
142  uint8_t rx = w ? (x + w - 1) : (ssd1306_lcd.width - 1);
145  ssd1306_intf.send(0x2A);
146  ssd1306_spiDataMode(1); // According to datasheet all args must be passed in data mode
147  ssd1306_intf.send(0);
148  ssd1306_intf.send(x + (s_rotation == 7 ? 32 : 0));
149  ssd1306_intf.send(0);
150  ssd1306_intf.send((rx < ssd1306_lcd.width ? rx : (ssd1306_lcd.width - 1))
151  + (s_rotation == 7 ? 32 : 0));
153  ssd1306_intf.send(0x2B);
154  ssd1306_spiDataMode(1); // According to datasheet all args must be passed in data mode
155  ssd1306_intf.send(0);
156  ssd1306_intf.send(y + (s_rotation == 6 ? 32: 0));
157  ssd1306_intf.send(0);
158  ssd1306_intf.send(ssd1306_lcd.height - 1 + (s_rotation == 6 ? 32: 0));
160  ssd1306_intf.send(0x2C);
162 }
163 
164 static void il9163_nextPage(void)
165 {
166  ssd1306_intf.stop();
167  ssd1306_lcd.set_block(s_column,s_page+1,0);
168 }
169 
170 static void il9163_nextPage2(void)
171 {
172 }
173 
175 {
178  ssd1306_intf.send( 0x36 );
180  ssd1306_intf.send( mode ? 0b00101000 : 0b00001000 );
181  ssd1306_intf.stop();
182  if (mode == LCD_MODE_SSD1306_COMPAT )
183  {
184  ssd1306_lcd.set_block = il9163_setBlock;
185  ssd1306_lcd.next_page = il9163_nextPage;
186  }
187  else if ( mode == LCD_MODE_NORMAL )
188  {
189  ssd1306_lcd.set_block = il9163_setBlock2;
190  ssd1306_lcd.next_page = il9163_nextPage2;
191  }
192  s_rotation = mode ? 0x00 : 0x04;
193 }
194 
195 static void il9163_sendPixels(uint8_t data)
196 {
197  for (uint8_t i=8; i>0; i--)
198  {
199  if ( data & 0x01 )
200  {
201  ssd1306_intf.send( (uint8_t)(ssd1306_color>>8) );
202  ssd1306_intf.send( (uint8_t)(ssd1306_color) );
203  }
204  else
205  {
206  ssd1306_intf.send( 0B00000000 );
207  ssd1306_intf.send( 0B00000000 );
208  }
209  data >>= 1;
210  }
211 }
212 
213 static void il9163_sendPixelsBuffer(const uint8_t *buffer, uint16_t len)
214 {
215  while(len--)
216  {
217  il9163_sendPixels(*buffer);
218  buffer++;
219  }
220 }
221 
222 static void il9163_sendPixel8(uint8_t data)
223 {
224  uint16_t color = RGB8_TO_RGB16(data);
225  ssd1306_intf.send( color >> 8 );
226  ssd1306_intf.send( color & 0xFF );
227 }
228 
229 static void il9163_sendPixel16(uint16_t color)
230 {
231  ssd1306_intf.send( color >> 8 );
232  ssd1306_intf.send( color & 0xFF );
233 }
234 
236 {
238  ssd1306_lcd.height = 128;
239  ssd1306_lcd.width = 128;
240  s_rgb_bit = 0b00001000; // set BGR mode mapping
241  ssd1306_lcd.set_block = il9163_setBlock;
242  ssd1306_lcd.next_page = il9163_nextPage;
243  ssd1306_lcd.send_pixels1 = il9163_sendPixels;
244  ssd1306_lcd.send_pixels_buffer1 = il9163_sendPixelsBuffer;
245  ssd1306_lcd.send_pixels8 = il9163_sendPixel8;
246  ssd1306_lcd.send_pixels16 = il9163_sendPixel16;
248  ssd1306_configureSpiDisplay(s_oled128x128_initData, sizeof(s_oled128x128_initData));
249 }
250 
251 void il9163_128x128_spi_init(int8_t rstPin, int8_t cesPin, int8_t dcPin)
252 {
253  if (rstPin >=0)
254  {
255  pinMode(rstPin, OUTPUT);
256  digitalWrite(rstPin, HIGH);
257  /* Wait at least 1ms after VCC is up for LCD */
258  delay(1);
259  /* Perform reset operation of LCD display */
260  digitalWrite(rstPin, LOW);
261  delay(20);
262  digitalWrite(rstPin, HIGH);
263  }
264  /* ssd1351 cannot work faster than at 4MHz per datasheet */
265  s_ssd1306_spi_clock = 8000000;
266  ssd1306_spiInit(cesPin, dcPin);
268 }
269 
270 void il9163_setRotation(uint8_t rotation)
271 {
272  uint8_t ram_mode;
273  if ((rotation^s_rotation) & 0x01)
274  {
275  uint16_t t = ssd1306_lcd.width;
277  ssd1306_lcd.height = t;
278  }
279  s_rotation = (rotation & 0x03) | (s_rotation & 0x04);
282  ssd1306_intf.send(0x28);
283  ssd1306_intf.send(0x36);
285  switch (s_rotation)
286  {
287  case 0:
288  ram_mode = 0b00000000;
289  break;
290  case 1: // 90 degree CW
291  ram_mode = 0b01000000;
292  break;
293  case 2: // 180 degree CW
294  ram_mode = 0b11100000;
295  break;
296  case 3: // 270 degree CW
297  ram_mode = 0b10000000;
298  break;
299  case 4:
300  ram_mode = 0b00000000;
301  break;
302  case 5: // 90 degree CW
303  ram_mode = 0b01100000;
304  break;
305  case 6: // 180 degree CW
306  ram_mode = 0b11000000;
307  break;
308  default: // 270 degree CW
309  ram_mode = 0b10100000;
310  break;
311  }
312  ssd1306_intf.send( ram_mode | s_rgb_bit );
314  ssd1306_intf.send(0x29);
315  ssd1306_intf.stop();
316 }
317 
319 // ST7735 support
321 
322 static void st7735_setBlock(lcduint_t x, lcduint_t y, lcduint_t w)
323 {
324  uint8_t rx = w ? (x + w - 1) : (ssd1306_lcd.width - 1);
325  s_column = x;
326  s_page = y;
329  ssd1306_intf.send(0x2B);
330  ssd1306_spiDataMode(1); // According to datasheet all args must be passed in data mode
331  ssd1306_intf.send(0);
332  ssd1306_intf.send(x);
333  ssd1306_intf.send(0);
334  ssd1306_intf.send((rx < ssd1306_lcd.width ? rx : (ssd1306_lcd.width - 1)));
336  ssd1306_intf.send(0x2A);
337  ssd1306_spiDataMode(1); // According to datasheet all args must be passed in data mode
338  ssd1306_intf.send(0);
339  ssd1306_intf.send((y<<3));
340  ssd1306_intf.send(0);
341  ssd1306_intf.send((((y<<3) + 7) < ssd1306_lcd.height ? ((y<<3) + 7) : (ssd1306_lcd.height - 1)));
343  ssd1306_intf.send(0x2C);
345 }
346 
347 #if 0
348 static void st7735_setBlock2(lcduint_t x, lcduint_t y, lcduint_t w)
349 {
350  uint8_t rx = w ? (x + w - 1) : (ssd1306_lcd.width - 1);
353  ssd1306_intf.send(0x2A);
354  ssd1306_spiDataMode(1); // According to datasheet all args must be passed in data mode
355  ssd1306_intf.send(0);
356  ssd1306_intf.send(x);
357  ssd1306_intf.send(0);
358  ssd1306_intf.send((rx < ssd1306_lcd.width ? rx : (ssd1306_lcd.width - 1)));
360  ssd1306_intf.send(0x2B);
361  ssd1306_spiDataMode(1); // According to datasheet all args must be passed in data mode
362  ssd1306_intf.send(0);
363  ssd1306_intf.send(y);
364  ssd1306_intf.send(0);
367  ssd1306_intf.send(0x2C);
369 }
370 #endif
371 
373 {
375  ssd1306_lcd.width = 128;
376  ssd1306_lcd.height = 160;
377  s_rgb_bit = 0b00000000; // set RGB mode mapping
378  ssd1306_lcd.set_block = st7735_setBlock;
379  ssd1306_lcd.next_page = il9163_nextPage;
380  ssd1306_lcd.send_pixels1 = il9163_sendPixels;
381  ssd1306_lcd.send_pixels_buffer1 = il9163_sendPixelsBuffer;
382  ssd1306_lcd.send_pixels8 = il9163_sendPixel8;
383  ssd1306_lcd.send_pixels16 = il9163_sendPixel16;
385  ssd1306_configureSpiDisplay(s_oled128x160_initData, sizeof(s_oled128x160_initData));
386 }
387 
388 void st7735_128x160_spi_init(int8_t rstPin, int8_t cesPin, int8_t dcPin)
389 {
390  if (rstPin >=0)
391  {
392  ssd1306_resetController( rstPin, 20 );
393  /* Give 120ms display to initialize */
394  delay(120);
395  }
396  /* ssd1351 cannot work faster than at 4MHz per datasheet */
397  s_ssd1306_spi_clock = 8000000;
398  ssd1306_spiInit(cesPin, dcPin);
400 }
void st7735_128x160_init(void)
Inits 128x160 RGB OLED display (based on st7735 controller).
Definition: lcd_il9163.c:372
void ssd1306_configureSpiDisplay(const uint8_t *config, uint8_t configSize)
Sends configuration being passed to lcd display spi controller.
Definition: lcd_common.c:52
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:388
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:82
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:270
void il9163_128x128_init(void)
Inits 128x128 RGB OLED display (based on il9163 controller).
Definition: lcd_il9163.c:235
void ssd1306_spiInit(int8_t cesPin, int8_t dcPin)
Definition: ssd1306_spi.c:37
ssd1306_lcd_t ssd1306_lcd
Definition: lcd_common.c:32
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:251
#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:174
void(* set_mode)(lcd_mode_t mode)
Sets library display mode for direct draw functions.
Definition: lcd_common.h:164