st77916
A no_std Rust driver for the Sitronix ST77916 TFT-LCD display controller
(360×390, 262K color) with embedded-graphics support.
The ST77916 is found in round and square LCD modules driven over QSPI, SPI, or parallel interfaces. It is used in boards such as the Waveshare ESP32-S3 Knob Touch LCD 1.8" (360×360, QSPI).
Features
- Generic over communication interface — implement
ControllerInterfacefor QSPI, SPI, parallel, or any other bus. - Generic over reset pin — GPIO, I2C expander, or any other mechanism via
ResetInterface. - Three buffering modes selected at compile-time via a generic parameter:
- Unbuffered (default) — caller owns pixel data, streams via
send_pixels(). - Single-buffered — internal framebuffer with
DrawTargetsupport and automatic dirty band tracking (only changed rows are flushed). - Double-buffered — two framebuffers for overlapping render and flush on hardware with separate CPU/DMA memory buses.
- Unbuffered (default) — caller owns pixel data, streams via
- Builder pattern with
with_init_commands()for vendor-specific panel init sequences. - Static or heap-allocated framebuffer.
embedded-graphicsDrawTargetimplementation with optimizedfill_solid,fill_contiguous, andclear.- RGB565, RGB888, and Gray8 color formats.
- Full ST77916 command set from the datasheet (V1.0, Sitronix, 2022/08).
Usage
Unbuffered (default)
The caller owns the pixel data and streams it directly. No DrawTarget support
unless .unbuffered::<COLOR>() is called on the builder.
use ;
const SIZE: DisplaySize = new;
let mut display = builder
.with_init_commands
.build?;
display.set_window?;
display.send_pixels?;
Single-buffered (recommended)
Internal framebuffer with embedded-graphics DrawTarget. Draw operations
write to RAM; flush() sends only the changed rows to the display.
use ;
use Rgb565;
const SIZE: DisplaySize = new;
const FB_SIZE: usize = framebuffer_size;
let mut display = builder
.with_init_commands
.
.build?;
// Draw with embedded-graphics
use *;
use ;
new
.into_styled
.draw?;
// Flush only the dirty rows to the display
display.flush?;
// Or force a full refresh (e.g. after wake from sleep)
display.full_flush?;
Double-buffered
Two framebuffers allow overlapping render and flush on hardware with separate
CPU and DMA memory buses. Draw operations target the back buffer;
swap_buffers() exchanges front and back, then flush_front() sends the
completed frame.
use ;
use Rgb565;
const SIZE: DisplaySize = new;
const FB_SIZE: usize = framebuffer_size;
let mut display = builder
.with_init_commands
.
.build?;
// Typical frame loop:
loop
Direct framebuffer access
For advanced use cases, the framebuffer can be accessed directly. After writing
pixels manually, call mark_dirty() so the next flush() includes the
modified rows.
let fb = display.framebuffer_mut;
fb.fill; // Write to first row (360 pixels × 2 bytes)
display.mark_dirty; // Mark row 0 as dirty
display.flush?;
Dirty Band Tracking
The single-buffered mode automatically tracks which rows have been modified by
DrawTarget operations (draw_iter, fill_solid, fill_contiguous, clear).
When flush() is called:
- If nothing changed since the last flush, it returns immediately (no SPI traffic).
- Otherwise, only the contiguous band of modified rows is sent — a single
set_window+send_pixelscall with no allocation.
This is transparent to all callers — the DrawTarget API is unchanged. For UIs
where only a small region updates each frame (e.g. a clock or status bar), this
can reduce flush time from ~12ms (full screen) to <1ms.
Use full_flush() to bypass dirty tracking when needed (e.g. after wake from
sleep or direct framebuffer writes without mark_dirty()).
QSPI Protocol
The ST77916 QSPI interface (datasheet Section 8.8.5) uses:
| Opcode | Mode | Description |
|---|---|---|
0x02 |
PP | Single-line cmd + addr + data (register writes) |
0x32 |
PP4O | Single-line cmd + addr, quad-line data (pixel writes) |
0x0B |
FAST READ | Single-line read with dummy byte |
Address format: [0x00, command_byte, 0x00] (24-bit).
This is the same framing used by the SH8601 and other similar controllers, so a QSPI implementation written for one will work with the other.
Datasheet
The ST77916 datasheet (V1.0, 264 pages) is hosted by Espressif:
https://dl.espressif.com/AE/esp-iot-solution/ST77916_SPEC_V1.0.pdf
Acknowledgements
This crate is heavily inspired by
theembeddedrustacean/sh8601-rs.
The trait-based architecture (ControllerInterface, ResetInterface), builder
pattern, framebuffer design, and embedded-graphics integration all follow the
patterns established in that crate. Many thanks to the author for the excellent
reference implementation.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.