Subversion Repositories DashDisplay

Rev

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