Subversion Repositories libSSD1306

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  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. }
  717.