use core::ops::Deref;
use core::ptr;
pub use crate::hal::spi::{FullDuplex, Mode, Phase, Polarity};
#[cfg(any(feature = "high", feature = "connectivity"))]
use crate::pac::SPI3;
use crate::pac::{SPI1, SPI2};
use crate::afio::MAPR;
use crate::dma::dma1::{C3, C5};
#[cfg(feature = "connectivity")]
use crate::dma::dma2::C2;
use crate::dma::{Transfer, TransferPayload, Transmit, TxDma, R};
use crate::gpio::gpioa::{PA5, PA6, PA7};
use crate::gpio::gpiob::{PB13, PB14, PB15, PB3, PB4, PB5};
#[cfg(feature = "connectivity")]
use crate::gpio::gpioc::{PC10, PC11, PC12};
use crate::gpio::{Alternate, Floating, Input, PushPull};
use crate::rcc::{Clocks, Enable, GetBusFreq, Reset, APB1, APB2};
use crate::time::Hertz;
use core::sync::atomic::{self, Ordering};
use embedded_dma::StaticReadBuffer;
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
Overrun,
ModeFault,
Crc,
}
use core::marker::PhantomData;
mod sealed {
pub trait Remap {
type Periph;
const REMAP: bool;
}
pub trait Sck<REMAP> {}
pub trait Miso<REMAP> {}
pub trait Mosi<REMAP> {}
pub struct _Sck;
pub struct _Miso;
pub struct _Mosi;
}
use sealed::{Miso, Mosi, Remap, Sck};
pub trait Pins<REMAP, P> {
type _Pos;
}
macro_rules! pins_impl {
( $( ( $($PINX:ident),+ ), ( $($TRAIT:ident),+ ), ( $($POS:ident),* ); )+ ) => {
$(
#[allow(unused_parens)]
impl<REMAP, $($PINX,)+> Pins<REMAP, ($(sealed::$POS),+)> for ($($PINX),+)
where
$($PINX: $TRAIT<REMAP>,)+
{
type _Pos = ($(sealed::$POS),+);
}
)+
};
}
pins_impl!(
(SCK, MISO, MOSI), (Sck, Miso, Mosi), (_Sck, _Miso, _Mosi);
(SCK, MOSI, MISO), (Sck, Mosi, Miso), (_Sck, _Mosi, _Miso);
(MOSI, SCK, MISO), (Mosi, Sck, Miso), (_Mosi, _Sck, _Miso);
(MOSI, MISO, SCK), (Mosi, Miso, Sck), (_Mosi, _Miso, _Sck);
(MISO, MOSI, SCK), (Miso, Mosi, Sck), (_Miso, _Mosi, _Sck);
(MISO, SCK, MOSI), (Miso, Sck, Mosi), (_Miso, _Sck, _Mosi);
);
pub struct Spi<SPI, REMAP, PINS, FRAMESIZE> {
spi: SPI,
pins: PINS,
_remap: PhantomData<REMAP>,
_framesize: PhantomData<FRAMESIZE>,
}
pub struct NoSck;
pub struct NoMiso;
pub struct NoMosi;
impl<REMAP> Sck<REMAP> for NoSck {}
impl<REMAP> Miso<REMAP> for NoMiso {}
impl<REMAP> Mosi<REMAP> for NoMosi {}
macro_rules! remap {
($name:ident, $SPIX:ident, $state:literal, $SCK:ident, $MISO:ident, $MOSI:ident) => {
pub struct $name;
impl Remap for $name {
type Periph = $SPIX;
const REMAP: bool = $state;
}
impl Sck<$name> for $SCK<Alternate<PushPull>> {}
impl Miso<$name> for $MISO<Input<Floating>> {}
impl Mosi<$name> for $MOSI<Alternate<PushPull>> {}
};
}
remap!(Spi1NoRemap, SPI1, false, PA5, PA6, PA7);
remap!(Spi1Remap, SPI1, true, PB3, PB4, PB5);
remap!(Spi2NoRemap, SPI2, false, PB13, PB14, PB15);
#[cfg(feature = "high")]
remap!(Spi3NoRemap, SPI3, false, PB3, PB4, PB5);
#[cfg(feature = "connectivity")]
remap!(Spi3Remap, SPI3, true, PC10, PC11, PC12);
impl<REMAP, PINS> Spi<SPI1, REMAP, PINS, u8> {
pub fn spi1<F, POS>(
spi: SPI1,
pins: PINS,
mapr: &mut MAPR,
mode: Mode,
freq: F,
clocks: Clocks,
apb: &mut APB2,
) -> Self
where
F: Into<Hertz>,
REMAP: Remap<Periph = SPI1>,
PINS: Pins<REMAP, POS>,
{
mapr.modify_mapr(|_, w| w.spi1_remap().bit(REMAP::REMAP));
Spi::<SPI1, _, _, u8>::_spi(spi, pins, mode, freq.into(), clocks, apb)
}
}
impl<REMAP, PINS> Spi<SPI2, REMAP, PINS, u8> {
pub fn spi2<F, POS>(
spi: SPI2,
pins: PINS,
mode: Mode,
freq: F,
clocks: Clocks,
apb: &mut APB1,
) -> Self
where
F: Into<Hertz>,
REMAP: Remap<Periph = SPI2>,
PINS: Pins<REMAP, POS>,
{
Spi::<SPI2, _, _, u8>::_spi(spi, pins, mode, freq.into(), clocks, apb)
}
}
#[cfg(any(feature = "high", feature = "connectivity"))]
impl<REMAP, PINS> Spi<SPI3, REMAP, PINS, u8> {
pub fn spi3<F, POS>(
spi: SPI3,
pins: PINS,
mapr: &mut MAPR,
mode: Mode,
freq: F,
clocks: Clocks,
apb: &mut APB1,
) -> Self
where
F: Into<Hertz>,
REMAP: Remap<Periph = SPI3>,
PINS: Pins<REMAP, POS>,
{
mapr.modify_mapr(|_, w| w.spi3_remap().bit(REMAP::REMAP));
Spi::<SPI3, _, _, u8>::_spi(spi, pins, mode, freq.into(), clocks, apb)
}
}
pub type SpiRegisterBlock = crate::pac::spi1::RegisterBlock;
pub trait SpiReadWrite<T> {
fn read_data_reg(&mut self) -> T;
fn write_data_reg(&mut self, data: T);
fn spi_write(&mut self, words: &[T]) -> Result<(), Error>;
}
impl<SPI, REMAP, PINS, FrameSize> SpiReadWrite<FrameSize> for Spi<SPI, REMAP, PINS, FrameSize>
where
SPI: Deref<Target = SpiRegisterBlock>,
FrameSize: Copy,
{
fn read_data_reg(&mut self) -> FrameSize {
unsafe { ptr::read_volatile(&self.spi.dr as *const _ as *const FrameSize) }
}
fn write_data_reg(&mut self, data: FrameSize) {
unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut FrameSize, data) }
}
fn spi_write(&mut self, words: &[FrameSize]) -> Result<(), Error> {
for word in words {
loop {
let sr = self.spi.sr.read();
if sr.txe().bit_is_set() {
self.write_data_reg(*word);
if sr.modf().bit_is_set() {
return Err(Error::ModeFault);
}
break;
}
}
}
loop {
let sr = self.spi.sr.read();
if sr.txe().bit_is_set() {
break;
}
}
loop {
let sr = self.spi.sr.read();
if !sr.bsy().bit_is_set() {
break;
}
}
let _ = self.read_data_reg();
let _ = self.spi.sr.read();
Ok(())
}
}
impl<SPI, REMAP, PINS, FrameSize> Spi<SPI, REMAP, PINS, FrameSize>
where
SPI: Deref<Target = SpiRegisterBlock>,
FrameSize: Copy,
{
#[deprecated(since = "0.6.0", note = "Please use release instead")]
pub fn free(self) -> (SPI, PINS) {
self.release()
}
pub fn release(self) -> (SPI, PINS) {
(self.spi, self.pins)
}
}
impl<SPI, REMAP, PINS> Spi<SPI, REMAP, PINS, u8>
where
SPI: Deref<Target = SpiRegisterBlock> + Enable + Reset,
SPI::Bus: GetBusFreq,
{
fn _spi(
spi: SPI,
pins: PINS,
mode: Mode,
freq: Hertz,
clocks: Clocks,
apb: &mut SPI::Bus,
) -> Self {
SPI::enable(apb);
SPI::reset(apb);
spi.cr2.write(|w| w.ssoe().clear_bit());
let br = match SPI::Bus::get_frequency(&clocks).0 / freq.0 {
0 => unreachable!(),
1..=2 => 0b000,
3..=5 => 0b001,
6..=11 => 0b010,
12..=23 => 0b011,
24..=47 => 0b100,
48..=95 => 0b101,
96..=191 => 0b110,
_ => 0b111,
};
spi.cr1.write(|w| {
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()
.dff()
.clear_bit()
.bidimode()
.clear_bit()
.rxonly()
.clear_bit()
.spe()
.set_bit()
});
Spi {
spi,
pins,
_remap: PhantomData,
_framesize: PhantomData,
}
}
pub fn frame_size_16bit(self) -> Spi<SPI, REMAP, PINS, u16> {
self.spi.cr1.modify(|_, w| w.spe().clear_bit());
self.spi.cr1.modify(|_, w| w.dff().set_bit());
self.spi.cr1.modify(|_, w| w.spe().set_bit());
Spi {
spi: self.spi,
pins: self.pins,
_remap: PhantomData,
_framesize: PhantomData,
}
}
}
impl<SPI, REMAP, PINS> Spi<SPI, REMAP, PINS, u16>
where
SPI: Deref<Target = SpiRegisterBlock>,
{
pub fn frame_size_8bit(self) -> Spi<SPI, REMAP, PINS, u8> {
self.spi.cr1.modify(|_, w| w.spe().clear_bit());
self.spi.cr1.modify(|_, w| w.dff().clear_bit());
self.spi.cr1.modify(|_, w| w.spe().set_bit());
Spi {
spi: self.spi,
pins: self.pins,
_remap: PhantomData,
_framesize: PhantomData,
}
}
}
impl<SPI, REMAP, PINS, FrameSize> crate::hal::spi::FullDuplex<FrameSize>
for Spi<SPI, REMAP, PINS, FrameSize>
where
SPI: Deref<Target = SpiRegisterBlock>,
FrameSize: Copy,
{
type Error = Error;
fn read(&mut self) -> nb::Result<FrameSize, 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(self.read_data_reg());
} else {
nb::Error::WouldBlock
})
}
fn send(&mut self, data: FrameSize) -> 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() {
self.write_data_reg(data);
return Ok(());
} else {
nb::Error::WouldBlock
})
}
}
impl<SPI, REMAP, PINS, FrameSize> crate::hal::blocking::spi::transfer::Default<FrameSize>
for Spi<SPI, REMAP, PINS, FrameSize>
where
SPI: Deref<Target = SpiRegisterBlock>,
FrameSize: Copy,
{
}
impl<SPI, REMAP, PINS> crate::hal::blocking::spi::Write<u8> for Spi<SPI, REMAP, PINS, u8>
where
SPI: Deref<Target = SpiRegisterBlock>,
{
type Error = Error;
fn write(&mut self, words: &[u8]) -> Result<(), Error> {
self.spi_write(words)
}
}
impl<SPI, REMAP, PINS> crate::hal::blocking::spi::Write<u16> for Spi<SPI, REMAP, PINS, u16>
where
SPI: Deref<Target = SpiRegisterBlock>,
{
type Error = Error;
fn write(&mut self, words: &[u16]) -> Result<(), Error> {
self.spi_write(words)
}
}
pub struct SpiPayload<SPI, REMAP, PINS> {
spi: Spi<SPI, REMAP, PINS, u8>,
}
pub type SpiTxDma<SPI, REMAP, PINS, CHANNEL> = TxDma<SpiPayload<SPI, REMAP, PINS>, CHANNEL>;
macro_rules! spi_dma {
($SPIi:ident, $TCi:ident) => {
impl<REMAP, PINS> Transmit for SpiTxDma<$SPIi, REMAP, PINS, $TCi> {
type TxChannel = $TCi;
type ReceivedWord = u8;
}
impl<REMAP, PINS> Spi<$SPIi, REMAP, PINS, u8> {
pub fn with_tx_dma(self, channel: $TCi) -> SpiTxDma<$SPIi, REMAP, PINS, $TCi> {
let payload = SpiPayload { spi: self };
SpiTxDma { payload, channel }
}
}
impl<REMAP, PINS> TransferPayload for SpiTxDma<$SPIi, REMAP, PINS, $TCi> {
fn start(&mut self) {
self.payload
.spi
.spi
.cr2
.modify(|_, w| w.txdmaen().set_bit());
self.channel.start();
}
fn stop(&mut self) {
self.payload
.spi
.spi
.cr2
.modify(|_, w| w.txdmaen().clear_bit());
self.channel.stop();
}
}
impl<B, REMAP, PIN> crate::dma::WriteDma<B, u8> for SpiTxDma<$SPIi, REMAP, PIN, $TCi>
where
B: StaticReadBuffer<Word = u8>,
{
fn write(mut self, buffer: B) -> Transfer<R, B, Self> {
let (ptr, len) = unsafe { buffer.static_read_buffer() };
self.channel.set_peripheral_address(
unsafe { &(*$SPIi::ptr()).dr as *const _ as u32 },
false,
);
self.channel.set_memory_address(ptr as u32, true);
self.channel.set_transfer_length(len);
atomic::compiler_fence(Ordering::Release);
self.channel.ch().cr.modify(|_, w| {
w
.mem2mem()
.clear_bit()
.pl()
.medium()
.msize()
.bits8()
.psize()
.bits8()
.circ()
.clear_bit()
.dir()
.set_bit()
});
self.start();
Transfer::r(buffer, self)
}
}
};
}
spi_dma!(SPI1, C3);
spi_dma!(SPI2, C5);
#[cfg(feature = "connectivity")]
spi_dma!(SPI3, C2);