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)) mDisplay(std::move(aDisplay))
{} {}
std::optional<HardwareAbstract::batteryStatus> HardwareAbstract::getBatteryStatus(){ std::optional<HardwareAbstract::batteryStatus> HardwareAbstract::getBatteryStatus(){
if(mBattery){ if(mBattery){
HardwareAbstract::batteryStatus currentStatus; HardwareAbstract::batteryStatus currentStatus;

View file

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

View file

@ -1,13 +1,27 @@
#pragma once #pragma once
#include <cstdint> #include <memory>
#include "lvgl.h" #include "lvgl.h"
class DisplayInterface class DisplayInterface
{ {
public: public:
DisplayInterface();
virtual void setBrightness(uint8_t brightness) = 0; virtual void setBrightness(uint8_t brightness) = 0;
virtual void turnOff() = 0; virtual void turnOff() = 0;
protected: 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 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( HardwareAbstract(
std::make_shared<Battery>(ADC_BAT,CRG_STAT), std::make_shared<Battery>(ADC_BAT,CRG_STAT),
wifiHandler::getInstance(), wifiHandler::getInstance(),
Display::getInstance() Display::getInstance(standbyTimer)
){} ){}
HardwareRevX::WakeReason getWakeReason() { HardwareRevX::WakeReason getWakeReason() {
@ -80,9 +80,6 @@ void HardwareRevX::init() {
Serial.begin(115200); Serial.begin(115200);
restorePreferences(); restorePreferences();
slowDisplayWakeup(); slowDisplayWakeup();
setupTFT();
setupTouchScreen();
initLVGL();
setupIMU(); setupIMU();
setupIR(); setupIR();
@ -100,72 +97,6 @@ std::shared_ptr<HardwareRevX> HardwareRevX::getInstance(){
return mInstance; 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() { void HardwareRevX::activityDetection() {
static int accXold; 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() { void HardwareRevX::setupIMU() {
// Setup hal // Setup hal

View file

@ -2,10 +2,8 @@
#include "SparkFunLIS3DH.h" #include "SparkFunLIS3DH.h"
#include "HardwareAbstract.hpp" #include "HardwareAbstract.hpp"
#include "Wire.h"
#include "lvgl.h" #include "lvgl.h"
#include "battery.hpp" #include "battery.hpp"
#include <Adafruit_FT6206.h>
#include <IRrecv.h> #include <IRrecv.h>
#include <IRremoteESP8266.h> #include <IRremoteESP8266.h>
#include <IRsend.h> #include <IRsend.h>
@ -13,7 +11,6 @@
#include <Keypad.h> // modified for inverted logic #include <Keypad.h> // modified for inverted logic
#include <Preferences.h> #include <Preferences.h>
#include <PubSubClient.h> #include <PubSubClient.h>
#include <TFT_eSPI.h> // Hardware-specific library
#include <functional> #include <functional>
#include <memory> #include <memory>
@ -39,9 +36,6 @@ protected:
void setupBacklight(); void setupBacklight();
void restorePreferences(); void restorePreferences();
void slowDisplayWakeup(); void slowDisplayWakeup();
void setupTFT();
void setupTouchScreen();
void initLVGL();
void setupIMU(); void setupIMU();
void setupIR(); void setupIR();
@ -49,11 +43,6 @@ protected:
void enterSleep(); void enterSleep();
void configIMUInterrupts(); 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 // Tasks
void startTasks(); void startTasks();
/// @brief Send Battery Notification every 5 Seconds /// @brief Send Battery Notification every 5 Seconds
@ -64,22 +53,6 @@ protected:
private: private:
HardwareRevX(); 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 // IMU Motion Detection
LIS3DH IMU = LIS3DH(I2C_MODE, 0x19); // Default constructor is I2C, addr 0x19. LIS3DH IMU = LIS3DH(I2C_MODE, 0x19); // Default constructor is I2C, addr 0x19.
int standbyTimer = SLEEP_TIMEOUT; int standbyTimer = SLEEP_TIMEOUT;
@ -96,10 +69,7 @@ private:
IRsend IrSender = IRsend(IR_LED, true); IRsend IrSender = IRsend(IR_LED, true);
IRrecv IrReceiver = IRrecv(IR_RX); 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 lv_color_t color_primary = lv_color_hex(0x303030); // gray

View file

@ -1,56 +1,58 @@
#include "display.hpp" #include "display.hpp"
#include "omoteconfig.h" #include "omoteconfig.h"
#include "Wire.h"
std::shared_ptr<Display> Display::mInstance = nullptr; std::shared_ptr<Display> Display::getInstance(int& standby_timer)
std::shared_ptr<Display> Display::getInstance()
{ {
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 std::static_pointer_cast<Display>(mInstance);
return 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; pinMode(mEnablePin, OUTPUT);
this->backlight_pin = backlight_pin; digitalWrite(mEnablePin, HIGH);
pinMode(this->enable_pin, OUTPUT); pinMode(mBacklightPin, OUTPUT);
digitalWrite(this->enable_pin, HIGH); digitalWrite(mBacklightPin, HIGH);
pinMode(this->backlight_pin, OUTPUT);
digitalWrite(this->backlight_pin, HIGH);
this->tft = TFT_eSPI();
ledcSetup(LCD_BACKLIGHT_LEDC_CHANNEL, LCD_BACKLIGHT_LEDC_FREQUENCY, LCD_BACKLIGHT_LEDC_BIT_RESOLUTION); 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); ledcWrite(LCD_BACKLIGHT_LEDC_CHANNEL, 0);
this->tft.init(); setupTFT();
this->tft.initDMA();
this->tft.setRotation(0);
this->tft.fillScreen(TFT_BLACK);
this->tft.setSwapBytes(true);
// Slowly charge the VSW voltage to prevent a brownout // Slowly charge the VSW voltage to prevent a brownout
// Workaround for hardware rev 1! // Workaround for hardware rev 1!
for(int i = 0; i < 100; i++){ for(int i = 0; i < 100; i++){
digitalWrite(this->enable_pin, HIGH); // LCD Logic off digitalWrite(this->mEnablePin, HIGH); // LCD Logic off
delayMicroseconds(1); 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) void Display::setupTFT() {
{ tft.init();
this->tft.startWrite(); tft.initDMA();
this->tft.setAddrWindow( x, y, w, h ); tft.setRotation(0);
this->tft.pushPixelsDMA(pixel_values, w * h); tft.fillScreen(TFT_BLACK);
this->tft.endWrite(); 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) void Display::setBrightness(uint8_t brigthness)
@ -60,11 +62,51 @@ void Display::setBrightness(uint8_t brigthness)
void Display::turnOff() void Display::turnOff()
{ {
digitalWrite(this->backlight_pin, HIGH); digitalWrite(this->mBacklightPin, HIGH);
digitalWrite(this->enable_pin, HIGH); digitalWrite(this->mEnablePin, HIGH);
pinMode(this->backlight_pin, INPUT); pinMode(this->mBacklightPin, INPUT);
pinMode(this->enable_pin, INPUT); pinMode(this->mEnablePin, INPUT);
gpio_hold_en((gpio_num_t) this->backlight_pin); gpio_hold_en((gpio_num_t) mBacklightPin);
gpio_hold_en((gpio_num_t) this->enable_pin); 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 #pragma once
#include "DisplayInterface.h" #include "DisplayInterface.h"
#include <TFT_eSPI.h> #include <Adafruit_FT6206.h>
#include <memory> #include <memory>
#include <TFT_eSPI.h>
#include "driver/ledc.h" #include "driver/ledc.h"
/*LEDC Channel to use for the LCD backlight*/ /*LEDC Channel to use for the LCD backlight*/
@ -17,19 +18,28 @@
class Display: public DisplayInterface class Display: public DisplayInterface
{ {
public: 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 setBrightness(uint8_t brightness) override;
virtual void turnOff() override; virtual void turnOff() override;
protected: protected:
virtual void flushDisplay(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {}; 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 screenInput(lv_indev_drv_t *indev_driver, lv_indev_data_t *data) override;
private: private:
static std::shared_ptr<Display> mInstance; // TODO Find a better way to handle timeout resets
int enable_pin; Display(int backlight_pin, int enable_pin, int& standby_timer);
int backlight_pin; void setupTFT();
Display(int backlight_pin, int enable_pin); void setupTouchScreen();
int mEnablePin;
int mBacklightPin;
TFT_eSPI tft; 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() { void HardwareSimulator::init() {
lv_init(); lv_init();
// Workaround for sdl2 `-m32` crash // Workaround for sdl2 `-m32` crash

View file

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