Merge develop into main (#391)
## Improvements - Created new base driver classes: `EspLcdDisplayV2' and `EspLcdSpiDisplay` - Updated `St7789Display` to implement `EspLcdSpiDisplay` - Updated all boards with ST7789 display ## Fixes - Ensure that `tmp/` is created on startup (for all writeable filesystems) - Fix for `lv_list` padding on small screen devices - Fix for `PreferencesEsp` not processing result when writing string to NVS ## Other - Remove unused build scripts
This commit is contained in:
committed by
GitHub
parent
37420db000
commit
db6d3b4acb
@@ -0,0 +1,226 @@
|
||||
#include "EspLcdDisplayV2.h"
|
||||
#include "EspLcdDisplayDriver.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <esp_lvgl_port_disp.h>
|
||||
#include <Tactility/Check.h>
|
||||
#include <Tactility/LogEsp.h>
|
||||
#include <Tactility/hal/touch/TouchDevice.h>
|
||||
|
||||
constexpr auto* TAG = "EspLcdDispV2";
|
||||
|
||||
inline unsigned int getBufferSize(const std::shared_ptr<EspLcdConfiguration>& configuration) {
|
||||
if (configuration->bufferSize != DEFAULT_BUFFER_SIZE) {
|
||||
return configuration->bufferSize;
|
||||
} else {
|
||||
return configuration->horizontalResolution * (configuration->verticalResolution / 10);
|
||||
}
|
||||
}
|
||||
|
||||
EspLcdDisplayV2::~EspLcdDisplayV2() {
|
||||
if (displayDriver != nullptr && displayDriver.use_count() > 1) {
|
||||
tt_crash("DisplayDriver is still in use. This will cause memory access violations.");
|
||||
}
|
||||
}
|
||||
|
||||
bool EspLcdDisplayV2::applyConfiguration() const {
|
||||
if (esp_lcd_panel_reset(panelHandle) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to reset panel");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_lcd_panel_init(panelHandle) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to init panel");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_lcd_panel_invert_color(panelHandle, configuration->invertColor) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to set panel to invert");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Warning: it looks like LVGL rotation is broken when "gap" is set and the screen is moved to a non-default orientation
|
||||
int gap_x = configuration->swapXY ? configuration->gapY : configuration->gapX;
|
||||
int gap_y = configuration->swapXY ? configuration->gapX : configuration->gapY;
|
||||
if (esp_lcd_panel_set_gap(panelHandle, gap_x, gap_y) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to set panel gap");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_lcd_panel_swap_xy(panelHandle, configuration->swapXY) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to swap XY ");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_lcd_panel_mirror(panelHandle, configuration->mirrorX, configuration->mirrorY) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to set panel to mirror");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_lcd_panel_invert_color(panelHandle, configuration->invertColor) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to set panel to invert");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (esp_lcd_panel_disp_on_off(panelHandle, true) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to turn display on");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EspLcdDisplayV2::start() {
|
||||
if (!createIoHandle(ioHandle)) {
|
||||
TT_LOG_E(TAG, "Failed to create IO handle");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_lcd_panel_dev_config_t panel_config = createPanelConfig(configuration, configuration->resetPin);
|
||||
|
||||
if (!createPanelHandle(ioHandle, panel_config, panelHandle)) {
|
||||
TT_LOG_E(TAG, "Failed to create panel handle");
|
||||
esp_lcd_panel_io_del(ioHandle);
|
||||
ioHandle = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!applyConfiguration()) {
|
||||
esp_lcd_panel_del(panelHandle);
|
||||
panelHandle = nullptr;
|
||||
esp_lcd_panel_io_del(ioHandle);
|
||||
ioHandle = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EspLcdDisplayV2::stop() {
|
||||
if (lvglDisplay != nullptr) {
|
||||
stopLvgl();
|
||||
lvglDisplay = nullptr;
|
||||
}
|
||||
|
||||
if (panelHandle != nullptr && esp_lcd_panel_del(panelHandle) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ioHandle != nullptr && esp_lcd_panel_io_del(ioHandle) != ESP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (displayDriver != nullptr && displayDriver.use_count() > 1) {
|
||||
TT_LOG_W(TAG, "DisplayDriver is still in use.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EspLcdDisplayV2::startLvgl() {
|
||||
assert(lvglDisplay == nullptr);
|
||||
|
||||
if (displayDriver != nullptr && displayDriver.use_count() > 1) {
|
||||
TT_LOG_W(TAG, "DisplayDriver is still in use.");
|
||||
}
|
||||
|
||||
auto lvgl_port_config = getLvglPortDisplayConfig(configuration, ioHandle, panelHandle);
|
||||
|
||||
if (isRgbPanel()) {
|
||||
auto rgb_config = getLvglPortDisplayRgbConfig(ioHandle, panelHandle);
|
||||
lvglDisplay = lvgl_port_add_disp_rgb(&lvgl_port_config , &rgb_config);
|
||||
} else {
|
||||
lvglDisplay = lvgl_port_add_disp(&lvgl_port_config );
|
||||
}
|
||||
|
||||
auto touch_device = getTouchDevice();
|
||||
if (touch_device != nullptr && touch_device->supportsLvgl()) {
|
||||
touch_device->startLvgl(lvglDisplay);
|
||||
}
|
||||
|
||||
return lvglDisplay != nullptr;
|
||||
}
|
||||
|
||||
bool EspLcdDisplayV2::stopLvgl() {
|
||||
if (lvglDisplay == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto touch_device = getTouchDevice();
|
||||
if (touch_device != nullptr) {
|
||||
touch_device->stopLvgl();
|
||||
}
|
||||
|
||||
lvgl_port_remove_disp(lvglDisplay);
|
||||
lvglDisplay = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
lvgl_port_display_cfg_t EspLcdDisplayV2::getLvglPortDisplayConfig(std::shared_ptr<EspLcdConfiguration> configuration, esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) {
|
||||
return lvgl_port_display_cfg_t {
|
||||
.io_handle = ioHandle,
|
||||
.panel_handle = panelHandle,
|
||||
.control_handle = nullptr,
|
||||
.buffer_size = getBufferSize(configuration),
|
||||
.double_buffer = false,
|
||||
.trans_size = 0,
|
||||
.hres = configuration->horizontalResolution,
|
||||
.vres = configuration->verticalResolution,
|
||||
.monochrome = configuration->monochrome,
|
||||
.rotation = {
|
||||
.swap_xy = configuration->swapXY,
|
||||
.mirror_x = configuration->mirrorX,
|
||||
.mirror_y = configuration->mirrorY,
|
||||
},
|
||||
.color_format = configuration->lvglColorFormat,
|
||||
.flags = {
|
||||
.buff_dma = 1,
|
||||
.buff_spiram = 0,
|
||||
.sw_rotate = 0,
|
||||
.swap_bytes = configuration->lvglSwapBytes,
|
||||
.full_refresh = 0,
|
||||
.direct_mode = 0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
std::shared_ptr<tt::hal::display::DisplayDriver> EspLcdDisplayV2::getDisplayDriver() {
|
||||
assert(lvglDisplay == nullptr); // Still attached to LVGL context. Call stopLvgl() first.
|
||||
if (displayDriver == nullptr) {
|
||||
auto lvgl_port_config = getLvglPortDisplayConfig(configuration, ioHandle, panelHandle);
|
||||
auto panel_config = createPanelConfig(configuration, GPIO_NUM_NC);
|
||||
|
||||
tt::hal::display::ColorFormat color_format;
|
||||
if (lvgl_port_config.color_format == LV_COLOR_FORMAT_I1) {
|
||||
color_format = tt::hal::display::ColorFormat::Monochrome;
|
||||
} else if (lvgl_port_config.color_format == LV_COLOR_FORMAT_RGB565) {
|
||||
if (panel_config.rgb_ele_order == LCD_RGB_ELEMENT_ORDER_RGB) {
|
||||
if (lvgl_port_config.flags.swap_bytes) {
|
||||
color_format = tt::hal::display::ColorFormat::RGB565Swapped;
|
||||
} else {
|
||||
color_format = tt::hal::display::ColorFormat::RGB565;
|
||||
}
|
||||
} else {
|
||||
if (lvgl_port_config.flags.swap_bytes) {
|
||||
color_format = tt::hal::display::ColorFormat::BGR565Swapped;
|
||||
} else {
|
||||
color_format = tt::hal::display::ColorFormat::BGR565;
|
||||
}
|
||||
}
|
||||
} else if (lvgl_port_config.color_format == LV_COLOR_FORMAT_RGB888) {
|
||||
color_format = tt::hal::display::ColorFormat::RGB888;
|
||||
} else {
|
||||
tt_crash("unsupported driver");
|
||||
}
|
||||
|
||||
displayDriver = std::make_shared<EspLcdDisplayDriver>(
|
||||
panelHandle,
|
||||
lock,
|
||||
lvgl_port_config.hres,
|
||||
lvgl_port_config.vres,
|
||||
color_format
|
||||
);
|
||||
}
|
||||
return displayDriver;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include <esp_lcd_panel_dev.h>
|
||||
#include <Tactility/Check.h>
|
||||
#include <Tactility/Lock.h>
|
||||
#include <Tactility/hal/display/DisplayDevice.h>
|
||||
|
||||
#include <esp_lcd_types.h>
|
||||
#include <esp_lvgl_port_disp.h>
|
||||
|
||||
constexpr auto DEFAULT_BUFFER_SIZE = 0;
|
||||
|
||||
struct EspLcdConfiguration {
|
||||
unsigned int horizontalResolution;
|
||||
unsigned int verticalResolution;
|
||||
int gapX;
|
||||
int gapY;
|
||||
bool monochrome;
|
||||
bool swapXY;
|
||||
bool mirrorX;
|
||||
bool mirrorY;
|
||||
bool invertColor;
|
||||
uint32_t bufferSize; // Size in pixel count. 0 means default, which is 1/10 of the screen size
|
||||
std::shared_ptr<tt::hal::touch::TouchDevice> touch;
|
||||
std::function<void(uint8_t)> _Nullable backlightDutyFunction;
|
||||
gpio_num_t resetPin;
|
||||
lv_color_format_t lvglColorFormat;
|
||||
bool lvglSwapBytes;
|
||||
};
|
||||
|
||||
class EspLcdDisplayV2 : public tt::hal::display::DisplayDevice {
|
||||
esp_lcd_panel_io_handle_t _Nullable ioHandle = nullptr;
|
||||
esp_lcd_panel_handle_t _Nullable panelHandle = nullptr;
|
||||
lv_display_t* _Nullable lvglDisplay = nullptr;
|
||||
std::shared_ptr<tt::hal::display::DisplayDriver> _Nullable displayDriver;
|
||||
std::shared_ptr<tt::Lock> lock;
|
||||
std::shared_ptr<EspLcdConfiguration> configuration;
|
||||
|
||||
bool applyConfiguration() const;
|
||||
|
||||
lvgl_port_display_cfg_t getLvglPortDisplayConfig(std::shared_ptr<EspLcdConfiguration> configuration, esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle);
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
virtual bool createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) = 0;
|
||||
|
||||
virtual esp_lcd_panel_dev_config_t createPanelConfig(std::shared_ptr<EspLcdConfiguration> espLcdConfiguration, gpio_num_t resetPin) = 0;
|
||||
|
||||
virtual bool createPanelHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_panel_dev_config_t& panelConfig, esp_lcd_panel_handle_t& panelHandle) = 0;
|
||||
|
||||
virtual bool isRgbPanel() const { return false; }
|
||||
|
||||
virtual lvgl_port_display_rgb_cfg_t getLvglPortDisplayRgbConfig(esp_lcd_panel_io_handle_t ioHandle, esp_lcd_panel_handle_t panelHandle) { tt_crash("Not supported"); }
|
||||
|
||||
// Used for sending commands such as setting curves
|
||||
esp_lcd_panel_io_handle_t getIoHandle() const { return ioHandle; }
|
||||
|
||||
public:
|
||||
|
||||
EspLcdDisplayV2(const std::shared_ptr<EspLcdConfiguration>& configuration, const std::shared_ptr<tt::Lock>& lock) :
|
||||
lock(lock),
|
||||
configuration(configuration)
|
||||
{
|
||||
assert(configuration != nullptr);
|
||||
assert(lock != nullptr);
|
||||
}
|
||||
|
||||
~EspLcdDisplayV2() override;
|
||||
|
||||
std::shared_ptr<tt::Lock> getLock() const { return lock; }
|
||||
|
||||
bool start() final;
|
||||
|
||||
bool stop() final;
|
||||
|
||||
// region LVGL
|
||||
|
||||
bool supportsLvgl() const final { return true; }
|
||||
|
||||
bool startLvgl() final;
|
||||
|
||||
bool stopLvgl() final;
|
||||
|
||||
lv_display_t* _Nullable getLvglDisplay() const final { return lvglDisplay; }
|
||||
|
||||
// endregion
|
||||
|
||||
std::shared_ptr<tt::hal::touch::TouchDevice> _Nullable getTouchDevice() override { return configuration->touch; }
|
||||
|
||||
// region Backlight
|
||||
|
||||
void setBacklightDuty(uint8_t backlightDuty) override {
|
||||
if (configuration->backlightDutyFunction != nullptr) {
|
||||
configuration->backlightDutyFunction(backlightDuty);
|
||||
}
|
||||
}
|
||||
|
||||
bool supportsBacklightDuty() const override { return configuration->backlightDutyFunction != nullptr; }
|
||||
|
||||
// endregion
|
||||
|
||||
// region DisplayDriver
|
||||
|
||||
bool supportsDisplayDriver() const override { return true; }
|
||||
|
||||
/** @return a NativeDisplay instance if this device supports it */
|
||||
std::shared_ptr<tt::hal::display::DisplayDriver> _Nullable getDisplayDriver() final;
|
||||
|
||||
// endregion
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
#include "EspLcdSpiDisplay.h"
|
||||
|
||||
#include <esp_lcd_panel_commands.h>
|
||||
#include <Tactility/LogEsp.h>
|
||||
|
||||
constexpr auto* TAG = "EspLcdSpiDsp";
|
||||
|
||||
bool EspLcdSpiDisplay::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) {
|
||||
TT_LOG_I(TAG, "createIoHandle");
|
||||
|
||||
const esp_lcd_panel_io_spi_config_t panel_io_config = {
|
||||
.cs_gpio_num = spiConfiguration->csPin,
|
||||
.dc_gpio_num = spiConfiguration->dcPin,
|
||||
.spi_mode = 0,
|
||||
.pclk_hz = spiConfiguration->pixelClockFrequency,
|
||||
.trans_queue_depth = spiConfiguration->transactionQueueDepth,
|
||||
.on_color_trans_done = nullptr,
|
||||
.user_ctx = nullptr,
|
||||
.lcd_cmd_bits = 8,
|
||||
.lcd_param_bits = 8,
|
||||
.cs_ena_pretrans = 0,
|
||||
.cs_ena_posttrans = 0,
|
||||
.flags = {
|
||||
.dc_high_on_cmd = 0,
|
||||
.dc_low_on_data = 0,
|
||||
.dc_low_on_param = 0,
|
||||
.octal_mode = 0,
|
||||
.quad_mode = 0,
|
||||
.sio_mode = 1,
|
||||
.lsb_first = 0,
|
||||
.cs_high_active = 0
|
||||
}
|
||||
};
|
||||
|
||||
if (esp_lcd_new_panel_io_spi(spiConfiguration->spiHostDevice, &panel_io_config, &outHandle) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to create panel");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EspLcdSpiDisplay::setGammaCurve(uint8_t index) {
|
||||
uint8_t gamma_curve;
|
||||
switch (index) {
|
||||
case 0:
|
||||
gamma_curve = 0x01;
|
||||
break;
|
||||
case 1:
|
||||
gamma_curve = 0x04;
|
||||
break;
|
||||
case 2:
|
||||
gamma_curve = 0x02;
|
||||
break;
|
||||
case 3:
|
||||
gamma_curve = 0x08;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
const uint8_t param[] = {
|
||||
gamma_curve
|
||||
};
|
||||
|
||||
auto io_handle = getIoHandle();
|
||||
assert(io_handle != nullptr);
|
||||
if (esp_lcd_panel_io_tx_param(io_handle, LCD_CMD_GAMSET, param, 1) != ESP_OK) {
|
||||
TT_LOG_E(TAG, "Failed to set gamma");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "EspLcdDisplayV2.h"
|
||||
#include <Tactility/hal/spi/Spi.h>
|
||||
|
||||
#include <esp_lcd_io_spi.h>
|
||||
#include <esp_lcd_types.h>
|
||||
|
||||
/**
|
||||
* Adds IO implementations on top of EspLcdDisplayV2
|
||||
* @warning This is an abstract class. You need to extend it to use it.
|
||||
*/
|
||||
class EspLcdSpiDisplay : public EspLcdDisplayV2 {
|
||||
|
||||
public:
|
||||
|
||||
struct SpiConfiguration {
|
||||
spi_host_device_t spiHostDevice;
|
||||
gpio_num_t csPin;
|
||||
gpio_num_t dcPin;
|
||||
unsigned int pixelClockFrequency = 80'000'000; // Hertz
|
||||
size_t transactionQueueDepth = 10;
|
||||
};
|
||||
|
||||
explicit EspLcdSpiDisplay(const std::shared_ptr<EspLcdConfiguration>& configuration, const std::shared_ptr<SpiConfiguration> spiConfiguration, int gammaCurveCount) :
|
||||
EspLcdDisplayV2(configuration, tt::hal::spi::getLock(spiConfiguration->spiHostDevice)),
|
||||
spiConfiguration(spiConfiguration),
|
||||
gammaCurveCount(gammaCurveCount)
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<SpiConfiguration> spiConfiguration;
|
||||
int gammaCurveCount;
|
||||
|
||||
protected:
|
||||
|
||||
bool createIoHandle(esp_lcd_panel_io_handle_t& ioHandle) override;
|
||||
|
||||
// region Gamma
|
||||
|
||||
void setGammaCurve(uint8_t index) override;
|
||||
|
||||
uint8_t getGammaCurveCount() const override { return gammaCurveCount; }
|
||||
|
||||
// endregion
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user