Subversion Repositories chibiosIgnition

Rev

Rev 3 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 mjames 1
/*********************************************************************
2
 This is a library for our Monochrome OLEDs based on SSD1306 drivers
3
 
4
 Pick one up today in the adafruit shop!
5
 ------> http://www.adafruit.com/category/63_98
6
 
7
 These displays use SPI to communicate, 4 or 5 pins are required to
8
 interface
9
 
10
 Adafruit invests time and resources providing this open source code,
11
 please support Adafruit and open-source hardware by purchasing
12
 products from Adafruit!
13
 
14
 Written by Limor Fried/Ladyada  for Adafruit Industries.
15
 BSD license, check license.txt for more information
16
 All text above, and the splash screen below must be included in any redistribution
17
 
18
 This code is taken from the ADAfruit library - it is used for playing with an OLED screen
19
 
20
 *********************************************************************/
21
 
22
#include "ch.h"
23
#include "hal.h"
24
 
25
#include <stdint.h>
26
#include <string.h>
27
#include "SSD1306.h"
28
#include "spiInterface.h"
29
 
30
#define swap(x,y) { typeof(x)t = x; x=y; y=t; }
31
#define abs(x)      ((x)>0?(x):-(x))
32
 
33
static uint8_t rotation = 0;
34
const uint16_t WIDTH = SSD1306_LCDWIDTH;
35
const uint16_t HEIGHT = SSD1306_LCDHEIGHT;
36
 
37
// the memory buffer for the LCD
38
 
39
// pointer to the current display - affects buffer used and also chipselect
40
 
41
uint8_t display_buffer[SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH
42
                / 8];
43
 
44
inline uint8_t * display_address(void) {
45
        return (uint8_t *) (&display_buffer[0]);
46
}
47
 
48
inline uint8_t getRotation(void) {
49
        return rotation;
50
}
51
 
52
inline int16_t width(void) {
53
        switch (rotation) {
54
        case 0:
55
                return WIDTH;
56
                break;
57
        case 1:
58
                return WIDTH;
59
                break;
60
        case 2:
61
                return HEIGHT;
62
                break;
63
        case 3:
64
                return -WIDTH;
65
                break;
66
        }
67
        return 0;
68
}
69
 
70
inline int16_t height(void) {
71
        switch (rotation) {
72
        case 0:
73
                return HEIGHT;
74
                break;
75
        case 1:
76
                return HEIGHT;
77
                break;
78
        case 2:
79
                return WIDTH;
80
                break;
81
        case 3:
82
                return -HEIGHT;
83
                break;
84
        }
85
        return 0;
86
}
87
 
88
 
89
// the most basic function, set a single pixel
90
inline void drawPixel(int16_t x, int16_t y, uint16_t color) {
91
        if ((x < 0) || (x >= width()) || (y < 0) || (y >= height()))
92
                return;
93
 
94
        // check rotation, move pixel around if necessary
95
        switch (getRotation()) {
96
        case 1:
97
                swap(x, y)
98
                ;
99
                x = WIDTH - x - 1;
100
                break;
101
        case 2:
102
                x = WIDTH - x - 1;
103
                y = HEIGHT - y - 1;
104
                break;
105
        case 3:
106
                swap(x, y)
107
                ;
108
                y = HEIGHT - y - 1;
109
                break;
110
        }
111
 
112
        // x is which column
113
        switch (color) {
114
        case BLACK:
115
                display_buffer[x + (y / 8) * SSD1306_LCDWIDTH] &= ~(1 << (y & 7));
116
                break;
117
 
118
        default:
119
        case WHITE:
120
                display_buffer[x + (y / 8) * SSD1306_LCDWIDTH] |= (1 << (y & 7));
121
                break;
122
 
123
        case INVERT:
124
                display_buffer[x + (y / 8) * SSD1306_LCDWIDTH] ^= (1 << (y & 7));
125
                break;
126
        }
127
}
128
 
