From a7915753c6f99de3ce97bf2992a6627cd6cb2618 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Wed, 19 Oct 2022 15:36:47 +1100 Subject: [PATCH 1/4] WIP sketch out init sequence --- main/CMakeLists.txt | 2 +- main/display.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++ main/display.hpp | 25 +++++++++++++++++ main/gay-ipod-fw.cpp | 10 ++----- main/gpio-expander.cpp | 1 + 5 files changed, 93 insertions(+), 8 deletions(-) create mode 100644 main/display.cpp create mode 100644 main/display.hpp diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 67e52965..75f64826 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,7 +1,7 @@ idf_component_register( SRCS "gay-ipod-fw.cpp" "dac.cpp" "gpio-expander.cpp" "battery.cpp" - "storage.cpp" "i2c.cpp" "playback.cpp" + "storage.cpp" "i2c.cpp" "playback.cpp" "display.cpp" INCLUDE_DIRS "." REQUIRES "esp_adc_cal" "fatfs" "audio_pipeline" "audio_stream" "result" "lvgl") diff --git a/main/display.cpp b/main/display.cpp new file mode 100644 index 00000000..551400bd --- /dev/null +++ b/main/display.cpp @@ -0,0 +1,63 @@ +#include "display.hpp" +#include +#include "driver/gpio.h" +#include "driver/spi_master.h" +#include "hal/gpio_types.h" +#include "hal/spi_types.h" + +namespace gay_ipod { + +static const gpio_num_t kCommandOrDataPin = GPIO_NUM_21; +static const gpio_num_t kLedPin = GPIO_NUM_22; + +static const uint8_t kDisplayWidth = 128; +static const uint8_t kDisplayHeight = 160; + +auto Display::create(GpioExpander* expander) + -> cpp::result, Error> { + // First, set up our GPIOs +#define SPI_QUADWP_IO (GPIO_NUM_22) +#define SPI_QUADHD_IO (GPIO_NUM_21) + gpio_config_t gpio_cfg = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = (1 << GPIO_OUTPUT_22) | (1 << GPIO_OUTPUT_21), + .pull_down_en = 0, + .pull_up_en = 0, + } + gpio_config(&gpio_cfg); + + gpio_set_level(kLedPin, 1); + gpio_set_level(kCommandOrDataPin, 1); + + // Next, init the SPI device + auto lock = expander->AcquireSpiBus(GpioExpander::DISPLAY); + spi_device_interface_config_t spi_cfg = { + .command_bits = 0, // Unused + .address_bits = 0, // Unused + .dummy_bits = 0, + .mode = 0, + .duty_cycle_pos = 0, // Unused + .cs_ena_pretrans = 0, + .cs_ena_posttrans = 0, + .clock_speed_hz = 32000000, + .input_delay_ns = 0, + .spics_io_num = -1, + .flags = 0, + .queue_size = 0, + .pre_cb = NULL, + .post_cb = NULL, + }; + spi_device_handle_t handle; + spi_bus_add_device(VSPI_HOST, &spi_cfg, &handle); + + // Now we reset the display into a known state, then configure it + // https://github.com/adafruit/Adafruit-ST7735-Library/blob/master/Adafruit_ST77xx.cpp + // https://github.com/adafruit/Adafruit-ST7735-Library/blob/master/Adafruit_ST7735.cpp + // commonInit with Rcmd1 + // displayInit with Rcmd2green + // displayInit with Rcmd3 + // setRotation +} + +} // namespace gay_ipod diff --git a/main/display.hpp b/main/display.hpp new file mode 100644 index 00000000..972d5d8a --- /dev/null +++ b/main/display.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "gpio-expander.hpp" + +namespace gay_ipod { + +/* + * Display driver for LVGL. + */ +class Display { + public: + enum Error {}; + static auto create(GpioExpander* expander) + -> cpp::result, Error>; + + Display(GpioExpander *gpio); + ~Display(); + + void WriteData(); + + private: + GpioExpander *gpio_; +}; + +} // namespace gay_ipod diff --git a/main/gay-ipod-fw.cpp b/main/gay-ipod-fw.cpp index ffbdea62..41cee9a6 100644 --- a/main/gay-ipod-fw.cpp +++ b/main/gay-ipod-fw.cpp @@ -21,6 +21,7 @@ #include "esp_log.h" #include "hal/gpio_types.h" #include "hal/spi_types.h" +#include "lvgl/lvgl.h" #define I2C_SDA_IO (GPIO_NUM_2) #define I2C_SCL_IO (GPIO_NUM_4) @@ -63,8 +64,8 @@ esp_err_t init_spi(void) { .mosi_io_num = SPI_SDO_IO, .miso_io_num = SPI_SDI_IO, .sclk_io_num = SPI_SCLK_IO, - .quadwp_io_num = SPI_QUADWP_IO, - .quadhd_io_num = SPI_QUADHD_IO, + .quadwp_io_num = -1, //SPI_QUADWP_IO, + .quadhd_io_num = -1, //SPI_QUADHD_IO, // Unused .data4_io_num = -1, @@ -125,11 +126,6 @@ extern "C" void app_main(void) { ESP_LOGI(TAG, "Everything looks good! Waiting a mo for debugger."); vTaskDelay(pdMS_TO_TICKS(1500)); - playback->Play("/sdcard/test.mp3"); - playback->set_volume(100); - - playback->ProcessEvents(); - ESP_LOGI(TAG, "Time to deinit."); ESP_LOGI(TAG, "Hooray!"); diff --git a/main/gpio-expander.cpp b/main/gpio-expander.cpp index 9234beac..6b472d1c 100644 --- a/main/gpio-expander.cpp +++ b/main/gpio-expander.cpp @@ -71,6 +71,7 @@ bool GpioExpander::get_input(Pin pin) const { } GpioExpander::SpiLock GpioExpander::AcquireSpiBus(ChipSelect cs) { + // TODO: also spi_device_acquire_bus? return SpiLock(*this, cs); } From 5a8df4f2b93061f53e883578ed85d575d0d52d7a Mon Sep 17 00:00:00 2001 From: jacqueline Date: Mon, 24 Oct 2022 14:17:29 +1100 Subject: [PATCH 2/4] WIP init sequence --- main/CMakeLists.txt | 22 ++-- main/dac.hpp | 2 +- main/display.cpp | 273 ++++++++++++++++++++++++++++++++++++----- main/display.hpp | 42 +++++-- main/gay-ipod-fw.cpp | 17 ++- main/gpio-expander.hpp | 2 +- main/i2c.cpp | 2 +- main/i2c.hpp | 2 +- main/playback.cpp | 35 +++--- main/playback.hpp | 2 +- 10 files changed, 327 insertions(+), 72 deletions(-) diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 75f64826..f31b0c38 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,7 +1,15 @@ -idf_component_register( - SRCS - "gay-ipod-fw.cpp" "dac.cpp" "gpio-expander.cpp" "battery.cpp" - "storage.cpp" "i2c.cpp" "playback.cpp" "display.cpp" - INCLUDE_DIRS "." - REQUIRES - "esp_adc_cal" "fatfs" "audio_pipeline" "audio_stream" "result" "lvgl") +idf_component_register(SRCS + "gay-ipod-fw.cpp" + "dac.cpp" + "gpio-expander.cpp" + "battery.cpp" + "storage.cpp" + "i2c.cpp" + "playback.cpp" + "display.cpp" INCLUDE_DIRS "." REQUIRES + "esp_adc_cal" + "fatfs" + "audio_pipeline" + "audio_stream" + "result" + "lvgl") diff --git a/main/dac.hpp b/main/dac.hpp index 68d4c21a..6d025384 100644 --- a/main/dac.hpp +++ b/main/dac.hpp @@ -2,8 +2,8 @@ #include "gpio-expander.hpp" -#include #include +#include #include "esp_err.h" #include "result.hpp" diff --git a/main/display.cpp b/main/display.cpp index 551400bd..db01717c 100644 --- a/main/display.cpp +++ b/main/display.cpp @@ -1,7 +1,9 @@ #include "display.hpp" #include +#include #include "driver/gpio.h" #include "driver/spi_master.h" +#include "freertos/portable.h" #include "hal/gpio_types.h" #include "hal/spi_types.h" @@ -12,52 +14,263 @@ static const gpio_num_t kLedPin = GPIO_NUM_22; static const uint8_t kDisplayWidth = 128; static const uint8_t kDisplayHeight = 160; +static const uint8_t kDelayBit = 0x80; -auto Display::create(GpioExpander* expander) +enum StCommands { + ST77XX_NOP = 0x00, + ST77XX_SWRESET = 0x01, + ST77XX_RDDID = 0x04, + ST77XX_RDDST = 0x09, + + ST77XX_SLPIN = 0x10, + ST77XX_SLPOUT = 0x11, + ST77XX_PTLON = 0x12, + ST77XX_NORON = 0x13, + + ST77XX_INVOFF = 0x20, + ST77XX_INVON = 0x21, + ST77XX_DISPOFF = 0x28, + ST77XX_DISPON = 0x29, + ST77XX_CASET = 0x2A, + ST77XX_RASET = 0x2B, + ST77XX_RAMWR = 0x2C, + ST77XX_RAMRD = 0x2E, + + ST77XX_PTLAR = 0x30, + ST77XX_TEOFF = 0x34, + ST77XX_TEON = 0x35, + ST77XX_MADCTL = 0x36, + ST77XX_COLMOD = 0x3A, + + ST77XX_MADCTL_MY = 0x80, + ST77XX_MADCTL_MX = 0x40, + ST77XX_MADCTL_MV = 0x20, + ST77XX_MADCTL_ML = 0x10, + ST77XX_MADCTL_RGB = 0x00, + + ST77XX_RDID1 = 0xDA, + ST77XX_RDID2 = 0xDB, + ST77XX_RDID3 = 0xDC, + ST77XX_RDID4 = 0xDD, + + ST7735_MADCTL_BGR = 0x08, + ST7735_MADCTL_MH = 0x04, + + ST7735_FRMCTR1 = 0xB1, + ST7735_FRMCTR2 = 0xB2, + ST7735_FRMCTR3 = 0xB3, + ST7735_INVCTR = 0xB4, + ST7735_DISSET5 = 0xB6, + + ST7735_PWCTR1 = 0xC0, + ST7735_PWCTR2 = 0xC1, + ST7735_PWCTR3 = 0xC2, + ST7735_PWCTR4 = 0xC3, + ST7735_PWCTR5 = 0xC4, + ST7735_VMCTR1 = 0xC5, + + ST7735_PWCTR6 = 0xFC, + + ST7735_GMCTRP1 = 0xE0, + ST7735_GMCTRN1 = 0xE1, +}; + +// Based on Adafruit library, which seems to be the most complete. +// clang-format off +static uint8_t kST7735RCommonHeader[]{ + 15, // 15 commands in list: + ST77XX_SWRESET, kDelayBit, // 1: Software reset, 0 args, w/delay + 150, // 150 ms delay + ST77XX_SLPOUT, kDelayBit, // 2: Out of sleep mode, 0 args, w/delay + 255, // 500 ms delay + ST7735_FRMCTR1, 3, // 3: Framerate ctrl - normal mode, 3 arg: + 0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D) + ST7735_FRMCTR2, 3, // 4: Framerate ctrl - idle mode, 3 args: + 0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D) + ST7735_FRMCTR3, 6, // 5: Framerate - partial mode, 6 args: + 0x01, 0x2C, 0x2D, // Dot inversion mode + 0x01, 0x2C, 0x2D, // Line inversion mode + ST7735_INVCTR, 1, // 6: Display inversion ctrl, 1 arg: + 0x07, // No inversion + ST7735_PWCTR1, 3, // 7: Power control, 3 args, no delay: + 0xA2, + 0x02, // -4.6V + 0x84, // AUTO mode + ST7735_PWCTR2, 1, // 8: Power control, 1 arg, no delay: + 0xC5, // VGH25=2.4C VGSEL=-10 VGH=3 * AVDD + ST7735_PWCTR3, 2, // 9: Power control, 2 args, no delay: + 0x0A, // Opamp current small + 0x00, // Boost frequency + ST7735_PWCTR4, 2, // 10: Power control, 2 args, no delay: + 0x8A, // BCLK/2, + 0x2A, // opamp current small & medium low + ST7735_PWCTR5, 2, // 11: Power control, 2 args, no delay: + 0x8A, 0xEE, + ST7735_VMCTR1, 1, // 12: Power control, 1 arg, no delay: + 0x0E, + ST77XX_INVOFF, 0, // 13: Don't invert display, no args + ST77XX_MADCTL, 1, // 14: Mem access ctl (directions), 1 arg: + 0xC8, // row/col addr, bottom-top refresh + ST77XX_COLMOD, 1, // 15: set color mode, 1 arg, no delay: + 0x05 +}; + +static uint8_t kST7735RCommonGreen[]{ + 2, // 2 commands in list: + ST77XX_CASET, 4, // 1: Column addr set, 4 args, no delay: + 0x00, 0x02, // XSTART = 0 + 0x00, 0x7F+0x02, // XEND = 127 + ST77XX_RASET, 4, // 2: Row addr set, 4 args, no delay: + 0x00, 0x01, // XSTART = 0 + 0x00, 0x9F+0x01}; + +static uint8_t kST7735RCommonFooter[]{ + 4, // 4 commands in list: + ST7735_GMCTRP1, 16 , // 1: Gamma Adjustments (pos. polarity), 16 args + delay: + 0x02, 0x1c, 0x07, 0x12, // (Not entirely necessary, but provides + 0x37, 0x32, 0x29, 0x2d, // accurate colors) + 0x29, 0x25, 0x2B, 0x39, + 0x00, 0x01, 0x03, 0x10, + ST7735_GMCTRN1, 16 , // 2: Gamma Adjustments (neg. polarity), 16 args + delay: + 0x03, 0x1d, 0x07, 0x06, // (Not entirely necessary, but provides + 0x2E, 0x2C, 0x29, 0x2D, // accurate colors) + 0x2E, 0x2E, 0x37, 0x3F, + 0x00, 0x00, 0x02, 0x10, + ST77XX_NORON, kDelayBit, // 3: Normal display on, no args, w/delay + 10, // 10 ms delay + ST77XX_DISPON, kDelayBit, // 4: Main screen turn on, no args w/delay + 100 +}; +// clang-format on + +InitialisationData kInitData = { + .num_sequences = 3, + .sequences = {kST7735RCommonHeader, kST7735RCommonGreen, + kST7735RCommonFooter}}; + +auto Display::create(GpioExpander* expander, + const InitialisationData& init_data) -> cpp::result, Error> { // First, set up our GPIOs -#define SPI_QUADWP_IO (GPIO_NUM_22) -#define SPI_QUADHD_IO (GPIO_NUM_21) gpio_config_t gpio_cfg = { - .intr_type = GPIO_INTR_DISABLE, - .mode = GPIO_MODE_OUTPUT, - .pin_bit_mask = (1 << GPIO_OUTPUT_22) | (1 << GPIO_OUTPUT_21), - .pull_down_en = 0, - .pull_up_en = 0, - } + .pin_bit_mask = GPIO_SEL_22 | GPIO_SEL_21, + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; gpio_config(&gpio_cfg); gpio_set_level(kLedPin, 1); - gpio_set_level(kCommandOrDataPin, 1); + gpio_set_level(kCommandOrDataPin, 0); // Next, init the SPI device auto lock = expander->AcquireSpiBus(GpioExpander::DISPLAY); spi_device_interface_config_t spi_cfg = { - .command_bits = 0, // Unused - .address_bits = 0, // Unused - .dummy_bits = 0, - .mode = 0, - .duty_cycle_pos = 0, // Unused - .cs_ena_pretrans = 0, - .cs_ena_posttrans = 0, - .clock_speed_hz = 32000000, - .input_delay_ns = 0, - .spics_io_num = -1, - .flags = 0, - .queue_size = 0, - .pre_cb = NULL, - .post_cb = NULL, + .command_bits = 0, // No command phase + .address_bits = 0, // No address phase + .dummy_bits = 0, + // For ST7789, mode should be 2 + .mode = 0, + .duty_cycle_pos = 0, // Unused + .cs_ena_pretrans = 0, + .cs_ena_posttrans = 0, + .clock_speed_hz = SPI_MASTER_FREQ_40M, + .input_delay_ns = 0, // TODO: tune? + .spics_io_num = -1, // TODO: change for R2 + .flags = 0, + .queue_size = 0, + .pre_cb = NULL, + .post_cb = NULL, }; spi_device_handle_t handle; spi_bus_add_device(VSPI_HOST, &spi_cfg, &handle); + auto display = std::make_unique(expander, handle); + // Now we reset the display into a known state, then configure it - // https://github.com/adafruit/Adafruit-ST7735-Library/blob/master/Adafruit_ST77xx.cpp - // https://github.com/adafruit/Adafruit-ST7735-Library/blob/master/Adafruit_ST7735.cpp - // commonInit with Rcmd1 - // displayInit with Rcmd2green - // displayInit with Rcmd3 // setRotation + for (int i = 0; i < init_data.num_sequences; i++) { + display->SendInitialisationSequence(init_data.sequences[i]); + } + + return std::move(display); +} + +Display::Display(GpioExpander* gpio, spi_device_handle_t handle) + : gpio_(gpio), handle_(handle) {} + +Display::~Display() { + // TODO. +} + +void Display::SendInitialisationSequence(uint8_t* data) { + uint8_t commands_remaining, command, num_args; + uint16_t sleep_duration_ms; + + // First byte of the data is the number of commands. + commands_remaining = *data; + while (commands_remaining > 0) { + command = *(data++); + num_args = *(data++); + bool has_delay = (num_args & kDelayBit) > 0; + num_args &= ~kDelayBit; + + SendCommandWithData(command, data, num_args); + + data += num_args; + if (has_delay) { + sleep_duration_ms = *(data++); + if (sleep_duration_ms == 0xFF) { + sleep_duration_ms = 500; + } + vTaskDelay(pdMS_TO_TICKS(sleep_duration_ms)); + } + } +} + +void Display::SendCommandWithData(uint8_t command, + uint8_t* data, + size_t length) { + gpio_set_level(kCommandOrDataPin, 0); + SendTransaction(&command, 1); + gpio_set_level(kCommandOrDataPin, 1); + SendTransaction(data, length); +} + +void Display::SendCmd(uint8_t* data, size_t length) { + gpio_set_level(kCommandOrDataPin, 0); + SendTransaction(data, length); +} + +void Display::SendData(uint8_t* data, size_t length) { + gpio_set_level(kCommandOrDataPin, 1); + SendTransaction(data, length); +} + +void Display::SendTransaction(uint8_t* data, size_t length) { + if (length == 0) { + return; + } + + // TODO: Check if we should malloc this from DMA-capable memory. + spi_transaction_t transaction; + transaction.rx_buffer = NULL; + // Length is in bits, so multiply by 8. + transaction.length = length * 8; + + // If the data to transmit is very short, then we can fit it directly + // inside the transaction struct. + if (length < 4) { + transaction.flags = SPI_TRANS_USE_TXDATA; + std::memcpy(&transaction.tx_data, data, length); + } else { + transaction.tx_buffer = data; + } + + // TODO: acquire the bus first? Or in an outer scope? + // TODO: fail gracefully + ESP_ERROR_CHECK(spi_device_polling_transmit(handle_, &transaction)); } -} // namespace gay_ipod +} // namespace gay_ipod diff --git a/main/display.hpp b/main/display.hpp index 972d5d8a..394a5f99 100644 --- a/main/display.hpp +++ b/main/display.hpp @@ -1,25 +1,47 @@ #pragma once +#include +#include "driver/spi_master.h" #include "gpio-expander.hpp" +#include "result.hpp" +// https://github.com/adafruit/Adafruit-ST7735-Library/blob/master/Adafruit_ST77xx.cpp +// https://github.com/adafruit/Adafruit-ST7735-Library/blob/master/Adafruit_ST7735.cpp namespace gay_ipod { +struct InitialisationData { + uint8_t num_sequences; + uint8_t* sequences[4]; +}; + +extern InitialisationData kInitData; + /* * Display driver for LVGL. */ class Display { - public: - enum Error {}; - static auto create(GpioExpander* expander) - -> cpp::result, Error>; + public: + enum Error {}; + static auto create(GpioExpander* expander, + const InitialisationData& init_data) + -> cpp::result, Error>; + + Display(GpioExpander* gpio, spi_device_handle_t handle); + ~Display(); + + void WriteData(); + + private: + GpioExpander* gpio_; + spi_device_handle_t handle_; - Display(GpioExpander *gpio); - ~Display(); + void SendInitialisationSequence(uint8_t* data); - void WriteData(); + void SendCommandWithData(uint8_t command, uint8_t* data, size_t length); - private: - GpioExpander *gpio_; + void SendCmd(uint8_t* data, size_t length); + void SendData(uint8_t* data, size_t length); + void SendTransaction(uint8_t* data, size_t length); }; -} // namespace gay_ipod +} // namespace gay_ipod diff --git a/main/gay-ipod-fw.cpp b/main/gay-ipod-fw.cpp index 41cee9a6..4f8b4fda 100644 --- a/main/gay-ipod-fw.cpp +++ b/main/gay-ipod-fw.cpp @@ -1,13 +1,14 @@ #include "battery.hpp" #include "dac.hpp" +#include "display.hpp" #include "gpio-expander.hpp" #include "playback.hpp" #include "storage.hpp" -#include #include -#include #include +#include +#include #include "audio_common.h" #include "audio_element.h" @@ -64,8 +65,8 @@ esp_err_t init_spi(void) { .mosi_io_num = SPI_SDO_IO, .miso_io_num = SPI_SDI_IO, .sclk_io_num = SPI_SCLK_IO, - .quadwp_io_num = -1, //SPI_QUADWP_IO, - .quadhd_io_num = -1, //SPI_QUADHD_IO, + .quadwp_io_num = -1, // SPI_QUADWP_IO, + .quadhd_io_num = -1, // SPI_QUADHD_IO, // Unused .data4_io_num = -1, @@ -126,6 +127,14 @@ extern "C" void app_main(void) { ESP_LOGI(TAG, "Everything looks good! Waiting a mo for debugger."); vTaskDelay(pdMS_TO_TICKS(1500)); + ESP_LOGI(TAG, "Init Display"); + auto display_res = gay_ipod::Display::create(&expander, gay_ipod::kInitData); + if (display_res.has_error()) { + ESP_LOGE(TAG, "Failed: %d", display_res.error()); + return; + } + std::unique_ptr display = std::move(display_res.value()); + ESP_LOGI(TAG, "Time to deinit."); ESP_LOGI(TAG, "Hooray!"); diff --git a/main/gpio-expander.hpp b/main/gpio-expander.hpp index 089aac42..2a12fba4 100644 --- a/main/gpio-expander.hpp +++ b/main/gpio-expander.hpp @@ -1,9 +1,9 @@ #pragma once +#include #include #include #include -#include #include #include diff --git a/main/i2c.cpp b/main/i2c.cpp index 65ed9012..d3bfaa59 100644 --- a/main/i2c.cpp +++ b/main/i2c.cpp @@ -10,7 +10,7 @@ static constexpr int kCmdLinkSize = I2C_LINK_RECOMMENDED_SIZE(12); I2CTransaction::I2CTransaction() { // Use a fixed size buffer to avoid many many tiny allocations. - buffer_ = (uint8_t*) calloc(sizeof(uint8_t), kCmdLinkSize); + buffer_ = (uint8_t*)calloc(sizeof(uint8_t), kCmdLinkSize); handle_ = i2c_cmd_link_create_static(buffer_, kCmdLinkSize); assert(handle_ != NULL && "failed to create command link"); } diff --git a/main/i2c.hpp b/main/i2c.hpp index 0993a305..db554f5d 100644 --- a/main/i2c.hpp +++ b/main/i2c.hpp @@ -78,7 +78,7 @@ class I2CTransaction { private: i2c_cmd_handle_t handle_; - uint8_t *buffer_; + uint8_t* buffer_; }; } // namespace gay_ipod diff --git a/main/playback.cpp b/main/playback.cpp index 401dc9a3..46dec680 100644 --- a/main/playback.cpp +++ b/main/playback.cpp @@ -17,7 +17,7 @@ static const i2s_port_t kI2SPort = I2S_NUM_0; namespace gay_ipod { -static audio_element_status_t status_from_the_void(void *status) { +static audio_element_status_t status_from_the_void(void* status) { uintptr_t as_pointer_int = reinterpret_cast(status); return static_cast(as_pointer_int); } @@ -182,39 +182,42 @@ void DacAudioPlayback::Pause() { void DacAudioPlayback::ProcessEvents() { while (1) { audio_event_iface_msg_t event; - esp_err_t err = audio_event_iface_listen(event_interface_, &event, portMAX_DELAY); + esp_err_t err = + audio_event_iface_listen(event_interface_, &event, portMAX_DELAY); if (err != ESP_OK) { ESP_LOGI(kTag, "error listening for event:%x", err); continue; } ESP_LOGI(kTag, "received event, cmd %i", event.cmd); - if (event.source_type == AUDIO_ELEMENT_TYPE_ELEMENT - && event.source == (void *) mp3_decoder_ - && event.cmd == AEL_MSG_CMD_REPORT_MUSIC_INFO) { + if (event.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && + event.source == (void*)mp3_decoder_ && + event.cmd == AEL_MSG_CMD_REPORT_MUSIC_INFO) { audio_element_info_t music_info = {0}; audio_element_getinfo(mp3_decoder_, &music_info); - ESP_LOGI(kTag, "sample_rate=%d, bits=%d, ch=%d", music_info.sample_rates, music_info.bits, music_info.channels); + ESP_LOGI(kTag, "sample_rate=%d, bits=%d, ch=%d", music_info.sample_rates, + music_info.bits, music_info.channels); audio_element_setinfo(i2s_stream_writer_, &music_info); - i2s_stream_set_clk(i2s_stream_writer_, music_info.sample_rates, music_info.bits, music_info.channels); + i2s_stream_set_clk(i2s_stream_writer_, music_info.sample_rates, + music_info.bits, music_info.channels); } - if (event.source_type == AUDIO_ELEMENT_TYPE_ELEMENT - && event.source == (void *) fatfs_stream_reader_ - && event.cmd == AEL_MSG_CMD_REPORT_STATUS) { + if (event.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && + event.source == (void*)fatfs_stream_reader_ && + event.cmd == AEL_MSG_CMD_REPORT_STATUS) { audio_element_status_t status = status_from_the_void(event.data); if (status == AEL_STATUS_STATE_FINISHED) { - // TODO: enqueue next track? + // TODO: enqueue next track? } } - if (event.source_type == AUDIO_ELEMENT_TYPE_ELEMENT - && event.source == (void *) i2s_stream_writer_ - && event.cmd == AEL_MSG_CMD_REPORT_STATUS) { + if (event.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && + event.source == (void*)i2s_stream_writer_ && + event.cmd == AEL_MSG_CMD_REPORT_STATUS) { audio_element_status_t status = status_from_the_void(event.data); if (status == AEL_STATUS_STATE_FINISHED) { - // TODO. - return; + // TODO. + return; } } diff --git a/main/playback.hpp b/main/playback.hpp index 88336105..493dd311 100644 --- a/main/playback.hpp +++ b/main/playback.hpp @@ -13,9 +13,9 @@ #include "audio_pipeline.h" #include "esp_err.h" #include "fatfs_stream.h" -#include "result.hpp" #include "i2s_stream.h" #include "mp3_decoder.h" +#include "result.hpp" namespace gay_ipod { From 43b04074a817d7e78105ae5b0a1fec66f67bbd40 Mon Sep 17 00:00:00 2001 From: jacqueline Date: Tue, 25 Oct 2022 17:03:22 +1100 Subject: [PATCH 3/4] WIP most of a basic display driver + lvgl, but not yet working :( --- lib/lv_conf.h | 2 +- main/CMakeLists.txt | 20 +-- main/display.cpp | 358 +++++++++++++++++++++++-------------------- main/display.hpp | 43 ++++-- main/gay-ipod-fw.cpp | 90 +++++++++-- sdkconfig | 16 +- 6 files changed, 307 insertions(+), 222 deletions(-) diff --git a/lib/lv_conf.h b/lib/lv_conf.h index 7b23498a..a32b33d5 100644 --- a/lib/lv_conf.h +++ b/lib/lv_conf.h @@ -27,7 +27,7 @@ #define LV_COLOR_DEPTH 16 /*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/ -#define LV_COLOR_16_SWAP 0 +#define LV_COLOR_16_SWAP 1 /*Enable features to draw on transparent background. *It's required if opa, and transform_* style properties are used. diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index f31b0c38..5ef0bf9e 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,15 +1,5 @@ -idf_component_register(SRCS - "gay-ipod-fw.cpp" - "dac.cpp" - "gpio-expander.cpp" - "battery.cpp" - "storage.cpp" - "i2c.cpp" - "playback.cpp" - "display.cpp" INCLUDE_DIRS "." REQUIRES - "esp_adc_cal" - "fatfs" - "audio_pipeline" - "audio_stream" - "result" - "lvgl") +idf_component_register( + SRCS "gay-ipod-fw.cpp" "dac.cpp" "gpio-expander.cpp" "battery.cpp" "storage.cpp" + "i2c.cpp" "playback.cpp" "display.cpp" "display-init.cpp" + INCLUDE_DIRS "." + REQUIRES "esp_adc_cal" "fatfs" "audio_pipeline" "audio_stream" "result" "lvgl") diff --git a/main/display.cpp b/main/display.cpp index db01717c..82cc5b6e 100644 --- a/main/display.cpp +++ b/main/display.cpp @@ -1,155 +1,77 @@ #include "display.hpp" +#include #include #include +#include +#include +#include "assert.h" +#include "display-init.hpp" #include "driver/gpio.h" #include "driver/spi_master.h" +#include "esp_attr.h" +#include "esp_heap_caps.h" #include "freertos/portable.h" +#include "freertos/portmacro.h" +#include "freertos/projdefs.h" #include "hal/gpio_types.h" #include "hal/spi_types.h" +#include "lvgl/lvgl.h" -namespace gay_ipod { - +static const char* kTag = "DISPLAY"; static const gpio_num_t kCommandOrDataPin = GPIO_NUM_21; static const gpio_num_t kLedPin = GPIO_NUM_22; static const uint8_t kDisplayWidth = 128; static const uint8_t kDisplayHeight = 160; -static const uint8_t kDelayBit = 0x80; - -enum StCommands { - ST77XX_NOP = 0x00, - ST77XX_SWRESET = 0x01, - ST77XX_RDDID = 0x04, - ST77XX_RDDST = 0x09, - - ST77XX_SLPIN = 0x10, - ST77XX_SLPOUT = 0x11, - ST77XX_PTLON = 0x12, - ST77XX_NORON = 0x13, - - ST77XX_INVOFF = 0x20, - ST77XX_INVON = 0x21, - ST77XX_DISPOFF = 0x28, - ST77XX_DISPON = 0x29, - ST77XX_CASET = 0x2A, - ST77XX_RASET = 0x2B, - ST77XX_RAMWR = 0x2C, - ST77XX_RAMRD = 0x2E, - - ST77XX_PTLAR = 0x30, - ST77XX_TEOFF = 0x34, - ST77XX_TEON = 0x35, - ST77XX_MADCTL = 0x36, - ST77XX_COLMOD = 0x3A, - - ST77XX_MADCTL_MY = 0x80, - ST77XX_MADCTL_MX = 0x40, - ST77XX_MADCTL_MV = 0x20, - ST77XX_MADCTL_ML = 0x10, - ST77XX_MADCTL_RGB = 0x00, - - ST77XX_RDID1 = 0xDA, - ST77XX_RDID2 = 0xDB, - ST77XX_RDID3 = 0xDC, - ST77XX_RDID4 = 0xDD, - - ST7735_MADCTL_BGR = 0x08, - ST7735_MADCTL_MH = 0x04, - - ST7735_FRMCTR1 = 0xB1, - ST7735_FRMCTR2 = 0xB2, - ST7735_FRMCTR3 = 0xB3, - ST7735_INVCTR = 0xB4, - ST7735_DISSET5 = 0xB6, - - ST7735_PWCTR1 = 0xC0, - ST7735_PWCTR2 = 0xC1, - ST7735_PWCTR3 = 0xC2, - ST7735_PWCTR4 = 0xC3, - ST7735_PWCTR5 = 0xC4, - ST7735_VMCTR1 = 0xC5, - - ST7735_PWCTR6 = 0xFC, - - ST7735_GMCTRP1 = 0xE0, - ST7735_GMCTRN1 = 0xE1, -}; - -// Based on Adafruit library, which seems to be the most complete. -// clang-format off -static uint8_t kST7735RCommonHeader[]{ - 15, // 15 commands in list: - ST77XX_SWRESET, kDelayBit, // 1: Software reset, 0 args, w/delay - 150, // 150 ms delay - ST77XX_SLPOUT, kDelayBit, // 2: Out of sleep mode, 0 args, w/delay - 255, // 500 ms delay - ST7735_FRMCTR1, 3, // 3: Framerate ctrl - normal mode, 3 arg: - 0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D) - ST7735_FRMCTR2, 3, // 4: Framerate ctrl - idle mode, 3 args: - 0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D) - ST7735_FRMCTR3, 6, // 5: Framerate - partial mode, 6 args: - 0x01, 0x2C, 0x2D, // Dot inversion mode - 0x01, 0x2C, 0x2D, // Line inversion mode - ST7735_INVCTR, 1, // 6: Display inversion ctrl, 1 arg: - 0x07, // No inversion - ST7735_PWCTR1, 3, // 7: Power control, 3 args, no delay: - 0xA2, - 0x02, // -4.6V - 0x84, // AUTO mode - ST7735_PWCTR2, 1, // 8: Power control, 1 arg, no delay: - 0xC5, // VGH25=2.4C VGSEL=-10 VGH=3 * AVDD - ST7735_PWCTR3, 2, // 9: Power control, 2 args, no delay: - 0x0A, // Opamp current small - 0x00, // Boost frequency - ST7735_PWCTR4, 2, // 10: Power control, 2 args, no delay: - 0x8A, // BCLK/2, - 0x2A, // opamp current small & medium low - ST7735_PWCTR5, 2, // 11: Power control, 2 args, no delay: - 0x8A, 0xEE, - ST7735_VMCTR1, 1, // 12: Power control, 1 arg, no delay: - 0x0E, - ST77XX_INVOFF, 0, // 13: Don't invert display, no args - ST77XX_MADCTL, 1, // 14: Mem access ctl (directions), 1 arg: - 0xC8, // row/col addr, bottom-top refresh - ST77XX_COLMOD, 1, // 15: set color mode, 1 arg, no delay: - 0x05 -}; - -static uint8_t kST7735RCommonGreen[]{ - 2, // 2 commands in list: - ST77XX_CASET, 4, // 1: Column addr set, 4 args, no delay: - 0x00, 0x02, // XSTART = 0 - 0x00, 0x7F+0x02, // XEND = 127 - ST77XX_RASET, 4, // 2: Row addr set, 4 args, no delay: - 0x00, 0x01, // XSTART = 0 - 0x00, 0x9F+0x01}; - -static uint8_t kST7735RCommonFooter[]{ - 4, // 4 commands in list: - ST7735_GMCTRP1, 16 , // 1: Gamma Adjustments (pos. polarity), 16 args + delay: - 0x02, 0x1c, 0x07, 0x12, // (Not entirely necessary, but provides - 0x37, 0x32, 0x29, 0x2d, // accurate colors) - 0x29, 0x25, 0x2B, 0x39, - 0x00, 0x01, 0x03, 0x10, - ST7735_GMCTRN1, 16 , // 2: Gamma Adjustments (neg. polarity), 16 args + delay: - 0x03, 0x1d, 0x07, 0x06, // (Not entirely necessary, but provides - 0x2E, 0x2C, 0x29, 0x2D, // accurate colors) - 0x2E, 0x2E, 0x37, 0x3F, - 0x00, 0x00, 0x02, 0x10, - ST77XX_NORON, kDelayBit, // 3: Normal display on, no args, w/delay - 10, // 10 ms delay - ST77XX_DISPON, kDelayBit, // 4: Main screen turn on, no args w/delay - 100 -}; -// clang-format on - -InitialisationData kInitData = { - .num_sequences = 3, - .sequences = {kST7735RCommonHeader, kST7735RCommonGreen, - kST7735RCommonFooter}}; +static const uint8_t kTransactionQueueSize = 10; + +/* + * The size of each of our two display buffers. This is fundamentally a balance + * between performance and memory usage. LVGL docs recommend a buffer 1/10th the + * size of the screen is the best tradeoff. + * We use two buffers so that one can be flushed to the screen at the same time + * as the other is being drawn. + */ +static const int kDisplayBufferSize = (kDisplayWidth * kDisplayHeight) / 10; + +// Allocate both buffers in static memory to ensure that they're in DRAM, with +// minimal fragmentation. We most cases we always need these buffers anyway, so +// it's not a memory hit we can avoid anyway. +// Note: 128 * 160 / 10 * 2 bpp * 2 buffers = 8 KiB +DMA_ATTR static lv_color_t sBuffer1[kDisplayBufferSize]; +DMA_ATTR static lv_color_t sBuffer2[kDisplayBufferSize]; + +namespace gay_ipod { + +// Static functions for interrop with the LVGL display driver API, which +// requires a function pointer. +namespace callback { +static std::atomic instance = nullptr; + +static void flush_cb(lv_disp_drv_t* disp_drv, + const lv_area_t* area, + lv_color_t* color_map) { + auto instance_unwrapped = instance.load(); + if (instance_unwrapped == nullptr) { + ESP_LOGW(kTag, "uncaught flush callback"); + return; + } + // TODO: what if a transaction comes in right now? + instance_unwrapped->Flush(disp_drv, area, color_map); +} + +static void IRAM_ATTR post_cb(spi_transaction_t* transaction) { + auto instance_unwrapped = instance.load(); + if (instance_unwrapped == nullptr) { + // Can't log in ISR. + return; + } + instance_unwrapped->PostTransaction(*transaction); +} +} // namespace callback auto Display::create(GpioExpander* expander, - const InitialisationData& init_data) + const displays::InitialisationData& init_data) -> cpp::result, Error> { // First, set up our GPIOs gpio_config_t gpio_cfg = { @@ -165,7 +87,6 @@ auto Display::create(GpioExpander* expander, gpio_set_level(kCommandOrDataPin, 0); // Next, init the SPI device - auto lock = expander->AcquireSpiBus(GpioExpander::DISPLAY); spi_device_interface_config_t spi_cfg = { .command_bits = 0, // No command phase .address_bits = 0, // No address phase @@ -175,46 +96,64 @@ auto Display::create(GpioExpander* expander, .duty_cycle_pos = 0, // Unused .cs_ena_pretrans = 0, .cs_ena_posttrans = 0, - .clock_speed_hz = SPI_MASTER_FREQ_40M, + .clock_speed_hz = SPI_MASTER_FREQ_8M, .input_delay_ns = 0, // TODO: tune? .spics_io_num = -1, // TODO: change for R2 .flags = 0, - .queue_size = 0, + .queue_size = kTransactionQueueSize, .pre_cb = NULL, - .post_cb = NULL, + .post_cb = &callback::post_cb, }; spi_device_handle_t handle; spi_bus_add_device(VSPI_HOST, &spi_cfg, &handle); + // TODO: ideally create this later? a bit awkward rn. auto display = std::make_unique(expander, handle); // Now we reset the display into a known state, then configure it - // setRotation + // TODO: set rotatoin + ESP_LOGI(kTag, "Sending init sequences"); for (int i = 0; i < init_data.num_sequences; i++) { display->SendInitialisationSequence(init_data.sequences[i]); } + // The hardware is now configured correctly. Next, initialise the LVGL display + // driver. + ESP_LOGI(kTag, "Init buffers"); + lv_disp_draw_buf_init(&display->buffers_, sBuffer1, sBuffer2, + kDisplayBufferSize); + lv_disp_drv_init(&display->driver_); + display->driver_.draw_buf = &display->buffers_; + display->driver_.hor_res = kDisplayWidth; + display->driver_.ver_res = kDisplayHeight; + display->driver_.flush_cb = &callback::flush_cb; + + ESP_LOGI(kTag, "Registering driver"); + display->display_ = lv_disp_drv_register(&display->driver_); + return std::move(display); } Display::Display(GpioExpander* gpio, spi_device_handle_t handle) - : gpio_(gpio), handle_(handle) {} + : gpio_(gpio), handle_(handle) { + callback::instance = this; +} Display::~Display() { + callback::instance = nullptr; // TODO. } -void Display::SendInitialisationSequence(uint8_t* data) { - uint8_t commands_remaining, command, num_args; +void Display::SendInitialisationSequence(const uint8_t* data) { + uint8_t command, num_args; uint16_t sleep_duration_ms; // First byte of the data is the number of commands. - commands_remaining = *data; - while (commands_remaining > 0) { + for (int i = *(data++); i > 0; i--) { command = *(data++); num_args = *(data++); - bool has_delay = (num_args & kDelayBit) > 0; - num_args &= ~kDelayBit; + bool has_delay = (num_args & displays::kDelayBit) > 0; + num_args &= ~displays::kDelayBit; SendCommandWithData(command, data, num_args); @@ -230,47 +169,126 @@ void Display::SendInitialisationSequence(uint8_t* data) { } void Display::SendCommandWithData(uint8_t command, - uint8_t* data, - size_t length) { - gpio_set_level(kCommandOrDataPin, 0); - SendTransaction(&command, 1); - gpio_set_level(kCommandOrDataPin, 1); - SendTransaction(data, length); + const uint8_t* data, + size_t length, + uintptr_t flags) { + SendCmd(&command, 1, flags); + SendData(data, length, flags); } -void Display::SendCmd(uint8_t* data, size_t length) { - gpio_set_level(kCommandOrDataPin, 0); - SendTransaction(data, length); +void Display::SendCmd(const uint8_t* data, size_t length, uintptr_t flags) { + SendTransaction(data, length, flags | SEND_COMMAND); } -void Display::SendData(uint8_t* data, size_t length) { - gpio_set_level(kCommandOrDataPin, 1); - SendTransaction(data, length); +void Display::SendData(const uint8_t* data, size_t length, uintptr_t flags) { + SendTransaction(data, length, flags | SEND_DATA); } -void Display::SendTransaction(uint8_t* data, size_t length) { +void Display::SendTransaction(const uint8_t* data, + size_t length, + uintptr_t flags) { if (length == 0) { return; } - // TODO: Check if we should malloc this from DMA-capable memory. - spi_transaction_t transaction; - transaction.rx_buffer = NULL; + // TODO: Use a memory pool for these. + spi_transaction_t* transaction = (spi_transaction_t*)heap_caps_calloc( + 1, sizeof(spi_transaction_t), MALLOC_CAP_DMA); + + transaction->rx_buffer = NULL; // Length is in bits, so multiply by 8. - transaction.length = length * 8; + transaction->length = length * 8; + transaction->rxlength = 0; // Match `length` value. + + transaction->user = nullptr; // TODO. // If the data to transmit is very short, then we can fit it directly // inside the transaction struct. - if (length < 4) { - transaction.flags = SPI_TRANS_USE_TXDATA; - std::memcpy(&transaction.tx_data, data, length); + if (length * 8 <= 32) { + transaction->flags = SPI_TRANS_USE_TXDATA; + std::memcpy(&transaction->tx_data, data, length); } else { - transaction.tx_buffer = data; + assert((flags & SMALL) == 0); + // TODO: copy data to a DMA-capable transaction buffer + transaction->tx_buffer = const_cast(data); } + transaction->user = reinterpret_cast(flags); + // TODO: acquire the bus first? Or in an outer scope? // TODO: fail gracefully - ESP_ERROR_CHECK(spi_device_polling_transmit(handle_, &transaction)); + // ESP_ERROR_CHECK(spi_device_queue_trans(handle_, transaction, + // portMAX_DELAY)); + // + + ServiceTransactions(); + if (flags & SEND_COMMAND) { + gpio_set_level(kCommandOrDataPin, 0); + } else if (flags & SEND_DATA) { + gpio_set_level(kCommandOrDataPin, 1); + } + + gpio_->with([&](auto& gpio_) { + gpio_.set_pin(GpioExpander::DISPLAY_CHIP_SELECT, 0); + }); + { + // auto lock = gpio_->AcquireSpiBus(GpioExpander::DISPLAY); + ESP_ERROR_CHECK(spi_device_polling_transmit(handle_, transaction)); + } + + free(transaction); +} + +void Display::Flush(lv_disp_drv_t* disp_drv, + const lv_area_t* area, + lv_color_t* color_map) { + // TODO: constants? based on init sequence? + uint16_t col_start = 2; + uint16_t row_start = 1; + uint16_t data[2] = {0, 0}; + + data[0] = SPI_SWAP_DATA_TX(area->x1 + row_start, 16); + data[1] = SPI_SWAP_DATA_TX(area->x2 + row_start, 16); + SendCommandWithData(displays::ST77XX_CASET, (uint8_t*)data, 4, SMALL); + + data[0] = SPI_SWAP_DATA_TX(area->y1 + col_start, 16); + data[1] = SPI_SWAP_DATA_TX(area->y2 + col_start, 16); + SendCommandWithData(displays::ST77XX_RASET, (uint8_t*)data, 4, SMALL); + + uint32_t size = lv_area_get_width(area) * lv_area_get_height(area); + SendCommandWithData(displays::ST77XX_RAMWR, (uint8_t*)color_map, size * 2, + FLUSH_BUFFER); + + // ESP_LOGI(kTag, "finished flush."); + // lv_disp_flush_ready(&driver_); +} + +void IRAM_ATTR Display::PostTransaction(const spi_transaction_t& transaction) { + if (reinterpret_cast(transaction.user) & FLUSH_BUFFER) { + lv_disp_flush_ready(&driver_); + } +} + +void Display::ServiceTransactions() { + // todo + if (1) + return; + spi_transaction_t* transaction = nullptr; + // TODO: just wait '1' here, provide mechanism to wait for sure (poll?) + while (spi_device_get_trans_result(handle_, &transaction, pdMS_TO_TICKS(1)) != + ESP_ERR_TIMEOUT) { + ESP_LOGI(kTag, "cleaning up finished transaction"); + + // TODO: a bit dodge lmao + // TODO: also this should happen in the post callback instead i guess? + if (transaction->length > 1000) { + ESP_LOGI(kTag, "finished flush."); + lv_disp_flush_ready(&driver_); + } + + // TODO: place back into pool. + free(transaction); + } } } // namespace gay_ipod diff --git a/main/display.hpp b/main/display.hpp index 394a5f99..69605c72 100644 --- a/main/display.hpp +++ b/main/display.hpp @@ -1,21 +1,14 @@ #pragma once #include +#include "display-init.hpp" #include "driver/spi_master.h" #include "gpio-expander.hpp" +#include "lvgl/lvgl.h" #include "result.hpp" -// https://github.com/adafruit/Adafruit-ST7735-Library/blob/master/Adafruit_ST77xx.cpp -// https://github.com/adafruit/Adafruit-ST7735-Library/blob/master/Adafruit_ST7735.cpp namespace gay_ipod { -struct InitialisationData { - uint8_t num_sequences; - uint8_t* sequences[4]; -}; - -extern InitialisationData kInitData; - /* * Display driver for LVGL. */ @@ -23,25 +16,45 @@ class Display { public: enum Error {}; static auto create(GpioExpander* expander, - const InitialisationData& init_data) + const displays::InitialisationData& init_data) -> cpp::result, Error>; Display(GpioExpander* gpio, spi_device_handle_t handle); ~Display(); void WriteData(); + void Flush(lv_disp_drv_t* disp_drv, + const lv_area_t* area, + lv_color_t* color_map); + void IRAM_ATTR PostTransaction(const spi_transaction_t& transaction); + + void ServiceTransactions(); private: GpioExpander* gpio_; spi_device_handle_t handle_; - void SendInitialisationSequence(uint8_t* data); + lv_disp_draw_buf_t buffers_; + lv_disp_drv_t driver_; + lv_disp_t* display_ = nullptr; + + enum TransactionData { + SEND_COMMAND = (1 << 0), + SEND_DATA = (1 << 1), + FLUSH_BUFFER = (1 << 2), + SMALL = (1 << 3), + }; + + void SendInitialisationSequence(const uint8_t* data); - void SendCommandWithData(uint8_t command, uint8_t* data, size_t length); + void SendCommandWithData(uint8_t command, + const uint8_t* data, + size_t length, + uintptr_t flags = 0); - void SendCmd(uint8_t* data, size_t length); - void SendData(uint8_t* data, size_t length); - void SendTransaction(uint8_t* data, size_t length); + void SendCmd(const uint8_t* data, size_t length, uintptr_t flags = 0); + void SendData(const uint8_t* data, size_t length, uintptr_t flags = 0); + void SendTransaction(const uint8_t* data, size_t length, uintptr_t flags = 0); }; } // namespace gay_ipod diff --git a/main/gay-ipod-fw.cpp b/main/gay-ipod-fw.cpp index 4f8b4fda..8525b422 100644 --- a/main/gay-ipod-fw.cpp +++ b/main/gay-ipod-fw.cpp @@ -1,12 +1,20 @@ #include "battery.hpp" +#include "core/lv_disp.h" +#include "core/lv_obj_pos.h" #include "dac.hpp" +#include "display-init.hpp" #include "display.hpp" +#include "esp_freertos_hooks.h" +#include "freertos/portmacro.h" #include "gpio-expander.hpp" +#include "misc/lv_color.h" +#include "misc/lv_timer.h" #include "playback.hpp" #include "storage.hpp" #include #include +#include #include #include @@ -23,6 +31,7 @@ #include "hal/gpio_types.h" #include "hal/spi_types.h" #include "lvgl/lvgl.h" +#include "widgets/lv_label.h" #define I2C_SDA_IO (GPIO_NUM_2) #define I2C_SCL_IO (GPIO_NUM_4) @@ -74,8 +83,9 @@ esp_err_t init_spi(void) { .data6_io_num = -1, .data7_io_num = -1, - // Use the DMA default size. - .max_transfer_sz = 0, + // Use the DMA default size. The display requires larger buffers, but it + // manages its down use of DMA-capable memory. + .max_transfer_sz = 128 * 16 * 2, // TODO: hmm .flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_IOMUX_PINS, .intr_flags = 0, }; @@ -85,6 +95,56 @@ esp_err_t init_spi(void) { return ESP_OK; } +void IRAM_ATTR tick_hook(void) { + lv_tick_inc(1); +} + +static const size_t kLvglStackSize = 8 * 1024; +static StaticTask_t sLvglTaskBuffer = {}; +static StackType_t sLvglStack[kLvglStackSize] = {0}; + +struct LvglArgs { + gay_ipod::GpioExpander* gpio_expander; +}; + +void lvgl_main(void* voidArgs) { + ESP_LOGI(TAG, "starting LVGL task"); + LvglArgs* args = (LvglArgs*)voidArgs; + gay_ipod::GpioExpander* gpio_expander = args->gpio_expander; + + // Dispose of the args now that we've gotten everything out of them. + delete args; + + ESP_LOGI(TAG, "init lvgl"); + lv_init(); + + // LVGL has been initialised, so we can now start reporting ticks to it. + esp_register_freertos_tick_hook(&tick_hook); + + ESP_LOGI(TAG, "init display"); + auto display_res = + gay_ipod::Display::create(gpio_expander, gay_ipod::displays::kST7735R); + if (display_res.has_error()) { + ESP_LOGE(TAG, "Failed: %d", display_res.error()); + return; + } + std::unique_ptr display = std::move(display_res.value()); + + auto label = lv_label_create(NULL); + lv_label_set_text(label, "g'day, cunts!"); + lv_obj_center(label); + lv_scr_load(label); + + while (1) { + lv_timer_handler(); + // display->ServiceTransactions(); + vTaskDelay(pdMS_TO_TICKS(10)); + } + + // TODO: break from the loop to kill this task, so that we can do our RAII + // cleanup, unregister our tick callback and so on. +} + extern "C" void app_main(void) { ESP_LOGI(TAG, "Initialising peripherals"); @@ -94,13 +154,14 @@ extern "C" void app_main(void) { ESP_ERROR_CHECK(gay_ipod::init_adc()); ESP_LOGI(TAG, "Init GPIOs"); - gay_ipod::GpioExpander expander; + gay_ipod::GpioExpander* expander = new gay_ipod::GpioExpander(); // for debugging usb ic // expander.set_sd_mux(gay_ipod::GpioExpander::USB); + /* ESP_LOGI(TAG, "Init SD card"); - auto storage_res = gay_ipod::SdStorage::create(&expander); + auto storage_res = gay_ipod::SdStorage::create(expander); if (storage_res.has_error()) { ESP_LOGE(TAG, "Failed: %d", storage_res.error()); return; @@ -108,7 +169,7 @@ extern "C" void app_main(void) { std::unique_ptr storage = std::move(storage_res.value()); ESP_LOGI(TAG, "Init DAC"); - auto dac_res = gay_ipod::AudioDac::create(&expander); + auto dac_res = gay_ipod::AudioDac::create(expander); if (storage_res.has_error()) { ESP_LOGE(TAG, "Failed: %d", dac_res.error()); return; @@ -123,19 +184,18 @@ extern "C" void app_main(void) { } std::unique_ptr playback = std::move(playback_res.value()); + */ ESP_LOGI(TAG, "Everything looks good! Waiting a mo for debugger."); vTaskDelay(pdMS_TO_TICKS(1500)); - ESP_LOGI(TAG, "Init Display"); - auto display_res = gay_ipod::Display::create(&expander, gay_ipod::kInitData); - if (display_res.has_error()) { - ESP_LOGE(TAG, "Failed: %d", display_res.error()); - return; - } - std::unique_ptr display = std::move(display_res.value()); - - ESP_LOGI(TAG, "Time to deinit."); + LvglArgs* lvglArgs = (LvglArgs*)calloc(1, sizeof(LvglArgs)); + lvglArgs->gpio_expander = expander; + xTaskCreateStaticPinnedToCore(&lvgl_main, "LVGL", 8 * 1024, (void*)lvglArgs, + 1, sLvglStack, &sLvglTaskBuffer, 1); - ESP_LOGI(TAG, "Hooray!"); + while (1) { + // TODO: Find owners for everything so we can quit this task safely. + vTaskDelay(pdMS_TO_TICKS(1000)); + } } diff --git a/sdkconfig b/sdkconfig index adc1a32a..c81660a8 100644 --- a/sdkconfig +++ b/sdkconfig @@ -411,11 +411,12 @@ CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y # CONFIG_ESP32_TIME_SYSCALL_USE_RTC is not set # CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set # CONFIG_ESP32_TIME_SYSCALL_USE_NONE is not set -CONFIG_ESP32_RTC_CLK_SRC_INT_RC=y -# CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_ESP32_RTC_CLK_SRC_INT_RC is not set +CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS=y # CONFIG_ESP32_RTC_CLK_SRC_EXT_OSC is not set # CONFIG_ESP32_RTC_CLK_SRC_INT_8MD256 is not set CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024 +CONFIG_ESP32_RTC_XTAL_CAL_RETRY=1 CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000 CONFIG_ESP32_XTAL_FREQ_40=y # CONFIG_ESP32_XTAL_FREQ_26 is not set @@ -598,6 +599,8 @@ CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y # CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set # CONFIG_ESP_SYSTEM_PANIC_GDBSTUB is not set # CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME is not set +CONFIG_ESP_SYSTEM_RTC_EXT_XTAL=y +CONFIG_ESP_SYSTEM_RTC_EXT_XTAL_BOOTSTRAP_CYCLES=5 # # Memory protection @@ -624,7 +627,7 @@ CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 CONFIG_ESP_INT_WDT_CHECK_CPU1=y CONFIG_ESP_TASK_WDT=y # CONFIG_ESP_TASK_WDT_PANIC is not set -CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 +CONFIG_ESP_TASK_WDT_TIMEOUT_S=10 CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y # CONFIG_ESP_PANIC_HANDLER_IRAM is not set @@ -1712,8 +1715,8 @@ CONFIG_BROWNOUT_DET_LVL_SEL_0=y # CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set # CONFIG_BROWNOUT_DET_LVL_SEL_7 is not set CONFIG_BROWNOUT_DET_LVL=0 -CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y -# CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL is not set +# CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC is not set +CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL=y # CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC is not set # CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 is not set # CONFIG_DISABLE_BASIC_ROM_CONSOLE is not set @@ -1736,6 +1739,7 @@ CONFIG_ESP32_REDUCE_PHY_TX_POWER=y CONFIG_ESP32S2_PANIC_PRINT_REBOOT=y # CONFIG_ESP32S2_PANIC_SILENT_REBOOT is not set # CONFIG_ESP32S2_PANIC_GDBSTUB is not set +CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES=5 CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 CONFIG_MAIN_TASK_STACK_SIZE=3584 @@ -1750,7 +1754,7 @@ CONFIG_INT_WDT_TIMEOUT_MS=300 CONFIG_INT_WDT_CHECK_CPU1=y CONFIG_TASK_WDT=y # CONFIG_TASK_WDT_PANIC is not set -CONFIG_TASK_WDT_TIMEOUT_S=5 +CONFIG_TASK_WDT_TIMEOUT_S=10 CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y # CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set From 1bcf59163b73ae688f6c7434519491d4613289ec Mon Sep 17 00:00:00 2001 From: jacqueline Date: Tue, 1 Nov 2022 15:30:15 +1100 Subject: [PATCH 4/4] WIP display stuff that /could/ work, but i think my display is bad :( --- main/display-init.cpp | 103 ++++++++++++++++++++++++++++++++++++++++++ main/display-init.hpp | 81 +++++++++++++++++++++++++++++++++ main/display.cpp | 43 +++++++----------- main/display.hpp | 19 +++++--- main/gay-ipod-fw.cpp | 2 +- 5 files changed, 215 insertions(+), 33 deletions(-) create mode 100644 main/display-init.cpp create mode 100644 main/display-init.hpp diff --git a/main/display-init.cpp b/main/display-init.cpp new file mode 100644 index 00000000..e4545339 --- /dev/null +++ b/main/display-init.cpp @@ -0,0 +1,103 @@ +#include "display-init.hpp" + +namespace gay_ipod { +namespace displays { + +/* Bit to use to signify we should delay after part of an init sequence */ +const uint8_t kDelayBit = 0x80; + +// ST7735 commands and general format from the Adafruit library for these +// displays. AFAICT it's the most complete implementation out there, and I +// really don't want to have to derive this from the datasheet myself. +// See https://github.com/adafruit/Adafruit-ST7735-Library/ + +// clang-format off +static const uint8_t kST7735RCommonHeader[]{ + 15, // 15 commands in list: + ST77XX_SWRESET, kDelayBit, // 1: Software reset, 0 args, w/delay + 150, // 150 ms delay + ST77XX_SLPOUT, kDelayBit, // 2: Out of sleep mode, 0 args, w/delay + 255, // 500 ms delay + ST7735_FRMCTR1, 3, // 3: Framerate ctrl - normal mode, 3 arg: + 0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D) + ST7735_FRMCTR2, 3, // 4: Framerate ctrl - idle mode, 3 args: + 0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D) + ST7735_FRMCTR3, 6, // 5: Framerate - partial mode, 6 args: + 0x01, 0x2C, 0x2D, // Dot inversion mode + 0x01, 0x2C, 0x2D, // Line inversion mode + ST7735_INVCTR, 1, // 6: Display inversion ctrl, 1 arg: + 0x07, // No inversion + ST7735_PWCTR1, 3, // 7: Power control, 3 args, no delay: + 0xA2, + 0x02, // -4.6V + 0x84, // AUTO mode + ST7735_PWCTR2, 1, // 8: Power control, 1 arg, no delay: + 0xC5, // VGH25=2.4C VGSEL=-10 VGH=3 * AVDD + ST7735_PWCTR3, 2, // 9: Power control, 2 args, no delay: + 0x0A, // Opamp current small + 0x00, // Boost frequency + ST7735_PWCTR4, 2, // 10: Power control, 2 args, no delay: + 0x8A, // BCLK/2, + 0x2A, // opamp current small & medium low + ST7735_PWCTR5, 2, // 11: Power control, 2 args, no delay: + 0x8A, 0xEE, + ST7735_VMCTR1, 1, // 12: Power control, 1 arg, no delay: + 0x0E, + ST77XX_INVOFF, 0, // 13: Don't invert display, no args + ST77XX_MADCTL, 1, // 14: Mem access ctl (directions), 1 arg: + 0xC8, // row/col addr, bottom-top refresh + ST77XX_COLMOD, 1, // 15: set color mode, 1 arg, no delay: + 0x05 +}; + +// Commands to include for the variant of the panel that has a green pull tab on +// the screen protector. +static const uint8_t kST7735RCommonGreen[]{ + 2, // 2 commands in list: + ST77XX_CASET, 4, // 1: Column addr set, 4 args, no delay: + 0x00, 0x02, // XSTART = 0 + 0x00, 0x7F+0x02, // XEND = 127 + ST77XX_RASET, 4, // 2: Row addr set, 4 args, no delay: + 0x00, 0x01, // XSTART = 0 + 0x00, 0x9F+0x01}; + +// Commands to include for the variant of the panel that has a red pull tab on +// the screen protector. +static const uint8_t kST7735RCommonRed[]{ + 3, // 2 commands in list: + ST77XX_CASET, 4, // 1: Column addr set, 4 args, no delay: + 0x00, 0x00, // XSTART = 0 + 0x00, 0x7F, // XEND = 127 + ST77XX_RASET, 4, // 2: Row addr set, 4 args, no delay: + 0x00, 0x00, // XSTART = 0 + 0x00, 0x9F, + ST77XX_MADCTL, 1, + 0xC0, +}; + +static const uint8_t kST7735RCommonFooter[]{ + 4, // 4 commands in list: + ST7735_GMCTRP1, 16 , // 1: Gamma Adjustments (pos. polarity), 16 args + delay: + 0x02, 0x1c, 0x07, 0x12, // (Not entirely necessary, but provides + 0x37, 0x32, 0x29, 0x2d, // accurate colors) + 0x29, 0x25, 0x2B, 0x39, + 0x00, 0x01, 0x03, 0x10, + ST7735_GMCTRN1, 16 , // 2: Gamma Adjustments (neg. polarity), 16 args + delay: + 0x03, 0x1d, 0x07, 0x06, // (Not entirely necessary, but provides + 0x2E, 0x2C, 0x29, 0x2D, // accurate colors) + 0x2E, 0x2E, 0x37, 0x3F, + 0x00, 0x00, 0x02, 0x10, + ST77XX_NORON, kDelayBit, // 3: Normal display on, no args, w/delay + 10, // 10 ms delay + ST77XX_DISPON, kDelayBit, // 4: Main screen turn on, no args w/delay + 100 +}; +// clang-format on + +const InitialisationData kST7735R = { + .num_sequences = 3, + .sequences = {kST7735RCommonHeader, kST7735RCommonRed, + kST7735RCommonFooter}}; + +} // namespace displays +} // namespace gay_ipod diff --git a/main/display-init.hpp b/main/display-init.hpp new file mode 100644 index 00000000..f11e9b57 --- /dev/null +++ b/main/display-init.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include + +namespace gay_ipod { +namespace displays { + +extern const uint8_t kDelayBit; + +struct InitialisationData { + uint8_t num_sequences; + const uint8_t* sequences[4]; +}; + +extern const InitialisationData kST7735R; + +/* + * Valid command bytes that can be sent to ST77XX displays, as well as commands + * for more specific variants. + */ +enum StCommands { + ST77XX_NOP = 0x00, + ST77XX_SWRESET = 0x01, + ST77XX_RDDID = 0x04, + ST77XX_RDDST = 0x09, + + ST77XX_SLPIN = 0x10, + ST77XX_SLPOUT = 0x11, + ST77XX_PTLON = 0x12, + ST77XX_NORON = 0x13, + + ST77XX_INVOFF = 0x20, + ST77XX_INVON = 0x21, + ST77XX_DISPOFF = 0x28, + ST77XX_DISPON = 0x29, + ST77XX_CASET = 0x2A, + ST77XX_RASET = 0x2B, + ST77XX_RAMWR = 0x2C, + ST77XX_RAMRD = 0x2E, + + ST77XX_PTLAR = 0x30, + ST77XX_TEOFF = 0x34, + ST77XX_TEON = 0x35, + ST77XX_MADCTL = 0x36, + ST77XX_COLMOD = 0x3A, + + ST77XX_MADCTL_MY = 0x80, + ST77XX_MADCTL_MX = 0x40, + ST77XX_MADCTL_MV = 0x20, + ST77XX_MADCTL_ML = 0x10, + ST77XX_MADCTL_RGB = 0x00, + + ST77XX_RDID1 = 0xDA, + ST77XX_RDID2 = 0xDB, + ST77XX_RDID3 = 0xDC, + ST77XX_RDID4 = 0xDD, + + ST7735_MADCTL_BGR = 0x08, + ST7735_MADCTL_MH = 0x04, + + ST7735_FRMCTR1 = 0xB1, + ST7735_FRMCTR2 = 0xB2, + ST7735_FRMCTR3 = 0xB3, + ST7735_INVCTR = 0xB4, + ST7735_DISSET5 = 0xB6, + + ST7735_PWCTR1 = 0xC0, + ST7735_PWCTR2 = 0xC1, + ST7735_PWCTR3 = 0xC2, + ST7735_PWCTR4 = 0xC3, + ST7735_PWCTR5 = 0xC4, + ST7735_VMCTR1 = 0xC5, + + ST7735_PWCTR6 = 0xFC, + + ST7735_GMCTRP1 = 0xE0, + ST7735_GMCTRN1 = 0xE1, +}; + +} // namespace displays +} // namespace gay_ipod diff --git a/main/display.cpp b/main/display.cpp index 82cc5b6e..8708436f 100644 --- a/main/display.cpp +++ b/main/display.cpp @@ -96,9 +96,9 @@ auto Display::create(GpioExpander* expander, .duty_cycle_pos = 0, // Unused .cs_ena_pretrans = 0, .cs_ena_posttrans = 0, - .clock_speed_hz = SPI_MASTER_FREQ_8M, - .input_delay_ns = 0, // TODO: tune? - .spics_io_num = -1, // TODO: change for R2 + .clock_speed_hz = SPI_MASTER_FREQ_40M, + .input_delay_ns = 0, + .spics_io_num = -1, // TODO: change for R2 .flags = 0, .queue_size = kTransactionQueueSize, .pre_cb = NULL, @@ -177,16 +177,17 @@ void Display::SendCommandWithData(uint8_t command, } void Display::SendCmd(const uint8_t* data, size_t length, uintptr_t flags) { - SendTransaction(data, length, flags | SEND_COMMAND); + SendTransaction(COMMAND, data, length, flags); } void Display::SendData(const uint8_t* data, size_t length, uintptr_t flags) { - SendTransaction(data, length, flags | SEND_DATA); + SendTransaction(DATA, data, length, flags); } -void Display::SendTransaction(const uint8_t* data, +void Display::SendTransaction(TransactionType type, + const uint8_t* data, size_t length, - uintptr_t flags) { + uint32_t flags) { if (length == 0) { return; } @@ -200,15 +201,12 @@ void Display::SendTransaction(const uint8_t* data, transaction->length = length * 8; transaction->rxlength = 0; // Match `length` value. - transaction->user = nullptr; // TODO. - // If the data to transmit is very short, then we can fit it directly // inside the transaction struct. if (length * 8 <= 32) { transaction->flags = SPI_TRANS_USE_TXDATA; std::memcpy(&transaction->tx_data, data, length); } else { - assert((flags & SMALL) == 0); // TODO: copy data to a DMA-capable transaction buffer transaction->tx_buffer = const_cast(data); } @@ -222,11 +220,7 @@ void Display::SendTransaction(const uint8_t* data, // ServiceTransactions(); - if (flags & SEND_COMMAND) { - gpio_set_level(kCommandOrDataPin, 0); - } else if (flags & SEND_DATA) { - gpio_set_level(kCommandOrDataPin, 1); - } + gpio_set_level(kCommandOrDataPin, type); gpio_->with([&](auto& gpio_) { gpio_.set_pin(GpioExpander::DISPLAY_CHIP_SELECT, 0); @@ -242,29 +236,26 @@ void Display::SendTransaction(const uint8_t* data, void Display::Flush(lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_map) { - // TODO: constants? based on init sequence? - uint16_t col_start = 2; - uint16_t row_start = 1; uint16_t data[2] = {0, 0}; - data[0] = SPI_SWAP_DATA_TX(area->x1 + row_start, 16); - data[1] = SPI_SWAP_DATA_TX(area->x2 + row_start, 16); - SendCommandWithData(displays::ST77XX_CASET, (uint8_t*)data, 4, SMALL); + data[0] = SPI_SWAP_DATA_TX(area->x1, 16); + data[1] = SPI_SWAP_DATA_TX(area->x2, 16); + SendCommandWithData(displays::ST77XX_CASET, (uint8_t*)data, 4); - data[0] = SPI_SWAP_DATA_TX(area->y1 + col_start, 16); - data[1] = SPI_SWAP_DATA_TX(area->y2 + col_start, 16); - SendCommandWithData(displays::ST77XX_RASET, (uint8_t*)data, 4, SMALL); + data[0] = SPI_SWAP_DATA_TX(area->y1, 16); + data[1] = SPI_SWAP_DATA_TX(area->y2, 16); + SendCommandWithData(displays::ST77XX_RASET, (uint8_t*)data, 4); uint32_t size = lv_area_get_width(area) * lv_area_get_height(area); SendCommandWithData(displays::ST77XX_RAMWR, (uint8_t*)color_map, size * 2, - FLUSH_BUFFER); + LVGL_FLUSH); // ESP_LOGI(kTag, "finished flush."); // lv_disp_flush_ready(&driver_); } void IRAM_ATTR Display::PostTransaction(const spi_transaction_t& transaction) { - if (reinterpret_cast(transaction.user) & FLUSH_BUFFER) { + if (reinterpret_cast(transaction.user) & LVGL_FLUSH) { lv_disp_flush_ready(&driver_); } } diff --git a/main/display.hpp b/main/display.hpp index 69605c72..2d6e9cd6 100644 --- a/main/display.hpp +++ b/main/display.hpp @@ -23,9 +23,11 @@ class Display { ~Display(); void WriteData(); + void Flush(lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_map); + void IRAM_ATTR PostTransaction(const spi_transaction_t& transaction); void ServiceTransactions(); @@ -38,11 +40,13 @@ class Display { lv_disp_drv_t driver_; lv_disp_t* display_ = nullptr; - enum TransactionData { - SEND_COMMAND = (1 << 0), - SEND_DATA = (1 << 1), - FLUSH_BUFFER = (1 << 2), - SMALL = (1 << 3), + enum TransactionType { + COMMAND = 0, + DATA = 1, + }; + + enum TransactionFlags { + LVGL_FLUSH = 1, }; void SendInitialisationSequence(const uint8_t* data); @@ -54,7 +58,10 @@ class Display { void SendCmd(const uint8_t* data, size_t length, uintptr_t flags = 0); void SendData(const uint8_t* data, size_t length, uintptr_t flags = 0); - void SendTransaction(const uint8_t* data, size_t length, uintptr_t flags = 0); + void SendTransaction(TransactionType type, + const uint8_t* data, + size_t length, + uintptr_t flags = 0); }; } // namespace gay_ipod diff --git a/main/gay-ipod-fw.cpp b/main/gay-ipod-fw.cpp index 8525b422..8d989146 100644 --- a/main/gay-ipod-fw.cpp +++ b/main/gay-ipod-fw.cpp @@ -191,7 +191,7 @@ extern "C" void app_main(void) { LvglArgs* lvglArgs = (LvglArgs*)calloc(1, sizeof(LvglArgs)); lvglArgs->gpio_expander = expander; - xTaskCreateStaticPinnedToCore(&lvgl_main, "LVGL", 8 * 1024, (void*)lvglArgs, + xTaskCreateStaticPinnedToCore(&lvgl_main, "LVGL", kLvglStackSize, (void*)lvglArgs, 1, sLvglStack, &sLvglTaskBuffer, 1); while (1) {