Files
basic1/README.md
2026-01-29 15:58:58 -05:00

640 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# RP2350 TFT Display with Touch and SD Card Demo
A modular embedded application for RP2350 microcontrollers featuring display, touch, and SD card support with hardware abstraction layers. **Now includes a reactive game template architecture!**
## Features
- **Reactive Game Template** - Event-driven architecture optimized for e-ink displays and power efficiency
- **Display Abstraction Layer** - Support for multiple display types (ST7796, ST7789, E-Paper)
- **Touch Abstraction Layer** - Extensible touch controller support (FT6336U)
- **SD Card with FatFS** - File system support with board-aware initialization
- **Multi-Board Support** - Single-file board configuration system for easy board switching
- **1-bit Rendering** - Memory-efficient monochrome graphics with GUI widgets
- **Automated Build Scripts** - Build for one board or all boards with single commands
- **Hardware Abstraction** - Factory pattern for displays and touch controllers
## 🎮 Reactive Game Template
The project now uses a **clean, event-driven architecture** perfect for building games and interactive applications:
### Key Features
-**Event-Driven**: Display only updates when input is received
- 🔋 **Power Efficient**: Uses `__wfi()` to sleep between inputs (< 1mA idle)
- 📄 **E-ink Optimized**: Minimizes screen refreshes for e-paper displays
- 🎯 **Interrupt-Driven**: Touch and button handling via hardware interrupts
- 🧩 **Modular**: Clear separation of input processing, game logic, and rendering
### Architecture Highlights
```
Interrupt → Set Flag → Wake CPU → Process Input → Update Game → Draw → Refresh → Sleep
```
**Before (Polling Loop):**
```cpp
while(1) {
if (touch_interrupt_flag) {
// Read touch data
// Process coordinates
// Draw directly
// Handle gestures inline
refresh_screen();
}
}
```
**After (Reactive Template):**
```cpp
while(1) {
__wfi(); // Sleep until interrupt
InputEvent input = process_button_input(config);
if (!input.valid) {
input = process_touch_input(config, &last_touch_time);
}
if (input.valid && game_update(&game_state, input, config, &renderer)) {
game_draw(&game_state, &renderer, &gui);
refresh_screen(bit_buffer, display);
}
}
```
### Creating Your Own Game
1. **Modify GameState** - Define your game variables
2. **Implement game_init()** - Set initial values
3. **Implement game_update()** - Handle input and update state
4. **Implement game_draw()** - Render your game graphics
The reactive loop and input system work automatically!
**📖 [Read the Full Template Usage Guide](TEMPLATE_USAGE.md)** for detailed examples and patterns.
### Example Game (Included)
The template includes a **Button Navigation Game** demonstrating:
- Hardware button input handling (KEY0 switches focus, KEY1 clicks)
- GUI component usage (buttons, radio buttons, status bars)
- State management (click counters, focus tracking)
- Visual feedback (filled buttons show focus)
- E-ink optimized refreshes
Perfect starting point for your own game!
## Supported Hardware Configurations
### Available Board Configurations
1. **BOARD_FEATHER_TFT** - Adafruit Feather RP2350 with 4.0" TFT ST7796
- Display: ST7796 (480x320 RGB TFT)
- Touch: FT6336U capacitive touch
- Features: High-speed refresh, backlight control, SD card support
2. **BOARD_PICO2_TFT** - Raspberry Pi Pico 2 with 4.0" TFT ST7796
- Display: ST7796 (480x320 RGB TFT)
- Touch: FT6336U capacitive touch
- Features: High-speed refresh, backlight control, SD card support
3. **BOARD_PICO2_EINK** - Raspberry Pi Pico 2 with E-Ink Display
- Display: E-Paper (400x300 monochrome)
- Touch: None (uses hardware buttons KEY0/KEY1)
- Features: Ultra-low power, partial/full refresh modes
### Supported Displays
- **ST7796** - 480x320 RGB TFT LCD (Fully implemented)
- **E-Paper** - Various sizes, monochrome (Fully implemented)
- **ST7789** - Ready for driver implementation
### Supported Touch Controllers
- **FT6336U** - I2C capacitive touch with gesture support (Fully implemented)
- Extensible architecture for additional controllers
## Board Configuration System
### Switching Between Boards
The project uses a single-file configuration system. To switch boards, simply edit `board_config.h` and uncomment your target board:
```cpp
// ---- SELECT YOUR BOARD HERE ----
// #define BOARD_FEATHER_TFT // Feather RP2350 + 4.0" TFT ST7796
// #define BOARD_PICO2_TFT // Pico 2 + 4.0" TFT ST7796
#define BOARD_PICO2_EINK // Pico 2 + E-Ink Display
// --------------------------------
```
**Important:** Only one board should be uncommented at a time!
### What Gets Configured
Each board configuration includes:
- Display type and resolution
- All pin assignments (SPI, I2C, GPIO)
- Touch controller configuration
- Coordinate transformation (rotation/inversion)
- SD card pins
- Hardware button pins (e-ink boards)
- Communication speeds (SPI/I2C baud rates)
### Board-Specific Files
Board configurations are located in `board_configs/`:
- `board_feather_tft.h` - Feather RP2350 + TFT configuration
- `board_pico2_tft.h` - Pico 2 + TFT configuration
- `board_pico2_eink.h` - Pico 2 + E-Ink configuration
## Building and Flashing
### Method 1: Build for Currently Selected Board
The simplest method - builds for whatever board is selected in `board_config.h`:
```bash
./build_and_flash.sh
```
This script will:
1. Detect which board is selected in `board_config.h`
2. Configure CMake if needed
3. Build the project using Ninja
4. Flash to the board using picotool (if board is in BOOTSEL mode)
To build without flashing:
```bash
mkdir -p build
cd build
cmake -G Ninja ..
ninja
```
The output file will be `build/basic1.uf2`.
### Method 2: Build for All Boards
To create UF2 files for all supported boards in one command:
```bash
./build_all_boards.sh
```
This script will:
1. Backup your current `board_config.h`
2. Build each board configuration automatically
3. Generate board-specific output files:
- `basic1_feather_tft.uf2`
- `basic1_pico2_tft.uf2`
- `basic1_pico2_eink.uf2`
4. Restore your original `board_config.h`
This is useful for:
- Creating releases for multiple hardware variants
- Testing code on all board configurations
- Batch building without manual configuration changes
### Manual Flashing
1. Hold the **BOOTSEL** button while connecting USB
2. The board appears as a USB drive (e.g., `RPI-RP2`)
3. Copy the appropriate `.uf2` file to the drive:
```bash
cp basic1.uf2 /Volumes/RPI-RP2/
```
4. The board automatically reboots and runs the program
### Using picotool
If picotool is installed at `~/.pico-sdk/picotool/`:
```bash
# Load and auto-reboot
picotool load build/basic1.uf2 -fx
# Or load without auto-reboot
picotool load build/basic1.uf2
```
## Project Structure
```
basic1/
├── basic1.cpp # Main application
├── board_config.h # Board selector (edit this to switch boards!)
├── board_configs/ # Board-specific configurations
│ ├── board_feather_tft.h # Feather RP2350 + TFT pin config
│ ├── board_pico2_tft.h # Pico 2 + TFT pin config
│ ├── board_pico2_eink.h # Pico 2 + E-Ink pin config
│ └── README.md # Board config documentation
├── CMakeLists.txt # Build configuration
├── build_all_boards.sh # Build all boards automatically
├── build_and_flash.sh # Build and flash current board
├── display/ # Display and GUI abstraction layer
│ ├── low_level_display.h # Display interface (factory pattern)
│ ├── low_level_display_st7796.cpp # ST7796 TFT implementation
│ ├── low_level_display_epaper.cpp # E-Paper implementation
│ ├── low_level_touch.h # Touch interface (factory pattern)
│ ├── low_level_touch_ft6336u.cpp # FT6336U touch implementation
│ ├── low_level_render.h/cpp # 1-bit rendering engine
│ └── low_level_gui.h/cpp # GUI widgets (buttons, gauges, etc.)
├── lib/ # Hardware drivers
│ ├── ft6336u/ # FT6336U capacitive touch driver
│ ├── st7796/ # ST7796 TFT display driver
│ ├── epaper/ # E-Paper display driver
│ ├── sd_card/ # SD card driver with FatFS test
│ └── fatfs/ # FatFS filesystem
├── fonts/ # 25+ bitmap font definitions (5x5 to 8x8)
└── fatfs_time.c # FatFS timestamp support
```
## Adding a New Board Configuration
1. Create a new config file in `board_configs/`:
```bash
cp board_configs/board_pico2_tft.h board_configs/board_myboard.h
```
2. Edit the new file and update all pin definitions for your hardware
3. Add your board to `board_config.h`:
```cpp
// ---- SELECT YOUR BOARD HERE ----
// #define BOARD_FEATHER_TFT
// #define BOARD_PICO2_TFT
// #define BOARD_PICO2_EINK
#define BOARD_MYBOARD // Your new board
// --------------------------------
```
4. Add the include section:
```cpp
#ifdef BOARD_FEATHER_TFT
#include "board_configs/board_feather_tft.h"
// ... other boards ...
#elif defined(BOARD_MYBOARD)
#include "board_configs/board_myboard.h"
#endif
```
5. (Optional) Add to `build_all_boards.sh` for automated builds
See `board_configs/README.md` for detailed configuration structure.
## Hardware Configuration Details
### Display Types
Each board configuration specifies a display type via `DISPLAY_TYPE_SELECTED`:
- `DISPLAY_TYPE_ST7796_VAL` (0) - ST7796 TFT (480x320)
- `DISPLAY_TYPE_ST7789_VAL` (1) - ST7789 TFT (ready for implementation)
- `DISPLAY_TYPE_EPAPER_VAL` (2) - E-Paper displays (various sizes)
### Touch Controller Types
Each board configuration specifies a touch type via `TOUCH_TYPE_SELECTED`:
- `TOUCH_TYPE_FT6336U_VAL` (0) - FT6336U capacitive touch with gesture support
- `TOUCH_TYPE_NONE_VAL` (1) - No touch controller (use hardware buttons)
### Touch Coordinate Transformation
Touch coordinates can be adjusted for different mounting orientations:
```c
#define TOUCH_SWAP_XY true // Swap X/Y coordinates (for 90° rotation)
#define TOUCH_INVERT_X true // Invert X axis (mirror horizontally)
#define TOUCH_INVERT_Y false // Invert Y axis (mirror vertically)
```
These settings are in each board's config file under `board_configs/`.
### Pin Assignments
Each board config file defines all hardware pin connections:
**Display Pins:**
- `DISPLAY_SPI_PORT` - SPI bus (spi0 or spi1)
- `DISPLAY_SCK_PIN`, `DISPLAY_MOSI_PIN`, `DISPLAY_MISO_PIN` - SPI data lines
- `DISPLAY_CS_PIN` - Chip select (active LOW)
- `DISPLAY_DC_PIN` - Data/Command select
- `DISPLAY_RST_PIN` - Hardware reset
- `DISPLAY_BL_PIN` - Backlight control (TFT only, -1 for e-ink)
- `DISPLAY_BUSY_PIN` - Busy signal (E-Paper only, -1 for TFT)
**Touch Pins:**
- `TOUCH_I2C_PORT` - I2C bus (i2c0 or i2c1)
- `TOUCH_SDA_PIN`, `TOUCH_SCL_PIN` - I2C data lines
- `TOUCH_INT_PIN` - Interrupt pin (active LOW when touch detected)
- `TOUCH_RST_PIN` - Hardware reset
**Button Pins (E-Ink boards):**
- `BUTTON_KEY0_PIN` - First hardware button (active LOW)
- `BUTTON_KEY1_PIN` - Second hardware button (active LOW)
**SD Card Pins (optional):**
- `SD_SPI_PORT`, `SD_CS_PIN`, `SD_MISO_PIN`, `SD_MOSI_PIN`, `SD_SCK_PIN`
### Communication Speeds
Configured per board based on display type:
- `SPI_BAUDRATE` - Display SPI speed (32MHz for TFT, 4MHz for e-ink)
- `I2C_BAUDRATE` - Touch controller I2C speed (typically 400kHz)
## Architecture
### Display Abstraction Layer
Provides a unified interface for different display types:
```cpp
class LowLevelDisplay {
public:
virtual bool init() = 0;
virtual void clear(bool white = true) = 0;
virtual void draw_buffer(const uint8_t* bit_buffer) = 0;
virtual void refresh() = 0;
// ... more methods
static LowLevelDisplay* create(DisplayType type, int width, int height);
};
```
Usage:
```cpp
LowLevelDisplay* display = LowLevelDisplay::create(
(DisplayType)DISPLAY_TYPE_SELECTED, V_WIDTH, V_HEIGHT);
display->init();
display->draw_buffer(buffer);
display->refresh();
```
### Touch Abstraction Layer
Unified interface for touch controllers:
```cpp
class LowLevelTouch {
public:
virtual bool init() = 0;
virtual bool read_touch(TouchData* data) = 0;
virtual bool is_touched() = 0;
// ... more methods
static LowLevelTouch* create(TouchType type, int width, int height,
bool swap_xy, bool invert_x, bool invert_y);
};
```
Usage:
```cpp
LowLevelTouch* touch = LowLevelTouch::create(
(TouchType)TOUCH_TYPE_SELECTED, V_WIDTH, V_HEIGHT,
TOUCH_SWAP_XY, TOUCH_INVERT_X, TOUCH_INVERT_Y);
TouchData touch_data;
if (touch->read_touch(&touch_data)) {
int x = touch_data.points[0].x;
int y = touch_data.points[0].y;
// Handle touch
}
```
### SD Card Abstraction
Board-aware initialization:
```cpp
// Initialize with board configuration
if (sd_card_init_with_board_config()) {
// Test FatFS functionality
sd_card_test_fatfs();
}
```
The test function:
- Mounts FatFS
- Lists directory contents
- Creates and reads test file
- Safely unmounts filesystem
## Pin Configurations Summary
Actual pin assignments are defined in `board_configs/*.h` files. Here's a quick reference:
### BOARD_FEATHER_TFT (Adafruit Feather RP2350)
- **Display (ST7796, SPI1):** SCK=18, MOSI=19, MISO=20, CS=17, DC=16, RST=15, BL=14
- **Touch (FT6336U, I2C0):** SDA=4, SCL=5, INT=6, RST=7
- **SD Card (SPI1):** CS=10 (shares SPI with display)
### BOARD_PICO2_TFT (Raspberry Pi Pico 2)
- **Display (ST7796, SPI0):** SCK=2, MOSI=3, MISO=4, CS=5, DC=6, RST=7, BL=8
- **Touch (FT6336U, I2C0):** SDA=12, SCL=13, INT=14, RST=15
- **SD Card (SPI1):** CS=17
### BOARD_PICO2_EINK (Raspberry Pi Pico 2 + E-Ink)
- **Display (E-Paper, SPI0):** SCK=10, MOSI=11, MISO=12, CS=9, DC=8, RST=12, BUSY=13
- **Touch:** None (uses hardware buttons instead)
- **Buttons:** KEY0=GP15 (active LOW), KEY1=GP17 (active LOW)
See individual board config files in `board_configs/` for complete details.
## Extending the System
### Adding a New Display Driver
1. Create implementation files:
```
display/low_level_display_mynewdisplay.h
display/low_level_display_mynewdisplay.cpp
```
2. Inherit from `LowLevelDisplay` base class and implement all virtual methods
3. Add a new constant in `board_config.h`:
```cpp
#define DISPLAY_TYPE_MYNEWDISPLAY_VAL 3
```
4. Update the factory in `display/low_level_display.cpp`:
```cpp
case 3: // DISPLAY_TYPE_MYNEWDISPLAY
display = new LowLevelDisplayMyNewDisplay(width, height);
break;
```
5. Update `CMakeLists.txt` to compile the new files
### Adding a New Touch Controller
1. Create implementation files:
```
display/low_level_touch_mynewtouch.h
display/low_level_touch_mynewtouch.cpp
```
2. Inherit from `LowLevelTouch` base class
3. Add constant and factory case similar to display drivers
4. Update CMakeLists.txt
### Workflow Summary
The typical development workflow:
1. **Select target board:** Edit `board_config.h`, uncomment your board
2. **Build:** Run `./build_and_flash.sh` (builds and flashes current board)
3. **Test:** Connect to USB, monitor via serial terminal
4. **Switch boards:** Edit `board_config.h`, rebuild
5. **Release:** Run `./build_all_boards.sh` to create UF2s for all boards
## Memory Usage
- **1-bit framebuffer:** Width×Height÷8 bytes
- 480×320: 19.2 KB
- 400×300: 15.0 KB
- **Display conversion:** Automatic 1-bit → RGB565 (TFT) or native (E-Paper)
- **Stack/heap:** Minimal, uses static buffers where possible
- **Code size:** ~100-150 KB depending on features enabled
## Troubleshooting
### Build Issues
**Error: "No board selected!"**
- You must uncomment exactly one `BOARD_xxx` define in `board_config.h`
**CMake configuration errors:**
- Delete `build/` directory and reconfigure: `rm -rf build && mkdir build`
### Display Issues
**Display stays blank:**
- Check SPI wiring (especially CS, DC, SCK, MOSI pins)
- Verify power supply (some displays need 5V logic level shifters)
- Check `DISPLAY_TYPE_SELECTED` matches your hardware
**Wrong colors or garbled display:**
- Verify display driver type (ST7796 vs ST7789)
- Check SPI baud rate (try reducing from 32MHz to 16MHz)
### Touch Issues
**Touch not responding:**
- Verify I2C wiring (SDA, SCL, INT, RST pins)
- Check for pull-up resistors on I2C lines (typically 4.7kΩ)
- Confirm touch controller type and I2C address
- Enable debug output to see if touch data is being read
**Touch coordinates inverted or swapped:**
- Adjust `TOUCH_SWAP_XY`, `TOUCH_INVERT_X`, `TOUCH_INVERT_Y` in board config
- These depend on physical mounting orientation
**Interrupt not triggering:**
- Verify `TOUCH_INT_PIN` is correctly defined
- Check INT pin is pulled high (internal pull-up or external resistor)
- Confirm touch controller is configured for interrupt mode
### SD Card Issues
**SD Card not detected:**
- Verify card is inserted and formatted (FAT32)
- Check SPI wiring and CS pin
- Ensure SD card shares SPI correctly with display (different CS pins)
- Try different SD card (some old/large cards have compatibility issues)
### E-Paper Display Issues
**Ghosting or partial images:**
- Use `full_refresh()` periodically to clear ghosting
- E-Paper retains previous image until fully refreshed
**Slow refresh:**
- Normal for e-paper (several seconds for full refresh)
- Use partial refresh for faster updates (may cause ghosting)
**BUSY pin timeout:**
- Verify `DISPLAY_BUSY_PIN` is correctly connected
- Increase timeout in e-paper driver if needed
## Development Tips
### Serial Debugging
Connect via USB and monitor serial output:
```bash
# macOS
screen /dev/tty.usbmodem* 115200
# Linux
screen /dev/ttyACM0 115200
# Exit screen: Ctrl+A, then K
```
The code includes `printf()` statements for debugging touch, display, and SD card operations.
### Power Optimization
For battery-powered projects:
- E-Paper displays use almost no power when idle
- Use `__wfi()` (Wait For Interrupt) to sleep between inputs
- Disable TFT backlight when idle
- Lower SPI/I2C baud rates to reduce power consumption
### Performance Tips
**TFT Displays:**
- Increase SPI baud rate up to 62.5MHz if display supports it
- Minimize full-screen refreshes (use partial updates if possible)
- 1-bit rendering is much faster than RGB565
**E-Paper Displays:**
- Use partial refresh for responsive UI (accepts some ghosting)
- Full refresh only when needed (menu changes, game over, etc.)
- Consider caching frequently used graphics
## Features by Component
### Display Features
- 1-bit monochrome rendering
- RGB565 color support (ST7796)
- Drawing primitives (lines, rectangles, circles)
- GUI widgets (windows, gauges, status bars)
- Multiple font support
### Touch Features
- Multi-touch support (up to 2 points)
- Coordinate transformation
- Touch debouncing
- Event types (press, lift, contact)
### SD Card Features
- SPI mode support
- FatFS integration
- Directory listing
- File read/write
- Automatic timestamps from compile time
## License
Copyright (c) 2021 Arm Limited and Contributors. All rights reserved.
SPDX-License-Identifier: Apache-2.0
## Dependencies
- Raspberry Pi Pico SDK 2.2.0+
- FatFS (included)
- TinyUSB (via SDK)
- Hardware drivers (included in `lib/`)
## Contributing
When adding new features:
1. Follow the abstraction layer pattern
2. Update `board_config.h` for hardware settings
3. Keep application code hardware-agnostic
4. Test on multiple boards if possible
5. Update this README