Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: IRremote HCSR04 TB6612FNG
Diff: oled/SSD1306-Library.cpp
- Revision:
- 97:59d348745d96
diff -r ec3a2da01f40 -r 59d348745d96 oled/SSD1306-Library.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/oled/SSD1306-Library.cpp Sun Jun 16 04:51:51 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