BalancingRobot/Software/HighLevelApp/i2c_oled.c (318 lines of code) (raw):

#include "i2c_oled.h" #include <stdbool.h> #include <stdint.h> #include <stdlib.h> #include "applibs_versions.h" #include <errno.h> #include "utils.h" #include <string.h> #include "soc/mt3620_i2cs.h" #include "applibs/i2c.h" #include <applibs/log.h> #define SSD1306_WIDE_LCDWIDTH 128 #define SSD1306_WIDE_LCDHEIGHT 64 #define SSD1306_TALL_LCDWIDTH 128 #define SSD1306_TALL_LCDHEIGHT 32 static uint8_t displayBuffer[1024]; // 128*64 pixels (128/8)*64 bytes - also covers 32*128 (4x128) = 512 static void ssd1306_command(uint8_t c); static void ssd1306_commands(uint8_t* commands, int numCommands); static void i2cSendBytes(uint8_t* data, size_t length); static bool IsPixel(uint8_t* image, int width, int height, int x, int y); static void SSD1306_SetPixelInternal(int x, int y, bool turnOn); static uint8_t oledDisplayAddress = 0x3C; static int _i2cfd = -1; static bool _useVerticalDisplay = false; static int DisplayWidth = 0; static int DisplayHeight = 0; bool SSD1306_Init(bool useVerticalDisplay) { _useVerticalDisplay = useVerticalDisplay; if (useVerticalDisplay) { DisplayWidth = SSD1306_TALL_LCDWIDTH; DisplayHeight = SSD1306_TALL_LCDHEIGHT; } else { DisplayWidth = SSD1306_WIDE_LCDWIDTH; DisplayHeight = SSD1306_WIDE_LCDHEIGHT; } if (_i2cfd > -1) { return true; } _i2cfd = I2CMaster_Open(MT3620_I2C_ISU2); if (_i2cfd == -1) { return false; } int result = I2CMaster_SetBusSpeed(_i2cfd, I2C_BUS_SPEED_FAST); if (result != 0) { return false; } result = I2CMaster_SetTimeout(_i2cfd, 100); if (result != 0) { return false; } result = I2CMaster_SetDefaultTargetAddress(_i2cfd, oledDisplayAddress); if (result != 0) { Log_Debug("I2CMaster_SetDefaultTargetAddress (1): errno=%d (%s)\n", errno, strerror(errno)); return false; } uint8_t data = 0x00; // I2C OLED Display 'turn off' I2C_DeviceAddress _devAddr = 0x00; bool deviceFound = false; for (int x = 0; x <= 0x7f; x++) { _devAddr = x; ssize_t ret = I2CMaster_Write(_i2cfd, _devAddr, &data, 1); if (ret != -1) { Log_Debug("Found address: 0x%02x\n", x); deviceFound = true; } } if (!deviceFound) { Log_Debug("Didn't find an I2C devices\n"); return false; } if (!_useVerticalDisplay) { // 128x64 ssd1306_command(0xAE); // display off ssd1306_command(0xD5); // clock ssd1306_command(0x81); // upper nibble is rate, lower nibble is divisor ssd1306_command(0xA8); // mux ratio ssd1306_command(0x3F); // rtfm ssd1306_command(0xD3); // display offset ssd1306_command(0x00); // rtfm ssd1306_command(0x00); ssd1306_command(0x8D); // charge pump ssd1306_command(0x14); // enable ssd1306_command(0x20); // memory addr mode ssd1306_command(0x00); // horizontal ssd1306_command(0xA1); // segment remap ssd1306_command(0xA5); // display on ssd1306_command(0xC8); // com scan direction ssd1306_command(0xDA); // com hardware cfg ssd1306_command(0x12); // alt com cfg ssd1306_command(0x81); // contrast aka current ssd1306_command(0x7F); // 128 is midpoint ssd1306_command(0xD9); // precharge ssd1306_command(0x11); // rtfm ssd1306_command(0xDB); // vcomh deselect level ssd1306_command(0x20); // rtfm ssd1306_command(0xA6); // non-inverted ssd1306_command(0xA4); // display scan on ssd1306_command(0x2e); ssd1306_command(0xAF); // drivers on } else { // 128x32 ssd1306_command(0xAE); // display off ssd1306_command(0xD5); // clock ssd1306_command(0x81); // upper nibble is rate, lower nibble is divisor ssd1306_command(0xA8); // mux ratio ssd1306_command(0x1F); // rtfm ssd1306_command(0xD3); // display offset ssd1306_command(0x00); // rtfm ssd1306_command(0x00); ssd1306_command(0x8D); // charge pump ssd1306_command(0x14); // enable ssd1306_command(0x20); // memory addr mode ssd1306_command(0x00); // horizontal ssd1306_command(0xA1); // segment remap ssd1306_command(0xA5); // display on ssd1306_command(0xC8); // com scan direction ssd1306_command(0xDA); // com hardware cfg ssd1306_command(0x02); // alt com cfg ssd1306_command(0x81); // contrast aka current ssd1306_command(0x7F); // 128 is midpoint ssd1306_command(0xD9); // precharge ssd1306_command(0x11); // rtfm ssd1306_command(0xDB); // vcomh deselect level ssd1306_command(0x20); // rtfm ssd1306_command(0xA6); // non-inverted ssd1306_command(0xA4); // display scan on ssd1306_command(0xAF); // drivers on } SSD1306_Clear(); SSD1306_Display(); return true; } static void ssd1306_commands(uint8_t* commands, int numCommands) { // need to stick '0x00' on the front to make this a command. uint8_t* buffer = (uint8_t*)malloc(numCommands + 1); memset(buffer, 0x00, numCommands + 1); memcpy(&buffer[1], commands, numCommands); i2cSendBytes(buffer, numCommands+1); free(buffer); } void SSD1306_DrawImage(uint8_t* image, int width, int height, int xOffset, int yOffset) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (IsPixel(image, width, height, x, y)) { SSD1306_SetPixelInternal(x+xOffset, y+yOffset, true); } else { SSD1306_SetPixelInternal(x + xOffset, y + yOffset, false); } } } //Log_Debug("Dump Full Display\n"); //DumpDisplayBuffer(displayBuffer, DisplayWidth, DisplayHeight); } static void SSD1306_SetPixelInternal(int x, int y, bool turnOn) { if (x >= 0 && y >= 0 && x < DisplayWidth && y < DisplayHeight) { if (turnOn) { displayBuffer[x + (y / 8) * DisplayWidth] |= 1 << (y % 8); } else { displayBuffer[x + (y / 8) * DisplayWidth] &= ~(1 << (y % 8)); } } } void SSD1306_RotateImage(uint8_t* inputImage,uint8_t* outputImage, uint8_t width, uint8_t height, int rotateTo) { if (rotateTo != 90 && rotateTo != 180 && rotateTo != 270) { Log_Debug("RotateImage, invalid rotation value %d\n",rotateTo); return; } if (width != height) { Log_Debug("RotateImage: Width/Height must be equal\n"); return; } if (rotateTo == 90) { for (int y = 0; y < height; y++) { for (int x = 0; x < height; x++) { if (IsPixel(inputImage, width, height, x, y)) { SSD1306_SetPixel(outputImage, width, height, height-1-y , x, IsPixel(inputImage, width, height, x, y)); } } } } if (rotateTo == 180) { for (int y = 0; y < height; y++) { for (int x = 0; x < height; x++) { if (IsPixel(inputImage, width, height, x, y)) { SSD1306_SetPixel(outputImage, width, height, width-1-x , height-1-y , IsPixel(inputImage, width, height, x, y)); } } } } if (rotateTo == 270) { for (int y = 0; y < height; y++) { for (int x = 0; x < height; x++) { if (IsPixel(inputImage, width, height, x, y)) { SSD1306_SetPixel(outputImage, width, height, y , width-x-1, IsPixel(inputImage, width, height, x, y)); } } } } } static bool IsPixel(uint8_t* image, int width, int height, int x, int y) { uint8_t bMap[8] = { 128,64,32,16,8,4,2,1 }; int bytesPerLine = width / 8; int xOffset = x / 8; int xRemain = x % 8; // bit position int byteOffset = (y * bytesPerLine) + xOffset; uint8_t val = image[byteOffset]; if (val & bMap[xRemain]) { return true; } return false; } void SSD1306_Clear(void) { memset(&displayBuffer[0], 0x00, 1024); } void SSD1306_SetPixel(uint8_t*image, int width, int height, int x, int y, bool turnOn) { if (x >= 0 && y >= 0 && x < width && y < height) { static const unsigned int mask[] = { 128,64,32,16,8,4,2,1 }; int Pos = ((width / 8) * y) + (x / 8); int pixel = x % 8; int bitMask = mask[pixel]; if (turnOn) { image[Pos] |= bitMask; } else { image[Pos] &= ~bitMask; } } } static void ssd1306_command(uint8_t c) { uint8_t command[2] = { 0x00, c }; // some use 0X00 other examples use 0X80. I tried both i2cSendBytes(command, 2); } void SSD1306_Display(void) { uint8_t pagecol[5] = { 0x22,0x00,0xff,0x21,0x00 }; ssd1306_commands(pagecol, 5); ssd1306_command(0x7f); uint8_t dataBuffer[32]; dataBuffer[0x00] = 0x40; int copyPos = 0; int copyLen = 31; bool haveData = true; int totalSent = 0; int totalBytes = 1024; //DumpDisplayBuffer(displayBuffer,DisplayWidth, DisplayHeight); while (haveData) { memcpy(&dataBuffer[1], &displayBuffer[copyPos], copyLen); totalSent += copyLen; i2cSendBytes(dataBuffer, copyLen + 1); copyPos += copyLen; if (totalBytes - copyPos >= 31) copyLen = 31; else { copyLen = totalBytes - copyPos; } if (copyPos >= totalBytes) haveData = false; } } static void i2cSendBytes(uint8_t* data, size_t length) { I2CMaster_Write(_i2cfd, oledDisplayAddress, data, length); delay(1); } void SSD1306_FillRegion(uint8_t *image, int width, int height, int x, int y, int regionWidth, int regionHeight, bool turnOn) { for (int n = y; n < y + regionHeight; n++) { for (int o = x; o < x + regionWidth; o++) { SSD1306_SetPixel(image, width, height, o, n, turnOn); } } }