129
void ssd1306_begin(uint8_t vccstate, uint8_t i2caddr) {
130
 
131
        (void) i2caddr;
132
        ssd1306resetDisplay();
133
 
134
 
135
#if defined SSD1306_128_32
136
                // Init sequence for 128x32 OLED module
137
                ssd1306commandSPIwrite(SSD1306_DISPLAYOFF);// 0xAE
138
                ssd1306commandSPIwrite(SSD1306_SETDISPLAYCLOCKDIV);// 0xD5
139
                ssd1306commandSPIwrite(0x80);// the suggested ratio 0x80
140
                ssd1306commandSPIwrite(SSD1306_SETMULTIPLEX);// 0xA8
141
                ssd1306commandSPIwrite(0x1F);
142
                ssd1306commandSPIwrite(SSD1306_SETDISPLAYOFFSET);// 0xD3
143
                ssd1306commandSPIwrite(0x0);// no offset
144
                ssd1306commandSPIwrite(SSD1306_SETSTARTLINE | 0x0);// line #0
145
                ssd1306commandSPIwrite(SSD1306_CHARGEPUMP);// 0x8D
146
                if (vccstate == SSD1306_EXTERNALVCC)
147
                {       ssd1306commandSPIwrite(0x10);}
148
                else
149
                {       ssd1306commandSPIwrite(0x14);}
150
                ssd1306commandSPIwrite(SSD1306_MEMORYMODE);                    // 0x20
151
                ssd1306commandSPIwrite(0x00);// 0x0 act like ks0108
152
                ssd1306commandSPIwrite(SSD1306_SEGREMAP | 0x1);
153
                ssd1306commandSPIwrite(SSD1306_COMSCANDEC);
154
                ssd1306commandSPIwrite(SSD1306_SETCOMPINS);// 0xDA
155
                ssd1306commandSPIwrite(0x02);
156
                ssd1306commandSPIwrite(SSD1306_SETCONTRAST);// 0x81
157
                ssd1306commandSPIwrite(0x8F);
158
                ssd1306commandSPIwrite(SSD1306_SETPRECHARGE);// 0xd9
159
                if (vccstate == SSD1306_EXTERNALVCC)
160
                {       ssd1306commandSPIwrite(0x22);}
161
                else
162
                {       ssd1306commandSPIwrite(0xF1);}
163
                ssd1306commandSPIwrite(SSD1306_SETVCOMDETECT);                 // 0xDB
164
                ssd1306commandSPIwrite(0x40);
165
                ssd1306commandSPIwrite(SSD1306_DISPLAYALLON_RESUME);// 0xA4
166
                ssd1306commandSPIwrite(SSD1306_NORMALDISPLAY);// 0xA6
167
#endif
168
 
169
#if defined SSD1306_128_64
170
                // Init sequence for 128x64 OLED module
171
                ssd1306commandSPIwrite(SSD1306_DISPLAYOFF);                    // 0xAE
172
                ssd1306commandSPIwrite(SSD1306_SETDISPLAYCLOCKDIV);            // 0xD5
173
                ssd1306commandSPIwrite(0x80);                                  // the suggested ratio 0x80
174
                ssd1306commandSPIwrite(SSD1306_SETMULTIPLEX);                  // 0xA8
175
                ssd1306commandSPIwrite(0x3F);
176
                ssd1306commandSPIwrite(SSD1306_SETDISPLAYOFFSET);              // 0xD3
177
                ssd1306commandSPIwrite(0x0);                                   // no offset
178
                ssd1306commandSPIwrite(SSD1306_SETSTARTLINE | 0x0);            // line #0
179
                ssd1306commandSPIwrite(SSD1306_CHARGEPUMP);                    // 0x8D
180
                if (vccstate == SSD1306_EXTERNALVCC) {
181
                        ssd1306commandSPIwrite(0x10);
182
                } else {
183
                        ssd1306commandSPIwrite(0x14);
184
                }
185
                ssd1306commandSPIwrite(SSD1306_MEMORYMODE);                    // 0x20
186
                ssd1306commandSPIwrite(0x00);                            // 0x0 act like ks0108
187
                ssd1306commandSPIwrite(SSD1306_SEGREMAP | 0x1);
188
                ssd1306commandSPIwrite(SSD1306_COMSCANDEC);
189
                ssd1306commandSPIwrite(SSD1306_SETCOMPINS);                    // 0xDA
190
                ssd1306commandSPIwrite(0x12);
191
                ssd1306commandSPIwrite(SSD1306_SETCONTRAST);                   // 0x81
192
                if (vccstate == SSD1306_EXTERNALVCC) {
193
                        ssd1306commandSPIwrite(0x9F);
194
                } else {
195
                        ssd1306commandSPIwrite(0xCF);
196
                }
197
                ssd1306commandSPIwrite(SSD1306_SETPRECHARGE);                  // 0xd9
198
                if (vccstate == SSD1306_EXTERNALVCC) {
199
                        ssd1306commandSPIwrite(0x22);
200
                } else {
201
                        ssd1306commandSPIwrite(0xF1);
202
                }
203
                ssd1306commandSPIwrite(SSD1306_SETVCOMDETECT);                 // 0xDB
204
                ssd1306commandSPIwrite(0x40);
205
                ssd1306commandSPIwrite(SSD1306_DISPLAYALLON_RESUME);           // 0xA4
206
                ssd1306commandSPIwrite(SSD1306_NORMALDISPLAY);                 // 0xA6
207
#endif
208
 
209
                ssd1306commandSPIwrite(SSD1306_DISPLAYON);               //--turn on oled panel
210
 
211
}
212
 
