use core::cell::UnsafeCell;
use core::marker::PhantomData;
use core::{ops::Deref, ptr};
pub use embedded_hal::spi::{Mode, Phase, Polarity};
use crate::pac::SPI1;
#[cfg(any(feature = "py32f030",))]
use crate::pac::SPI2;
use crate::gpio::*;
use crate::rcc::{Clocks, Rcc};
use crate::time::Hertz;
pub struct EightBit;
pub struct SixteenBit;
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
Overrun,
ModeFault,
Crc,
}
pub struct Spi<SPI, SCKPIN, MISOPIN, MOSIPIN, WIDTH> {
spi: SPI,
pins: (SCKPIN, MISOPIN, MOSIPIN),
_width: PhantomData<WIDTH>,
}
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::pac::$SPI> for $sck {}
)+
$(
impl MisoPin<crate::pac::$SPI> for $miso {}
)+
$(
impl MosiPin<crate::pac::$SPI> for $mosi {}
)+
)+
}
}
#[cfg(any(feature = "py32f030", feature = "py32f003", feature = "py32f002a"))]
spi_pins! {
SPI1 => {
sck => [
gpioa::PA1<Alternate<AF0>>,
gpioa::PA2<Alternate<AF10>>,
gpioa::PA5<Alternate<AF0>>,
gpiob::PB3<Alternate<AF0>>,
],
miso => [
gpioa::PA0<Alternate<AF10>>,
gpioa::PA3<Alternate<AF0>>,
gpioa::PA6<Alternate<AF0>>,
gpioa::PA7<Alternate<AF10>>,
gpioa::PA13<Alternate<AF10>>,
gpiob::PB4<Alternate<AF0>>,
],
mosi => [
gpioa::PA1<Alternate<AF10>>,
gpioa::PA2<Alternate<AF0>>,
gpioa::PA3<Alternate<AF10>>,
gpioa::PA7<Alternate<AF0>>,
gpioa::PA12<Alternate<AF0>>,
gpiob::PB5<Alternate<AF0>>,
],
}
}
#[cfg(feature = "py32f030")]
spi_pins! {
SPI1 => {
sck => [
gpioa::PA9<Alternate<AF10>>,
],
miso => [
gpioa::PA11<Alternate<AF0>>,
],
mosi => [
gpioa::PA8<Alternate<AF10>>,
],
}
SPI2 => {
sck => [
gpioa::PA1<Alternate<AF0>>,
gpiob::PB2<Alternate<AF1>>,
gpiob::PB8<Alternate<AF1>>,
gpiof::PF0<Alternate<AF3>>,
],
miso => [
gpioa::PA3<Alternate<AF0>>,
gpioa::PA9<Alternate<AF0>>,
gpiob::PB6<Alternate<AF3>>,
gpiof::PF1<Alternate<AF3>>,
gpiof::PF3<Alternate<AF3>>,
],
mosi => [
gpioa::PA4<Alternate<AF2>>,
gpioa::PA10<Alternate<AF0>>,
gpiob::PB7<Alternate<AF1>>,
gpiof::PF2<Alternate<AF3>>,
],
}
}
#[cfg(feature = "py32f002b")]
spi_pins! {
SPI1 => {
sck => [
gpiob::PB0<Alternate<AF0>>,
gpiob::PB2<Alternate<AF0>>,
],
miso => [
gpioa::PA1<Alternate<AF0>>,
gpiob::PB6<Alternate<AF2>>,
gpioc::PC1<Alternate<AF0>>,
],
mosi => [
gpioa::PA0<Alternate<AF0>>,
gpioa::PA7<Alternate<AF0>>,
gpiob::PB7<Alternate<AF0>>,
],
}
}
macro_rules! spi {
($($SPI:ident: ($spi:ident, $spiXen:ident, $spiXrst:ident, $apbenr:ident, $apbrstr:ident),)+) => {
$(
impl<SCKPIN, MISOPIN, MOSIPIN> Spi<$SPI, SCKPIN, MISOPIN, MOSIPIN, EightBit> {
/// 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>,
{
rcc.regs.$apbenr.modify(|_, w| w.$spiXen().set_bit());
rcc.regs.$apbrstr.modify(|_, w| w.$spiXrst().set_bit());
rcc.regs.$apbrstr.modify(|_, w| w.$spiXrst().clear_bit());
Spi::<$SPI, SCKPIN, MISOPIN, MOSIPIN, EightBit> { spi, pins, _width: PhantomData }.spi_init(mode, speed, rcc.clocks).into_8bit_width()
}
}
)+
}
}
spi! {
SPI1: (spi1, spi1en, spi1rst, apbenr2, apbrstr2),
}
#[cfg(feature = "py32f030")]
spi! {
SPI2: (spi2, spi2en, spi2rst, apbenr1, apbrstr1),
}
#[allow(dead_code)]
type SpiRegisterBlock = crate::pac::spi1::RegisterBlock;
impl<SPI, SCKPIN, MISOPIN, MOSIPIN, WIDTH> Spi<SPI, SCKPIN, MISOPIN, MOSIPIN, WIDTH>
where
SPI: Deref<Target = SpiRegisterBlock>,
{
fn spi_init<F>(self, mode: Mode, speed: F, clocks: Clocks) -> Self
where
F: Into<Hertz>,
{
self.spi.cr1.modify(|_, w| w.spe().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,
};
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 into_8bit_width(self) -> Spi<SPI, SCKPIN, MISOPIN, MOSIPIN, EightBit> {
#[cfg(any(feature = "py32f030", feature = "py32f003", feature = "py32f002a"))]
self.spi
.cr2
.write(|w| w.frxth().set_bit().ds().eight_bit().ssoe().clear_bit());
#[cfg(feature = "py32f002b")]
self.spi
.cr2
.write(|w| w.ds().eight_bit().ssoe().clear_bit());
Spi {
spi: self.spi,
pins: self.pins,
_width: PhantomData,
}
}
pub fn into_16bit_width(self) -> Spi<SPI, SCKPIN, MISOPIN, MOSIPIN, SixteenBit> {
#[cfg(any(feature = "py32f030", feature = "py32f003", feature = "py32f002a"))]
self.spi
.cr2
.write(|w| w.frxth().set_bit().ds().sixteen_bit().ssoe().clear_bit());
#[cfg(feature = "py32f002b")]
self.spi
.cr2
.write(|w| w.ds().sixteen_bit().ssoe().clear_bit());
Spi {
spi: self.spi,
pins: self.pins,
_width: PhantomData,
}
}
fn set_send_only(&mut self) {
self.spi
.cr1
.modify(|_, w| w.bidimode().set_bit().bidioe().set_bit());
}
fn set_bidi(&mut self) {
self.spi
.cr1
.modify(|_, w| w.bidimode().clear_bit().bidioe().clear_bit());
}
fn check_read(&mut self) -> 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.rxne().bit_is_set() {
return Ok(());
} else {
nb::Error::WouldBlock
})
}
fn send_buffer_size(&mut self) -> u8 {
match self.spi.sr.read().ftlvl().bits() {
0 => 4,
1 => 3,
2 => 2,
_ => 0,
}
}
fn check_send(&mut self) -> 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.txe().bit_is_set() && sr.bsy().bit_is_clear() {
return Ok(());
} else {
nb::Error::WouldBlock
})
}
fn read_u8(&mut self) -> u8 {
unsafe { ptr::read_volatile(&self.spi.dr as *const _ as *const u8) }
}
fn send_u8(&mut self, byte: u8) {
let dr = &self.spi.dr as *const _ as *const UnsafeCell<u8>;
unsafe { ptr::write_volatile(UnsafeCell::raw_get(dr), byte) };
}
fn read_u16(&mut self) -> u16 {
unsafe { ptr::read_volatile(&self.spi.dr as *const _ as *const u16) }
}
fn send_u16(&mut self, byte: u16) {
let dr = &self.spi.dr as *const _ as *const UnsafeCell<u16>;
unsafe { ptr::write_volatile(UnsafeCell::raw_get(dr), byte) };
}
pub fn release(self) -> (SPI, (SCKPIN, MISOPIN, MOSIPIN)) {
(self.spi, self.pins)
}
}
impl<SPI, SCKPIN, MISOPIN, MOSIPIN> ::embedded_hal::blocking::spi::Transfer<u8>
for Spi<SPI, SCKPIN, MISOPIN, MOSIPIN, EightBit>
where
SPI: Deref<Target = SpiRegisterBlock>,
{
type Error = Error;
fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> {
self.set_bidi();
for word in words.iter_mut() {
nb::block!(self.check_send())?;
self.send_u8(*word);
nb::block!(self.check_read())?;
*word = self.read_u8();
}
Ok(words)
}
}
impl<SPI, SCKPIN, MISOPIN, MOSIPIN> ::embedded_hal::blocking::spi::Write<u8>
for Spi<SPI, SCKPIN, MISOPIN, MOSIPIN, EightBit>
where
SPI: Deref<Target = SpiRegisterBlock>,
{
type Error = Error;
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
let mut bufcap: u8 = 0;
self.set_send_only();
nb::block!(self.check_send())?;
for word in words {
while bufcap == 0 {
bufcap = self.send_buffer_size();
}
self.send_u8(*word);
bufcap -= 1;
}
nb::block!(self.check_send()).ok();
Ok(())
}
}
impl<SPI, SCKPIN, MISOPIN, MOSIPIN> ::embedded_hal::blocking::spi::Transfer<u16>
for Spi<SPI, SCKPIN, MISOPIN, MOSIPIN, SixteenBit>
where
SPI: Deref<Target = SpiRegisterBlock>,
{
type Error = Error;
fn transfer<'w>(&mut self, words: &'w mut [u16]) -> Result<&'w [u16], Self::Error> {
self.set_bidi();
for word in words.iter_mut() {
nb::block!(self.check_send())?;
self.send_u16(*word);
nb::block!(self.check_read())?;
*word = self.read_u16();
}
Ok(words)
}
}
impl<SPI, SCKPIN, MISOPIN, MOSIPIN> ::embedded_hal::blocking::spi::Write<u16>
for Spi<SPI, SCKPIN, MISOPIN, MOSIPIN, SixteenBit>
where
SPI: Deref<Target = SpiRegisterBlock>,
{
type Error = Error;
fn write(&mut self, words: &[u16]) -> Result<(), Self::Error> {
self.set_send_only();
for word in words {
nb::block!(self.check_send())?;
self.send_u16(*word);
}
nb::block!(self.check_send()).ok();
Ok(())
}
}