* adjust left limit for drawing do draw area also if part just of it is visible - e.g. do not skip whole letter in rolling description if half of it can be shown * render just visible area part * fix visible area part computation
318 lines
7.5 KiB
C++
318 lines
7.5 KiB
C++
/*
|
|
* OLED.cpp
|
|
*
|
|
* Created on: 29Aug.,2017
|
|
* Author: Ben V. Brown
|
|
*/
|
|
|
|
#include <OLED.hpp>
|
|
#include <string.h>
|
|
#include "Translation.h"
|
|
#include "cmsis_os.h"
|
|
/*Setup params for the OLED screen*/
|
|
/*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*/
|
|
0x80, 0x52,/*Divide ratios*/
|
|
0x80, 0xA8,/*Set Multiplex Ratio*/
|
|
0x80, 0x0F,/*16 == max brightness,39==dimmest*/
|
|
0x80, 0xC0,/*Set COM Scan direction*/
|
|
0x80, 0xD3,/*Set vertical Display offset*/
|
|
0x80, 0x00,/*0 Offset*/
|
|
0x80, 0x40,/*Set Display start line to 0*/
|
|
0x80, 0xA0,/*Set Segment remap to normal*/
|
|
0x80, 0x8D,/*Charge Pump*/
|
|
0x80, 0x14,/*Charge Pump settings*/
|
|
0x80, 0xDA,/*Set VCOM Pins hardware config*/
|
|
0x80, 0x02,/*Combination 2*/
|
|
0x80, 0x81,/*Contrast*/
|
|
0x80, 0x33,/*^51*/
|
|
0x80, 0xD9,/*Set pre-charge period*/
|
|
0x80, 0xF1,/*Pre charge period*/
|
|
0x80, 0xDB,/*Adjust VCOMH regulator ouput*/
|
|
0x80, 0x30,/*VCOM level*/
|
|
0x80, 0xA4,/*Enable the display GDDR*/
|
|
0x80, 0XA6,/*Normal display*/
|
|
0x80, 0x20,/*Memory Mode*/
|
|
0x80, 0x00,/*Wrap memory*/
|
|
0x80, 0xAF /*Display on*/
|
|
};
|
|
//Setup based on the SSD1307 and modified for the SSD1306
|
|
|
|
OLED::OLED(I2C_HandleTypeDef* i2cHandle) {
|
|
i2c = i2cHandle;
|
|
cursor_x = cursor_y = 0;
|
|
currentFont = FONT_12;
|
|
fontWidth = 12;
|
|
inLeftHandedMode = false;
|
|
firstStripPtr = &screenBuffer[13];
|
|
secondStripPtr = &screenBuffer[13 + 96];
|
|
fontHeight = 16;
|
|
fontWidth = 12;
|
|
displayOffset = 0;
|
|
displayOnOffState = true;
|
|
|
|
}
|
|
|
|
void OLED::initialize() {
|
|
HAL_Delay(5);
|
|
HAL_GPIO_WritePin(OLED_RESET_GPIO_Port, OLED_RESET_Pin, GPIO_PIN_SET);
|
|
HAL_Delay(5);
|
|
//Send the setup settings
|
|
HAL_I2C_Master_Transmit(i2c, DEVICEADDR_OLED, (uint8_t*) OLED_Setup_Array,
|
|
configLength, 0xFFFF);
|
|
//displayOnOff(true);
|
|
|
|
}
|
|
|
|
//Write out the buffer to the OLEd & call any rendering objects
|
|
void OLED::refresh() {
|
|
screenBuffer[0] = 0x80;
|
|
screenBuffer[1] = 0x21;
|
|
screenBuffer[2] = 0x80;
|
|
screenBuffer[3] = inLeftHandedMode ? 0 : 32; //display is shifted by 32 in left handed mode as driver ram is 128 wide
|
|
screenBuffer[4] = 0x80;
|
|
screenBuffer[5] = inLeftHandedMode ? 95 : 0x7F; //End address of the ram segment we are writing to (96 wide)
|
|
|
|
screenBuffer[6] = 0x80; //Set pages to rollover after 2
|
|
screenBuffer[7] = 0x22;
|
|
screenBuffer[8] = 0x80;
|
|
screenBuffer[9] = 0x00; //start page 0
|
|
screenBuffer[10] = 0x80;
|
|
screenBuffer[11] = 0x01;
|
|
|
|
screenBuffer[12] = 0x40; //start of data marker
|
|
taskENTER_CRITICAL();
|
|
//Because I2C is shared, we cant task switch in the middle of the xfer
|
|
|
|
HAL_I2C_Master_Transmit(i2c, DEVICEADDR_OLED, screenBuffer, 12 + 96 * 2 + 1,
|
|
500);
|
|
taskEXIT_CRITICAL();
|
|
|
|
}
|
|
|
|
/*
|
|
* Prints a char to the screen.
|
|
* UTF font handling is done using the two input chars.
|
|
* Precursor is the command char that is used to select the table.
|
|
*/
|
|
void OLED::drawChar(char c, char PrecursorCommand) {
|
|
if (c < ' ') {
|
|
return;
|
|
}
|
|
uint16_t index = 0;
|
|
if (PrecursorCommand == 0) {
|
|
//Fonts are offset to start at the space char
|
|
index = (c - ' ');
|
|
} else {
|
|
//This is for extended range
|
|
//We decode the precursor command to find the offset
|
|
//Latin starts at 96
|
|
c -= 0x80;
|
|
|
|
switch (PrecursorCommand) {
|
|
|
|
case 0xC2:
|
|
index = (96 - 32) + (c);
|
|
break; //-32 compensate for chars excluded from font C2 section
|
|
case 0xC3:
|
|
index = (128) + (c);
|
|
break;
|
|
#if defined(LANG_RU) || defined(LANG_UK) || defined(LANG_SR) || defined(LANG_BG) || defined(LANG_MK)
|
|
case 0xD0:
|
|
index = (192) + (c);
|
|
break;
|
|
case 0xD1:
|
|
index = (256) + (c);
|
|
break;
|
|
#else
|
|
case 0xC4:
|
|
index = (192) + (c);
|
|
break;
|
|
case 0xC5:
|
|
index = (256) + (c);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
uint8_t* charPointer;
|
|
charPointer = ((uint8_t*) currentFont)
|
|
+ ((fontWidth * (fontHeight / 8)) * index);
|
|
|
|
drawArea(cursor_x, cursor_y, fontWidth, fontHeight, charPointer);
|
|
|
|
cursor_x += fontWidth;
|
|
}
|
|
|
|
void OLED::displayOnOff(bool on) {
|
|
|
|
if (on != displayOnOffState) {
|
|
uint8_t data[6] = { 0x80, 0X8D, 0x80, 0X14, 0x80, 0XAF }; //on
|
|
if (!on) {
|
|
data[3] = 0x10;
|
|
data[5] = 0xAE;
|
|
}
|
|
taskENTER_CRITICAL();
|
|
|
|
HAL_I2C_Master_Transmit(i2c, DEVICEADDR_OLED, data, 6, 0xFFFF);
|
|
taskEXIT_CRITICAL();
|
|
displayOnOffState = on;
|
|
}
|
|
}
|
|
|
|
void OLED::setRotation(bool leftHanded) {
|
|
if (inLeftHandedMode != leftHanded) {
|
|
//send command struct again with changes
|
|
if (leftHanded == 1) {
|
|
OLED_Setup_Array[11] = 0xC8; //c1?
|
|
OLED_Setup_Array[19] = 0xA1;
|
|
} else if (leftHanded == 0) {
|
|
OLED_Setup_Array[11] = 0xC0;
|
|
OLED_Setup_Array[19] = 0xA0;
|
|
}
|
|
taskENTER_CRITICAL();
|
|
|
|
HAL_I2C_Master_Transmit(i2c, DEVICEADDR_OLED,
|
|
(uint8_t*) OLED_Setup_Array, 50, 0xFFFF);
|
|
taskEXIT_CRITICAL();
|
|
inLeftHandedMode = leftHanded;
|
|
}
|
|
}
|
|
|
|
//print a string to the current cursor location
|
|
void OLED::print(const char* str) {
|
|
while (str[0]) {
|
|
if (str[0] >= 0x80) {
|
|
drawChar(str[1], str[0]);
|
|
str++; //skip this marker
|
|
} else
|
|
drawChar(str[0]);
|
|
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
|
|
currentFont = FONT_6x8;
|
|
fontHeight = 8;
|
|
fontWidth = 6;
|
|
} else if (fontNumber == 2) {
|
|
currentFont = ExtraFontChars;
|
|
fontHeight = 16;
|
|
fontWidth = 12;
|
|
} else {
|
|
currentFont = FONT_12;
|
|
fontHeight = 16;
|
|
fontWidth = 12;
|
|
}
|
|
}
|
|
|
|
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
|
|
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) ? 17 : 18);
|
|
}
|
|
void OLED::drawSymbol(uint8_t symbolID) {
|
|
//draw a symbol to the current cursor location
|
|
setFont(2);
|
|
drawChar(' ' + symbolID); // space offset is in all fonts, so we pad it here and remove it later
|
|
setFont(0);
|
|
}
|
|
|
|
//Draw an area, but y must be aligned on 0/8 offset
|
|
void OLED::drawArea(int16_t x, int8_t y, uint8_t wide, uint8_t height,
|
|
const uint8_t* ptr) {
|
|
// Splat this from x->x+wide in two strides
|
|
if (x <= -wide)
|
|
return; //cutoffleft
|
|
if (x > 96)
|
|
return; //cutoff right
|
|
|
|
uint8_t visibleStart = 0;
|
|
uint8_t visibleEnd = wide;
|
|
|
|
// trimming to draw partials
|
|
if(x < 0) {
|
|
visibleStart -= x; //subtract negative value == add absolute value
|
|
}
|
|
if(x + wide > 96) {
|
|
visibleEnd = 96 - x;
|
|
}
|
|
|
|
if (y == 0) {
|
|
//Splat first line of data
|
|
for (uint8_t xx = visibleStart; xx < visibleEnd; xx++) {
|
|
firstStripPtr[xx + x] = ptr[xx];
|
|
}
|
|
}
|
|
if (y == 8 || height == 16) {
|
|
// Splat the second line
|
|
for (uint8_t xx = visibleStart; xx < visibleEnd; xx++) {
|
|
secondStripPtr[x + xx] = ptr[xx + (height == 16 ? wide : 0)];
|
|
}
|
|
}
|
|
}
|