#![no_std]
extern crate alloc;
pub mod commands;
#[cfg(feature = "embedded-graphics")]
mod embedded_graphics;
#[cfg(feature = "embedded-graphics")]
pub use embedded_graphics::*;
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 type DriverResult<T, IFACE, RST> =
Result<T, DriverError<<IFACE as ControllerInterface>::Error, <RST as ResetInterface>::Error>>;
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 struct St77916<IFACE, RST, BUF = ()>
where
IFACE: ControllerInterface,
RST: ResetInterface,
{
pub(crate) interface: IFACE,
pub(crate) reset: RST,
pub(crate) config: DisplaySize,
#[allow(dead_code)] pub(crate) buffer: BUF,
}
pub struct St77916Builder<IFACE, RST, BUF = ()>
where
IFACE: ControllerInterface,
RST: ResetInterface,
{
pub(crate) interface: IFACE,
pub(crate) reset: RST,
pub(crate) config: DisplaySize,
pub(crate) init_commands: Option<&'static [(u8, &'static [u8], u16)]>,
#[allow(dead_code)] pub(crate) 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 build<DELAY>(
self,
color: ColorMode,
delay: &mut DELAY,
) -> DriverResult<St77916<IFACE, RST>, IFACE, RST>
where
DELAY: DelayNs,
{
build_driver(
self.interface,
self.reset,
self.config,
self.init_commands,
(),
delay,
color,
)
}
}
pub(crate) fn build_driver<IFACE, RST, BUF, DELAY>(
interface: IFACE,
reset: RST,
config: DisplaySize,
init_commands: Option<&[(u8, &[u8], u16)]>,
buffer: BUF,
delay: &mut DELAY,
color: ColorMode,
) -> DriverResult<St77916<IFACE, RST, BUF>, IFACE, RST>
where
IFACE: ControllerInterface,
RST: ResetInterface,
DELAY: DelayNs,
{
let mut driver = St77916 {
interface,
reset,
config,
buffer,
};
driver.hard_reset()?;
run_init(&mut driver.interface, 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) -> DriverResult<(), IFACE, RST> {
self.reset.reset().map_err(DriverError::ResetError)
}
pub fn send_command(&mut self, cmd: u8) -> DriverResult<(), IFACE, RST> {
self.interface
.send_command(cmd)
.map_err(DriverError::InterfaceError)
}
pub fn send_command_with_data(
&mut self,
cmd: u8,
data: &[u8],
) -> DriverResult<(), IFACE, RST> {
self.interface
.send_command_with_data(cmd, data)
.map_err(DriverError::InterfaceError)
}
pub fn send_pixels(&mut self, pixels: &[u8]) -> DriverResult<(), IFACE, RST> {
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,
) -> DriverResult<(), IFACE, RST> {
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]) -> DriverResult<(), IFACE, RST> {
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) -> DriverResult<(), IFACE, RST>
where
DELAY: DelayNs,
{
self.send_command(commands::SLPIN)?;
delay.delay_ms(5);
Ok(())
}
pub fn sleep_out<DELAY>(&mut self, delay: &mut DELAY) -> DriverResult<(), IFACE, RST>
where
DELAY: DelayNs,
{
self.send_command(commands::SLPOUT)?;
delay.delay_ms(120);
Ok(())
}
pub fn display_off(&mut self) -> DriverResult<(), IFACE, RST> {
self.send_command(commands::DISPOFF)
}
pub fn display_on(&mut self) -> DriverResult<(), IFACE, RST> {
self.send_command(commands::DISPON)
}
pub fn set_madctl(&mut self, value: u8) -> DriverResult<(), IFACE, RST> {
self.send_command_with_data(commands::MADCTL, &[value])
}
pub fn set_brightness(&mut self, value: u8) -> DriverResult<(), IFACE, RST> {
self.send_command_with_data(commands::WRDISBV, &[value])
}
pub fn size(&self) -> DisplaySize {
self.config
}
}