From 8bf65351ea198049de9cb6f56abf67dc3eb0cf63 Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Fri, 11 May 2018 04:32:37 +0200 Subject: [PATCH] Shrink OLED driver code. (#296) * Precompute command header. * Update data header on-demand. * Use more aggressive inlining. * Do not clear number buffer twice. --- workspace/TS100/inc/OLED.hpp | 66 ++++++++++++++------ workspace/TS100/src/OLED.cpp | 115 +++++++++++------------------------ 2 files changed, 85 insertions(+), 96 deletions(-) diff --git a/workspace/TS100/inc/OLED.hpp b/workspace/TS100/inc/OLED.hpp index c8098cd1..66d3c93e 100644 --- a/workspace/TS100/inc/OLED.hpp +++ b/workspace/TS100/inc/OLED.hpp @@ -12,6 +12,7 @@ #include #include "stm32f1xx_hal.h" #include +#include #include "FRToSI2C.hpp" #include "Font.h" #ifdef __cplusplus @@ -21,34 +22,65 @@ extern "C" { #ifdef __cplusplus } #endif -#define DEVICEADDR_OLED (0x3c<<1) -#define OLED_WIDTH 96 +#define DEVICEADDR_OLED (0x3c<<1) +#define OLED_WIDTH 96 +#define FRAMEBUFFER_START 17 + class OLED { public: - OLED(FRToSI2C* i2cHandle); // Initialize Driver and store I2C pointer + OLED(FRToSI2C* i2cHandle); // Initialize Driver and store I2C pointer void initialize(); // Startup the I2C coms (brings screen out of reset etc) - void refresh(); // Draw the buffer out to the LCD using the DMA Channel + + // Draw the buffer out to the LCD using the DMA Channel + void refresh() { + i2c->Transmit( DEVICEADDR_OLED, screenBuffer, FRAMEBUFFER_START + (OLED_WIDTH * 2)); + } + void drawChar(char c, char preCursorCommand = '\0'); // Draw a character to a specific location - void displayOnOff(bool on); // Turn the screen on or not + // Turn the screen on or not + void displayOnOff(bool on) { + displayOnOffState = on; + screenBuffer[1] = on ? 0xAF : 0xAE; + } void setRotation(bool leftHanded); // Set the rotation for the screen - bool getRotation(); // Get the current rotation of the LCD + // Get the current rotation of the LCD + bool getRotation() const { + return inLeftHandedMode; + } void print(const char* string); // Draw a string to the current location, with current font - void setCursor(int16_t x, int16_t y); // Set the cursor location by pixels - void setCharCursor(int16_t x, int16_t y); //Set cursor location by chars in current font + // Set the cursor location by pixels + void setCursor(int16_t x, int16_t y) { + cursor_x = x; + cursor_y = y; + } + //Set cursor location by chars in current font + void setCharCursor(int16_t x, int16_t y) { + cursor_x = x * fontWidth; + cursor_y = y * fontHeight; + } void setFont(uint8_t fontNumber); // (Future) Set the font that is being used - void drawImage(const uint8_t* buffer, uint8_t x, uint8_t width); + void drawImage(const uint8_t* buffer, uint8_t x, uint8_t width) { + drawArea(x, 0, width, 16, buffer); + } // Draws an image to the buffer, at x offset from top to bottom (fixed height renders) void printNumber(uint16_t number, uint8_t places); // Draws a number at the current cursor location - void clearScreen(); // Clears the buffer - void drawBattery(uint8_t state); // Draws the battery level symbol - void drawCheckbox(bool state); // Draws a checkbox + // Clears the buffer + void clearScreen() { + memset(&screenBuffer[FRAMEBUFFER_START], 0, OLED_WIDTH * 2); + } + // Draws the battery level symbol + void drawBattery(uint8_t state) { + drawSymbol(3 + (state > 10 ? 10 : state)); + } + // Draws a checkbox + void drawCheckbox(bool state) { + drawSymbol((state) ? 16 : 17); + } void drawSymbol(uint8_t symbolID);//Used for drawing symbols of a predictable width - void drawArea(int16_t x, int8_t y, uint8_t wide, uint8_t height, - const uint8_t* ptr); - void fillArea(int16_t x, int8_t y, uint8_t wide, uint8_t height, - const uint8_t value); + void drawArea(int16_t x, int8_t y, uint8_t wide, uint8_t height, const uint8_t* ptr); //Draw an area, but y must be aligned on 0/8 offset + void fillArea(int16_t x, int8_t y, uint8_t wide, uint8_t height, const uint8_t value); //Fill an area, but y must be aligned on 0/8 offset void drawFilledRect(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1,bool clear); void drawHeatSymbol(uint8_t state); private: @@ -57,7 +89,6 @@ private: FRToSI2C* i2c; //i2c Pointer const uint8_t* currentFont; // Pointer to the current font used for rendering to the buffer - uint8_t screenBuffer[16 + 96 + 96 + 10]; // The data buffer uint8_t* firstStripPtr; // Pointers to the strips to allow for buffer having extra content uint8_t* secondStripPtr; //Pointers to the strips bool inLeftHandedMode; // Whether the screen is in left or not (used for offsets in GRAM) @@ -65,6 +96,7 @@ private: uint8_t fontWidth, fontHeight; int16_t cursor_x, cursor_y; uint8_t displayOffset; + uint8_t screenBuffer[16 + (OLED_WIDTH * 2) + 10]; // The data buffer }; #endif /* OLED_HPP_ */ diff --git a/workspace/TS100/src/OLED.cpp b/workspace/TS100/src/OLED.cpp index 723fc744..15e85070 100644 --- a/workspace/TS100/src/OLED.cpp +++ b/workspace/TS100/src/OLED.cpp @@ -13,7 +13,6 @@ /*http://www.displayfuture.com/Display/datasheet/controller/SSD1307.pdf*/ /*All commands are prefixed with 0x80*/ /*Data packets are prefixed with 0x40*/ -const uint8_t configLength = 50; uint8_t OLED_Setup_Array[] = { /**/ 0x80, 0xAE,/*Display off*/ 0x80, 0xD5,/*Set display clock divide ratio / osc freq*/ @@ -43,54 +42,42 @@ uint8_t OLED_Setup_Array[] = { /**/ }; //Setup based on the SSD1307 and modified for the SSD1306 +const uint8_t REFRESH_COMMANDS[17] = { + 0x80, 0xAF, + 0x80, 0x21, + 0x80, 0x20, + 0x80, 0x7F, + 0x80, 0xC0, + 0x80, 0x22, + 0x80, 0x00, + 0x80, 0x01, + 0x40 +}; + OLED::OLED(FRToSI2C* i2cHandle) { i2c = i2cHandle; cursor_x = cursor_y = 0; currentFont = FONT_12; fontWidth = 12; inLeftHandedMode = false; - firstStripPtr = &screenBuffer[16 + 1]; - secondStripPtr = &screenBuffer[16 + 1 + 96]; + firstStripPtr = &screenBuffer[FRAMEBUFFER_START]; + secondStripPtr = &screenBuffer[FRAMEBUFFER_START + OLED_WIDTH]; fontHeight = 16; - fontWidth = 12; displayOffset = 0; displayOnOffState = true; - } void OLED::initialize() { + memcpy(&screenBuffer[0], &REFRESH_COMMANDS[0], sizeof(REFRESH_COMMANDS)); + HAL_Delay(5); HAL_GPIO_WritePin(OLED_RESET_GPIO_Port, OLED_RESET_Pin, GPIO_PIN_SET); HAL_Delay(10); //Send the setup settings - i2c->Transmit( DEVICEADDR_OLED, (uint8_t*) OLED_Setup_Array, configLength); + i2c->Transmit( DEVICEADDR_OLED, (uint8_t*) OLED_Setup_Array, sizeof(OLED_Setup_Array)); displayOnOff(true); } -//Write out the buffer to the OLEd & call any rendering objects -void OLED::refresh() { - screenBuffer[0] = 0x80; - screenBuffer[1] = displayOnOffState ? 0xAF : 0xAE; - screenBuffer[2] = 0x80; - screenBuffer[3] = 0x21; - screenBuffer[4] = 0x80; - screenBuffer[5] = inLeftHandedMode ? 0 : 32; //display is shifted by 32 in left handed mode as driver ram is 128 wide - screenBuffer[6] = 0x80; - screenBuffer[7] = inLeftHandedMode ? 95 : 0x7F; //End address of the ram segment we are writing to (96 wide) - screenBuffer[8] = 0x80; /*Set COM Scan direction*/ - screenBuffer[9] = inLeftHandedMode ? 0xC8 : 0xC0; - screenBuffer[10] = 0x80; //Set pages to rollover after 2 - screenBuffer[11] = 0x22; - screenBuffer[12] = 0x80; - screenBuffer[13] = 0x00; //start page 0 - screenBuffer[14] = 0x80; - screenBuffer[15] = 0x01; - screenBuffer[16] = 0x40; //start of data marker - - i2c->Transmit( DEVICEADDR_OLED, screenBuffer, 16 + (96 * 2) + 1); - -} - /* * Prints a char to the screen. * UTF font handling is done using the two input chars. @@ -151,25 +138,26 @@ void OLED::drawChar(char c, char PrecursorCommand) { cursor_x += fontWidth; } -void OLED::displayOnOff(bool on) { - displayOnOffState = on; -} - void OLED::setRotation(bool leftHanded) { - if (inLeftHandedMode != leftHanded) { - //send command struct again with changes - if (leftHanded == 1) { + if (inLeftHandedMode == leftHanded) { + return; + } + + //send command struct again with changes + if (leftHanded) { OLED_Setup_Array[11] = 0xC8; //c1? OLED_Setup_Array[19] = 0xA1; - } else if (leftHanded == 0) { + } else { OLED_Setup_Array[11] = 0xC0; OLED_Setup_Array[19] = 0xA0; } - i2c->Transmit( DEVICEADDR_OLED, (uint8_t*) OLED_Setup_Array, - configLength); + i2c->Transmit( DEVICEADDR_OLED, (uint8_t*) OLED_Setup_Array, sizeof(OLED_Setup_Array)); inLeftHandedMode = leftHanded; + + screenBuffer[5] = inLeftHandedMode ? 0 : 32; //display is shifted by 32 in left handed mode as driver ram is 128 wide + screenBuffer[7] = inLeftHandedMode ? 95 : 0x7F; //End address of the ram segment we are writing to (96 wide) + screenBuffer[9] = inLeftHandedMode ? 0xC8 : 0xC0; } -} //print a string to the current cursor location void OLED::print(const char* str) { @@ -183,14 +171,6 @@ void OLED::print(const char* str) { } } -void OLED::setCursor(int16_t x, int16_t y) { - cursor_x = x; - cursor_y = y; -} -void OLED::setCharCursor(int16_t x, int16_t y) { - cursor_x = x * fontWidth; - cursor_y = y * fontHeight; -} void OLED::setFont(uint8_t fontNumber) { if (fontNumber == 1) { //small font @@ -208,58 +188,35 @@ void OLED::setFont(uint8_t fontNumber) { } } -void OLED::drawImage(const uint8_t* buffer, uint8_t x, uint8_t width) { - drawArea(x, 0, width, 16, buffer); -} - //maximum places is 5 void OLED::printNumber(uint16_t number, uint8_t places) { - char buffer[6]; - buffer[5] = 0; //null + char buffer[6] = { 0 }; + if (places == 5) { buffer[4] = '0' + number % 10; number /= 10; - } else - buffer[4] = 0; + } if (places > 3) { buffer[3] = '0' + number % 10; number /= 10; - } else - buffer[3] = 0; + } if (places > 2) { buffer[2] = '0' + number % 10; number /= 10; - } else - buffer[2] = 0; + } if (places > 1) { buffer[1] = '0' + number % 10; number /= 10; - } else - buffer[1] = 0; + } + buffer[0] = '0' + number % 10; number /= 10; print(buffer); } -void OLED::clearScreen() { - memset(firstStripPtr, 0, 96); - memset(secondStripPtr, 0, 96); -} - -bool OLED::getRotation() { - return inLeftHandedMode; -} -void OLED::drawBattery(uint8_t state) { - if (state > 10) - state = 10; - drawSymbol(3 + state); -} -void OLED::drawCheckbox(bool state) { - drawSymbol((state) ? 16 : 17); -} void OLED::drawSymbol(uint8_t symbolID) { //draw a symbol to the current cursor location setFont(2);