use core::marker::PhantomData;
use crate::time::Hertz;
pub use crate::traits::wg::spi::{FullDuplex, Mode, Phase, Polarity};
use crate::typestates::pin::{
flexcomm::{
ChipSelect,
Spi,
SpiPins,
},
PinId,
};
pub mod prelude {
pub use super::Error as SpiError;
pub use super::Result as SpiResult;
pub use super::SpiMaster;
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Overrun,
ModeFault,
Crc,
}
pub type Result<T> = nb::Result<T, Error>;
pub struct SpiMaster<SCK, MOSI, MISO, CS, SPI, PINS>
where
SCK: PinId,
MOSI: PinId,
MISO: PinId,
CS: PinId,
SPI: Spi,
PINS: SpiPins<SCK, MOSI, MISO, CS, SPI>,
{
spi: SPI,
pins: PINS,
_sck: PhantomData<SCK>,
_mosi: PhantomData<MOSI>,
_miso: PhantomData<MISO>,
_cs: PhantomData<CS>,
cs: ChipSelect,
}
impl<SCK, MOSI, MISO, CS, SPI, PINS> SpiMaster<SCK, MOSI, MISO, CS, SPI, PINS>
where
SCK: PinId,
MOSI: PinId,
MISO: PinId,
CS: PinId,
SPI: Spi,
PINS: SpiPins<SCK, MOSI, MISO, CS, SPI>,
{
pub fn new<Speed: Into<Hertz>>(spi: SPI, pins: PINS, speed: Speed, mode: Mode) -> Self {
let speed: Hertz = speed.into();
let speed: u32 = speed.0;
while spi.stat.read().mstidle().bit_is_clear() {
continue;
}
spi.fifocfg
.modify(|_, w| w.enabletx().disabled().enablerx().disabled());
spi.cfg.modify(|_, w| {
w.enable()
.disabled()
.master()
.master_mode()
.lsbf()
.standard() .cpha()
.bit(mode.phase == Phase::CaptureOnSecondTransition)
.cpol()
.bit(mode.polarity == Polarity::IdleHigh)
.loop_()
.disabled()
});
let div: u32 = 12_000_000 / speed - 1;
debug_assert!(div <= 0xFFFF);
spi.div
.modify(|_, w| unsafe { w.divval().bits(div as u16) });
spi.fifocfg
.modify(|_, w| w.enabletx().enabled().enablerx().enabled());
spi.cfg.modify(|_, w| w.enable().enabled());
Self {
spi,
pins,
_sck: PhantomData,
_mosi: PhantomData,
_miso: PhantomData,
_cs: PhantomData,
cs: PINS::CS,
}
}
pub fn release(self) -> (SPI, PINS) {
(self.spi, self.pins)
}
fn return_on_error(&self) -> Result<()> {
Ok(())
}
}
impl<SCK, MOSI, MISO, CS, SPI, PINS> FullDuplex<u8> for SpiMaster<SCK, MOSI, MISO, CS, SPI, PINS>
where
SCK: PinId,
MOSI: PinId,
MISO: PinId,
CS: PinId,
SPI: Spi,
PINS: SpiPins<SCK, MOSI, MISO, CS, SPI>,
{
type Error = Error;
fn read(&mut self) -> Result<u8> {
if self.spi.fifostat.read().rxnotempty().bit_is_set() {
let byte = self.spi.fiford.read().rxdata().bits();
Ok(byte as u8)
} else {
Err(nb::Error::WouldBlock)
}
}
fn send(&mut self, byte: u8) -> Result<()> {
self.return_on_error()?;
if self.spi.fifostat.read().txnotfull().bit_is_set() {
use ChipSelect::*;
match self.cs {
Chip0 => {
self.spi.fifowr.write(|w| unsafe {
w
.len()
.bits(7) .txssel0_n()
.asserted()
.txdata()
.bits(byte as u16)
});
}
Chip1 => {
self.spi.fifowr.write(|w| unsafe {
w
.len()
.bits(7) .txssel1_n()
.asserted()
.txdata()
.bits(byte as u16)
});
}
Chip2 => {
self.spi.fifowr.write(|w| unsafe {
w
.len()
.bits(7) .txssel2_n()
.asserted()
.txdata()
.bits(byte as u16)
});
}
Chip3 => {
self.spi.fifowr.write(|w| unsafe {
w
.len()
.bits(7) .txssel3_n()
.asserted()
.txdata()
.bits(byte as u16)
});
}
NoChips => {
self.spi.fifowr.write(|w| unsafe {
w
.len()
.bits(7) .txdata()
.bits(byte as u16)
});
}
}
Ok(())
} else {
Err(nb::Error::WouldBlock)
}
}
}
impl<SCK, MOSI, MISO, CS, SPI, PINS> crate::traits::wg::blocking::spi::transfer::Default<u8>
for SpiMaster<SCK, MOSI, MISO, CS, SPI, PINS>
where
SCK: PinId,
MOSI: PinId,
MISO: PinId,
CS: PinId,
SPI: Spi,
PINS: SpiPins<SCK, MOSI, MISO, CS, SPI>,
{
}
impl<SCK, MOSI, MISO, CS, SPI, PINS> crate::traits::wg::blocking::spi::write::Default<u8>
for SpiMaster<SCK, MOSI, MISO, CS, SPI, PINS>
where
SCK: PinId,
MOSI: PinId,
MISO: PinId,
CS: PinId,
SPI: Spi,
PINS: SpiPins<SCK, MOSI, MISO, CS, SPI>,
{
}