Merge pull request 'Support padding the left side of the display with black columns' (#302) from display_padding into main

Reviewed-on: https://codeberg.org/cool-tech-zone/tangara-fw/pulls/302
custom
cooljqln 1 month ago
commit 34e7ce869b
  1. 62
      src/drivers/display.cpp
  2. 1
      src/drivers/display_init.cpp
  3. 4
      src/drivers/include/drivers/display.hpp
  4. 1
      src/drivers/include/drivers/display_init.hpp
  5. 4
      src/drivers/include/drivers/nvs.hpp
  6. 14
      src/drivers/nvs.cpp
  7. 3
      src/tangara/ui/ui_fsm.cpp

@ -136,21 +136,35 @@ auto Display::Create(IGpios& expander,
spi_device_handle_t handle; spi_device_handle_t handle;
spi_bus_add_device(VSPI_HOST, &spi_cfg, &handle); spi_bus_add_device(VSPI_HOST, &spi_cfg, &handle);
auto display = std::make_unique<Display>(expander, handle); auto display = std::make_unique<Display>(expander, handle, init_data.pad);
// Now we reset the display into a known state, then configure it // Now we reset the display into a known state, then configure it
ESP_LOGI(kTag, "Sending init sequences"); 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++) { for (int i = 0; i < init_data.num_sequences; i++) {
display->SendInitialisationSequence(init_data.sequences[i]); display->SendInitialisationSequence(init_data.sequences[i]);
} }
display->WriteLeftPad(reinterpret_cast<uint8_t*>(kDisplayBuffer));
expander.SdMuxEnable(true);
spi_device_release_bus(handle);
// The hardware is now configured correctly. Next, initialise the LVGL display // The hardware is now configured correctly. Next, initialise the LVGL display
// driver. // driver.
ESP_LOGI(kTag, "Init buffers"); ESP_LOGI(kTag, "Init buffers");
assert(esp_ptr_dma_capable(kDisplayBuffer)); assert(esp_ptr_dma_capable(kDisplayBuffer));
ESP_LOGI(kTag, "Creating display"); 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, lv_display_set_buffers(display->display_, kDisplayBuffer, NULL,
sizeof(kDisplayBuffer), sizeof(kDisplayBuffer),
LV_DISPLAY_RENDER_MODE_PARTIAL); LV_DISPLAY_RENDER_MODE_PARTIAL);
@ -162,12 +176,13 @@ auto Display::Create(IGpios& expander,
return display.release(); 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), : gpio_(gpio),
handle_(handle), handle_(handle),
first_flush_finished_(false), first_flush_finished_(false),
display_on_(false), display_on_(false),
brightness_(0) {} brightness_(0),
pad_(pad) {}
Display::~Display() { Display::~Display() {
ledc_fade_func_uninstall(); 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) { 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. // First byte of the data is the number of commands.
for (int i = *(data++); i > 0; i--) { for (int i = *(data++); i > 0; i--) {
uint8_t command = *(data++); uint8_t command = *(data++);
@ -249,9 +257,33 @@ void Display::SendInitialisationSequence(const uint8_t* data) {
vTaskDelay(pdMS_TO_TICKS(sleep_duration_ms)); vTaskDelay(pdMS_TO_TICKS(sleep_duration_ms));
} }
} }
}
gpio_.SdMuxEnable(true); void Display::WriteLeftPad(uint8_t* buffer) {
spi_device_release_bus(handle_); 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<uint8_t*>(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<uint8_t*>(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 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. // First we need to specify the rectangle of the display we're writing into.
uint16_t data[2] = {0, 0}; uint16_t data[2] = {0, 0};
data[0] = SPI_SWAP_DATA_TX(area->x1, 16); data[0] = SPI_SWAP_DATA_TX(area->x1 + pad_, 16);
data[1] = SPI_SWAP_DATA_TX(area->x2, 16); data[1] = SPI_SWAP_DATA_TX(area->x2 + pad_, 16);
SendCommandWithData(displays::ST77XX_CASET, reinterpret_cast<uint8_t*>(data), SendCommandWithData(displays::ST77XX_CASET, reinterpret_cast<uint8_t*>(data),
4); 4);

@ -103,6 +103,7 @@ static const uint8_t kST7735RCommonFooter[]{
const InitialisationData kST7735R = { const InitialisationData kST7735R = {
.width = 160, .width = 160,
.height = 128, .height = 128,
.pad = 0,
.num_sequences = 3, .num_sequences = 3,
.sequences = {kST7735RCommonHeader, kST7735RCommonGreen, .sequences = {kST7735RCommonHeader, kST7735RCommonGreen,
kST7735RCommonFooter}}; kST7735RCommonFooter}};

@ -34,7 +34,7 @@ class Display {
static auto Create(IGpios& expander, static auto Create(IGpios& expander,
const displays::InitialisationData& init_data) -> Display*; 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(); ~Display();
auto SetDisplayOn(bool) -> void; auto SetDisplayOn(bool) -> void;
@ -54,6 +54,7 @@ class Display {
bool first_flush_finished_; bool first_flush_finished_;
bool display_on_; bool display_on_;
uint_fast8_t brightness_; uint_fast8_t brightness_;
uint_fast8_t pad_;
lv_display_t* display_ = nullptr; lv_display_t* display_ = nullptr;
@ -63,6 +64,7 @@ class Display {
}; };
void SendInitialisationSequence(const uint8_t* data); void SendInitialisationSequence(const uint8_t* data);
void WriteLeftPad(uint8_t *buffer);
void SendCommandWithData(uint8_t command, const uint8_t* data, size_t length); void SendCommandWithData(uint8_t command, const uint8_t* data, size_t length);

@ -17,6 +17,7 @@ extern const uint8_t kDelayBit;
struct InitialisationData { struct InitialisationData {
uint16_t width; uint16_t width;
uint16_t height; uint16_t height;
uint8_t pad;
uint8_t num_sequences; uint8_t num_sequences;
const uint8_t* sequences[4]; const uint8_t* sequences[4];
}; };

@ -93,6 +93,9 @@ class NvsStorage {
auto FastCharge() -> bool; auto FastCharge() -> bool;
auto FastCharge(bool) -> void; auto FastCharge(bool) -> void;
auto DisplayLeftPadding() -> uint8_t;
auto DisplayLeftPadding(uint8_t) -> void;
auto PreferredBluetoothDevice() -> std::optional<bluetooth::MacAndName>; auto PreferredBluetoothDevice() -> std::optional<bluetooth::MacAndName>;
auto PreferredBluetoothDevice(std::optional<bluetooth::MacAndName>) -> void; auto PreferredBluetoothDevice(std::optional<bluetooth::MacAndName>) -> void;
@ -177,6 +180,7 @@ class NvsStorage {
Setting<uint8_t> lock_polarity_; Setting<uint8_t> lock_polarity_;
Setting<uint16_t> display_cols_; Setting<uint16_t> display_cols_;
Setting<uint16_t> display_rows_; Setting<uint16_t> display_rows_;
Setting<uint8_t> display_left_padding_;
Setting<uint8_t> haptic_motor_type_; Setting<uint8_t> haptic_motor_type_;
Setting<LraData> lra_calibration_; Setting<LraData> lra_calibration_;
Setting<uint8_t> fast_charge_; Setting<uint8_t> fast_charge_;

@ -41,6 +41,7 @@ static constexpr char kKeyScrollSensitivity[] = "scroll";
static constexpr char kKeyLockPolarity[] = "lockpol"; static constexpr char kKeyLockPolarity[] = "lockpol";
static constexpr char kKeyDisplayCols[] = "dispcols"; static constexpr char kKeyDisplayCols[] = "dispcols";
static constexpr char kKeyDisplayRows[] = "disprows"; static constexpr char kKeyDisplayRows[] = "disprows";
static constexpr char kKeyDisplayLeftPadding[] = "displeftpad";
static constexpr char kKeyHapticMotorType[] = "hapticmtype"; static constexpr char kKeyHapticMotorType[] = "hapticmtype";
static constexpr char kKeyLraCalibration[] = "lra_cali"; static constexpr char kKeyLraCalibration[] = "lra_cali";
static constexpr char kKeyDbAutoIndex[] = "dbautoindex"; static constexpr char kKeyDbAutoIndex[] = "dbautoindex";
@ -266,6 +267,7 @@ NvsStorage::NvsStorage(nvs_handle_t handle)
lock_polarity_(kKeyLockPolarity), lock_polarity_(kKeyLockPolarity),
display_cols_(kKeyDisplayCols), display_cols_(kKeyDisplayCols),
display_rows_(kKeyDisplayRows), display_rows_(kKeyDisplayRows),
display_left_padding_(kKeyDisplayLeftPadding),
haptic_motor_type_(kKeyHapticMotorType), haptic_motor_type_(kKeyHapticMotorType),
lra_calibration_(kKeyLraCalibration), lra_calibration_(kKeyLraCalibration),
fast_charge_(kKeyFastCharge), fast_charge_(kKeyFastCharge),
@ -297,6 +299,7 @@ auto NvsStorage::Read() -> void {
lock_polarity_.read(handle_); lock_polarity_.read(handle_);
display_cols_.read(handle_); display_cols_.read(handle_);
display_rows_.read(handle_); display_rows_.read(handle_);
display_left_padding_.read(handle_);
haptic_motor_type_.read(handle_); haptic_motor_type_.read(handle_);
lra_calibration_.read(handle_); lra_calibration_.read(handle_);
fast_charge_.read(handle_); fast_charge_.read(handle_);
@ -323,6 +326,7 @@ auto NvsStorage::Write() -> bool {
lock_polarity_.write(handle_); lock_polarity_.write(handle_);
display_cols_.write(handle_); display_cols_.write(handle_);
display_rows_.write(handle_); display_rows_.write(handle_);
display_left_padding_.write(handle_);
haptic_motor_type_.write(handle_); haptic_motor_type_.write(handle_);
lra_calibration_.write(handle_); lra_calibration_.write(handle_);
fast_charge_.write(handle_); fast_charge_.write(handle_);
@ -403,6 +407,16 @@ auto NvsStorage::DisplaySize(
display_rows_.set(std::move(size.second)); display_rows_.set(std::move(size.second));
} }
auto NvsStorage::DisplayLeftPadding() -> uint8_t {
std::lock_guard<std::mutex> lock{mutex_};
return display_left_padding_.get().value_or(0);
}
auto NvsStorage::DisplayLeftPadding(uint8_t val) -> void {
std::lock_guard<std::mutex> lock{mutex_};
display_left_padding_.set(val);
}
auto NvsStorage::PreferredBluetoothDevice() auto NvsStorage::PreferredBluetoothDevice()
-> std::optional<bluetooth::MacAndName> { -> std::optional<bluetooth::MacAndName> {
std::lock_guard<std::mutex> lock{mutex_}; std::lock_guard<std::mutex> lock{mutex_};

@ -352,10 +352,13 @@ void UiState::react(const internal::InitDisplay& ev) {
// HACK: correct the display size for our prototypes. // HACK: correct the display size for our prototypes.
// ev.nvs.DisplaySize({161, 130}); // ev.nvs.DisplaySize({161, 130});
// HACK: correct the display padding for batch 2.
// ev.nvs.DisplayLeftPadding(3);
auto actual_size = ev.nvs.DisplaySize(); auto actual_size = ev.nvs.DisplaySize();
init_data.width = actual_size.first.value_or(init_data.width); init_data.width = actual_size.first.value_or(init_data.width);
init_data.height = actual_size.second.value_or(init_data.height); 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)); sDisplay.reset(drivers::Display::Create(ev.gpios, init_data));
sCurrentScreen.reset(new screens::Splash()); sCurrentScreen.reset(new screens::Splash());

Loading…
Cancel
Save