dd
Dependencies: Final HCSR04 TB6612FNG
oled/SSD1306-Library.cpp
- Committer:
- eunmango
- Date:
- 2019-06-16
- Revision:
- 97:b483e656bd14
File content as of revision 97:b483e656bd14:
/* * 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; } } }