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/systick/systick.h"
49 #include "drivers/lcd/smallfonts.h"
51 void ssd1306SendByte(uint8_t byte
);
53 #define CMD(c) do { gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 1 ); \
54 gpioSetValue( SSD1306_DC_PORT, SSD1306_DC_PIN, 0 ); \
55 gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 0 ); \
56 ssd1306SendByte( c ); \
57 gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 1 ); \
59 #define DATA(c) do { gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 1 ); \
60 gpioSetValue( SSD1306_DC_PORT, SSD1306_DC_PIN, 1 ); \
61 gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 0 ); \
62 ssd1306SendByte( c ); \
63 gpioSetValue( SSD1306_CS_PORT, SSD1306_CS_PIN, 1 ); \
65 #define DELAY(mS) do { systickDelay( mS / CFG_SYSTICK_DELAY_IN_MS ); } while(0);
67 uint8_t _ssd1306buffer
[SSD1306_LCDWIDTH
* SSD1306_LCDHEIGHT
/ 8];
69 /**************************************************************************/
71 /**************************************************************************/
73 /**************************************************************************/
75 @brief Simulates an SPI write using GPIO
80 /**************************************************************************/
81 void ssd1306SendByte(uint8_t byte
)
85 // Make sure clock pin starts high
86 gpioSetValue(SSD1306_SCLK_PORT
, SSD1306_SCLK_PIN
, 1);
88 // Write from MSB to LSB
92 gpioSetValue(SSD1306_SCLK_PORT
, SSD1306_SCLK_PIN
, 0);
93 // Set data pin high or low depending on the value of the current bit
94 gpioSetValue(SSD1306_SDAT_PORT
, SSD1306_SDAT_PIN
, byte
& (1 << i
) ? 1 : 0);
96 gpioSetValue(SSD1306_SCLK_PORT
, SSD1306_SCLK_PIN
, 1);
100 /**************************************************************************/
102 @brief Draws a single graphic character using the supplied font
104 /**************************************************************************/
105 static void ssd1306DrawChar(uint16_t x
, uint16_t y
, uint8_t c
, struct FONT_DEF font
)
107 uint8_t col
, column
[font
.u8Width
];
109 // Make sure we are exceeding the display limits
110 // This also gets checked in ssd1306DrawPixel, but if we start
111 // outside the limits we can avoid some unecessary work at the outset
112 if ((x
> SSD1306_LCDWIDTH
) || (y
> SSD1306_LCDHEIGHT
))
115 // Check if the requested character is available
116 if ((c
>= font
.u8FirstChar
) && (c
<= font
.u8LastChar
))
118 // Retrieve appropriate columns from font data
119 for (col
= 0; col
< font
.u8Width
; col
++)
121 column
[col
] = font
.au8FontTable
[((c
- 32) * font
.u8Width
) + col
]; // Get first column of appropriate character
126 // Requested character is not available in this font ... send a space instead
127 for (col
= 0; col
< font
.u8Width
; col
++)
129 column
[col
] = 0xFF; // Send solid space
133 // Render each column
134 uint16_t xoffset
, yoffset
;
135 for (xoffset
= 0; xoffset
< font
.u8Width
; xoffset
++)
137 for (yoffset
= 0; yoffset
< (font
.u8Height
+ 1); yoffset
++)
140 bit
= (column
[xoffset
] << (8 - (yoffset
+ 1))); // Shift current row bit left
141 bit
= (bit
>> 7); // Shift current row but right (results in 0x01 for black, and 0x00 for white)
144 ssd1306DrawPixel(x
+ xoffset
, y
+ yoffset
);
150 /**************************************************************************/
152 /**************************************************************************/
154 /**************************************************************************/
156 @brief Initialises the SSD1306 LCD display
158 /**************************************************************************/
159 void ssd1306Init(uint8_t vccstate
)
161 // Set all pins to output
162 gpioSetDir(SSD1306_SCLK_PORT
, SSD1306_SCLK_PIN
, gpioDirection_Output
);
163 gpioSetDir(SSD1306_SDAT_PORT
, SSD1306_SDAT_PIN
, gpioDirection_Output
);
164 gpioSetDir(SSD1306_DC_PORT
, SSD1306_DC_PIN
, gpioDirection_Output
);
165 gpioSetDir(SSD1306_RST_PORT
, SSD1306_RST_PIN
, gpioDirection_Output
);
166 gpioSetDir(SSD1306_CS_PORT
, SSD1306_CS_PIN
, gpioDirection_Output
);
169 gpioSetValue(SSD1306_RST_PORT
, SSD1306_RST_PIN
, 1);
171 gpioSetValue(SSD1306_RST_PORT
, SSD1306_RST_PIN
, 0);
173 gpioSetValue(SSD1306_RST_PORT
, SSD1306_RST_PIN
, 1);
175 #if defined SSD1306_128_32
176 // Init sequence taken from datasheet for UG-2832HSWEG04 (128x32 OLED module)
177 CMD(SSD1306_DISPLAYOFF
); // 0xAE
178 CMD(SSD1306_SETDISPLAYCLOCKDIV
); // 0xD5
179 CMD(0x80); // the suggested ratio 0x80
180 CMD(SSD1306_SETMULTIPLEX
); // 0xA8
182 CMD(SSD1306_SETDISPLAYOFFSET
); // 0xD3
183 CMD(0x0); // no offset
184 CMD(SSD1306_SETSTARTLINE
| 0x0); // line #0
185 CMD(SSD1306_CHARGEPUMP
); // 0x8D
186 if (vccstate
== SSD1306_EXTERNALVCC
)
190 CMD(SSD1306_SEGREMAP
| 0x1);
191 CMD(SSD1306_COMSCANDEC
);
192 CMD(SSD1306_SETCOMPINS
); // 0xDA
194 CMD(SSD1306_SETCONTRAST
); // 0x81
195 if (vccstate
== SSD1306_EXTERNALVCC
)
199 CMD(SSD1306_SETPRECHARGE
); // 0xd9
200 if (vccstate
== SSD1306_EXTERNALVCC
)
204 CMD(SSD1306_SETVCOMDETECT
); // 0xDB
205 CMD(0x40); // 0x20 is default?
206 CMD(SSD1306_DISPLAYALLON_RESUME
); // 0xA4
207 CMD(SSD1306_NORMALDISPLAY
); // 0xA6
210 #if defined SSD1306_128_64
211 // Init sequence taken from datasheet for UG-2864HSWEG01 (128x64 OLED module)
212 CMD(SSD1306_DISPLAYOFF
); // 0xAE
213 CMD(SSD1306_SETDISPLAYCLOCKDIV
); // 0xD5
214 CMD(0x80); // the suggested ratio 0x80
215 CMD(SSD1306_SETMULTIPLEX
); // 0xA8
217 CMD(SSD1306_SETDISPLAYOFFSET
); // 0xD3
218 CMD(0x0); // no offset
219 CMD(SSD1306_SETSTARTLINE
| 0x0); // line #0
220 CMD(SSD1306_CHARGEPUMP
); // 0x8D
221 if (vccstate
== SSD1306_EXTERNALVCC
)
225 CMD(SSD1306_SEGREMAP
| 0x1);
226 CMD(SSD1306_COMSCANDEC
);
227 CMD(SSD1306_SETCOMPINS
); // 0xDA
229 CMD(SSD1306_SETCONTRAST
); // 0x81
230 if (vccstate
== SSD1306_EXTERNALVCC
)
234 CMD(SSD1306_SETPRECHARGE
); // 0xd9
235 if (vccstate
== SSD1306_EXTERNALVCC
)
239 CMD(SSD1306_SETVCOMDETECT
); // 0xDB
240 CMD(0x40); // 0x20 is default?
241 CMD(SSD1306_DISPLAYALLON_RESUME
); // 0xA4
242 CMD(SSD1306_NORMALDISPLAY
); // 0xA6
245 // Enabled the OLED panel
246 CMD(SSD1306_DISPLAYON
);
249 /**************************************************************************/
251 @brief Draws a single pixel in image buffer
254 The x position (0..127)
256 The y position (0..63)
258 /**************************************************************************/
259 void ssd1306DrawPixel(uint8_t x
, uint8_t y
)
261 if ((x
>= SSD1306_LCDWIDTH
) || (y
>= SSD1306_LCDHEIGHT
))
264 _ssd1306buffer
[x
+ (y
/8)*SSD1306_LCDWIDTH
] |= (1 << y
%8);
267 /**************************************************************************/
269 @brief Clears a single pixel in image buffer
272 The x position (0..127)
274 The y position (0..63)
276 /**************************************************************************/
277 void ssd1306ClearPixel(uint8_t x
, uint8_t y
)
279 if ((x
>= SSD1306_LCDWIDTH
) || (y
>= SSD1306_LCDHEIGHT
))
282 _ssd1306buffer
[x
+ (y
/8)*SSD1306_LCDWIDTH
] &= ~(1 << y
%8);
285 /**************************************************************************/
287 @brief Gets the value (1 or 0) of the specified pixel from the buffer
290 The x position (0..127)
292 The y position (0..63)
294 @return 1 if the pixel is enabled, 0 if disabled
296 /**************************************************************************/
297 uint8_t ssd1306GetPixel(uint8_t x
, uint8_t y
)
299 if ((x
>= SSD1306_LCDWIDTH
) || (y
>=SSD1306_LCDHEIGHT
)) return 0;
300 return _ssd1306buffer
[x
+ (y
/8)*SSD1306_LCDWIDTH
] & (1 << y
%8) ? 1 : 0;
303 /**************************************************************************/
305 @brief Clears the screen
307 /**************************************************************************/
308 void ssd1306ClearScreen()
310 memset(_ssd1306buffer
, 0, 1024);
313 /**************************************************************************/
315 @brief Renders the contents of the pixel buffer on the LCD
317 /**************************************************************************/
318 void ssd1306Refresh(void)
320 CMD(SSD1306_SETLOWCOLUMN
| 0x0); // low col = 0
321 CMD(SSD1306_SETHIGHCOLUMN
| 0x0); // hi col = 0
322 CMD(SSD1306_SETSTARTLINE
| 0x0); // line #0
325 for (i
=0; i
<1024; i
++)
327 DATA(_ssd1306buffer
[i
]);
331 /**************************************************************************/
333 @brief Draws a string using the supplied font data.
336 Starting x co-ordinate
338 Starting y co-ordinate
342 Pointer to the FONT_DEF to use when drawing the string
348 #include "drivers/lcd/bitmap/ssd1306/ssd1306.h"
349 #include "drivers/lcd/smallfonts.h"
351 // Configure the pins and initialise the LCD screen
352 ssd1306Init(SSD1306_INTERNALVCC);
354 // Render some text on the screen
355 ssd1306DrawString(1, 10, "5x8 System", Font_System5x8);
356 ssd1306DrawString(1, 20, "7x8 System", Font_System7x8);
358 // Refresh the screen to see the results
363 /**************************************************************************/
364 void ssd1306DrawString(uint16_t x
, uint16_t y
, char* text
, struct FONT_DEF font
)
367 for (l
= 0; l
< strlen(text
); l
++)
369 ssd1306DrawChar(x
+ (l
* (font
.u8Width
+ 1)), y
, text
[l
], font
);
373 /**************************************************************************/
375 @brief Shifts the contents of the frame buffer up the specified
379 The number of pixels to shift the frame buffer up, leaving
380 a blank space at the bottom of the frame buffer x pixels
387 #include "drivers/lcd/bitmap/ssd1306/ssd1306.h"
388 #include "drivers/lcd/smallfonts.h"
390 // Configure the pins and initialise the LCD screen
393 // Enable the backlight
396 // Continually write some text, scrolling upward one line each time
399 // Shift the buffer up 8 pixels (adjust for font-height)
400 ssd1306ShiftFrameBuffer(8);
401 // Render some text on the screen with different fonts
402 ssd1306DrawString(1, 56, "INSERT TEXT HERE", Font_System5x8);
403 // Refresh the screen to see the results
405 // Wait a bit before writing the next line
411 /**************************************************************************/
412 void ssd1306ShiftFrameBuffer( uint8_t height
)
414 if (height
== 0) return;
415 if (height
>= SSD1306_LCDHEIGHT
)
417 // Clear the entire frame buffer
418 ssd1306ClearScreen();
422 // This is horribly inefficient, but at least easy to understand
423 // In a production environment, this should be significantly optimised
426 for (y
= 0; y
< SSD1306_LCDHEIGHT
; y
++)
428 for (x
= 0; x
< SSD1306_LCDWIDTH
; x
++)
430 if ((SSD1306_LCDHEIGHT
- 1) - y
> height
)
432 // Shift height from further ahead in the buffer
433 ssd1306GetPixel(x
, y
+ height
) ? ssd1306DrawPixel(x
, y
) : ssd1306ClearPixel(x
, y
);
437 // Clear the entire line
438 ssd1306ClearPixel(x
, y
);