use crate::gpio::gpioa::{PA11, PA12, PA5, PA6, PA7};
use crate::gpio::gpiob::{PB13, PB14, PB15, PB3, PB4, PB5};
use crate::gpio::gpioc::{PC10, PC11, PC12};
use crate::gpio::{Floating, Input};
use crate::rcc::Rcc;
use crate::stm32::{SPI1, SPI2, SPI3};
use crate::time::Hertz;
use core::ptr;
use hal;
use nb;
pub use hal::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
#[derive(Debug)]
pub enum Error {
Overrun,
ModeFault,
Crc,
#[doc(hidden)]
_Extensible,
}
pub trait Pins<SPI> {}
pub trait PinSck<SPI> {}
pub trait PinMiso<SPI> {}
pub trait PinMosi<SPI> {}
impl<SPI, SCK, MISO, MOSI> Pins<SPI> for (SCK, MISO, MOSI)
where
SCK: PinSck<SPI>,
MISO: PinMiso<SPI>,
MOSI: PinMosi<SPI>,
{
}
pub struct NoSck;
pub struct NoMiso;
pub struct NoMosi;
macro_rules! pins {
($($SPIX:ty: SCK: [$($SCK:ty),*] MISO: [$($MISO:ty),*] MOSI: [$($MOSI:ty),*])+) => {
$(
$(
impl PinSck<$SPIX> for $SCK {}
)*
$(
impl PinMiso<$SPIX> for $MISO {}
)*
$(
impl PinMosi<$SPIX> for $MOSI {}
)*
)+
}
}
pins! {
SPI1:
SCK: [
NoSck,
PA5<Input<Floating>>,
PB3<Input<Floating>>
]
MISO: [
NoMiso,
PA6<Input<Floating>>,
PA11<Input<Floating>>,
PB4<Input<Floating>>
]
MOSI: [
NoMosi,
PA7<Input<Floating>>,
PA12<Input<Floating>>,
PB5<Input<Floating>>
]
SPI2:
SCK: [
NoSck,
PB13<Input<Floating>>
]
MISO: [
NoMiso,
PB14<Input<Floating>>
]
MOSI: [
NoMosi,
PB15<Input<Floating>>
]
SPI3:
SCK: [
NoSck,
PB3<Input<Floating>>,
PC10<Input<Floating>>
]
MISO: [
NoMiso,
PB4<Input<Floating>>,
PC11<Input<Floating>>
]
MOSI: [
NoMosi,
PB5<Input<Floating>>,
PC12<Input<Floating>>
]
}
#[derive(Debug)]
pub struct Spi<SPI, PINS> {
spi: SPI,
pins: PINS,
}
pub trait SpiExt<SPI>: Sized {
fn spi<PINS, T>(self, pins: PINS, mode: Mode, freq: T, rcc: &mut Rcc) -> Spi<SPI, PINS>
where
PINS: Pins<SPI>,
T: Into<Hertz>;
}
macro_rules! spi {
($($SPIX:ident: ($spiX:ident, $apbXenr:ident, $spiXen:ident, $pclkX:ident),)+) => {
$(
impl<PINS> Spi<$SPIX, PINS> {
pub fn $spiX<T>(
spi: $SPIX,
pins: PINS,
mode: Mode,
freq: T,
rcc: &mut Rcc
) -> Self
where
PINS: Pins<$SPIX>,
T: Into<Hertz>
{
rcc.rb.$apbXenr.modify(|_, w| w.$spiXen().set_bit());
spi.cr2.write(|w| w.ssoe().clear_bit());
let spi_freq = freq.into().0;
let apb_freq = rcc.clocks.$pclkX().0;
let br = match apb_freq / spi_freq {
0 => unreachable!(),
1..=2 => 0b000,
3..=5 => 0b001,
6..=11 => 0b010,
12..=23 => 0b011,
24..=47 => 0b100,
48..=95 => 0b101,
96..=191 => 0b110,
_ => 0b111,
};
#[allow(unused)]
spi.cr1.write(|w| unsafe {
w.cpha()
.bit(mode.phase == Phase::CaptureOnSecondTransition)
.cpol()
.bit(mode.polarity == Polarity::IdleHigh)
.mstr()
.set_bit()
.br()
.bits(br)
.lsbfirst()
.clear_bit()
.ssm()
.set_bit()
.ssi()
.set_bit()
.rxonly()
.clear_bit()
.dff()
.clear_bit()
.bidimode()
.clear_bit()
.spe()
.set_bit()
});
Spi { spi, pins }
}
pub fn free(self) -> ($SPIX, PINS) {
(self.spi, self.pins)
}
}
impl SpiExt<$SPIX> for $SPIX {
fn spi<PINS, T>(self, pins: PINS, mode: Mode, freq: T, rcc: &mut Rcc) -> Spi<$SPIX, PINS>
where
PINS: Pins<$SPIX>,
T: Into<Hertz>
{
Spi::$spiX(self, pins, mode, freq, rcc)
}
}
impl<PINS> hal::spi::FullDuplex<u8> for Spi<$SPIX, PINS> {
type Error = Error;
fn read(&mut self) -> nb::Result<u8, Error> {
let sr = self.spi.sr.read();
Err(if sr.ovr().bit_is_set() {
nb::Error::Other(Error::Overrun)
} else if sr.modf().bit_is_set() {
nb::Error::Other(Error::ModeFault)
} else if sr.crcerr().bit_is_set() {
nb::Error::Other(Error::Crc)
} else if sr.rxne().bit_is_set() {
return Ok(unsafe {
ptr::read_volatile(&self.spi.dr as *const _ as *const u8)
});
} else {
nb::Error::WouldBlock
})
}
fn send(&mut self, byte: u8) -> nb::Result<(), Error> {
let sr = self.spi.sr.read();
Err(if sr.ovr().bit_is_set() {
nb::Error::Other(Error::Overrun)
} else if sr.modf().bit_is_set() {
nb::Error::Other(Error::ModeFault)
} else if sr.crcerr().bit_is_set() {
nb::Error::Other(Error::Crc)
} else if sr.txe().bit_is_set() {
unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut u8, byte) }
return Ok(());
} else {
nb::Error::WouldBlock
})
}
}
impl<PINS> ::hal::blocking::spi::transfer::Default<u8> for Spi<$SPIX, PINS> {}
impl<PINS> ::hal::blocking::spi::write::Default<u8> for Spi<$SPIX, PINS> {}
)+
}
}
spi! {
SPI1: (spi1, apb2enr, spi1en, apb2_clk),
SPI2: (spi2, apb1enr, spi2en, apb1_clk),
SPI3: (spi3, apb1enr, spi3en, apb1_clk),
}