Add auto-sleep/wake functionality with timer-based dimming
- Added PWM brightness control to ST7796 driver (0-100%) - Implemented hardware sleep mode for ST7796 (saves ~150mA) - Added sleep/wake functions preserving framebuffer and settings - Implemented timer-based inactivity detection (checks every 10s) - Auto-sleep after 5 minutes of no user input - Touch controller remains active during TFT sleep for instant wake - E-ink display also sleeps after timeout, requires re-init on wake - Added hardware_pwm library dependency to CMakeLists.txt - Brightness and sleep/wake methods exposed through display abstraction layer
This commit is contained in:
@@ -58,6 +58,7 @@
|
||||
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/spi.h"
|
||||
#include "hardware/pwm.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
@@ -118,6 +119,9 @@ static uint16_t width; // Display width in pixels (e.g., 480)
|
||||
static uint16_t height; // Display height in pixels (e.g., 320)
|
||||
static uint16_t x_offset; // X offset for display alignment (currently 0)
|
||||
static uint16_t y_offset; // Y offset for display alignment (currently 0)
|
||||
static uint8_t current_brightness = 100; // Current brightness level (0-100)
|
||||
static uint pwm_slice; // PWM slice number for backlight control
|
||||
static uint pwm_channel; // PWM channel number for backlight control
|
||||
|
||||
/**
|
||||
* @brief Activate chip select (pull CS LOW)
|
||||
@@ -359,12 +363,27 @@ void st7796_init(const struct st7796_config *c, uint16_t w, uint16_t h) {
|
||||
gpio_init(config->gpio_rst);
|
||||
gpio_set_dir(config->gpio_rst, GPIO_OUT);
|
||||
|
||||
// Initialize backlight pin
|
||||
// Initialize backlight pin with PWM for brightness control
|
||||
// Most TFT displays have LED backlights that need power
|
||||
if (config->gpio_bl >= 0) {
|
||||
gpio_init(config->gpio_bl);
|
||||
gpio_set_dir(config->gpio_bl, GPIO_OUT);
|
||||
gpio_put(config->gpio_bl, 1); // Turn on backlight immediately
|
||||
// Configure GPIO for PWM function
|
||||
gpio_set_function(config->gpio_bl, GPIO_FUNC_PWM);
|
||||
|
||||
// Find PWM slice and channel for this GPIO
|
||||
pwm_slice = pwm_gpio_to_slice_num(config->gpio_bl);
|
||||
pwm_channel = pwm_gpio_to_channel(config->gpio_bl);
|
||||
|
||||
// Configure PWM
|
||||
// PWM frequency = clock_freq / (wrap + 1)
|
||||
// We want ~1 kHz to avoid flicker: 125 MHz / 125000 = 1000 Hz
|
||||
pwm_set_wrap(pwm_slice, 65535); // 16-bit resolution
|
||||
pwm_set_clkdiv(pwm_slice, 1.907f); // 125 MHz / 1.907 / 65536 ≈ 1 kHz
|
||||
|
||||
// Start at full brightness
|
||||
pwm_set_chan_level(pwm_slice, pwm_channel, 65535);
|
||||
pwm_set_enabled(pwm_slice, true);
|
||||
|
||||
current_brightness = 100;
|
||||
}
|
||||
|
||||
// Hardware reset sequence
|
||||
@@ -934,3 +953,112 @@ void st7796_fill_triangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,
|
||||
st7796_fill_rect(a, y, b - a + 1, 1, color);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set display brightness using PWM on backlight pin
|
||||
*
|
||||
* Controls the backlight LED brightness by adjusting the PWM duty cycle.
|
||||
* The brightness parameter is a percentage that's converted to the
|
||||
* appropriate PWM level (0-65535 for 16-bit PWM).
|
||||
*
|
||||
* Implementation:
|
||||
* - brightness = 0: PWM level = 0 (backlight off)
|
||||
* - brightness = 100: PWM level = 65535 (backlight fully on)
|
||||
* - brightness = 50: PWM level = 32767 (50% duty cycle)
|
||||
*
|
||||
* The conversion formula: PWM_level = (brightness * 65535) / 100
|
||||
*
|
||||
* Human perception of brightness is logarithmic, but we use linear
|
||||
* PWM for simplicity. For perceived linear brightness, a gamma
|
||||
* correction curve could be applied.
|
||||
*
|
||||
* @param brightness Brightness level (0-100 percent)
|
||||
*/
|
||||
void st7796_set_brightness(uint8_t brightness) {
|
||||
// Clamp brightness to valid range
|
||||
if (brightness > 100) {
|
||||
brightness = 100;
|
||||
}
|
||||
|
||||
// Store current brightness
|
||||
current_brightness = brightness;
|
||||
|
||||
// Convert percentage to 16-bit PWM level (0-65535)
|
||||
// Using 32-bit intermediate to avoid overflow
|
||||
uint32_t pwm_level = ((uint32_t)brightness * 65535) / 100;
|
||||
|
||||
// Set PWM duty cycle
|
||||
if (config->gpio_bl >= 0) {
|
||||
pwm_set_chan_level(pwm_slice, pwm_channel, (uint16_t)pwm_level);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get current display brightness level
|
||||
*
|
||||
* Returns the brightness level that was last set via st7796_set_brightness()
|
||||
* or the default value (100) if brightness was never changed.
|
||||
*
|
||||
* @return Current brightness level (0-100 percent)
|
||||
*/
|
||||
uint8_t st7796_get_brightness(void) {
|
||||
return current_brightness;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Put display into sleep mode (low power)
|
||||
*
|
||||
* Enters deep sleep mode to minimize power consumption while maintaining
|
||||
* initialization state. The framebuffer contents and all settings are
|
||||
* preserved, so waking up is fast.
|
||||
*
|
||||
* Sleep sequence:
|
||||
* 1. Turn off display (DISPOFF) - stops showing framebuffer
|
||||
* 2. Enter sleep mode (SLPIN) - stops oscillator, minimal power
|
||||
* 3. Turn off backlight - set PWM to 0
|
||||
*
|
||||
* Total power savings: Display draws ~10μA in sleep vs ~150mA active
|
||||
* Touch controller on separate I2C bus remains fully functional
|
||||
*/
|
||||
void st7796_sleep(void) {
|
||||
// Turn off display output first
|
||||
write_command(ST7796_DISPOFF);
|
||||
sleep_ms(10);
|
||||
|
||||
// Enter sleep mode (stops internal oscillator)
|
||||
write_command(ST7796_SLPIN);
|
||||
sleep_ms(120); // Wait for sleep mode to take effect (spec: 120ms)
|
||||
|
||||
// Turn off backlight to save power
|
||||
if (config->gpio_bl >= 0) {
|
||||
pwm_set_chan_level(pwm_slice, pwm_channel, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wake display from sleep mode
|
||||
*
|
||||
* Exits sleep mode and restores display to full operation.
|
||||
* All framebuffer contents and settings are preserved.
|
||||
*
|
||||
* Wake sequence:
|
||||
* 1. Exit sleep mode (SLPOUT) - restarts oscillator
|
||||
* 2. Wait for oscillator to stabilize (120ms)
|
||||
* 3. Turn on display (DISPON) - starts showing framebuffer
|
||||
* 4. Restore backlight to previous brightness
|
||||
*/
|
||||
void st7796_wake(void) {
|
||||
// Exit sleep mode (restart oscillator)
|
||||
write_command(ST7796_SLPOUT);
|
||||
sleep_ms(120); // Wait for oscillator to stabilize (spec: 120ms)
|
||||
|
||||
// Turn on display output
|
||||
write_command(ST7796_DISPON);
|
||||
sleep_ms(10);
|
||||
|
||||
// Restore backlight to previous brightness level
|
||||
if (config->gpio_bl >= 0) {
|
||||
uint32_t pwm_level = ((uint32_t)current_brightness * 65535) / 100;
|
||||
pwm_set_chan_level(pwm_slice, pwm_channel, (uint16_t)pwm_level);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,6 +357,93 @@ void st7796_draw_triangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, ui
|
||||
*/
|
||||
void st7796_fill_triangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);
|
||||
|
||||
/**
|
||||
* @brief Set display brightness using PWM on backlight pin
|
||||
*
|
||||
* Controls the backlight LED brightness using hardware PWM. The brightness
|
||||
* is specified as a percentage (0-100), where:
|
||||
* - 0 = Backlight completely off (minimum brightness)
|
||||
* - 100 = Backlight fully on (maximum brightness)
|
||||
* - Values in between = Proportional PWM duty cycle
|
||||
*
|
||||
* PWM Configuration:
|
||||
* - Frequency: 1000 Hz (1 kHz) - high enough to avoid flicker
|
||||
* - Resolution: 16-bit (0-65535 range internally)
|
||||
* - Smooth transitions without visible flickering
|
||||
*
|
||||
* Use cases:
|
||||
* - Power saving: Reduce brightness when idle
|
||||
* - Auto-dimming: Lower brightness after timeout
|
||||
* - Manual control: User brightness preferences
|
||||
* - Night mode: Very low brightness for dark environments
|
||||
*
|
||||
* @param brightness Brightness level (0-100 percent)
|
||||
* 0 = off, 100 = full brightness
|
||||
*
|
||||
* Performance: Instant - PWM is handled by hardware
|
||||
*
|
||||
* Note: Must be called after st7796_init() which configures the backlight pin
|
||||
*/
|
||||
void st7796_set_brightness(uint8_t brightness);
|
||||
|
||||
/**
|
||||
* @brief Get current display brightness level
|
||||
*
|
||||
* Returns the currently configured brightness level as a percentage (0-100).
|
||||
* This reflects the last value set by st7796_set_brightness() or the
|
||||
* default value (100) if brightness was never explicitly set.
|
||||
*
|
||||
* @return Current brightness level (0-100 percent)
|
||||
*/
|
||||
uint8_t st7796_get_brightness(void);
|
||||
|
||||
/**
|
||||
* @brief Put display into sleep mode (low power)
|
||||
*
|
||||
* Enters sleep mode to save power while keeping the controller initialized.
|
||||
* In sleep mode:
|
||||
* - Display is turned off (blank screen)
|
||||
* - Internal oscillator is stopped
|
||||
* - Framebuffer contents are preserved
|
||||
* - Backlight is turned off (PWM set to 0)
|
||||
* - Power consumption is minimized (~10μA typical)
|
||||
*
|
||||
* Touch controller remains active and functional since it's on a separate
|
||||
* I2C bus. Touch interrupts can wake the system from sleep.
|
||||
*
|
||||
* Use st7796_wake() to exit sleep mode and restore display.
|
||||
*
|
||||
* Commands sent:
|
||||
* - DISPOFF (0x28): Turn off display
|
||||
* - SLPIN (0x10): Enter sleep mode
|
||||
* - Backlight PWM set to 0
|
||||
*
|
||||
* Performance: ~120ms to enter sleep mode
|
||||
*/
|
||||
void st7796_sleep(void);
|
||||
|
||||
/**
|
||||
* @brief Wake display from sleep mode
|
||||
*
|
||||
* Exits sleep mode and restores display to normal operation.
|
||||
* After waking:
|
||||
* - Internal oscillator restarts
|
||||
* - Display controller becomes active
|
||||
* - Framebuffer contents are preserved
|
||||
* - Display is turned on
|
||||
* - Backlight is restored to previous brightness
|
||||
*
|
||||
* Commands sent:
|
||||
* - SLPOUT (0x11): Exit sleep mode
|
||||
* - DISPON (0x29): Turn on display
|
||||
* - Backlight PWM restored to saved level
|
||||
*
|
||||
* Performance: ~120ms to fully wake up
|
||||
*
|
||||
* Note: Must call st7796_init() before first use of sleep/wake
|
||||
*/
|
||||
void st7796_wake(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user