You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

387 lines
11 KiB

//! API for the integrate SPI peripherals
//!
//! The spi bus acts as the master (generating the clock) and you need to handle the CS separately.
//!
//! The most significant bit is transmitted first & only 8-bit transfers are supported
//!
//! # Example
//! Echo incoming data in the next transfer
//! ``` no_run
//! use stm32f0xx_hal as hal;
//!
//! use crate::hal::stm32;
//! use crate::hal::prelude::*;
//! use crate::hal::spi::{Spi, Mode, Phase, Polarity};
//!
//! cortex_m::interrupt::free(|cs| {
//! let mut p = stm32::Peripherals::take().unwrap();
//! let mut rcc = p.RCC.constrain().freeze(&mut p.FLASH);
//!
//! let gpioa = p.GPIOA.split(&mut rcc);
//!
//! // Configure pins for SPI
//! let sck = gpioa.pa5.into_alternate_af0(cs);
//! let miso = gpioa.pa6.into_alternate_af0(cs);
//! let mosi = gpioa.pa7.into_alternate_af0(cs);
//!
//! // Configure SPI with 1MHz rate
//! let mut spi = Spi::spi1(p.SPI1, (sck, miso, mosi), Mode {
//! polarity: Polarity::IdleHigh,
//! phase: Phase::CaptureOnSecondTransition,
//! }, 1.mhz(), &mut rcc);
//!
//! let mut data = [ 0 ];
//! loop {
//! spi.transfer(&mut data).unwrap();
//! }
//! });
//! ```
use core::{ops::Deref, ptr};
use nb;
pub use embedded_hal::spi::{Mode, Phase, Polarity};
// TODO Put this inside the macro
// Currently that causes a compiler panic
use crate::stm32::SPI1;
#[cfg(any(
feature = "stm32f030x8",
feature = "stm32f030xc",
feature = "stm32f042",
feature = "stm32f048",
feature = "stm32f051",
feature = "stm32f058",
feature = "stm32f070xb",
feature = "stm32f071",
feature = "stm32f072",
feature = "stm32f078",
feature = "stm32f091",
feature = "stm32f098",
))]
use crate::stm32::SPI2;
use crate::gpio::*;
use crate::rcc::{Clocks, Rcc};
use crate::time::Hertz;
/// SPI error
#[derive(Debug)]
pub enum Error {
/// Overrun occurred
Overrun,
/// Mode fault occurred
ModeFault,
/// CRC error
Crc,
#[doc(hidden)]
_Extensible,
}
/// SPI abstraction
pub struct Spi<SPI, SCKPIN, MISOPIN, MOSIPIN> {
spi: SPI,
pins: (SCKPIN, MISOPIN, MOSIPIN),
}
pub trait SckPin<SPI> {}
pub trait MisoPin<SPI> {}
pub trait MosiPin<SPI> {}
macro_rules! spi_pins {
($($SPI:ident => {
sck => [$($sck:ty),+ $(,)*],
miso => [$($miso:ty),+ $(,)*],
mosi => [$($mosi:ty),+ $(,)*],
})+) => {
$(
$(
impl SckPin<crate::stm32::$SPI> for $sck {}
)+
$(
impl MisoPin<crate::stm32::$SPI> for $miso {}
)+
$(
impl MosiPin<crate::stm32::$SPI> for $mosi {}
)+
)+
}
}
spi_pins! {
SPI1 => {
sck => [gpioa::PA5<Alternate<AF0>>, gpiob::PB3<Alternate<AF0>>],
miso => [gpioa::PA6<Alternate<AF0>>, gpiob::PB4<Alternate<AF0>>],
mosi => [gpioa::PA7<Alternate<AF0>>, gpiob::PB5<Alternate<AF0>>],
}
}
#[cfg(any(
feature = "stm32f030x4",
feature = "stm32f030x6",
feature = "stm32f031",
feature = "stm32f038",
))]
spi_pins! {
SPI1 => {
sck => [gpiob::PB13<Alternate<AF0>>],
miso => [gpiob::PB14<Alternate<AF0>>],
mosi => [gpiob::PB15<Alternate<AF0>>],
}
}
// TODO: The ST SVD files are missing the entire PE enable register.
// So those pins do not exist in the register definitions.
// Re-enable as soon as this gets fixed.
// #[cfg(any(
// feature = "stm32f071",
// feature = "stm32f072",
// feature = "stm32f078",
// feature = "stm32f091",
// feature = "stm32f098",
// ))]
// spi_pins! {
// SPI1 => {
// sck => [gpioe::PE13<Alternate<AF1>>],
// miso => [gpioe::PE14<Alternate<AF1>>],
// mosi => [gpioe::PE15<Alternate<AF1>>],
// }
// }
#[cfg(any(
feature = "stm32f030x8",
feature = "stm32f030xc",
feature = "stm32f042",
feature = "stm32f048",
feature = "stm32f051",
feature = "stm32f058",
feature = "stm32f070xb",
feature = "stm32f071",
feature = "stm32f072",
feature = "stm32f078",
feature = "stm32f091",
feature = "stm32f098",
))]
spi_pins! {
SPI2 => {
sck => [gpiob::PB13<Alternate<AF0>>],
miso => [gpiob::PB14<Alternate<AF0>>],
mosi => [gpiob::PB15<Alternate<AF0>>],
}
}
#[cfg(any(
feature = "stm32f030xc",
feature = "stm32f070xb",
feature = "stm32f071",
feature = "stm32f072",
feature = "stm32f078",
feature = "stm32f091",
feature = "stm32f098",
))]
spi_pins! {
SPI2 => {
sck => [gpiob::PB10<Alternate<AF5>>],
miso => [gpioc::PC2<Alternate<AF1>>],
mosi => [gpioc::PC3<Alternate<AF1>>],
}
}
#[cfg(any(
feature = "stm32f071",
feature = "stm32f072",
feature = "stm32f078",
feature = "stm32f091",
feature = "stm32f098",
))]
spi_pins! {
SPI2 => {
sck => [gpiod::PD1<Alternate<AF1>>],
miso => [gpiod::PD3<Alternate<AF1>>],
mosi => [gpiod::PD4<Alternate<AF1>>],
}
}
macro_rules! spi {
($($SPI:ident: ($spi:ident, $spiXen:ident, $spiXrst:ident, $apbenr:ident, $apbrstr:ident),)+) => {
$(
impl<SCKPIN, MISOPIN, MOSIPIN> Spi<$SPI, SCKPIN, MISOPIN, MOSIPIN> {
/// Creates a new spi instance
pub fn $spi<F>(
spi: $SPI,
pins: (SCKPIN, MISOPIN, MOSIPIN),
mode: Mode,
speed: F,
rcc: &mut Rcc,
) -> Self
where
SCKPIN: SckPin<$SPI>,
MISOPIN: MisoPin<$SPI>,
MOSIPIN: MosiPin<$SPI>,
F: Into<Hertz>,
{
/* Enable clock for SPI */
rcc.regs.$apbenr.modify(|_, w| w.$spiXen().set_bit());
/* Reset SPI */
rcc.regs.$apbrstr.modify(|_, w| w.$spiXrst().set_bit());
rcc.regs.$apbrstr.modify(|_, w| w.$spiXrst().clear_bit());
Spi { spi, pins }.spi_init(mode, speed, rcc.clocks)
}
}
)+
}
}
spi! {
SPI1: (spi1, spi1en, spi1rst, apb2enr, apb2rstr),
}
#[cfg(any(
feature = "stm32f030x8",
feature = "stm32f030xc",
feature = "stm32f042",
feature = "stm32f048",
feature = "stm32f051",
feature = "stm32f058",
feature = "stm32f070xb",
feature = "stm32f071",
feature = "stm32f072",
feature = "stm32f078",
feature = "stm32f091",
feature = "stm32f098",
))]
spi! {
SPI2: (spi2, spi2en, spi2rst, apb1enr, apb1rstr),
}
// It's s needed for the impls, but rustc doesn't recognize that
#[allow(dead_code)]
type SpiRegisterBlock = crate::stm32::spi1::RegisterBlock;
impl<SPI, SCKPIN, MISOPIN, MOSIPIN> Spi<SPI, SCKPIN, MISOPIN, MOSIPIN>
where
SPI: Deref<Target = SpiRegisterBlock>,
{
fn spi_init<F>(self: Self, mode: Mode, speed: F, clocks: Clocks) -> Self
where
F: Into<Hertz>,
{
/* Make sure the SPI unit is disabled so we can configure it */
self.spi.cr1.modify(|_, w| w.spe().clear_bit());
// FRXTH: 8-bit threshold on RX FIFO
// DS: 8-bit data size
// SSOE: cleared to disable SS output
//
// NOTE(unsafe): DS reserved bit patterns are 0b0000, 0b0001, and 0b0010. 0b0111 is valid
// (reference manual, pp 804)
self.spi
.cr2
.write(|w| unsafe { w.frxth().set_bit().ds().bits(0b0111).ssoe().clear_bit() });
let br = match clocks.pclk().0 / speed.into().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,
};
// mstr: master configuration
// lsbfirst: MSB first
// ssm: enable software slave management (NSS pin free for other uses)
// ssi: set nss high = master mode
// dff: 8 bit frames
// bidimode: 2-line unidirectional
// spe: enable the SPI bus
self.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()
.rxonly()
.clear_bit()
.bidimode()
.clear_bit()
.spe()
.set_bit()
});
self
}
pub fn release(self) -> (SPI, (SCKPIN, MISOPIN, MOSIPIN)) {
(self.spi, self.pins)
}
}
impl<SPI, SCKPIN, MISOPIN, MOSIPIN> ::embedded_hal::spi::FullDuplex<u8>
for Spi<SPI, SCKPIN, MISOPIN, MOSIPIN>
where
SPI: Deref<Target = SpiRegisterBlock>,
{
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() {
// NOTE(read_volatile) read only 1 byte (the svd2rust API only allows
// reading a half-word)
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() {
// NOTE(write_volatile) see note above
unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut u8, byte) }
return Ok(());
} else {
nb::Error::WouldBlock
})
}
}
impl<SPI, SCKPIN, MISOPIN, MOSIPIN> ::embedded_hal::blocking::spi::transfer::Default<u8>
for Spi<SPI, SCKPIN, MISOPIN, MOSIPIN>
where
SPI: Deref<Target = SpiRegisterBlock>,
{
}
impl<SPI, SCKPIN, MISOPIN, MOSIPIN> ::embedded_hal::blocking::spi::write::Default<u8>
for Spi<SPI, SCKPIN, MISOPIN, MOSIPIN>
where
SPI: Deref<Target = SpiRegisterBlock>,
{
}