Home | History | Annotate | Download | only in lcd
      1 /*
      2  * Author: Jon Trulson <jtrulson (at) ics.com>
      3  * Copyright (c) 2015 Intel Corporation.
      4  *
      5  * Author: Tyler Gibson <tgibson (at) microsoft.com>
      6  * Copyright (c) 2015 Microsoft Corporation.
      7  *
      8  * Permission is hereby granted, free of charge, to any person obtaining
      9  * a copy of this software and associated documentation files (the
     10  * "Software"), to deal in the Software without restriction, including
     11  * without limitation the rights to use, copy, modify, merge, publish,
     12  * distribute, sublicense, and/or sell copies of the Software, and to
     13  * permit persons to whom the Software is furnished to do so, subject to
     14  * the following conditions:
     15  *
     16  * The above copyright notice and this permission notice shall be
     17  * included in all copies or substantial portions of the Software.
     18  *
     19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     22  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     23  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     24  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     25  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     26  */
     27 #include <unistd.h>
     28 #include <iostream>
     29 
     30 #include "eboled.h"
     31 
     32 using namespace upm;
     33 using namespace std;
     34 
     35 static uint16_t screenBuffer[BUFFER_SIZE];
     36 
     37 EBOLED::EBOLED(int spi, int CD, int reset) :
     38   m_spi(spi), m_gpioCD(CD), m_gpioRST(reset)
     39 {
     40   m_name = "EBOLED";
     41   m_textColor = COLOR_WHITE;
     42   m_textWrap = 0;
     43   m_textSize = 1;
     44   m_cursorX = 0;
     45   m_cursorY = 0;
     46 
     47   m_gpioCD.dir(mraa::DIR_OUT);
     48   m_gpioRST.dir(mraa::DIR_OUT);
     49 
     50   //1000000 is standard.
     51   m_spi.frequency(10000000);
     52 
     53   // reset the device
     54   m_gpioRST.write(1);
     55   usleep(5000);
     56   m_gpioRST.write(0);
     57   usleep(10000);
     58   m_gpioRST.write(1);
     59 
     60   command(CMD_DISPLAYOFF);
     61 
     62   command(CMD_SETDISPLAYCLOCKDIV);
     63   command(0x80);
     64 
     65   command(CMD_SETMULTIPLEX);
     66   command(0x2f);
     67 
     68   command(CMD_SETDISPLAYOFFSET);
     69   command(0x0);                 // no offset
     70 
     71   command(CMD_SETSTARTLINE | 0x0); // line #0
     72 
     73   command(CMD_CHARGEPUMP);      // enable charge pump
     74   command(0x14);
     75 
     76   command(CMD_NORMALDISPLAY);
     77   command(CMD_DISPLAYALLONRESUME);
     78 
     79   command(CMD_SEGREMAP | 0x1);  // reverse mapping (SEG0==COL127)
     80   command(CMD_COMSCANDEC);
     81 
     82   command(CMD_SETCOMPINS);      // custom COM PIN mapping
     83   command(0x12);
     84 
     85   command(CMD_SETCONTRAST);
     86   command(0x8f);
     87 
     88   command(CMD_SETPRECHARGE);
     89   command(0xf1);
     90 
     91   command(CMD_SETVCOMDESELECT);
     92   command(0x40);
     93 
     94   command(CMD_DISPLAYON);
     95 
     96   usleep(4500);
     97 
     98   setAddressingMode(HORIZONTAL);
     99 
    100   //Set Page Address range, required for horizontal addressing mode.
    101   command(CMD_SETPAGEADDRESS); // triple-byte cmd
    102   command(0x00); //Initial page address
    103   command(0x05); //Final page address
    104 
    105   //Set Column Address range, required for horizontal addressing mode.
    106   command(CMD_SETCOLUMNADDRESS); // triple-byte cmd
    107   command(0x20); // this display has a horizontal offset of 20 columns
    108   command(0x5f); // 64 columns wide - 0 based 63 offset
    109 }
    110 
    111 EBOLED::~EBOLED()
    112 {
    113   clear();
    114 }
    115 
    116 mraa::Result EBOLED::refresh()
    117 {
    118   mraa::Result error = mraa::SUCCESS;;
    119 
    120   m_gpioCD.write(1);            // data mode
    121   for(int i=0; i<BUFFER_SIZE; i++)
    122   {
    123     error = data(screenBuffer[i]);
    124     if(error != mraa::SUCCESS)
    125       return error;
    126   }
    127 
    128   return error;
    129 }
    130 
    131 mraa::Result EBOLED::write (std::string msg)
    132 {
    133   int len = msg.length();
    134   uint8_t temp_cursorX = m_cursorX;
    135   for (int idx = 0; idx < len; idx++)
    136   {
    137     if (msg[idx] == '\n')
    138     {
    139       m_cursorY += m_textSize * 9;
    140       temp_cursorX = m_cursorX;
    141     }
    142     else if (msg[idx] == '\r')
    143     {
    144       // skip em
    145     }
    146     else
    147     {
    148       drawChar(temp_cursorX, m_cursorY, msg[idx], m_textColor, m_textSize);
    149       temp_cursorX += m_textSize * 6;
    150 
    151       //textColor used to avoid wrapping if COLOR_BLACK is set.
    152       if (m_textWrap && (m_textColor > OLED_WIDTH - temp_cursorX - 6))
    153       {
    154         m_cursorY += m_textSize * 9;
    155         temp_cursorX = m_cursorX;
    156       }
    157     }
    158   }
    159   return mraa::SUCCESS;;
    160 }
    161 
    162 mraa::Result EBOLED::setCursor (int row, int column) {
    163   m_cursorX = column;
    164   m_cursorY = row;
    165   return mraa::SUCCESS;;
    166 }
    167 
    168 void EBOLED::setTextColor (uint8_t textColor) {
    169   m_textColor   = textColor;
    170 }
    171 
    172 void EBOLED::setTextSize (uint8_t size) {
    173   m_textSize = (size > 0) ? size : 1;
    174 }
    175 
    176 void EBOLED::setTextWrap (uint8_t wrap) {
    177   m_textWrap = wrap;
    178 }
    179 
    180 void EBOLED::drawChar (uint8_t x, uint8_t y, uint8_t data, uint8_t color, uint8_t size) {
    181   if( (x >= OLED_WIDTH)            || // Clip right
    182       (y >= OLED_HEIGHT)           || // Clip bottom
    183       ((x + 6 * size - 1) < 0)  || // Clip left
    184       ((y + 8 * size - 1) < 0))    // Clip top
    185   return;
    186 
    187   if (data < 0x20 || data > 0x7F) {
    188     data = 0x20; // space
    189   }
    190 
    191   for (int8_t i=0; i<6; i++ ) {
    192     uint8_t line;
    193     if (i == 6)
    194       line = 0x0;
    195     else
    196     {
    197       //32 offset to align standard ASCII range to index
    198       line = BasicFont[data - 32][i+1];
    199       for (int8_t j = 0; j<8; j++)
    200       {
    201         if (line & 0x1)
    202         {
    203           if (size == 1) // default size
    204             drawPixel(x+i, y+j, color);
    205           else
    206             drawRectangleFilled(x+(i*size), y+(j*size), size, size, color); // big size
    207         }
    208         line >>= 1;
    209       }
    210     }
    211   }
    212 }
    213 
    214 mraa::Result EBOLED::clear()
    215 {
    216   mraa::Result error = mraa::SUCCESS;;
    217 
    218   m_gpioCD.write(1);            // data mode
    219   for(int i=0; i<BUFFER_SIZE; i++)
    220   {
    221     error = data(0x0000);
    222     if(error != mraa::SUCCESS)
    223       return error;
    224   }
    225 
    226   return mraa::SUCCESS;;
    227 }
    228 
    229 mraa::Result EBOLED::home()
    230 {
    231   return setCursor(0, 0);
    232 }
    233 
    234 void EBOLED::drawPixel(int8_t x, int8_t y, uint8_t color)
    235 {
    236   if(x<0 || x>=OLED_WIDTH || y<0 || y>=OLED_HEIGHT)
    237     return;
    238 
    239   /* Screenbuffer is uint16 array, but pages are 8bit high so each buffer
    240    * index is two columns.  This means the index is based on x/2 and
    241    * OLED_WIDTH/2 = VERT_COLUMNS.
    242    *
    243    * Then to set the appropriate bit, we need to shift based on the y
    244    * offset in relation to the page and then adjust for high/low based
    245    * on the x position.
    246   */
    247 
    248   switch(color)
    249   {
    250     case COLOR_XOR:
    251       screenBuffer[(x/2) + ((y/8) * VERT_COLUMNS)] ^= (1<<(y%8+(x%2 * 8)));
    252       return;
    253     case COLOR_WHITE:
    254       screenBuffer[(x/2) + ((y/8) * VERT_COLUMNS)] |= (1<<(y%8+(x%2 * 8)));
    255       return;
    256     case COLOR_BLACK:
    257       screenBuffer[(x/2) + ((y/8) * VERT_COLUMNS)] &= ~(1<<(y%8+(x%2 * 8)));
    258       return;
    259   }
    260 }
    261 
    262 void EBOLED::drawLine(int8_t x0, int8_t y0, int8_t x1, int8_t y1, uint8_t color)
    263 {
    264   int16_t steep = abs(y1 - y0) > abs(x1 - x0);
    265 
    266   if (steep) {
    267     swap(x0, y0);
    268     swap(x1, y1);
    269   }
    270 
    271   if (x0 > x1) {
    272     swap(x0, x1);
    273     swap(y0, y1);
    274   }
    275 
    276   int16_t dx, dy;
    277   dx = x1 - x0;
    278   dy = abs (y1 - y0);
    279 
    280   int16_t err = dx / 2;
    281   int16_t ystep;
    282 
    283   if (y0 < y1) {
    284     ystep = 1;
    285   } else {
    286     ystep = -1;
    287   }
    288 
    289   for (; x0 <= x1; x0++) {
    290     if (steep) {
    291       drawPixel(y0, x0, color);
    292     } else {
    293       drawPixel(x0, y0, color);
    294     }
    295     err -= dy;
    296     if (err < 0) {
    297       y0 += ystep;
    298       err += dx;
    299     }
    300   }
    301 }
    302 
    303 void EBOLED::drawLineHorizontal(int8_t x, int8_t y, uint8_t width, uint8_t color)
    304 {
    305   drawLine(x, y, x+width-1, y, color);
    306 }
    307 
    308 void EBOLED::drawLineVertical(int8_t x, int8_t y, uint8_t height, uint8_t color)
    309 {
    310   drawLine(x, y, x, y+height-1, color);
    311 }
    312 
    313 void EBOLED::drawRectangle(int8_t x, int8_t y, uint8_t width, uint8_t height, uint8_t color)
    314 {
    315   drawLineHorizontal(x, y, width, color);
    316   drawLineHorizontal(x, y+height-1, color);
    317 
    318   uint8_t innerHeight = height - 2;
    319   if(innerHeight > 0)
    320   {
    321     drawLineVertical(x, y+1, innerHeight, color);
    322     drawLineVertical(x+width-1, y+1, innerHeight, color);
    323   }
    324 }
    325 
    326 void EBOLED::drawRoundedRectangle(int8_t x, int8_t y, int8_t width, int8_t height, int16_t radius, uint8_t color) {
    327   // smarter version
    328   drawLineHorizontal(x+radius  , y         , width-2*radius,  color); // Top
    329   drawLineHorizontal(x+radius  , y+height-1, width-2*radius,  color); // Bottom
    330   drawLineVertical(  x         , y+radius  , height-2*radius, color); // Left
    331   drawLineVertical(  x+width-1 , y+radius  , height-2*radius, color); // Right
    332   // draw four corners
    333   drawRoundCorners(x+radius        , y+radius         , radius, 1, color);
    334   drawRoundCorners(x+width-radius-1, y+radius         , radius, 2, color);
    335   drawRoundCorners(x+width-radius-1, y+height-radius-1, radius, 4, color);
    336   drawRoundCorners(x+radius        , y+height-radius-1, radius, 8, color);
    337 }
    338 
    339 void EBOLED::drawRectangleFilled(int8_t x, int8_t y, uint8_t width, uint8_t height, uint8_t color)
    340 {
    341   for (uint8_t i=x; i<x+width; i++) {
    342     drawLineVertical(i, y, height, color);
    343   }
    344 }
    345 
    346 void EBOLED::drawTriangle(int8_t x0, int8_t y0, int8_t x1, int8_t y1, int8_t x2, int8_t y2, uint8_t color)
    347 {
    348   drawLine(x0, y0, x1, y1, color);
    349   drawLine(x1, y1, x2, y2, color);
    350   drawLine(x2, y2, x0, y0, color);
    351 }
    352 
    353 void EBOLED::drawTriangleFilled ( int8_t x0, int8_t y0, int8_t x1, int8_t y1, int8_t x2, int8_t y2, uint8_t color) {
    354 
    355   int16_t a, b, y, last;
    356 
    357   // Sort coordinates by Y order (y2 >= y1 >= y0)
    358   if (y0 > y1) {
    359     swap(y0, y1); swap(x0, x1);
    360   }
    361   if (y1 > y2) {
    362     swap(y2, y1); swap(x2, x1);
    363   }
    364   if (y0 > y1) {
    365     swap(y0, y1); swap(x0, x1);
    366   }
    367 
    368   if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing
    369     a = b = x0;
    370     if(x1 < a)      a = x1;
    371     else if(x1 > b) b = x1;
    372     if(x2 < a)      a = x2;
    373     else if(x2 > b) b = x2;
    374     drawLineHorizontal(a, y0, b-a+1, color);
    375     return;
    376   }
    377 
    378   int16_t
    379     dx01 = x1 - x0,
    380     dy01 = y1 - y0,
    381     dx02 = x2 - x0,
    382     dy02 = y2 - y0,
    383     dx12 = x2 - x1,
    384     dy12 = y2 - y1;
    385   int32_t
    386     sa   = 0,
    387     sb   = 0;
    388 
    389   // For upper part of triangle, find scanline crossings for segments
    390   // 0-1 and 0-2.  If y1=y2 (flat-bottomed triangle), the scanline y1
    391   // is included here (and second loop will be skipped, avoiding a /0
    392   // error there), otherwise scanline y1 is skipped here and handled
    393   // in the second loop...which also avoids a /0 error here if y0=y1
    394   // (flat-topped triangle).
    395   if(y1 == y2) last = y1;   // Include y1 scanline
    396   else         last = y1-1; // Skip it
    397 
    398   for(y=y0; y<=last; y++) {
    399     a   = x0 + sa / dy01;
    400     b   = x0 + sb / dy02;
    401     sa += dx01;
    402     sb += dx02;
    403     /* longhand:
    404     a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
    405     b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
    406     */
    407     if(a > b) swap(a,b);
    408     drawLineHorizontal(a, y, b-a+1, color);
    409   }
    410 
    411   // For lower part of triangle, find scanline crossings for segments
    412   // 0-2 and 1-2.  This loop is skipped if y1=y2.
    413   sa = dx12 * (y - y1);
    414   sb = dx02 * (y - y0);
    415   for(; y<=y2; y++) {
    416     a   = x1 + sa / dy12;
    417     b   = x0 + sb / dy02;
    418     sa += dx12;
    419     sb += dx02;
    420     /* longhand:
    421     a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
    422     b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
    423     */
    424     if(a > b) swap(a,b);
    425     drawLineHorizontal(a, y, b-a+1, color);
    426   }
    427 }
    428 
    429 void EBOLED::drawCircle(int16_t x0, int16_t y0, int16_t radius, uint8_t color)
    430 {
    431   int16_t f = 1 - radius;
    432   int16_t ddF_x = 1;
    433   int16_t ddF_y = -2 * radius;
    434   int16_t x = 0;
    435   int16_t y = radius;
    436 
    437   drawPixel(x0  , y0+radius, color);
    438   drawPixel(x0  , y0-radius, color);
    439   drawPixel(x0+radius, y0  , color);
    440   drawPixel(x0-radius, y0  , color);
    441 
    442   while (x<y)
    443   {
    444     if (f >= 0)
    445     {
    446       y--;
    447       ddF_y += 2;
    448       f += ddF_y;
    449     }
    450     x++;
    451 
    452     ddF_x += 2;
    453     f += ddF_x;
    454 
    455     drawPixel(x0 + x, y0 + y, color);
    456     drawPixel(x0 - x, y0 + y, color);
    457     drawPixel(x0 + x, y0 - y, color);
    458     drawPixel(x0 - x, y0 - y, color);
    459     drawPixel(x0 + y, y0 + x, color);
    460     drawPixel(x0 - y, y0 + x, color);
    461     drawPixel(x0 + y, y0 - x, color);
    462     drawPixel(x0 - y, y0 - x, color);
    463   }
    464 }
    465 
    466 void EBOLED::drawRoundCorners( int8_t x0, int8_t y0, int16_t radius, uint8_t cornername, uint8_t color) {
    467   int16_t f     = 1 - radius;
    468   int16_t ddF_x = 1;
    469   int16_t ddF_y = -2 * radius;
    470   int16_t x     = 0;
    471   int16_t y     = radius;
    472 
    473   while (x<y) {
    474     if (f >= 0) {
    475       y--;
    476       ddF_y += 2;
    477       f     += ddF_y;
    478     }
    479     x++;
    480     ddF_x += 2;
    481     f     += ddF_x;
    482     if (cornername & 0x4) {
    483       drawPixel(x0 + x, y0 + y, color);
    484       drawPixel(x0 + y, y0 + x, color);
    485     }
    486     if (cornername & 0x2) {
    487       drawPixel(x0 + x, y0 - y, color);
    488       drawPixel(x0 + y, y0 - x, color);
    489     }
    490     if (cornername & 0x8) {
    491       drawPixel(x0 - y, y0 + x, color);
    492       drawPixel(x0 - x, y0 + y, color);
    493     }
    494     if (cornername & 0x1) {
    495       drawPixel(x0 - y, y0 - x, color);
    496       drawPixel(x0 - x, y0 - y, color);
    497     }
    498   }
    499 }
    500 
    501 void EBOLED::drawCircleFilled(int8_t x0, int8_t y0, int16_t radius, uint8_t color) {
    502   drawLineVertical(x0, y0-radius, 2*radius+1, color);
    503   drawRoundedCornersFilled(x0, y0, radius, 3, 0, color);
    504 }
    505 
    506 void EBOLED::drawRoundedCornersFilled(int8_t x0, int8_t y0, int16_t radius, uint8_t cornername, int16_t delta, uint8_t color) {
    507 
    508   int16_t f     = 1 - radius;
    509   int16_t ddF_x = 1;
    510   int16_t ddF_y = -2 * radius;
    511   int16_t x     = 0;
    512   int16_t y     = radius;
    513 
    514   while (x<y) {
    515     if (f >= 0) {
    516       y--;
    517       ddF_y += 2;
    518       f     += ddF_y;
    519     }
    520     x++;
    521     ddF_x += 2;
    522     f     += ddF_x;
    523 
    524     if (cornername & 0x1) {
    525       drawLineVertical(x0+x, y0-y, 2*y+1+delta, color);
    526       drawLineVertical(x0+y, y0-x, 2*x+1+delta, color);
    527     }
    528     if (cornername & 0x2) {
    529       drawLineVertical(x0-x, y0-y, 2*y+1+delta, color);
    530       drawLineVertical(x0-y, y0-x, 2*x+1+delta, color);
    531     }
    532   }
    533 }
    534 
    535 void EBOLED::fillScreen(uint8_t color)
    536 {
    537   drawRectangleFilled(0, 0, OLED_WIDTH-1, OLED_HEIGHT-1, color);
    538 }
    539 
    540 mraa::Result EBOLED::setAddressingMode(displayAddressingMode mode)
    541 {
    542   mraa::Result rv;
    543 
    544   rv = command(CMD_MEMORYADDRMODE);
    545   rv = command(mode);
    546 
    547   return rv;
    548 }
    549 
    550 mraa::Result EBOLED::command(uint8_t cmd)
    551 {
    552   m_gpioCD.write(0);            // command mode
    553   m_spi.writeByte(cmd);
    554   return mraa::SUCCESS;
    555 }
    556 
    557 mraa::Result EBOLED::data(uint16_t data)
    558 {
    559   m_spi.write_word(data);
    560   return mraa::SUCCESS;
    561 }
    562 
    563 void EBOLED::clearScreenBuffer()
    564 {
    565   for(int i=0; i<BUFFER_SIZE;i++)
    566     screenBuffer[i] = 0x0000;
    567 }
    568