ST7796S TFT Display Driver — ESP32-S3 / ESP-IDF v6.0 C library · MSP4020 480×320 px · RGB565 · SPI · DMA
- What is st7796s?
- Feature List
- Hardware Wiring
- Installation as an ESP-IDF Component
- Quick Start — Hello World
- Migration: UART Hello World → TFT Hello World
- Complete API Reference
- Using with esp32-tft-designer
- Adding a New Pattern
- Examples
- Project Structure
- Troubleshooting
st7796s is a pure C driver for the ST7796S TFT controller, designed for the ESP32-S3 with ESP-IDF v6.0.
It targets the MSP4020 panel (480×320 px, RGB565, SPI) and provides:
- A full graphics primitive API (fill, pixel, lines, rectangles, circles)
- An 8×16 bitmap text system with scale support
- Reusable pattern functions (WiFi icon — extendable)
- DMA-accelerated pixel transfer via SPI2
- Backlight PWM control via LEDC
- 4 hardware rotation modes validated on MSP4020
The library is standalone and independent of any design tool. It can be used directly or alongside esp32-tft-designer to display visually designed screens.
| Feature | Description |
|---|---|
| DMA pixel fill | tft_fill_rect / tft_fill_screen using DMA buffer (15 360 bytes) |
| Pixel-level drawing | tft_draw_pixel with out-of-bounds clipping |
| Optimized lines | tft_draw_hline / tft_draw_vline via fill_rect |
| Rectangle | tft_draw_rect (outline) |
| Circles | tft_draw_circle / tft_fill_circle (Bresenham midpoint algorithm) |
| Bitmap text | 8×16 px font, ASCII 32–126, scale ×1 to ×4, auto line-wrap |
| Hello World | tft_show_hello_world() — centered, decorative, yellow text |
| WiFi icon | tft_draw_wifi_icon() — 3 concentric half-arcs + center dot |
| Bitmap push | tft_draw_bitmap() — raw RGB565 pixel buffer |
| Test patterns | 9 built-in patterns (color bars, gradient, checkerboard, circles…) |
| 4 rotations | TFT_ROT_0/90/180/270, MADCTL validated on MSP4020 |
| Backlight PWM | tft_set_backlight() — 0–255, LEDC timer 0 / channel 0 |
| PSRAM support | DMA buffer allocated in PSRAM if available |
| Error handling | All functions return esp_err_t, ESP_ERROR_CHECK compatible |
| MSP4020 Pin | Signal | ESP32-S3 GPIO | Notes |
|---|---|---|---|
| VCC | Power | 3V3 | 3.3V only — never 5V |
| GND | Ground | GND | Common ground |
| CS | SPI Chip Select | GPIO 10 | Active LOW |
| RESET | Hardware reset | GPIO 9 | Active LOW pulse on init |
| DC / RS | Data / Command | GPIO 8 | HIGH = data, LOW = command |
| SDI / MOSI | SPI data out | GPIO 11 | SPI2_HOST MOSI |
| SCK / CLK | SPI clock | GPIO 12 | SPI2_HOST CLK |
| LED / BL | Backlight | GPIO 46 | PWM via LEDC timer 0 |
| SDO / MISO | SPI data in | GPIO 13 | Optional — read-back only |
SPI: mode 0, 40 MHz write, 10 MHz read.
To change pin numbers, modify the
#define TFT_PIN_*constants ininclude/st7796s.h.
# In your ESP-IDF project root
mkdir -p components
cp -r /path/to/st7796s-lib components/st7796sYour main/CMakeLists.txt:
idf_component_register(
SRCS "main.c"
INCLUDE_DIRS "."
REQUIRES st7796s
)# main/idf_component.yml
dependencies:
st7796s:
path: "../../../" # Points to the library root
idf:
version: ">=6.0.0"#include "st7796s.h"
void app_main(void)
{
tft_handle_t tft;
// Initialize display (SPI, GPIO, backlight, reset sequence)
ESP_ERROR_CHECK(tft_init(&tft));
// Display centered Hello World with decorative frame
ESP_ERROR_CHECK(tft_show_hello_world(&tft));
// Or draw manually:
tft_fill_screen(&tft, TFT_BLUE);
tft_draw_string(&tft, 10, 10, "Hello ESP32!", TFT_WHITE, TFT_BLUE, 2);
tft_draw_wifi_icon(&tft, 240, 160, 40, TFT_CYAN, 2, TFT_BLUE);
while (1) { vTaskDelay(pdMS_TO_TICKS(1000)); }
}This section shows the exact changes to migrate from a standard UART Hello World to TFT display output.
#include "esp_log.h"
static const char *TAG = "HELLO";
void app_main(void) {
ESP_LOGI(TAG, "Hello World from ESP32-S3!");
for (int i = 10; i >= 0; i--) {
ESP_LOGI(TAG, "Restarting in %d...", i);
vTaskDelay(pdMS_TO_TICKS(1000));
}
esp_restart();
}#include "esp_log.h"
#include "st7796s.h" // ← ADD THIS / AJOUTER CECI
static const char *TAG = "HELLO";
void app_main(void) {
tft_handle_t tft; // ← ADD: declare handle
ESP_ERROR_CHECK(tft_init(&tft)); // ← ADD: initialize display
// ← REPLACE ESP_LOGI with TFT output:
tft_show_hello_world(&tft);
tft_draw_string(&tft, 8, tft.height - 20,
"Hello World from ESP32-S3!",
TFT_LIGHTGRAY, TFT_BLACK, 1);
// UART log still works alongside TFT:
ESP_LOGI(TAG, "Hello World on TFT: %u×%u px", tft.width, tft.height);
while (1) { vTaskDelay(pdMS_TO_TICKS(10000)); }
// No more esp_restart() needed
}| UART version | TFT version | |
|---|---|---|
| Output | Serial monitor | TFT display |
| New include | — | #include "st7796s.h" |
| New variable | — | tft_handle_t tft; |
| New init call | — | tft_init(&tft) |
| Output call | ESP_LOGI(...) |
tft_draw_string(...) |
| CMakeLists REQUIRES | — | st7796s |
| Function | Description | Returns |
|---|---|---|
tft_init(handle) |
Initialize SPI, GPIO, backlight, ST7796S sequence | ESP_OK | error |
tft_deinit(handle) |
Release SPI and free DMA buffer | ESP_OK |
| Function | Description | Returns |
|---|---|---|
tft_set_rotation(handle, rotation) |
Change rotation (TFT_ROT_0–270) | ESP_OK | INVALID_ARG |
tft_set_backlight(handle, brightness) |
PWM backlight 0–255 | void |
| Function | Parameters | Description |
|---|---|---|
tft_fill_screen(h, color) |
RGB565 | Fill entire screen via DMA |
tft_fill_rect(h, x, y, w, h, color) |
top-left + size + RGB565 | Fill rectangle via DMA |
tft_draw_pixel(h, x, y, color) |
position + RGB565 | Single pixel |
tft_draw_hline(h, x, y, len, color) |
start + length | Horizontal line |
tft_draw_vline(h, x, y, len, color) |
start + length | Vertical line |
tft_draw_rect(h, x, y, w, h, color) |
top-left + size | Rectangle outline |
tft_draw_circle(h, x0, y0, r, color) |
center + radius | Circle outline (Bresenham) |
tft_fill_circle(h, x0, y0, r, color) |
center + radius | Filled circle |
| Function | Parameters | Description |
|---|---|---|
tft_draw_char(h, x, y, c, fg, bg, scale) |
pos + char + colors + scale | Single character |
tft_draw_string(h, x, y, str, fg, bg, scale) |
pos + string + colors + scale | String with auto-wrap |
tft_show_hello_world(h) |
— | Centered Hello World + frame |
Scale: 1 = 8×16 px, 2 = 16×32 px, 3 = 24×48 px, 4 = 32×64 px.
| Function | Parameters | Description |
|---|---|---|
tft_draw_wifi_icon(h, cx, cy, r, color, thickness, bg_color) |
center + radius + color + thickness + bg | WiFi signal icon |
| Function | Parameters | Description |
|---|---|---|
tft_draw_bitmap(h, x, y, w, h, data) |
area + pixel array | Raw RGB565 pixel buffer |
tft_draw_test_pattern(h, pattern) |
tft_test_pattern_t | One of 9 built-in patterns |
TFT_BLACK, TFT_WHITE, TFT_RED, TFT_GREEN, TFT_BLUE, TFT_CYAN,
TFT_MAGENTA, TFT_YELLOW, TFT_ORANGE, TFT_PURPLE, TFT_GRAY,
TFT_DARKGREEN, TFT_DARKBLUE, TFT_DARKRED, TFT_LIGHTGRAY.
Custom color: RGB565(r, g, b) — converts RGB888 to RGB565 at compile time.
esp32-tft-designer is a web-based visual editor that generates C/C++ code
using the functions of this library.
1. Start esp32-tft-designer:
cd esp32-tft-designer
./esp32-tft-container.sh start
→ http://localhost:5100
2. Design your screen visually in the browser
3. Click "Generate C" → select "st7796s (ESP-IDF)"
→ Preview the generated code
4. Click "Export ZIP"
→ Download tft_screen_TIMESTAMP.zip
5. Extract tft_screen_st7796s_TIMESTAMP.c into main/
6. In your main.c:
#include "tft_screen_st7796s.h"
tft_handle_t tft;
ESP_ERROR_CHECK(tft_init(&tft));
tft_draw_screen(&tft); // Generated function
7. idf.py build && idf.py flash
The designer generates calls to:
tft_fill_screen()— for the backgroundtft_draw_pixel(),tft_draw_hline(),tft_draw_vline()— for pixels and linestft_fill_rect(),tft_draw_rect()— for rectanglestft_draw_circle(),tft_fill_circle()— for circlestft_draw_string()— for texttft_draw_wifi_icon()— for the WiFi pattern
The library is designed to be extended with new patterns.
The WiFi icon (tft_draw_wifi_icon) serves as the complete reference model.
| Step | File | Action |
|---|---|---|
| 1 | include/st7796s.h |
Declare the function (copy WiFi Doxygen block as template) |
| 2 | st7796s.c |
Implement in section 7. Patterns |
| 3 | esp32-tft-designer/codegen.py |
Add elif t == "your_type": in generate_st7796s() and generate_lovyangfx() |
| 4 | esp32-tft-designer/app.js |
Add cases in buildElement(), drawElement(), getBBox(), getHandles(), layerLabel(), buildPropsHTML() |
Full step-by-step guide with code examples: docs/adding_new_pattern.md
| Example | Location | Description |
|---|---|---|
| hello_world_uart | examples/hello_world_uart/ |
Classic UART Hello World — starting point |
| hello_world_tft | examples/hello_world_tft/ |
TFT Hello World — shows the UART→TFT migration |
| designer_integration | examples/designer_integration/ |
Full designer + st7796s integration |
cd examples/hello_world_tft
idf.py set-target esp32s3
idf.py build
idf.py -p /dev/ttyUSB0 flash monitorst7796s-lib/
├── include/
│ ├── st7796s.h ← Public API — all declarations + Doxygen docs
│ └── font8x16.h ← Bitmap font array declaration
├── st7796s.c ← Implementation (897 lines, bilingual comments)
├── font8x16.c ← 8×16 bitmap font data (ASCII 32–126)
├── CMakeLists.txt ← IDF component registration
├── idf_component.yml ← IDF Component Manager manifest
├── README.md ← This file (English)
├── README.fr.md ← French documentation
├── docs/
│ └── adding_new_pattern.md ← Step-by-step guide to extend the library
└── examples/
├── hello_world_uart/ ← Classic UART Hello World
├── hello_world_tft/ ← TFT Hello World (UART→TFT migration)
└── designer_integration/ ← esp32-tft-designer + st7796s integration
| Symptom | Possible cause | Solution |
|---|---|---|
| White or blank screen | RST pulse too short | Check RST wiring and 120 ms delay |
| Wrong colors (R/B swapped) | Wrong BGR flag | Change MADCTL_BGR in tft_set_rotation() |
| Garbage pixels | SPI noise at 40 MHz | Reduce to 20 MHz: TFT_SPI_FREQ_HZ (20*1000*1000) |
ESP_ERR_NO_MEM |
No DMA RAM | Ensure CONFIG_ESP32S3_DATA_CACHE_SIZE is large enough |
Compilation error: tft_draw_wifi_icon undefined |
Old header | Rebuild after updating st7796s.h |
Text is ? characters |
Non-ASCII input | Use only ASCII 32–126 |
st7796s — MIT License — ESP32-S3 / MSP4020 / ST7796S