213
void invertDisplay(uint8_t i) {
214
        if (i) {
215
                ssd1306commandSPIwrite(SSD1306_INVERTDISPLAY);
216
        } else {
217
                ssd1306commandSPIwrite(SSD1306_NORMALDISPLAY);
218
        }
219
}
220
 
221
 
222
// startscrollright
223
// Activate a right handed scroll for rows start through stop
224
// Hint, the display is 16 rows tall. To scroll the whole display, run:
225
// display.scrollright(0x00, 0x0F) 
226
void startscrollright(uint8_t start, uint8_t stop) {
227
        ssd1306commandSPIwrite(SSD1306_RIGHT_HORIZONTAL_SCROLL);
228
        ssd1306commandSPIwrite(0X00);
229
        ssd1306commandSPIwrite(start);
230
        ssd1306commandSPIwrite(0X00);
231
        ssd1306commandSPIwrite(stop);
232
        ssd1306commandSPIwrite(0X00);
233
        ssd1306commandSPIwrite(0XFF);
234
        ssd1306commandSPIwrite(SSD1306_ACTIVATE_SCROLL);
235
}
236
 
237
// startscrollleft
238
// Activate a right handed scroll for rows start through stop
239
// Hint, the display is 16 rows tall. To scroll the whole display, run:
240
// display.scrollright(0x00, 0x0F) 
241
void startscrollleft(uint8_t start, uint8_t stop) {
242
        ssd1306commandSPIwrite(SSD1306_LEFT_HORIZONTAL_SCROLL);
243
        ssd1306commandSPIwrite(0X00);
244
        ssd1306commandSPIwrite(start);
245
        ssd1306commandSPIwrite(0X00);
246
        ssd1306commandSPIwrite(stop);
247
        ssd1306commandSPIwrite(0X00);
248
        ssd1306commandSPIwrite(0XFF);
249
        ssd1306commandSPIwrite(SSD1306_ACTIVATE_SCROLL);
250
}
251
 
252
// startscrolldiagright
253
// Activate a diagonal scroll for rows start through stop
254
// Hint, the display is 16 rows tall. To scroll the whole display, run:
255
// display.scrollright(0x00, 0x0F) 
256
void startscrolldiagright(uint8_t start, uint8_t stop) {
257
        ssd1306commandSPIwrite(SSD1306_SET_VERTICAL_SCROLL_AREA);
258
        ssd1306commandSPIwrite(0X00);
259
        ssd1306commandSPIwrite(SSD1306_LCDHEIGHT);
260
        ssd1306commandSPIwrite(SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL);
261
        ssd1306commandSPIwrite(0X00);
262
        ssd1306commandSPIwrite(start);
263
        ssd1306commandSPIwrite(0X00);
264
        ssd1306commandSPIwrite(stop);
265
        ssd1306commandSPIwrite(0X01);
266
        ssd1306commandSPIwrite(SSD1306_ACTIVATE_SCROLL);
267
}
268
 
