dd

Dependencies:   Final HCSR04 TB6612FNG

Revision:
97:b483e656bd14
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/oled/SSD1306-Library.cpp	Sun Jun 16 04:44:35 2019 +0000
@@ -0,0 +1,525 @@
+/*
+ * SSD1306-Library.cpp
+ *
+ *  Created on: 19 Apr 2017
+ *  Author: ebj
+ *
+ *  I2C version
+ *     CS   GND
+ *     DC   GND  for i2c addr 0x3C, VCC for addr 0x3D
+ *     RES   Vcc
+ */
+
+#include "mbed.h"
+
+#include "SSD1306-Library.h"
+
+extern Serial pc;
+
+extern "C" {
+}
+
+#define NUM_ELEMENTS(x)   ((sizeof x)/(sizeof x[0]))
+
+
+I2C i2c(I2C_SDA, I2C_SCL);
+DigitalOut rst(D9); //D13);         //reset pin on D13
+
+
+// the memory buffer for the LCD
+static uint8_t buffer[SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH / 8];
+
+volatile uint8_t dma_pause = 0;
+
+SSD1306::SSD1306(int16_t w, int16_t h) : Adafruit_GFX(w, h) {
+}
+
+#define ssd1306_swap(a, b) { int16_t t = a; a = b; b = t; }
+
+void SSD1306::hw_setup() {
+   i2c.frequency(400000);
+}
+
+
+// the most basic function, set a single pixel
+void SSD1306::drawPixel(int16_t x, int16_t y, uint16_t color) {
+   if ((x < 0) || (x >= width()) || (y < 0) || (y >= height()))
+      return;
+
+   // check rotation, move pixel around if necessary
+   switch (rotation) {
+   case 1:
+      ssd1306_swap(x, y);
+      x = WIDTH - x - 1;
+      break;
+   case 2:
+      x = WIDTH - x - 1;
+      y = HEIGHT - y - 1;
+      break;
+   case 3:
+      ssd1306_swap(x, y);
+      y = HEIGHT - y - 1;
+      break;
+   }
+
+   // x is which column
+   uint8_t *p = &buffer[x + (y/8)*SSD1306_LCDWIDTH];
+   uint8_t v = 1 << (y & 7);
+
+   switch (color) {
+   case WHITE:
+      *p |= v;
+      break;
+   case BLACK:
+      *p &= ~v;
+      break;
+   case INVERSE:
+      *p ^= v;
+      break;
+   }
+
+}
+
+void SSD1306::_sendData(const uint8_t *blk, uint32_t len, bool isData) {
+    const uint8_t *p = blk;
+
+   //pc.printf("SendData...\r\n");
+    // now send the data
+    uint8_t control = 0x00 | (isData ? 0x40 : 0x00);
+
+ 
+    if (isData) {
+      i2c.start();
+      //pc.printf("%0.2x ", *p);
+       i2c.write(0x3C<<1);
+       
+       //control |= 0x80;
+       i2c.write(control);
+      
+       for (int32_t i=0; i<len; i++, p++) {
+          //pc.printf("%0.2x ", *p);
+           int error = i2c.write(*p);
+           //int error = 1;
+           //wait(0.1);
+           if (error != 1)
+               pc.printf("I2C error: %0.2d\r\n", error);
+       }
+       i2c.stop();
+
+    } else {
+       for (int32_t i=0; i<len; i++, p++) {
+          i2c.start();
+          //pc.printf("%0.2x ", *p);
+           i2c.write(0x3C<<1);
+           i2c.write(control);
+           int error = i2c.write(*p);
+           //int error = 1;
+           //wait(0.1);
+           i2c.stop();
+           if (error != 1)
+               pc.printf("I2C error: %0.2d\r\n", error);
+       }
+    }
+}
+
+void SSD1306::sendCommands(const uint8_t *blk, uint32_t len) {
+   _sendData(blk, len, false);
+}
+
+void SSD1306::sendData(const uint8_t *blk, uint32_t len) {
+   _sendData(blk, len, true);
+}
+
+void SSD1306::begin(bool reset) {
+    if (reset) {      //pulse the reset pin -- maybe replace with RC network
+       rst = 1;
+       wait_ms(1);
+       rst = 0;
+       wait_ms(10);
+       rst = 1;   
+    }
+       
+   const uint8_t cmds[] = {
+      SSD1306_DISPLAYOFF,
+      SSD1306_SETDISPLAYCLOCKDIV,
+      0x80,                                   // the suggested ratio 0x80
+      SSD1306_SETMULTIPLEX,
+      SSD1306_LCDHEIGHT - 1,
+      SSD1306_SETDISPLAYOFFSET,
+      0x0,                                    // no offset
+      SSD1306_SETSTARTLINE | 0x0,             // line #0
+      SSD1306_CHARGEPUMP,
+      0x14,
+      SSD1306_MEMORYMODE,
+      0x00,                                   // 0x0 act like ks0108
+      SSD1306_SEGREMAP | 0x1,
+      SSD1306_COMSCANDEC,
+      SSD1306_SETCOMPINS,
+      0x12,
+      SSD1306_SETCONTRAST,
+      0xCF,
+      SSD1306_SETPRECHARGE,
+      0xF1,
+      SSD1306_SETVCOMDETECT,
+      0x40,
+      SSD1306_DISPLAYALLON_RESUME,
+      SSD1306_NORMALDISPLAY,
+      SSD1306_DEACTIVATE_SCROLL,
+      SSD1306_DISPLAYON                     //--turn on oled panel
+   };
+
+        sendCommands(cmds, NUM_ELEMENTS(cmds));
+
+}
+
+
+void SSD1306::display(void) {
+   const uint8_t cmds[] = {
+      SSD1306_COLUMNADDR,
+      0,               // Column start address (0 = reset)
+      SSD1306_LCDWIDTH - 1,      // Column end address (127 = reset)
+      SSD1306_PAGEADDR,
+      0,             // Page start address (0 = reset)
+      7             // Page end address
+      };
+
+    sendCommands(cmds, NUM_ELEMENTS(cmds));
+   //now send the screen image
+   sendData(buffer, NUM_ELEMENTS(buffer));
+}
+
+void SSD1306::invertDisplay(uint8_t i) {
+   const uint8_t normalCmd[] = { SSD1306_NORMALDISPLAY };
+   const uint8_t invertCmd[] = { SSD1306_INVERTDISPLAY };
+        sendCommands( i ? invertCmd : normalCmd, 1);
+}
+
+
+void SSD1306::_scroll(uint8_t mode, uint8_t start, uint8_t stop) {
+   uint8_t cmds[] = { mode, 0, start, 0, stop, 0, 0xFF, SSD1306_ACTIVATE_SCROLL };
+        sendCommands(cmds, NUM_ELEMENTS(cmds));
+}
+// startscrollright
+// Activate a right handed scroll for rows start through stop
+// Hint, the display is 16 rows tall. To scroll the whole display, run:
+// display.scrollright(0x00, 0x0F)
+void SSD1306::startscrollright(uint8_t start, uint8_t stop) {
+   _scroll(SSD1306_RIGHT_HORIZONTAL_SCROLL, start, stop);
+}
+
+// startscrollleft
+// Activate a right handed scroll for rows start through stop
+// Hint, the display is 16 rows tall. To scroll the whole display, run:
+// display.scrollright(0x00, 0x0F)
+void SSD1306::startscrollleft(uint8_t start, uint8_t stop) {
+   _scroll(SSD1306_LEFT_HORIZONTAL_SCROLL, start, stop);
+}
+
+// startscrolldiagright
+// Activate a diagonal scroll for rows start through stop
+// Hint, the display is 16 rows tall. To scroll the whole display, run:
+// display.scrollright(0x00, 0x0F)
+void SSD1306::startscrolldiagright(uint8_t start, uint8_t stop) {
+    uint8_t cmds[] = { 
+   SSD1306_SET_VERTICAL_SCROLL_AREA,
+   0X00,
+   SSD1306_LCDHEIGHT,
+   SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL,
+   0X00,
+   start,
+   0X00,
+   stop,
+   0X01,
+   SSD1306_ACTIVATE_SCROLL
+    };
+
+    sendCommands(cmds, NUM_ELEMENTS(cmds));
+}
+
+// startscrolldiagleft
+// Activate a diagonal scroll for rows start through stop
+// Hint, the display is 16 rows tall. To scroll the whole display, run:
+// display.scrollright(0x00, 0x0F)
+void SSD1306::startscrolldiagleft(uint8_t start, uint8_t stop) {
+    uint8_t cmds[] = { 
+   SSD1306_SET_VERTICAL_SCROLL_AREA,
+   0X00,
+   SSD1306_LCDHEIGHT,
+   SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL,
+   0X00,
+   start,
+   0X00,
+   stop,
+   0X01,
+   SSD1306_ACTIVATE_SCROLL
+    };
+
+    sendCommands(cmds, NUM_ELEMENTS(cmds));
+}
+
+
+void SSD1306::stopscroll(void) {
+   const uint8_t cmds[] = { SSD1306_DEACTIVATE_SCROLL };
+        sendCommands(cmds, NUM_ELEMENTS(cmds));
+}
+
+// Dim the display
+// dim = true: display is dimmed
+// dim = false: display is normal
+void SSD1306::dim(bool dim) {
+   // the range of contrast to too small to be really useful
+   // it is useful to dim the display
+   uint8_t cmds[] = {SSD1306_SETCONTRAST, dim ? 0 : 0xCF};
+        sendCommands(cmds, NUM_ELEMENTS(cmds));
+}
+
+// clear everything
+void SSD1306::clearDisplay(void) {
+   memset(buffer, 0, (SSD1306_LCDWIDTH * SSD1306_LCDHEIGHT / 8));
+}
+
+#if 1
+void SSD1306::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) {
+   bool bSwap = false;
+   switch (rotation) {
+   case 0:
+      // 0 degree rotation, do nothing
+      break;
+   case 1:
+      // 90 degree rotation, swap x & y for rotation, then invert x
+      bSwap = true;
+      ssd1306_swap(x, y);
+      x = WIDTH - x - 1;
+      break;
+   case 2:
+      // 180 degree rotation, invert x and y - then shift y around for height.
+      x = WIDTH - x - 1;
+      y = HEIGHT - y - 1;
+      x -= (w - 1);
+      break;
+   case 3:
+      // 270 degree rotation, swap x & y for rotation, then invert y  and adjust y for w (not to become h)
+      bSwap = true;
+      ssd1306_swap(x, y);
+      y = HEIGHT - y - 1;
+      y -= (w - 1);
+      break;
+   }
+
+   if (bSwap) {
+      drawFastVLineInternal(x, y, w, color);
+   } else {
+      drawFastHLineInternal(x, y, w, color);
+   }
+}
+#endif
+
+
+void SSD1306::drawFastHLineInternal(int16_t x, int16_t y, int16_t w,
+      uint16_t color) {
+   // Do bounds/limit checks
+   if (y < 0 || y >= HEIGHT) {
+      return;
+   }
+
+   // make sure we don't try to draw below 0
+   if (x < 0) {
+      w += x;
+      x = 0;
+   }
+
+   // make sure we don't go off the edge of the display
+   if ((x + w) > WIDTH) {
+      w = (WIDTH - x);
+   }
+
+   // if our width is now negative, punt
+   if (w <= 0) {
+      return;
+   }
+
+   // set up the pointer for  movement through the buffer
+   register uint8_t *pBuf = buffer;
+   // adjust the buffer pointer for the current row
+   pBuf += ((y / 8) * SSD1306_LCDWIDTH);
+   // and offset x columns in
+   pBuf += x;
+
+   register uint8_t mask = 1 << (y & 7);
+
+   switch (color) {
+   case WHITE:
+      while (w--)
+         *pBuf++ |= mask;
+      break;
+   case BLACK:
+      mask = ~mask;
+      while (w--)
+         *pBuf++ &= mask;
+      break;
+   case INVERSE:
+      while (w--)
+         *pBuf++ ^= mask;
+      break;
+   }
+}
+
+void SSD1306::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) {
+   bool bSwap = false;
+   switch (rotation) {
+   case 0:
+      break;
+   case 1:
+      // 90 degree rotation, swap x & y for rotation, then invert x and adjust x for h (now to become w)
+      bSwap = true;
+      ssd1306_swap(x, y)
+      x = WIDTH - x - 1;
+      x -= (h - 1);
+      break;
+   case 2:
+      // 180 degree rotation, invert x and y - then shift y around for height.
+      x = WIDTH - x - 1;
+      y = HEIGHT - y - 1;
+      y -= (h - 1);
+      break;
+   case 3:
+      // 270 degree rotation, swap x & y for rotation, then invert y
+      bSwap = true;
+      ssd1306_swap(x, y)
+      y = HEIGHT - y - 1;
+      break;
+   }
+
+   if (bSwap) {
+      drawFastHLineInternal(x, y, h, color);
+   } else {
+      drawFastVLineInternal(x, y, h, color);
+   }
+}
+
+void SSD1306::drawFastVLineInternal(int16_t x, int16_t __y, int16_t __h, uint16_t color) {
+
+   // do nothing if we're off the left or right side of the screen
+   if (x < 0 || x >= WIDTH) {
+      return;
+   }
+
+   // make sure we don't try to draw below 0
+   if (__y < 0) {
+      // __y is negative, this will subtract enough from __h to account for __y being 0
+      __h += __y;
+      __y = 0;
+
+   }
+
+   // make sure we don't go past the height of the display
+   if ((__y + __h) > HEIGHT) {
+      __h = (HEIGHT - __y);
+   }
+
+   // if our height is now negative, punt
+   if (__h <= 0) {
+      return;
+   }
+
+   // this display doesn't need ints for coordinates, use local byte registers for faster juggling
+   register uint8_t y = __y;
+   register uint8_t h = __h;
+
+   // set up the pointer for fast movement through the buffer
+   register uint8_t *pBuf = buffer;
+   // adjust the buffer pointer for the current row
+   pBuf += ((y / 8) * SSD1306_LCDWIDTH);
+   // and offset x columns in
+   pBuf += x;
+
+   // do the first partial byte, if necessary - this requires some masking
+   register uint8_t mod = (y & 7);
+   if (mod) {
+      // mask off the high n bits we want to set
+      mod = 8 - mod;
+
+      // note - lookup table results in a nearly 10% performance improvement in fill* functions
+      // register uint8_t mask = ~(0xFF >> (mod));
+      static uint8_t premask[8] = { 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC,
+            0xFE };
+      register uint8_t mask = premask[mod];
+
+      // adjust the mask if we're not going to reach the end of this byte
+      if (h < mod) {
+         mask &= (0XFF >> (mod - h));
+      }
+
+      switch (color) {
+      case WHITE:
+         *pBuf |= mask;
+         break;
+      case BLACK:
+         *pBuf &= ~mask;
+         break;
+      case INVERSE:
+         *pBuf ^= mask;
+         break;
+      }
+
+      // fast exit if we're done here!
+      if (h < mod) {
+         return;
+      }
+
+      h -= mod;
+
+      pBuf += SSD1306_LCDWIDTH;
+   }
+
+   // write solid bytes while we can - effectively doing 8 rows at a time
+   if (h >= 8) {
+      if (color == INVERSE) { // separate copy of the code so we don't impact performance of the black/white write version with an extra comparison per loop
+         do {
+            *pBuf = ~(*pBuf);
+
+            // adjust the buffer forward 8 rows worth of data
+            pBuf += SSD1306_LCDWIDTH;
+
+            // 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)
+            h -= 8;
+         } while (h >= 8);
+      } else {
+         // store a local value to work with
+         register uint8_t val = (color == WHITE) ? 255 : 0;
+
+         do {
+            // write our value in
+            *pBuf = val;
+
+            // adjust the buffer forward 8 rows worth of data
+            pBuf += SSD1306_LCDWIDTH;
+
+            // 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)
+            h -= 8;
+         } while (h >= 8);
+      }
+   }
+
+   // now do the final partial byte, if necessary
+   if (h) {
+      mod = h & 7;
+      // this time we want to mask the low bits of the byte, vs the high bits we did above
+      // register uint8_t mask = (1 << mod) - 1;
+      // note - lookup table results in a nearly 10% performance improvement in fill* functions
+      static uint8_t postmask[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F,
+            0x7F };
+      register uint8_t mask = postmask[mod];
+      switch (color) {
+      case WHITE:
+         *pBuf |= mask;
+         break;
+      case BLACK:
+         *pBuf &= ~mask;
+         break;
+      case INVERSE:
+         *pBuf ^= mask;
+         break;
+      }
+   }
+}
\ No newline at end of file