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.
Features
- 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 - Automatic pin configuration for different RP2350 boards
- 1-bit Rendering - Memory-efficient monochrome graphics with GUI widgets
- Board Configuration - Single configuration file for all hardware settings
Supported Hardware
Boards
- Adafruit Feather RP2350 (default)
- Raspberry Pi Pico 2
- Raspberry Pi Pico 2 W
Displays
- ST7796 (480x320 TFT LCD) - Fully implemented
- ST7789 - Ready for driver implementation
- E-Paper - Ready for driver implementation
Touch Controllers
- FT6336U (I2C capacitive touch) - Fully implemented
- Extensible for additional controllers
Storage
- SD Card via SPI with FatFS file system
Quick Start
Building for Default Board (Feather RP2350)
mkdir -p build
cd build
cmake -G Ninja ..
ninja
Building for Specific Board
cmake -G Ninja -DPICO_BOARD=pico2 ..
ninja
Available boards: adafruit_feather_rp2350, pico2, pico2_w
Building for All Boards
./build_all_boards.sh
Generates:
basic1_adafruit_feather_rp2350.uf2basic1_pico2.uf2basic1_pico2_w.uf2
Flashing
- Hold BOOTSEL button while connecting USB
- Copy the
.uf2file to the mounted drive - Device will automatically reboot and run
Or use the flash script:
./build_and_flash.sh
Project Structure
basic1/
├── basic1.cpp # Main application
├── board_config.h # Board-specific pin configuration
├── CMakeLists.txt # Build configuration
├── build_all_boards.sh # Multi-board build script
├── build_and_flash.sh # Build and flash helper
│
├── display/ # Display and GUI abstraction
│ ├── low_level_display.h # Display interface
│ ├── low_level_display_*.cpp # Display implementations
│ ├── low_level_touch.h # Touch interface
│ ├── low_level_touch_*.cpp # Touch implementations
│ ├── low_level_render.h/cpp # 1-bit rendering engine
│ └── low_level_gui.h/cpp # GUI widgets
│
├── lib/ # Hardware drivers
│ ├── ft6336u/ # FT6336U touch driver
│ ├── st7796/ # ST7796 display driver
│ ├── sd_card/ # SD card driver with FatFS test
│ └── fatfs/ # FatFS filesystem
│
├── fonts/ # Bitmap font definitions
└── fatfs_time.c # FatFS timestamp (uses compile time)
Configuration
Board Configuration (board_config.h)
All hardware settings are defined per board:
// Display configuration
#define DISPLAY_WIDTH 480
#define DISPLAY_HEIGHT 320
#define DISPLAY_TYPE_SELECTED 0 // DISPLAY_TYPE_ST7796
// Touch configuration
#define TOUCH_TYPE_SELECTED 0 // TOUCH_TYPE_FT6336U
#define TOUCH_SWAP_XY true
#define TOUCH_INVERT_X true
#define TOUCH_INVERT_Y false
// SPI pins for display
#define DISPLAY_SPI_PORT spi1
#define DISPLAY_SCK_PIN 18
#define DISPLAY_MOSI_PIN 19
#define DISPLAY_MISO_PIN 20
#define DISPLAY_CS_PIN 17
// ... more pins
Changing Display Type
Modify DISPLAY_TYPE_SELECTED in board_config.h:
0= ST7796 (480x320 TFT)1= ST77892= E-Paper
Changing Touch Type
Modify TOUCH_TYPE_SELECTED in board_config.h:
0= FT6336U1= None (disable touch)
Touch Coordinate Transformation
Adjust orientation in board_config.h:
#define TOUCH_SWAP_XY true // Swap X/Y coordinates
#define TOUCH_INVERT_X true // Invert X axis
#define TOUCH_INVERT_Y false // Invert Y axis
Architecture
Display Abstraction Layer
Provides a unified interface for different display types:
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:
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:
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:
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:
// 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
Adafruit Feather RP2350
- Display (SPI1): SCK=18, MOSI=19, MISO=20, CS=17, DC=16, RST=15, BL=14
- Touch (I2C0): SDA=4, SCL=5, INT=6, RST=7
- SD Card (SPI1): CS=10 (shares SPI with display)
Raspberry Pi Pico 2 / Pico 2 W
- Display (SPI0): SCK=2, MOSI=3, MISO=4, CS=5, DC=6, RST=7, BL=8
- Touch (I2C0): SDA=12, SCL=13, INT=14, RST=15
- SD Card (SPI1): CS=17
Adding New Hardware
Adding a New Display Driver
-
Create implementation files:
display/low_level_display_mydriver.h display/low_level_display_mydriver.cpp -
Implement the
LowLevelDisplayinterface -
Add to factory in
low_level_display_factory.cpp:case DISPLAY_TYPE_MYDRIVER: display = new LowLevelDisplayMyDriver(width, height); break; -
Update
CMakeLists.txtto include new files
Adding a New Touch Controller
-
Create implementation files:
display/low_level_touch_mydriver.h display/low_level_touch_mydriver.cpp -
Implement the
LowLevelTouchinterface -
Add to factory in
low_level_touch_factory.cpp -
Update
CMakeLists.txt
Adding a New Board
Edit board_config.h:
#elif defined(PICO_BOARD_MY_CUSTOM_BOARD)
#define BOARD_NAME "My Custom Board"
// Display configuration
#define DISPLAY_WIDTH 480
#define DISPLAY_HEIGHT 320
#define DISPLAY_TYPE_SELECTED 0
// Touch configuration
#define TOUCH_TYPE_SELECTED 0
// ... define all pins
#endif
Memory Usage
- 1-bit framebuffer: 480×320÷8 = 19.2 KB
- Display conversion: Automatic 1-bit → RGB565/monochrome
- Stack/heap: Minimal, uses static buffers where possible
Known Issues & Troubleshooting
Touch Not Working
- I2C Communication Failure: Check wiring, pull-up resistors, I2C address
- Wrong Coordinates: Adjust
TOUCH_SWAP_XY,TOUCH_INVERT_X/Ysettings
SD Card Not Detected
- CMD0 Returns 0x00: Check card insertion, CS pin, SPI wiring
- Mount Fails: Ensure card is formatted as FAT/FAT32
Display Shows Nothing
- Check SPI wiring and CS/DC/RST pins
- Verify backlight is connected and enabled
- Check voltage levels (3.3V logic)
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:
- Follow the abstraction layer pattern
- Update
board_config.hfor hardware settings - Keep application code hardware-agnostic
- Test on multiple boards if possible
- Update this README