use embedded_hal::{
delay::*,
digital::{InputPin, OutputPin},
spi::{Operation, SpiDevice},
};
#[cfg(feature = "defmt")]
use defmt;
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
SpiError,
GPIOError,
BusyTimeout,
BufferAlignment,
}
pub trait IT8951Interface {
fn set_busy_timeout(&mut self, timeout: core::time::Duration);
fn wait_while_busy(&mut self) -> Result<(), Error>;
fn write_data(&mut self, data: u16) -> Result<(), Error>;
fn write_multi_data(&mut self, data: &[u8]) -> Result<(), Error>;
fn write_command(&mut self, cmd: u16) -> Result<(), Error>;
fn write_command_with_args(&mut self, cmd: u16, args: &[u16]) -> Result<(), Error> {
self.write_command(cmd)?;
for arg in args {
self.write_data(*arg)?;
}
Ok(())
}
fn read_data(&mut self) -> Result<u16, Error>;
fn read_multi_data(&mut self, buf: &mut [u8]) -> Result<(), Error>;
fn reset(&mut self) -> Result<(), Error>;
fn delay(&mut self, duration: core::time::Duration) -> Result<(), Error>;
}
pub struct IT8951SPIInterface<SPI, BUSY, RST, DELAY> {
spi: SPI,
busy: BUSY,
rst: Option<RST>,
delay: DELAY,
timeout: core::time::Duration,
}
impl<SPI, BUSY, RST, DELAY> IT8951SPIInterface<SPI, BUSY, RST, DELAY>
where
SPI: SpiDevice,
BUSY: InputPin,
RST: OutputPin,
DELAY: DelayNs,
{
pub fn new(
spi: SPI,
busy: BUSY,
rst: RST,
delay: DELAY,
) -> IT8951SPIInterface<SPI, BUSY, RST, DELAY> {
IT8951SPIInterface {
spi,
busy,
rst: Some(rst),
delay,
timeout: core::time::Duration::from_secs(1),
}
}
pub fn new_no_rst(
spi: SPI,
busy: BUSY,
delay: DELAY,
) -> IT8951SPIInterface<SPI, BUSY, RST, DELAY> {
IT8951SPIInterface {
spi,
busy,
rst: None,
delay,
timeout: core::time::Duration::from_secs(1),
}
}
}
impl<SPI, BUSY, RST, DELAY> IT8951Interface for IT8951SPIInterface<SPI, BUSY, RST, DELAY>
where
SPI: SpiDevice,
BUSY: InputPin,
RST: OutputPin,
DELAY: DelayNs,
{
fn set_busy_timeout(&mut self, timeout: core::time::Duration) {
self.timeout = timeout
}
fn wait_while_busy(&mut self) -> Result<(), Error> {
let timeout_us = self.timeout.as_micros() as u32;
const BACKOFF_CAP_US: u32 = 1000;
let mut delay_us = 200_u32;
let mut accumulated_delay_us = 0_u32;
while self.busy.is_low().map_err(|_| Error::GPIOError)? {
if accumulated_delay_us > timeout_us {
#[cfg(feature = "defmt")]
defmt::warn!("Timeout while waiting, waited {}μs", timeout_us);
return Err(Error::BusyTimeout);
}
self.delay.delay_us(delay_us);
accumulated_delay_us += delay_us;
if delay_us < BACKOFF_CAP_US {
delay_us *= 2;
}
}
Ok(())
}
fn write_data(&mut self, data: u16) -> Result<(), Error> {
self.wait_while_busy()?;
let buf = [0x00, 0x00, (data >> 8) as u8, data as u8];
if self.spi.write(&buf).is_err() {
#[cfg(feature = "defmt")]
defmt::warn!("SPI Error while writing");
return Err(Error::SpiError);
}
Ok(())
}
fn write_multi_data(&mut self, data: &[u8]) -> Result<(), Error> {
self.wait_while_busy()?;
if !data.len().is_multiple_of(2) {
#[cfg(feature = "defmt")]
defmt::warn!("Buffer alignment error");
return Err(Error::BufferAlignment);
};
if self
.spi
.transaction(&mut [Operation::Write(&[0x00, 0x00]), Operation::Write(data)])
.is_err()
{
#[cfg(feature = "defmt")]
defmt::warn!("SPI Error while writing");
return Err(Error::SpiError);
}
Ok(())
}
fn write_command(&mut self, cmd: u16) -> Result<(), Error> {
self.wait_while_busy()?;
let buf = [0x60, 0x00, (cmd >> 8) as u8, cmd as u8];
if self.spi.write(&buf).is_err() {
#[cfg(feature = "defmt")]
defmt::warn!("SPI Error while writing");
return Err(Error::SpiError);
}
Ok(())
}
fn read_data(&mut self) -> Result<u16, Error> {
self.wait_while_busy()?;
let mut buf = [0x10, 0x00, 0x00, 0x00, 0x00, 0x00];
if self.spi.transfer_in_place(&mut buf).is_err() {
#[cfg(feature = "defmt")]
defmt::warn!("SPI Error while reading");
return Err(Error::SpiError);
}
Ok(u16::from_be_bytes([buf[4], buf[5]]))
}
fn read_multi_data(&mut self, buf: &mut [u8]) -> Result<(), Error> {
self.wait_while_busy()?;
if !buf.len().is_multiple_of(2) {
#[cfg(feature = "defmt")]
defmt::warn!("Buffer alignment error");
return Err(Error::BufferAlignment);
};
let cmd = [0x10_u8, 0x00, 0x00, 0x00];
if self
.spi
.transaction(&mut [Operation::Write(&cmd), Operation::TransferInPlace(buf)])
.is_err()
{
#[cfg(feature = "defmt")]
defmt::warn!("SPI Error while reading");
return Err(Error::SpiError);
}
Ok(())
}
fn reset(&mut self) -> Result<(), Error> {
let Some(rst) = self.rst.as_mut() else {
return Ok(());
};
if rst.set_high().is_err() {
#[cfg(feature = "defmt")]
defmt::warn!("IO Error while resetting");
return Err(Error::GPIOError);
}
self.delay.delay_ms(200);
if rst.set_low().is_err() {
#[cfg(feature = "defmt")]
defmt::warn!("IO Error while resetting");
return Err(Error::GPIOError);
}
self.delay.delay_ms(20);
if rst.set_high().is_err() {
#[cfg(feature = "defmt")]
defmt::warn!("IO Error while resetting");
return Err(Error::GPIOError);
}
self.delay.delay_ms(200);
Ok(())
}
fn delay(&mut self, duration: core::time::Duration) -> Result<(), Error> {
self.delay.delay_us(duration.as_micros() as u32);
Ok(())
}
}