Files
Juli 33089b95ab Wheel side setting: rotate the display 180° for left-handed use
Hardware mirror in the ST7789 (rotation 3 <-> rotation 1); the 35-px
window offset is the same both ways. Stored in settings v3.
2026-09-17 15:52:56 +02:00

111 lines
4.1 KiB
C

#include "display.h"
#include "board.h"
#include "sdkconfig.h"
#include "driver/spi_master.h"
#include "driver/ledc.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lvgl_port.h"
#include "esp_log.h"
static const char *TAG = "display";
#define LCD_SPI_HOST SPI2_HOST
#define BL_TIMER LEDC_TIMER_0
#define BL_CHANNEL LEDC_CHANNEL_0
static esp_lcd_panel_handle_t panel;
static void backlight_init(void)
{
ledc_timer_config_t timer = {
.speed_mode = LEDC_LOW_SPEED_MODE, .duty_resolution = LEDC_TIMER_10_BIT,
.timer_num = BL_TIMER, .freq_hz = 5000, .clk_cfg = LEDC_AUTO_CLK,
};
ESP_ERROR_CHECK(ledc_timer_config(&timer));
ledc_channel_config_t ch = {
.gpio_num = board->lcd_bl, .speed_mode = LEDC_LOW_SPEED_MODE, .channel = BL_CHANNEL,
.timer_sel = BL_TIMER, .duty = 0, .hpoint = 0,
};
ESP_ERROR_CHECK(ledc_channel_config(&ch));
}
void display_set_backlight(uint8_t percent)
{
if (percent > 100) percent = 100;
// perceptual-ish curve so 50% doesn't look like 90%
uint32_t duty = (1023u * percent * percent) / 10000u;
ledc_set_duty(LEDC_LOW_SPEED_MODE, BL_CHANNEL, duty);
ledc_update_duty(LEDC_LOW_SPEED_MODE, BL_CHANNEL);
}
void display_init(void)
{
backlight_init();
spi_bus_config_t bus = {
.mosi_io_num = board->lcd_mosi, .miso_io_num = board->lcd_miso, .sclk_io_num = board->lcd_sclk,
.quadwp_io_num = -1, .quadhd_io_num = -1,
.max_transfer_sz = LCD_H_RES * 48 * sizeof(uint16_t),
};
ESP_ERROR_CHECK(spi_bus_initialize(LCD_SPI_HOST, &bus, SPI_DMA_CH_AUTO));
esp_lcd_panel_io_handle_t io = NULL;
esp_lcd_panel_io_spi_config_t io_cfg = {
.cs_gpio_num = board->lcd_cs, .dc_gpio_num = board->lcd_dc,
.spi_mode = 0, .pclk_hz = CONFIG_WIDGET_LCD_PCLK_MHZ * 1000 * 1000,
.trans_queue_depth = 10, .lcd_cmd_bits = 8, .lcd_param_bits = 8,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_SPI_HOST, &io_cfg, &io));
esp_lcd_panel_dev_config_t panel_cfg = {
.reset_gpio_num = board->lcd_rst,
.rgb_ele_order = board->lcd_bgr ? LCD_RGB_ELEMENT_ORDER_BGR : LCD_RGB_ELEMENT_ORDER_RGB,
.bits_per_pixel = 16,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io, &panel_cfg, &panel));
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel));
ESP_ERROR_CHECK(esp_lcd_panel_init(panel));
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel, true)); // IPS panel, TFT_INVERSION_ON
// TFT_eSPI setRotation(3) == MADCTL MV|MY; the 170 px axis sits at RAM column 35.
ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel, true));
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel, false, true));
ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel, 0, 35));
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel, true));
lvgl_port_cfg_t port_cfg = ESP_LVGL_PORT_INIT_CONFIG();
port_cfg.task_stack = 10240;
port_cfg.task_affinity = 1;
ESP_ERROR_CHECK(lvgl_port_init(&port_cfg));
lvgl_port_display_cfg_t disp_cfg = {
.io_handle = io, .panel_handle = panel,
.buffer_size = LCD_H_RES * 40, .double_buffer = true,
.hres = LCD_H_RES, .vres = LCD_V_RES, .monochrome = false,
.rotation = {.swap_xy = true, .mirror_x = false, .mirror_y = true},
.color_format = LV_COLOR_FORMAT_RGB565,
.flags = {.buff_dma = true, .buff_spiram = false, .swap_bytes = true},
};
lv_display_t *disp = lvgl_port_add_disp(&disp_cfg);
if (!disp) { ESP_LOGE(TAG, "lvgl_port_add_disp failed"); abort(); }
ESP_LOGI(TAG, "ST7789 %dx%d up at %d MHz", LCD_H_RES, LCD_V_RES, CONFIG_WIDGET_LCD_PCLK_MHZ);
}
void display_off(void)
{
display_set_backlight(0);
if (panel) esp_lcd_panel_disp_on_off(panel, false);
}
void display_set_flipped(bool flipped)
{
if (!panel) return;
// Rotation 3 (MV|MY) is the stock orientation; rotation 1 (MV|MX) is its 180° twin.
// The 170-px window is centred in the 240-column RAM, so the 35-px gap is the same both ways.
lvgl_port_lock(0);
esp_lcd_panel_mirror(panel, flipped, !flipped);
lv_obj_invalidate(lv_screen_active());
lvgl_port_unlock();
}