feat(platform): add PWM backlight brightness control
This commit is contained in:
@@ -36,6 +36,7 @@ set(platform_priv_requires
|
|||||||
esp_driver_gpio
|
esp_driver_gpio
|
||||||
esp_driver_i2c
|
esp_driver_i2c
|
||||||
esp_driver_i2s
|
esp_driver_i2s
|
||||||
|
esp_driver_ledc
|
||||||
esp_driver_rmt
|
esp_driver_rmt
|
||||||
esp_driver_spi
|
esp_driver_spi
|
||||||
esp_event
|
esp_event
|
||||||
|
|||||||
@@ -452,6 +452,15 @@ esp_err_t platform_multimodal_audio_register_downstream_playback_callbacks(
|
|||||||
|
|
||||||
esp_err_t platform_init_lcd_device(void);
|
esp_err_t platform_init_lcd_device(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set LCD backlight brightness using PWM.
|
||||||
|
*
|
||||||
|
* @param brightness_percent Brightness from 0 (off) to 100 (full).
|
||||||
|
* @return ESP_OK on success, ESP_ERR_INVALID_ARG outside the supported range,
|
||||||
|
* or ESP_ERR_INVALID_STATE before LCD initialization.
|
||||||
|
*/
|
||||||
|
esp_err_t platform_lcd_set_backlight_brightness(uint8_t brightness_percent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Start LCD render loop.
|
* @brief Start LCD render loop.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
esp_err_t lcd_direct_init_device(void);
|
esp_err_t lcd_direct_init_device(void);
|
||||||
|
esp_err_t lcd_direct_set_backlight_brightness(uint8_t brightness_percent);
|
||||||
void lcd_direct_start_render_loop(void);
|
void lcd_direct_start_render_loop(void);
|
||||||
esp_err_t lcd_direct_stop_render_loop(int timeout_ms);
|
esp_err_t lcd_direct_stop_render_loop(int timeout_ms);
|
||||||
esp_err_t lcd_direct_set_status_overlay(const platform_lcd_status_overlay_t *overlay);
|
esp_err_t lcd_direct_set_status_overlay(const platform_lcd_status_overlay_t *overlay);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "driver/gpio.h"
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/ledc.h"
|
||||||
#include "driver/spi_master.h"
|
#include "driver/spi_master.h"
|
||||||
#include "esp_attr.h"
|
#include "esp_attr.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
@@ -58,6 +59,12 @@
|
|||||||
#define LCD_DIRECT_LAST_FRAME_EXTRA_HOLD_MS 200U
|
#define LCD_DIRECT_LAST_FRAME_EXTRA_HOLD_MS 200U
|
||||||
#define LCD_DIRECT_BASIC_FACE_FRAME_HOLD_MS 100U
|
#define LCD_DIRECT_BASIC_FACE_FRAME_HOLD_MS 100U
|
||||||
#define LCD_DIRECT_BASIC_FACE_LAST_FRAME_HOLD_MS 100U
|
#define LCD_DIRECT_BASIC_FACE_LAST_FRAME_HOLD_MS 100U
|
||||||
|
#define LCD_DIRECT_BACKLIGHT_PWM_FREQUENCY_HZ 20000U
|
||||||
|
#define LCD_DIRECT_BACKLIGHT_PWM_DUTY_RESOLUTION LEDC_TIMER_10_BIT
|
||||||
|
#define LCD_DIRECT_BACKLIGHT_PWM_DUTY_MAX ((1U << 10U) - 1U)
|
||||||
|
#define LCD_DIRECT_BACKLIGHT_PWM_TIMER LEDC_TIMER_0
|
||||||
|
#define LCD_DIRECT_BACKLIGHT_PWM_CHANNEL LEDC_CHANNEL_0
|
||||||
|
#define LCD_DIRECT_BACKLIGHT_LOCK_TIMEOUT_MS 100U
|
||||||
|
|
||||||
#define LCD_CMD_MADCTL 0x36
|
#define LCD_CMD_MADCTL 0x36
|
||||||
#define LCD_CMD_RAMCTRL 0xB0
|
#define LCD_CMD_RAMCTRL 0xB0
|
||||||
@@ -133,6 +140,7 @@ static const lcd_direct_init_cmd_t s_st7789_init_cmds[] = {
|
|||||||
static esp_lcd_panel_io_handle_t s_panel_io;
|
static esp_lcd_panel_io_handle_t s_panel_io;
|
||||||
static esp_lcd_panel_handle_t s_panel;
|
static esp_lcd_panel_handle_t s_panel;
|
||||||
static SemaphoreHandle_t s_lock;
|
static SemaphoreHandle_t s_lock;
|
||||||
|
static SemaphoreHandle_t s_backlight_lock;
|
||||||
static SemaphoreHandle_t s_completion_sem;
|
static SemaphoreHandle_t s_completion_sem;
|
||||||
static SemaphoreHandle_t s_preheat_done_sem;
|
static SemaphoreHandle_t s_preheat_done_sem;
|
||||||
static SemaphoreHandle_t s_transfer_done_sem;
|
static SemaphoreHandle_t s_transfer_done_sem;
|
||||||
@@ -465,10 +473,49 @@ static esp_err_t lcd_direct_get_png_image(const char *path,
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t lcd_direct_backlight_duty(uint8_t brightness_percent)
|
||||||
|
{
|
||||||
|
uint32_t duty = ((uint32_t)brightness_percent * LCD_DIRECT_BACKLIGHT_PWM_DUTY_MAX + 50U) / 100U;
|
||||||
|
#if !CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH
|
||||||
|
duty = LCD_DIRECT_BACKLIGHT_PWM_DUTY_MAX - duty;
|
||||||
|
#endif
|
||||||
|
return duty;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t lcd_direct_backlight_init(void)
|
||||||
|
{
|
||||||
|
const ledc_timer_config_t timer_config = {
|
||||||
|
.speed_mode = LEDC_LOW_SPEED_MODE,
|
||||||
|
.duty_resolution = LCD_DIRECT_BACKLIGHT_PWM_DUTY_RESOLUTION,
|
||||||
|
.timer_num = LCD_DIRECT_BACKLIGHT_PWM_TIMER,
|
||||||
|
.freq_hz = LCD_DIRECT_BACKLIGHT_PWM_FREQUENCY_HZ,
|
||||||
|
.clk_cfg = LEDC_AUTO_CLK,
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(ledc_timer_config(&timer_config), TAG, "backlight PWM timer config failed");
|
||||||
|
|
||||||
|
const ledc_channel_config_t channel_config = {
|
||||||
|
.gpio_num = CONFIG_TQ_SCREEN_BACKLIGHT_GPIO,
|
||||||
|
.speed_mode = LEDC_LOW_SPEED_MODE,
|
||||||
|
.channel = LCD_DIRECT_BACKLIGHT_PWM_CHANNEL,
|
||||||
|
.intr_type = LEDC_INTR_DISABLE,
|
||||||
|
.timer_sel = LCD_DIRECT_BACKLIGHT_PWM_TIMER,
|
||||||
|
.duty = lcd_direct_backlight_duty(CONFIG_TQ_SCREEN_BACKLIGHT_BRIGHTNESS_PERCENT),
|
||||||
|
.hpoint = 0,
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(ledc_channel_config(&channel_config), TAG, "backlight PWM channel config failed");
|
||||||
|
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"Backlight PWM initialized gpio=%d frequency=%uHz brightness=%d%% active_high=%d",
|
||||||
|
CONFIG_TQ_SCREEN_BACKLIGHT_GPIO,
|
||||||
|
(unsigned)LCD_DIRECT_BACKLIGHT_PWM_FREQUENCY_HZ,
|
||||||
|
CONFIG_TQ_SCREEN_BACKLIGHT_BRIGHTNESS_PERCENT,
|
||||||
|
CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH ? 1 : 0);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static esp_err_t lcd_direct_gpio_init(void)
|
static esp_err_t lcd_direct_gpio_init(void)
|
||||||
{
|
{
|
||||||
uint64_t pin_mask = (1ULL << CONFIG_TQ_SCREEN_BACKLIGHT_GPIO) |
|
uint64_t pin_mask = (1ULL << CONFIG_TQ_SCREEN_DC_GPIO);
|
||||||
(1ULL << CONFIG_TQ_SCREEN_DC_GPIO);
|
|
||||||
if (CONFIG_TQ_SCREEN_RESET_PIN >= 0) {
|
if (CONFIG_TQ_SCREEN_RESET_PIN >= 0) {
|
||||||
pin_mask |= (1ULL << CONFIG_TQ_SCREEN_RESET_PIN);
|
pin_mask |= (1ULL << CONFIG_TQ_SCREEN_RESET_PIN);
|
||||||
}
|
}
|
||||||
@@ -482,13 +529,11 @@ static esp_err_t lcd_direct_gpio_init(void)
|
|||||||
};
|
};
|
||||||
ESP_RETURN_ON_ERROR(gpio_config(&gpio_config_data), TAG, "gpio config failed");
|
ESP_RETURN_ON_ERROR(gpio_config(&gpio_config_data), TAG, "gpio config failed");
|
||||||
|
|
||||||
gpio_set_level(CONFIG_TQ_SCREEN_BACKLIGHT_GPIO,
|
|
||||||
CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH ? 1 : 0);
|
|
||||||
gpio_set_level(CONFIG_TQ_SCREEN_DC_GPIO, 0);
|
gpio_set_level(CONFIG_TQ_SCREEN_DC_GPIO, 0);
|
||||||
if (CONFIG_TQ_SCREEN_RESET_PIN >= 0) {
|
if (CONFIG_TQ_SCREEN_RESET_PIN >= 0) {
|
||||||
gpio_set_level(CONFIG_TQ_SCREEN_RESET_PIN, 1);
|
gpio_set_level(CONFIG_TQ_SCREEN_RESET_PIN, 1);
|
||||||
}
|
}
|
||||||
return ESP_OK;
|
return lcd_direct_backlight_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t lcd_direct_panel_init(void)
|
static esp_err_t lcd_direct_panel_init(void)
|
||||||
@@ -1301,6 +1346,11 @@ esp_err_t lcd_direct_init_device(void)
|
|||||||
|
|
||||||
s_lock = xSemaphoreCreateMutex();
|
s_lock = xSemaphoreCreateMutex();
|
||||||
ESP_RETURN_ON_FALSE(s_lock != NULL, ESP_ERR_NO_MEM, TAG, "lock create failed");
|
ESP_RETURN_ON_FALSE(s_lock != NULL, ESP_ERR_NO_MEM, TAG, "lock create failed");
|
||||||
|
s_backlight_lock = xSemaphoreCreateMutex();
|
||||||
|
ESP_RETURN_ON_FALSE(s_backlight_lock != NULL,
|
||||||
|
ESP_ERR_NO_MEM,
|
||||||
|
TAG,
|
||||||
|
"backlight lock create failed");
|
||||||
s_completion_sem = xSemaphoreCreateBinary();
|
s_completion_sem = xSemaphoreCreateBinary();
|
||||||
ESP_RETURN_ON_FALSE(s_completion_sem != NULL, ESP_ERR_NO_MEM, TAG, "completion sem create failed");
|
ESP_RETURN_ON_FALSE(s_completion_sem != NULL, ESP_ERR_NO_MEM, TAG, "completion sem create failed");
|
||||||
s_preheat_done_sem = xSemaphoreCreateBinary();
|
s_preheat_done_sem = xSemaphoreCreateBinary();
|
||||||
@@ -1331,6 +1381,38 @@ esp_err_t lcd_direct_init_device(void)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t lcd_direct_set_backlight_brightness(uint8_t brightness_percent)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(brightness_percent <= 100U,
|
||||||
|
ESP_ERR_INVALID_ARG,
|
||||||
|
TAG,
|
||||||
|
"backlight brightness must be between 0 and 100 percent");
|
||||||
|
ESP_RETURN_ON_FALSE(s_initialized && s_backlight_lock != NULL,
|
||||||
|
ESP_ERR_INVALID_STATE,
|
||||||
|
TAG,
|
||||||
|
"LCD backlight is not initialized");
|
||||||
|
ESP_RETURN_ON_FALSE(xSemaphoreTake(s_backlight_lock,
|
||||||
|
pdMS_TO_TICKS(LCD_DIRECT_BACKLIGHT_LOCK_TIMEOUT_MS)) == pdTRUE,
|
||||||
|
ESP_ERR_TIMEOUT,
|
||||||
|
TAG,
|
||||||
|
"backlight lock timed out");
|
||||||
|
|
||||||
|
esp_err_t err = ledc_set_duty(LEDC_LOW_SPEED_MODE,
|
||||||
|
LCD_DIRECT_BACKLIGHT_PWM_CHANNEL,
|
||||||
|
lcd_direct_backlight_duty(brightness_percent));
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
err = ledc_update_duty(LEDC_LOW_SPEED_MODE, LCD_DIRECT_BACKLIGHT_PWM_CHANNEL);
|
||||||
|
}
|
||||||
|
xSemaphoreGive(s_backlight_lock);
|
||||||
|
|
||||||
|
if (err == ESP_OK) {
|
||||||
|
ESP_LOGI(TAG, "Backlight brightness set to %u%%", (unsigned)brightness_percent);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "Backlight brightness update failed: %s", esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
void lcd_direct_start_render_loop(void)
|
void lcd_direct_start_render_loop(void)
|
||||||
{
|
{
|
||||||
if (!s_initialized) {
|
if (!s_initialized) {
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ esp_err_t platform_init_lcd_device(void)
|
|||||||
return lcd_direct_init_device();
|
return lcd_direct_init_device();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t platform_lcd_set_backlight_brightness(uint8_t brightness_percent)
|
||||||
|
{
|
||||||
|
return lcd_direct_set_backlight_brightness(brightness_percent);
|
||||||
|
}
|
||||||
|
|
||||||
void platform_start_lcd_render_loop(void)
|
void platform_start_lcd_render_loop(void)
|
||||||
{
|
{
|
||||||
lcd_direct_start_render_loop();
|
lcd_direct_start_render_loop();
|
||||||
|
|||||||
@@ -578,6 +578,15 @@ menu "Example Configuration"
|
|||||||
bool "Backlight active high"
|
bool "Backlight active high"
|
||||||
default y
|
default y
|
||||||
|
|
||||||
|
config TQ_SCREEN_BACKLIGHT_BRIGHTNESS_PERCENT
|
||||||
|
int "Default backlight brightness (percent)"
|
||||||
|
default 100
|
||||||
|
range 0 100
|
||||||
|
depends on TQ_SCREEN_ENABLE
|
||||||
|
help
|
||||||
|
Initial LCD backlight PWM duty after screen initialization.
|
||||||
|
Set 0 to turn the backlight off or 100 for full brightness.
|
||||||
|
|
||||||
config TQ_SCREEN_MIRROR_X
|
config TQ_SCREEN_MIRROR_X
|
||||||
bool "Mirror panel in X direction"
|
bool "Mirror panel in X direction"
|
||||||
default y
|
default y
|
||||||
|
|||||||
@@ -277,6 +277,7 @@ CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=y
|
|||||||
CONFIG_TQ_SCREEN_INVERT_COLOR=y
|
CONFIG_TQ_SCREEN_INVERT_COLOR=y
|
||||||
CONFIG_TQ_SCREEN_SWAP_COLOR_BYTES=y
|
CONFIG_TQ_SCREEN_SWAP_COLOR_BYTES=y
|
||||||
CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH=y
|
CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH=y
|
||||||
|
CONFIG_TQ_SCREEN_BACKLIGHT_BRIGHTNESS_PERCENT=100
|
||||||
CONFIG_TQ_SCREEN_MIRROR_X=y
|
CONFIG_TQ_SCREEN_MIRROR_X=y
|
||||||
CONFIG_TQ_SCREEN_SWAP_XY=y
|
CONFIG_TQ_SCREEN_SWAP_XY=y
|
||||||
CONFIG_TQ_SCREEN_X_GAP=0
|
CONFIG_TQ_SCREEN_X_GAP=0
|
||||||
|
|||||||
Reference in New Issue
Block a user