Subversion Repositories DashDisplay

Rev

Rev 30 | Details | Compare with Previous | Last modification | View Log | RSS feed

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