Shrink OLED driver code. (#296)
* Precompute command header. * Update data header on-demand. * Use more aggressive inlining. * Do not clear number buffer twice.
This commit is contained in:
committed by
Ben V. Brown
parent
215fe8e9e8
commit
8bf65351ea
@@ -12,6 +12,7 @@
|
|||||||
#include <hardware.h>
|
#include <hardware.h>
|
||||||
#include "stm32f1xx_hal.h"
|
#include "stm32f1xx_hal.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
#include "FRToSI2C.hpp"
|
#include "FRToSI2C.hpp"
|
||||||
#include "Font.h"
|
#include "Font.h"
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -23,32 +24,63 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
#define DEVICEADDR_OLED (0x3c<<1)
|
#define DEVICEADDR_OLED (0x3c<<1)
|
||||||
#define OLED_WIDTH 96
|
#define OLED_WIDTH 96
|
||||||
|
#define FRAMEBUFFER_START 17
|
||||||
|
|
||||||
|
|
||||||
class OLED {
|
class OLED {
|
||||||
public:
|
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 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 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
|
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 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
|
// Set the cursor location by pixels
|
||||||
void setCharCursor(int16_t x, int16_t y); //Set cursor location by chars in current font
|
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 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)
|
// Draws an image to the buffer, at x offset from top to bottom (fixed height renders)
|
||||||
void printNumber(uint16_t number, uint8_t places);
|
void printNumber(uint16_t number, uint8_t places);
|
||||||
// Draws a number at the current cursor location
|
// Draws a number at the current cursor location
|
||||||
void clearScreen(); // Clears the buffer
|
// Clears the buffer
|
||||||
void drawBattery(uint8_t state); // Draws the battery level symbol
|
void clearScreen() {
|
||||||
void drawCheckbox(bool state); // Draws a checkbox
|
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 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,
|
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
|
||||||
const uint8_t* ptr);
|
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 fillArea(int16_t x, int8_t y, uint8_t wide, uint8_t height,
|
|
||||||
const uint8_t value);
|
|
||||||
void drawFilledRect(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1,bool clear);
|
void drawFilledRect(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1,bool clear);
|
||||||
void drawHeatSymbol(uint8_t state);
|
void drawHeatSymbol(uint8_t state);
|
||||||
private:
|
private:
|
||||||
@@ -57,7 +89,6 @@ private:
|
|||||||
|
|
||||||
FRToSI2C* i2c; //i2c Pointer
|
FRToSI2C* i2c; //i2c Pointer
|
||||||
const uint8_t* currentFont; // Pointer to the current font used for rendering to the buffer
|
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* firstStripPtr; // Pointers to the strips to allow for buffer having extra content
|
||||||
uint8_t* secondStripPtr; //Pointers to the strips
|
uint8_t* secondStripPtr; //Pointers to the strips
|
||||||
bool inLeftHandedMode; // Whether the screen is in left or not (used for offsets in GRAM)
|
bool inLeftHandedMode; // Whether the screen is in left or not (used for offsets in GRAM)
|
||||||
@@ -65,6 +96,7 @@ private:
|
|||||||
uint8_t fontWidth, fontHeight;
|
uint8_t fontWidth, fontHeight;
|
||||||
int16_t cursor_x, cursor_y;
|
int16_t cursor_x, cursor_y;
|
||||||
uint8_t displayOffset;
|
uint8_t displayOffset;
|
||||||
|
uint8_t screenBuffer[16 + (OLED_WIDTH * 2) + 10]; // The data buffer
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* OLED_HPP_ */
|
#endif /* OLED_HPP_ */
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
/*http://www.displayfuture.com/Display/datasheet/controller/SSD1307.pdf*/
|
/*http://www.displayfuture.com/Display/datasheet/controller/SSD1307.pdf*/
|
||||||
/*All commands are prefixed with 0x80*/
|
/*All commands are prefixed with 0x80*/
|
||||||
/*Data packets are prefixed with 0x40*/
|
/*Data packets are prefixed with 0x40*/
|
||||||
const uint8_t configLength = 50;
|
|
||||||
uint8_t OLED_Setup_Array[] = { /**/
|
uint8_t OLED_Setup_Array[] = { /**/
|
||||||
0x80, 0xAE,/*Display off*/
|
0x80, 0xAE,/*Display off*/
|
||||||
0x80, 0xD5,/*Set display clock divide ratio / osc freq*/
|
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
|
//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) {
|
OLED::OLED(FRToSI2C* i2cHandle) {
|
||||||
i2c = i2cHandle;
|
i2c = i2cHandle;
|
||||||
cursor_x = cursor_y = 0;
|
cursor_x = cursor_y = 0;
|
||||||
currentFont = FONT_12;
|
currentFont = FONT_12;
|
||||||
fontWidth = 12;
|
fontWidth = 12;
|
||||||
inLeftHandedMode = false;
|
inLeftHandedMode = false;
|
||||||
firstStripPtr = &screenBuffer[16 + 1];
|
firstStripPtr = &screenBuffer[FRAMEBUFFER_START];
|
||||||
secondStripPtr = &screenBuffer[16 + 1 + 96];
|
secondStripPtr = &screenBuffer[FRAMEBUFFER_START + OLED_WIDTH];
|
||||||
fontHeight = 16;
|
fontHeight = 16;
|
||||||
fontWidth = 12;
|
|
||||||
displayOffset = 0;
|
displayOffset = 0;
|
||||||
displayOnOffState = true;
|
displayOnOffState = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OLED::initialize() {
|
void OLED::initialize() {
|
||||||
|
memcpy(&screenBuffer[0], &REFRESH_COMMANDS[0], sizeof(REFRESH_COMMANDS));
|
||||||
|
|
||||||
HAL_Delay(5);
|
HAL_Delay(5);
|
||||||
HAL_GPIO_WritePin(OLED_RESET_GPIO_Port, OLED_RESET_Pin, GPIO_PIN_SET);
|
HAL_GPIO_WritePin(OLED_RESET_GPIO_Port, OLED_RESET_Pin, GPIO_PIN_SET);
|
||||||
HAL_Delay(10);
|
HAL_Delay(10);
|
||||||
//Send the setup settings
|
//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);
|
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.
|
* Prints a char to the screen.
|
||||||
* UTF font handling is done using the two input chars.
|
* UTF font handling is done using the two input chars.
|
||||||
@@ -151,25 +138,26 @@ void OLED::drawChar(char c, char PrecursorCommand) {
|
|||||||
cursor_x += fontWidth;
|
cursor_x += fontWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OLED::displayOnOff(bool on) {
|
|
||||||
displayOnOffState = on;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OLED::setRotation(bool leftHanded) {
|
void OLED::setRotation(bool leftHanded) {
|
||||||
if (inLeftHandedMode != leftHanded) {
|
if (inLeftHandedMode == leftHanded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//send command struct again with changes
|
//send command struct again with changes
|
||||||
if (leftHanded == 1) {
|
if (leftHanded) {
|
||||||
OLED_Setup_Array[11] = 0xC8; //c1?
|
OLED_Setup_Array[11] = 0xC8; //c1?
|
||||||
OLED_Setup_Array[19] = 0xA1;
|
OLED_Setup_Array[19] = 0xA1;
|
||||||
} else if (leftHanded == 0) {
|
} else {
|
||||||
OLED_Setup_Array[11] = 0xC0;
|
OLED_Setup_Array[11] = 0xC0;
|
||||||
OLED_Setup_Array[19] = 0xA0;
|
OLED_Setup_Array[19] = 0xA0;
|
||||||
}
|
}
|
||||||
i2c->Transmit( DEVICEADDR_OLED, (uint8_t*) OLED_Setup_Array,
|
i2c->Transmit( DEVICEADDR_OLED, (uint8_t*) OLED_Setup_Array, sizeof(OLED_Setup_Array));
|
||||||
configLength);
|
|
||||||
inLeftHandedMode = leftHanded;
|
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
|
//print a string to the current cursor location
|
||||||
void OLED::print(const char* str) {
|
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) {
|
void OLED::setFont(uint8_t fontNumber) {
|
||||||
if (fontNumber == 1) {
|
if (fontNumber == 1) {
|
||||||
//small font
|
//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
|
//maximum places is 5
|
||||||
void OLED::printNumber(uint16_t number, uint8_t places) {
|
void OLED::printNumber(uint16_t number, uint8_t places) {
|
||||||
char buffer[6];
|
char buffer[6] = { 0 };
|
||||||
buffer[5] = 0; //null
|
|
||||||
if (places == 5) {
|
if (places == 5) {
|
||||||
buffer[4] = '0' + number % 10;
|
buffer[4] = '0' + number % 10;
|
||||||
number /= 10;
|
number /= 10;
|
||||||
} else
|
}
|
||||||
buffer[4] = 0;
|
|
||||||
|
|
||||||
if (places > 3) {
|
if (places > 3) {
|
||||||
buffer[3] = '0' + number % 10;
|
buffer[3] = '0' + number % 10;
|
||||||
number /= 10;
|
number /= 10;
|
||||||
} else
|
}
|
||||||
buffer[3] = 0;
|
|
||||||
|
|
||||||
if (places > 2) {
|
if (places > 2) {
|
||||||
buffer[2] = '0' + number % 10;
|
buffer[2] = '0' + number % 10;
|
||||||
number /= 10;
|
number /= 10;
|
||||||
} else
|
}
|
||||||
buffer[2] = 0;
|
|
||||||
|
|
||||||
if (places > 1) {
|
if (places > 1) {
|
||||||
buffer[1] = '0' + number % 10;
|
buffer[1] = '0' + number % 10;
|
||||||
number /= 10;
|
number /= 10;
|
||||||
} else
|
}
|
||||||
buffer[1] = 0;
|
|
||||||
buffer[0] = '0' + number % 10;
|
buffer[0] = '0' + number % 10;
|
||||||
number /= 10;
|
number /= 10;
|
||||||
print(buffer);
|
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) {
|
void OLED::drawSymbol(uint8_t symbolID) {
|
||||||
//draw a symbol to the current cursor location
|
//draw a symbol to the current cursor location
|
||||||
setFont(2);
|
setFont(2);
|
||||||
|
|||||||
Reference in New Issue
Block a user