diff --git a/src/drivers/display.cpp b/src/drivers/display.cpp index 2fcb9dc0..0ae37688 100644 --- a/src/drivers/display.cpp +++ b/src/drivers/display.cpp @@ -136,21 +136,35 @@ auto Display::Create(IGpios& expander, spi_device_handle_t handle; spi_bus_add_device(VSPI_HOST, &spi_cfg, &handle); - auto display = std::make_unique(expander, handle); + auto display = std::make_unique(expander, handle, init_data.pad); // Now we reset the display into a known state, then configure it ESP_LOGI(kTag, "Sending init sequences"); + + // Hold the SPI bus for the entire init sequence, as otherwise SD init may + // grab it and delay showing the boot splash. The total time until boot is + // finished may be increased by doing this, but a short boot with no feedback + // feels worse than a longer boot that doesn't tell you anything. + spi_device_acquire_bus(handle, portMAX_DELAY); + expander.SdMuxEnable(false); + for (int i = 0; i < init_data.num_sequences; i++) { display->SendInitialisationSequence(init_data.sequences[i]); } + display->WriteLeftPad(reinterpret_cast(kDisplayBuffer)); + + expander.SdMuxEnable(true); + spi_device_release_bus(handle); + // The hardware is now configured correctly. Next, initialise the LVGL display // driver. ESP_LOGI(kTag, "Init buffers"); assert(esp_ptr_dma_capable(kDisplayBuffer)); ESP_LOGI(kTag, "Creating display"); - display->display_ = lv_display_create(init_data.width, init_data.height); + display->display_ = + lv_display_create(init_data.width - init_data.pad, init_data.height); lv_display_set_buffers(display->display_, kDisplayBuffer, NULL, sizeof(kDisplayBuffer), LV_DISPLAY_RENDER_MODE_PARTIAL); @@ -162,12 +176,13 @@ auto Display::Create(IGpios& expander, return display.release(); } -Display::Display(IGpios& gpio, spi_device_handle_t handle) +Display::Display(IGpios& gpio, spi_device_handle_t handle, uint_fast8_t pad) : gpio_(gpio), handle_(handle), first_flush_finished_(false), display_on_(false), - brightness_(0) {} + brightness_(0), + pad_(pad) {} Display::~Display() { ledc_fade_func_uninstall(); @@ -223,13 +238,6 @@ auto Display::SetDutyCycle(uint_fast8_t new_duty, bool fade) -> void { } void Display::SendInitialisationSequence(const uint8_t* data) { - // Hold the SPI bus for the entire init sequence, as otherwise SD init may - // grab it and delay showing the boot splash. The total time until boot is - // finished may be increased by doing this, but a short boot with no feedback - // feels worse than a longer boot that doesn't tell you anything. - spi_device_acquire_bus(handle_, portMAX_DELAY); - gpio_.SdMuxEnable(false); - // First byte of the data is the number of commands. for (int i = *(data++); i > 0; i--) { uint8_t command = *(data++); @@ -249,9 +257,33 @@ void Display::SendInitialisationSequence(const uint8_t* data) { vTaskDelay(pdMS_TO_TICKS(sleep_duration_ms)); } } +} - gpio_.SdMuxEnable(true); - spi_device_release_bus(handle_); +void Display::WriteLeftPad(uint8_t* buffer) { + if (pad_ == 0) { + return; + } + + uint16_t data1[2] = {0, 0}; + + // Select the left pad_ cols + data1[0] = SPI_SWAP_DATA_TX(0, 16); + data1[1] = SPI_SWAP_DATA_TX(pad_, 16); + SendCommandWithData(displays::ST77XX_CASET, reinterpret_cast(data1), + 4); + + // Select all rows + data1[0] = SPI_SWAP_DATA_TX(0, 16); + data1[1] = SPI_SWAP_DATA_TX(128, 16); + SendCommandWithData(displays::ST77XX_RASET, reinterpret_cast(data1), + 4); + + // Prep our beautiful zeroes. + size_t len = pad_ * 128 * 3; + std::memset(buffer, 0, len); + + // Write our beautiful zeroes. + SendCommandWithData(displays::ST77XX_RAMWR, buffer, len); } IRAM_ATTR @@ -309,8 +341,8 @@ void Display::OnLvglFlush(const lv_area_t* area, uint8_t* color_map) { // First we need to specify the rectangle of the display we're writing into. uint16_t data[2] = {0, 0}; - data[0] = SPI_SWAP_DATA_TX(area->x1, 16); - data[1] = SPI_SWAP_DATA_TX(area->x2, 16); + data[0] = SPI_SWAP_DATA_TX(area->x1 + pad_, 16); + data[1] = SPI_SWAP_DATA_TX(area->x2 + pad_, 16); SendCommandWithData(displays::ST77XX_CASET, reinterpret_cast(data), 4); diff --git a/src/drivers/display_init.cpp b/src/drivers/display_init.cpp index edd36a8d..62cd18dd 100644 --- a/src/drivers/display_init.cpp +++ b/src/drivers/display_init.cpp @@ -103,6 +103,7 @@ static const uint8_t kST7735RCommonFooter[]{ const InitialisationData kST7735R = { .width = 160, .height = 128, + .pad = 0, .num_sequences = 3, .sequences = {kST7735RCommonHeader, kST7735RCommonGreen, kST7735RCommonFooter}}; diff --git a/src/drivers/include/drivers/display.hpp b/src/drivers/include/drivers/display.hpp index e5001c48..88bb07c4 100644 --- a/src/drivers/include/drivers/display.hpp +++ b/src/drivers/include/drivers/display.hpp @@ -34,7 +34,7 @@ class Display { static auto Create(IGpios& expander, const displays::InitialisationData& init_data) -> Display*; - Display(IGpios& gpio, spi_device_handle_t handle); + Display(IGpios& gpio, spi_device_handle_t handle, uint_fast8_t pad); ~Display(); auto SetDisplayOn(bool) -> void; @@ -54,6 +54,7 @@ class Display { bool first_flush_finished_; bool display_on_; uint_fast8_t brightness_; + uint_fast8_t pad_; lv_display_t* display_ = nullptr; @@ -63,6 +64,7 @@ class Display { }; void SendInitialisationSequence(const uint8_t* data); + void WriteLeftPad(uint8_t *buffer); void SendCommandWithData(uint8_t command, const uint8_t* data, size_t length); diff --git a/src/drivers/include/drivers/display_init.hpp b/src/drivers/include/drivers/display_init.hpp index 9bf5b3f5..6ccdb417 100644 --- a/src/drivers/include/drivers/display_init.hpp +++ b/src/drivers/include/drivers/display_init.hpp @@ -17,6 +17,7 @@ extern const uint8_t kDelayBit; struct InitialisationData { uint16_t width; uint16_t height; + uint8_t pad; uint8_t num_sequences; const uint8_t* sequences[4]; }; diff --git a/src/drivers/include/drivers/nvs.hpp b/src/drivers/include/drivers/nvs.hpp index 21d32f42..7ef1fbf7 100644 --- a/src/drivers/include/drivers/nvs.hpp +++ b/src/drivers/include/drivers/nvs.hpp @@ -93,6 +93,9 @@ class NvsStorage { auto FastCharge() -> bool; auto FastCharge(bool) -> void; + auto DisplayLeftPadding() -> uint8_t; + auto DisplayLeftPadding(uint8_t) -> void; + auto PreferredBluetoothDevice() -> std::optional; auto PreferredBluetoothDevice(std::optional) -> void; @@ -177,6 +180,7 @@ class NvsStorage { Setting lock_polarity_; Setting display_cols_; Setting display_rows_; + Setting display_left_padding_; Setting haptic_motor_type_; Setting lra_calibration_; Setting fast_charge_; diff --git a/src/drivers/nvs.cpp b/src/drivers/nvs.cpp index 3250e556..374c71d6 100644 --- a/src/drivers/nvs.cpp +++ b/src/drivers/nvs.cpp @@ -41,6 +41,7 @@ static constexpr char kKeyScrollSensitivity[] = "scroll"; static constexpr char kKeyLockPolarity[] = "lockpol"; static constexpr char kKeyDisplayCols[] = "dispcols"; static constexpr char kKeyDisplayRows[] = "disprows"; +static constexpr char kKeyDisplayLeftPadding[] = "displeftpad"; static constexpr char kKeyHapticMotorType[] = "hapticmtype"; static constexpr char kKeyLraCalibration[] = "lra_cali"; static constexpr char kKeyDbAutoIndex[] = "dbautoindex"; @@ -266,6 +267,7 @@ NvsStorage::NvsStorage(nvs_handle_t handle) lock_polarity_(kKeyLockPolarity), display_cols_(kKeyDisplayCols), display_rows_(kKeyDisplayRows), + display_left_padding_(kKeyDisplayLeftPadding), haptic_motor_type_(kKeyHapticMotorType), lra_calibration_(kKeyLraCalibration), fast_charge_(kKeyFastCharge), @@ -297,6 +299,7 @@ auto NvsStorage::Read() -> void { lock_polarity_.read(handle_); display_cols_.read(handle_); display_rows_.read(handle_); + display_left_padding_.read(handle_); haptic_motor_type_.read(handle_); lra_calibration_.read(handle_); fast_charge_.read(handle_); @@ -323,6 +326,7 @@ auto NvsStorage::Write() -> bool { lock_polarity_.write(handle_); display_cols_.write(handle_); display_rows_.write(handle_); + display_left_padding_.write(handle_); haptic_motor_type_.write(handle_); lra_calibration_.write(handle_); fast_charge_.write(handle_); @@ -403,6 +407,16 @@ auto NvsStorage::DisplaySize( display_rows_.set(std::move(size.second)); } +auto NvsStorage::DisplayLeftPadding() -> uint8_t { + std::lock_guard lock{mutex_}; + return display_left_padding_.get().value_or(0); +} + +auto NvsStorage::DisplayLeftPadding(uint8_t val) -> void { + std::lock_guard lock{mutex_}; + display_left_padding_.set(val); +} + auto NvsStorage::PreferredBluetoothDevice() -> std::optional { std::lock_guard lock{mutex_}; diff --git a/src/tangara/ui/ui_fsm.cpp b/src/tangara/ui/ui_fsm.cpp index ebc4b635..020917e2 100644 --- a/src/tangara/ui/ui_fsm.cpp +++ b/src/tangara/ui/ui_fsm.cpp @@ -352,10 +352,13 @@ void UiState::react(const internal::InitDisplay& ev) { // HACK: correct the display size for our prototypes. // ev.nvs.DisplaySize({161, 130}); + // HACK: correct the display padding for batch 2. + // ev.nvs.DisplayLeftPadding(3); auto actual_size = ev.nvs.DisplaySize(); init_data.width = actual_size.first.value_or(init_data.width); init_data.height = actual_size.second.value_or(init_data.height); + init_data.pad = ev.nvs.DisplayLeftPadding(); sDisplay.reset(drivers::Display::Create(ev.gpios, init_data)); sCurrentScreen.reset(new screens::Splash());