1 /**************************************************************************/
4 @author K. Townsend (microBuilder.eu)
8 Driver for 128x64 OLED display based on the SSD1306 controller.
10 This driver is based on the SSD1306 Library from Limor Fried
11 (Adafruit Industries) at: https://github.com/adafruit/SSD1306
15 Software License Agreement (BSD License)
17 Copyright (c) 2012, microBuilder SARL
20 Redistribution and use in source and binary forms, with or without
21 modification, are permitted provided that the following conditions are met:
22 1. Redistributions of source code must retain the above copyright
23 notice, this list of conditions and the following disclaimer.
24 2. Redistributions in binary form must reproduce the above copyright
25 notice, this list of conditions and the following disclaimer in the
26 documentation and/or other materials provided with the distribution.
27 3. Neither the name of the copyright holders nor the
28 names of its contributors may be used to endorse or promote products
29 derived from this software without specific prior written permission.
31 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
32 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
33 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
34 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
35 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
36 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
38 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
40 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 /**************************************************************************/
47 #include "core/gpio/gpio.h"
48 #include "core/i2c/i2c.h"
49 #include "core/systick/systick.h"
50 #include "drivers/displays/smallfonts.h"
52 #define DELAY(mS) do { systickDelay( mS / CFG_SYSTICK_DELAY_IN_MS ); } while(0);
55 uint8_t _ssd1306buffer
[SSD1306_LCDWIDTH
* SSD1306_LCDHEIGHT
/ 8];
57 #if defined SSD1306_BUS_I2C
58 void ssd1306SendCommand(uint8_t byte
);
59 void ssd1306SendData(uint8_t data
);
61 extern volatile uint8_t I2CMasterBuffer
[I2C_BUFSIZE
];
62 extern volatile uint8_t I2CSlaveBuffer
[I2C_BUFSIZE
];
63 extern volatile uint32_t I2CReadLength
, I2CWriteLength
;
66 #if defined SSD1306_BUS_SPI
67 void ssd1306SendByte(uint8_t byte
);
69 #define CMD(c) do { gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 1 ); \
70 gpioSetValue( SSD1306_DC_PORT, SSD1306_DC_PIN, 0 ); \
71 gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 0 ); \
72 ssd1306SendByte( c ); \
73 gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 1 ); \
75 #define DATA(c) do { gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 1 ); \
76 gpioSetValue( SSD1306_DC_PORT, SSD1306_DC_PIN, 1 ); \
77 gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 0 ); \
78 ssd1306SendByte( c ); \
79 gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 1 ); \
83 /**************************************************************************/
85 /**************************************************************************/
87 #if defined SSD1306_BUS_SPI
88 /**************************************************************************/
90 @brief Simulates an SPI write using GPIO
95 /**************************************************************************/
96 void ssd1306SendByte(uint8_t byte
)
100 // Make sure clock pin starts high
101 gpioSetValue(SSD1306_SCLK_PORT
, SSD1306_SCLK_PIN
, 1);
103 // Write from MSB to LSB
107 gpioSetValue(SSD1306_SCLK_PORT
, SSD1306_SCLK_PIN
, 0);
108 // Set data pin high or low depending on the value of the current bit
109 gpioSetValue(SSD1306_SDAT_PORT
, SSD1306_SDAT_PIN
, byte
& (1 << i
) ? 1 : 0);
110 // Set clock pin high
111 gpioSetValue(SSD1306_SCLK_PORT
, SSD1306_SCLK_PIN
, 1);
116 #if defined SSD1306_BUS_I2C
117 /**************************************************************************/
119 @brief Sends a command via I2C
124 /**************************************************************************/
125 void ssd1306SendCommand(uint8_t byte
)
127 uint8_t control
= 0x00; // Co = 0, D/C = 0
130 // Clear write buffers
132 for ( i
= 0; i
< I2C_BUFSIZE
; i
++ )
134 I2CMasterBuffer
[i
] = 0x00;
137 // Send the specified bytes
140 I2CMasterBuffer
[0] = SSD1306_I2C_ADDRESS
;
141 I2CMasterBuffer
[1] = control
;
142 I2CMasterBuffer
[2] = byte
;
143 i2cState
= i2cEngine();
145 // Check if we got an ACK
146 if ((i2cState
== I2CSTATE_NACK
) || (i2cState
== I2CSTATE_SLA_NACK
))
148 // I2C slave didn't acknowledge the master transfer
149 // The PN532 probably isn't connected properly or the
150 // bus select pins are in the wrong state
151 // return PN532_ERROR_NACK;
154 // return PN532_ERROR_NONE;
157 /**************************************************************************/
159 @brief Sends data via I2C
164 /**************************************************************************/
165 void ssd1306SendData(uint8_t data
)
167 uint8_t control
= 0x40; // Co = 0, D/C = 1
170 // Clear write buffers
172 for ( i
= 0; i
< I2C_BUFSIZE
; i
++ )
174 I2CMasterBuffer
[i
] = 0x00;
177 // Send the specified bytes
180 I2CMasterBuffer
[0] = SSD1306_I2C_ADDRESS
;
181 I2CMasterBuffer
[1] = control
;
182 I2CMasterBuffer
[2] = data
;
183 i2cState
= i2cEngine();
185 // Check if we got an ACK
186 if ((i2cState
== I2CSTATE_NACK
) || (i2cState
== I2CSTATE_SLA_NACK
))
188 // I2C slave didn't acknowledge the master transfer
189 // The PN532 probably isn't connected properly or the
190 // bus select pins are in the wrong state
191 // return PN532_ERROR_NACK;
194 // return PN532_ERROR_NONE;
198 /**************************************************************************/
200 @brief Draws a single graphic character using the supplied font
202 /**************************************************************************/
203 static void ssd1306DrawChar(uint16_t x
, uint16_t y
, uint8_t c
, struct FONT_DEF font
)
205 uint8_t col
, column
[font
.u8Width
];
207 // Make sure we are exceeding the display limits
208 // This also gets checked in ssd1306DrawPixel, but if we start
209 // outside the limits we can avoid some unecessary work at the outset
210 if ((x
> SSD1306_LCDWIDTH
) || (y
> SSD1306_LCDHEIGHT
))
213 // Check if the requested character is available
214 if ((c
>= font
.u8FirstChar
) && (c
<= font
.u8LastChar
))
216 // Retrieve appropriate columns from font data
217 for (col
= 0; col
< font
.u8Width
; col
++)
219 column
[col
] = font
.au8FontTable
[((c
- font
.u8FirstChar
) * font
.u8Width
) + col
]; // Get first column of appropriate character
224 // Requested character is not available in this font ... send a space instead
225 for (col
= 0; col
< font
.u8Width
; col
++)
227 column
[col
] = 0xFF; // Send solid space
231 // Render each column
232 uint16_t xoffset
, yoffset
;
233 for (xoffset
= 0; xoffset
< font
.u8Width
; xoffset
++)
235 for (yoffset
= 0; yoffset
< (font
.u8Height
+ 1); yoffset
++)
238 bit
= (column
[xoffset
] << (8 - (yoffset
+ 1))); // Shift current row bit left
239 bit
= (bit
>> 7); // Shift current row but right (results in 0x01 for black, and 0x00 for white)
242 ssd1306DrawPixel(x
+ xoffset
, y
+ yoffset
);
248 /**************************************************************************/
250 /**************************************************************************/
252 /**************************************************************************/
254 @brief Initialises the SSD1306 LCD display
256 /**************************************************************************/
257 void ssd1306Init(uint8_t vccstate
)
259 // SPI Initialisation
260 #if defined SSD1306_BUS_SPI
261 // Set all pins to output
262 gpioSetDir(SSD1306_SCLK_PORT
, SSD1306_SCLK_PIN
, gpioDirection_Output
);
263 gpioSetDir(SSD1306_SDAT_PORT
, SSD1306_SDAT_PIN
, gpioDirection_Output
);
264 gpioSetDir(SSD1306_DC_PORT
, SSD1306_DC_PIN
, gpioDirection_Output
);
265 gpioSetDir(SSD1306_RST_PORT
, SSD1306_RST_PIN
, gpioDirection_Output
);
266 gpioSetDir(SSD1306_CS_PORT
, SSD1306_CS_PIN
, gpioDirection_Output
);
269 gpioSetValue(SSD1306_RST_PORT
, SSD1306_RST_PIN
, 1);
271 gpioSetValue(SSD1306_RST_PORT
, SSD1306_RST_PIN
, 0);
273 gpioSetValue(SSD1306_RST_PORT
, SSD1306_RST_PIN
, 1);
275 #if defined SSD1306_128_32
276 // Init sequence taken from datasheet for UG-2832HSWEG04 (128x32 OLED module)
277 CMD(SSD1306_DISPLAYOFF
); // 0xAE
278 CMD(SSD1306_SETDISPLAYCLOCKDIV
); // 0xD5
279 CMD(0x80); // the suggested ratio 0x80
280 CMD(SSD1306_SETMULTIPLEX
); // 0xA8
282 CMD(SSD1306_SETDISPLAYOFFSET
); // 0xD3
283 CMD(0x0); // no offset
284 CMD(SSD1306_SETSTARTLINE
| 0x0); // line #0
285 CMD(SSD1306_CHARGEPUMP
); // 0x8D
286 if (vccstate
== SSD1306_EXTERNALVCC
)
290 CMD(SSD1306_MEMORYMODE
); // 0x20
291 CMD(0x00); // 0x0 act like ks0108
292 CMD(SSD1306_SEGREMAP
| 0x1);
293 CMD(SSD1306_COMSCANDEC
);
294 CMD(SSD1306_SETCOMPINS
); // 0xDA
296 CMD(SSD1306_SETCONTRAST
); // 0x81
297 if (vccstate
== SSD1306_EXTERNALVCC
)
301 CMD(SSD1306_SETPRECHARGE
); // 0xd9
302 if (vccstate
== SSD1306_EXTERNALVCC
)
306 CMD(SSD1306_SETVCOMDETECT
); // 0xDB
308 CMD(SSD1306_DISPLAYALLON_RESUME
); // 0xA4
309 CMD(SSD1306_NORMALDISPLAY
); // 0xA6
312 #if defined SSD1306_128_64
313 // Init sequence taken from datasheet for UG-2864HSWEG01 (128x64 OLED module)
314 CMD(SSD1306_DISPLAYOFF
); // 0xAE
315 CMD(SSD1306_SETDISPLAYCLOCKDIV
); // 0xD5
316 CMD(0x80); // the suggested ratio 0x80
317 CMD(SSD1306_SETMULTIPLEX
); // 0xA8
319 CMD(SSD1306_SETDISPLAYOFFSET
); // 0xD3
320 CMD(0x0); // no offset
321 CMD(SSD1306_SETSTARTLINE
| 0x0); // line #0
322 CMD(SSD1306_CHARGEPUMP
); // 0x8D
323 if (vccstate
== SSD1306_EXTERNALVCC
)
327 CMD(SSD1306_MEMORYMODE
); // 0x20
328 CMD(0x00); // 0x0 act like ks0108
329 CMD(SSD1306_SEGREMAP
| 0x1);
330 CMD(SSD1306_COMSCANDEC
);
331 CMD(SSD1306_SETCOMPINS
); // 0xDA
333 CMD(SSD1306_SETCONTRAST
); // 0x81
334 if (vccstate
== SSD1306_EXTERNALVCC
)
338 CMD(SSD1306_SETPRECHARGE
); // 0xd9
339 if (vccstate
== SSD1306_EXTERNALVCC
)
343 CMD(SSD1306_SETVCOMDETECT
); // 0xDB
345 CMD(SSD1306_DISPLAYALLON_RESUME
); // 0xA4
346 CMD(SSD1306_NORMALDISPLAY
); // 0xA6
348 // Enabled the OLED panel
349 CMD(SSD1306_DISPLAYON
);
352 // I2C Initialisation
353 #if defined SSD1306_BUS_I2C
354 #if defined SSD1306_128_32
355 // Init sequence taken from datasheet for UG-2832HSWEG04 (128x32 OLED module)
356 ssd1306SendCommand(SSD1306_DISPLAYOFF
); // 0xAE
357 ssd1306SendCommand(SSD1306_SETDISPLAYCLOCKDIV
); // 0xD5
358 ssd1306SendCommand(0x80); // the suggested ratio 0x80
359 ssd1306SendCommand(SSD1306_SETMULTIPLEX
); // 0xA8
360 ssd1306SendCommand(0x1F); // 31
361 ssd1306SendCommand(SSD1306_SETDISPLAYOFFSET
); // 0xD3
362 ssd1306SendCommand(0x0); // no offset
363 ssd1306SendCommand(SSD1306_SETSTARTLINE
| 0x0); // line #0
364 ssd1306SendCommand(SSD1306_CHARGEPUMP
); // 0x8D
365 if (vccstate
== SSD1306_EXTERNALVCC
)
366 { ssd1306SendCommand(0x10); }
368 { ssd1306SendCommand(0x14); }
369 ssd1306SendCommand(SSD1306_MEMORYMODE
); // 0x20
370 ssd1306SendCommand(0x00); // 0x0 act like ks0108
371 ssd1306SendCommand(SSD1306_SEGREMAP
| 0x1);
372 ssd1306SendCommand(SSD1306_COMSCANDEC
);
373 ssd1306SendCommand(SSD1306_SETCOMPINS
); // 0xDA
374 ssd1306SendCommand(0x02);
375 ssd1306SendCommand(SSD1306_SETCONTRAST
); // 0x81
376 if (vccstate
== SSD1306_EXTERNALVCC
)
377 { ssd1306SendCommand(0x9F); }
379 { ssd1306SendCommand(0xCF); }
380 ssd1306SendCommand(SSD1306_SETPRECHARGE
); // 0xd9
381 if (vccstate
== SSD1306_EXTERNALVCC
)
382 { ssd1306SendCommand(0x22); }
384 { ssd1306SendCommand(0xF1); }
385 ssd1306SendCommand(SSD1306_SETVCOMDETECT
); // 0xDB
386 ssd1306SendCommand(0x40);
387 ssd1306SendCommand(SSD1306_DISPLAYALLON_RESUME
); // 0xA4
388 ssd1306SendCommand(SSD1306_NORMALDISPLAY
); // 0xA6
391 #if defined SSD1306_128_64
392 // Init sequence taken from datasheet for UG-2864HSWEG01 (128x64 OLED module)
393 ssd1306SendCommand(SSD1306_DISPLAYOFF
); // 0xAE
394 ssd1306SendCommand(SSD1306_SETDISPLAYCLOCKDIV
); // 0xD5
395 ssd1306SendCommand(0x80); // the suggested ratio 0x80
396 ssd1306SendCommand(SSD1306_SETMULTIPLEX
); // 0xA8
397 ssd1306SendCommand(0x3F); // 63
398 ssd1306SendCommand(SSD1306_SETDISPLAYOFFSET
); // 0xD3
399 ssd1306SendCommand(0x0); // no offset
400 ssd1306SendCommand(SSD1306_SETSTARTLINE
| 0x0); // line #0
401 ssd1306SendCommand(SSD1306_CHARGEPUMP
); // 0x8D
402 if (vccstate
== SSD1306_EXTERNALVCC
)
403 { ssd1306SendCommand(0x10); }
405 { ssd1306SendCommand(0x14); }
406 ssd1306SendCommand(SSD1306_MEMORYMODE
); // 0x20
407 ssd1306SendCommand(0x00); // 0x0 act like ks0108
408 ssd1306SendCommand(SSD1306_SEGREMAP
| 0x1);
409 ssd1306SendCommand(SSD1306_COMSCANDEC
);
410 ssd1306SendCommand(SSD1306_SETCOMPINS
); // 0xDA
411 ssd1306SendCommand(0x12);
412 ssd1306SendCommand(SSD1306_SETCONTRAST
); // 0x81
413 if (vccstate
== SSD1306_EXTERNALVCC
)
414 { ssd1306SendCommand(0x9F); }
416 { ssd1306SendCommand(0xCF); }
417 ssd1306SendCommand(SSD1306_SETPRECHARGE
); // 0xd9
418 if (vccstate
== SSD1306_EXTERNALVCC
)
419 { ssd1306SendCommand(0x22); }
421 { ssd1306SendCommand(0xF1); }
422 ssd1306SendCommand(SSD1306_SETVCOMDETECT
); // 0xDB
423 ssd1306SendCommand(0x40);
424 ssd1306SendCommand(SSD1306_DISPLAYALLON_RESUME
); // 0xA4
425 ssd1306SendCommand(SSD1306_NORMALDISPLAY
); // 0xA6
427 // Enable the OLED panel
428 ssd1306SendCommand(SSD1306_DISPLAYON
);
432 /**************************************************************************/
434 @brief Draws a single pixel in image buffer
437 The x position (0..127)
439 The y position (0..63)
441 /**************************************************************************/
442 void ssd1306DrawPixel(uint8_t x
, uint8_t y
)
444 if ((x
>= SSD1306_LCDWIDTH
) || (y
>= SSD1306_LCDHEIGHT
))
447 _ssd1306buffer
[x
+ (y
/8)*SSD1306_LCDWIDTH
] |= (1 << y
%8);
450 /**************************************************************************/
452 @brief Clears a single pixel in image buffer
455 The x position (0..127)
457 The y position (0..63)
459 /**************************************************************************/
460 void ssd1306ClearPixel(uint8_t x
, uint8_t y
)
462 if ((x
>= SSD1306_LCDWIDTH
) || (y
>= SSD1306_LCDHEIGHT
))
465 _ssd1306buffer
[x
+ (y
/8)*SSD1306_LCDWIDTH
] &= ~(1 << y
%8);
468 /**************************************************************************/
470 @brief Gets the value (1 or 0) of the specified pixel from the buffer
473 The x position (0..127)
475 The y position (0..63)
477 @return 1 if the pixel is enabled, 0 if disabled
479 /**************************************************************************/
480 uint8_t ssd1306GetPixel(uint8_t x
, uint8_t y
)
482 if ((x
>= SSD1306_LCDWIDTH
) || (y
>=SSD1306_LCDHEIGHT
)) return 0;
483 return _ssd1306buffer
[x
+ (y
/8)*SSD1306_LCDWIDTH
] & (1 << y
%8) ? 1 : 0;
486 /**************************************************************************/
488 @brief Clears the screen
490 /**************************************************************************/
491 void ssd1306ClearScreen()
493 memset(_ssd1306buffer
, 0x00, sizeof(_ssd1306buffer
));
496 /**************************************************************************/
498 @brief Renders the contents of the pixel buffer on the LCD
500 /**************************************************************************/
501 void ssd1306Refresh(void)
503 #if defined SSD1306_BUS_SPI
504 CMD(SSD1306_SETLOWCOLUMN
| 0x0); // low col = 0
505 CMD(SSD1306_SETHIGHCOLUMN
| 0x0); // hi col = 0
506 CMD(SSD1306_SETSTARTLINE
| 0x0); // line #0
509 for (i
= 0; i
< sizeof(_ssd1306buffer
); i
++)
511 DATA(_ssd1306buffer
[i
]);
515 #if defined SSD1306_BUS_I2C
516 ssd1306SendCommand(SSD1306_SETLOWCOLUMN
| 0x0); // low col = 0
517 ssd1306SendCommand(SSD1306_SETHIGHCOLUMN
| 0x0); // hi col = 0
518 ssd1306SendCommand(SSD1306_SETSTARTLINE
| 0x0); // line #0
521 for (i
= 0; i
< sizeof(_ssd1306buffer
); i
++)
523 ssd1306SendData(_ssd1306buffer
[i
]);
528 /**************************************************************************/
530 @brief Draws a string using the supplied font data.
533 Starting x co-ordinate
535 Starting y co-ordinate
539 Pointer to the FONT_DEF to use when drawing the string
545 #include "drivers/displays/bitmap/ssd1306/ssd1306.h"
546 #include "drivers/displays/smallfonts.h"
548 // Configure the pins and initialise the LCD screen
549 ssd1306Init(SSD1306_INTERNALVCC);
551 // Render some text on the screen
552 ssd1306DrawString(1, 10, "5x8 System", Font_System5x8);
553 ssd1306DrawString(1, 20, "7x8 System", Font_System7x8);
555 // Refresh the screen to see the results
560 /**************************************************************************/
561 void ssd1306DrawString(uint16_t x
, uint16_t y
, char* text
, struct FONT_DEF font
)
564 for (l
= 0; l
< strlen(text
); l
++)
566 ssd1306DrawChar(x
+ (l
* (font
.u8Width
+ 1)), y
, text
[l
], font
);
570 /**************************************************************************/
572 @brief Shifts the contents of the frame buffer up the specified
576 The number of pixels to shift the frame buffer up, leaving
577 a blank space at the bottom of the frame buffer x pixels
584 #include "drivers/displays/bitmap/ssd1306/ssd1306.h"
585 #include "drivers/displays/smallfonts.h"
587 // Configure the pins and initialise the LCD screen
588 ssd1306Init(SSD1306_INTERNALVCC);
590 // Continually write some text, scrolling upward one line each time
593 // Shift the buffer up 8 pixels (adjust for font-height)
594 ssd1306ShiftFrameBuffer(8);
595 // Render some text on the screen with different fonts
596 ssd1306DrawString(1, 56, "INSERT TEXT HERE", Font_System5x8);
597 // Refresh the screen to see the results
599 // Wait a bit before writing the next line
605 /**************************************************************************/
606 void ssd1306ShiftFrameBuffer( uint8_t height
)
608 if (height
== 0) return;
609 if (height
>= SSD1306_LCDHEIGHT
)
611 // Clear the entire frame buffer
612 ssd1306ClearScreen();
616 // This is horribly inefficient, but at least easy to understand
617 // In a production environment, this should be significantly optimised
620 for (y
= 0; y
< SSD1306_LCDHEIGHT
; y
++)
622 for (x
= 0; x
< SSD1306_LCDWIDTH
; x
++)
624 if ((SSD1306_LCDHEIGHT
- 1) - y
> height
)
626 // Shift height from further ahead in the buffer
627 ssd1306GetPixel(x
, y
+ height
) ? ssd1306DrawPixel(x
, y
) : ssd1306ClearPixel(x
, y
);
631 // Clear the entire line
632 ssd1306ClearPixel(x
, y
);