269
// startscrolldiagleft
270
// Activate a diagonal scroll for rows start through stop
271
// Hint, the display is 16 rows tall. To scroll the whole display, run:
272
// display.scrollright(0x00, 0x0F) 
273
void startscrolldiagleft(uint8_t start, uint8_t stop) {
274
        ssd1306commandSPIwrite(SSD1306_SET_VERTICAL_SCROLL_AREA);
275
        ssd1306commandSPIwrite(0X00);
276
        ssd1306commandSPIwrite(SSD1306_LCDHEIGHT);
277
        ssd1306commandSPIwrite(SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL);
278
        ssd1306commandSPIwrite(0X00);
279
        ssd1306commandSPIwrite(start);
280
        ssd1306commandSPIwrite(0X00);
281
        ssd1306commandSPIwrite(stop);
282
        ssd1306commandSPIwrite(0X01);
283
        ssd1306commandSPIwrite(SSD1306_ACTIVATE_SCROLL);
284
}
285
 
286
void stopscroll(void) {
287
        ssd1306commandSPIwrite(SSD1306_DEACTIVATE_SCROLL);
288
}
289
 
290
// Dim the display
291
// dim = true: display is dimmed
292
// dim = false: display is normal
293
void dim(uint8_t contrast) {
294
 
295
        // the range of contrast to too small to be really useful
296
        // it is useful to dim the display
297
        ssd1306commandSPIwrite(SSD1306_SETCONTRAST);
298
        ssd1306commandSPIwrite(contrast);
299
}
300
 
301
void display(void) {
302
 
303
 
304
        // select entire display as window to write into
305
        ssd1306commandSPIwrite(SSD1306_COLUMNADDR);
306
        ssd1306commandSPIwrite(0);   // Column start address (0 = reset)
307
        ssd1306commandSPIwrite(SSD1306_RAMWIDTH-1); // Column end address (127 = reset)
308
 
309
        ssd1306commandSPIwrite(SSD1306_PAGEADDR);
310
        ssd1306commandSPIwrite(0); // Page start address (0 = reset)
311
        ssd1306commandSPIwrite((SSD1306_LCDHEIGHT == 64) ? 7 : 3); // Page end address
312
 
313
        int row;
314
 
315
        int col = SSD1306_RAMWIDTH == 132 ? 2 : 0;
316
        for (row = 0; row < SSD1306_LCDHEIGHT / 8; row++) {
317
                // set the cursor to
318
                ssd1306commandSPIwrite(0xB0 + row); //set page address
319
                ssd1306commandSPIwrite(col & 0xf); //set lower column address
320
                ssd1306commandSPIwrite(0x10 | (col >> 4)); //set higher column address
321
 
322
                ssd1306SendDisplay(
323
                                (uint8_t *) (&display_buffer[0]) + row * SSD1306_LCDWIDTH,
324
                                SSD1306_LCDWIDTH);
325
 
326
        }
327
 
328
}
329
 
330
// clear everything
331
void clearDisplay(void) {
332
        memset(&display_buffer, 0, (SSD1306_LCDWIDTH * SSD1306_LCDHEIGHT / 8));
333
}
334
 
335
void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) {
7 mjames 336
        bool bSwap = false;
3 mjames 337
        switch (rotation) {
338
        case 0:
339
                // 0 degree rotation, do nothing
340
                break;
341
        case 1:
342
                // 90 degree rotation, swap x & y for rotation, then invert x
343
                bSwap = true;
344
                swap(x, y)
345
                ;
346
                x = WIDTH - x - 1;
347
                break;
348
        case 2:
349
                // 180 degree rotation, invert x and y - then shift y around for height.
350
                x = WIDTH - x - 1;
351
                y = HEIGHT - y - 1;
352
                x -= (w - 1);
353
                break;
354
        case 3:
355
                // 270 degree rotation, swap x & y for rotation, then invert y  and adjust y for w (not to become h)
356
                bSwap = true;
357
                swap(x, y)
358
                ;
359
                y = HEIGHT - y - 1;
360
                y -= (w - 1);
361
                break;
362
        }
363
 
364
        if (bSwap) {
365
                drawFastVLineInternal(x, y, w, color);
366
        } else {
367
                drawFastHLineInternal(x, y, w, color);
368
        }
369
}
370
 
