# display-driver
[![Crates.io][badge-license]][crates]
[![Crates.io][badge-version]][crates]
[![docs.rs][badge-docsrs]][docsrs]
[badge-license]: https://img.shields.io/crates/l/display-driver?style=for-the-badge
[badge-version]: https://img.shields.io/crates/v/display-driver?style=for-the-badge
[badge-docsrs]: https://img.shields.io/docsrs/display-driver?style=for-the-badge
[crates]: https://crates.io/crates/display-driver
[docsrs]: https://docs.rs/display-driver
An Async display driver framework designed to provide a unified interface for various LCD panels.
<img src="./docs/assets/combined1.jpg" style="zoom:50%;" />
## Features
- **Async-Native:** Built from the ground up with first-class `async/await` support.
- **Bus / Interface Layer:**
Unlike simple byte-stream interfaces, `display-driver` is designed for complex communication requirements. Features include **atomic commands, stream payload classification, ROI-aware transfers, duplex operations, and hardware-accelerated fill**.
While this architecture is crucial for supporting advanced interfaces like MIPI DSI, QSPI, or hardware with 2D graphics acceleration, it also ensures high performance for simple buses like SPI.
- **Panel Logic Layer:**
- **MIPI DCS Standard:** Simplifies driver implementation for common controllers (e.g., ST77xx, ILI9xxx).
- **Zero-Cost Polymorphism:** Leverages the `Spec` trait for static configuration (e.g., Gamma curves) without runtime overhead. This system includes built-in presets (e.g., `ST7735 Generic_128x128_Type1`) while fully supporting custom Spec implementations, and automatically handles coordinate offsets across different rotations.
- **Static Init Sequences:** Uses statically computed initialization sequences to minimize Flash/RAM usage—vital for async state machines.
## Peek
```rust
use display_driver::{ColorFormat, DisplayDriver, Orientation, LCDResetOption};
// The `Spec` (Generic128x160Type1) defines the hardware-specific constants (Gamma, Voltage).
use display_driver_st7735::{St7735, spec::generic::Generic128x160Type1};
// 1. Configure Reset
let reset_opt = LCDResetOption::new_pin(reset_pin);
// 2. Create the Panel instance using a Generic Spec (e.g., Generic128x160Type1)
let panel = St7735::<Generic128x160Type1, _, _>::new(reset_opt);
// 3. Bind Bus and Panel, Configure, and Initialize
// The driver orchestrates the logic, delegating transport to 'bus' and commands to 'panel'.
let mut display = DisplayDriver::builder(bus, panel)
.with_color_format(ColorFormat::RGB565)
// This framework automatically handles offsets.
.with_orientation(Orientation::Deg90)
.init(&mut delay).await.unwrap();
// Now you can use `display` to draw:
display.write_frame(fb).await.unwrap();
```
## Display Bus Implementations
- [SPI](./buses/spi): SPI bus implementation.
- [QSPI](./buses/qspi): QSPI bus implementation.
- [SF32 LCDC](https://github.com/OpenSiFli/sifli-rs/tree/main/sifli-hal): Bus Implementation for SF32LB52x LCDC Hardware.
## Display Panel Implementations
- [mipidcs](./mipidcs): Common impl for standard MIPI DCS.
- [st7735](./panels/st7735): ST7735, commonly used in TFT LCD.
- [st7789](./panels/st7789): ST7789, commonly used in TFT LCD.
- [st77916](./panels/st77916): ST77916, commonly used in QSPI round screens.
- [gc9a01](./panels/gc9a01): GC9A01, commonly used in round screens.
- [co5300](./panels/co5300): CO5300, commonly used in AMOLED.
## Examples
check [Examples](./examples) for more.
## Display framework
- embedded-graphics
`DisplayDriver` is optimized for asynchronous, batched transfers and does not implement `embedded-graphics`'s `DrawTarget` directly. For framebuffer-based drawing, wrap it in [`FrameBufferedDisplayDriver`](https://docs.rs/display-driver/latest/display_driver/eg/struct.FrameBufferedDisplayDriver.html). The wrapper implements `DrawTarget`, so `embedded-graphics` primitives can draw into the framebuffer and then be transferred to the panel asynchronously.
```rust
let mut framebuffer: Framebuffer<
Rgb565,
RawU16,
BigEndian,
WIDTH,
HEIGHT,
{ embedded_graphics::framebuffer::buffer_size::<Rgb565>(WIDTH, HEIGHT) },
> = Framebuffer::new();
let mut display = FrameBufferedDisplayDriver::new(driver, &mut framebuffer);
display.clear(Rgb565::BLACK)?;
display.flush().await?;
```
For a framebuffer that represents a screen sub-region, use `new_partial(driver, area, &mut framebuffer)`; its `Area` dimensions must exactly match the framebuffer dimensions. `set_area` can move that region later under the same constraint.
Use `flush_lines` to transfer an inclusive range of framebuffer rows, or the `*_with_frame_control` variants when coordinating multi-part writes, double buffering, or TE synchronization. See the [Examples](./examples) for complete targets.
- Slint
A [**Slint example for SF32**](https://github.com/decaday/sf32-slint-example) is also available.
## TODOs
- Other Driver ICs and Panels
- Use Macros to replace `InitStep::maybe_cmd_with`
- Tearing Effect Control ( #6 )
## License
This project is under Apache License, Version 2.0 ([LICENSE](./LICENSE) or <http://www.apache.org/licenses/LICENSE-2.0>).