How the 160x80 LCD Display (ST7735S) Is Controlled in RPi Pico WAV Player
The RPi Pico WAV Player controls the 160×80 ST7735S LCD through the pico_st7735_80x160 submodule, which provides a low-level C API (LCD_*) that the LcdCanvas class wraps to handle initialization, configuration, and drawing operations.
The project uses a 0.96-inch 160×80 pixel ST7735S panel (referenced as I6Ox8O in the schematic) to render its user interface. All display operations are abstracted through a dedicated canvas layer that translates high-level UI commands into SPI transactions managed by an external driver library.
Selecting the ST7735S Driver
The target panel is selected at compile time in src/LcdCanvas.h through a conditional macro that includes the low-level driver headers:
#define USE_ST7735S_160x80
#if defined(USE_ST7735S_160x80)
#include "lcd_extra.h" // Driver from the pico_st7735_80x160 submodule
#endif
This inclusion pulls in the pico_st7735_80x160 library, which is linked as a Git submodule under lib/pico_st7735_80x160. The library exposes a C-based API prefixed with LCD_ that handles all hardware-specific initialization and pixel pushing.
LCD Initialization and Configuration
Hardware initialization is centralized in LcdCanvas::configureLcd(), defined in src/LcdCanvas.cpp. This function maps the Raspberry Pi Pico’s GPIO pins to the display’s SPI interface and selects one of three predefined hardware configurations based on the board type and user settings.
The configuration structure pico_st7735_80x160_config_t defines SPI clock frequency, SPI instance (spi1), and pin assignments for CS, SCK, MOSI, DC, RST, and BLK (backlight):
pico_st7735_80x160_config_t lcd_cfg[3] = {
{ SPI_CLK_FREQ_DEFAULT, spi1, pin_spi_cs, pin_spi_sck, pin_spi_mosi,
pin_lcd_dc, pin_lcd_rst, pin_lcd_blk, PWM_DUTY_DEFAULT,
INVERSION_DEFAULT, RGB_ORDER_DEFAULT, ROTATION_DEFAULT,
H_OFS_DEFAULT, V_OFS_DEFAULT, X_MIRROR_DEFAULT },
{ … }, // Alternative offset configuration
{ … } // X-mirror configuration
};
After selecting the appropriate configuration entry, the function invokes the driver’s setup routines:
LCD_Config(&lcd_cfg[cfg_id]); // Store configuration in driver
LCD_Init(); // Execute hardware reset and initialization
LCD_Clear(BLACK); // Clear framebuffer to black
LCD_Init() performs the hardware reset sequence, configures the ST7735S controller registers, and prepares the SPI interface for DMA-backed transfers.
High-Level Drawing API
The LcdCanvas class wraps the low-level LCD_* functions to provide a UI-friendly drawing interface. Key primitives include:
LCD_FillBackground()– Clears the entire screen, invoked byLcdCanvas::clear().LCD_SetRotation()– Changes display orientation at runtime, wrapped byLcdCanvas::setRotation().LCD_ShowIcon()– Renders bitmap icons fromLcdCanvasIcon.h, used byBatteryIconBox::draw().LCD_Fill()– Draws filled rectangles for UI elements like battery level bars and progress indicators.LCD_ShowString()– Displays text strings, utilized internally byTextBox::draw().
The UI composition layer (LcdCanvas) manages screen states (opening, list view, play mode, power-off) by orchestrating these primitives. For example, drawing a battery icon with a dynamic charge level involves calling LCD_ShowIcon() for the battery outline and LCD_Fill() for the charge bar:
void BatteryIconBox::draw() {
if (!isUpdated) return;
isUpdated = false;
clear();
LCD_ShowIcon(pos_x, pos_y, icon, !bgOpaque, fgColor);
// Draw charge level bar
uint16_t color = (level >= 50) ? 0x0600 :
(level >= 20) ? 0xc600 : 0xc000;
LCD_Fill(pos_x+4, pos_y+13-level/10,
pos_x+4+8-1, pos_y+13-level/10+level/10+1-1, color);
}
Submodule Integration
The low-level driver is not maintained within the main repository. Instead, it is imported as a Git submodule defined in .gitmodules and linked via CMake in the root CMakeLists.txt:
add_subdirectory(pico_st7735_80x160)
This approach allows the project to track updates to the pico_st7735_80x160 library independently while ensuring the build system compiles the driver sources alongside the application code.
Summary
- Driver Selection: The project targets the ST7735S panel using the
USE_ST7735S_160x80macro insrc/LcdCanvas.h, which includes thepico_st7735_80x160submodule. - Configuration:
LcdCanvas::configureLcd()insrc/LcdCanvas.cppsets up SPI pins, clock, and panel-specific offsets, then callsLCD_Config()andLCD_Init()to activate the hardware. - Drawing API: The UI layer uses high-level primitives such as
LCD_Fill(),LCD_ShowIcon(), andLCD_ShowString()to render screens, while the driver handles SPI DMA transfers to the ST7735S controller. - Submodule Architecture: The low-level driver resides in a separate repository linked as a Git submodule, integrated via CMake to keep the display logic modular and maintainable.
Frequently Asked Questions
How is the ST7735S LCD connected to the Raspberry Pi Pico?
The display connects via SPI1 with dedicated GPIO pins for chip select (CS), serial clock (SCK), master-out-slave-in (MOSI), data/command (DC), reset (RST), and backlight (BLK). The specific pin numbers are defined in src/LcdCanvas.cpp within the configureLcd() function and vary based on the detected board_type_t.
What is the purpose of the pico_st7735_80x160 submodule?
The submodule provides the low-level C driver (LCD_* API) that implements the SPI communication protocol and ST7735S initialization sequence. Keeping this code in a separate repository allows the WAV Player project to share the driver with other applications and receive bug fixes or optimizations without modifying the main player source code.
Can the display rotation be changed at runtime?
Yes. The driver supports runtime rotation through the LCD_SetRotation() function, which is wrapped by LcdCanvas::setRotation() in src/LcdCanvas.cpp. This updates the ST7735S memory access control registers to change the orientation between portrait and landscape modes without requiring a hardware reset.
How does the project handle different hardware revisions of the LCD?
The configureLcd() function defines three configuration presets in an array of pico_st7735_80x160_config_t structures. Each preset specifies different horizontal and vertical offsets (H_OFS, V_OFS) and mirroring options to accommodate variations in ST7735S panel manufacturers. The user selects the appropriate configuration via the cfg_id parameter, which is stored in the device configuration menu.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →