Subversion Repositories libOLED

Rev

Rev 8 | Go to most recent revision | 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. // Dim the display
  330. // dim = true: display is dimmed
  331. // dim = false: display is normal
  332. void display_t::dim(uint8_t contrast)
  333. {
  334.  
  335.   // the range of contrast to too small to be really useful
  336.   // it is useful to dim the display
  337.  
  338.   oledSetCD(0);
  339.   oledWrite(SSD1306_SETCONTRAST);
  340.   oledWrite(contrast);
  341. }
  342.  
  343. void display_t::display(void)
  344. {
  345.   oledSetCD(0);
  346.   // select entire display as window to write into
  347.   oledWrite(SSD1306_COLUMNADDR);
  348.   oledWrite(0);              // Column start address (0 = reset)
  349.   oledWrite(m_ramwidth - 1); // Column end address (127 = reset)
  350.  
  351.   oledWrite(SSD1306_PAGEADDR);
  352.   oledWrite(0);                        // Page start address (0 = reset)
  353.   oledWrite((m_height == 64) ? 7 : 3); // Page end address
  354.  
  355.   int row;
  356.  
  357.   int col = m_ramwidth == 132 ? 2 : 0;
  358.   for (row = 0; row < m_height / 8; row++)
  359.   {
  360.     oledSetCD(0);
  361.     // set the cursor to
  362.     oledWrite(0xB0 + row);        // set page address
  363.     oledWrite(col & 0xf);         // set lower column address
  364.     oledWrite(0x10 | (col >> 4)); // set higher column address
  365.  
  366.     oledSetCD(1);
  367.     oledWrite(m_data + row * m_width, m_width);
  368.   }
  369. }
  370.  
  371. // clear everything
  372. void display_t::clearDisplay(colour_t colour)
  373. {
  374.   switch (colour)
  375.   {
  376.   case WHITE:
  377.   case OVERLAY:
  378.     memset(m_data, 255, dataSize(m_width, m_height));
  379.     break;
  380.   case BLACK:
  381.     memset(m_data, 0, dataSize(m_width, m_height));
  382.     break;
  383.   case INVERT:
  384.     for (size_t i = 0; i < dataSize(m_width, m_height); i++)
  385.       m_data[i] ^= 255;
  386.     break;
  387.   }
  388. }
  389.  
  390. void display_t::drawRectangle(int16_t x1, int16_t y1, int16_t x2, int16_t y2,
  391.                               colour_t color)
  392. {
  393.   for (int16_t x = x1; x <= x2; x++)
  394.     for (int16_t y = y1; y < y2; y++)
  395.     {
  396.       switch (color)
  397.       {
  398.       case BLACK:
  399.         m_data[x + (y / 8) * m_width] &= ~(1 << (y & 7));
  400.         break;
  401.  
  402.       default:
  403.       case WHITE:
  404.       case OVERLAY:
  405.         m_data[x + (y / 8) * m_width] |= (1 << (y & 7));
  406.         break;
  407.  
  408.       case INVERT:
  409.         m_data[x + (y / 8) * m_width] ^= (1 << (y & 7));
  410.         break;
  411.       }
  412.     }
  413. }
  414.  
  415. /* using Bresenham draw algorithm */
  416. void display_t::drawLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2,
  417.                          colour_t colour, int8_t pattern)
  418. {
  419.   int16_t x, y, dx, dy,  // deltas
  420.       dx2, dy2,          // scaled deltas
  421.       ix, iy,            // increase rate on the x and y axis
  422.       err;               // the error term
  423.   int8_t patt = pattern; // drawing pattern bit mask 1= solid 10= dots , 111000 equal dot/dash
  424.  
  425.   uint16_t i; // looping variable
  426.  
  427.   setPixelMode(colour);
  428.  
  429.   // identify the first pixel
  430.   x = x1;
  431.   y = y1;
  432.  
  433.   // difference between starting and ending points
  434.   dx = x2 - x1;
  435.   dy = y2 - y1;
  436.  
  437.   // calculate direction of the vector and store in ix and iy
  438.   if (dx >= 0)
  439.     ix = 1;
  440.  
  441.   if (dx < 0)
  442.   {
  443.     ix = -1;
  444.     dx = abs(dx);
  445.   }
  446.  
  447.   if (dy >= 0)
  448.     iy = 1;
  449.  
  450.   if (dy < 0)
  451.   {
  452.     iy = -1;
  453.     dy = abs(dy);
  454.   }
  455.  
  456.   // scale deltas and store in dx2 and dy2
  457.   dx2 = dx * 2;
  458.   dy2 = dy * 2;
  459.  
  460.   // all  variables are set and it's time to enter the main loop.
  461.  
  462.   if (dx > dy) // dx is the major axis
  463.   {
  464.     // initialize the error term
  465.     err = dy2 - dx;
  466.  
  467.     for (i = 0; i <= dx; i++)
  468.     {
  469.       // draw pattern based on using pattern as a sequential bit mask.
  470.       if (patt & 1)
  471.         drawPixel(x, y, 1);
  472.       patt = (patt <= 1) ? pattern : patt >> 1;
  473.  
  474.       if (err >= 0)
  475.       {
  476.         err -= dx2;
  477.         y += iy;
  478.       }
  479.       err += dy2;
  480.       x += ix;
  481.     }
  482.   }
  483.  
  484.   else // dy is the major axis
  485.   {
  486.     // initialize the error term
  487.     err = dx2 - dy;
  488.  
  489.     for (i = 0; i <= dy; i++)
  490.     {
  491.       // draw pattern based on using pattern as a sequential bit mask.
  492.       if (patt & 1)
  493.         drawPixel(x, y, 1);
  494.       patt = (patt <= 1) ? pattern : patt >> 1;
  495.       if (err >= 0)
  496.       {
  497.         err -= dy2;
  498.         x += ix;
  499.       }
  500.       err += dx2;
  501.       y += iy;
  502.     }
  503.   }
  504. }
  505.