use crate::clock::{get_master_clock_frequency, Enabled, SpiClock};
use crate::gpio::{Pa12, Pa13, Pa14, PfA};
use crate::pac::SPI;
use crate::pdc::*;
use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering};
use embedded_dma::{ReadBuffer, WriteBuffer};
use paste::paste;
pub use embedded_hal::spi;
pub use fugit::HertzU32 as Hertz;
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct SpiU8(u8);
impl From<u8> for SpiU8 {
fn from(val: u8) -> Self {
Self(val)
}
}
impl From<u16> for SpiU8 {
fn from(val: u16) -> Self {
Self(val as _)
}
}
impl From<SpiU16> for SpiU8 {
fn from(val: SpiU16) -> Self {
Self(val.0 as _)
}
}
impl From<SpiU8> for u8 {
fn from(val: SpiU8) -> Self {
val.0 as _
}
}
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct SpiU16(u16);
impl From<u8> for SpiU16 {
fn from(val: u8) -> Self {
Self(val as _)
}
}
impl From<u16> for SpiU16 {
fn from(val: u16) -> Self {
Self(val)
}
}
impl From<SpiU8> for SpiU16 {
fn from(val: SpiU8) -> Self {
Self(val.0 as _)
}
}
impl From<SpiU16> for u16 {
fn from(val: SpiU16) -> Self {
val.0 as _
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, defmt::Format)]
pub enum Error {
Overrun,
Underrun,
ModeFault,
SpiDisabled,
InvalidCs(u8),
FixedModeSet,
VariableModeSet,
UnexpectedPcs(u16, u8),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, defmt::Format)]
pub enum ChipSelectActive {
ActiveAfterTransfer,
ActiveOnConsecutiveTransfers,
InactiveAfterEachTransfer,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, defmt::Format)]
pub enum BitWidth {
Width8Bit = 0,
Width9Bit = 1,
Width10Bit = 2,
Width11Bit = 3,
Width12Bit = 4,
Width13Bit = 5,
Width14Bit = 6,
Width15Bit = 7,
Width16Bit = 8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, defmt::Format)]
pub enum PeripheralSelectMode {
Fixed,
Variable,
ChipSelectDecode,
}
#[derive(Clone, PartialEq, Eq)]
pub struct ChipSelectSettings {
mode: spi::Mode,
csa: ChipSelectActive,
scbr: u8,
dlybs: u8,
dlybct: u8,
bits: BitWidth,
}
impl ChipSelectSettings {
pub fn new(
mode: spi::Mode,
csa: ChipSelectActive,
bits: BitWidth,
baud: Hertz,
dlybs: u8,
dlybct: u8,
) -> ChipSelectSettings {
let pclk = get_master_clock_frequency();
let scbr = ((pclk.raw() + baud.raw() - 1) / baud.raw()) as u8;
if scbr < 1 {
panic!("scbr must be greater than 0: {}", scbr);
}
ChipSelectSettings {
mode,
csa,
scbr,
dlybs,
dlybct,
bits,
}
}
}
pub struct SpiMaster<FRAMESIZE> {
spi: SPI,
clock: PhantomData<SpiClock<Enabled>>,
miso: PhantomData<Pa12<PfA>>,
mosi: PhantomData<Pa13<PfA>>,
spck: PhantomData<Pa14<PfA>>,
cs: u8,
lastxfer: bool,
framesize: PhantomData<FRAMESIZE>,
}
impl<FRAMESIZE> SpiMaster<FRAMESIZE> {
#[allow(clippy::too_many_arguments)]
pub fn new(
spi: SPI,
_clock: SpiClock<Enabled>,
_miso: Pa12<PfA>,
_mosi: Pa13<PfA>,
_spck: Pa14<PfA>,
psm: PeripheralSelectMode,
wdrbt: bool,
llb: bool,
dlybcs: u8,
) -> SpiMaster<FRAMESIZE> {
unsafe {
spi.cr.write_with_zero(|w| w.spidis().set_bit());
spi.cr.write_with_zero(|w| w.swrst().set_bit());
spi.cr.write_with_zero(|w| w.spien().set_bit());
let (ps, pcsdec) = match psm {
PeripheralSelectMode::Fixed => (false, false),
PeripheralSelectMode::Variable => (true, false),
PeripheralSelectMode::ChipSelectDecode => (true, true),
};
spi.wpmr
.write_with_zero(|w| w.wpkey().bits(0x535049).wpen().clear_bit());
spi.mr.write_with_zero(|w| {
w.mstr()
.set_bit()
.ps()
.bit(ps)
.pcsdec()
.bit(pcsdec)
.modfdis()
.clear_bit()
.wdrbt()
.bit(wdrbt)
.llb()
.bit(llb)
.dlybcs()
.bits(dlybcs)
});
}
SpiMaster {
spi,
clock: PhantomData,
miso: PhantomData,
mosi: PhantomData,
spck: PhantomData,
cs: 0, lastxfer: false, framesize: PhantomData,
}
}
pub fn cs_setup(&mut self, cs: u8, settings: ChipSelectSettings) -> Result<(), Error> {
let cs = if self.spi.mr.read().pcsdec().bit_is_set() {
match cs {
0..=3 => 0,
4..=7 => 1,
8..=11 => 2,
12..=14 => 3,
_ => {
return Err(Error::InvalidCs(cs));
}
}
} else if cs > 3 {
return Err(Error::InvalidCs(cs));
} else {
cs
};
let cpol = match settings.mode.polarity {
spi::Polarity::IdleLow => false,
spi::Polarity::IdleHigh => true,
};
let ncpha = match settings.mode.phase {
spi::Phase::CaptureOnFirstTransition => true,
spi::Phase::CaptureOnSecondTransition => false,
};
let (csaat, csnaat) = match settings.csa {
ChipSelectActive::ActiveAfterTransfer => (true, false),
ChipSelectActive::ActiveOnConsecutiveTransfers => (false, false),
ChipSelectActive::InactiveAfterEachTransfer => (false, true),
};
unsafe {
self.spi.csr[cs as usize].write_with_zero(|w| {
w.cpol()
.bit(cpol)
.ncpha()
.bit(ncpha)
.csnaat()
.bit(csnaat)
.csaat()
.bit(csaat)
.bits_()
.bits(settings.bits as u8)
.scbr()
.bits(settings.scbr)
.dlybs()
.bits(settings.dlybs)
.dlybct()
.bits(settings.dlybct)
});
}
Ok(())
}
pub fn cs_select(&mut self, cs: u8) -> Result<(), Error> {
let pcs_id = match cs {
0 => 0b0000, 1 => 0b0001, 2 => 0b0011, 3 => 0b0111, _ => 0b1111, };
if self.spi.mr.read().ps().bit_is_clear() {
self.spi.mr.modify(|_, w| unsafe { w.pcs().bits(pcs_id) });
} else {
if self.spi.mr.read().pcsdec().bit_is_set() {
if cs > 15 {
return Err(Error::InvalidCs(cs));
}
self.cs = cs;
} else {
if cs > 3 {
return Err(Error::InvalidCs(cs));
}
self.cs = pcs_id;
}
}
Ok(())
}
pub fn lastxfer(&mut self, lastxfer: bool) {
if self.spi.mr.read().ps().bit_is_clear() {
unsafe {
self.spi.cr.write_with_zero(|w| w.lastxfer().set_bit());
}
} else {
self.lastxfer = lastxfer;
}
}
pub fn enable_rdrf_interrupt(&mut self) {
unsafe {
self.spi.ier.write_with_zero(|w| w.rdrf().set_bit());
}
}
pub fn disable_rdrf_interrupt(&mut self) {
unsafe {
self.spi.idr.write_with_zero(|w| w.rdrf().set_bit());
}
}
pub fn enable_tdre_interrupt(&mut self) {
unsafe {
self.spi.ier.write_with_zero(|w| w.tdre().set_bit());
}
}
pub fn disable_tdre_interrupt(&mut self) {
unsafe {
self.spi.idr.write_with_zero(|w| w.tdre().set_bit());
}
}
pub fn enable_modf_interrupt(&mut self) {
unsafe {
self.spi.ier.write_with_zero(|w| w.modf().set_bit());
}
}
pub fn disable_modf_interrupt(&mut self) {
unsafe {
self.spi.idr.write_with_zero(|w| w.modf().set_bit());
}
}
pub fn enable_ovres_interrupt(&mut self) {
unsafe {
self.spi.ier.write_with_zero(|w| w.ovres().set_bit());
}
}
pub fn disable_ovres_interrupt(&mut self) {
unsafe {
self.spi.idr.write_with_zero(|w| w.ovres().set_bit());
}
}
}
fn variable_pcs_to_cs(pcs: u8) -> Result<u8, Error> {
if (pcs & 0x1) == 0 {
Ok(0)
} else if (pcs & 0x2) == 0 {
Ok(1)
} else if (pcs & 0x4) == 0 {
Ok(2)
} else if (pcs & 0x8) == 0 {
Ok(3)
} else {
Err(Error::InvalidCs(0xF))
}
}
impl<FRAMESIZE> spi::FullDuplex<FRAMESIZE> for SpiMaster<FRAMESIZE>
where
FRAMESIZE: Copy + From<SpiU16>,
SpiU16: From<FRAMESIZE> + From<SpiU8>,
u8: From<FRAMESIZE>,
{
type Error = Error;
fn read(&mut self) -> nb::Result<FRAMESIZE, Error> {
let sr = self.spi.sr.read();
Err(if sr.ovres().bit_is_set() {
defmt::trace!("Send overrun");
nb::Error::Other(Error::Overrun)
} else if sr.modf().bit_is_set() {
defmt::trace!("Mode fault");
nb::Error::Other(Error::ModeFault)
} else if sr.spiens().bit_is_clear() {
defmt::trace!("SPI disabled");
nb::Error::Other(Error::SpiDisabled)
} else if sr.rdrf().bit_is_set() {
let rdr = self.spi.rdr.read();
if self.spi.mr.read().ps().bit_is_set()
&& variable_pcs_to_cs(rdr.pcs().bits())? != self.cs
{
nb::Error::Other(Error::UnexpectedPcs(rdr.rd().bits(), rdr.pcs().bits()))
} else {
return Ok(SpiU16(rdr.rd().bits()).into());
}
} else {
nb::Error::WouldBlock
})
}
fn send(&mut self, byte: FRAMESIZE) -> nb::Result<(), Error> {
let sr = self.spi.sr.read();
Err(if sr.ovres().bit_is_set() {
defmt::trace!("Send overrun");
nb::Error::Other(Error::Overrun)
} else if sr.modf().bit_is_set() {
defmt::trace!("Send mode fault");
nb::Error::Other(Error::ModeFault)
} else if sr.spiens().bit_is_clear() {
defmt::trace!("Send spi disabled");
nb::Error::Other(Error::SpiDisabled)
} else if sr.tdre().bit_is_set() {
if self.spi.mr.read().ps().bit_is_clear() {
self.write_fixed_data_reg(byte);
} else {
self.write_variable_data_reg(byte);
}
return Ok(());
} else {
nb::Error::WouldBlock
})
}
}
impl<FRAMESIZE> crate::hal::blocking::spi::transfer::Default<FRAMESIZE> for SpiMaster<FRAMESIZE>
where
FRAMESIZE: Copy + From<SpiU16>,
SpiU16: From<FRAMESIZE> + From<SpiU8>,
u8: From<FRAMESIZE>,
{
}
impl crate::hal::blocking::spi::Write<SpiU8> for SpiMaster<SpiU8> {
type Error = Error;
fn write(&mut self, words: &[SpiU8]) -> Result<(), Error> {
self.spi_write(words)
}
}
impl crate::hal::blocking::spi::Write<SpiU16> for SpiMaster<SpiU16> {
type Error = Error;
fn write(&mut self, words: &[SpiU16]) -> Result<(), Error> {
self.spi_write(words)
}
}
pub trait SpiReadWrite<T> {
fn read_data_reg(&mut self) -> T;
fn write_fixed_data_reg(&mut self, data: T);
fn write_variable_data_reg(&mut self, data: T);
fn spi_write(&mut self, words: &[T]) -> Result<(), Error>;
}
impl<FRAMESIZE> SpiReadWrite<FRAMESIZE> for SpiMaster<FRAMESIZE>
where
FRAMESIZE: Copy + From<SpiU16>,
SpiU16: From<FRAMESIZE> + From<SpiU8>,
{
fn read_data_reg(&mut self) -> FRAMESIZE {
let rdr = self.spi.rdr.read();
SpiU16(rdr.rd().bits()).into()
}
fn write_fixed_data_reg(&mut self, data: FRAMESIZE) {
unsafe {
let data: SpiU16 = data.into();
self.spi.tdr.write_with_zero(|w| w.td().bits(data.0));
}
}
fn write_variable_data_reg(&mut self, data: FRAMESIZE) {
unsafe {
let data: SpiU16 = data.into();
self.spi.tdr.write_with_zero(|w| {
w.td()
.bits(data.0)
.pcs()
.bits(self.cs)
.lastxfer()
.bit(self.lastxfer)
});
}
}
fn spi_write(&mut self, words: &[FRAMESIZE]) -> Result<(), Error> {
for word in words {
loop {
let sr = self.spi.sr.read();
if sr.tdre().bit_is_set() {
if self.spi.mr.read().ps().bit_is_clear() {
self.write_fixed_data_reg(*word);
} else {
self.write_variable_data_reg(*word);
}
if sr.modf().bit_is_set() {
return Err(Error::ModeFault);
}
}
}
}
Ok(())
}
}
pub struct Fixed;
pub struct Variable;
pub struct SpiPayload<MODE, FRAMESIZE> {
spi: SpiMaster<FRAMESIZE>,
_mode: PhantomData<MODE>,
}
pub type SpiRxDma<MODE, FRAMESIZE> = RxDma<SpiPayload<MODE, FRAMESIZE>>;
pub type SpiTxDma<MODE, FRAMESIZE> = TxDma<SpiPayload<MODE, FRAMESIZE>>;
pub type SpiRxTxDma<MODE, FRAMESIZE> = RxTxDma<SpiPayload<MODE, FRAMESIZE>>;
macro_rules! spi_pdc {
(
$Mode:ident, $Framesize:ident
) => {
paste! {
impl SpiMaster<$Framesize> {
pub fn with_pdc_rx(self) -> SpiRxDma<$Mode, $Framesize> {
let payload = SpiPayload {
spi: self,
_mode: PhantomData,
};
RxDma { payload }
}
pub fn with_pdc_tx(self) -> SpiTxDma<$Mode, $Framesize> {
let payload = SpiPayload {
spi: self,
_mode: PhantomData,
};
TxDma { payload }
}
pub fn with_pdc_rxtx(self) -> SpiRxTxDma<$Mode, $Framesize> {
let payload = SpiPayload {
spi: self,
_mode: PhantomData,
};
RxTxDma { payload }
}
}
pub type [<SpiMaster $Framesize>] = SpiMaster<$Framesize>;
pdc_rx! { [<SpiMaster $Framesize>]: spi, sr }
pdc_tx! { [<SpiMaster $Framesize>]: spi, sr }
pdc_rxtx! { [<SpiMaster $Framesize>]: spi }
impl Transmit for SpiTxDma<$Mode, $Framesize> {
type ReceivedWord = $Framesize;
}
impl Receive for SpiRxDma<$Mode, $Framesize> {
type TransmittedWord = $Framesize;
}
impl Receive for SpiRxTxDma<$Mode, $Framesize> {
type TransmittedWord = $Framesize;
}
impl Transmit for SpiRxTxDma<$Mode, $Framesize> {
type ReceivedWord = $Framesize;
}
impl SpiRxDma<$Mode, $Framesize> {
pub fn revert(mut self) -> SpiMaster<$Framesize> {
self.payload.spi.stop_rx_pdc();
self.payload.spi
}
}
impl<B> ReadDma<B, $Framesize> for SpiRxDma<$Mode, $Framesize>
where
Self: TransferPayload,
B: WriteBuffer<Word = $Framesize>,
{
fn read(mut self, mut buffer: B) -> Transfer<W, B, Self> {
let (ptr, len) = unsafe { buffer.write_buffer() };
self.payload.spi.set_receive_address(ptr as u32);
self.payload.spi.set_receive_counter(len as u16);
compiler_fence(Ordering::Release);
self.start();
Transfer::w(buffer, self)
}
}
impl TransferPayload for SpiRxDma<$Mode, $Framesize> {
fn start(&mut self) {
self.payload.spi.start_rx_pdc();
}
fn stop(&mut self) {
self.payload.spi.stop_rx_pdc();
}
fn in_progress(&self) -> bool {
self.payload.spi.rx_in_progress()
}
}
impl SpiTxDma<$Mode, $Framesize> {
pub fn revert(mut self) -> SpiMaster<$Framesize> {
self.payload.spi.stop_tx_pdc();
self.payload.spi
}
}
impl<B> WriteDma<B, $Framesize> for SpiTxDma<$Mode, $Framesize>
where
Self: TransferPayload,
B: ReadBuffer<Word = $Framesize>,
{
fn write(mut self, buffer: B) -> Transfer<R, B, Self> {
let (ptr, len) = unsafe { buffer.read_buffer() };
self.payload.spi.set_transmit_address(ptr as u32);
self.payload.spi.set_transmit_counter(len as u16);
compiler_fence(Ordering::Release);
self.start();
Transfer::r(buffer, self)
}
}
impl TransferPayload for SpiTxDma<$Mode, $Framesize> {
fn start(&mut self) {
self.payload.spi.start_tx_pdc();
}
fn stop(&mut self) {
self.payload.spi.stop_tx_pdc();
}
fn in_progress(&self) -> bool {
self.payload.spi.tx_in_progress()
}
}
impl SpiRxTxDma<$Mode, $Framesize> {
pub fn revert(mut self) -> SpiMaster<$Framesize> {
self.payload.spi.stop_rxtx_pdc();
self.payload.spi
}
}
impl<RXB, TXB> ReadWriteDma<RXB, TXB, $Framesize> for SpiRxTxDma<$Mode, $Framesize>
where
Self: TransferPayload,
RXB: WriteBuffer<Word = $Framesize>,
TXB: ReadBuffer<Word = $Framesize>,
{
fn read_write(mut self, mut rx_buffer: RXB, tx_buffer: TXB) -> Transfer<W, (RXB, TXB), Self> {
let (ptr, rx_len) = unsafe { rx_buffer.write_buffer() };
self.payload.spi.set_receive_address(ptr as u32);
self.payload.spi.set_receive_counter(rx_len as u16);
let (ptr, tx_len) = unsafe { tx_buffer.read_buffer() };
self.payload.spi.set_transmit_address(ptr as u32);
self.payload.spi.set_transmit_counter(tx_len as u16);
if rx_len != tx_len {
panic!("rx_len: {} != tx:len: {}", rx_len, tx_len);
}
compiler_fence(Ordering::Release);
self.start();
Transfer::w((rx_buffer, tx_buffer), self)
}
}
impl<RXB, TXB> ReadWriteDmaLen<RXB, TXB, $Framesize> for SpiRxTxDma<$Mode, $Framesize>
where
Self: TransferPayload,
RXB: WriteBuffer<Word = $Framesize>,
TXB: ReadBuffer<Word = $Framesize>,
{
fn read_write_len(mut self, mut rx_buffer: RXB, rx_buf_len: usize, tx_buffer: TXB, tx_buf_len: usize) -> Transfer<W, (RXB, TXB), Self> {
let (ptr, rx_len) = unsafe { rx_buffer.write_buffer() };
self.payload.spi.set_receive_address(ptr as u32);
self.payload.spi.set_receive_counter(rx_buf_len as u16);
if rx_len < rx_buf_len {
panic!("rx_len: {} < rx_buf_len: {}", rx_len, rx_buf_len);
}
let (ptr, tx_len) = unsafe { tx_buffer.read_buffer() };
self.payload.spi.set_transmit_address(ptr as u32);
self.payload.spi.set_transmit_counter(tx_buf_len as u16);
if tx_len < tx_buf_len {
panic!("tx_len: {} < tx_buf_len: {}", tx_len, tx_buf_len);
}
compiler_fence(Ordering::Release);
self.start();
Transfer::w((rx_buffer, tx_buffer), self)
}
}
impl TransferPayload for SpiRxTxDma<$Mode, $Framesize> {
fn start(&mut self) {
self.payload.spi.start_rxtx_pdc();
}
fn stop(&mut self) {
self.payload.spi.stop_rxtx_pdc();
}
fn in_progress(&self) -> bool {
self.payload.spi.tx_in_progress() || self.payload.spi.rx_in_progress()
}
}
}
}
}
spi_pdc! { Fixed, u8 }
spi_pdc! { Fixed, u16 }
spi_pdc! { Variable, u32 }