6aa038a64ef37869aae2d7c1a9929a4b89cdef6a
[hackover2013-badge-firmware.git] / drivers / lcd / bitmap / ssd1306 / ssd1306.c
1 /**************************************************************************/
2 /*!
3 @file ssd1306.c
4 @author K. Townsend (microBuilder.eu)
5
6 @section DESCRIPTION
7
8 Driver for 128x64 OLED display based on the SSD1306 controller.
9
10 This driver is based on the SSD1306 Library from Limor Fried
11 (Adafruit Industries) at: https://github.com/adafruit/SSD1306
12
13 @section LICENSE
14
15 Software License Agreement (BSD License)
16
17 Copyright (c) 2012, microBuilder SARL
18 All rights reserved.
19
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.
30
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.
41 */
42 /**************************************************************************/
43 #include <string.h>
44
45 #include "ssd1306.h"
46
47 #include "core/gpio/gpio.h"
48 #include "core/systick/systick.h"
49 #include "drivers/lcd/smallfonts.h"
50
51 void ssd1306SendByte(uint8_t byte);
52
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 ); \
58 } while (0);
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 ); \
64 } while (0);
65 #define DELAY(mS) do { systickDelay( mS / CFG_SYSTICK_DELAY_IN_MS ); } while(0);
66
67 uint8_t _ssd1306buffer[SSD1306_LCDWIDTH * SSD1306_LCDHEIGHT / 8];
68
69 /**************************************************************************/
70 /* Private Methods */
71 /**************************************************************************/
72
73 /**************************************************************************/
74 /*!
75 @brief Simulates an SPI write using GPIO
76
77 @param[in] byte
78 The byte to send
79 */
80 /**************************************************************************/
81 void ssd1306SendByte(uint8_t byte)
82 {
83 int8_t i;
84
85 // Make sure clock pin starts high
86 gpioSetValue(SSD1306_SCLK_PORT, SSD1306_SCLK_PIN, 1);
87
88 // Write from MSB to LSB
89 for (i=7; i>=0; i--)
90 {
91 // Set clock pin low
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);
95 // Set clock pin high
96 gpioSetValue(SSD1306_SCLK_PORT, SSD1306_SCLK_PIN, 1);
97 }
98 }
99
100 /**************************************************************************/
101 /*!
102 @brief Draws a single graphic character using the supplied font
103 */
104 /**************************************************************************/
105 static void ssd1306DrawChar(uint16_t x, uint16_t y, uint8_t c, struct FONT_DEF font)
106 {
107 uint8_t col, column[font.u8Width];
108
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))
113 return;
114
115 // Check if the requested character is available
116 if ((c >= font.u8FirstChar) && (c <= font.u8LastChar))
117 {
118 // Retrieve appropriate columns from font data
119 for (col = 0; col < font.u8Width; col++)
120 {
121 column[col] = font.au8FontTable[((c - 32) * font.u8Width) + col]; // Get first column of appropriate character
122 }
123 }
124 else
125 {
126 // Requested character is not available in this font ... send a space instead
127 for (col = 0; col < font.u8Width; col++)
128 {
129 column[col] = 0xFF; // Send solid space
130 }
131 }
132
133 // Render each column
134 uint16_t xoffset, yoffset;
135 for (xoffset = 0; xoffset < font.u8Width; xoffset++)
136 {
137 for (yoffset = 0; yoffset < (font.u8Height + 1); yoffset++)
138 {
139 uint8_t bit = 0x00;
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)
142 if (bit)
143 {
144 ssd1306DrawPixel(x + xoffset, y + yoffset);
145 }
146 }
147 }
148 }
149
150 /**************************************************************************/
151 /* Public Methods */
152 /**************************************************************************/
153
154 /**************************************************************************/
155 /*!
156 @brief Initialises the SSD1306 LCD display
157 */
158 /**************************************************************************/
159 void ssd1306Init(uint8_t vccstate)
160 {
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);
167
168 // Reset the LCD
169 gpioSetValue(SSD1306_RST_PORT, SSD1306_RST_PIN, 1);
170 DELAY(1);
171 gpioSetValue(SSD1306_RST_PORT, SSD1306_RST_PIN, 0);
172 DELAY(10);
173 gpioSetValue(SSD1306_RST_PORT, SSD1306_RST_PIN, 1);
174
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
181 CMD(0x1F); // 31
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)
187 { CMD(0x10) }
188 else
189 { CMD(0x14) }
190 CMD(SSD1306_MEMORYMODE); // 0x20
191 CMD(0x00); // 0x0 act like ks0108
192 CMD(SSD1306_SEGREMAP | 0x1);
193 CMD(SSD1306_COMSCANDEC);
194 CMD(SSD1306_SETCOMPINS); // 0xDA
195 CMD(0x02);
196 CMD(SSD1306_SETCONTRAST); // 0x81
197 if (vccstate == SSD1306_EXTERNALVCC)
198 { CMD(0x9F) }
199 else
200 { CMD(0xCF) }
201 CMD(SSD1306_SETPRECHARGE); // 0xd9
202 if (vccstate == SSD1306_EXTERNALVCC)
203 { CMD(0x22) }
204 else
205 { CMD(0xF1) }
206 CMD(SSD1306_SETVCOMDETECT); // 0xDB
207 CMD(0x40);
208 CMD(SSD1306_DISPLAYALLON_RESUME); // 0xA4
209 CMD(SSD1306_NORMALDISPLAY); // 0xA6
210 #endif
211
212 #if defined SSD1306_128_64
213 // Init sequence taken from datasheet for UG-2864HSWEG01 (128x64 OLED module)
214 CMD(SSD1306_DISPLAYOFF); // 0xAE
215 CMD(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5
216 CMD(0x80); // the suggested ratio 0x80
217 CMD(SSD1306_SETMULTIPLEX); // 0xA8
218 CMD(0x3F); // 63
219 CMD(SSD1306_SETDISPLAYOFFSET); // 0xD3
220 CMD(0x0); // no offset
221 CMD(SSD1306_SETSTARTLINE | 0x0); // line #0
222 CMD(SSD1306_CHARGEPUMP); // 0x8D
223 if (vccstate == SSD1306_EXTERNALVCC)
224 { CMD(0x10) }
225 else
226 { CMD(0x14) }
227 CMD(SSD1306_MEMORYMODE); // 0x20
228 CMD(0x00); // 0x0 act like ks0108
229 CMD(SSD1306_SEGREMAP | 0x1);
230 CMD(SSD1306_COMSCANDEC);
231 CMD(SSD1306_SETCOMPINS); // 0xDA
232 CMD(0x12);
233 CMD(SSD1306_SETCONTRAST); // 0x81
234 if (vccstate == SSD1306_EXTERNALVCC)
235 { CMD(0x9F) }
236 else
237 { CMD(0xCF) }
238 CMD(SSD1306_SETPRECHARGE); // 0xd9
239 if (vccstate == SSD1306_EXTERNALVCC)
240 { CMD(0x22) }
241 else
242 { CMD(0xF1) }
243 CMD(SSD1306_SETVCOMDETECT); // 0xDB
244 CMD(0x40);
245 CMD(SSD1306_DISPLAYALLON_RESUME); // 0xA4
246 CMD(SSD1306_NORMALDISPLAY); // 0xA6
247 #endif
248
249 // Enabled the OLED panel
250 CMD(SSD1306_DISPLAYON);
251 }
252
253 /**************************************************************************/
254 /*!
255 @brief Draws a single pixel in image buffer
256
257 @param[in] x
258 The x position (0..127)
259 @param[in] y
260 The y position (0..63)
261 */
262 /**************************************************************************/
263 void ssd1306DrawPixel(uint8_t x, uint8_t y)
264 {
265 if ((x >= SSD1306_LCDWIDTH) || (y >= SSD1306_LCDHEIGHT))
266 return;
267
268 _ssd1306buffer[x+ (y/8)*SSD1306_LCDWIDTH] |= (1 << y%8);
269 }
270
271 /**************************************************************************/
272 /*!
273 @brief Clears a single pixel in image buffer
274
275 @param[in] x
276 The x position (0..127)
277 @param[in] y
278 The y position (0..63)
279 */
280 /**************************************************************************/
281 void ssd1306ClearPixel(uint8_t x, uint8_t y)
282 {
283 if ((x >= SSD1306_LCDWIDTH) || (y >= SSD1306_LCDHEIGHT))
284 return;
285
286 _ssd1306buffer[x+ (y/8)*SSD1306_LCDWIDTH] &= ~(1 << y%8);
287 }
288
289 /**************************************************************************/
290 /*!
291 @brief Gets the value (1 or 0) of the specified pixel from the buffer
292
293 @param[in] x
294 The x position (0..127)
295 @param[in] y
296 The y position (0..63)
297
298 @return 1 if the pixel is enabled, 0 if disabled
299 */
300 /**************************************************************************/
301 uint8_t ssd1306GetPixel(uint8_t x, uint8_t y)
302 {
303 if ((x >= SSD1306_LCDWIDTH) || (y >=SSD1306_LCDHEIGHT)) return 0;
304 return _ssd1306buffer[x+ (y/8)*SSD1306_LCDWIDTH] & (1 << y%8) ? 1 : 0;
305 }
306
307 /**************************************************************************/
308 /*!
309 @brief Clears the screen
310 */
311 /**************************************************************************/
312 void ssd1306ClearScreen()
313 {
314 memset(_ssd1306buffer, 0, 1024);
315 }
316
317 /**************************************************************************/
318 /*!
319 @brief Renders the contents of the pixel buffer on the LCD
320 */
321 /**************************************************************************/
322 void ssd1306Refresh(void)
323 {
324 CMD(SSD1306_SETLOWCOLUMN | 0x0); // low col = 0
325 CMD(SSD1306_SETHIGHCOLUMN | 0x0); // hi col = 0
326 CMD(SSD1306_SETSTARTLINE | 0x0); // line #0
327
328 uint16_t i;
329 for (i=0; i<1024; i++)
330 {
331 DATA(_ssd1306buffer[i]);
332 }
333 }
334
335 /**************************************************************************/
336 /*!
337 @brief Draws a string using the supplied font data.
338
339 @param[in] x
340 Starting x co-ordinate
341 @param[in] y
342 Starting y co-ordinate
343 @param[in] text
344 The string to render
345 @param[in] font
346 Pointer to the FONT_DEF to use when drawing the string
347
348 @section Example
349
350 @code
351
352 #include "drivers/lcd/bitmap/ssd1306/ssd1306.h"
353 #include "drivers/lcd/smallfonts.h"
354
355 // Configure the pins and initialise the LCD screen
356 ssd1306Init(SSD1306_INTERNALVCC);
357
358 // Render some text on the screen
359 ssd1306DrawString(1, 10, "5x8 System", Font_System5x8);
360 ssd1306DrawString(1, 20, "7x8 System", Font_System7x8);
361
362 // Refresh the screen to see the results
363 ssd1306Refresh();
364
365 @endcode
366 */
367 /**************************************************************************/
368 void ssd1306DrawString(uint16_t x, uint16_t y, char* text, struct FONT_DEF font)
369 {
370 uint8_t l;
371 for (l = 0; l < strlen(text); l++)
372 {
373 ssd1306DrawChar(x + (l * (font.u8Width + 1)), y, text[l], font);
374 }
375 }
376
377 /**************************************************************************/
378 /*!
379 @brief Shifts the contents of the frame buffer up the specified
380 number of pixels
381
382 @param[in] height
383 The number of pixels to shift the frame buffer up, leaving
384 a blank space at the bottom of the frame buffer x pixels
385 high
386
387 @section Example
388
389 @code
390
391 #include "drivers/lcd/bitmap/ssd1306/ssd1306.h"
392 #include "drivers/lcd/smallfonts.h"
393
394 // Configure the pins and initialise the LCD screen
395 ssd1306Init(SSD1306_INTERNALVCC);
396
397 // Continually write some text, scrolling upward one line each time
398 while (1)
399 {
400 // Shift the buffer up 8 pixels (adjust for font-height)
401 ssd1306ShiftFrameBuffer(8);
402 // Render some text on the screen with different fonts
403 ssd1306DrawString(1, 56, "INSERT TEXT HERE", Font_System5x8);
404 // Refresh the screen to see the results
405 ssd1306Refresh();
406 // Wait a bit before writing the next line
407 systickDelay(1000);
408 }
409
410 @endcode
411 */
412 /**************************************************************************/
413 void ssd1306ShiftFrameBuffer( uint8_t height )
414 {
415 if (height == 0) return;
416 if (height >= SSD1306_LCDHEIGHT)
417 {
418 // Clear the entire frame buffer
419 ssd1306ClearScreen();
420 return;
421 }
422
423 // This is horribly inefficient, but at least easy to understand
424 // In a production environment, this should be significantly optimised
425
426 uint8_t y, x;
427 for (y = 0; y < SSD1306_LCDHEIGHT; y++)
428 {
429 for (x = 0; x < SSD1306_LCDWIDTH; x++)
430 {
431 if ((SSD1306_LCDHEIGHT - 1) - y > height)
432 {
433 // Shift height from further ahead in the buffer
434 ssd1306GetPixel(x, y + height) ? ssd1306DrawPixel(x, y) : ssd1306ClearPixel(x, y);
435 }
436 else
437 {
438 // Clear the entire line
439 ssd1306ClearPixel(x, y);
440 }
441 }
442 }
443 }
This page took 0.060441 seconds and 3 git commands to generate.