371
void drawFastHLineInternal(int16_t x, int16_t y, int16_t w, uint16_t color) {
372
        // Do bounds/limit checks
373
        if (y < 0 || y >= HEIGHT) {
374
                return;
375
        }
376
 
377
        // make sure we don't try to draw below 0
378
        if (x < 0) {
379
                w += x;
380
                x = 0;
381
        }
382
 
383
        // make sure we don't go off the edge of the display
384
        if ((x + w) > WIDTH) {
385
                w = (HEIGHT - x);
386
        }
387
 
388
        // if our width is now negative, punt
389
        if (w <= 0) {
390
                return;
391
        }
392
 
393
        // set up the pointer for  movement through the buffer
394
        register uint8_t *pBuf = &display_buffer[0];
395
        // adjust the buffer pointer for the current row
396
        pBuf += ((y / 8) * SSD1306_LCDWIDTH);
397
        // and offset x columns in
398
        pBuf += x;
399
 
400
        register uint8_t mask = 1 << (y & 7);
401
 
402
        if (color == WHITE) {
403
                while (w--) {
404
                        *pBuf++ |= mask;
405
                }
406
        } else {
407
                mask = ~mask;
408
                while (w--) {
409
                        *pBuf++ &= mask;
410
                }
411
        }
412
}
413
 
414
void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) {
7 mjames 415
        bool bSwap = false;
3 mjames 416
        switch (rotation) {
417
        case 0:
418
                break;
419
        case 1:
420
                // 90 degree rotation, swap x & y for rotation, then invert x and adjust x for h (now to become w)
421
                bSwap = true;
422
                swap(x, y)
423
                ;
424
                x = WIDTH - x - 1;
425
                x -= (h - 1);
426
                break;
427
        case 2:
428
                // 180 degree rotation, invert x and y - then shift y around for height.
429
                x = WIDTH - x - 1;
430
                y = HEIGHT - y - 1;
431
                y -= (h - 1);
432
                break;
433
        case 3:
434
                // 270 degree rotation, swap x & y for rotation, then invert y
435
                bSwap = true;
436
                swap(x, y)
437
                ;
438
                y = HEIGHT - y - 1;
439
                break;
440
        }
441
 
442
        if (bSwap) {
443
                drawFastHLineInternal(x, y, h, color);
444
        } else {
445
                drawFastVLineInternal(x, y, h, color);
446
        }
447
}
448
 
