189 lines
No EOL
5.9 KiB
C
189 lines
No EOL
5.9 KiB
C
/**
|
|
* ============================================================================
|
|
* === SSD1306 128x64 IIC LCD Interface
|
|
* ============================================================================
|
|
*
|
|
* Provides a simple interface for rendering text to an SSD1306 LCD.
|
|
*
|
|
* digimint
|
|
*/
|
|
|
|
#include "iic.h"
|
|
#include "lcd/FontComicSansMS16.h"
|
|
#include "lcd/ssd1306.h"
|
|
#include "lcd/SymbolsFont.h"
|
|
#include "sdkconfig.h"
|
|
|
|
#include <driver/i2c.h>
|
|
#include <esp_log.h>
|
|
#include <freertos/FreeRTOS.h>
|
|
#include <freertos/portmacro.h>
|
|
#include <freertos/semphr.h>
|
|
#include <freertos/task.h>
|
|
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
uint8_t LCD_data[8][128];
|
|
bool LCD_rowChanged[8];
|
|
StaticSemaphore_t _lcSemaphore_buf;
|
|
|
|
volatile bool _lcDirty = false;
|
|
SemaphoreHandle_t _lcSemaphore = NULL;
|
|
|
|
static inline void _setPx(uint16_t x, uint16_t y){
|
|
uint16_t bank = y >> 3;
|
|
uint16_t posi = y & 0b111;
|
|
|
|
LCD_data[bank][x] |= (1 << posi);
|
|
LCD_rowChanged[bank] = true;
|
|
}
|
|
|
|
static inline void _clrPx(uint16_t x, uint16_t y){
|
|
uint16_t bank = y >> 3;
|
|
uint16_t posi = y & 0b111;
|
|
|
|
LCD_data[bank][x] &= ~(1 << posi);
|
|
LCD_rowChanged[bank] = true;
|
|
}
|
|
|
|
void LCD_renderChar(font_t font, const char oChr, uint16_t x, uint16_t y, bool invert){
|
|
char chr = oChr;
|
|
if(chr < ' ' || chr > '~'){
|
|
chr = ' ';
|
|
}
|
|
|
|
chr -= ' ';
|
|
uint_fast16_t charsPerRow = ((font.width & 0b111) == 0) ? (font.width >> 3) : ((font.width >> 3) + 1);
|
|
const char* toRender = &(font.fTable)[chr*(font.cSize)];
|
|
for(uint16_t xx = 0; xx < font.width; ++xx){
|
|
for(uint16_t yy = 0; yy < font.height; ++yy){
|
|
if(
|
|
toRender[yy*charsPerRow + (xx >> 3)]
|
|
& (1 << (7-(xx & 0b111)))
|
|
){
|
|
invert ? _clrPx(xx+x, yy+y) : _setPx(xx+x, yy+y);
|
|
}else{
|
|
invert ? _setPx(xx+x, yy+y) : _clrPx(xx+x, yy+y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LCD_renderText(font_t font, const char* str, uint16_t x, uint16_t y, bool invert){
|
|
uint_fast16_t i = 0;
|
|
while(*str != 0){
|
|
LCD_renderChar(font, *str, font.width*i+x, y, invert);
|
|
++str;
|
|
++i;
|
|
}
|
|
}
|
|
|
|
bool LCD_updateDisplay(bool block){
|
|
if(!xSemaphoreTake(_lcSemaphore, 0)){
|
|
if(!block) return false;
|
|
else xSemaphoreTake(_lcSemaphore, portMAX_DELAY);
|
|
}
|
|
for(uint32_t j = 0; j < 8; ++j){
|
|
// if(LCD_rowChanged[j] == false) continue;
|
|
for(uint32_t i = 0; i < 128; ++i){
|
|
iicData(IIC_NUM_LCD, IIC_LCD_ADDR, LCD_data[j][i]);
|
|
}
|
|
LCD_rowChanged[j] = false;
|
|
}
|
|
xSemaphoreGive(_lcSemaphore);
|
|
return true;
|
|
}
|
|
|
|
// void T_LCD_updateTask(void* params){
|
|
// while(1){
|
|
// Display_render();
|
|
// vTaskDelay(50 / portTICK_PERIOD_MS);
|
|
// bool needsUpdate = false;
|
|
// for(uint8_t i = 0; i < 8; ++i) needsUpdate = needsUpdate || LCD_rowChanged[i];
|
|
// if(needsUpdate == true) LCD_updateDisplay(true);
|
|
// }
|
|
// }
|
|
|
|
void LCD_init(TaskHandle_t* updateTaskHandle){
|
|
ESP_LOGI(LCD_TAG, "Initializing SSD1306 LCD Interface...");
|
|
|
|
// Initialize the semaphore for LCD access.
|
|
_lcSemaphore = xSemaphoreCreateBinaryStatic(&_lcSemaphore_buf);
|
|
xSemaphoreGive(_lcSemaphore);
|
|
|
|
ESP_LOGD(LCD_TAG, " Initializing IIC");
|
|
iicInit(
|
|
IIC_NUM_LCD,
|
|
CONFIG_LCD_SCL,
|
|
CONFIG_LCD_SDA,
|
|
true,
|
|
200000
|
|
);
|
|
|
|
ESP_LOGD(LCD_TAG, " Sending LCD initialization commands");
|
|
|
|
/* Shouldn't be necessary to take the semaphore here, but better safe than
|
|
* sorry.
|
|
*/
|
|
if(!xSemaphoreTake(_lcSemaphore, 0)) esp_system_abort("Semaphore error during LCD initialization!");
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0xAE);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0xD5);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x80);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0xA8);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x3F);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0xD3);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x00);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x40);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x8D);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x14);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x20);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x00);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0xA1);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0xC8);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0xDA);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x12);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x81);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0xCF);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0xD9);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0xF1);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0xDB);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x40);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0xA4);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0xA6);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0xAF);
|
|
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x00);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x10);
|
|
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x21);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x00);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x7F);
|
|
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x22);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x00);
|
|
iicCommand(IIC_NUM_LCD, IIC_LCD_ADDR, 0x07);
|
|
xSemaphoreGive(_lcSemaphore);
|
|
|
|
ESP_LOGD(LCD_TAG, " Initializing data buffer");
|
|
for(uint32_t j = 0; j < 8; ++j){
|
|
for(uint32_t i = 0; i < 128; ++i){
|
|
LCD_data[j][i] = 0;
|
|
}
|
|
}
|
|
|
|
ESP_LOGD(LCD_TAG, " Clearing Display");
|
|
if(!LCD_updateDisplay(false)){
|
|
ESP_LOGW(LCD_TAG, "Unable to clear LCD display (communication semaphore already in use).");
|
|
}
|
|
|
|
// ESP_LOGD(LCD_TAG, " Spawning LCD update task");
|
|
// TaskHandle_t tsk;
|
|
// if(updateTaskHandle == NULL){
|
|
// updateTaskHandle = &tsk;
|
|
// }
|
|
// xTaskCreate(&T_LCD_updateTask, "LCD Update Task", 2048, NULL, 2, updateTaskHandle);
|
|
|
|
ESP_LOGI(LCD_TAG, "Ready.");
|
|
} |