diff --git a/components/byte90/include/byte90.hpp b/components/byte90/include/byte90.hpp index 3587f556a..7fb881c7c 100644 --- a/components/byte90/include/byte90.hpp +++ b/components/byte90/include/byte90.hpp @@ -170,6 +170,10 @@ class Byte90 : public BaseComponent { /// \return A shared pointer to the display std::shared_ptr> display() const; + /// Get a shared pointer to the low-level display driver + /// \return A shared pointer to the display driver + const std::shared_ptr &display_driver() const { return display_driver_; } + /// Set the brightness of the backlight /// \param brightness The brightness of the backlight as a percentage (0 - 100) void brightness(float brightness); @@ -215,6 +219,17 @@ class Byte90 : public BaseComponent { void write_lcd_frame(const uint16_t x, const uint16_t y, const uint16_t width, const uint16_t height, uint8_t *data); + /// Write lines to the LCD + /// \param xs The x start coordinate + /// \param ys The y start coordinate + /// \param xe The x end coordinate + /// \param ye The y end coordinate + /// \param data The data to write + /// \param user_data User data to pass to the SPI transaction callback + /// \note This method queues the panel transfer asynchronously and may return + /// before the write has completed. + void write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data); + protected: Byte90(); @@ -330,7 +345,7 @@ class Byte90 : public BaseComponent { // display std::shared_ptr> display_; - std::unique_ptr display_driver_; + std::shared_ptr display_driver_{static_cast(nullptr)}; static constexpr int spi_queue_size = 6; std::unique_ptr lcd_spi_; std::unique_ptr lcd_; diff --git a/components/byte90/src/display.cpp b/components/byte90/src/display.cpp index 54d256d12..dda6f3b9b 100644 --- a/components/byte90/src/display.cpp +++ b/components/byte90/src/display.cpp @@ -1,5 +1,7 @@ #include "byte90.hpp" +#include + using namespace espp; //////////////////////// @@ -56,7 +58,7 @@ bool Byte90::initialize_lcd() { return false; } - display_driver_ = std::make_unique( + display_driver_ = std::make_shared( espp::display_drivers::Config{.panel_io = lcd_.get(), .write_command = nullptr, .read_command = nullptr, @@ -153,6 +155,39 @@ void Byte90::write_lcd_frame(const uint16_t xs, const uint16_t ys, const uint16_ } } +void Byte90::write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, + uint32_t user_data) { + if (!lcd_) { + return; + } + if (data == nullptr) { + logger_.error("lcd_send_lines: Null data for ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + if (xs < 0 || ys < 0 || xe < xs || ye < ys) { + logger_.error("lcd_send_lines: Bad region: ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + size_t width = static_cast(xe - xs + 1); + size_t height = static_cast(ye - ys + 1); + size_t length = width * height * lcd_bytes_per_pixel; + lcd_->wait(); + std::array window = { + static_cast(xs & 0xff), + static_cast(xe & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::caset)); + lcd_->queue_data(window); + window = { + static_cast(ys & 0xff), + static_cast(ye & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::raset)); + lcd_->queue_data(window); + lcd_->queue_command(static_cast(DisplayDriver::Command::ramwr)); + lcd_->queue_pixels(data, length, user_data); +} + Byte90::Pixel *Byte90::vram0() const { if (!display_) { return nullptr; diff --git a/components/esp-box/include/esp-box.hpp b/components/esp-box/include/esp-box.hpp index bacdf9774..2a8ac26d4 100644 --- a/components/esp-box/include/esp-box.hpp +++ b/components/esp-box/include/esp-box.hpp @@ -187,6 +187,10 @@ class EspBox : public BaseComponent { /// \return A shared pointer to the display std::shared_ptr> display() const { return display_; } + /// Get a shared pointer to the low-level display driver + /// \return A shared pointer to the display driver + const std::shared_ptr &display_driver() const { return display_driver_; } + /// Set the brightness of the backlight /// \param brightness The brightness of the backlight as a percentage (0 - 100) /// \note This function will only work after initialize_lcd() has been called @@ -234,6 +238,17 @@ class EspBox : public BaseComponent { void write_lcd_frame(const uint16_t x, const uint16_t y, const uint16_t width, const uint16_t height, uint8_t *data); + /// Write lines to the LCD + /// \param xs The x start coordinate + /// \param ys The y start coordinate + /// \param xe The x end coordinate + /// \param ye The y end coordinate + /// \param data The data to write + /// \param user_data User data to pass to the SPI transaction callback + /// \note This method queues the panel transfer asynchronously and may return + /// before the write has completed. + void write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data); + ///////////////////////////////////////////////////////////////////////////// // Button ///////////////////////////////////////////////////////////////////////////// @@ -502,7 +517,7 @@ class EspBox : public BaseComponent { std::vector backlight_channel_configs_; std::shared_ptr backlight_; std::shared_ptr> display_; - std::unique_ptr display_driver_; + std::shared_ptr display_driver_{static_cast(nullptr)}; std::unique_ptr lcd_spi_; std::unique_ptr lcd_; uint8_t *frame_buffer0_{nullptr}; diff --git a/components/esp-box/src/video.cpp b/components/esp-box/src/video.cpp index 4a2a3ece2..9574b4f96 100644 --- a/components/esp-box/src/video.cpp +++ b/components/esp-box/src/video.cpp @@ -1,5 +1,7 @@ #include "esp-box.hpp" +#include + using namespace espp; //////////////////////// @@ -65,7 +67,7 @@ bool EspBox::initialize_lcd() { lcd_spi_.reset(); return false; } - display_driver_ = std::make_unique( + display_driver_ = std::make_shared( espp::display_drivers::Config{.panel_io = lcd_.get(), .write_command = nullptr, .read_command = nullptr, @@ -167,6 +169,43 @@ void EspBox::write_lcd_frame(const uint16_t xs, const uint16_t ys, const uint16_ } } +void EspBox::write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, + uint32_t user_data) { + if (!lcd_) { + return; + } + if (data == nullptr) { + logger_.error("lcd_send_lines: Null data for ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + if (xs < 0 || ys < 0 || xe < xs || ye < ys) { + logger_.error("lcd_send_lines: Bad region: ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + size_t width = static_cast(xe - xs + 1); + size_t height = static_cast(ye - ys + 1); + size_t length = width * height * lcd_bytes_per_pixel; + lcd_->wait(); + std::array window = { + static_cast((xs >> 8) & 0xff), + static_cast(xs & 0xff), + static_cast((xe >> 8) & 0xff), + static_cast(xe & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::caset)); + lcd_->queue_data(window); + window = { + static_cast((ys >> 8) & 0xff), + static_cast(ys & 0xff), + static_cast((ye >> 8) & 0xff), + static_cast(ye & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::raset)); + lcd_->queue_data(window); + lcd_->queue_command(static_cast(DisplayDriver::Command::ramwr)); + lcd_->queue_pixels(data, length, user_data); +} + EspBox::Pixel *EspBox::vram0() const { if (!display_) { return nullptr; diff --git a/components/m5stack-tab5/include/m5stack-tab5.hpp b/components/m5stack-tab5/include/m5stack-tab5.hpp index 0102d3c5f..6705e8bc3 100644 --- a/components/m5stack-tab5/include/m5stack-tab5.hpp +++ b/components/m5stack-tab5/include/m5stack-tab5.hpp @@ -76,6 +76,9 @@ class M5StackTab5 : public BaseComponent { /// Alias for the pixel type used by the Tab5 display using Pixel = lv_color16_t; + /// Alias for the low-level display driver interface + using DisplayDriver = espp::display_drivers::Controller; + /// Enum for display controller type enum class DisplayController { UNKNOWN, ILI9881, ST7123 }; @@ -240,6 +243,21 @@ class M5StackTab5 : public BaseComponent { /// \return The display height in pixels, according to the current orientation size_t rotated_display_height() const; + /// Get a shared pointer to the low-level display driver + /// \return A shared pointer to the display driver + const std::shared_ptr &display_driver() const { return display_driver_; } + + /// Write lines to the LCD + /// \param xs The x start coordinate + /// \param ys The y start coordinate + /// \param xe The x end coordinate + /// \param ye The y end coordinate + /// \param data The data to write + /// \param user_data User data to pass to the lower-level transport + /// \note This method queues the panel transfer asynchronously and may return + /// before the write has completed. + void write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data); + ///////////////////////////////////////////////////////////////////////////// // Audio System ///////////////////////////////////////////////////////////////////////////// @@ -736,7 +754,7 @@ class M5StackTab5 : public BaseComponent { // Display state std::shared_ptr> display_; - std::unique_ptr display_driver_; + std::shared_ptr display_driver_{static_cast(nullptr)}; std::shared_ptr backlight_; std::vector backlight_channel_configs_; struct LcdHandles { diff --git a/components/m5stack-tab5/src/video.cpp b/components/m5stack-tab5/src/video.cpp index 47393be5b..258a59f1a 100644 --- a/components/m5stack-tab5/src/video.cpp +++ b/components/m5stack-tab5/src/video.cpp @@ -221,7 +221,7 @@ bool M5StackTab5::initialize_lcd() { display_driver_.reset(); if (detected_controller == DisplayController::ILI9881) { logger_.info("Initializing as ILI9881"); - auto display_driver = std::make_unique(display_config); + auto display_driver = std::make_shared(display_config); if (display_driver->initialize()) { logger_.info("Successfully initialized ILI9881 display controller"); display_driver_ = std::move(display_driver); @@ -229,7 +229,7 @@ bool M5StackTab5::initialize_lcd() { } } else if (detected_controller == DisplayController::ST7123) { logger_.info("Initializing as ST7123"); - auto display_driver = std::make_unique(display_config); + auto display_driver = std::make_shared(display_config); if (display_driver->initialize()) { logger_.info("Successfully initialized ST7123 display controller"); display_driver_ = std::move(display_driver); @@ -333,6 +333,19 @@ size_t M5StackTab5::rotated_display_height() const { } } +void M5StackTab5::write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, + uint32_t user_data) { + (void)user_data; + if (lcd_handles_.panel == nullptr || data == nullptr) { + return; + } + if (xs < 0 || ys < 0 || xe < xs || ye < ys) { + logger_.error("write_lcd_lines: Bad region: ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + esp_lcd_panel_draw_bitmap(lcd_handles_.panel, xs, ys, xe + 1, ye + 1, data); +} + void M5StackTab5::brightness(float brightness) { brightness = std::clamp(brightness, 0.0f, 100.0f); if (backlight_) { diff --git a/components/matouch-rotary-display/include/matouch-rotary-display.hpp b/components/matouch-rotary-display/include/matouch-rotary-display.hpp index f560d2562..f661bc4a8 100644 --- a/components/matouch-rotary-display/include/matouch-rotary-display.hpp +++ b/components/matouch-rotary-display/include/matouch-rotary-display.hpp @@ -189,6 +189,10 @@ class MatouchRotaryDisplay : public BaseComponent { /// \return A shared pointer to the display std::shared_ptr> display() const; + /// Get a shared pointer to the low-level display driver + /// \return A shared pointer to the display driver + const std::shared_ptr &display_driver() const { return display_driver_; } + /// Set the brightness of the backlight /// \param brightness The brightness of the backlight as a percentage (0 - 100) /// \note This function will only work after initialize_lcd() has been called @@ -236,6 +240,17 @@ class MatouchRotaryDisplay : public BaseComponent { void write_lcd_frame(const uint16_t x, const uint16_t y, const uint16_t width, const uint16_t height, uint8_t *data); + /// Write lines to the LCD + /// \param xs The x start coordinate + /// \param ys The y start coordinate + /// \param xe The x end coordinate + /// \param ye The y end coordinate + /// \param data The data to write + /// \param user_data User data to pass to the SPI transaction callback + /// \note This method queues the panel transfer asynchronously and may return + /// before the write has completed. + void write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data); + protected: MatouchRotaryDisplay(); bool update_cst816(); @@ -339,7 +354,7 @@ class MatouchRotaryDisplay : public BaseComponent { std::shared_ptr> display_; std::vector backlight_channel_configs_{}; std::shared_ptr backlight_{}; - std::unique_ptr display_driver_; + std::shared_ptr display_driver_{static_cast(nullptr)}; std::unique_ptr lcd_spi_; std::unique_ptr lcd_; uint8_t *frame_buffer0_{nullptr}; diff --git a/components/matouch-rotary-display/src/matouch-rotary-display.cpp b/components/matouch-rotary-display/src/matouch-rotary-display.cpp index 648bcdcb8..0dddd78b6 100644 --- a/components/matouch-rotary-display/src/matouch-rotary-display.cpp +++ b/components/matouch-rotary-display/src/matouch-rotary-display.cpp @@ -1,5 +1,7 @@ #include "matouch-rotary-display.hpp" +#include + using namespace espp; MatouchRotaryDisplay::MatouchRotaryDisplay() @@ -247,7 +249,7 @@ bool MatouchRotaryDisplay::initialize_lcd() { lcd_spi_.reset(); return false; } - display_driver_ = std::make_unique( + display_driver_ = std::make_shared( espp::display_drivers::Config{.panel_io = lcd_.get(), .write_command = nullptr, .read_command = nullptr, @@ -363,6 +365,43 @@ void MatouchRotaryDisplay::write_lcd_frame(const uint16_t xs, const uint16_t ys, } } +void MatouchRotaryDisplay::write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, + uint32_t user_data) { + if (!lcd_) { + return; + } + if (data == nullptr) { + logger_.error("lcd_send_lines: Null data for ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + if (xs < 0 || ys < 0 || xe < xs || ye < ys) { + logger_.error("lcd_send_lines: Bad region: ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + size_t width = static_cast(xe - xs + 1); + size_t height = static_cast(ye - ys + 1); + size_t length = width * height * lcd_bytes_per_pixel; + lcd_->wait(); + std::array window = { + static_cast((xs >> 8) & 0xff), + static_cast(xs & 0xff), + static_cast((xe >> 8) & 0xff), + static_cast(xe & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::caset)); + lcd_->queue_data(window); + window = { + static_cast((ys >> 8) & 0xff), + static_cast(ys & 0xff), + static_cast((ye >> 8) & 0xff), + static_cast(ye & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::raset)); + lcd_->queue_data(window); + lcd_->queue_command(static_cast(DisplayDriver::Command::ramwr)); + lcd_->queue_pixels(data, length, user_data); +} + MatouchRotaryDisplay::Pixel *MatouchRotaryDisplay::vram0() const { if (!display_) { return nullptr; diff --git a/components/seeed-studio-round-display/include/seeed-studio-round-display.hpp b/components/seeed-studio-round-display/include/seeed-studio-round-display.hpp index 3f9509de4..d1acc7120 100644 --- a/components/seeed-studio-round-display/include/seeed-studio-round-display.hpp +++ b/components/seeed-studio-round-display/include/seeed-studio-round-display.hpp @@ -198,6 +198,10 @@ class SsRoundDisplay : public espp::BaseComponent { /// \return A shared pointer to the display std::shared_ptr> display() const; + /// Get a shared pointer to the low-level display driver + /// \return A shared pointer to the display driver + const std::shared_ptr &display_driver() const { return display_driver_; } + /// Set the brightness of the backlight /// \param brightness The brightness of the backlight as a percentage (0 - 100) /// \note This function will only work after initialize_lcd() has been called @@ -245,6 +249,17 @@ class SsRoundDisplay : public espp::BaseComponent { void write_lcd_frame(const uint16_t x, const uint16_t y, const uint16_t width, const uint16_t height, uint8_t *data); + /// Write lines to the LCD + /// \param xs The x start coordinate + /// \param ys The y start coordinate + /// \param xe The x end coordinate + /// \param ye The y end coordinate + /// \param data The data to write + /// \param user_data User data to pass to the SPI transaction callback + /// \note This method queues the panel transfer asynchronously and may return + /// before the write has completed. + void write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data); + protected: SsRoundDisplay(); void touch_interrupt_handler(const espp::Interrupt::Event &event); @@ -303,7 +318,7 @@ class SsRoundDisplay : public espp::BaseComponent { std::shared_ptr> display_; std::vector backlight_channel_configs_{}; std::shared_ptr backlight_{}; - std::unique_ptr display_driver_; + std::shared_ptr display_driver_{static_cast(nullptr)}; std::unique_ptr lcd_spi_; std::unique_ptr lcd_; uint8_t *frame_buffer0_{nullptr}; diff --git a/components/seeed-studio-round-display/src/seeed-studio-round-display.cpp b/components/seeed-studio-round-display/src/seeed-studio-round-display.cpp index 848b1556d..2b57a5253 100644 --- a/components/seeed-studio-round-display/src/seeed-studio-round-display.cpp +++ b/components/seeed-studio-round-display/src/seeed-studio-round-display.cpp @@ -1,5 +1,7 @@ #include "seeed-studio-round-display.hpp" +#include + using namespace espp; SsRoundDisplay::PinConfig SsRoundDisplay::pin_config_; @@ -206,7 +208,7 @@ bool SsRoundDisplay::initialize_lcd() { lcd_spi_.reset(); return false; } - display_driver_ = std::make_unique( + display_driver_ = std::make_shared( espp::display_drivers::Config{.panel_io = lcd_.get(), .write_command = nullptr, .read_command = nullptr, @@ -322,6 +324,43 @@ void SsRoundDisplay::write_lcd_frame(const uint16_t xs, const uint16_t ys, const } } +void SsRoundDisplay::write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, + uint32_t user_data) { + if (!lcd_) { + return; + } + if (data == nullptr) { + logger_.error("lcd_send_lines: Null data for ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + if (xs < 0 || ys < 0 || xe < xs || ye < ys) { + logger_.error("lcd_send_lines: Bad region: ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + size_t width = static_cast(xe - xs + 1); + size_t height = static_cast(ye - ys + 1); + size_t length = width * height * lcd_bytes_per_pixel; + lcd_->wait(); + std::array window = { + static_cast((xs >> 8) & 0xff), + static_cast(xs & 0xff), + static_cast((xe >> 8) & 0xff), + static_cast(xe & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::caset)); + lcd_->queue_data(window); + window = { + static_cast((ys >> 8) & 0xff), + static_cast(ys & 0xff), + static_cast((ye >> 8) & 0xff), + static_cast(ye & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::raset)); + lcd_->queue_data(window); + lcd_->queue_command(static_cast(DisplayDriver::Command::ramwr)); + lcd_->queue_pixels(data, length, user_data); +} + SsRoundDisplay::Pixel *SsRoundDisplay::vram0() const { if (!display_) { return nullptr; diff --git a/components/smartpanlee-sc01-plus/include/smartpanlee-sc01-plus.hpp b/components/smartpanlee-sc01-plus/include/smartpanlee-sc01-plus.hpp index ab165edc4..8f3d772b9 100755 --- a/components/smartpanlee-sc01-plus/include/smartpanlee-sc01-plus.hpp +++ b/components/smartpanlee-sc01-plus/include/smartpanlee-sc01-plus.hpp @@ -176,6 +176,9 @@ class SmartPanleeSc01Plus : public BaseComponent { /// Get the high-level LVGL display wrapper. /// \return Shared pointer to the display wrapper, or nullptr if not initialized. std::shared_ptr> display() const { return display_; } + /// Get a shared pointer to the low-level display driver. + /// \return Shared pointer to the display driver, or a typed-null handle if not initialized. + const std::shared_ptr &display_driver() const { return display_driver_; } /// Set the display backlight brightness. /// \param brightness Brightness percentage in the range [0, 100]. void brightness(float brightness); @@ -198,6 +201,16 @@ class SmartPanleeSc01Plus : public BaseComponent { /// Get the current display height after applying LVGL rotation. /// \return Rotated height in pixels. size_t rotated_display_height() const; + /// Write lines to the LCD using the legacy scanline-oriented transport signature. + /// \param xs Inclusive start X coordinate. + /// \param ys Inclusive start Y coordinate. + /// \param xe Inclusive end X coordinate. + /// \param ye Inclusive end Y coordinate. + /// \param data Pointer to pixel payload data. + /// \param user_data Opaque user data passed by the caller. + /// \note This method queues the panel transfer asynchronously and may return + /// before the write has completed. + void write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data); /// Initialize the speaker audio path using the board's published I2S pins. /// \return True if audio playback support was initialized, false otherwise. @@ -266,7 +279,6 @@ class SmartPanleeSc01Plus : public BaseComponent { bool audio_task_callback(std::mutex &m, std::condition_variable &cv, bool &task_notified); bool update_touch(); void write_command(uint8_t command, std::span parameters, uint32_t user_data); - void write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data); static constexpr auto internal_i2c_port = I2C_NUM_1; static constexpr auto internal_i2c_clock_speed = 400 * 1000; @@ -346,7 +358,7 @@ class SmartPanleeSc01Plus : public BaseComponent { touch_callback_t touch_callback_{nullptr}; std::shared_ptr> display_; - std::unique_ptr display_driver_; + std::shared_ptr display_driver_{static_cast(nullptr)}; std::shared_ptr backlight_; std::vector backlight_channel_configs_; esp_lcd_i80_bus_handle_t lcd_bus_{nullptr}; diff --git a/components/smartpanlee-sc01-plus/src/smartpanlee-sc01-plus.cpp b/components/smartpanlee-sc01-plus/src/smartpanlee-sc01-plus.cpp index 2ae268e42..7c2638e2a 100644 --- a/components/smartpanlee-sc01-plus/src/smartpanlee-sc01-plus.cpp +++ b/components/smartpanlee-sc01-plus/src/smartpanlee-sc01-plus.cpp @@ -221,7 +221,7 @@ bool SmartPanleeSc01Plus::initialize_lcd() { ESP_ERROR_CHECK(esp_lcd_new_panel_io_i80(lcd_bus_, &io_config, &panel_io_)); using namespace std::placeholders; - display_driver_ = std::make_unique(espp::display_drivers::Config{ + display_driver_ = std::make_shared(espp::display_drivers::Config{ .write_command = std::bind(&SmartPanleeSc01Plus::write_command, this, _1, _2, _3), .lcd_send_lines = std::bind(&SmartPanleeSc01Plus::write_lcd_lines, this, _1, _2, _3, _4, _5, _6), @@ -556,6 +556,17 @@ void SmartPanleeSc01Plus::write_command(uint8_t command, std::span window = { static_cast((xs >> 8) & 0xFF), static_cast(xs & 0xFF), @@ -574,8 +585,9 @@ void SmartPanleeSc01Plus::write_lcd_lines(int xs, int ys, int xe, int ye, const ESP_ERROR_CHECK(esp_lcd_panel_io_tx_param(panel_io_, static_cast(DisplayDriver::Command::raset), window.data(), window.size())); - size_t num_bytes = - static_cast(xe - xs + 1) * static_cast(ye - ys + 1) * sizeof(Pixel); + size_t width = static_cast(xe - xs + 1); + size_t height = static_cast(ye - ys + 1); + size_t num_bytes = width * height * sizeof(Pixel); ESP_ERROR_CHECK(esp_lcd_panel_io_tx_color( panel_io_, static_cast(DisplayDriver::Command::ramwr), data, num_bytes)); } diff --git a/components/t-deck/include/t-deck.hpp b/components/t-deck/include/t-deck.hpp index a8dc3f6a9..b27c76d5d 100644 --- a/components/t-deck/include/t-deck.hpp +++ b/components/t-deck/include/t-deck.hpp @@ -338,6 +338,10 @@ class TDeck : public BaseComponent { /// \return A shared pointer to the display std::shared_ptr> display() const; + /// Get a shared pointer to the low-level display driver + /// \return A shared pointer to the display driver + const std::shared_ptr &display_driver() const { return display_driver_; } + /// Set the brightness of the backlight /// \param brightness The brightness of the backlight as a percentage (0 - 100) /// \note This function will only work after initialize_lcd() has been called @@ -401,8 +405,8 @@ class TDeck : public BaseComponent { /// \param ye The y end coordinate /// \param data The data to write /// \param user_data User data to pass to the spi transaction callback - /// \note This method queues the data to be written to the LCD, only blocking - /// if there is an ongoing SPI transaction + /// \note This method queues the panel transfer asynchronously and may return + /// before the write has completed. void write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data); ///////////////////////////////////////////////////////////////////////////// @@ -646,7 +650,7 @@ class TDeck : public BaseComponent { // display std::shared_ptr> display_; - std::unique_ptr display_driver_; + std::shared_ptr display_driver_{static_cast(nullptr)}; std::vector backlight_channel_configs_{}; std::shared_ptr backlight_{}; static constexpr int spi_queue_size = 6; diff --git a/components/t-deck/src/t-deck.cpp b/components/t-deck/src/t-deck.cpp index 66de0c60f..cf80dc9fb 100644 --- a/components/t-deck/src/t-deck.cpp +++ b/components/t-deck/src/t-deck.cpp @@ -1,5 +1,7 @@ #include "t-deck.hpp" +#include + using namespace espp; TDeck::TDeck() @@ -316,7 +318,7 @@ bool TDeck::initialize_lcd() { return false; } - display_driver_ = std::make_unique( + display_driver_ = std::make_shared( espp::display_drivers::Config{.panel_io = lcd_.get(), .write_command = nullptr, .read_command = nullptr, @@ -422,11 +424,17 @@ void IRAM_ATTR TDeck::write_lcd_lines(int xs, int ys, int xe, int ye, const uint if (!lcd_) { return; } - size_t length = (xe - xs + 1) * (ye - ys + 1) * 2; - if (length == 0) { - logger_.error("lcd_send_lines: Bad length: ({},{}) to ({},{})", xs, ys, xe, ye); + if (data == nullptr) { + logger_.error("lcd_send_lines: Null data for ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + if (xs < 0 || ys < 0 || xe < xs || ye < ys) { + logger_.error("lcd_send_lines: Bad region: ({},{}) to ({},{})", xs, ys, xe, ye); return; } + size_t width = static_cast(xe - xs + 1); + size_t height = static_cast(ye - ys + 1); + size_t length = width * height * 2; lcd_->wait(); std::array window = { static_cast((xs >> 8) & 0xff), diff --git a/components/t-dongle-s3/include/t-dongle-s3.hpp b/components/t-dongle-s3/include/t-dongle-s3.hpp index 3f0e59fa0..aa63ff41e 100644 --- a/components/t-dongle-s3/include/t-dongle-s3.hpp +++ b/components/t-dongle-s3/include/t-dongle-s3.hpp @@ -44,6 +44,9 @@ class TDongleS3 : public BaseComponent { /// Alias for the pixel type used by the T-Dongle-S3 display using Pixel = lv_color16_t; + /// Alias for the display driver + using DisplayDriver = espp::St7789; + /// Maximum number of bytes that can be transferred in a single SPI /// transaction to the Display. 32k on the ESP32-S3. static constexpr size_t SPI_MAX_TRANSFER_BYTES = SPI_LL_DMA_MAX_BIT_LEN / 8; @@ -146,6 +149,10 @@ class TDongleS3 : public BaseComponent { /// \return A shared pointer to the display std::shared_ptr> display() const; + /// Get a shared pointer to the low-level display driver + /// \return A shared pointer to the display driver + const std::shared_ptr &display_driver() const { return display_driver_; } + /// Set the brightness of the backlight /// \param brightness The brightness of the backlight as a percentage (0 - 100) /// \note This function will only work after initialize_lcd() has been called @@ -193,6 +200,17 @@ class TDongleS3 : public BaseComponent { void write_lcd_frame(const uint16_t x, const uint16_t y, const uint16_t width, const uint16_t height, uint8_t *data); + /// Write lines to the LCD + /// \param xs The x start coordinate + /// \param ys The y start coordinate + /// \param xe The x end coordinate + /// \param ye The y end coordinate + /// \param data The data to write + /// \param user_data User data to pass to the SPI transaction callback + /// \note This method queues the panel transfer asynchronously and may return + /// before the write has completed. + void write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data); + ///////////////////////////////////////////////////////////////////////////// // uSD Card ///////////////////////////////////////////////////////////////////////////// @@ -251,8 +269,6 @@ class TDongleS3 : public BaseComponent { static constexpr bool mirror_x = false; static constexpr bool mirror_y = false; static constexpr gpio_num_t backlight_io = GPIO_NUM_38; - using DisplayDriver = espp::St7789; - // button (boot button) static constexpr gpio_num_t button_io = GPIO_NUM_0; // active low @@ -298,7 +314,7 @@ class TDongleS3 : public BaseComponent { // display std::shared_ptr> display_; - std::unique_ptr display_driver_; + std::shared_ptr display_driver_{static_cast(nullptr)}; std::vector backlight_channel_configs_{}; std::shared_ptr backlight_{}; static constexpr int spi_queue_size = 6; diff --git a/components/t-dongle-s3/src/t-dongle-s3.cpp b/components/t-dongle-s3/src/t-dongle-s3.cpp index 0920eeac8..8a26d9263 100644 --- a/components/t-dongle-s3/src/t-dongle-s3.cpp +++ b/components/t-dongle-s3/src/t-dongle-s3.cpp @@ -1,5 +1,7 @@ #include "t-dongle-s3.hpp" +#include + using namespace espp; TDongleS3::TDongleS3() @@ -172,7 +174,7 @@ bool TDongleS3::initialize_lcd() { lcd_spi_.reset(); return false; } - display_driver_ = std::make_unique( + display_driver_ = std::make_shared( espp::display_drivers::Config{.panel_io = lcd_.get(), .write_command = nullptr, .read_command = nullptr, @@ -269,6 +271,43 @@ void TDongleS3::write_lcd_frame(const uint16_t xs, const uint16_t ys, const uint } } +void IRAM_ATTR TDongleS3::write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, + uint32_t user_data) { + if (!lcd_) { + return; + } + if (data == nullptr) { + logger_.error("lcd_send_lines: Null data for ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + if (xs < 0 || ys < 0 || xe < xs || ye < ys) { + logger_.error("lcd_send_lines: Bad region: ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + size_t width = static_cast(xe - xs + 1); + size_t height = static_cast(ye - ys + 1); + size_t length = width * height * lcd_bytes_per_pixel; + lcd_->wait(); + std::array window = { + static_cast((xs >> 8) & 0xff), + static_cast(xs & 0xff), + static_cast((xe >> 8) & 0xff), + static_cast(xe & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::caset)); + lcd_->queue_data(window); + window = { + static_cast((ys >> 8) & 0xff), + static_cast(ys & 0xff), + static_cast((ye >> 8) & 0xff), + static_cast(ye & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::raset)); + lcd_->queue_data(window); + lcd_->queue_command(static_cast(DisplayDriver::Command::ramwr)); + lcd_->queue_pixels(data, length, user_data); +} + TDongleS3::Pixel *TDongleS3::vram0() const { if (!display_) { return nullptr; diff --git a/components/wrover-kit/include/wrover-kit.hpp b/components/wrover-kit/include/wrover-kit.hpp index 983da77e5..f7917fd1e 100644 --- a/components/wrover-kit/include/wrover-kit.hpp +++ b/components/wrover-kit/include/wrover-kit.hpp @@ -30,6 +30,9 @@ class WroverKit : public BaseComponent { /// Alias for the pixel type used by the wrover-kit display using Pixel = lv_color16_t; + /// Alias for the display driver + using DisplayDriver = espp::Ili9341; + /// Maximum number of bytes that can be transferred in a single SPI /// transaction to the Display. 2MB on ESP32. static constexpr size_t SPI_MAX_TRANSFER_BYTES = SPI_LL_DMA_MAX_BIT_LEN / 8; @@ -97,6 +100,10 @@ class WroverKit : public BaseComponent { /// \return A shared pointer to the display std::shared_ptr> display() const; + /// Get a shared pointer to the low-level display driver + /// \return A shared pointer to the display driver + const std::shared_ptr &display_driver() const { return display_driver_; } + /// Set the brightness of the backlight /// \param brightness The brightness of the backlight as a percentage (0 - 100) /// \note This function will only work after initialize_lcd() has been called @@ -144,6 +151,17 @@ class WroverKit : public BaseComponent { void write_lcd_frame(const uint16_t x, const uint16_t y, const uint16_t width, const uint16_t height, uint8_t *data); + /// Write lines to the LCD + /// \param xs The x start coordinate + /// \param ys The y start coordinate + /// \param xe The x end coordinate + /// \param ye The y end coordinate + /// \param data The data to write + /// \param user_data User data to pass to the SPI transaction callback + /// \note This method queues the panel transfer asynchronously and may return + /// before the write has completed. + void write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data); + protected: WroverKit(); void lcd_wait_lines(); @@ -169,13 +187,12 @@ class WroverKit : public BaseComponent { static constexpr bool mirror_x = false; static constexpr bool mirror_y = false; static constexpr bool swap_xy = true; - using DisplayDriver = espp::Ili9341; // display std::shared_ptr> display_; std::vector backlight_channel_configs_{}; std::shared_ptr backlight_{}; - std::unique_ptr display_driver_; + std::shared_ptr display_driver_{static_cast(nullptr)}; std::unique_ptr lcd_spi_; std::unique_ptr lcd_; uint8_t *frame_buffer0_{nullptr}; diff --git a/components/wrover-kit/src/wrover-kit.cpp b/components/wrover-kit/src/wrover-kit.cpp index 4e76172b7..9b0843495 100644 --- a/components/wrover-kit/src/wrover-kit.cpp +++ b/components/wrover-kit/src/wrover-kit.cpp @@ -1,5 +1,7 @@ #include "wrover-kit.hpp" +#include + using namespace espp; WroverKit::WroverKit() @@ -68,7 +70,7 @@ bool WroverKit::initialize_lcd() { return false; } display_driver_ = - std::make_unique(espp::display_drivers::Config{.panel_io = lcd_.get(), + std::make_shared(espp::display_drivers::Config{.panel_io = lcd_.get(), .write_command = nullptr, .read_command = nullptr, .lcd_send_lines = nullptr, @@ -161,6 +163,43 @@ void WroverKit::write_lcd_frame(const uint16_t xs, const uint16_t ys, const uint } } +void WroverKit::write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, + uint32_t user_data) { + if (!lcd_) { + return; + } + if (data == nullptr) { + logger_.error("lcd_send_lines: Null data for ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + if (xs < 0 || ys < 0 || xe < xs || ye < ys) { + logger_.error("lcd_send_lines: Bad region: ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + size_t width = static_cast(xe - xs + 1); + size_t height = static_cast(ye - ys + 1); + size_t length = width * height * lcd_bytes_per_pixel; + lcd_->wait(); + std::array window = { + static_cast((xs >> 8) & 0xff), + static_cast(xs & 0xff), + static_cast((xe >> 8) & 0xff), + static_cast(xe & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::caset)); + lcd_->queue_data(window); + window = { + static_cast((ys >> 8) & 0xff), + static_cast(ys & 0xff), + static_cast((ye >> 8) & 0xff), + static_cast(ye & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::raset)); + lcd_->queue_data(window); + lcd_->queue_command(static_cast(DisplayDriver::Command::ramwr)); + lcd_->queue_pixels(data, length, user_data); +} + WroverKit::Pixel *WroverKit::vram0() const { if (!display_) { return nullptr; diff --git a/components/ws-s3-geek/include/ws-s3-geek.hpp b/components/ws-s3-geek/include/ws-s3-geek.hpp index 81183d074..9b2a14cb1 100644 --- a/components/ws-s3-geek/include/ws-s3-geek.hpp +++ b/components/ws-s3-geek/include/ws-s3-geek.hpp @@ -42,6 +42,9 @@ class WsS3Geek : public BaseComponent { /// Alias for the pixel type used by the display using Pixel = lv_color16_t; + /// Alias for the display driver + using DisplayDriver = espp::St7789; + /// Maximum number of bytes that can be transferred in a single SPI /// transaction to the Display. 32k on the ESP32-S3. static constexpr size_t SPI_MAX_TRANSFER_BYTES = SPI_LL_DMA_MAX_BIT_LEN / 8; @@ -116,6 +119,10 @@ class WsS3Geek : public BaseComponent { /// \return A shared pointer to the display std::shared_ptr> display() const; + /// Get a shared pointer to the low-level display driver + /// \return A shared pointer to the display driver + const std::shared_ptr &display_driver() const { return display_driver_; } + /// Set the brightness of the backlight /// \param brightness The brightness of the backlight as a percentage (0 - 100) /// \note This function will only work after initialize_lcd() has been called @@ -149,6 +156,17 @@ class WsS3Geek : public BaseComponent { void write_lcd_frame(const uint16_t x, const uint16_t y, const uint16_t width, const uint16_t height, uint8_t *data); + /// Write lines to the LCD + /// \param xs The x start coordinate + /// \param ys The y start coordinate + /// \param xe The x end coordinate + /// \param ye The y end coordinate + /// \param data The data to write + /// \param user_data User data to pass to the SPI transaction callback + /// \note This method queues the panel transfer asynchronously and may return + /// before the write has completed. + void write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data); + ///////////////////////////////////////////////////////////////////////////// // uSD Card ///////////////////////////////////////////////////////////////////////////// @@ -198,7 +216,6 @@ class WsS3Geek : public BaseComponent { static constexpr bool mirror_x = false; static constexpr bool mirror_y = false; static constexpr gpio_num_t backlight_io = GPIO_NUM_7; - using DisplayDriver = espp::St7789; // button (boot button) static constexpr gpio_num_t button_io = GPIO_NUM_0; // active low @@ -244,7 +261,7 @@ class WsS3Geek : public BaseComponent { // display std::shared_ptr> display_; - std::unique_ptr display_driver_; + std::shared_ptr display_driver_{static_cast(nullptr)}; std::vector backlight_channel_configs_{}; std::shared_ptr backlight_{}; static constexpr int spi_queue_size = 6; diff --git a/components/ws-s3-geek/src/ws-s3-geek.cpp b/components/ws-s3-geek/src/ws-s3-geek.cpp index 903576720..57625d273 100644 --- a/components/ws-s3-geek/src/ws-s3-geek.cpp +++ b/components/ws-s3-geek/src/ws-s3-geek.cpp @@ -1,5 +1,7 @@ #include "ws-s3-geek.hpp" +#include + using namespace espp; WsS3Geek::WsS3Geek() @@ -98,7 +100,7 @@ bool WsS3Geek::initialize_lcd() { lcd_spi_.reset(); return false; } - display_driver_ = std::make_unique( + display_driver_ = std::make_shared( espp::display_drivers::Config{.panel_io = lcd_.get(), .write_command = nullptr, .read_command = nullptr, @@ -190,6 +192,43 @@ void WsS3Geek::write_lcd_frame(const uint16_t xs, const uint16_t ys, const uint1 } } +void IRAM_ATTR WsS3Geek::write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, + uint32_t user_data) { + if (!lcd_) { + return; + } + if (data == nullptr) { + logger_.error("lcd_send_lines: Null data for ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + if (xs < 0 || ys < 0 || xe < xs || ye < ys) { + logger_.error("lcd_send_lines: Bad region: ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + size_t width = static_cast(xe - xs + 1); + size_t height = static_cast(ye - ys + 1); + size_t length = width * height * lcd_bytes_per_pixel; + lcd_->wait(); + std::array window = { + static_cast((xs >> 8) & 0xff), + static_cast(xs & 0xff), + static_cast((xe >> 8) & 0xff), + static_cast(xe & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::caset)); + lcd_->queue_data(window); + window = { + static_cast((ys >> 8) & 0xff), + static_cast(ys & 0xff), + static_cast((ye >> 8) & 0xff), + static_cast(ye & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::raset)); + lcd_->queue_data(window); + lcd_->queue_command(static_cast(DisplayDriver::Command::ramwr)); + lcd_->queue_pixels(data, length, user_data); +} + WsS3Geek::Pixel *WsS3Geek::vram0() const { if (!display_) { return nullptr; diff --git a/components/ws-s3-lcd-1-47/include/ws-s3-lcd-1-47.hpp b/components/ws-s3-lcd-1-47/include/ws-s3-lcd-1-47.hpp index 924a17a0d..ecc10fad1 100644 --- a/components/ws-s3-lcd-1-47/include/ws-s3-lcd-1-47.hpp +++ b/components/ws-s3-lcd-1-47/include/ws-s3-lcd-1-47.hpp @@ -44,6 +44,9 @@ class WsS3Lcd147 : public BaseComponent { /// Alias for the pixel type used by the display using Pixel = lv_color16_t; + /// Alias for the display driver + using DisplayDriver = espp::St7789; + /// Maximum number of bytes that can be transferred in a single SPI /// transaction to the Display. 32k on the ESP32-S3. static constexpr size_t SPI_MAX_TRANSFER_BYTES = SPI_LL_DMA_MAX_BIT_LEN / 8; @@ -118,6 +121,10 @@ class WsS3Lcd147 : public BaseComponent { /// \return A shared pointer to the display std::shared_ptr> display() const; + /// Get a shared pointer to the low-level display driver + /// \return A shared pointer to the display driver + const std::shared_ptr &display_driver() const { return display_driver_; } + /// Set the brightness of the backlight /// \param brightness The brightness of the backlight as a percentage (0 - 100) /// \note This function will only work after initialize_lcd() has been called @@ -165,6 +172,17 @@ class WsS3Lcd147 : public BaseComponent { void write_lcd_frame(const uint16_t x, const uint16_t y, const uint16_t width, const uint16_t height, uint8_t *data); + /// Write lines to the LCD + /// \param xs The x start coordinate + /// \param ys The y start coordinate + /// \param xe The x end coordinate + /// \param ye The y end coordinate + /// \param data The data to write + /// \param user_data User data to pass to the SPI transaction callback + /// \note This method queues the panel transfer asynchronously and may return + /// before the write has completed. + void write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data); + ///////////////////////////////////////////////////////////////////////////// // uSD Card ///////////////////////////////////////////////////////////////////////////// @@ -236,7 +254,6 @@ class WsS3Lcd147 : public BaseComponent { static constexpr bool mirror_x = false; static constexpr bool mirror_y = false; static constexpr gpio_num_t backlight_io = GPIO_NUM_48; - using DisplayDriver = espp::St7789; // button (boot button) static constexpr gpio_num_t button_io = GPIO_NUM_0; // active low @@ -281,7 +298,7 @@ class WsS3Lcd147 : public BaseComponent { // display std::shared_ptr> display_; - std::unique_ptr display_driver_; + std::shared_ptr display_driver_{static_cast(nullptr)}; std::vector backlight_channel_configs_{}; std::shared_ptr backlight_{}; static constexpr int spi_queue_size = 6; diff --git a/components/ws-s3-lcd-1-47/src/ws-s3-lcd-1-47.cpp b/components/ws-s3-lcd-1-47/src/ws-s3-lcd-1-47.cpp index 7530cd39d..022b151ef 100644 --- a/components/ws-s3-lcd-1-47/src/ws-s3-lcd-1-47.cpp +++ b/components/ws-s3-lcd-1-47/src/ws-s3-lcd-1-47.cpp @@ -1,5 +1,7 @@ #include "ws-s3-lcd-1-47.hpp" +#include + using namespace espp; WsS3Lcd147::WsS3Lcd147() @@ -97,7 +99,7 @@ bool WsS3Lcd147::initialize_lcd() { lcd_spi_.reset(); return false; } - display_driver_ = std::make_unique( + display_driver_ = std::make_shared( espp::display_drivers::Config{.panel_io = lcd_.get(), .write_command = nullptr, .read_command = nullptr, @@ -194,6 +196,43 @@ void WsS3Lcd147::write_lcd_frame(const uint16_t xs, const uint16_t ys, const uin } } +void IRAM_ATTR WsS3Lcd147::write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, + uint32_t user_data) { + if (!lcd_) { + return; + } + if (data == nullptr) { + logger_.error("lcd_send_lines: Null data for ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + if (xs < 0 || ys < 0 || xe < xs || ye < ys) { + logger_.error("lcd_send_lines: Bad region: ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + size_t width = static_cast(xe - xs + 1); + size_t height = static_cast(ye - ys + 1); + size_t length = width * height * lcd_bytes_per_pixel; + lcd_->wait(); + std::array window = { + static_cast((xs >> 8) & 0xff), + static_cast(xs & 0xff), + static_cast((xe >> 8) & 0xff), + static_cast(xe & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::caset)); + lcd_->queue_data(window); + window = { + static_cast((ys >> 8) & 0xff), + static_cast(ys & 0xff), + static_cast((ye >> 8) & 0xff), + static_cast(ye & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::raset)); + lcd_->queue_data(window); + lcd_->queue_command(static_cast(DisplayDriver::Command::ramwr)); + lcd_->queue_pixels(data, length, user_data); +} + WsS3Lcd147::Pixel *WsS3Lcd147::vram0() const { if (!display_) { return nullptr; diff --git a/components/ws-s3-touch/include/ws-s3-touch.hpp b/components/ws-s3-touch/include/ws-s3-touch.hpp index bf7d1ae7e..ba0bf3bd0 100644 --- a/components/ws-s3-touch/include/ws-s3-touch.hpp +++ b/components/ws-s3-touch/include/ws-s3-touch.hpp @@ -176,6 +176,10 @@ class WsS3Touch : public BaseComponent { /// \return A shared pointer to the display std::shared_ptr> display() const { return display_; } + /// Get a shared pointer to the low-level display driver + /// \return A shared pointer to the display driver + const std::shared_ptr &display_driver() const { return display_driver_; } + /// Set the brightness of the backlight /// \param brightness The brightness of the backlight as a percentage (0 - 100) /// \note This function will only work after initialize_lcd() has been called @@ -209,6 +213,17 @@ class WsS3Touch : public BaseComponent { void write_lcd_frame(const uint16_t x, const uint16_t y, const uint16_t width, const uint16_t height, uint8_t *data); + /// Write lines to the LCD + /// \param xs The x start coordinate + /// \param ys The y start coordinate + /// \param xe The x end coordinate + /// \param ye The y end coordinate + /// \param data The data to write + /// \param user_data User data to pass to the SPI transaction callback + /// \note This method queues the panel transfer asynchronously and may return + /// before the write has completed. + void write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data); + ///////////////////////////////////////////////////////////////////////////// // Button ///////////////////////////////////////////////////////////////////////////// @@ -403,7 +418,7 @@ class WsS3Touch : public BaseComponent { std::shared_ptr> display_; std::vector backlight_channel_configs_{}; std::shared_ptr backlight_{}; - std::unique_ptr display_driver_; + std::shared_ptr display_driver_{static_cast(nullptr)}; std::unique_ptr lcd_spi_; std::unique_ptr lcd_; diff --git a/components/ws-s3-touch/src/video.cpp b/components/ws-s3-touch/src/video.cpp index 8e3f54cf7..517c923eb 100644 --- a/components/ws-s3-touch/src/video.cpp +++ b/components/ws-s3-touch/src/video.cpp @@ -1,5 +1,7 @@ #include "ws-s3-touch.hpp" +#include + using namespace espp; //////////////////////// @@ -56,7 +58,7 @@ bool WsS3Touch::initialize_lcd() { return false; } logger_.info("Initializing the display driver..."); - display_driver_ = std::make_unique( + display_driver_ = std::make_shared( espp::display_drivers::Config{.panel_io = lcd_.get(), .write_command = nullptr, .read_command = nullptr, @@ -171,6 +173,43 @@ void WsS3Touch::write_lcd_frame(const uint16_t xs, const uint16_t ys, const uint } } +void WsS3Touch::write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, + uint32_t user_data) { + if (!lcd_) { + return; + } + if (data == nullptr) { + logger_.error("lcd_send_lines: Null data for ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + if (xs < 0 || ys < 0 || xe < xs || ye < ys) { + logger_.error("lcd_send_lines: Bad region: ({},{}) to ({},{})", xs, ys, xe, ye); + return; + } + size_t width = static_cast(xe - xs + 1); + size_t height = static_cast(ye - ys + 1); + size_t length = width * height * lcd_bytes_per_pixel; + lcd_->wait(); + std::array window = { + static_cast((xs >> 8) & 0xff), + static_cast(xs & 0xff), + static_cast((xe >> 8) & 0xff), + static_cast(xe & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::caset)); + lcd_->queue_data(window); + window = { + static_cast((ys >> 8) & 0xff), + static_cast(ys & 0xff), + static_cast((ye >> 8) & 0xff), + static_cast(ye & 0xff), + }; + lcd_->queue_command(static_cast(DisplayDriver::Command::raset)); + lcd_->queue_data(window); + lcd_->queue_command(static_cast(DisplayDriver::Command::ramwr)); + lcd_->queue_pixels(data, length, user_data); +} + WsS3Touch::Pixel *WsS3Touch::vram0() const { if (!display_) { return nullptr;