Turn Display Interface into a singleton Abstract class that can do the registration of callbacks to LVGL and allow implementers of the abstract to support LVGL.

This commit is contained in:
Matthew Colvin 2023-08-13 22:14:40 -05:00 committed by MatthewColvin
parent 419ac45e98
commit 5731148bf6
10 changed files with 154 additions and 163 deletions

View file

@ -0,0 +1,35 @@
#include "DisplayInterface.h"
std::shared_ptr<DisplayInterface> DisplayInterface::mInstance = nullptr;
DisplayInterface::DisplayInterface(){
lv_init();
lv_disp_draw_buf_init(&mdraw_buf, mbufA, mbufB,
SCREEN_WIDTH * SCREEN_HEIGHT / 10);
// Initialize the display driver
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = SCREEN_WIDTH;
disp_drv.ver_res = SCREEN_HEIGHT;
disp_drv.flush_cb = &DisplayInterface::flushDisplayImpl;
disp_drv.draw_buf = &mdraw_buf;
lv_disp_drv_register(&disp_drv);
// Initialize the touchscreen driver
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = &DisplayInterface::screenInputImpl;
lv_indev_drv_register(&indev_drv);
}
void DisplayInterface::flushDisplayImpl(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
mInstance->flushDisplay(disp, area, color_p);
}
void DisplayInterface::screenInputImpl(lv_indev_drv_t *indev_driver, lv_indev_data_t *data) {
mInstance->screenInput(indev_driver, data);
}

View file

