use core::ptr;
use crate::hal::spi::FullDuplex;
pub use crate::hal::spi::{Mode, Phase, Polarity};
use crate::pac::{
spi1::cr2::{DS_A, FRXTH_A},
SPI1, SPI2, SPI3,
};
use crate::stm32::spi1;
use crate::gpio::gpioa::{PA5, PA6, PA7};
#[cfg(any(
feature = "stm32f301",
feature = "stm32f302",
feature = "stm32f303",
feature = "stm32f318",
feature = "stm32f328",
feature = "stm32f334",
feature = "stm32f358",
feature = "stm32f398"
))]
use crate::gpio::gpiob::PB13;
use crate::gpio::gpiob::{PB14, PB15, PB5};
use crate::gpio::gpioc::{PC10, PC11, PC12};
use crate::gpio::{AF5, AF6};
use crate::rcc::Clocks;
#[cfg(any(
feature = "stm32f301",
feature = "stm32f302",
feature = "stm32f303",
feature = "stm32f318",
feature = "stm32f328",
feature = "stm32f358",
feature = "stm32f373",
feature = "stm32f378",
feature = "stm32f398"
))]
use crate::rcc::APB1;
#[cfg(any(
feature = "stm32f302",
feature = "stm32f303",
feature = "stm32f328",
feature = "stm32f334",
feature = "stm32f358",
feature = "stm32f373",
feature = "stm32f378",
feature = "stm32f398"
))]
use crate::rcc::APB2;
use crate::time::Hertz;
use core::marker::PhantomData;
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Overrun,
ModeFault,
Crc,
}
pub unsafe trait SckPin<SPI> {}
pub unsafe trait MisoPin<SPI> {}
pub unsafe trait MosiPin<SPI> {}
unsafe impl SckPin<SPI1> for PA5<AF5> {}
#[cfg(any(
feature = "stm32f301",
feature = "stm32f302",
feature = "stm32f303",
feature = "stm32f318",
feature = "stm32f328",
feature = "stm32f334",
feature = "stm32f358",
feature = "stm32f398"
))]
unsafe impl SckPin<SPI2> for PB13<AF5> {}
unsafe impl SckPin<SPI3> for PC10<AF6> {}
unsafe impl MisoPin<SPI1> for PA6<AF5> {}
unsafe impl MisoPin<SPI2> for PB14<AF5> {}
unsafe impl MisoPin<SPI3> for PC11<AF6> {}
unsafe impl MosiPin<SPI1> for PA7<AF5> {}
unsafe impl MosiPin<SPI1> for PB5<AF5> {}
unsafe impl MosiPin<SPI2> for PB15<AF5> {}
unsafe impl MosiPin<SPI3> for PB5<AF6> {}
unsafe impl MosiPin<SPI3> for PC12<AF6> {}
pub trait Word {
fn register_config() -> (FRXTH_A, DS_A);
}
impl Word for u8 {
fn register_config() -> (FRXTH_A, DS_A) {
(FRXTH_A::QUARTER, DS_A::EIGHTBIT)
}
}
impl Word for u16 {
fn register_config() -> (FRXTH_A, DS_A) {
(FRXTH_A::HALF, DS_A::SIXTEENBIT)
}
}
pub struct Spi<SPI, PINS, WORD = u8> {
spi: SPI,
pins: PINS,
_word: PhantomData<WORD>,
}
macro_rules! hal {
($($SPIX:ident: ($spiX:ident, $APBX:ident, $spiXen:ident, $spiXrst:ident, $pclkX:ident),)+) => {
$(
impl<SCK, MISO, MOSI, WORD> Spi<$SPIX, (SCK, MISO, MOSI), WORD> {
pub fn $spiX<F>(
spi: $SPIX,
pins: (SCK, MISO, MOSI),
mode: Mode,
freq: F,
clocks: Clocks,
apb2: &mut $APBX,
) -> Self
where
F: Into<Hertz>,
SCK: SckPin<$SPIX>,
MISO: MisoPin<$SPIX>,
MOSI: MosiPin<$SPIX>,
WORD: Word,
{
apb2.enr().modify(|_, w| w.$spiXen().enabled());
apb2.rstr().modify(|_, w| w.$spiXrst().reset());
apb2.rstr().modify(|_, w| w.$spiXrst().clear_bit());
let (frxth, ds) = WORD::register_config();
spi.cr2.write(|w| {
w.frxth().variant(frxth);
w.ds().variant(ds);
w.ssoe().disabled()
});
spi.cr1.write(|w| {
w.mstr().master();
match mode.phase {
Phase::CaptureOnFirstTransition => w.cpha().first_edge(),
Phase::CaptureOnSecondTransition => w.cpha().second_edge(),
};
match mode.polarity {
Polarity::IdleLow => w.cpol().idle_low(),
Polarity::IdleHigh => w.cpol().idle_high(),
};
w.br().variant(Self::compute_baud_rate(clocks.$pclkX(), freq.into()));
w.spe()
.enabled()
.lsbfirst()
.msbfirst()
.ssi()
.slave_not_selected()
.ssm()
.enabled()
.crcen()
.disabled()
.bidimode()
.unidirectional()
});
Spi { spi, pins, _word: PhantomData }
}
pub fn free(self) -> ($SPIX, (SCK, MISO, MOSI)) {
(self.spi, self.pins)
}
pub fn reclock<F>(&mut self, freq: F, clocks: Clocks)
where F: Into<Hertz>
{
self.spi.cr1.modify(|_, w| w.spe().disabled());
self.spi.cr1.modify(|_, w| {
w.br().variant(Self::compute_baud_rate(clocks.$pclkX(), freq.into()));
w.spe().enabled()
});
}
fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> spi1::cr1::BR_A {
use spi1::cr1::BR_A;
match clocks.0 / freq.0 {
0 => unreachable!(),
1..=2 => BR_A::DIV2,
3..=5 => BR_A::DIV4,
6..=11 => BR_A::DIV8,
12..=23 => BR_A::DIV16,
24..=39 => BR_A::DIV32,
40..=95 => BR_A::DIV64,
96..=191 => BR_A::DIV128,
_ => BR_A::DIV256,
}
}
}
impl<PINS, WORD> FullDuplex<WORD> for Spi<$SPIX, PINS, WORD> {
type Error = Error;
fn read(&mut self) -> nb::Result<WORD, Error> {
let sr = self.spi.sr.read();
Err(if sr.ovr().is_overrun() {
nb::Error::Other(Error::Overrun)
} else if sr.modf().is_fault() {
nb::Error::Other(Error::ModeFault)
} else if sr.crcerr().is_no_match() {
nb::Error::Other(Error::Crc)
} else if sr.rxne().is_not_empty() {
let read_ptr = &self.spi.dr as *const _ as *const WORD;
let value = unsafe { ptr::read_volatile(read_ptr) };
return Ok(value);
} else {
nb::Error::WouldBlock
})
}
fn send(&mut self, word: WORD) -> nb::Result<(), Error> {
let sr = self.spi.sr.read();
Err(if sr.ovr().is_overrun() {
nb::Error::Other(Error::Overrun)
} else if sr.modf().is_fault() {
nb::Error::Other(Error::ModeFault)
} else if sr.crcerr().is_no_match() {
nb::Error::Other(Error::Crc)
} else if sr.txe().is_empty() {
let write_ptr = &self.spi.dr as *const _ as *mut WORD;
unsafe { ptr::write_volatile(write_ptr, word) };
return Ok(());
} else {
nb::Error::WouldBlock
})
}
}
impl<PINS, WORD> crate::hal::blocking::spi::transfer::Default<WORD> for Spi<$SPIX, PINS, WORD> {}
impl<PINS, WORD> crate::hal::blocking::spi::write::Default<WORD> for Spi<$SPIX, PINS, WORD> {}
)+
}
}
#[cfg(feature = "stm32f334")]
hal! {
SPI1: (spi1, APB2, spi1en, spi1rst, pclk2),
}
#[cfg(any(feature = "stm32f301", feature = "stm32f318"))]
hal! {
SPI2: (spi2, APB1, spi2en, spi2rst, pclk1),
SPI3: (spi3, APB1, spi3en, spi3rst, pclk1),
}
#[cfg(any(
feature = "stm32f302",
feature = "stm32f303",
feature = "stm32f328",
feature = "stm32f358",
feature = "stm32f373",
feature = "stm32f378",
feature = "stm32f398"
))]
hal! {
SPI1: (spi1, APB2, spi1en, spi1rst, pclk2),
SPI2: (spi2, APB1, spi2en, spi2rst, pclk1),
SPI3: (spi3, APB1, spi3en, spi3rst, pclk1),
}