Subversion Repositories libSSD1306

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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