- 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
452 lines
15 KiB
C
452 lines
15 KiB
C
/*
|
|
* Copyright (c) 2021 Arm Limited and Contributors. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* ST7796 TFT LCD Display Driver
|
|
*
|
|
* ==============================================================================
|
|
* ABOUT THE ST7796 DISPLAY CONTROLLER
|
|
* ==============================================================================
|
|
*
|
|
* The ST7796 is a single-chip controller/driver for 262K-color, graphic type
|
|
* TFT-LCD displays. It consists of 960 source line and 480 gate line driving
|
|
* circuits. This chip is capable of connecting directly to an external
|
|
* microprocessor via an 8-bit/9-bit/16-bit/18-bit parallel interface or
|
|
* Serial Peripheral Interface (SPI).
|
|
*
|
|
* Key Features:
|
|
* - Resolution: Up to 480RGB x 320 dots
|
|
* - Display Colors: 262K colors (RGB565 format - 16 bits per pixel)
|
|
* - Interface: 4-wire SPI (used in this driver)
|
|
* - Framebuffer: Internal RAM that holds the pixel data (480x320x16bit = 300KB)
|
|
* - Power: 2.5V~3.3V I/O voltage, with internal regulators for LCD drivers
|
|
*
|
|
* ==============================================================================
|
|
* SPI COMMUNICATION PROTOCOL
|
|
* ==============================================================================
|
|
*
|
|
* The ST7796 uses a 4-wire SPI interface:
|
|
* 1. SCK (Serial Clock) - Clock signal from MCU
|
|
* 2. MOSI/SDA (Master Out Slave In) - Data signal from MCU to display
|
|
* 3. CS (Chip Select) - Active LOW, selects the display
|
|
* 4. DC (Data/Command) - HIGH for data, LOW for commands
|
|
*
|
|
* Additional pins:
|
|
* 5. RST (Reset) - Hardware reset, active LOW
|
|
* 6. BL (Backlight) - LED backlight control, HIGH for ON
|
|
*
|
|
* Communication sequence:
|
|
* 1. Pull CS LOW to select the display
|
|
* 2. Set DC LOW for command, HIGH for data
|
|
* 3. Clock out 8 bits on MOSI while toggling SCK
|
|
* 4. Pull CS HIGH to deselect
|
|
*
|
|
* ==============================================================================
|
|
* RGB565 COLOR FORMAT
|
|
* ==============================================================================
|
|
*
|
|
* RGB565 is a 16-bit color format:
|
|
* - Red: 5 bits (bits 15-11) - 32 levels
|
|
* - Green: 6 bits (bits 10-5) - 64 levels (human eye more sensitive to green)
|
|
* - Blue: 5 bits (bits 4-0) - 32 levels
|
|
*
|
|
* Color encoding: RRRR RGGG GGGB BBBB
|
|
* Example: 0xF800 = Red, 0x07E0 = Green, 0x001F = Blue, 0xFFFF = White
|
|
*
|
|
* ==============================================================================
|
|
* DISPLAY ORIENTATION (MADCTL)
|
|
* ==============================================================================
|
|
*
|
|
* MADCTL (0x36) controls memory access and display rotation:
|
|
* - MY (bit 7): Row Address Order (flip vertical)
|
|
* - MX (bit 6): Column Address Order (flip horizontal)
|
|
* - MV (bit 5): Row/Column Exchange (swap X/Y for rotation)
|
|
* - ML (bit 4): Vertical Refresh Order
|
|
* - BGR (bit 3): RGB or BGR color order
|
|
*
|
|
* Current configuration: 0xE0 (MY=1, MX=1, MV=1)
|
|
* This provides landscape mode (480x320) with correct orientation.
|
|
*
|
|
* ==============================================================================
|
|
* MISSING FEATURES & POTENTIAL IMPROVEMENTS
|
|
* ==============================================================================
|
|
*
|
|
* Currently NOT implemented (but supported by hardware):
|
|
*
|
|
* 1. HARDWARE SCROLLING:
|
|
* - Vertical Scrolling Definition (VSCRDEF) and Vertical Scroll Start Address
|
|
* - Could enable smooth scrolling without redrawing entire screen
|
|
* - Useful for: text scrolling, game backgrounds, UI animations
|
|
*
|
|
* 2. PARTIAL DISPLAY MODE:
|
|
* - Show only a portion of the display (save power)
|
|
* - Commands: PTLON (0x12), PTLAR (0x30)
|
|
*
|
|
* 3. IDLE MODE:
|
|
* - Reduced color depth (8 colors) for power saving
|
|
* - Commands: IDMON (0x38), IDMOFF (0x39)
|
|
*
|
|
* 4. DISPLAY INVERSION:
|
|
* - Currently set to INVOFF, but INVON available
|
|
* - Some displays look better with inversion ON
|
|
*
|
|
* 5. TEAR EFFECT CONTROL:
|
|
* - TE (Tearing Effect) pin synchronization
|
|
* - Prevents tearing during screen updates
|
|
* - Commands: TEON (0x35), TEOFF (0x34)
|
|
*
|
|
* 6. GAMMA CORRECTION:
|
|
* - Fine-tune color accuracy and contrast
|
|
* - Commands: PGC (0xE0), NGC (0xE1)
|
|
*
|
|
* 7. BRIGHTNESS/CONTRAST CONTROL:
|
|
* - Software brightness via WRCABCMB command
|
|
* - Currently only hardware backlight control
|
|
*
|
|
* 8. POWER CONTROL:
|
|
* - Deep sleep modes
|
|
* - Display ON/OFF without re-initialization
|
|
* - Commands: SLPIN (0x10), SLPOUT (0x11)
|
|
*
|
|
* 9. DMA SUPPORT:
|
|
* - Use DMA for SPI transfers instead of blocking writes
|
|
* - Would significantly improve performance for large updates
|
|
*
|
|
* 10. DOUBLE BUFFERING:
|
|
* - Currently single-buffered (direct to framebuffer)
|
|
* - Could implement software double buffer for tear-free updates
|
|
*
|
|
* 11. IMAGE/BITMAP LOADING:
|
|
* - Load images from SD card or flash
|
|
* - BMP, PNG decoders
|
|
*
|
|
* 12. TEXT RENDERING:
|
|
* - Font library for text display
|
|
* - Variable font sizes
|
|
*
|
|
* 13. HARDWARE ACCELERATED FEATURES:
|
|
* - Some displays have built-in shape drawing (not ST7796)
|
|
* - Window clipping for faster partial updates
|
|
*
|
|
* Performance Optimization Ideas:
|
|
* - Increase SPI speed beyond 80MHz (try 100-125MHz)
|
|
* - Use DMA for background transfers
|
|
* - Implement dirty rectangle tracking (only update changed areas)
|
|
* - Cache frequently drawn graphics
|
|
*
|
|
* ==============================================================================
|
|
*/
|
|
|
|
#ifndef _PICO_ST7796_H_
|
|
#define _PICO_ST7796_H_
|
|
|
|
#include "hardware/spi.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/**
|
|
* @brief Configuration structure for ST7796 display
|
|
*
|
|
* This structure holds all the pin mappings and SPI interface configuration
|
|
* needed to communicate with the display. All GPIO pins should be valid
|
|
* Raspberry Pi Pico pins, except gpio_cs which can be -1 to disable CS control.
|
|
*/
|
|
struct st7796_config {
|
|
spi_inst_t* spi; // SPI instance (spi0 or spi1)
|
|
uint gpio_din; // MOSI/SDA pin (data out from MCU)
|
|
uint gpio_clk; // SCK pin (clock)
|
|
int gpio_cs; // CS pin (chip select, -1 to disable)
|
|
uint gpio_dc; // DC pin (data/command select)
|
|
uint gpio_rst; // RST pin (hardware reset)
|
|
uint gpio_bl; // Backlight pin (LED control)
|
|
};
|
|
|
|
/**
|
|
* @brief Initialize the ST7796 display
|
|
*
|
|
* This function performs the complete initialization sequence:
|
|
* 1. Configures all GPIO pins (SPI, CS, DC, RST, BL)
|
|
* 2. Initializes SPI at 80-100 MHz
|
|
* 3. Performs hardware reset
|
|
* 4. Sends initialization commands to the display
|
|
* 5. Sets up RGB565 color mode and landscape orientation
|
|
* 6. Turns on backlight and display
|
|
*
|
|
* @param config Pointer to st7796_config structure with pin mappings
|
|
* @param width Display width in pixels (typically 480 for landscape)
|
|
* @param height Display height in pixels (typically 320 for landscape)
|
|
*
|
|
* Note: After this call, display is ready but blank. Call st7796_fill()
|
|
* to clear with a color, or start drawing primitives.
|
|
*/
|
|
void st7796_init(const struct st7796_config *config, uint16_t width, uint16_t height);
|
|
|
|
/**
|
|
* @brief Fill entire display with a single color
|
|
*
|
|
* This is the fastest way to clear the screen or set a background.
|
|
* Uses optimized 512-byte buffering for maximum SPI throughput.
|
|
*
|
|
* @param color RGB565 color value (e.g., 0x0000=black, 0xFFFF=white)
|
|
*
|
|
* Performance: ~15ms at 80MHz SPI for full 480x320 screen
|
|
*/
|
|
void st7796_fill(uint16_t color);
|
|
|
|
/**
|
|
* @brief Write a single pixel at current cursor position
|
|
*
|
|
* Writes one pixel without changing the drawing window. Use after
|
|
* st7796_set_cursor() to position the write location. Rarely used
|
|
* directly - most applications use st7796_draw_pixel() instead.
|
|
*
|
|
* @param color RGB565 color value
|
|
*/
|
|
void st7796_put(uint16_t color);
|
|
|
|
/**
|
|
* @brief Set cursor position for subsequent writes
|
|
*
|
|
* Sets the drawing window starting at (x, y) and extending to the
|
|
* bottom-right of the display. Subsequent calls to st7796_put()
|
|
* will write pixels starting from this position.
|
|
*
|
|
* @param x X coordinate (0 to width-1)
|
|
* @param y Y coordinate (0 to height-1)
|
|
*/
|
|
void st7796_set_cursor(uint16_t x, uint16_t y);
|
|
|
|
/**
|
|
* @brief Write multiple pixels at current cursor position
|
|
*
|
|
* Writes an array of RGB565 color values starting at the current
|
|
* cursor position. Advances the cursor automatically.
|
|
*
|
|
* @param data Pointer to array of RGB565 color values
|
|
* @param len Number of pixels to write
|
|
*
|
|
* Note: Less efficient than fill_rect for solid colors due to
|
|
* per-pixel conversion overhead.
|
|
*/
|
|
void st7796_write(const uint16_t *data, size_t len);
|
|
|
|
/**
|
|
* @brief Draw a single pixel at specified coordinates
|
|
*
|
|
* Draws one pixel at (x, y) with the specified color. This function
|
|
* sets up a 1x1 drawing window, so it's slower than batch operations.
|
|
* Use fill_rect() or other primitives when possible.
|
|
*
|
|
* @param x X coordinate (0 to width-1)
|
|
* @param y Y coordinate (0 to height-1)
|
|
* @param color RGB565 color value
|
|
*
|
|
* Performance: ~100-200 pixels/second due to window setup overhead
|
|
*/
|
|
void st7796_draw_pixel(uint16_t x, uint16_t y, uint16_t color);
|
|
|
|
/**
|
|
* @brief Draw a rectangle outline
|
|
*
|
|
* Draws a hollow rectangle with 1-pixel thick borders. Implemented
|
|
* as 4 calls to fill_rect() for efficiency.
|
|
*
|
|
* @param x Top-left X coordinate
|
|
* @param y Top-left Y coordinate
|
|
* @param w Width in pixels
|
|
* @param h Height in pixels
|
|
* @param color RGB565 color value
|
|
*/
|
|
void st7796_draw_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color);
|
|
|
|
/**
|
|
* @brief Draw a filled rectangle
|
|
*
|
|
* Draws a solid rectangle. This is one of the fastest drawing operations
|
|
* due to optimized 512-byte buffering.
|
|
*
|
|
* @param x Top-left X coordinate
|
|
* @param y Top-left Y coordinate
|
|
* @param w Width in pixels
|
|
* @param h Height in pixels
|
|
* @param color RGB565 color value
|
|
*
|
|
* Performance: Can draw full screen in ~15ms at 80MHz SPI
|
|
*/
|
|
void st7796_fill_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color);
|
|
|
|
/**
|
|
* @brief Draw a circle outline
|
|
*
|
|
* Draws a hollow circle using the midpoint circle algorithm (Bresenham).
|
|
* Draws 8 symmetric points per iteration for efficiency.
|
|
*
|
|
* @param x0 Center X coordinate
|
|
* @param y0 Center Y coordinate
|
|
* @param r Radius in pixels
|
|
* @param color RGB565 color value
|
|
*
|
|
* Note: Implemented as individual pixel draws, so performance is
|
|
* O(r) with ~100-200 pixels/second throughput.
|
|
*/
|
|
void st7796_draw_circle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color);
|
|
|
|
/**
|
|
* @brief Draw a filled circle
|
|
*
|
|
* Draws a solid circle using midpoint circle algorithm with horizontal
|
|
* line fills. Much faster than drawing individual pixels.
|
|
*
|
|
* @param x0 Center X coordinate
|
|
* @param y0 Center Y coordinate
|
|
* @param r Radius in pixels
|
|
* @param color RGB565 color value
|
|
*
|
|
* Performance: Better than draw_circle due to fill_rect optimization
|
|
*/
|
|
void st7796_fill_circle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color);
|
|
|
|
/**
|
|
* @brief Draw a line between two points
|
|
*
|
|
* Draws a line using Bresenham's line algorithm. Works for any angle.
|
|
*
|
|
* @param x0 Start point X coordinate
|
|
* @param y0 Start point Y coordinate
|
|
* @param x1 End point X coordinate
|
|
* @param y1 End point Y coordinate
|
|
* @param color RGB565 color value
|
|
*
|
|
* Performance: O(max(dx, dy)) with ~100-200 pixels/second throughput
|
|
*/
|
|
void st7796_draw_line(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color);
|
|
|
|
/**
|
|
* @brief Draw a triangle outline
|
|
*
|
|
* Draws a hollow triangle by connecting three points with lines.
|
|
*
|
|
* @param x0 First vertex X coordinate
|
|
* @param y0 First vertex Y coordinate
|
|
* @param x1 Second vertex X coordinate
|
|
* @param y1 Second vertex Y coordinate
|
|
* @param x2 Third vertex X coordinate
|
|
* @param y2 Third vertex Y coordinate
|
|
* @param color RGB565 color value
|
|
*/
|
|
void st7796_draw_triangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);
|
|
|
|
/**
|
|
* @brief Draw a filled triangle
|
|
*
|
|
* Draws a solid triangle using scanline fill algorithm. Sorts vertices
|
|
* by Y coordinate and fills horizontal spans.
|
|
*
|
|
* @param x0 First vertex X coordinate
|
|
* @param y0 First vertex Y coordinate
|
|
* @param x1 Second vertex X coordinate
|
|
* @param y1 Second vertex Y coordinate
|
|
* @param x2 Third vertex X coordinate
|
|
* @param y2 Third vertex Y coordinate
|
|
* @param color RGB565 color value
|
|
*
|
|
* Performance: Better than draw_triangle due to fill_rect optimization
|
|
*/
|
|
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
|
|
|
|
#endif
|