/* * OLED.cpp * * Created on: 29Aug.,2017 * Author: Ben V. Brown */ #include #include #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, 0xFFFF); taskEXIT_CRITICAL(); } void OLED::drawChar(char c, char PrecursorCommand) { //prints a char to the screen if (c < ' ') return; //We are left with uint8_t* charPointer; //Fonts are offset to start at the space char. /* * UTF font handling is done using the two input chars * Precursor is the command char that is used to select the table * */ uint16_t index = 0; if (PrecursorCommand == 0) index = (c - ' '); else { //This is for extended range //We decode the precursor command to find the offset //Latin stats at 96 c -= 0x80; if (PrecursorCommand == 0xC3) index = (128) + (c); else if (PrecursorCommand == 0xC2) index = (96) + (c); else if (PrecursorCommand == 0xD0) index = (192) + (c); else if (PrecursorCommand == 0xD1) index = (256) + (c); else return; } charPointer = ((uint8_t*) currentFont) + ((fontWidth * (fontHeight / 8)) * index); if (cursor_x >= 0 && cursor_x < 96) 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::setFont(uint8_t fontNumber) { if (fontNumber == 1) { //small font currentFont = ASCII6x8; 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::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 < 0) return; //cutoffleft if ((x + wide) > 96) return; //cutoff right if (y == 0) { //Splat first line of data for (uint8_t xx = 0; xx < (wide); xx++) { firstStripPtr[xx + x] = ptr[xx]; } } if (y == 8 || height == 16) { // Splat the second line for (uint8_t xx = 0; xx < wide; xx++) { secondStripPtr[x + xx] = ptr[xx + (height == 16 ? wide : 0)]; } } }