449
void drawFastVLineInternal(int16_t x, int16_t __y, int16_t __h, uint16_t color) {
450
 
451
        // do nothing if we're off the left or right side of the screen
452
        if (x < 0 || x >= WIDTH) {
453
                return;
454
        }
455
 
456
        // make sure we don't try to draw below 0
457
        if (__y < 0) {
458
                // __y is negative, this will subtract enough from __h to account for __y being 0
459
                __h += __y;
460
                __y = 0;
461
 
462
        }
463
 
464
        // make sure we don't go past the height of the display
465
        if ((__y + __h) > HEIGHT) {
466
                __h = (HEIGHT - __y);
467
        }
468
 
469
        // if our height is now negative, punt
470
        if (__h <= 0) {
471
                return;
472
        }
473
 
474
        // this display doesn't need ints for coordinates, use local byte registers for faster juggling
475
        register uint8_t y = __y;
476
        register uint8_t h = __h;
477
 
478
        // set up the pointer for fast movement through the buffer
479
        register uint8_t *pBuf = &display_buffer[0];
480
        // adjust the buffer pointer for the current row
481
        pBuf += ((y / 8) * SSD1306_LCDWIDTH);
482
        // and offset x columns in
483
        pBuf += x;
484
 
485
        // do the first partial byte, if necessary - this requires some masking
486
        register uint8_t mod = (y & 7);
487
        if (mod) {
488
                // mask off the high n bits we want to set
489
                mod = 8 - mod;
490
 
491
                // note - lookup table results in a nearly 10% performance improvement in fill* functions
492
                // register uint8_t mask = ~(0xFF >> (mod));
493
                static uint8_t premask[8] = { 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC,
494
                                0xFE };
495
                register uint8_t mask = premask[mod];
496
 
497
                // adjust the mask if we're not going to reach the end of this byte
498
                if (h < mod) {
499
                        mask &= (0XFF >> (mod - h));
500
                }
501
 
502
                if (color == WHITE) {
503
                        *pBuf |= mask;
504
                } else {
505
                        *pBuf &= ~mask;
506
                }
507
 
508
                // fast exit if we're done here!
509
                if (h < mod) {
510
                        return;
511
                }
512
 
513
                h -= mod;
514
 
515
                pBuf += SSD1306_LCDWIDTH;
516
        }
517
 
518
        // write solid bytes while we can - effectively doing 8 rows at a time
519
        if (h >= 8) {
520
                // store a local value to work with
521
                register uint8_t val = (color == WHITE) ? 255 : 0;
522
 
523
                do {
524
                        // write our value in
525
                        *pBuf = val;
526
 
527
                        // adjust the buffer forward 8 rows worth of data
528
                        pBuf += SSD1306_LCDWIDTH;
529
 
530
                        // adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now)
531
                        h -= 8;
532
                } while (h >= 8);
533
        }
534
 
535
        // now do the final partial byte, if necessary
536
        if (h) {
537
                mod = h & 7;
538
                // this time we want to mask the low bits of the byte, vs the high bits we did above
539
                // register uint8_t mask = (1 << mod) - 1;
540
                // note - lookup table results in a nearly 10% performance improvement in fill* functions
541
                static uint8_t postmask[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F,
542
                                0x7F };
543
                register uint8_t mask = postmask[mod];
544
                if (color == WHITE) {
545
                        *pBuf |= mask;
546
                } else {
547
                        *pBuf &= ~mask;
548
                }
549
        }
550
}
551
 
552
/* using Bresenham draw algorithm */
553
void drawLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t color) {
554
        int16_t x,   y,
555
                dx,  dy,    //deltas
556
                        dx2, dy2,       //scaled deltas
557
                    ix,  iy,    //increase rate on the x and y axis
558
                        err;    //the error term
559
        uint16_t i;             //looping variable
560
 
561
        // identify the first pixel
562
        x = x1;
563
        y = y1;
564
 
565
        // difference between starting and ending points
566
        dx = x2 - x1;
567
        dy = y2 - y1;
568
 
569
        // calculate direction of the vector and store in ix and iy
570
        if (dx >= 0)
571
                ix = 1;
572
 
573
        if (dx < 0) {
574
                ix = -1;
575
                dx = abs(dx);
576
        }
577
 
578
        if (dy >= 0)
579
                iy = 1;
580
 
581
        if (dy < 0) {
582
                iy = -1;
583
                dy = abs(dy);
584
        }
585
 
586
        // scale deltas and store in dx2 and dy2
587
        dx2 = dx * 2;
588
        dy2 = dy * 2;
589
 
590
// all  variables are set and it's time to enter the main loop.
591
 
592
        if (dx > dy)    // dx is the major axis
593
                        {
594
                // initialize the error term
595
                err = dy2 - dx;
596
 
597
                for (i = 0; i <= dx; i++) {
598
                        drawPixel(x, y, color);
599
                        if (err >= 0) {
600
                                err -= dx2;
601
                                y += iy;
602
                        }
603
                        err += dy2;
604
                        x += ix;
605
                }
606
        }
607
 
608
        else            // dy is the major axis
609
        {
610
                // initialize the error term
611
                err = dx2 - dy;
612
 
613
                for (i = 0; i <= dy; i++) {
614
                        drawPixel(x, y, color);
615
                        if (err >= 0) {
616
                                err -= dy2;
617
                                x += ix;
618
                        }
619
                        err += dx2;
620
                        y += iy;
621
                }
622
        }
623
}