diff --git a/include/graphics/driver/LGFXDriver.h b/include/graphics/LGFX/LGFXDriver.h similarity index 99% rename from include/graphics/driver/LGFXDriver.h rename to include/graphics/LGFX/LGFXDriver.h index e13fb79d..1b1f5526 100644 --- a/include/graphics/driver/LGFXDriver.h +++ b/include/graphics/LGFX/LGFXDriver.h @@ -1,5 +1,6 @@ #pragma once +#ifdef LGFX_DRIVER #include "LovyanGFX.h" #include "graphics/driver/DisplayDriverConfig.h" #include "graphics/driver/TFTDriver.h" @@ -465,3 +466,5 @@ template void LGFXDriver::printConfig(void) ILOG_DEBUG("BL pin assigned"); } } + +#endif \ No newline at end of file diff --git a/include/graphics/LGFX/LGFX_GENERIC.h b/include/graphics/LGFX/LGFX_GENERIC.h index 3be7115d..b09226e7 100644 --- a/include/graphics/LGFX/LGFX_GENERIC.h +++ b/include/graphics/LGFX/LGFX_GENERIC.h @@ -12,6 +12,7 @@ * build_flags = * -D LGFX_DRIVER_TEMPLATE * -D LGFX_DRIVER=LGFX_GENERIC + * -D GFX_DRIVER_INC=\"graphics/LGFX/LGFX_GENERIC.h\" * -D LGFX_PANEL=ST7789 * -D LGFX_TOUCH=XPT2046 * -D LGFX_INVERT_COLOR=true @@ -30,6 +31,8 @@ */ #define LGFX_USE_V1 +#include "hal/gpio_types.h" +#include "util/ILog.h" #include #ifndef SPI_FREQUENCY @@ -206,6 +209,20 @@ class LGFX_GENERIC : public lgfx::LGFX_Device bool hasButton(void) { return false; } + bool init_impl(bool use_reset, bool use_clear) override + { + // increase output power to SPI GPIOs to be able to run with higher frequency + if (LGFX_PIN_SCK >= 0) { + gpio_set_direction((gpio_num_t)LGFX_PIN_SCK, GPIO_MODE_OUTPUT); + gpio_set_drive_capability((gpio_num_t)LGFX_PIN_SCK, GPIO_DRIVE_CAP_3); + } + if (LGFX_PIN_MOSI >= 0) { + gpio_set_direction((gpio_num_t)LGFX_PIN_SCK, GPIO_MODE_OUTPUT); + gpio_set_drive_capability((gpio_num_t)LGFX_PIN_MOSI, GPIO_DRIVE_CAP_3); + } + return lgfx::LGFX_Device::init_impl(use_reset, use_clear); + } + LGFX_GENERIC(void) { { diff --git a/include/graphics/LVGL/LVGLConfig.h b/include/graphics/LVGL/LVGLConfig.h new file mode 100644 index 00000000..be732958 --- /dev/null +++ b/include/graphics/LVGL/LVGLConfig.h @@ -0,0 +1,41 @@ +#pragma once + +#include "graphics/LVGL/LVGLDriver.h" +#include "graphics/LVGL/LVGLSpiBusDriver.h" +#include "graphics/driver/DisplayDriverConfig.h" +#include "lvgl.h" +#include "strings.h" +#include "util/ILog.h" + +class DisplayDeviceDriver; + +/** + * @brief Runtime configuration class for lvgl driver + * + */ +class LVGLConfig : public DisplayDeviceDriver +{ + public: + uint16_t screenWidth = 0; + uint16_t screenHeight = 0; + + LVGLConfig(void); + LVGLConfig(const DisplayDriverConfig &cfg); + void init(void); + lv_display_t *createDisplay(uint32_t hor_res, uint32_t ver_res); + void touchpad_read(lv_indev_t *indev_driver, lv_indev_data_t *data); + + bool hasButton(void); + bool hasTouch(void); + bool light(void); + + uint8_t getBrightness(void); + void setBrightness(uint8_t setBrightness); + + ~LVGLConfig(); + + protected: + private: + const DisplayDriverConfig *config; + DisplayDeviceDriver *lvglDeviceDriver; +}; diff --git a/include/graphics/LVGL/LVGLDriver.h b/include/graphics/LVGL/LVGLDriver.h new file mode 100644 index 00000000..afc26988 --- /dev/null +++ b/include/graphics/LVGL/LVGLDriver.h @@ -0,0 +1,176 @@ +#pragma once + +#ifdef LVGL_DRIVER +#include "drivers/display/lcd/lv_lcd_generic_mipi.h" +#include "graphics/driver/DisplayDriverConfig.h" +#include "graphics/driver/TFTDriver.h" +#include "input/InputDriver.h" +#include "util/ILog.h" + +/** + * Base class for all specific LVGL drivers classes + */ +template class LVGLDriver : public TFTDriver +{ + public: + const uint32_t defaultScreenTimeout = 60 * 1000; + const uint32_t defaultScreenLockTimeout = 5 * 1000; + const uint32_t defaultBrightness = 153; + + LVGLDriver(uint16_t width, uint16_t height); + LVGLDriver(const DisplayDriverConfig &cfg); + void init(DeviceGUI *gui) override; + bool hasTouch(void) override; + bool hasButton(void) override { return lvgldriver->hasButton(); } + bool hasLight(void) override { return lvgldriver->light(); } + bool isPowersaving(void) override { return powerSaving; } + void task_handler(void) override; + + uint8_t getBrightness(void) override { return lvgldriver->getBrightness(); } + void setBrightness(uint8_t brightness) override {} // TODO + + uint16_t getScreenTimeout() override { return screenTimeout / 1000; } + void setScreenTimeout(uint16_t timeout) override { screenTimeout = timeout * 1000; }; + + ~LVGLDriver() { delete lvgldriver; } + + protected: + void init_lvgl(void); + static void display_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map); + static void touchpad_read(lv_indev_t *indev_driver, lv_indev_data_t *data) + { + return lvgldriver->touchpad_read(indev_driver, data); + } + + uint32_t screenTimeout; + uint32_t lastBrightness; + bool powerSaving; + + private: + static LVGL *lvgldriver; + lv_display_t *display; + size_t bufsize; + std::pair drawBuffer = {nullptr, nullptr}; +}; + +template LVGL *LVGLDriver::lvgldriver = nullptr; + +template +LVGLDriver::LVGLDriver(uint16_t width, uint16_t height) + : TFTDriver(lvgldriver ? lvgldriver : new LVGL, width, height), screenTimeout(defaultScreenTimeout), + lastBrightness(defaultBrightness), powerSaving(false), display(nullptr), bufsize(0) +{ + lvgldriver = this->tft; +} + +template +LVGLDriver::LVGLDriver(const DisplayDriverConfig &cfg) + : TFTDriver(lvgldriver ? lvgldriver : new LVGL(cfg), cfg.width(), cfg.height()), screenTimeout(defaultScreenTimeout), + lastBrightness(defaultBrightness), powerSaving(false), display(nullptr), bufsize(0) +{ + lvgldriver = this->tft; +} + +template void LVGLDriver::init(DeviceGUI *gui) +{ + ILOG_DEBUG("LVGLDriver::init..."); + TFTDriver::init(gui); + init_lvgl(); + + // LVGL: setup display device driver + ILOG_DEBUG("LVGL display driver create..."); + display = lvgldriver->createDisplay(DisplayDriver::screenWidth, DisplayDriver::screenHeight); + + std::pair draw_buffers = {nullptr, nullptr}; + const auto buffer_size = + DisplayDriver::screenWidth * DisplayDriver::screenHeight * lv_color_format_get_size(lv_display_get_color_format(display)); + + ILOG_INFO("allocating %d bytes for LVGL draw buffers", buffer_size); + // ESP_ERROR_CHECK(esp_dma_malloc(EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), ESP_DMA_MALLOC_FLAG_PSRAM, draw_buffers.first, + // nullptr)); ESP_ERROR_CHECK(esp_dma_malloc(EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), ESP_DMA_MALLOC_FLAG_PSRAM, + // draw_buffers.second, nullptr)); + + draw_buffers.first = (lv_color_t *)lv_malloc(buffer_size); + if (draw_buffers.first == nullptr) { + LV_LOG_ERROR("display draw buffer malloc failed"); + return; + } + + draw_buffers.second = (lv_color_t *)lv_malloc(buffer_size); + if (draw_buffers.second == nullptr) { + LV_LOG_ERROR("display buffer malloc failed"); + lv_free(draw_buffers.first); + return; + } + lv_display_set_buffers(display, draw_buffers.first, draw_buffers.second, buffer_size, LV_DISPLAY_RENDER_MODE_PARTIAL); + + lv_display_set_flush_cb(display, LVGLDriver::display_flush); + + #if defined(DISPLAY_SET_RESOLUTION) + ILOG_DEBUG("Set display resolution: %dx%d", lvgldriver->screenWidth, lgflvgldriverx->screenHeight); + lv_display_set_resolution(this->display, lvgldriver->screenWidth, lvgldriver->screenHeight); + #endif + + if (hasTouch()) { + lv_indev_t *indev = lv_indev_create(); + lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); + lv_indev_set_read_cb(indev, touchpad_read); + lv_indev_set_display(indev, this->display); + } +} + +template void LVGLDriver::init_lvgl(void) +{ + lvgldriver->init(); + lvgldriver->setBrightness(defaultBrightness); + // lvgldriver->fillScreen(LVGL::color565(0x3D, 0xDA, 0x83)); +} + +template bool LVGLDriver::hasTouch(void) +{ + return lvgldriver->hasTouch(); +} + +template void LVGLDriver::task_handler(void) +{ + DisplayDriver::task_handler(); +} + +// Display flushing not using DMA */ +template void LVGLDriver::display_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) +{ + uint32_t w = area->x2 - area->x1 + 1; + uint32_t h = area->y2 - area->y1 + 1; + lv_draw_sw_rgb565_swap(px_map, w * h); +#if 0 // TODO: //// lvgldriver->pushImage(area->x1, area->y1, w, h, (uint16_t *)px_map) //// + + pixelcopy_t pc(px_map, _write_conv.depth, get_depth::value, hasPalette()); + if (std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value) + { + pc.no_convert = false; + pc.fp_copy = pixelcopy_t::get_fp_copy_rgb_affine(_write_conv.depth); + } + pixelcopy_t *param = &pc; + uint32_t x_mask = 7 >> (param->src_bits >> 1); + param->src_bitwidth = (w + x_mask) & (~x_mask); + + int32_t dx=0, dw=w; + if (0 < _clip_l - x) { dx = _clip_l - x; dw -= dx; x = _clip_l; } + + if (_adjust_width(x, dx, dw, _clip_l, _clip_r - _clip_l + 1)) return; + param->src_x32 = param->src_x32_add * dx; + + int32_t dy=0, dh=h; + if (0 < _clip_t - y) { dy = _clip_t - y; dh -= dy; y = _clip_t; } + if (_adjust_width(y, dy, dh, _clip_t, _clip_b - _clip_t + 1)) return; + param->src_y = dy; + + startWrite(); + _panel->writeImage(x, y, dw, dh, param, use_dma); + endWrite(); +#endif + + lv_display_flush_ready(disp); +} + +#endif \ No newline at end of file diff --git a/include/graphics/LVGL/LVGLSpiBusDriver.h b/include/graphics/LVGL/LVGLSpiBusDriver.h new file mode 100644 index 00000000..b024ccd8 --- /dev/null +++ b/include/graphics/LVGL/LVGLSpiBusDriver.h @@ -0,0 +1,53 @@ +#pragma once + +#ifdef LVGL_DRIVER +#ifdef ARCH_PORTDUINO +#include "HardwareSPI.h" +#else +#include "SPI.h" +#endif +#include "drivers/display/lcd/lv_lcd_generic_mipi.h" +#include "graphics/driver/DisplayDeviceDriver.h" +//#include "graphics/driver/DisplayDriverConfig.h" +#include + +class LVGLSpiBusDriver +{ + public: + using CreateCB = + std::function; + + LVGLSpiBusDriver(uint16_t width, uint16_t height, CreateCB createDisplayFunc); + void init(int16_t spiBus, int16_t sclk, int16_t mosi, int16_t miso, int16_t dc, int16_t rst, int16_t cs); + lv_display_t *createDisplay(uint32_t hor_res, uint32_t ver_res); + virtual ~LVGLSpiBusDriver() + { + if (spi) + delete spi; + } + + protected: + LVGLSpiBusDriver() = default; + virtual void connect(void); + + private: + LVGLSpiBusDriver(const LVGLSpiBusDriver &) = delete; + LVGLSpiBusDriver &operator=(const LVGLSpiBusDriver &) = delete; + + static void lcd_send_cmd_cb(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, const uint8_t *param, size_t param_size); + static void lcd_send_color_cb(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, uint8_t *param, size_t param_size); + + int8_t rst; + int8_t dc; + int8_t cs; + int8_t sclk; + int8_t mosi; + int8_t miso; + int8_t spiBus; + SPISettings spiSettings; + static SPIClass *spi; + static LVGLSpiBusDriver *instance; + CreateCB lv_createDisplay; +}; + +#endif \ No newline at end of file diff --git a/include/graphics/LVGL/LVGL_T_DECK.h b/include/graphics/LVGL/LVGL_T_DECK.h new file mode 100644 index 00000000..6fa6fc1b --- /dev/null +++ b/include/graphics/LVGL/LVGL_T_DECK.h @@ -0,0 +1,35 @@ +#pragma once + +#ifdef LVGL_DRIVER + +#include "graphics/driver/LVGL_ST7789.h" +#include "lvgl.h" +#include "stdint.h" + +class LVGLSpiBusDriver; + +class LVGL_TDECK : public LVGL_ST7789 +{ + public: + static constexpr uint16_t c_screenWidth = 320; + static constexpr uint16_t c_screenHeight = 240; + + LVGL_TDECK(void); + LVGL_TDECK(const DisplayDriverConfig &cfg); + + void init(void) override; + bool hasButton(void) const override { return true; } + bool hasTouch(void) const override { return true; } + bool light(void) override { return true; } + uint8_t getBrightness(void) const override { return 150; } + void setBrightness(uint8_t setBrightness) override {} + + void touchpad_read(lv_indev_t *indev_driver, lv_indev_data_t *data); + + ~LVGL_TDECK() {} + + protected: + private: +}; + +#endif diff --git a/include/graphics/driver/DisplayDeviceDriver.h b/include/graphics/driver/DisplayDeviceDriver.h new file mode 100644 index 00000000..3ff90836 --- /dev/null +++ b/include/graphics/driver/DisplayDeviceDriver.h @@ -0,0 +1,36 @@ +#pragma once + +#include "lvgl.h" +#include "stdint.h" + +/** + * Base class for all implemented display device drivers + */ +class DisplayDeviceDriver +{ + public: + DisplayDeviceDriver(uint16_t width, uint16_t height) : screenWidth(width), screenHeight(height){}; + + virtual void init(void) {} + virtual lv_display_t *createDisplay(uint32_t hor_res, uint32_t ver_res) = 0; + + virtual uint8_t getBrightness(void) const { return 128; } + virtual void setBrightness(uint8_t brightness) {} + + virtual uint16_t getScreenTimeout() const { return 0; } + virtual void setScreenTimeout(uint16_t timeout) {} + + virtual bool hasButton(void) const { return false; } + virtual bool hasTouch(void) const { return false; } + virtual bool light(void) { return false; } + + virtual ~DisplayDeviceDriver(){}; + + protected: + const uint16_t screenWidth; + const uint16_t screenHeight; + + private: + DisplayDeviceDriver(const DisplayDeviceDriver &) = delete; + DisplayDeviceDriver &operator=(const DisplayDeviceDriver &) = delete; +}; \ No newline at end of file diff --git a/include/graphics/driver/DisplayDriverConfig.h b/include/graphics/driver/DisplayDriverConfig.h index bfc6fc76..7a59bc93 100644 --- a/include/graphics/driver/DisplayDriverConfig.h +++ b/include/graphics/driver/DisplayDriverConfig.h @@ -15,6 +15,8 @@ class DisplayDriver; class DisplayDriverConfig { public: + enum struct driver_t { LGFX, LVGL, ADAFRUIT, OTHER }; + enum struct device_t { NONE, CUSTOM_TFT, @@ -40,6 +42,7 @@ class DisplayDriverConfig struct panel_config_t { char *type = nullptr; + driver_t driver = driver_t::LGFX; uint16_t panel_width = 0; uint16_t panel_height = 0; bool rotation = false; @@ -166,6 +169,8 @@ class DisplayDriverConfig private: friend class DisplayDriverFactory; friend class LGFXConfig; + friend class LVGLConfig; + friend class LVGL_ST7789; enum device_t _device; panel_config_t _panel; diff --git a/include/graphics/driver/EINKDriver.h b/include/graphics/driver/EINKDriver.h new file mode 100644 index 00000000..b820a2de --- /dev/null +++ b/include/graphics/driver/EINKDriver.h @@ -0,0 +1,23 @@ +#pragma once + +#include "graphics/driver/DisplayDriver.h" + +template class EINKDriver : public DisplayDriver +{ + public: + EINKDriver(EINK *eink, uint16_t width, uint16_t height); + void init(DeviceGUI *gui) override; + + protected: + EINK *einkDriver; +}; + +template +EINKDriver::EINKDriver(EINK *eink, uint16_t width, uint16_t height) : DisplayDriver(width, height), einkDriver(eink) +{ +} + +template void EINKDriver::init(DeviceGUI *gui) +{ + DisplayDriver::init(gui); +} diff --git a/include/graphics/driver/LVGL_ST7789.h b/include/graphics/driver/LVGL_ST7789.h new file mode 100644 index 00000000..039910a0 --- /dev/null +++ b/include/graphics/driver/LVGL_ST7789.h @@ -0,0 +1,37 @@ +#pragma once + +#ifdef LVGL_DRIVER + +#include "graphics/LVGL/LVGLDriver.h" +#include "graphics/driver/DisplayDeviceDriver.h" +#include "stdint.h" + +class LVGLSpiBusDriver; + +class LVGL_ST7789 : public DisplayDeviceDriver +{ + public: + LVGL_ST7789(uint16_t width, uint16_t height); + LVGL_ST7789(const DisplayDriverConfig &cfg); + + void init(void) override; + uint8_t getBrightness(void) const override { return 128; } + void setBrightness(uint8_t setBrightness) override {} + + lv_display_t *createDisplay(uint32_t hor_res, uint32_t ver_res) override; + void touchpad_read(lv_indev_t *indev_driver, lv_indev_data_t *data); + + LVGLSpiBusDriver *getBusDriver(void) const { return driver; } + + virtual ~LVGL_ST7789(); + + protected: + int16_t bl; + LVGLSpiBusDriver *driver; + + private: + LVGL_ST7789(const LVGL_ST7789 &) = delete; + LVGL_ST7789 &operator=(const LVGL_ST7789 &) = delete; +}; + +#endif diff --git a/include/graphics/driver/TFTDriver.h b/include/graphics/driver/TFTDriver.h index ab26ffa8..c96350ef 100644 --- a/include/graphics/driver/TFTDriver.h +++ b/include/graphics/driver/TFTDriver.h @@ -2,6 +2,10 @@ #include "graphics/driver/DisplayDriver.h" +#ifdef ARCH_PORTDUINO +#include "Common.h" // millis() +#endif + template class TFTDriver : public DisplayDriver { public: diff --git a/include/lv_conf.h b/include/lv_conf.h index 36fb5f18..2e902a39 100644 --- a/include/lv_conf.h +++ b/include/lv_conf.h @@ -1097,10 +1097,18 @@ #endif /*Drivers for LCD devices connected via SPI/parallel port*/ +#ifndef LV_USE_ST7735 #define LV_USE_ST7735 0 +#endif +#ifndef LV_USE_ST7789 #define LV_USE_ST7789 0 +#endif +#ifndef LV_USE_ST7796 #define LV_USE_ST7796 0 +#endif +#ifndef LV_USE_ILI9341 #define LV_USE_ILI9341 0 +#endif #define LV_USE_GENERIC_MIPI (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341) diff --git a/locale/nl.yml b/locale/nl.yml index 3ad3ffd3..d026603a 100644 --- a/locale/nl.yml +++ b/locale/nl.yml @@ -30,7 +30,7 @@ nl: Settings & Tools: Instellingen en Tools Settings (advanced): Instellingen (geavanceerd) Locations Map: Lokaties Kaart - Locations Map (%d/%d): Lokaties Kaart (%d/%d) + 'Locations Map (%d/%d)': 'Lokaties Kaart (%d/%d)' no chats: geen chats Node Search: Node zoeken Packet Statistics: Packet Statistieken diff --git a/source/graphics/LVGL/LVGLConfig.cpp b/source/graphics/LVGL/LVGLConfig.cpp new file mode 100644 index 00000000..bcfc199c --- /dev/null +++ b/source/graphics/LVGL/LVGLConfig.cpp @@ -0,0 +1,87 @@ +#ifdef LVGL_DRIVER + +#include "graphics/LVGL/LVGLConfig.h" +#if defined(LV_USE_ST7789) +#include "graphics/driver/LVGL_ST7789.h" +#endif + +#include "util/ILog.h" +#include + +LVGLConfig::LVGLConfig(void) : DisplayDeviceDriver(0, 0), config(nullptr) +{ + ILOG_ERROR("ctor called without config"); + assert(0); +} + +LVGLConfig::LVGLConfig(const DisplayDriverConfig &cfg) : DisplayDeviceDriver(cfg.width(), cfg.height()), + screenWidth(cfg.width()), screenHeight(cfg.height()), config(new DisplayDriverConfig(cfg)), lvglDeviceDriver(nullptr) +{ + ILOG_DEBUG("LVGLConfig ..."); +} + +void LVGLConfig::init() +{ + ILOG_DEBUG("LVGLConfig::init() ..."); + if (strcasecmp(config->_panel.type, "ST7789") == 0) { +#if defined(LV_USE_ST7789) + lvglDeviceDriver = new LVGL_ST7789(*config); + lvglDeviceDriver->init(); + lv_display_t *display = lvglDeviceDriver->createDisplay(screenWidth, screenHeight); +#else + ILOG_CRIT("LVGL device panel support not configured: '%s'", config->_panel.type); +#endif + } else { + ILOG_CRIT("LVGL device panel support not yet implemented for '%s'", config->_panel.type); + return; + } + + // TODO: allocate touch driver + if (config->_touch.type && strcasecmp(config->_touch.type, "NOTOUCH") != 0) { + ILOG_ERROR("LVGLConfig: touch not yet implemented!"); + } +} + +lv_display_t *LVGLConfig::createDisplay(uint32_t hor_res, uint32_t ver_res) +{ + ILOG_DEBUG("LVGLConfig::createDisplay() ..."); + return lvglDeviceDriver->createDisplay(hor_res, ver_res); +} + +void LVGLConfig::touchpad_read(lv_indev_t *indev_driver, lv_indev_data_t *data) +{ + ILOG_DEBUG("LVGLConfig::touchpad_read()"); +} + +bool LVGLConfig::hasButton(void) +{ + return false; // TODO +} + +bool LVGLConfig::hasTouch(void) +{ + return true; // TODO +} + +bool LVGLConfig::light(void) +{ + return false; // TODO +} + +uint8_t LVGLConfig::getBrightness(void) +{ + return 128; // TODO +} + +void LVGLConfig::setBrightness(uint8_t setBrightness) +{ + // TODO +} + +LVGLConfig::~LVGLConfig() +{ + delete lvglDeviceDriver; + delete config; +} + +#endif \ No newline at end of file diff --git a/source/graphics/LVGL/LVGLGraphics.cpp b/source/graphics/LVGL/LVGLGraphics.cpp index a037d594..d399fde0 100644 --- a/source/graphics/LVGL/LVGLGraphics.cpp +++ b/source/graphics/LVGL/LVGLGraphics.cpp @@ -11,6 +11,9 @@ void LVGLGraphics::init(void) lv_log_register_print_cb(lv_debug); #endif lv_init(); + lv_delay_set_cb([](uint32_t ms) { + delay(ms); + }); #if LV_USE_LOG lv_log_register_print_cb(lv_debug); #endif diff --git a/source/graphics/LVGL/LVGLSpiBusDriver.cpp b/source/graphics/LVGL/LVGLSpiBusDriver.cpp new file mode 100644 index 00000000..686653b2 --- /dev/null +++ b/source/graphics/LVGL/LVGLSpiBusDriver.cpp @@ -0,0 +1,135 @@ +#if defined(LV_USE_ST7789) && LV_USE_ST7789 == 1 + +#include "graphics/LVGL/LVGLSpiBusDriver.h" +#include "util/ILog.h" + +SPIClass *LVGLSpiBusDriver::spi = nullptr; +LVGLSpiBusDriver *LVGLSpiBusDriver::instance = nullptr; + +LVGLSpiBusDriver::LVGLSpiBusDriver(uint16_t width, uint16_t height, CreateCB createDisplayFunc) + : /* DisplayDeviceDriver(width, height), */ + rst(-1), dc(-1), cs(-1), sclk(-1), mosi(-1), miso(-1), spiBus(-1), lv_createDisplay(createDisplayFunc) +{ + ILOG_DEBUG("LVGLSpiBusDriver()..."); + instance = this; +} + +void LVGLSpiBusDriver::init(int16_t spiBus, int16_t sclk, int16_t mosi, int16_t miso, int16_t dc, int16_t rst, int16_t cs) +{ + ILOG_DEBUG("LVGLSpiBusDriver::init(): spiBus=%d, sclk=%d, mosi=%d, miso=%d, dc=%d, rst=%d, cs=%d", spiBus, sclk, mosi, miso, + dc, rst, cs); + this->spiBus = spiBus; + this->sclk = sclk; + this->mosi = mosi; + this->miso = miso; + this->dc = dc; + this->rst = rst; + this->cs = cs; + this->spi = new SPIClass(spiBus); + this->spiSettings = SPISettings(40000000, MSBFIRST, SPI_MODE0); + this->connect(); +} + +void LVGLSpiBusDriver::connect(void) +{ + ILOG_DEBUG("LVGLSpiBusDriver::connect()..."); + if (spi) { +#ifdef ARCH_ESP32 + spi->begin(sclk, miso, mosi, cs); + spi->setClockDivider(SPI_CLOCK_DIV2); +#else + spi->begin(40000000); +#endif + } + // Set pin modes + pinMode(dc, OUTPUT); + pinMode(rst, OUTPUT); + + if (cs != -1) { + pinMode(cs, OUTPUT); + digitalWrite(cs, HIGH); + } + + // Reset the display + if (rst != -1) { + digitalWrite(rst, HIGH); + delay(1); + digitalWrite(rst, LOW); + delay(10); + digitalWrite(rst, HIGH); + } +} + +/** + * Create an LCD display + * @param hor_res horizontal resolution + * @param ver_res vertical resolution + * @param flags default configuration settings (mirror, RGB ordering, etc.) + * @param send_cmd platform-dependent function to send a command to the LCD controller (usually uses polling transfer) + * @param send_color platform-dependent function to send pixel data to the LCD controller (usually uses DMA transfer: must + * implement a 'ready' callback) + * @return pointer to the created display + */ +lv_display_t *LVGLSpiBusDriver::createDisplay(uint32_t hor_res, uint32_t ver_res) +{ + ILOG_DEBUG("lv_st7789_create..."); + if (spi) { + lv_display_t *display = lv_createDisplay(hor_res, ver_res, LV_LCD_FLAG_NONE, lcd_send_cmd_cb, lcd_send_color_cb); + lv_display_set_color_format(display, LV_COLOR_FORMAT_RGB565); + return display; + } else { + ILOG_ERROR("SPI not initialized!"); + return nullptr; + } +} + +/** + * Send short command to the LCD. + * This function shall wait until the transaction finishes. + * + */ +void LVGLSpiBusDriver::lcd_send_cmd_cb(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, const uint8_t *param, + size_t param_size) +{ + ILOG_DEBUG("LVGLSpiBusDriver::lcd_send_cmd_cb() %d/%d bytes...", cmd_size, param_size); + + digitalWrite(instance->dc, LOW); // Command mode + if (instance->cs != -1) { + digitalWrite(instance->cs, LOW); + } + instance->spi->beginTransaction(instance->spiSettings); + instance->spi->transfer((void*)cmd, cmd_size); + + digitalWrite(instance->dc, HIGH); // Data mode + if (param && param_size > 0) { + instance->spi->transfer((void*)param, param_size); + } + instance->spi->endTransaction(); + if (instance->cs != -1) { + digitalWrite(instance->cs, HIGH); + } +} + +/** + * Send large array of pixel data to the LCD. If necessary, this function has to do the byte-swapping. + * This function can do the transfer in the background. + * + */ +void LVGLSpiBusDriver::lcd_send_color_cb(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, uint8_t *param, + size_t param_size) +{ + ILOG_DEBUG("LVGLSpiBusDriver::lcd_send_color_cb() %d bytes...", param_size); + + digitalWrite(instance->dc, HIGH); // Data mode + if (instance->cs != -1) { + digitalWrite(instance->cs, LOW); + } + instance->spi->beginTransaction(instance->spiSettings); + instance->spi->transfer((void*)param, param_size); + instance->spi->endTransaction(); + if (instance->cs != -1) { + digitalWrite(instance->cs, HIGH); + } +} + +#endif diff --git a/source/graphics/LVGL/LVGL_T_DECK.cpp b/source/graphics/LVGL/LVGL_T_DECK.cpp new file mode 100644 index 00000000..9b73c36f --- /dev/null +++ b/source/graphics/LVGL/LVGL_T_DECK.cpp @@ -0,0 +1,28 @@ +#if defined(T_DECK) && defined(LVGL_DRIVER) + +#include "graphics/LVGL/LVGL_T_DECK.h" +#include "drivers/display/st7789/lv_st7789.h" +#include "graphics/LVGL/LVGLSpiBusDriver.h" +#include "hal/spi_types.h" + +LVGL_TDECK::LVGL_TDECK(void) : LVGL_ST7789(c_screenWidth, c_screenHeight) +{ + ILOG_DEBUG("LVGL_TDECK::LVGL_TDECK()..."); + bl = 42; +} + +LVGL_TDECK::LVGL_TDECK(const DisplayDriverConfig &cfg) : LVGL_ST7789(cfg) {} + +void LVGL_TDECK::init(void) +{ + ILOG_DEBUG("LVGL_TDECK::init..."); + LVGL_ST7789::init(); + driver->init(SPI2_HOST, 40, 41, 38, 11, -1, 12); +} + +void LVGL_TDECK::touchpad_read(lv_indev_t *indev_driver, lv_indev_data_t *data) +{ + ILOG_DEBUG("LVGL_TDECK::touchpad_read()"); +} + +#endif \ No newline at end of file diff --git a/source/graphics/driver/DisplayDriverFactory.cpp b/source/graphics/driver/DisplayDriverFactory.cpp index fa405018..373ce18e 100644 --- a/source/graphics/driver/DisplayDriverFactory.cpp +++ b/source/graphics/driver/DisplayDriverFactory.cpp @@ -1,14 +1,21 @@ #include "graphics/driver/DisplayDriverFactory.h" -#include "graphics/LGFX/LGFXConfig.h" -#include + #if defined(LGFX_DRIVER) || defined(ARCH_PORTDUINO) -#include "graphics/driver/LGFXDriver.h" +#include "graphics/LGFX/LGFXConfig.h" +#include "graphics/LGFX/LGFXDriver.h" +#endif + +#if defined(LVGL_DRIVER) || defined(ARCH_PORTDUINO) +#include "graphics/LVGL/LVGLConfig.h" +#include "graphics/LVGL/LVGLDriver.h" #endif + #if defined(OLED_DRIVER) || defined(ARCH_PORTDUINO) #include "graphics/driver/OLEDDriver.h" #endif + #if defined(EINK_DRIVER) || defined(ARCH_PORTDUINO) -// TODO #include "graphics/driver/EINKDriver.h" +#include "graphics/driver/EINKDriver.h" #endif #if defined(USE_FRAMEBUFFER) #include "graphics/driver/FBDriver.h" @@ -17,59 +24,22 @@ #include "graphics/driver/X11Driver.h" #endif -#ifndef ARCH_PORTDUINO -#ifdef LGFX_DRIVER_TEMPLATE -#include "graphics/LGFX/LGFX_GENERIC.h" -#endif -#ifdef T_HMI -#include "graphics/LGFX/LGFX_T_HMI.h" -#endif -#ifdef SENSECAP_INDICATOR -#include "graphics/LGFX/LGFX_INDICATOR.h" -#endif -#ifdef ESP_4848S040 -#include "graphics/LGFX/LGFX_4848S040.h" -#endif -#ifdef MAKERFABS_480X480 -#include "graphics/LGFX/LGFX_MAKERFABS480X480.h" -#endif -#ifdef T_DECK -#include "graphics/LGFX/LGFX_T_DECK.h" -#endif -#ifdef PICOMPUTER_S3 -#include "graphics/LGFX/LGFX_PICOMPUTER_S3.h" -#endif -#ifdef T_WATCH_S3 -#include "graphics/LGFX/LGFX_T_WATCH_S3.h" -#endif -#ifdef UNPHONE -#include "graphics/LGFX/LGFX_UNPHONE.h" -#endif -#ifdef ELECROW_PANEL -#include "graphics/LGFX/LGFX_ELECROW70.h" -#endif -#ifdef ESP32_2432S022 -#include "graphics/LGFX/LGFX_ESP2432S022.h" -#endif -#ifdef ESP32_2432S028RV1 -#include "graphics/LGFX/LGFX_ESP2432S028RV1.h" -#endif -#ifdef ESP32_2432S028RV2 -#include "graphics/LGFX/LGFX_ESP2432S028RV2.h" -#endif -#ifdef WT32_SC01 -#include "graphics/LGFX/LGFX_WT_SC01PLUS.h" -#endif -#ifdef HELTEC_TRACKER -#include "graphics/LGFX/LGFX_HELTEC_TRACKER.h" -#endif -#ifdef NODEMCU_32S -#include "graphics/LGFX/LGFX_ESPILI9341XPT2046.h" +#if defined(USE_SDL) +#include "graphics/driver/SDLDriver.h" #endif + +#if defined(GFX_DRIVER_INC) +#include GFX_DRIVER_INC #endif +#include +#include "util/ILog.h" + DisplayDriverFactory::DisplayDriverFactory() {} +/** + * ctor for compiled-in display driver + */ DisplayDriver *DisplayDriverFactory::create(uint16_t width, uint16_t height) { #if defined(USE_FRAMEBUFFER) @@ -77,30 +47,43 @@ DisplayDriver *DisplayDriverFactory::create(uint16_t width, uint16_t height) #endif #if defined(USE_X11) return &X11Driver::create(width, height); +#elif defined(USE_SDL) + return &SDLDriver::create(width, height); #elif defined(LGFX_DRIVER) return new LGFXDriver(width, height); +#elif defined(LVGL_DRIVER) + return new LVGLDriver(width, height); #endif ILOG_CRIT("DisplayDriverFactory: missing or wrong configuration"); assert(false); return nullptr; } +/** + * ctor for run-time configurable display driver + */ DisplayDriver *DisplayDriverFactory::create(const DisplayDriverConfig &cfg) { -#if defined(LGFXConfig) || defined(ARCH_PORTDUINO) +#if defined(ARCH_PORTDUINO) if (cfg._device == DisplayDriverConfig::device_t::CUSTOM_TFT) { - // for now assume LGFX driver, but could be also TFT_eSPI if implemented +#if defined(LVGL_DRIVER) + if (strcasecmp(cfg._panel.type, "ST7789") == 0) { + // TODO: add all ported LVGL drivers, e.g. ST7735, ST7796, ILI9341 + return new LVGLDriver(cfg); + } +#elif defined(LVGL_DRIVER) return new LGFXDriver(cfg); +#endif } #endif -#if defined(OLEDConfig) || defined(ARCH_PORTDUINO) +#if defined(OLED_DRIVER) || defined(ARCH_PORTDUINO) if (cfg._device == DisplayDriverConfig::device_t::CUSTOM_OLED) { - // TODO return new OLEDDriver(cfg); + //return new OLEDDriver(cfg); } #endif -#if defined(EINKConfig) || defined(ARCH_PORTDUINO) +#if defined(EINK_DRIVER) || defined(ARCH_PORTDUINO) if (cfg._device == DisplayDriverConfig::device_t::CUSTOM_EINK) { - // TODO return new EINKDriver(cfg); + //return new EINKDriver(cfg); } #endif #if defined(USE_FRAMEBUFFER) @@ -113,77 +96,25 @@ DisplayDriver *DisplayDriverFactory::create(const DisplayDriverConfig &cfg) return &X11Driver::create(cfg.width(), cfg.height()); } #endif - switch (cfg._device) { -#ifndef ARCH_PORTDUINO -#if !defined(LGFX_DRIVER) -#error "LGFX_DRIVER must be defined!" -#endif -#if defined(T_HMI) - case DisplayDriverConfig::device_t::THMI: - return new LGFXDriver(cfg.width(), cfg.height()); - break; -#elif defined(T_DECK) - case DisplayDriverConfig::device_t::TDECK: - return new LGFXDriver(cfg.width(), cfg.height()); - break; -#elif defined(SENSECAP_INDICATOR) - case DisplayDriverConfig::device_t::INDICATOR: - return new LGFXDriver(cfg.width(), cfg.height()); - break; -#elif defined(ESP_4848S040) - case DisplayDriverConfig::device_t::ESP4848S040: - return new LGFXDriver(cfg.width(), cfg.height()); - break; -#elif defined(MAKERFABS_480X480) - case DisplayDriverConfig::device_t::MAKERFABS480X480: - return new LGFXDriver(cfg.width(), cfg.height()); - break; -#elif defined(PICOMPUTER_S3) - case DisplayDriverConfig::device_t::BPICOMPUTER_S3: - return new LGFXDriver(cfg.width(), cfg.height()); - break; -#elif defined(TWATCH_S3) - case DisplayDriverConfig::device_t::TWATCH_S3: - return new LGFXDriver(cfg.width(), cfg.height()); - break; -#elif defined(UNPHONE) - case DisplayDriverConfig::device_t::UNPHONE_V9: - return new LGFXDriver(cfg.width(), cfg.height()); - break; -#elif defined(ELECROW_PANEL) - case DisplayDriverConfig::device_t::ELECROW_ADV: - return new LGFXDriver(cfg.width(), cfg.height()); - break; -#elif defined(HELTEC_TRACKER) - case DisplayDriverConfig::device_t::HELTEC_TRACKER: - // return new LGFXDriver(cfg.width(), cfg.height()); - break; -#elif defined(WT_SC01_PLUS) - case DisplayDriverConfig::device_t::WT32_SC01_PLUS: - return new LGFXDriver(cfg.width(), cfg.height()); - break; -#elif defined(ESP2432S028RV1) - case DisplayDriverConfig::device_t::ESP2432S028RV1: - return new LGFXDriver(cfg.width(), cfg.height()); - break; -#elif defined(ESP2432S028RV2) - case DisplayDriverConfig::device_t::ESP2432S028RV2: - return new LGFXDriver(cfg.width(), cfg.height()); - break; -#endif -#elif defined(USE_FRAMEBUFFER) - case DisplayDriverConfig::device_t::FB: +#if defined(USE_FRAMEBUFFER) + if (cfg._device == DisplayDriverConfig::device_t::FB) { return &FBDriver::create(cfg.width(), cfg.height()); - break; -#elif defined(USE_X11) - case DisplayDriverConfig::device_t::X11: - return &X11Driver::create(cfg.width(), cfg.height()); - break; + } #endif - default: - ILOG_CRIT("LGFX device_t config not implemented: %d", cfg._device); - assert(false); - break; +#if defined(USE_SDL) + if (cfg._device == DisplayDriverConfig::device_t::SDL) { + return &SDLDriver::create(cfg.width(), cfg.height()); } +#endif + +#if !defined(ARCH_PORTDUINO) +#if defined(LGFX_DRIVER) + return new LGFXDriver(cfg.width(), cfg.height()); +#elif defined(LVGL_DRIVER) + return new LVGLDriver(cfg.width(), cfg.height()); +#endif +#endif + ILOG_CRIT("DisplayDriverFactory: missing or wrong GFX configuration for device %d", static_cast(cfg._device)); + assert(false); return nullptr; } diff --git a/source/graphics/driver/LVGL_ST7789.cpp b/source/graphics/driver/LVGL_ST7789.cpp new file mode 100644 index 00000000..9becd944 --- /dev/null +++ b/source/graphics/driver/LVGL_ST7789.cpp @@ -0,0 +1,59 @@ +#if defined(LV_USE_ST7789) && defined(LVGL_DRIVER) + +#include "graphics/driver/LVGL_ST7789.h" +#include "drivers/display/st7789/lv_st7789.h" +#include "graphics/LVGL/LVGLSpiBusDriver.h" +//#include "hal/spi_types.h" + +LVGL_ST7789::LVGL_ST7789(uint16_t width, uint16_t height) : DisplayDeviceDriver(width, height), + bl(-1), driver(nullptr) +{ + ILOG_DEBUG("LVGL_ST7789::LVGL_ST7789()..."); + auto st7789Func = [](uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags, lv_lcd_send_cmd_cb_t send_cmd_cb, + lv_lcd_send_color_cb_t send_color_cb) { + return lv_st7789_create(hor_res, ver_res, flags, send_cmd_cb, send_color_cb); + }; + driver = new LVGLSpiBusDriver(screenWidth, screenHeight, st7789Func); +} + +LVGL_ST7789::LVGL_ST7789(const DisplayDriverConfig &cfg) : DisplayDeviceDriver(cfg.width(), cfg.height()), + bl(cfg._light.pin_bl), driver(nullptr) +{ + ILOG_DEBUG("LVGL_ST7789::LVGL_ST7789() with config..."); + static auto st7789Func = [](uint32_t hor_res, uint32_t ver_res, lv_lcd_flag_t flags, lv_lcd_send_cmd_cb_t send_cmd_cb, + lv_lcd_send_color_cb_t send_color_cb) { + return lv_st7789_create(hor_res, ver_res, flags, send_cmd_cb, send_color_cb); + }; + driver = new LVGLSpiBusDriver(cfg.width(), cfg.height(), st7789Func); + driver->init(cfg._bus.spi.spi_host, cfg._bus.spi.pin_sclk, cfg._bus.spi.pin_mosi, cfg._bus.spi.pin_miso, + cfg._bus.spi.pin_dc, cfg._panel.pin_rst, cfg._panel.pin_cs); +} + +void LVGL_ST7789::init(void) +{ + ILOG_DEBUG("LVGL_ST7789::init()..."); + if (bl >= 0) { + pinMode(bl, OUTPUT); + digitalWrite(bl, HIGH); // Turn on backlight + } +} + +void LVGL_ST7789::touchpad_read(lv_indev_t *indev_driver, lv_indev_data_t *data) +{ + ILOG_DEBUG("LVGL_ST7789::touchpad_read()"); +} + +/** + * create lvgl display + */ +lv_display_t *LVGL_ST7789::createDisplay(uint32_t hor_res, uint32_t ver_res) +{ + return driver->createDisplay(hor_res, ver_res); +} + +LVGL_ST7789::~LVGL_ST7789() +{ + if (driver) + delete driver; +} +#endif \ No newline at end of file