#![no_std]
extern crate alloc;
pub mod commands;
mod graphics_core;
use alloc::boxed::Box;
use core::marker::PhantomData;
use embedded_graphics_core::pixelcolor::raw::{RawData, RawU16};
use embedded_graphics_core::pixelcolor::{Gray8, GrayColor, Rgb565, Rgb888, RgbColor};
use embedded_graphics_core::prelude::PixelColor;
use embedded_hal::delay::DelayNs;
#[derive(Debug, Clone, Copy)]
pub struct DisplaySize {
pub width: u16,
pub height: u16,
}
impl DisplaySize {
pub const fn new(width: u16, height: u16) -> Self {
Self { width, height }
}
}
#[derive(Debug)]
pub enum DriverError<InterfaceError, ResetError> {
InterfaceError(InterfaceError),
ResetError(ResetError),
InvalidConfiguration(&'static str),
}
pub trait ControllerInterface {
type Error;
fn send_command(&mut self, cmd: u8) -> Result<(), Self::Error>;
fn send_command_with_data(&mut self, cmd: u8, data: &[u8]) -> Result<(), Self::Error>;
fn send_pixels(&mut self, pixels: &[u8]) -> Result<(), Self::Error>;
}
pub trait ResetInterface {
type Error;
fn reset(&mut self) -> Result<(), Self::Error>;
}
pub enum ColorMode {
Rgb565,
Rgb666,
}
impl ColorMode {
pub const fn bytes_per_pixel(&self) -> usize {
match self {
ColorMode::Rgb565 => 2,
ColorMode::Rgb666 => 3,
}
}
pub const fn colmod_param(&self) -> u8 {
match self {
ColorMode::Rgb565 => commands::COLMOD_RGB565,
ColorMode::Rgb666 => commands::COLMOD_RGB666,
}
}
}
pub trait St77916Color: PixelColor + Copy {
const BYTES_PER_PIXEL: usize;
fn encode(self, out: &mut [u8]);
fn fill_row(color: Self, row: &mut [u8]) {
let bpp = Self::BYTES_PER_PIXEL;
let mut tmp = [0u8; 4];
color.encode(&mut tmp[..bpp]);
for chunk in row.chunks_exact_mut(bpp) {
chunk.copy_from_slice(&tmp[..bpp]);
}
}
fn fill_buf(color: Self, buf: &mut [u8]) {
Self::fill_row(color, buf);
}
}
impl St77916Color for Rgb565 {
const BYTES_PER_PIXEL: usize = 2;
#[inline]
fn encode(self, out: &mut [u8]) {
let raw = RawU16::from(self).into_inner();
out[0] = (raw >> 8) as u8;
out[1] = raw as u8;
}
fn fill_buf(color: Self, buf: &mut [u8]) {
let raw = RawU16::from(color).into_inner();
let hi = (raw >> 8) as u8;
let lo = raw as u8;
if hi == lo {
buf.fill(hi);
} else {
for chunk in buf.chunks_exact_mut(2) {
chunk[0] = hi;
chunk[1] = lo;
}
}
}
}
impl St77916Color for Rgb888 {
const BYTES_PER_PIXEL: usize = 3;
#[inline]
fn encode(self, out: &mut [u8]) {
out[0] = self.r();
out[1] = self.g();
out[2] = self.b();
}
fn fill_buf(color: Self, buf: &mut [u8]) {
let r = color.r();
let g = color.g();
let b = color.b();
if r == g && r == b {
buf.fill(r);
} else {
for chunk in buf.chunks_exact_mut(3) {
chunk[0] = r;
chunk[1] = g;
chunk[2] = b;
}
}
}
}
impl St77916Color for Gray8 {
const BYTES_PER_PIXEL: usize = 1;
#[inline]
fn encode(self, out: &mut [u8]) {
out[0] = self.luma();
}
fn fill_buf(color: Self, buf: &mut [u8]) {
buf.fill(color.luma());
}
}
pub const fn framebuffer_size(display: DisplaySize, color: ColorMode) -> usize {
(display.width as usize) * (display.height as usize) * color.bytes_per_pixel()
}
pub enum Framebuffer {
Static(&'static mut [u8]),
Heap(Box<[u8]>),
}
impl Framebuffer {
pub fn heap<const N: usize>() -> Self {
Framebuffer::Heap(Box::new([0u8; N]))
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
match self {
Framebuffer::Static(arr) => arr,
Framebuffer::Heap(boxed) => boxed,
}
}
pub fn as_slice(&self) -> &[u8] {
match self {
Framebuffer::Static(arr) => arr,
Framebuffer::Heap(boxed) => boxed,
}
}
pub fn len(&self) -> usize {
self.as_slice().len()
}
pub fn is_empty(&self) -> bool {
self.as_slice().is_empty()
}
}
impl core::ops::Deref for Framebuffer {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl core::ops::DerefMut for Framebuffer {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut_slice()
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct DirtyBand {
y_min: u16,
y_max: u16,
}
impl DirtyBand {
pub fn clean() -> Self {
Self {
y_min: u16::MAX,
y_max: 0,
}
}
#[inline]
pub fn is_clean(&self) -> bool {
self.y_min > self.y_max
}
#[inline]
pub fn mark(&mut self, y_start: u16, y_end: u16) {
self.y_min = self.y_min.min(y_start);
self.y_max = self.y_max.max(y_end);
}
#[inline]
pub fn mark_all(&mut self, height: u16) {
self.y_min = 0;
self.y_max = height - 1;
}
#[inline]
pub fn reset(&mut self) {
*self = Self::clean();
}
}
pub struct Buffered<COLOR: St77916Color = Rgb565> {
data: Framebuffer,
dirty: DirtyBand,
_color: PhantomData<COLOR>,
}
pub struct DoubleBuffered<COLOR: St77916Color = Rgb565> {
bufs: [Framebuffer; 2],
back_idx: usize,
_color: PhantomData<COLOR>,
}
impl<COLOR: St77916Color> DoubleBuffered<COLOR> {
fn back(&self) -> &Framebuffer {
&self.bufs[self.back_idx]
}
fn back_mut(&mut self) -> &mut Framebuffer {
&mut self.bufs[self.back_idx]
}
fn front(&self) -> &Framebuffer {
&self.bufs[self.back_idx ^ 1]
}
}
pub struct Unbuffered<COLOR: St77916Color = Rgb565> {
_color: PhantomData<COLOR>,
}
pub struct St77916<IFACE, RST, BUF = ()>
where
IFACE: ControllerInterface,
RST: ResetInterface,
{
interface: IFACE,
reset: RST,
config: DisplaySize,
buffer: BUF,
}
pub struct St77916Builder<IFACE, RST, BUF = ()>
where
IFACE: ControllerInterface,
RST: ResetInterface,
{
interface: IFACE,
reset: RST,
config: DisplaySize,
init_commands: Option<&'static [(u8, &'static [u8], u16)]>,
buffer: BUF,
}
impl<IFACE, RST, BUF> St77916Builder<IFACE, RST, BUF>
where
IFACE: ControllerInterface,
RST: ResetInterface,
{
pub fn with_init_commands(mut self, commands: &'static [(u8, &'static [u8], u16)]) -> Self {
self.init_commands = Some(commands);
self
}
}
impl<IFACE, RST> St77916Builder<IFACE, RST, ()>
where
IFACE: ControllerInterface,
RST: ResetInterface,
{
pub fn buffered<COLOR: St77916Color>(
self,
framebuffer: Framebuffer,
) -> St77916Builder<IFACE, RST, Buffered<COLOR>> {
St77916Builder {
interface: self.interface,
reset: self.reset,
config: self.config,
init_commands: self.init_commands,
buffer: Buffered {
data: framebuffer,
dirty: DirtyBand::clean(),
_color: PhantomData,
},
}
}
pub fn double_buffered<COLOR: St77916Color>(
self,
fb1: Framebuffer,
fb2: Framebuffer,
) -> St77916Builder<IFACE, RST, DoubleBuffered<COLOR>> {
St77916Builder {
interface: self.interface,
reset: self.reset,
config: self.config,
init_commands: self.init_commands,
buffer: DoubleBuffered {
bufs: [fb1, fb2],
back_idx: 0,
_color: PhantomData,
},
}
}
pub fn unbuffered<COLOR: St77916Color>(self) -> St77916Builder<IFACE, RST, Unbuffered<COLOR>> {
St77916Builder {
interface: self.interface,
reset: self.reset,
config: self.config,
init_commands: self.init_commands,
buffer: Unbuffered {
_color: PhantomData,
},
}
}
pub fn build<DELAY>(
self,
color: ColorMode,
delay: &mut DELAY,
) -> Result<St77916<IFACE, RST>, DriverError<IFACE::Error, RST::Error>>
where
DELAY: DelayNs,
{
let mut driver = St77916 {
interface: self.interface,
reset: self.reset,
config: self.config,
buffer: (),
};
driver.hard_reset()?;
run_init(&mut driver.interface, self.init_commands, delay, color)
.map_err(DriverError::InterfaceError)?;
Ok(driver)
}
}
impl<IFACE, RST, COLOR: St77916Color> St77916Builder<IFACE, RST, Unbuffered<COLOR>>
where
IFACE: ControllerInterface,
RST: ResetInterface,
{
#[allow(clippy::type_complexity)]
pub fn build<DELAY>(
self,
color: ColorMode,
delay: &mut DELAY,
) -> Result<St77916<IFACE, RST, Unbuffered<COLOR>>, DriverError<IFACE::Error, RST::Error>>
where
DELAY: DelayNs,
{
let mut driver = St77916 {
interface: self.interface,
reset: self.reset,
config: self.config,
buffer: self.buffer,
};
driver.hard_reset()?;
run_init(&mut driver.interface, self.init_commands, delay, color)
.map_err(DriverError::InterfaceError)?;
Ok(driver)
}
}
impl<IFACE, RST, COLOR: St77916Color> St77916Builder<IFACE, RST, Buffered<COLOR>>
where
IFACE: ControllerInterface,
RST: ResetInterface,
{
#[allow(clippy::type_complexity)]
pub fn build<DELAY>(
self,
color: ColorMode,
delay: &mut DELAY,
) -> Result<St77916<IFACE, RST, Buffered<COLOR>>, DriverError<IFACE::Error, RST::Error>>
where
DELAY: DelayNs,
{
let mut driver = St77916 {
interface: self.interface,
reset: self.reset,
config: self.config,
buffer: self.buffer,
};
driver.hard_reset()?;
run_init(&mut driver.interface, self.init_commands, delay, color)
.map_err(DriverError::InterfaceError)?;
Ok(driver)
}
}
impl<IFACE, RST, COLOR: St77916Color> St77916Builder<IFACE, RST, DoubleBuffered<COLOR>>
where
IFACE: ControllerInterface,
RST: ResetInterface,
{
#[allow(clippy::type_complexity)]
pub fn build<DELAY>(
self,
color: ColorMode,
delay: &mut DELAY,
) -> Result<St77916<IFACE, RST, DoubleBuffered<COLOR>>, DriverError<IFACE::Error, RST::Error>>
where
DELAY: DelayNs,
{
let mut driver = St77916 {
interface: self.interface,
reset: self.reset,
config: self.config,
buffer: self.buffer,
};
driver.hard_reset()?;
run_init(&mut driver.interface, self.init_commands, delay, color)
.map_err(DriverError::InterfaceError)?;
Ok(driver)
}
}
fn run_init<IFACE: ControllerInterface, DELAY: DelayNs>(
interface: &mut IFACE,
init_commands: Option<&[(u8, &[u8], u16)]>,
delay: &mut DELAY,
color: ColorMode,
) -> Result<(), IFACE::Error> {
if let Some(cmds) = init_commands {
for &(cmd, data, delay_ms) in cmds {
if data.is_empty() {
interface.send_command(cmd)?;
} else {
interface.send_command_with_data(cmd, data)?;
}
if delay_ms > 0 {
delay.delay_ms(delay_ms as u32);
}
}
} else {
interface.send_command(commands::SWRESET)?;
delay.delay_ms(120);
interface.send_command(commands::SLPOUT)?;
delay.delay_ms(120);
interface.send_command_with_data(commands::COLMOD, &[color.colmod_param()])?;
delay.delay_ms(5);
interface.send_command_with_data(commands::MADCTL, &[0x00])?;
interface.send_command(commands::INVON)?;
interface.send_command(commands::DISPON)?;
delay.delay_ms(20);
}
Ok(())
}
impl<IFACE, RST> St77916<IFACE, RST>
where
IFACE: ControllerInterface,
RST: ResetInterface,
{
pub fn builder(
interface: IFACE,
reset: RST,
config: DisplaySize,
) -> St77916Builder<IFACE, RST> {
St77916Builder {
interface,
reset,
config,
init_commands: None,
buffer: (),
}
}
}
impl<IFACE, RST, BUF> St77916<IFACE, RST, BUF>
where
IFACE: ControllerInterface,
RST: ResetInterface,
{
pub fn interface_mut(&mut self) -> &mut IFACE {
&mut self.interface
}
pub fn hard_reset(&mut self) -> Result<(), DriverError<IFACE::Error, RST::Error>> {
self.reset.reset().map_err(DriverError::ResetError)
}
pub fn send_command(&mut self, cmd: u8) -> Result<(), DriverError<IFACE::Error, RST::Error>> {
self.interface
.send_command(cmd)
.map_err(DriverError::InterfaceError)
}
pub fn send_command_with_data(
&mut self,
cmd: u8,
data: &[u8],
) -> Result<(), DriverError<IFACE::Error, RST::Error>> {
self.interface
.send_command_with_data(cmd, data)
.map_err(DriverError::InterfaceError)
}
pub fn send_pixels(
&mut self,
pixels: &[u8],
) -> Result<(), DriverError<IFACE::Error, RST::Error>> {
self.interface
.send_pixels(pixels)
.map_err(DriverError::InterfaceError)
}
pub fn set_window(
&mut self,
x_start: u16,
y_start: u16,
x_end: u16,
y_end: u16,
) -> Result<(), DriverError<IFACE::Error, RST::Error>> {
self.send_command_with_data(
commands::CASET,
&[
(x_start >> 8) as u8,
(x_start & 0xFF) as u8,
(x_end >> 8) as u8,
(x_end & 0xFF) as u8,
],
)?;
self.send_command_with_data(
commands::RASET,
&[
(y_start >> 8) as u8,
(y_start & 0xFF) as u8,
(y_end >> 8) as u8,
(y_end & 0xFF) as u8,
],
)
}
pub fn flush_pixels(
&mut self,
pixels: &[u8],
) -> Result<(), DriverError<IFACE::Error, RST::Error>> {
self.set_window(0, 0, self.config.width - 1, self.config.height - 1)?;
self.send_pixels(pixels)
}
pub fn sleep_in<DELAY>(
&mut self,
delay: &mut DELAY,
) -> Result<(), DriverError<IFACE::Error, RST::Error>>
where
DELAY: DelayNs,
{
self.send_command(commands::SLPIN)?;
delay.delay_ms(5);
Ok(())
}
pub fn sleep_out<DELAY>(
&mut self,
delay: &mut DELAY,
) -> Result<(), DriverError<IFACE::Error, RST::Error>>
where
DELAY: DelayNs,
{
self.send_command(commands::SLPOUT)?;
delay.delay_ms(120);
Ok(())
}
pub fn display_off(&mut self) -> Result<(), DriverError<IFACE::Error, RST::Error>> {
self.send_command(commands::DISPOFF)
}
pub fn display_on(&mut self) -> Result<(), DriverError<IFACE::Error, RST::Error>> {
self.send_command(commands::DISPON)
}
pub fn set_madctl(&mut self, value: u8) -> Result<(), DriverError<IFACE::Error, RST::Error>> {
self.send_command_with_data(commands::MADCTL, &[value])
}
pub fn set_brightness(
&mut self,
value: u8,
) -> Result<(), DriverError<IFACE::Error, RST::Error>> {
self.send_command_with_data(commands::WRDISBV, &[value])
}
pub fn size(&self) -> DisplaySize {
self.config
}
}
impl<IFACE, RST, COLOR: St77916Color> St77916<IFACE, RST, Buffered<COLOR>>
where
IFACE: ControllerInterface,
RST: ResetInterface,
{
pub fn flush(&mut self) -> Result<(), DriverError<IFACE::Error, RST::Error>> {
if self.buffer.dirty.is_clean() {
return Ok(());
}
let y_min = self.buffer.dirty.y_min;
let y_max = self.buffer.dirty.y_max;
let bpp = COLOR::BYTES_PER_PIXEL;
let stride = self.config.width as usize * bpp;
self.set_window(0, y_min, self.config.width - 1, y_max)?;
let start = y_min as usize * stride;
let end = (y_max as usize + 1) * stride;
self.interface
.send_pixels(&self.buffer.data[start..end])
.map_err(DriverError::InterfaceError)?;
self.buffer.dirty.reset();
Ok(())
}
pub fn full_flush(&mut self) -> Result<(), DriverError<IFACE::Error, RST::Error>> {
self.set_window(0, 0, self.config.width - 1, self.config.height - 1)?;
self.interface
.send_pixels(&self.buffer.data)
.map_err(DriverError::InterfaceError)?;
self.buffer.dirty.reset();
Ok(())
}
pub fn framebuffer_mut(&mut self) -> &mut [u8] {
self.buffer.data.as_mut_slice()
}
pub fn framebuffer(&self) -> &[u8] {
self.buffer.data.as_slice()
}
pub fn mark_dirty(&mut self, y_start: u16, y_end: u16) {
self.buffer.dirty.mark(y_start, y_end);
}
pub fn mark_all_dirty(&mut self) {
self.buffer.dirty.mark_all(self.config.height);
}
pub fn is_clean(&self) -> bool {
self.buffer.dirty.is_clean()
}
pub fn partial_flush(
&mut self,
x_start: u16,
x_end: u16,
y_start: u16,
y_end: u16,
) -> Result<(), DriverError<IFACE::Error, RST::Error>> {
self.set_window(x_start, y_start, x_end, y_end)?;
let bpp = COLOR::BYTES_PER_PIXEL;
let fb_stride = self.config.width as usize * bpp;
let row_width = (x_end - x_start + 1) as usize * bpp;
for y in y_start..=y_end {
let offset = y as usize * fb_stride + x_start as usize * bpp;
let row_end = offset + row_width;
if row_end <= self.buffer.data.len() {
self.interface
.send_pixels(&self.buffer.data[offset..row_end])
.map_err(DriverError::InterfaceError)?;
} else {
return Err(DriverError::InvalidConfiguration(
"Framebuffer slice out of bounds",
));
}
}
Ok(())
}
}
impl<IFACE, RST, COLOR: St77916Color> St77916<IFACE, RST, DoubleBuffered<COLOR>>
where
IFACE: ControllerInterface,
RST: ResetInterface,
{
pub fn swap_buffers(&mut self) {
self.buffer.back_idx ^= 1;
}
pub fn flush_front(&mut self) -> Result<(), DriverError<IFACE::Error, RST::Error>> {
self.set_window(0, 0, self.config.width - 1, self.config.height - 1)?;
self.interface
.send_pixels(self.buffer.front())
.map_err(DriverError::InterfaceError)
}
pub fn flush(&mut self) -> Result<(), DriverError<IFACE::Error, RST::Error>> {
self.set_window(0, 0, self.config.width - 1, self.config.height - 1)?;
self.interface
.send_pixels(self.buffer.back())
.map_err(DriverError::InterfaceError)
}
pub fn back_buffer_mut(&mut self) -> &mut [u8] {
self.buffer.back_mut().as_mut_slice()
}
pub fn front_buffer(&self) -> &[u8] {
self.buffer.front().as_slice()
}
}