@ -10,6 +10,7 @@ HardwareAbstract::HardwareAbstract(
mDisplay(std::move(aDisplay))
{}
std::optional<HardwareAbstract::batteryStatus> HardwareAbstract::getBatteryStatus(){
if(mBattery){
HardwareAbstract::batteryStatus currentStatus;

View file

@ -48,4 +48,6 @@ public:
std::shared_ptr<BatteryInterface> mBattery;
std::shared_ptr<wifiHandlerInterface> mWifiHandler;
std::shared_ptr<DisplayInterface> mDisplay;
};

View file

@ -1,13 +1,27 @@
#pragma once
#include <cstdint>
#include <memory>
#include "lvgl.h"
class DisplayInterface
{
public:
DisplayInterface();
virtual void setBrightness(uint8_t brightness) = 0;
virtual void turnOff() = 0;
protected:
virtual void pushPixel(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t* pixel_values) = 0;
// Set this with a getInstance method in the Child Class
static std::shared_ptr<DisplayInterface> mInstance;
virtual void flushDisplay(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) = 0;
virtual void screenInput(lv_indev_drv_t *indev_driver, lv_indev_data_t *data) = 0;
private:
// Used to satisfy LVGL APIs
static void flushDisplayImpl(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p);
static void screenInputImpl(lv_indev_drv_t *indev_driver, lv_indev_data_t *data);
// LVGL Screen Buffers
lv_disp_draw_buf_t mdraw_buf;
lv_color_t mbufA[SCREEN_WIDTH * SCREEN_HEIGHT / 10];
lv_color_t mbufB[SCREEN_WIDTH * SCREEN_HEIGHT / 10];
};

View file

@ -55,7 +55,7 @@ HardwareRevX::HardwareRevX():
HardwareAbstract(
std::make_shared<Battery>(ADC_BAT,CRG_STAT),
wifiHandler::getInstance(),
Display::getInstance()
Display::getInstance(standbyTimer)
){}
HardwareRevX::WakeReason getWakeReason() {
@ -80,9 +80,6 @@ void HardwareRevX::init() {
Serial.begin(115200);
restorePreferences();
slowDisplayWakeup();
setupTFT();
setupTouchScreen();
initLVGL();
setupIMU();
setupIR();
@ -100,72 +97,6 @@ std::shared_ptr<HardwareRevX> HardwareRevX::getInstance(){
return mInstance;
}
void HardwareRevX::initLVGL() {
lv_init();
lv_disp_draw_buf_init(&mdraw_buf, mbufA, mbufB,
SCREEN_WIDTH * SCREEN_HEIGHT / 10);
// Initialize the display driver
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = SCREEN_WIDTH;
disp_drv.ver_res = SCREEN_HEIGHT;
disp_drv.flush_cb = &HardwareRevX::displayFlushImpl;
disp_drv.draw_buf = &mdraw_buf;
lv_disp_drv_register(&disp_drv);
// Initialize the touchscreen driver
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = &HardwareRevX::touchPadReadImpl;
lv_indev_drv_register(&indev_drv);
}
void HardwareRevX::handleDisplayFlush(lv_disp_drv_t *disp,
const lv_area_t *area,
lv_color_t *color_p) {
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
tft.startWrite();
tft.setAddrWindow(area->x1, area->y1, w, h);
tft.pushPixelsDMA((uint16_t *)&color_p->full, w * h);
tft.endWrite();
lv_disp_flush_ready(disp);
}
void HardwareRevX::handleTouchPadRead(lv_indev_drv_t *indev_driver,
lv_indev_data_t *data) {
// int16_t touchX, touchY;
touchPoint = touch.getPoint();
int16_t touchX = touchPoint.x;
int16_t touchY = touchPoint.y;
bool touched = false;
if ((touchX > 0) || (touchY > 0)) {
touched = true;
standbyTimer = SLEEP_TIMEOUT;
}
if (!touched) {
data->state = LV_INDEV_STATE_REL;
} else {
data->state = LV_INDEV_STATE_PR;
// Set the coordinates
data->point.x = SCREEN_WIDTH - touchX;
data->point.y = SCREEN_HEIGHT - touchY;
// Serial.print( "touchpoint: x" );
// Serial.print( touchX );
// Serial.print( " y" );
// Serial.println( touchY );
// tft.drawFastHLine(0, screenHeight - touchY, screenWidth, TFT_RED);
// tft.drawFastVLine(screenWidth - touchX, 0, screenHeight, TFT_RED);
}
}
void HardwareRevX::activityDetection() {
static int accXold;
@ -332,20 +263,7 @@ void HardwareRevX::restorePreferences() {
}
}
void HardwareRevX::setupTFT() {
// Setup TFT
tft.init();
tft.initDMA();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
tft.setSwapBytes(true);
}
void HardwareRevX::setupTouchScreen() {
// Configure i2c pins and set frequency to 400kHz
Wire.begin(SDA, SCL, 400000);
touch.begin(128); // Initialize touchscreen and set sensitivity threshold
}
void HardwareRevX::setupIMU() {
// Setup hal

View file

@ -2,10 +2,8 @@
#include "SparkFunLIS3DH.h"
#include "HardwareAbstract.hpp"
#include "Wire.h"
#include "lvgl.h"
#include "battery.hpp"
#include <Adafruit_FT6206.h>
#include <IRrecv.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
@ -13,7 +11,6 @@
#include <Keypad.h> // modified for inverted logic
#include <Preferences.h>
#include <PubSubClient.h>
#include <TFT_eSPI.h> // Hardware-specific library
#include <functional>
#include <memory>
@ -39,9 +36,6 @@ protected:
void setupBacklight();
void restorePreferences();
void slowDisplayWakeup();
void setupTFT();
void setupTouchScreen();
void initLVGL();
void setupIMU();
void setupIR();
@ -49,11 +43,6 @@ protected:
void enterSleep();
void configIMUInterrupts();
// UI/UX Handlers
void handleDisplayFlush(lv_disp_drv_t *disp, const lv_area_t *area,
lv_color_t *color_p);
void handleTouchPadRead(lv_indev_drv_t *indev_driver, lv_indev_data_t *data);
// Tasks
void startTasks();
/// @brief Send Battery Notification every 5 Seconds
@ -64,22 +53,6 @@ protected:
private:
HardwareRevX();
// Static Wrappers Needed to Satisfy C APIs
static void displayFlushImpl(lv_disp_drv_t *disp, const lv_area_t *area,
lv_color_t *color_p) {
mInstance->handleDisplayFlush(disp, area, color_p);
}
static void touchPadReadImpl(lv_indev_drv_t *indev_driver,
lv_indev_data_t *data) {
mInstance->handleTouchPadRead(indev_driver, data);
}
Adafruit_FT6206 touch = Adafruit_FT6206();
TS_Point touchPoint;
TS_Point oldPoint;
TFT_eSPI tft = TFT_eSPI();
// IMU Motion Detection
LIS3DH IMU = LIS3DH(I2C_MODE, 0x19); // Default constructor is I2C, addr 0x19.
int standbyTimer = SLEEP_TIMEOUT;
@ -96,10 +69,7 @@ private:
IRsend IrSender = IRsend(IR_LED, true);
IRrecv IrReceiver = IRrecv(IR_RX);
// LVGL Screen Buffers
lv_disp_draw_buf_t mdraw_buf;
lv_color_t mbufA[SCREEN_WIDTH * SCREEN_HEIGHT / 10];
lv_color_t mbufB[SCREEN_WIDTH * SCREEN_HEIGHT / 10];
lv_color_t color_primary = lv_color_hex(0x303030); // gray

View file

@ -1,56 +1,58 @@
#include "display.hpp"
#include "omoteconfig.h"
#include "Wire.h"
std::shared_ptr<Display> Display::mInstance = nullptr;
std::shared_ptr<Display> Display::getInstance()
std::shared_ptr<Display> Display::getInstance(int& standby_timer)
{
if (mInstance == nullptr)
if (DisplayInterface::mInstance == nullptr)
{
mInstance = std::shared_ptr<Display>(new Display(LCD_EN, LCD_BL));
DisplayInterface::mInstance = std::shared_ptr<Display>(new Display(LCD_EN, LCD_BL, standby_timer));
}
return mInstance;
return std::static_pointer_cast<Display>(mInstance);
}
Display::Display(int backlight_pin, int enable_pin)
Display::Display(int backlight_pin, int enable_pin, int& standby_timer): DisplayInterface(),
mBacklightPin(backlight_pin),
mEnablePin(enable_pin),
tft(TFT_eSPI()),
touch(Adafruit_FT6206()),
standbyTimer(standby_timer)
{
this->enable_pin = enable_pin;
this->backlight_pin = backlight_pin;
pinMode(this->enable_pin, OUTPUT);
digitalWrite(this->enable_pin, HIGH);
pinMode(this->backlight_pin, OUTPUT);
digitalWrite(this->backlight_pin, HIGH);
this->tft = TFT_eSPI();
pinMode(mEnablePin, OUTPUT);
digitalWrite(mEnablePin, HIGH);
pinMode(mBacklightPin, OUTPUT);
digitalWrite(mBacklightPin, HIGH);
ledcSetup(LCD_BACKLIGHT_LEDC_CHANNEL, LCD_BACKLIGHT_LEDC_FREQUENCY, LCD_BACKLIGHT_LEDC_BIT_RESOLUTION);
ledcAttachPin(this->backlight_pin, LCD_BACKLIGHT_LEDC_CHANNEL);
ledcAttachPin(mBacklightPin, LCD_BACKLIGHT_LEDC_CHANNEL);
ledcWrite(LCD_BACKLIGHT_LEDC_CHANNEL, 0);
this->tft.init();
this->tft.initDMA();
this->tft.setRotation(0);
this->tft.fillScreen(TFT_BLACK);
this->tft.setSwapBytes(true);
setupTFT();
// Slowly charge the VSW voltage to prevent a brownout
// Workaround for hardware rev 1!
for(int i = 0; i < 100; i++){
digitalWrite(this->enable_pin, HIGH); // LCD Logic off
digitalWrite(this->mEnablePin, HIGH); // LCD Logic off
delayMicroseconds(1);
digitalWrite(this->enable_pin, LOW); // LCD Logic on
digitalWrite(this->mEnablePin, LOW); // LCD Logic on
}
setupTouchScreen();
}
void Display::pushPixel(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t* pixel_values)
{
this->tft.startWrite();
this->tft.setAddrWindow( x, y, w, h );
this->tft.pushPixelsDMA(pixel_values, w * h);
this->tft.endWrite();
void Display::setupTFT() {
tft.init();
tft.initDMA();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
tft.setSwapBytes(true);
}
void Display::setupTouchScreen(){
// Configure i2c pins and set frequency to 400kHz
Wire.begin(SDA, SCL, 400000);
touch.begin(128); // Initialize touchscreen and set sensitivity threshold
}
void Display::setBrightness(uint8_t brigthness)
@ -60,11 +62,51 @@ void Display::setBrightness(uint8_t brigthness)
void Display::turnOff()
{
digitalWrite(this->backlight_pin, HIGH);
digitalWrite(this->enable_pin, HIGH);
pinMode(this->backlight_pin, INPUT);
pinMode(this->enable_pin, INPUT);
gpio_hold_en((gpio_num_t) this->backlight_pin);
gpio_hold_en((gpio_num_t) this->enable_pin);
digitalWrite(this->mBacklightPin, HIGH);
digitalWrite(this->mEnablePin, HIGH);
pinMode(this->mBacklightPin, INPUT);
pinMode(this->mEnablePin, INPUT);
gpio_hold_en((gpio_num_t) mBacklightPin);
gpio_hold_en((gpio_num_t) mEnablePin);
}
void Display::screenInput(lv_indev_drv_t *indev_driver, lv_indev_data_t *data){
// int16_t touchX, touchY;
touchPoint = touch.getPoint();
int16_t touchX = touchPoint.x;
int16_t touchY = touchPoint.y;
bool touched = false;
if ((touchX > 0) || (touchY > 0)) {
touched = true;
standbyTimer = SLEEP_TIMEOUT;
}
if (!touched) {
data->state = LV_INDEV_STATE_REL;
} else {
data->state = LV_INDEV_STATE_PR;
// Set the coordinates
data->point.x = SCREEN_WIDTH - touchX;
data->point.y = SCREEN_HEIGHT - touchY;
// Serial.print( "touchpoint: x" );
// Serial.print( touchX );
// Serial.print( " y" );
// Serial.println( touchY );
// tft.drawFastHLine(0, screenHeight - touchY, screenWidth, TFT_RED);
// tft.drawFastVLine(screenWidth - touchX, 0, screenHeight, TFT_RED);
}
}
void Display::flushDisplay(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
tft.startWrite();
tft.setAddrWindow(area->x1, area->y1, w, h);
tft.pushPixelsDMA((uint16_t *)&color_p->full, w * h);
tft.endWrite();
lv_disp_flush_ready(disp);
}

View file

@ -1,7 +1,8 @@
#pragma once
#include "DisplayInterface.h"
#include <TFT_eSPI.h>
#include <Adafruit_FT6206.h>
#include <memory>
#include <TFT_eSPI.h>
#include "driver/ledc.h"
/*LEDC Channel to use for the LCD backlight*/
@ -17,19 +18,28 @@
class Display: public DisplayInterface
{
public:
static std::shared_ptr<Display> getInstance();
static std::shared_ptr<Display> getInstance(int& standby_timer);
virtual void setBrightness(uint8_t brightness) override;
virtual void turnOff() override;
protected:
virtual void flushDisplay(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {};
virtual void pushPixel(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint16_t* pixel_values) override;
virtual void flushDisplay(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p);
virtual void screenInput(lv_indev_drv_t *indev_driver, lv_indev_data_t *data) override;
private:
static std::shared_ptr<Display> mInstance;
int enable_pin;
int backlight_pin;
Display(int backlight_pin, int enable_pin);
// TODO Find a better way to handle timeout resets
Display(int backlight_pin, int enable_pin, int& standby_timer);
void setupTFT();
void setupTouchScreen();
int mEnablePin;
int mBacklightPin;
TFT_eSPI tft;
Adafruit_FT6206 touch;
TS_Point touchPoint;
TS_Point oldPoint;
int& standbyTimer;
};

View file

@ -25,8 +25,6 @@ static int tick_thread(void * data)
}
void HardwareSimulator::init() {
lv_init();
// Workaround for sdl2 `-m32` crash

View file

@ -48,6 +48,7 @@ lib_archive = false
build_src_filter =
+<../OmoteUI/*>
+<../HAL/HardwareAbstract.cpp>
+<../HAL/DisplayInterface.cpp>