Subversion Repositories libOLED

Rev

Rev 11 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * displayclass.cpp
  3.  *
  4.  *  Created on: 31 Oct 2020
  5.  *      Author: mike
  6.  */
  7.  
  8. #include "libOLED/displayclass.H"
  9.  
  10. #include <cstring>
  11. #include <cstdlib>
  12. namespace
  13. {
  14.   uint8_t const SSD1306_SETCONTRAST = 0x81;
  15.   uint8_t const SSD1306_DISPLAYALLON_RESUME = 0xA4;
  16.   uint8_t const SSD1306_DISPLAYALLON = 0xA5;
  17.   uint8_t const SSD1306_NORMALDISPLAY = 0xA6;
  18.   uint8_t const SSD1306_INVERTDISPLAY = 0xA7;
  19.   uint8_t const SSD1306_DISPLAYOFF = 0xAE;
  20.   uint8_t const SSD1306_DISPLAYON = 0xAF;
  21.  
  22.   uint8_t const SSD1306_SETDISPLAYOFFSET = 0xD3;
  23.   uint8_t const SSD1306_SETCOMPINS = 0xDA;
  24.  
  25.   uint8_t const SSD1306_SETVCOMDETECT = 0xDB;
  26.  
  27.   uint8_t const SSD1306_SETDISPLAYCLOCKDIV = 0xD5;
  28.   uint8_t const SSD1306_SETPRECHARGE = 0xD9;
  29.  
  30.   uint8_t const SSD1306_SETMULTIPLEX = 0xA8;
  31.  
  32.   uint8_t const SSD1306_SETLOWCOLUMN = 0x00;
  33.   uint8_t const SSD1306_SETHIGHCOLUMN = 0x10;
  34.  
  35.   uint8_t const SSD1306_SETSTARTLINE = 0x40;
  36.  
  37.   uint8_t const SSD1306_MEMORYMODE = 0x20;
  38.   uint8_t const SSD1306_COLUMNADDR = 0x21;
  39.   uint8_t const SSD1306_PAGEADDR = 0x22;
  40.  
  41.   uint8_t const SSD1306_COMSCANINC = 0xC0;
  42.   uint8_t const SSD1306_COMSCANDEC = 0xC8;
  43.  
  44.   uint8_t const SSD1306_SEGREMAP = 0xA0;
  45.  
  46.   uint8_t const SSD1306_CHARGEPUMP = 0x8D;
  47.  
  48.   uint8_t const SSD1306_EXTERNALVCC = 0x1;
  49.   uint8_t const SSD1306_SWITCHCAPVCC = 0x2;
  50.  
  51.   // Scrolling #defines
  52.   uint8_t const SSD1306_ACTIVATE_SCROLL = 0x2F;
  53.   uint8_t const SSD1306_DEACTIVATE_SCROLL = 0x2E;
  54.   uint8_t const SSD1306_SET_VERTICAL_SCROLL_AREA = 0xA3;
  55.   uint8_t const SSD1306_RIGHT_HORIZONTAL_SCROLL = 0x26;
  56.   uint8_t const SSD1306_LEFT_HORIZONTAL_SCROLL = 0x27;
  57.   uint8_t const SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29;
  58.   uint8_t const SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A;
  59.  
  60.   template <class T>
  61.   void
  62.   swap(T &x, T &y)
  63.   {
  64.     T temp = x;
  65.     x = y;
  66.     y = temp;
  67.   }
  68.  
  69.   template <class T>
  70.   T abs(T x)
  71.   {
  72.     return x < 0 ? -x : x;
  73.   }
  74.  
  75. }
  76. // provided to allow a destructor to destroy something not deleted
  77. // this is only OK because destructors shouldnt be needed
  78. void operator delete(void *data, unsigned int f)
  79. {
  80.   (void)data;
  81.   (void)f;
  82. }
  83.  
  84. // provided to implement an "error handler" on pure virtual
  85. extern "C" void
  86. __cxa_pure_virtual()
  87. {
  88.   while (1)
  89.     ;
  90. }
  91.  
  92. display_t::display_t(int const width, int const height, int const ramwidth,
  93.                      uint8_t *const data) : m_width(width), m_height(height), m_ramwidth(ramwidth), m_cursor_x(0), m_cursor_y(0), m_rotation(0), m_colour(WHITE), m_data(data)
  94. {
  95. }
  96.  
  97. display_t::~display_t()
  98. {
  99. }
  100.  
  101. void display_t::reset()
  102. {
  103.   oledReset();
  104. }
  105.  
  106. void display_t::init()
  107. {
  108.   uint8_t const vccstate = SSD1306_EXTERNALVCC;
  109.  
  110.   oledSetCD(0);
  111.  
  112.   // Init sequence for 128x32 or 128x64  OLED module
  113.   oledWrite(SSD1306_DISPLAYOFF);         // 0xAE
  114.   oledWrite(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5
  115.   oledWrite(0x80);                       // the suggested ratio 0x80
  116.   oledWrite(SSD1306_SETMULTIPLEX);       // 0xA8
  117.   oledWrite(m_height - 1);
  118.   oledWrite(SSD1306_SETDISPLAYOFFSET);   // 0xD3
  119.   oledWrite(0x0);                        // no offset
  120.   oledWrite(SSD1306_SETSTARTLINE | 0x0); // line #0
  121.   oledWrite(SSD1306_CHARGEPUMP);         // 0x8D
  122.   oledWrite(vccstate == SSD1306_EXTERNALVCC ? 0x10 : 0x14);
  123.   oledWrite(SSD1306_MEMORYMODE); // 0x20
  124.   oledWrite(0x00);               // 0x0 act like ks0108
  125.   oledWrite(SSD1306_SEGREMAP | 0x1);
  126.   oledWrite(SSD1306_COMSCANDEC);
  127.   oledWrite(SSD1306_SETCOMPINS); // 0xDA
  128.   oledWrite(m_height == 32 ? 0x02 : 0x12);
  129.   oledWrite(SSD1306_SETCONTRAST); // 0x81
  130.   oledWrite(vccstate == SSD1306_EXTERNALVCC ? 0x9F : 0xCF);
  131.   oledWrite(SSD1306_SETPRECHARGE); // 0xd9
  132.   oledWrite(vccstate == SSD1306_EXTERNALVCC ? 0x22 : 0xF1);
  133.   oledWrite(SSD1306_SETVCOMDETECT); // 0xDB
  134.   oledWrite(0x40);
  135.   oledWrite(SSD1306_DISPLAYALLON_RESUME); // 0xA4
  136.   oledWrite(SSD1306_NORMALDISPLAY);       // 0xA6
  137.  
  138.   oledWrite(SSD1306_DISPLAYON); //--turn on oled panel
  139.  
  140.   clearDisplay();
  141. }
  142.  
  143. uint8_t
  144. display_t::getRotation(void)
  145. {
  146.   return m_rotation;
  147. }
  148.  
  149. int16_t
  150. display_t::width(void)
  151. {
  152.   switch (m_rotation)
  153.   {
  154.   case 0:
  155.     return m_width;
  156.     break;
  157.   case 1:
  158.     return m_width;
  159.     break;
  160.   case 2:
  161.     return m_height;
  162.     break;
  163.   case 3:
  164.     return -m_width;
  165.     break;
  166.   }
  167.   return 0;
  168. }
  169.  
  170. int16_t
  171. display_t::height(void)
  172. {
  173.   switch (m_rotation)
  174.   {
  175.   case 0:
  176.     return m_height;
  177.     break;
  178.   case 1:
  179.     return m_height;
  180.     break;
  181.   case 2:
  182.     return m_width;
  183.     break;
  184.   case 3:
  185.     return -m_height;
  186.     break;
  187.   }
  188.   return 0;
  189. }
  190.  
  191. // the most basic function, set a single pixel
  192. void display_t::drawPixel(int16_t x, int16_t y, bool pixel)
  193. {
  194.   if ((x < 0) || (x >= m_width) || (y < 0) || (y >= m_height))
  195.     return;
  196.  
  197.   // check rotation, move pixel around if necessary
  198.   switch (m_rotation)
  199.   {
  200.   case 1:
  201.     swap(x, y);
  202.     x = m_width - x - 1;
  203.     break;
  204.   case 2:
  205.     x = m_width - x - 1;
  206.     y = m_height - y - 1;
  207.     break;
  208.   case 3:
  209.     swap(x, y);
  210.     y = m_height - y - 1;
  211.     break;
  212.   }
  213.  
  214.   // x is which column
  215.   //  BLACK,  and 0,             invert 0
  216.   //  WHITE,   and 0,            invert 1
  217.   //  OVERLAY, and 1 (preserve) , invert 0/
  218.   // INVERT,  and 1, (preserve) , invert 1
  219.  
  220.   switch (m_colour)
  221.   {
  222.   case BLACK:
  223.   case WHITE:
  224.     m_data[x + (y / 8) * m_width] &= ~(1 << (y & 7));
  225.     break;
  226.   default:
  227.     break;
  228.   }
  229.   uint8_t pixData = 0;
  230.   switch (m_colour)
  231.   {
  232.   case BLACK:
  233.     pixData = pixel ? 0 : 1;
  234.     break;
  235.   case WHITE:
  236.   case OVERLAY:
  237.   case INVERT:
  238.     pixData = pixel ? 1 : 0;
  239.     break;
  240.   }
  241.  
  242.   m_data[x + (y / 8) * m_width] ^= (pixData << (y & 7));
  243. }
  244.  
  245. void display_t::invertDisplay(uint8_t i)
  246. {
  247.   oledSetCD(0);
  248.   oledWrite(i ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY);
  249. }
  250.  
  251. // startscrollright
  252. // Activate a right handed scroll for rows start through stop
  253. // Hint, the display is 16 rows tall. To scroll the whole display, run:
  254. // display.scrollright(0x00, 0x0F)
  255. void display_t::startscrollright(uint8_t start, uint8_t stop)
  256. {
  257.   oledSetCD(0);
  258.   oledWrite(SSD1306_RIGHT_HORIZONTAL_SCROLL);
  259.   oledWrite(0X00);
  260.   oledWrite(start);
  261.   oledWrite(0X00);
  262.   oledWrite(stop);
  263.   oledWrite(0X00);
  264.   oledWrite(0XFF);
  265.   oledWrite(SSD1306_ACTIVATE_SCROLL);
  266. }
  267.  
  268. // startscrollleft
  269. // Activate a right handed scroll for rows start through stop
  270. // Hint, the display is 16 rows tall. To scroll the whole display, run:
  271. // display.scrollright(0x00, 0x0F)
  272. void display_t::startscrollleft(uint8_t start, uint8_t stop)
  273. {
  274.   oledSetCD(0);
  275.   oledWrite(SSD1306_LEFT_HORIZONTAL_SCROLL);
  276.   oledWrite(0X00);
  277.   oledWrite(start);
  278.   oledWrite(0X00);
  279.   oledWrite(stop);
  280.   oledWrite(0X00);
  281.   oledWrite(0XFF);
  282.   oledWrite(SSD1306_ACTIVATE_SCROLL);
  283. }
  284.  
  285. // startscrolldiagright
  286. // Activate a diagonal scroll for rows start through stop
  287. // Hint, the display is 16 rows tall. To scroll the whole display, run:
  288. // display.scrollright(0x00, 0x0F)
  289. void display_t::startscrolldiagright(uint8_t start, uint8_t stop)
  290. {
  291.   oledSetCD(0);
  292.   oledWrite(SSD1306_SET_VERTICAL_SCROLL_AREA);
  293.   oledWrite(0X00);
  294.   oledWrite(m_height);
  295.   oledWrite(SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL);
  296.   oledWrite(0X00);
  297.   oledWrite(start);
  298.   oledWrite(0X00);
  299.   oledWrite(stop);
  300.   oledWrite(0X01);
  301.   oledWrite(SSD1306_ACTIVATE_SCROLL);
  302. }
  303.  
  304. // startscrolldiagleft
  305. // Activate a diagonal scroll for rows start through stop
  306. // Hint, the display is 16 rows tall. To scroll the whole display, run:
  307. // display.scrollright(0x00, 0x0F)
  308. void display_t::startscrolldiagleft(uint8_t start, uint8_t stop)
  309. {
  310.   oledSetCD(0);
  311.   oledWrite(SSD1306_SET_VERTICAL_SCROLL_AREA);
  312.   oledWrite(0X00);
  313.   oledWrite(m_height);
  314.   oledWrite(SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL);
  315.   oledWrite(0X00);
  316.   oledWrite(start);
  317.   oledWrite(0X00);
  318.   oledWrite(stop);
  319.   oledWrite(0X01);
  320.   oledWrite(SSD1306_ACTIVATE_SCROLL);
  321. }
  322.  
  323. void display_t::stopscroll(void)
  324. {
  325.   oledSetCD(0);
  326.   oledWrite(SSD1306_DEACTIVATE_SCROLL);
  327. }
  328.  
  329.  
  330. void display_t::dim(uint8_t contrast)
  331. {
  332.  
  333.   // the range of contrast to too small to be really useful
  334.   // it is useful to dim the display
  335.  
  336.   oledSetCD(0);
  337.   oledWrite(SSD1306_SETCONTRAST);
  338.   oledWrite(contrast);
  339. }
  340.  
  341. void display_t::display(void)
  342. {
  343.   oledSetCD(0);
  344.   // select entire display as window to write into
  345.   oledWrite(SSD1306_COLUMNADDR);
  346.   oledWrite(0);              // Column start address (0 = reset)
  347.   oledWrite(m_ramwidth - 1); // Column end address (127 = reset)
  348.  
  349.   oledWrite(SSD1306_PAGEADDR);
  350.   oledWrite(0);                        // Page start address (0 = reset)
  351.   oledWrite((m_height == 64) ? 7 : 3); // Page end address
  352.  
  353.   int row;
  354.  
  355.   int col = m_ramwidth == 132 ? 2 : 0;
  356.   for (row = 0; row < m_height / 8; row++)
  357.   {
  358.     oledSetCD(0);
  359.     // set the cursor to
  360.     oledWrite(0xB0 + row);        // set page address
  361.     oledWrite(col & 0xf);         // set lower column address
  362.     oledWrite(0x10 | (col >> 4)); // set higher column address
  363.  
  364.     oledSetCD(1);
  365.     oledWrite(m_data + row * m_width, m_width);
  366.   }
  367. }
  368.  
  369. // clear everything
  370. void display_t::clearDisplay(colour_t colour)
  371. {
  372.   switch (colour)
  373.   {
  374.   case WHITE:
  375.   case OVERLAY:
  376.     memset(m_data, 255, dataSize(m_width, m_height));
  377.     break;
  378.   case BLACK:
  379.     memset(m_data, 0, dataSize(m_width, m_height));
  380.     break;
  381.   case INVERT:
  382.     for (size_t i = 0; i < dataSize(m_width, m_height); i++)
  383.       m_data[i] ^= 255;
  384.     break;
  385.   }
  386. }
  387.  
  388. void display_t::drawRectangle(int16_t x1, int16_t y1, int16_t x2, int16_t y2,
  389.                               colour_t color)
  390. {
  391.   for (int16_t x = x1; x <= x2; x++)
  392.     for (int16_t y = y1; y < y2; y++)
  393.     {
  394.       switch (color)
  395.       {
  396.       case BLACK:
  397.         m_data[x + (y / 8) * m_width] &= ~(1 << (y & 7));
  398.         break;
  399.  
  400.       default:
  401.       case WHITE:
  402.       case OVERLAY:
  403.         m_data[x + (y / 8) * m_width] |= (1 << (y & 7));
  404.         break;
  405.  
  406.       case INVERT:
  407.         m_data[x + (y / 8) * m_width] ^= (1 << (y & 7));
  408.         break;
  409.       }
  410.     }
  411. }
  412.  
  413. /* using Bresenham draw algorithm */
  414. void display_t::drawLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2,
  415.                          colour_t colour, int8_t pattern)
  416. {
  417.   int16_t x, y, dx, dy,  // deltas
  418.       dx2, dy2,          // scaled deltas
  419.       ix, iy,            // increase rate on the x and y axis
  420.       err;               // the error term
  421.   int8_t patt = pattern; // drawing pattern bit mask 1= solid 10= dots , 111000 equal dot/dash
  422.  
  423.   uint16_t i; // looping variable
  424.  
  425.   setPixelMode(colour);
  426.  
  427.   // identify the first pixel
  428.   x = x1;
  429.   y = y1;
  430.  
  431.   // difference between starting and ending points
  432.   dx = x2 - x1;
  433.   dy = y2 - y1;
  434.  
  435.   // calculate direction of the vector and store in ix and iy
  436.   if (dx >= 0)
  437.     ix = 1;
  438.  
  439.   if (dx < 0)
  440.   {
  441.     ix = -1;
  442.     dx = abs(dx);
  443.   }
  444.  
  445.   if (dy >= 0)
  446.     iy = 1;
  447.  
  448.   if (dy < 0)
  449.   {
  450.     iy = -1;
  451.     dy = abs(dy);
  452.   }
  453.  
  454.   // scale deltas and store in dx2 and dy2
  455.   dx2 = dx * 2;
  456.   dy2 = dy * 2;
  457.  
  458.   // all  variables are set and it's time to enter the main loop.
  459.  
  460.   if (dx > dy) // dx is the major axis
  461.   {
  462.     // initialize the error term
  463.     err = dy2 - dx;
  464.  
  465.     for (i = 0; i <= dx; i++)
  466.     {
  467.       // draw pattern based on using pattern as a sequential bit mask.
  468.       if (patt & 1)
  469.         drawPixel(x, y, 1);
  470.       patt = (patt <= 1) ? pattern : patt >> 1;
  471.  
  472.       if (err >= 0)
  473.       {
  474.         err -= dx2;
  475.         y += iy;
  476.       }
  477.       err += dy2;
  478.       x += ix;
  479.     }
  480.   }
  481.  
  482.   else // dy is the major axis
  483.   {
  484.     // initialize the error term
  485.     err = dx2 - dy;
  486.  
  487.     for (i = 0; i <= dy; i++)
  488.     {
  489.       // draw pattern based on using pattern as a sequential bit mask.
  490.       if (patt & 1)
  491.         drawPixel(x, y, 1);
  492.       patt = (patt <= 1) ? pattern : patt >> 1;
  493.       if (err >= 0)
  494.       {
  495.         err -= dy2;
  496.         x += ix;
  497.       }
  498.       err += dx2;
  499.       y += iy;
  500.     }
  501.   }
  502. }
  503.