use core::future::poll_fn;
use core::task::Poll;
use embassy_hal_internal::{Peri, PeripheralType};
use embassy_sync::waitqueue::AtomicWaker;
use embedded_hal::spi::{ErrorKind, ErrorType, MODE_0, Mode, Phase, Polarity};
use pac::peripherals::lpspi::{LPSPI, LPSPI_TDR};
use crate::pac;
const FRAMESZ_8BIT: u16 = 7;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
Overrun,
Underrun,
}
impl embedded_hal::spi::Error for Error {
fn kind(&self) -> ErrorKind {
match self {
Error::Overrun => ErrorKind::Overrun,
Error::Underrun => ErrorKind::Other,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Config {
pub frequency: u32,
pub mode: Mode,
pub lsb_first: bool,
pub pcs: u8,
}
impl Default for Config {
fn default() -> Self {
Self {
frequency: 1_000_000,
mode: MODE_0,
lsb_first: false,
pcs: 0,
}
}
}
const fn sckdiv(clock_hz: u32, frequency: u32) -> u8 {
assert!(frequency > 0);
let div = clock_hz.div_ceil(frequency);
if div < 2 {
0
} else {
let d = div - 2;
if d > 255 { 255 } else { d as u8 }
}
}
pub struct State {
waker: AtomicWaker,
}
impl State {
pub(crate) const fn new() -> Self {
Self {
waker: AtomicWaker::new(),
}
}
fn on_interrupt(&self, r: LPSPI) {
let sr = r.SR().read();
let ier = r.IER().read();
let fire = (ier.TDIE() && sr.TDF())
|| (ier.RDIE() && sr.RDF())
|| (ier.TEIE() && sr.TEF())
|| (ier.REIE() && sr.REF());
if fire {
r.IER().modify(|w| {
w.set_TDIE(false);
w.set_RDIE(false);
w.set_TEIE(false);
w.set_REIE(false);
});
self.waker.wake();
}
}
}
pub(crate) trait SealedInstance {
fn regs() -> LPSPI;
fn state() -> &'static State;
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + PeripheralType {
const IRQ: Option<u16>;
}
macro_rules! impl_spi {
($instance:ident, $base:expr, $irq:expr) => {
impl crate::spi::SealedInstance for crate::peripherals::$instance {
#[inline]
fn regs() -> crate::pac::peripherals::lpspi::LPSPI {
unsafe { crate::pac::peripherals::lpspi::LPSPI::from_ptr($base as *mut ()) }
}
#[inline]
fn state() -> &'static crate::spi::State {
static STATE: crate::spi::State = crate::spi::State::new();
&STATE
}
}
impl crate::spi::Instance for crate::peripherals::$instance {
const IRQ: ::core::option::Option<u16> = $irq;
}
};
}
pub(crate) use impl_spi;
pub fn on_interrupt<T: Instance>() {
T::state().on_interrupt(T::regs());
}
pub struct Spi {
regs: LPSPI,
state: &'static State,
cpol: bool,
cpha: bool,
lsb_first: bool,
pcs: u8,
}
impl Spi {
pub fn new<T: Instance>(peri: Peri<'static, T>, clock_hz: u32, config: Config) -> Self {
let _ = peri;
let r = T::regs();
r.CR().write(|w| w.set_RST(true));
r.CR().write(|w| {
w.set_RST(false);
w.set_RTF(true);
w.set_RRF(true);
});
r.CFGR1().modify(|w| w.set_MASTER(true));
r.CCR()
.modify(|w| w.set_SCKDIV(sckdiv(clock_hz, config.frequency)));
r.FCR().write(|w| {
w.set_TXWATER(0);
w.set_RXWATER(0);
});
r.CR().modify(|w| w.set_MEN(true));
let (cpol, cpha) = mode_bits(config.mode);
Self {
regs: r,
state: T::state(),
cpol,
cpha,
lsb_first: config.lsb_first,
pcs: config.pcs,
}
}
fn tx_fifo_len(&self) -> u32 {
1 << self.regs.PARAM().read().TXFIFO()
}
fn set_frame(&self, cont: bool) {
self.regs.TCR().write(|w| {
w.set_FRAMESZ(FRAMESZ_8BIT);
w.set_CPOL(self.cpol);
w.set_CPHA(self.cpha);
w.set_LSBF(self.lsb_first);
w.set_PCS(self.pcs);
w.set_CONT(cont);
});
}
fn check_error(&self) -> Result<(), Error> {
let sr = self.regs.SR().read();
if sr.REF() {
self.regs.SR().write(|w| w.set_REF(true));
return Err(Error::Overrun);
}
if sr.TEF() {
self.regs.SR().write(|w| w.set_TEF(true));
return Err(Error::Underrun);
}
Ok(())
}
fn transfer_word(&mut self, tx: u8, cont: bool) -> Result<u8, Error> {
let depth = self.tx_fifo_len();
loop {
self.check_error()?;
if u32::from(self.regs.FSR().read().TXCOUNT()) < depth {
break;
}
}
self.set_frame(cont);
self.regs.TDR().write_value(LPSPI_TDR(u32::from(tx)));
loop {
self.check_error()?;
if self.regs.FSR().read().RXCOUNT() > 0 {
return Ok(self.regs.RDR().read().DATA() as u8);
}
}
}
fn transfer_blocking(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
let len = read.len().max(write.len());
for i in 0..len {
let tx = write.get(i).copied().unwrap_or(0);
let cont = i + 1 < len;
let rx = self.transfer_word(tx, cont)?;
if let Some(slot) = read.get_mut(i) {
*slot = rx;
}
}
Ok(())
}
async fn wait_flag(
&self,
arm: fn(&LPSPI),
ready: impl Fn(&Self) -> Option<Result<(), Error>>,
) -> Result<(), Error> {
poll_fn(|cx| {
if let Some(res) = ready(self) {
return Poll::Ready(res);
}
self.state.waker.register(cx.waker());
critical_section::with(|_| arm(&self.regs));
if let Some(res) = ready(self) {
return Poll::Ready(res);
}
Poll::Pending
})
.await
}
fn arm_errors(r: &LPSPI) {
r.IER().modify(|w| {
w.set_TEIE(true);
w.set_REIE(true);
});
}
async fn wait_tx_space(&self) -> Result<(), Error> {
let depth = self.tx_fifo_len();
self.wait_flag(
|r| {
Self::arm_errors(r);
r.IER().modify(|w| w.set_TDIE(true));
},
|s| match s.check_error() {
Err(e) => Some(Err(e)),
Ok(()) if u32::from(s.regs.FSR().read().TXCOUNT()) < depth => Some(Ok(())),
Ok(()) => None,
},
)
.await
}
async fn wait_rx(&self) -> Result<(), Error> {
self.wait_flag(
|r| {
Self::arm_errors(r);
r.IER().modify(|w| w.set_RDIE(true));
},
|s| match s.check_error() {
Err(e) => Some(Err(e)),
Ok(()) if s.regs.FSR().read().RXCOUNT() > 0 => Some(Ok(())),
Ok(()) => None,
},
)
.await
}
async fn transfer_word_async(&mut self, tx: u8, cont: bool) -> Result<u8, Error> {
self.wait_tx_space().await?;
self.set_frame(cont);
self.regs.TDR().write_value(LPSPI_TDR(u32::from(tx)));
self.wait_rx().await?;
Ok(self.regs.RDR().read().DATA() as u8)
}
async fn transfer_async(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
let len = read.len().max(write.len());
for i in 0..len {
let tx = write.get(i).copied().unwrap_or(0);
let cont = i + 1 < len;
let rx = self.transfer_word_async(tx, cont).await?;
if let Some(slot) = read.get_mut(i) {
*slot = rx;
}
}
Ok(())
}
}
fn mode_bits(mode: Mode) -> (bool, bool) {
(
matches!(mode.polarity, Polarity::IdleHigh),
matches!(mode.phase, Phase::CaptureOnSecondTransition),
)
}
impl ErrorType for Spi {
type Error = Error;
}
impl embedded_hal::spi::SpiBus<u8> for Spi {
fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {
self.transfer_blocking(words, &[])
}
fn write(&mut self, words: &[u8]) -> Result<(), Error> {
self.transfer_blocking(&mut [], words)
}
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
self.transfer_blocking(read, write)
}
fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> {
let len = words.len();
for (i, word) in words.iter_mut().enumerate() {
let cont = i + 1 < len;
*word = self.transfer_word(*word, cont)?;
}
Ok(())
}
fn flush(&mut self) -> Result<(), Error> {
while self.regs.SR().read().MBF() {
self.check_error()?;
}
Ok(())
}
}
impl embedded_hal_async::spi::SpiBus<u8> for Spi {
async fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {
self.transfer_async(words, &[]).await
}
async fn write(&mut self, words: &[u8]) -> Result<(), Error> {
self.transfer_async(&mut [], words).await
}
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
self.transfer_async(read, write).await
}
async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> {
let len = words.len();
for (i, word) in words.iter_mut().enumerate() {
let cont = i + 1 < len;
*word = self.transfer_word_async(*word, cont).await?;
}
Ok(())
}
async fn flush(&mut self) -> Result<(), Error> {
while self.regs.SR().read().MBF() {
self.check_error()?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::sckdiv;
#[test]
fn sckdiv_rounds_up_to_stay_within_frequency() {
assert_eq!(sckdiv(100_000_000, 1_000_000), 98);
}
#[test]
fn sckdiv_saturates_and_floors() {
assert_eq!(sckdiv(1_000_000, 100_000_000), 0);
assert_eq!(sckdiv(u32::MAX, 1), 255);
}
}