diff --git a/CHANGELOG.md b/CHANGELOG.md index 230faead..7225631a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/). [#494]: https://github.com/stm32-rs/stm32f1xx-hal/pull/494 [#496]: https://github.com/stm32-rs/stm32f1xx-hal/pull/496 +### Breaking changes + +- Relax pin type generics for `Serial`, `I2c`, `Spi`, `Can`. + Use enums of pin tuples and `Enum::from<(tuple)>` for pin remap before passing to peripheral. + Remove `RemapStruct`s. +- Use independent `Spi` and `SpiSlave` structures instead of `OP` generic + ## [v0.10.0] - 2022-12-12 - `Timer`: adds `get_interrupt` to `Timer` diff --git a/examples/can-echo.rs b/examples/can-echo.rs index a3ea5c02..ac0da945 100644 --- a/examples/can-echo.rs +++ b/examples/can-echo.rs @@ -10,7 +10,7 @@ use panic_halt as _; use bxcan::filter::Mask32; use cortex_m_rt::entry; use nb::block; -use stm32f1xx_hal::{can::Can, pac, prelude::*}; +use stm32f1xx_hal::{gpio::Floating, pac, prelude::*}; #[entry] fn main() -> ! { @@ -27,15 +27,15 @@ fn main() -> ! { let mut afio = dp.AFIO.constrain(); let mut can1 = { - #[cfg(not(feature = "connectivity"))] - let can = Can::new(dp.CAN1, dp.USB); - #[cfg(feature = "connectivity")] - let can = Can::new(dp.CAN1); + let gpioa = dp.GPIOA.split(); + let rx = gpioa.pa11; + let tx = gpioa.pa12; - let mut gpioa = dp.GPIOA.split(); - let rx = gpioa.pa11.into_floating_input(&mut gpioa.crh); - let tx = gpioa.pa12.into_alternate_push_pull(&mut gpioa.crh); - can.assign_pins((tx, rx), &mut afio.mapr); + let can = dp.CAN1.can::( + #[cfg(not(feature = "connectivity"))] + dp.USB, + (tx, rx, &mut afio.mapr), + ); // APB1 (PCLK1): 8MHz, Bit rate: 125kBit/s, Sample Point 87.5% // Value was calculated with http://www.bittiming.can-wiki.info/ @@ -50,12 +50,10 @@ fn main() -> ! { #[cfg(feature = "connectivity")] let _can2 = { - let can = Can::new(dp.CAN2); - - let mut gpiob = dp.GPIOB.split(); - let rx = gpiob.pb5.into_floating_input(&mut gpiob.crl); - let tx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); - can.assign_pins((tx, rx), &mut afio.mapr); + let gpiob = dp.GPIOB.split(); + let can = dp + .CAN2 + .can::((gpiob.pb6, gpiob.pb5, &mut afio.mapr)); // APB1 (PCLK1): 8MHz, Bit rate: 125kBit/s, Sample Point 87.5% // Value was calculated with http://www.bittiming.can-wiki.info/ diff --git a/examples/can-loopback.rs b/examples/can-loopback.rs index 1812a809..09ca81c2 100644 --- a/examples/can-loopback.rs +++ b/examples/can-loopback.rs @@ -12,7 +12,7 @@ use panic_halt as _; use cortex_m_rt::entry; use nb::block; -use stm32f1xx_hal::{can::Can, pac, prelude::*}; +use stm32f1xx_hal::{can::Can, gpio::Floating, pac, prelude::*}; #[entry] fn main() -> ! { @@ -25,11 +25,11 @@ fn main() -> ! { // resonator must be used. rcc.cfgr.use_hse(8.MHz()).freeze(&mut flash.acr); - #[cfg(not(feature = "connectivity"))] - let can = Can::new(dp.CAN1, dp.USB); - - #[cfg(feature = "connectivity")] - let can = Can::new(dp.CAN1); + let can = Can::<_, Floating>::new_loopback( + dp.CAN1, + #[cfg(not(feature = "connectivity"))] + dp.USB, + ); // Use loopback mode: No pins need to be assigned to peripheral. // APB1 (PCLK1): 8MHz, Bit rate: 500Bit/s, Sample Point 87.5% diff --git a/examples/can-rtic.rs b/examples/can-rtic.rs index c4273a68..415a5c43 100644 --- a/examples/can-rtic.rs +++ b/examples/can-rtic.rs @@ -55,7 +55,7 @@ mod app { use super::{enqueue_frame, PriorityFrame}; use bxcan::{filter::Mask32, ExtendedId, Fifo, Frame, Interrupts, Rx0, StandardId, Tx}; use heapless::binary_heap::{BinaryHeap, Max}; - use stm32f1xx_hal::{can::Can, pac::CAN1, prelude::*}; + use stm32f1xx_hal::{can::Can, gpio::Floating, pac::CAN1, prelude::*}; #[local] struct Local { @@ -82,18 +82,21 @@ mod app { .pclk2(64.MHz()) .freeze(&mut flash.acr); + // Select pins for CAN1. + let gpioa = cx.device.GPIOA.split(); + let can_rx_pin = gpioa.pa11; + let can_tx_pin = gpioa.pa12; + let mut afio = cx.device.AFIO.constrain(); + #[cfg(not(feature = "connectivity"))] - let can = Can::new(cx.device.CAN1, cx.device.USB); + let can = Can::<_, Floating>::new( + cx.device.CAN1, + cx.device.USB, + (can_tx_pin, can_rx_pin, &mut afio.mapr), + ); #[cfg(feature = "connectivity")] - let can = Can::new(cx.device.CAN1); - - // Select pins for CAN1. - let mut gpioa = cx.device.GPIOA.split(); - let can_rx_pin = gpioa.pa11.into_floating_input(&mut gpioa.crh); - let can_tx_pin = gpioa.pa12.into_alternate_push_pull(&mut gpioa.crh); - let mut afio = cx.device.AFIO.constrain(); - can.assign_pins((can_tx_pin, can_rx_pin), &mut afio.mapr); + let can = Can::<_, Floating>::new(cx.device.CAN1, (can_tx_pin, can_rx_pin, &mut afio.mapr)); // APB1 (PCLK1): 16MHz, Bit rate: 1000kBit/s, Sample Point 87.5% // Value was calculated with http://www.bittiming.can-wiki.info/ diff --git a/examples/mfrc522.rs b/examples/mfrc522.rs index fc9f5447..257c2fcc 100644 --- a/examples/mfrc522.rs +++ b/examples/mfrc522.rs @@ -7,9 +7,12 @@ use panic_itm as _; use cortex_m::iprintln; use cortex_m_rt::entry; -use embedded_hal_02::spi::{Mode, Phase, Polarity}; use mfrc522::Mfrc522; -use stm32f1xx_hal::{pac, prelude::*, spi::Spi}; +use stm32f1xx_hal::{ + pac, + prelude::*, + spi::{Mode, Phase, Polarity, Spi}, +}; pub const MODE: Mode = Mode { polarity: Polarity::IdleLow, phase: Phase::CaptureOnFirstTransition, @@ -32,10 +35,9 @@ fn main() -> ! { let sck = gpioa.pa5.into_alternate_push_pull(&mut gpioa.crl); let miso = gpioa.pa6; let mosi = gpioa.pa7.into_alternate_push_pull(&mut gpioa.crl); - let spi = Spi::spi1( + let spi = Spi::new( dp.SPI1, - (sck, miso, mosi), - &mut afio.mapr, + (sck, miso, mosi, &mut afio.mapr), MODE, 1.MHz(), &clocks, diff --git a/examples/serial-dma-circ.rs b/examples/serial-dma-circ.rs index f6e7aad1..94f6ac23 100644 --- a/examples/serial-dma-circ.rs +++ b/examples/serial-dma-circ.rs @@ -50,8 +50,7 @@ fn main() -> ! { let serial = Serial::new( p.USART1, - (tx, rx), - &mut afio.mapr, + (tx, rx, &mut afio.mapr), Config::default().baudrate(9_600.bps()), &clocks, ); diff --git a/examples/serial-dma-peek.rs b/examples/serial-dma-peek.rs index 39b4e0ee..af1541db 100644 --- a/examples/serial-dma-peek.rs +++ b/examples/serial-dma-peek.rs @@ -49,8 +49,7 @@ fn main() -> ! { let serial = Serial::new( p.USART1, - (tx, rx), - &mut afio.mapr, + (tx, rx, &mut afio.mapr), Config::default(), &clocks, ); diff --git a/examples/serial-dma-rx.rs b/examples/serial-dma-rx.rs index 0d78fc33..b0d190cd 100644 --- a/examples/serial-dma-rx.rs +++ b/examples/serial-dma-rx.rs @@ -49,8 +49,7 @@ fn main() -> ! { let serial = Serial::new( p.USART1, - (tx, rx), - &mut afio.mapr, + (tx, rx, &mut afio.mapr), Config::default().baudrate(9_600.bps()), &clocks, ); diff --git a/examples/serial-dma-tx.rs b/examples/serial-dma-tx.rs index be1dd7b5..5e839638 100644 --- a/examples/serial-dma-tx.rs +++ b/examples/serial-dma-tx.rs @@ -49,8 +49,7 @@ fn main() -> ! { let serial = Serial::new( p.USART1, - (tx, rx), - &mut afio.mapr, + (tx, rx, &mut afio.mapr), Config::default().baudrate(9600.bps()), &clocks, ); diff --git a/examples/serial-fmt.rs b/examples/serial-fmt.rs index 1fe371c1..b12fc75f 100644 --- a/examples/serial-fmt.rs +++ b/examples/serial-fmt.rs @@ -61,8 +61,7 @@ fn main() -> ! { // the registers are used to enable and configure the device. let serial = Serial::new( p.USART3, - (tx, rx), - &mut afio.mapr, + (tx, rx, &mut afio.mapr), Config::default().baudrate(9600.bps()), &clocks, ); diff --git a/examples/serial-interrupt-idle.rs b/examples/serial-interrupt-idle.rs index c8e91811..fa79a4da 100644 --- a/examples/serial-interrupt-idle.rs +++ b/examples/serial-interrupt-idle.rs @@ -45,7 +45,7 @@ fn main() -> ! { // Set up the usart device. Takes ownership over the USART register and tx/rx pins. The rest of // the registers are used to enable and configure the device. let (mut tx, mut rx) = - Serial::new(p.USART1, (tx, rx), &mut afio.mapr, 115_200.bps(), &clocks).split(); + Serial::new(p.USART1, (tx, rx, &mut afio.mapr), 115_200.bps(), &clocks).split(); tx.listen(); rx.listen(); rx.listen_idle(); @@ -69,7 +69,7 @@ static mut WIDX: usize = 0; unsafe fn write(buf: &[u8]) { if let Some(tx) = TX.as_mut() { buf.iter() - .for_each(|w| if let Err(_err) = nb::block!(tx.write_u8(*w)) {}) + .for_each(|w| if let Err(_err) = nb::block!(tx.write(*w)) {}) } } #[interrupt] diff --git a/examples/serial.rs b/examples/serial.rs index b94bc41f..e62e0cf2 100644 --- a/examples/serial.rs +++ b/examples/serial.rs @@ -14,11 +14,7 @@ use cortex_m::asm; use nb::block; use cortex_m_rt::entry; -use stm32f1xx_hal::{ - pac, - prelude::*, - serial::{Config, Serial}, -}; +use stm32f1xx_hal::{pac, prelude::*, serial::Config}; #[entry] fn main() -> ! { @@ -60,10 +56,8 @@ fn main() -> ! { // Set up the usart device. Take ownership over the USART register and tx/rx pins. The rest of // the registers are used to enable and configure the device. - let mut serial = Serial::new( - p.USART3, - (tx, rx), - &mut afio.mapr, + let mut serial = p.USART3.serial( + (tx, rx, &mut afio.mapr), Config::default().baudrate(9600.bps()), &clocks, ); diff --git a/examples/serial_9bits.rs b/examples/serial_9bits.rs index 2eebee2d..043587da 100644 --- a/examples/serial_9bits.rs +++ b/examples/serial_9bits.rs @@ -13,6 +13,7 @@ use cortex_m_rt::entry; use nb::block; use panic_halt as _; use stm32f1xx_hal::{ + gpio::{Floating, PushPull}, pac, prelude::*, serial::{self, Config, Error, Serial}, @@ -111,17 +112,16 @@ fn main() -> ! { let mut afio = p.AFIO.constrain(); // Prepare the GPIOB peripheral. - let mut gpiob = p.GPIOB.split(); + let gpiob = p.GPIOB.split(); - let tx_pin = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh); + let tx_pin = gpiob.pb10; let rx_pin = gpiob.pb11; // Set up the usart device. Take ownership over the USART register and tx/rx pins. The rest of // the registers are used to enable and configure the device. - let serial = Serial::new( + let serial = Serial::<_, PushPull, Floating>::new( p.USART3, - (tx_pin, rx_pin), - &mut afio.mapr, + (tx_pin, rx_pin, &mut afio.mapr), Config::default() .baudrate(9600.bps()) .wordlength_9bits() diff --git a/examples/serial_config.rs b/examples/serial_config.rs index 6c2f646f..59f01e4b 100644 --- a/examples/serial_config.rs +++ b/examples/serial_config.rs @@ -60,8 +60,7 @@ fn main() -> ! { // the registers are used to enable and configure the device. let serial = Serial::new( p.USART3, - (tx, rx), - &mut afio.mapr, + (tx, rx, &mut afio.mapr), serial::Config::default() .baudrate(9600.bps()) .stopbits(serial::StopBits::STOP2) diff --git a/examples/serial_reconfigure.rs b/examples/serial_reconfigure.rs index c34ee900..c4f6c37b 100644 --- a/examples/serial_reconfigure.rs +++ b/examples/serial_reconfigure.rs @@ -62,8 +62,7 @@ fn main() -> ! { // the registers are used to enable and configure the device. let mut serial = Serial::new( p.USART3, - (tx, rx), - &mut afio.mapr, + (tx, rx, &mut afio.mapr), Config::default().baudrate(9600.bps()), &clocks, ); diff --git a/examples/spi-dma.rs b/examples/spi-dma.rs index 331b3c09..56350f44 100644 --- a/examples/spi-dma.rs +++ b/examples/spi-dma.rs @@ -40,7 +40,7 @@ fn main() -> ! { polarity: Polarity::IdleLow, phase: Phase::CaptureOnFirstTransition, }; - let spi = Spi::spi2(dp.SPI2, pins, spi_mode, 100.kHz(), &clocks); + let spi = Spi::new(dp.SPI2, pins, spi_mode, 100.kHz(), &clocks); // Set up the DMA device let dma = dp.DMA1.split(); diff --git a/examples/spi-slave.rs b/examples/spi-slave.rs index 5bf1725f..e322bab3 100644 --- a/examples/spi-slave.rs +++ b/examples/spi-slave.rs @@ -20,28 +20,13 @@ pub const MODE: Mode = Mode { }; use stm32f1xx_hal::{ - gpio::{ - gpiob::{PB13, PB14, PB15}, - Alternate, Floating, Input, PushPull, - }, + gpio::Floating, pac::{self, interrupt, Peripherals, SPI2}, prelude::*, - spi::{Event, Slave, Spi, Spi2NoRemap}, + spi::{Event, SpiSlave}, }; -type SlaveSpi = Spi< - SPI2, - Spi2NoRemap, - ( - PB13>, - PB14>, - PB15>, - ), - u8, - Slave, ->; - -static mut SPI2SLAVE: Option = None; +static mut SPI2SLAVE: Option> = None; #[entry] fn main() -> ! { @@ -53,29 +38,26 @@ fn main() -> ! { let clocks = rcc.cfgr.freeze(&mut flash.acr); let mut afio = dp.AFIO.constrain(); - let mut gpioa = dp.GPIOA.split(); + let gpioa = dp.GPIOA.split(); let mut gpiob = dp.GPIOB.split(); // SPI1 - let sck = gpioa.pa5.into_alternate_push_pull(&mut gpioa.crl); + // Convert pins during SPI initialization + let sck = gpioa.pa5; let miso = gpioa.pa6; - let mosi = gpioa.pa7.into_alternate_push_pull(&mut gpioa.crl); + let mosi = gpioa.pa7; - let spi1 = Spi::spi1( - dp.SPI1, - (sck, miso, mosi), - &mut afio.mapr, - MODE, - 10.kHz(), - &clocks, - ); + let spi1 = dp + .SPI1 + .spi::((sck, miso, mosi, &mut afio.mapr), MODE, 10.kHz(), &clocks); // SPI2 + // Convert pins before SPI initialization let sck = gpiob.pb13; let miso = gpiob.pb14.into_alternate_push_pull(&mut gpiob.crh); let mosi = gpiob.pb15; - let spi2 = Spi::spi2_slave(dp.SPI2, (sck, miso, mosi), MODE); + let spi2 = dp.SPI2.spi_slave((sck, miso, mosi), MODE); // Set up the DMA device let dma = dp.DMA1.split(); @@ -133,7 +115,7 @@ unsafe fn SPI2() { asm::bkpt(); } if spi2.is_rx_not_empty() { - if let Ok(w) = nb::block!(spi2.read()) { + if let Ok(w) = nb::block!(spi2.read_nonblocking()) { R_BUFFER[RIDX] = w; RIDX += 1; if RIDX >= R_BUFFER_LEN - 1 { @@ -142,7 +124,7 @@ unsafe fn SPI2() { } } if spi2.is_tx_empty() { - if let Ok(()) = nb::block!(spi2.send(W_BUFFER[WIDX])) { + if let Ok(()) = nb::block!(spi2.write_nonblocking(W_BUFFER[WIDX])) { WIDX += 1; if WIDX >= W_BUFFER_LEN { WIDX = 0; diff --git a/examples/spi.rs b/examples/spi.rs index 0d87be38..37aafac9 100644 --- a/examples/spi.rs +++ b/examples/spi.rs @@ -6,24 +6,19 @@ use cortex_m_rt::entry; use panic_halt as _; -use embedded_hal_02::spi::{Mode, Phase, Polarity}; pub const MODE: Mode = Mode { phase: Phase::CaptureOnSecondTransition, polarity: Polarity::IdleHigh, }; use stm32f1xx_hal::{ - gpio::gpioa::PA4, - gpio::{Output, PushPull}, + gpio::{Output, PA4}, pac::{Peripherals, SPI1}, prelude::*, - spi::{Pins, Spi, Spi1NoRemap}, + spi::{Mode, Phase, Polarity, Spi}, }; -fn setup() -> ( - Spi, u8>, - PA4>, -) { +fn setup() -> (Spi, PA4) { let dp = Peripherals::take().unwrap(); let mut flash = dp.FLASH.constrain(); @@ -40,10 +35,9 @@ fn setup() -> ( let mosi = gpioa.pa7.into_alternate_push_pull(&mut gpioa.crl); let cs = gpioa.pa4.into_push_pull_output(&mut gpioa.crl); - let spi = Spi::spi1( + let spi = Spi::new( dp.SPI1, - (sck, miso, mosi), - &mut afio.mapr, + (sck, miso, mosi, &mut afio.mapr), MODE, 1.MHz(), &clocks, diff --git a/src/can.rs b/src/can.rs index 7073763d..34810aa3 100644 --- a/src/can.rs +++ b/src/can.rs @@ -20,117 +20,203 @@ //! | RX | PB5 | PB12 | use crate::afio::MAPR; -use crate::gpio::{self, Alternate, Input}; +use crate::gpio::{self, Alternate, Cr, Floating, Input, PinMode, PullUp}; use crate::pac::{self, RCC}; -pub trait Pins: crate::Sealed { - type Instance; - fn remap(mapr: &mut MAPR); +pub trait InMode {} +impl InMode for Floating {} +impl InMode for PullUp {} + +pub mod can1 { + use super::*; + + remap! { + Pins: [ + #[cfg(not(feature = "connectivity"))] + All, Tx, Rx, PA12, PA11 => { |_, w| unsafe { w.can_remap().bits(0) } }; + #[cfg(feature = "connectivity")] + All, Tx, Rx, PA12, PA11 => { |_, w| unsafe { w.can1_remap().bits(0) } }; + #[cfg(not(feature = "connectivity"))] + Remap, RemapTx, RemapRx, PB9, PB8 => { |_, w| unsafe { w.can_remap().bits(10) } }; + #[cfg(feature = "connectivity")] + Remap, RemapTx, RemapRx, PB9, PB8 => { |_, w| unsafe { w.can1_remap().bits(10) } }; + ] + } } -impl crate::Sealed - for (gpio::PA12>, gpio::PA11>) -{ -} -impl Pins for (gpio::PA12>, gpio::PA11>) { - type Instance = pac::CAN1; - - fn remap(mapr: &mut MAPR) { - #[cfg(not(feature = "connectivity"))] - mapr.modify_mapr(|_, w| unsafe { w.can_remap().bits(0) }); - #[cfg(feature = "connectivity")] - mapr.modify_mapr(|_, w| unsafe { w.can1_remap().bits(0) }); +#[cfg(feature = "connectivity")] +pub mod can2 { + use super::*; + + remap! { + Pins: [ + All, Tx, Rx, PB6, PB5 => { |_, w| w.can2_remap().bit(false) }; + Remap, RemapTx, RemapRx, PB13, PB12 => { |_, w| w.can2_remap().bit(true) }; + ] } } -impl crate::Sealed for (gpio::PB9>, gpio::PB8>) {} -impl Pins for (gpio::PB9>, gpio::PB8>) { - type Instance = pac::CAN1; - - fn remap(mapr: &mut MAPR) { - #[cfg(not(feature = "connectivity"))] - mapr.modify_mapr(|_, w| unsafe { w.can_remap().bits(0b10) }); - #[cfg(feature = "connectivity")] - mapr.modify_mapr(|_, w| unsafe { w.can1_remap().bits(0b10) }); +macro_rules! remap { + ($name:ident: [ + $($(#[$attr:meta])* $rname:ident, $txonly:ident, $rxonly:ident, $TX:ident, $RX:ident => { $remapex:expr };)+ + ]) => { + pub enum $name { + $( + $(#[$attr])* + $rname { tx: gpio::$TX, rx: gpio::$RX> }, + $(#[$attr])* + $txonly { tx: gpio::$TX }, + $(#[$attr])* + $rxonly { rx: gpio::$RX> }, + )+ + } + + $( + $(#[$attr])* + impl From<(gpio::$TX, gpio::$RX>, &mut MAPR)> for $name { + fn from(p: (gpio::$TX, gpio::$RX>, &mut MAPR)) -> Self { + p.2.modify_mapr($remapex); + Self::$rname { tx: p.0, rx: p.1 } + } + } + + $(#[$attr])* + impl From<(gpio::$TX, gpio::$RX, &mut MAPR)> for $name + where + Input: PinMode, + INMODE: InMode, + { + fn from(p: (gpio::$TX, gpio::$RX, &mut MAPR)) -> Self { + let mut cr = Cr; + let tx = p.0.into_mode(&mut cr); + let rx = p.1.into_mode(&mut cr); + p.2.modify_mapr($remapex); + Self::$rname { tx, rx } + } + } + + $(#[$attr])* + impl From<(gpio::$TX, &mut MAPR)> for $name { + fn from(p: (gpio::$TX, &mut MAPR)) -> Self { + let tx = p.0.into_mode(&mut Cr); + p.1.modify_mapr($remapex); + Self::$txonly { tx } + } + } + + $(#[$attr])* + impl From<(gpio::$RX, &mut MAPR)> for $name + where + Input: PinMode, + INMODE: InMode, + { + fn from(p: (gpio::$RX, &mut MAPR)) -> Self { + let rx = p.0.into_mode(&mut Cr); + p.1.modify_mapr($remapex); + Self::$rxonly { rx } + } + } + )+ } } - -#[cfg(feature = "connectivity")] -impl crate::Sealed - for (gpio::PB13>, gpio::PB12>) -{ +use remap; + +pub trait CanExt: Sized + Instance { + fn can( + self, + #[cfg(not(feature = "connectivity"))] usb: pac::USB, + pins: impl Into>, + ) -> Can; + fn can_loopback( + self, + #[cfg(not(feature = "connectivity"))] usb: pac::USB, + ) -> Can; } -#[cfg(feature = "connectivity")] -impl Pins for (gpio::PB13>, gpio::PB12>) { - type Instance = pac::CAN2; - fn remap(mapr: &mut MAPR) { - mapr.modify_mapr(|_, w| w.can2_remap().clear_bit()); +impl CanExt for CAN { + fn can( + self, + #[cfg(not(feature = "connectivity"))] usb: pac::USB, + pins: impl Into>, + ) -> Can { + Can::new( + self, + #[cfg(not(feature = "connectivity"))] + usb, + pins, + ) + } + fn can_loopback( + self, + #[cfg(not(feature = "connectivity"))] usb: pac::USB, + ) -> Can { + Can::new_loopback( + self, + #[cfg(not(feature = "connectivity"))] + usb, + ) } } +pub trait Instance: crate::rcc::Enable { + type Pins; +} +impl Instance for pac::CAN1 { + type Pins = can1::Pins; +} #[cfg(feature = "connectivity")] -impl crate::Sealed for (gpio::PB6>, gpio::PB5>) {} -#[cfg(feature = "connectivity")] -impl Pins for (gpio::PB6>, gpio::PB5>) { - type Instance = pac::CAN2; - - fn remap(mapr: &mut MAPR) { - mapr.modify_mapr(|_, w| w.can2_remap().set_bit()); - } +impl Instance for pac::CAN2 { + type Pins = can2::Pins; } /// Interface to the CAN peripheral. -pub struct Can { - _peripheral: Instance, +#[allow(unused)] +pub struct Can { + can: CAN, + pins: Option>, } -impl Can -where - Instance: crate::rcc::Enable, -{ - /// Creates a CAN interaface. +impl Can { + /// Creates a CAN interface. /// /// CAN shares SRAM with the USB peripheral. Take ownership of USB to /// prevent accidental shared usage. - #[cfg(not(feature = "connectivity"))] - pub fn new(can: Instance, _usb: pac::USB) -> Can { + pub fn new( + can: CAN, + #[cfg(not(feature = "connectivity"))] _usb: pac::USB, + pins: impl Into>, + ) -> Can { let rcc = unsafe { &(*RCC::ptr()) }; - Instance::enable(rcc); + CAN::enable(rcc); - Can { _peripheral: can } + let pins = Some(pins.into()); + Can { can, pins } } - /// Creates a CAN interaface. - #[cfg(feature = "connectivity")] - pub fn new(can: Instance) -> Can { + /// Creates a CAN interface in loopback mode + pub fn new_loopback( + can: CAN, + #[cfg(not(feature = "connectivity"))] _usb: pac::USB, + ) -> Can { let rcc = unsafe { &(*RCC::ptr()) }; - Instance::enable(rcc); - - Can { _peripheral: can } - } + CAN::enable(rcc); - /// Routes CAN TX signals and RX signals to pins. - pub fn assign_pins

(&self, _pins: P, mapr: &mut MAPR) - where - P: Pins, - { - P::remap(mapr); + Can { can, pins: None } } } -unsafe impl bxcan::Instance for Can { +unsafe impl bxcan::Instance for Can { const REGISTERS: *mut bxcan::RegisterBlock = pac::CAN1::ptr() as *mut _; } #[cfg(feature = "connectivity")] -unsafe impl bxcan::Instance for Can { +unsafe impl bxcan::Instance for Can { const REGISTERS: *mut bxcan::RegisterBlock = pac::CAN2::ptr() as *mut _; } -unsafe impl bxcan::FilterOwner for Can { +unsafe impl bxcan::FilterOwner for Can { const NUM_FILTER_BANKS: u8 = 28; } #[cfg(feature = "connectivity")] -unsafe impl bxcan::MasterInstance for Can {} +unsafe impl bxcan::MasterInstance for Can {} diff --git a/src/gpio.rs b/src/gpio.rs index 4a4a5d5c..cead5be3 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -215,7 +215,7 @@ mod sealed { } use sealed::Interruptable; -use sealed::PinMode; +pub(crate) use sealed::PinMode; impl Interruptable for Input {} impl Interruptable for Dynamic {} @@ -639,43 +639,38 @@ where /// pin. #[inline] pub fn into_alternate_push_pull( - mut self, + self, cr: &mut ::Cr, ) -> Pin> { - self.mode::>(cr); - Pin::new() + self.into_mode(cr) } /// Configures the pin to operate as an alternate function open-drain output /// pin. #[inline] pub fn into_alternate_open_drain( - mut self, + self, cr: &mut ::Cr, ) -> Pin> { - self.mode::>(cr); - Pin::new() + self.into_mode(cr) } /// Configures the pin to operate as a floating input pin #[inline] - pub fn into_floating_input(mut self, cr: &mut ::Cr) -> Pin> { - self.mode::>(cr); - Pin::new() + pub fn into_floating_input(self, cr: &mut ::Cr) -> Pin> { + self.into_mode(cr) } /// Configures the pin to operate as a pulled down input pin #[inline] - pub fn into_pull_down_input(mut self, cr: &mut ::Cr) -> Pin> { - self.mode::>(cr); - Pin::new() + pub fn into_pull_down_input(self, cr: &mut ::Cr) -> Pin> { + self.into_mode(cr) } /// Configures the pin to operate as a pulled up input pin #[inline] - pub fn into_pull_up_input(mut self, cr: &mut ::Cr) -> Pin> { - self.mode::>(cr); - Pin::new() + pub fn into_pull_up_input(self, cr: &mut ::Cr) -> Pin> { + self.into_mode(cr) } /// Configures the pin to operate as an open-drain output pin. @@ -694,8 +689,7 @@ where initial_state: PinState, ) -> Pin> { self._set_state(initial_state); - self.mode::>(cr); - Pin::new() + self.into_mode(cr) } /// Configures the pin to operate as an push-pull output pin. @@ -714,26 +708,23 @@ where initial_state: PinState, ) -> Pin> { self._set_state(initial_state); - self.mode::>(cr); - Pin::new() + self.into_mode(cr) } /// Configures the pin to operate as an push-pull output pin. /// The state will not be changed. #[inline] pub fn into_push_pull_output_with_current_state( - mut self, + self, cr: &mut ::Cr, ) -> Pin> { - self.mode::>(cr); - Pin::new() + self.into_mode(cr) } /// Configures the pin to operate as an analog input pin #[inline] - pub fn into_analog(mut self, cr: &mut ::Cr) -> Pin { - self.mode::(cr); - Pin::new() + pub fn into_analog(self, cr: &mut ::Cr) -> Pin { + self.into_mode(cr) } /// Configures the pin as a pin that can change between input @@ -977,6 +968,12 @@ where (r_bits & !(0b1111 << Self::OFFSET)) | (bits << Self::OFFSET) }); } + + #[inline] + pub(crate) fn into_mode(mut self, cr: &mut ::Cr) -> Pin { + self.mode::(cr); + Pin::new() + } } gpio!(GPIOA, gpioa, PAx, 'A', [ diff --git a/src/i2c.rs b/src/i2c.rs index f62a25e7..5462757b 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -4,8 +4,7 @@ // parts of this code is based on // https://www.st.com/content/ccc/resource/technical/document/application_note/5d/ae/a3/6f/08/69/4e/9b/CD00209826.pdf/files/CD00209826.pdf/jcr:content/translations/en.CD00209826.pdf -use crate::afio::MAPR; -use crate::gpio::{self, Alternate, OpenDrain}; +use crate::gpio::{self, Alternate, Cr, OpenDrain}; use crate::pac::{DWT, I2C1, I2C2, RCC}; use crate::rcc::{BusClock, Clocks, Enable, Reset}; use crate::time::{kHz, Hertz}; @@ -86,42 +85,93 @@ impl From for Mode { } } -/// Helper trait to ensure that the correct I2C pins are used for the corresponding interface -pub trait Pins { - const REMAP: bool; +pub mod i2c1 { + use crate::afio::MAPR; + + use super::*; + + remap! { + Pins: [ + No, PB6, PB7 => MAPR { |_, w| w.i2c1_remap().bit(false) }; + Remap, PB8, PB9 => MAPR { |_, w| w.i2c1_remap().bit(true) }; + ] + } } +pub mod i2c2 { + use super::*; -impl Pins - for ( - gpio::PB6>, - gpio::PB7>, - ) -{ - const REMAP: bool = false; + remap! { + Pins: [ + No, PB10, PB11; + ] + } } -impl Pins - for ( - gpio::PB8>, - gpio::PB9>, - ) -{ - const REMAP: bool = true; +macro_rules! remap { + ($name:ident: [ + $($rname:ident, $SCL:ident, $SDA:ident $( => $MAPR:ident { $remapex:expr })?;)+ + ]) => { + pub enum $name { + $( + $rname { scl: gpio::$SCL>, sda: gpio::$SDA> }, + )+ + } + + $( + impl From<(gpio::$SCL>, gpio::$SDA> $(, &mut $MAPR)?)> for $name { + fn from(p: (gpio::$SCL>, gpio::$SDA> $(, &mut $MAPR)?)) -> Self { + $(p.2.modify_mapr($remapex);)? + Self::$rname { scl: p.0, sda: p.1 } + } + } + + impl From<(gpio::$SCL, gpio::$SDA $(, &mut $MAPR)?)> for $name { + fn from(p: (gpio::$SCL, gpio::$SDA $(, &mut $MAPR)?)) -> Self { + let mut cr = Cr; + let scl = p.0.into_mode(&mut cr); + let sda = p.1.into_mode(&mut cr); + $(p.2.modify_mapr($remapex);)? + Self::$rname { scl, sda } + } + } + )+ + } } +use remap; -impl Pins - for ( - gpio::PB10>, - gpio::PB11>, - ) -{ - const REMAP: bool = false; +pub trait I2cExt: Sized + Instance { + fn i2c(self, pins: impl Into, mode: impl Into, clocks: &Clocks) -> I2c; + + fn blocking_i2c( + self, + pins: impl Into, + mode: impl Into, + clocks: &Clocks, + start_timeout_us: u32, + start_retries: u8, + addr_timeout_us: u32, + data_timeout_us: u32, + ) -> BlockingI2c { + Self::i2c(self, pins, mode, clocks).blocking( + start_timeout_us, + start_retries, + addr_timeout_us, + data_timeout_us, + clocks, + ) + } +} + +impl I2cExt for I2C { + fn i2c(self, pins: impl Into, mode: impl Into, clocks: &Clocks) -> I2c { + I2c::new(self, pins, mode, clocks) + } } /// I2C peripheral operating in master mode -pub struct I2c { +pub struct I2c { i2c: I2C, - pins: PINS, + pins: I2C::Pins, mode: Mode, pclk1: Hertz, } @@ -129,44 +179,24 @@ pub struct I2c { pub trait Instance: crate::Sealed + Deref + Enable + Reset + BusClock { + type Pins; } -impl Instance for I2C1 {} -impl Instance for I2C2 {} - -impl I2c { - /// Creates a generic I2C1 object on pins PB6 and PB7 or PB8 and PB9 (if remapped) - pub fn i2c1>( - i2c: I2C1, - pins: PINS, - mapr: &mut MAPR, - mode: M, - clocks: &Clocks, - ) -> Self - where - PINS: Pins, - { - mapr.modify_mapr(|_, w| w.i2c1_remap().bit(PINS::REMAP)); - I2c::::configure(i2c, pins, mode, clocks) - } +impl Instance for I2C1 { + type Pins = i2c1::Pins; } - -impl I2c { - /// Creates a generic I2C2 object on pins PB10 and PB11 using the embedded-hal `BlockingI2c` trait. - pub fn i2c2>(i2c: I2C2, pins: PINS, mode: M, clocks: &Clocks) -> Self - where - PINS: Pins, - { - I2c::::configure(i2c, pins, mode, clocks) - } +impl Instance for I2C2 { + type Pins = i2c2::Pins; } -impl I2c -where - I2C: Instance, -{ - /// Configures the I2C peripheral to work in master mode - fn configure>(i2c: I2C, pins: PINS, mode: M, clocks: &Clocks) -> Self { +impl I2c { + /// Creates a generic I2C object + pub fn new( + i2c: I2C, + pins: impl Into, + mode: impl Into, + clocks: &Clocks, + ) -> Self { let mode = mode.into(); let rcc = unsafe { &(*RCC::ptr()) }; I2C::enable(rcc); @@ -178,7 +208,7 @@ where let mut i2c = I2c { i2c, - pins, + pins: pins.into(), mode, pclk1, }; @@ -187,10 +217,7 @@ where } } -impl I2c -where - I2C: Instance, -{ +impl I2c { /// Initializes I2C. Configures the `I2C_TRISE`, `I2C_CRX`, and `I2C_CCR` registers /// according to the system frequency and I2C mode. fn init(&mut self) { @@ -256,7 +283,7 @@ where } /// Releases the I2C peripheral and associated pins - pub fn release(self) -> (I2C, PINS) { + pub fn release(self) -> (I2C, I2C::Pins) { (self.i2c, self.pins) } } diff --git a/src/i2c/blocking.rs b/src/i2c/blocking.rs index f98f3f4a..af8ba7f7 100644 --- a/src/i2c/blocking.rs +++ b/src/i2c/blocking.rs @@ -4,8 +4,8 @@ use super::*; /// /// **NOTE**: Before using blocking I2C, you need to enable the DWT cycle counter using the /// [DWT::enable_cycle_counter] method. -pub struct BlockingI2c { - nb: I2c, +pub struct BlockingI2c { + nb: I2c, start_retries: u8, timeouts: DwtTimeouts, } @@ -17,67 +17,30 @@ pub struct DwtTimeouts { data: u32, } -impl BlockingI2c { +impl BlockingI2c { /// Creates a blocking I2C1 object on pins PB6 and PB7 or PB8 and PB9 using the embedded-hal `BlockingI2c` trait. #[allow(clippy::too_many_arguments)] - pub fn i2c1>( - i2c: I2C1, - pins: PINS, - mapr: &mut MAPR, - mode: M, + pub fn new( + i2c: I2C, + pins: impl Into, + mode: impl Into, clocks: &Clocks, start_timeout_us: u32, start_retries: u8, addr_timeout_us: u32, data_timeout_us: u32, - ) -> Self - where - PINS: Pins, - { - mapr.modify_mapr(|_, w| w.i2c1_remap().bit(PINS::REMAP)); - BlockingI2c::::configure( - i2c, - pins, - mode, - clocks, + ) -> Self { + I2c::new(i2c, pins, mode, clocks).blocking( start_timeout_us, start_retries, addr_timeout_us, data_timeout_us, - ) - } -} - -impl BlockingI2c { - /// Creates a blocking I2C2 object on pins PB10 and PB11 - #[allow(clippy::too_many_arguments)] - pub fn i2c2>( - i2c: I2C2, - pins: PINS, - mode: M, - clocks: &Clocks, - start_timeout_us: u32, - start_retries: u8, - addr_timeout_us: u32, - data_timeout_us: u32, - ) -> Self - where - PINS: Pins, - { - BlockingI2c::::configure( - i2c, - pins, - mode, clocks, - start_timeout_us, - start_retries, - addr_timeout_us, - data_timeout_us, ) } } -impl I2c { +impl I2c { /// Generates a blocking I2C instance from a universal I2C object pub fn blocking( self, @@ -86,7 +49,7 @@ impl I2c { addr_timeout_us: u32, data_timeout_us: u32, clocks: &Clocks, - ) -> BlockingI2c { + ) -> BlockingI2c { let sysclk_mhz = clocks.sysclk().to_MHz(); BlockingI2c { nb: self, @@ -98,7 +61,7 @@ impl I2c { }, } } - pub fn blocking_default(self, clocks: &Clocks) -> BlockingI2c { + pub fn blocking_default(self, clocks: Clocks) -> BlockingI2c { let sysclk_mhz = clocks.sysclk().to_MHz(); BlockingI2c { nb: self, @@ -163,29 +126,7 @@ macro_rules! busy_wait_cycles { }}; } -impl BlockingI2c { - #[allow(clippy::too_many_arguments)] - fn configure>( - i2c: I2C, - pins: PINS, - mode: M, - clocks: &Clocks, - start_timeout_us: u32, - start_retries: u8, - addr_timeout_us: u32, - data_timeout_us: u32, - ) -> Self { - I2c::::configure(i2c, pins, mode, clocks).blocking( - start_timeout_us, - start_retries, - addr_timeout_us, - data_timeout_us, - clocks, - ) - } -} - -impl BlockingI2c { +impl BlockingI2c { /// Check if START condition is generated. If the condition is not generated, this /// method returns `WouldBlock` so the program can act accordingly /// (busy wait, async, ...) diff --git a/src/i2c/hal_02.rs b/src/i2c/hal_02.rs index ca4b67df..afcd3c4d 100644 --- a/src/i2c/hal_02.rs +++ b/src/i2c/hal_02.rs @@ -1,7 +1,7 @@ use super::*; use embedded_hal_02::blocking::i2c::{Operation, Read, Transactional, Write, WriteRead}; -impl Write for BlockingI2c { +impl Write for BlockingI2c { type Error = Error; fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { @@ -9,7 +9,7 @@ impl Write for BlockingI2c { } } -impl Read for BlockingI2c { +impl Read for BlockingI2c { type Error = Error; fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -17,7 +17,7 @@ impl Read for BlockingI2c { } } -impl WriteRead for BlockingI2c { +impl WriteRead for BlockingI2c { type Error = Error; fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { @@ -25,10 +25,7 @@ impl WriteRead for BlockingI2c { } } -impl Transactional for BlockingI2c -where - I2C: Instance, -{ +impl Transactional for BlockingI2c { type Error = Error; fn exec(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { diff --git a/src/i2c/hal_1.rs b/src/i2c/hal_1.rs index 9cfa2ade..121aaed1 100644 --- a/src/i2c/hal_1.rs +++ b/src/i2c/hal_1.rs @@ -12,7 +12,7 @@ impl Error for super::Error { } } -impl ErrorType for super::BlockingI2c { +impl ErrorType for super::BlockingI2c { type Error = super::Error; } @@ -20,7 +20,7 @@ mod blocking { use super::super::{BlockingI2c, Instance}; use embedded_hal::i2c::Operation; - impl embedded_hal::i2c::I2c for BlockingI2c { + impl embedded_hal::i2c::I2c for BlockingI2c { fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { self.read(addr, buffer) } diff --git a/src/prelude.rs b/src/prelude.rs index c5a3a523..1da61912 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,5 +1,7 @@ pub use crate::adc::ChannelTimeSequence as _stm32_hal_adc_ChannelTimeSequence; pub use crate::afio::AfioExt as _stm32_hal_afio_AfioExt; +#[cfg(feature = "has-can")] +pub use crate::can::CanExt as _; pub use crate::crc::CrcExt as _stm32_hal_crc_CrcExt; pub use crate::dma::CircReadDma as _stm32_hal_dma_CircReadDma; pub use crate::dma::DmaExt as _stm32_hal_dma_DmaExt; @@ -10,7 +12,10 @@ pub use crate::flash::FlashExt as _stm32_hal_flash_FlashExt; pub use crate::gpio::GpioExt as _stm32_hal_gpio_GpioExt; pub use crate::hal_02::adc::OneShot as _embedded_hal_adc_OneShot; pub use crate::hal_02::prelude::*; +pub use crate::i2c::I2cExt as _; pub use crate::rcc::RccExt as _stm32_hal_rcc_RccExt; +pub use crate::serial::SerialExt as _; +pub use crate::spi::SpiExt as _; pub use crate::time::U32Ext as _stm32_hal_time_U32Ext; #[cfg(feature = "rtic")] pub use crate::timer::MonoTimerExt as _stm32f4xx_hal_timer_MonoTimerExt; diff --git a/src/serial.rs b/src/serial.rs index 9647fb0b..d9bafe8c 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -68,7 +68,7 @@ use embedded_dma::{ReadBuffer, WriteBuffer}; use crate::afio::MAPR; use crate::dma::{dma1, CircBuffer, RxDma, Transfer, TxDma, R, W}; -use crate::gpio::{self, Alternate, Input}; +use crate::gpio::{self, Alternate, Cr, Floating, Input, PinMode, PullUp, PushPull}; use crate::pac::{RCC, USART1, USART2, USART3}; use crate::rcc::{BusClock, Clocks, Enable, Reset}; use crate::time::{Bps, U32Ext}; @@ -76,49 +76,145 @@ use crate::time::{Bps, U32Ext}; mod hal_02; mod hal_1; +pub trait InMode {} +impl InMode for Floating {} +impl InMode for PullUp {} + // USART REMAPPING, see: https://www.st.com/content/ccc/resource/technical/document/reference_manual/59/b9/ba/7f/11/af/43/d5/CD00171190.pdf/files/CD00171190.pdf/jcr:content/translations/en.CD00171190.pdf // Section 9.3.8 -pub trait Pins { - fn remap(mapr: &mut MAPR); +pub mod usart1 { + use super::*; + + remap! { + Pins: [ + All, Tx, Rx, PA9, PA10 => { |_, w| w.usart1_remap().bit(false) }; + Remap, RemapTx, RemapRx, PB6, PB7 => { |_, w| w.usart1_remap().bit(true) }; + ] + } +} + +pub mod usart2 { + use super::*; + + remap! { + Pins: [ + All, Tx, Rx, PA2, PA3 => { |_, w| w.usart2_remap().bit(false) }; + Remap, RemapTx, RemapRx, PD5, PD6 => { |_, w| w.usart2_remap().bit(true) }; + ] + } +} + +pub mod usart3 { + use super::*; + + remap! { + Pins: [ + RxTx, Tx, Rx, PB10, PB11 => { |_, w| unsafe { w.usart3_remap().bits(0b00)} }; + Remap1, Remap1Tx, Remap1Rx, PC10, PC11 => { |_, w| unsafe { w.usart3_remap().bits(0b01)} }; + Remap2, Remap2Tx, Remap2Rx, PD8, PD9 => { |_, w| unsafe { w.usart3_remap().bits(0b11)} }; + ] + } } macro_rules! remap { - ($($USART:ty, $TX:ident, $RX:ident => { $remapex:expr };)+) => { + ($name:ident: [ + $($rname:ident, $txonly:ident, $rxonly:ident, $TX:ident, $RX:ident => { $remapex:expr };)+ + ]) => { + pub enum $name { + $( + $rname { tx: gpio::$TX>, rx: gpio::$RX> }, + $txonly { tx: gpio::$TX> }, + $rxonly { rx: gpio::$RX> }, + )+ + } + $( - impl Pins<$USART> for (gpio::$TX>, gpio::$RX>) { - fn remap(mapr: &mut MAPR) { - mapr.modify_mapr($remapex); + impl From<(gpio::$TX>, gpio::$RX>, &mut MAPR)> for $name { + fn from(p: (gpio::$TX>, gpio::$RX>, &mut MAPR)) -> Self { + p.2.modify_mapr($remapex); + Self::$rname { tx: p.0, rx: p.1 } + } + } + + impl From<(gpio::$TX, gpio::$RX, &mut MAPR)> for $name + where + Alternate: PinMode, + Input: PinMode, + INMODE: InMode, + { + fn from(p: (gpio::$TX, gpio::$RX, &mut MAPR)) -> Self { + let mut cr = Cr; + let tx = p.0.into_mode(&mut cr); + let rx = p.1.into_mode(&mut cr); + p.2.modify_mapr($remapex); + Self::$rname { tx, rx } + } + } + + impl From<(gpio::$TX, &mut MAPR)> for $name + where + Alternate: PinMode, + { + fn from(p: (gpio::$TX, &mut MAPR)) -> Self { + let tx = p.0.into_mode(&mut Cr); + p.1.modify_mapr($remapex); + Self::$txonly { tx } + } + } + + impl From<(gpio::$RX, &mut MAPR)> for $name + where + Input: PinMode, + INMODE: InMode, + { + fn from(p: (gpio::$RX, &mut MAPR)) -> Self { + let rx = p.0.into_mode(&mut Cr); + p.1.modify_mapr($remapex); + Self::$rxonly { rx } } } )+ } } +use remap; -remap!( - USART1, PA9, PA10 => { |_, w| w.usart1_remap().bit(false) }; - USART1, PB6, PB7 => { |_, w| w.usart1_remap().bit(true) }; - - USART2, PA2, PA3 => { |_, w| w.usart2_remap().bit(false) }; - USART2, PD5, PD6 => { |_, w| w.usart2_remap().bit(true) }; +pub trait SerialExt: Sized + Instance { + fn serial( + self, + pins: impl Into>, + config: impl Into, + clocks: &Clocks, + ) -> Serial; +} - USART3, PB10, PB11 => { |_, w| unsafe { w.usart3_remap().bits(0b00)} }; - USART3, PC10, PC11 => { |_, w| unsafe { w.usart3_remap().bits(0b01)} }; - USART3, PD8, PD9 => { |_, w| unsafe { w.usart3_remap().bits(0b11)} }; -); +impl SerialExt for USART { + fn serial( + self, + pins: impl Into>, + config: impl Into, + clocks: &Clocks, + ) -> Serial { + Serial::new(self, pins, config, clocks) + } +} use crate::pac::usart1 as uart_base; pub trait Instance: crate::Sealed + Deref + Enable + Reset + BusClock { + type Pins; + #[doc(hidden)] fn ptr() -> *const uart_base::RegisterBlock; } macro_rules! inst { - ($($USARTX:ident)+) => { + ($($USARTX:ident, $usart:ident;)+) => { $( impl Instance for $USARTX { + type Pins = $usart::Pins; + fn ptr() -> *const uart_base::RegisterBlock { <$USARTX>::ptr() as *const _ } @@ -128,17 +224,13 @@ macro_rules! inst { } inst! { - USART1 - USART2 - USART3 + USART1, usart1; + USART2, usart2; + USART3, usart3; } -/// Serial error kind -/// -/// This represents a common set of serial operation errors. HAL implementations are -/// free to define more specific or additional error types. However, by providing -/// a mapping to these common serial errors, generic code can still react to them. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +/// Serial error +#[derive(Debug)] #[non_exhaustive] pub enum Error { /// The peripheral receive buffer was overrun. @@ -252,10 +344,10 @@ impl From for Config { } /// Serial abstraction -pub struct Serial { +pub struct Serial { pub tx: Tx, pub rx: Rx, - pub token: ReleaseToken, + pub token: ReleaseToken>, } /// Serial transmitter @@ -274,7 +366,7 @@ pub struct ReleaseToken { pins: PINS, } -impl Serial { +impl Serial { /// Configures the serial interface and creates the interface /// struct. /// @@ -292,23 +384,19 @@ impl Serial { /// corresponding pins. `APBX` is used to reset the USART.) pub fn new( usart: USART, - pins: PINS, - mapr: &mut MAPR, + pins: impl Into>, config: impl Into, clocks: &Clocks, - ) -> Self - where - PINS: Pins, - { + ) -> Self { // Enable and reset USART let rcc = unsafe { &(*RCC::ptr()) }; USART::enable(rcc); USART::reset(rcc); - PINS::remap(mapr); - apply_config::(config.into(), clocks); + let pins = pins.into(); + // UE: enable USART // TE: enable transceiver // RE: enable receiver @@ -360,7 +448,7 @@ impl Serial { /// // Release `Serial` /// let (usart, (tx_pin, rx_pin)) = serial.release(); /// ``` - pub fn release(self) -> (USART, PINS) { + pub fn release(self) -> (USART, USART::Pins) { (self.token.usart, self.token.pins) } @@ -600,7 +688,7 @@ pub enum Event { Idle, } -impl Serial { +impl Serial { /// Starts listening to the USART by enabling the _Received data /// ready to be read (RXNE)_ interrupt and _Transmit data /// register empty (TXE)_ interrupt @@ -660,178 +748,187 @@ pub type Tx3 = Tx; use crate::dma::{Receive, TransferPayload, Transmit}; macro_rules! serialdma { - ($( + ( $USARTX:ident: ( $rxdma:ident, $txdma:ident, $dmarxch:ty, $dmatxch:ty, - ), - )+) => { - $( - pub type $rxdma = RxDma, $dmarxch>; - pub type $txdma = TxDma, $dmatxch>; + ) + ) => { + pub type $rxdma = RxDma, $dmarxch>; + pub type $txdma = TxDma, $dmatxch>; + + impl Receive for $rxdma { + type RxChannel = $dmarxch; + type TransmittedWord = u8; + } - impl Receive for $rxdma { - type RxChannel = $dmarxch; - type TransmittedWord = u8; - } + impl Transmit for $txdma { + type TxChannel = $dmatxch; + type ReceivedWord = u8; + } - impl Transmit for $txdma { - type TxChannel = $dmatxch; - type ReceivedWord = u8; + impl TransferPayload for $rxdma { + fn start(&mut self) { + self.channel.start(); + } + fn stop(&mut self) { + self.channel.stop(); } + } - impl TransferPayload for $rxdma { - fn start(&mut self) { - self.channel.start(); - } - fn stop(&mut self) { - self.channel.stop(); - } + impl TransferPayload for $txdma { + fn start(&mut self) { + self.channel.start(); } + fn stop(&mut self) { + self.channel.stop(); + } + } - impl TransferPayload for $txdma { - fn start(&mut self) { - self.channel.start(); + impl Rx<$USARTX> { + pub fn with_dma(self, channel: $dmarxch) -> $rxdma { + unsafe { + (*$USARTX::ptr()).cr3.modify(|_, w| w.dmar().set_bit()); } - fn stop(&mut self) { - self.channel.stop(); + RxDma { + payload: self, + channel, } } + } - impl Rx<$USARTX> { - pub fn with_dma(self, channel: $dmarxch) -> $rxdma { - unsafe { (*$USARTX::ptr()).cr3.modify(|_, w| w.dmar().set_bit()); } - RxDma { - payload: self, - channel, - } + impl Tx<$USARTX> { + pub fn with_dma(self, channel: $dmatxch) -> $txdma { + unsafe { + (*$USARTX::ptr()).cr3.modify(|_, w| w.dmat().set_bit()); } - } - - impl Tx<$USARTX> { - pub fn with_dma(self, channel: $dmatxch) -> $txdma { - unsafe { (*$USARTX::ptr()).cr3.modify(|_, w| w.dmat().set_bit()); } - TxDma { - payload: self, - channel, - } + TxDma { + payload: self, + channel, } } + } - impl $rxdma { - pub fn release(mut self) -> (Rx<$USARTX>, $dmarxch) { - self.stop(); - unsafe { (*$USARTX::ptr()).cr3.modify(|_, w| w.dmar().clear_bit()); } - let RxDma {payload, channel} = self; - ( - payload, - channel - ) + impl $rxdma { + pub fn release(mut self) -> (Rx<$USARTX>, $dmarxch) { + self.stop(); + unsafe { + (*$USARTX::ptr()).cr3.modify(|_, w| w.dmar().clear_bit()); } + let RxDma { payload, channel } = self; + (payload, channel) } + } - impl $txdma { - pub fn release(mut self) -> (Tx<$USARTX>, $dmatxch) { - self.stop(); - unsafe { (*$USARTX::ptr()).cr3.modify(|_, w| w.dmat().clear_bit()); } - let TxDma {payload, channel} = self; - ( - payload, - channel, - ) + impl $txdma { + pub fn release(mut self) -> (Tx<$USARTX>, $dmatxch) { + self.stop(); + unsafe { + (*$USARTX::ptr()).cr3.modify(|_, w| w.dmat().clear_bit()); } + let TxDma { payload, channel } = self; + (payload, channel) } + } - impl crate::dma::CircReadDma for $rxdma - where - &'static mut [B; 2]: WriteBuffer, - B: 'static, - { - fn circ_read(mut self, mut buffer: &'static mut [B; 2]) -> CircBuffer { - // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it - // until the end of the transfer. - let (ptr, len) = unsafe { buffer.write_buffer() }; - self.channel.set_peripheral_address(unsafe{ &(*$USARTX::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() .set_bit() - .dir() .clear_bit() - }); - - self.start(); - - CircBuffer::new(buffer, self) - } + impl crate::dma::CircReadDma for $rxdma + where + &'static mut [B; 2]: WriteBuffer, + B: 'static, + { + fn circ_read(mut self, mut buffer: &'static mut [B; 2]) -> CircBuffer { + // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it + // until the end of the transfer. + let (ptr, len) = unsafe { buffer.write_buffer() }; + self.channel.set_peripheral_address( + unsafe { &(*$USARTX::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(); + w.pl().medium(); + w.msize().bits8(); + w.psize().bits8(); + w.circ().set_bit(); + w.dir().clear_bit() + }); + + self.start(); + + CircBuffer::new(buffer, self) } + } - impl crate::dma::ReadDma for $rxdma - where - B: WriteBuffer, - { - fn read(mut self, mut buffer: B) -> Transfer { - // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it - // until the end of the transfer. - let (ptr, len) = unsafe { buffer.write_buffer() }; - self.channel.set_peripheral_address(unsafe{ &(*$USARTX::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() .clear_bit() - }); - self.start(); - - Transfer::w(buffer, self) - } + impl crate::dma::ReadDma for $rxdma + where + B: WriteBuffer, + { + fn read(mut self, mut buffer: B) -> Transfer { + // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it + // until the end of the transfer. + let (ptr, len) = unsafe { buffer.write_buffer() }; + self.channel.set_peripheral_address( + unsafe { &(*$USARTX::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(); + w.pl().medium(); + w.msize().bits8(); + w.psize().bits8(); + w.circ().clear_bit(); + w.dir().clear_bit() + }); + self.start(); + + Transfer::w(buffer, self) } + } - impl crate::dma::WriteDma for $txdma - where - B: ReadBuffer, - { - fn write(mut self, buffer: B) -> Transfer { - // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it - // until the end of the transfer. - let (ptr, len) = unsafe { buffer.read_buffer() }; - - self.channel.set_peripheral_address(unsafe{ &(*$USARTX::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) - } + impl crate::dma::WriteDma for $txdma + where + B: ReadBuffer, + { + fn write(mut self, buffer: B) -> Transfer { + // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it + // until the end of the transfer. + let (ptr, len) = unsafe { buffer.read_buffer() }; + + self.channel.set_peripheral_address( + unsafe { &(*$USARTX::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(); + w.pl().medium(); + w.msize().bits8(); + w.psize().bits8(); + w.circ().clear_bit(); + w.dir().set_bit() + }); + self.start(); + + Transfer::r(buffer, self) } - )+ - } + } + }; } serialdma! { @@ -840,17 +937,21 @@ serialdma! { TxDma1, dma1::C5, dma1::C4, - ), + ) +} +serialdma! { USART2: ( RxDma2, TxDma2, dma1::C6, dma1::C7, - ), + ) +} +serialdma! { USART3: ( RxDma3, TxDma3, dma1::C3, dma1::C2, - ), + ) } diff --git a/src/spi.rs b/src/spi.rs index 95fe72ae..3a1775f0 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -36,7 +36,7 @@ mod hal_02; mod hal_1; -use core::ops::Deref; +use core::ops::{Deref, DerefMut}; use core::ptr; use crate::pac::{self, RCC}; @@ -46,7 +46,7 @@ use crate::dma::dma1; #[cfg(feature = "connectivity")] use crate::dma::dma2; use crate::dma::{Receive, RxDma, RxTxDma, Transfer, TransferPayload, Transmit, TxDma, R, W}; -use crate::gpio::{self, Alternate, Input}; +use crate::gpio::{self, Alternate, Cr, Floating, Input, PinMode, PullUp, PushPull}; use crate::rcc::{BusClock, Clocks, Enable, Reset}; use crate::time::Hertz; @@ -104,263 +104,452 @@ pub enum Error { use core::marker::PhantomData; -/// Spi in Master mode (type state) -pub struct Master; -/// Spi in Slave mode (type state) -pub struct Slave; +pub trait InMode {} +impl InMode for Floating {} +impl InMode for PullUp {} -mod sealed { - pub trait Remap { - type Periph; - const REMAP: bool; +pub mod spi1 { + use super::*; + + remap! { + MasterPins, SlavePins: [ + All, Nomosi, Nomiso, PA5, PA6, PA7 => MAPR { |_, w| w.spi1_remap().bit(false) }; + Remap, RemapNomosi, RemapNomiso, PB3, PB4, PB5 => MAPR { |_, w| w.spi1_remap().bit(true) }; + ] } - pub trait Sck {} - pub trait Miso {} - pub trait Mosi {} - pub trait Ssck {} - pub trait So {} - pub trait Si {} } -pub use sealed::Remap; -use sealed::{Miso, Mosi, Sck, Si, So, Ssck}; -pub trait Pins {} +pub mod spi2 { + use super::*; -impl Pins for (SCK, MISO, MOSI) -where - SCK: Sck, - MISO: Miso, - MOSI: Mosi, -{ + remap! { + MasterPins, SlavePins: [ + All, Nomosi, Nomiso, PB13, PB14, PB15; + ] + } } - -impl Pins for (SCK, MISO, MOSI) -where - SCK: Ssck, - MISO: So, - MOSI: Si, -{ +#[cfg(any(feature = "high", feature = "connectivity"))] +pub mod spi3 { + use super::*; + + remap! { + MasterPins, SlavePins: [ + #[cfg(not(feature = "connectivity"))] + All, Nomosi, Nomiso, PB3, PB4, PB5; + #[cfg(feature = "connectivity")] + All, Nomosi, Nomiso, PB3, PB4, PB5 => MAPR { |_, w| w.spi3_remap().bit(false) }; + #[cfg(feature = "connectivity")] + Remap, RemapNomosi, RemapNomiso, PC10, PC11, PC12 => MAPR { |_, w| w.spi3_remap().bit(true) }; + ] + } } -pub struct Spi { - spi: SPI, - pins: PINS, - _remap: PhantomData, - _framesize: PhantomData, - _operation: PhantomData, -} +macro_rules! remap { + ($master:ident, $slave:ident: [ + $($(#[$attr:meta])* $rname:ident, $nomosi:ident, $nomiso:ident, $SCK:ident, $MISO:ident, $MOSI:ident $( => $MAPR:ident { $remapex:expr })?;)+ + ]) => { + pub enum $master { + $( + $(#[$attr])* + $rname { sck: gpio::$SCK, miso: gpio::$MISO>, mosi: gpio::$MOSI }, + $(#[$attr])* + $nomosi { sck: gpio::$SCK, miso: gpio::$MISO> }, + $(#[$attr])* + $nomiso { sck: gpio::$SCK, mosi: gpio::$MOSI }, + )+ + } -/// The bit format to send the data in -#[derive(Debug, Clone, Copy)] -pub enum SpiBitFormat { - /// Least significant bit first - LsbFirst, - /// Most significant bit first - MsbFirst, -} + pub enum $slave { + $( + $(#[$attr])* + $rname { sck: gpio::$SCK, miso: gpio::$MISO>, mosi: gpio::$MOSI> }, + $(#[$attr])* + $nomosi { sck: gpio::$SCK, miso: gpio::$MISO> }, + $(#[$attr])* + $nomiso { sck: gpio::$SCK, mosi: gpio::$MOSI> }, + )+ + } -/// A filler type for when the SCK pin is unnecessary -pub struct NoSck; -/// A filler type for when the Miso pin is unnecessary -pub struct NoMiso; -/// A filler type for when the Mosi pin is unnecessary -pub struct NoMosi; + $( + // For master mode -impl Sck for NoSck {} -impl Miso for NoMiso {} -impl Mosi for NoMosi {} -impl So for NoMiso {} -impl Si for NoMosi {} + $(#[$attr])* + impl From<(gpio::$SCK, gpio::$MISO>, gpio::$MOSI $(, &mut $MAPR)?)> for $master { + fn from(p: (gpio::$SCK, gpio::$MISO>, gpio::$MOSI $(, &mut $MAPR)?)) -> Self { + $(p.3.modify_mapr($remapex);)? + Self::$rname { sck: p.0, miso: p.1, mosi: p.2 } + } + } -macro_rules! remap { - ($name:ident, $SPIX:ty, $state:literal, $SCK:ident, $MISO:ident, $MOSI:ident) => { - pub struct $name; - impl Remap for $name { - type Periph = $SPIX; - const REMAP: bool = $state; - } - // Master mode pins - impl Sck<$name> for gpio::$SCK> {} - impl Miso<$name> for gpio::$MISO> {} - impl Mosi<$name> for gpio::$MOSI> {} - // Slave mode pins - impl Ssck<$name> for gpio::$SCK> {} - impl So<$name> for gpio::$MISO> {} - impl Si<$name> for gpio::$MOSI> {} - }; -} + $(#[$attr])* + impl From<(gpio::$SCK, gpio::$MISO, gpio::$MOSI $(, &mut $MAPR)?)> for $master + where + Input: PinMode, + INMODE: InMode, + { + fn from(p: (gpio::$SCK, gpio::$MISO, gpio::$MOSI $(, &mut $MAPR)?)) -> Self { + let mut cr = Cr; + let sck = p.0.into_mode(&mut cr); + let miso = p.1.into_mode(&mut cr); + let mosi = p.2.into_mode(&mut cr); + $(p.3.modify_mapr($remapex);)? + Self::$rname { sck, miso, mosi } + } + } -remap!(Spi1NoRemap, pac::SPI1, false, PA5, PA6, PA7); -remap!(Spi1Remap, pac::SPI1, true, PB3, PB4, PB5); -remap!(Spi2NoRemap, pac::SPI2, false, PB13, PB14, PB15); -#[cfg(any(feature = "high", feature = "connectivity"))] -remap!(Spi3NoRemap, pac::SPI3, false, PB3, PB4, PB5); -#[cfg(feature = "connectivity")] -remap!(Spi3Remap, pac::SPI3, true, PC10, PC11, PC12); + $(#[$attr])* + impl From<(gpio::$SCK, gpio::$MISO> $(, &mut $MAPR)?)> for $master { + fn from(p: (gpio::$SCK, gpio::$MISO> $(, &mut $MAPR)?)) -> Self { + $(p.2.modify_mapr($remapex);)? + Self::$nomosi { sck: p.0, miso: p.1 } + } + } -pub trait Instance: - crate::Sealed + Deref + Enable + Reset + BusClock -{ -} + $(#[$attr])* + impl From<(gpio::$SCK, gpio::$MISO $(, &mut $MAPR)?)> for $master + where + Input: PinMode, + INMODE: InMode, + { + fn from(p: (gpio::$SCK, gpio::$MISO $(, &mut $MAPR)?)) -> Self { + let mut cr = Cr; + let sck = p.0.into_mode(&mut cr); + let miso = p.1.into_mode(&mut cr); + $(p.2.modify_mapr($remapex);)? + Self::$nomosi { sck, miso } + } + } -impl Instance for pac::SPI1 {} -impl Instance for pac::SPI2 {} -#[cfg(any(feature = "high", feature = "connectivity"))] -impl Instance for pac::SPI3 {} + $(#[$attr])* + impl From<(gpio::$SCK, gpio::$MOSI $(, &mut $MAPR)?)> for $master { + fn from(p: (gpio::$SCK, gpio::$MOSI $(, &mut $MAPR)?)) -> Self { + let mut cr = Cr; + let sck = p.0.into_mode(&mut cr); + let mosi = p.1.into_mode(&mut cr); + $(p.2.modify_mapr($remapex);)? + Self::$nomiso { sck, mosi } + } + } -impl Spi { - /** - Constructs an SPI instance using SPI1 in 8bit dataframe mode. + // For slave mode - The pin parameter tuple (sck, miso, mosi) should be `(PA5, PA6, PA7)` or `(PB3, PB4, PB5)` configured as `(Alternate<...>, Input<...>, Alternate<...>)`. + $(#[$attr])* + impl From<(gpio::$SCK, gpio::$MISO>, gpio::$MOSI> $(, &mut $MAPR)?)> for $slave { + fn from(p: (gpio::$SCK, gpio::$MISO>, gpio::$MOSI> $(, &mut $MAPR)?)) -> Self { + $(p.3.modify_mapr($remapex);)? + Self::$rname { sck: p.0, miso: p.1, mosi: p.2 } + } + } - You can also use `NoSck`, `NoMiso` or `NoMosi` if you don't want to use the pins - */ - pub fn spi1( - spi: pac::SPI1, - pins: PINS, - mapr: &mut MAPR, - mode: impl Into, + $(#[$attr])* + impl From<(gpio::$SCK, gpio::$MISO, gpio::$MOSI $(, &mut $MAPR)?)> for $slave + where + Alternate: PinMode, + Input: PinMode, + INMODE: InMode, + { + fn from(p: (gpio::$SCK, gpio::$MISO, gpio::$MOSI $(, &mut $MAPR)?)) -> Self { + let mut cr = Cr; + let sck = p.0.into_mode(&mut cr); + let miso = p.1.into_mode(&mut cr); + let mosi = p.2.into_mode(&mut cr); + $(p.3.modify_mapr($remapex);)? + Self::$rname { sck, miso, mosi } + } + } + + $(#[$attr])* + impl From<(gpio::$SCK, gpio::$MISO $(, &mut $MAPR)?)> for $slave + where + Alternate: PinMode, + { + fn from(p: (gpio::$SCK, gpio::$MISO $(, &mut $MAPR)?)) -> Self { + let mut cr = Cr; + let sck = p.0.into_mode(&mut cr); + let miso = p.1.into_mode(&mut cr); + $(p.2.modify_mapr($remapex);)? + Self::$nomosi { sck, miso } + } + } + + $(#[$attr])* + impl From<(gpio::$SCK, gpio::$MOSI $(, &mut $MAPR)?)> for $slave + where + Input: PinMode, + INMODE: InMode, + { + fn from(p: (gpio::$SCK, gpio::$MOSI $(, &mut $MAPR)?)) -> Self { + let mut cr = Cr; + let sck = p.0.into_mode(&mut cr); + let mosi = p.1.into_mode(&mut cr); + $(p.2.modify_mapr($remapex);)? + Self::$nomiso { sck, mosi } + } + } + )+ + } +} +use remap; + +pub trait SpiExt: Sized + Instance { + fn spi( + self, + pins: impl Into>, + mode: Mode, + freq: Hertz, + clocks: &Clocks, + ) -> Spi; + fn spi_u16( + self, + pins: impl Into>, + mode: Mode, freq: Hertz, clocks: &Clocks, - ) -> Self - where - REMAP: Remap, - PINS: Pins, - { - mapr.modify_mapr(|_, w| w.spi1_remap().bit(REMAP::REMAP)); - Spi::::configure(spi, pins, mode, freq, clocks) + ) -> Spi { + Self::spi(self, pins, mode, freq, clocks).frame_size_16bit() + } + fn spi_slave( + self, + pins: impl Into>, + mode: Mode, + ) -> SpiSlave; + fn spi_slave_u16( + self, + pins: impl Into>, + mode: Mode, + ) -> SpiSlave { + Self::spi_slave(self, pins, mode).frame_size_16bit() } } -impl Spi { - /** - Constructs an SPI instance using SPI1 in 8bit dataframe mode. +impl SpiExt for SPI { + fn spi( + self, + pins: impl Into>, + mode: Mode, + freq: Hertz, + clocks: &Clocks, + ) -> Spi { + Spi::new(self, pins, mode, freq, clocks) + } + fn spi_slave( + self, + pins: impl Into>, + mode: Mode, + ) -> SpiSlave { + SpiSlave::new(self, pins, mode) + } +} - The pin parameter tuple (sck, miso, mosi) should be `(PA5, PA6, PA7)` or `(PB3, PB4, PB5)` configured as `(Input<...>, Alternate<...>, Input<...>)`. +pub struct SpiInner { + spi: SPI, + _framesize: PhantomData, +} - You can also use `NoMiso` or `NoMosi` if you don't want to use the pins - */ - pub fn spi1_slave(spi: pac::SPI1, pins: PINS, mapr: &mut MAPR, mode: impl Into) -> Self - where - REMAP: Remap, - PINS: Pins, - { - mapr.modify_mapr(|_, w| w.spi1_remap().bit(REMAP::REMAP)); - Spi::::configure(spi, pins, mode) +impl SpiInner { + fn new(spi: SPI) -> Self { + Self { + spi, + _framesize: PhantomData, + } } } -impl Spi { - /** - Constructs an SPI instance using SPI2 in 8bit dataframe mode. +/// Spi in Master mode +pub struct Spi { + inner: SpiInner, + pins: SPI::MasterPins, +} - The pin parameter tuple (sck, miso, mosi) should be `(PB13, PB14, PB15)` configured as `(Alternate<...>, Input<...>, Alternate<...>)`. +/// Spi in Slave mode +pub struct SpiSlave { + inner: SpiInner, + pins: SPI::SlavePins, +} - You can also use `NoSck`, `NoMiso` or `NoMosi` if you don't want to use the pins - */ - pub fn spi2(spi: pac::SPI2, pins: PINS, mode: Mode, freq: Hertz, clocks: &Clocks) -> Self - where - REMAP: Remap, - PINS: Pins, - { - Spi::::configure(spi, pins, mode, freq, clocks) +impl Deref for Spi { + type Target = SpiInner; + fn deref(&self) -> &Self::Target { + &self.inner } } -impl Spi { - /** - Constructs an SPI instance using SPI2 in 8bit dataframe mode. +impl DerefMut for Spi { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} - The pin parameter tuple (sck, miso, mosi) should be `(PB13, PB14, PB15)` configured as `(Input<...>, Alternate<...>, Input<...>)`. +impl Deref + for SpiSlave +{ + type Target = SpiInner; + fn deref(&self) -> &Self::Target { + &self.inner + } +} - You can also use `NoMiso` or `NoMosi` if you don't want to use the pins - */ - pub fn spi2_slave(spi: pac::SPI2, pins: PINS, mode: Mode) -> Self - where - REMAP: Remap, - PINS: Pins, - { - Spi::::configure(spi, pins, mode) +impl DerefMut + for SpiSlave +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner } } -#[cfg(any(feature = "high", feature = "connectivity"))] -impl Spi { - /** - Constructs an SPI instance using SPI3 in 8bit dataframe mode. +/// The bit format to send the data in +#[derive(Debug, Clone, Copy)] +pub enum SpiBitFormat { + /// Least significant bit first + LsbFirst, + /// Most significant bit first + MsbFirst, +} - The pin parameter tuple (sck, miso, mosi) should be `(PB3, PB4, PB5)` configured as `(Alternate<...>, Input<...>, Alternate<...>)`. +pub trait Instance: + crate::Sealed + Deref + Enable + Reset + BusClock +{ + type MasterPins; + type SlavePins; +} - You can also use `NoSck`, `NoMiso` or `NoMosi` if you don't want to use the pins - */ - #[cfg(not(feature = "connectivity"))] - pub fn spi3(spi: pac::SPI3, pins: PINS, mode: Mode, freq: Hertz, clocks: &Clocks) -> Self - where - REMAP: Remap, - PINS: Pins, - { - Spi::::configure(spi, pins, mode, freq, clocks) - } +impl Instance for pac::SPI1 { + type MasterPins = spi1::MasterPins; + type SlavePins = spi1::SlavePins; +} +impl Instance for pac::SPI2 { + type MasterPins = spi2::MasterPins; + type SlavePins = spi2::SlavePins; +} +#[cfg(any(feature = "high", feature = "connectivity"))] +impl Instance for pac::SPI3 { + type MasterPins = spi3::MasterPins; + type SlavePins = spi3::SlavePins; +} +impl Spi { /** - Constructs an SPI instance using SPI3 in 8bit dataframe mode. + Constructs an SPI instance using SPI1 in 8bit dataframe mode. - The pin parameter tuple (sck, miso, mosi) should be `(PB3, PB4, PB5)` or `(PC10, PC11, PC12)` configured as `(Alternate<...>, Input<...>, Alternate<...>)`. + The pin parameter tuple (sck, miso, mosi) should be `(PA5, PA6, PA7)` or `(PB3, PB4, PB5)` configured as `(Alternate, Input<...>, Alternate)`. You can also use `NoSck`, `NoMiso` or `NoMosi` if you don't want to use the pins */ - #[cfg(feature = "connectivity")] - pub fn spi3( - spi: pac::SPI3, - pins: PINS, - mapr: &mut MAPR, + pub fn new( + spi: SPI, + pins: impl Into>, mode: Mode, freq: Hertz, clocks: &Clocks, - ) -> Self - where - REMAP: Remap, - PINS: Pins, - { - mapr.modify_mapr(|_, w| w.spi3_remap().bit(REMAP::REMAP)); - Spi::::configure(spi, pins, mode, freq, clocks) + ) -> Self { + // enable or reset SPI + let rcc = unsafe { &(*RCC::ptr()) }; + SPI::enable(rcc); + SPI::reset(rcc); + + // disable SS output + spi.cr2.write(|w| w.ssoe().clear_bit()); + + let br = match SPI::clock(clocks) / 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, + }; + + let pins = pins.into(); + + spi.cr1.write(|w| { + // clock phase from config + w.cpha().bit(mode.phase == Phase::CaptureOnSecondTransition); + // clock polarity from config + w.cpol().bit(mode.polarity == Polarity::IdleHigh); + // mstr: master configuration + w.mstr().set_bit(); + // baudrate value + w.br().bits(br); + // lsbfirst: MSB first + w.lsbfirst().clear_bit(); + // ssm: enable software slave management (NSS pin free for other uses) + w.ssm().set_bit(); + // ssi: set nss high = master mode + w.ssi().set_bit(); + // dff: 8 bit frames + w.dff().clear_bit(); + // bidimode: 2-line unidirectional + w.bidimode().clear_bit(); + // both TX and RX are used + w.rxonly().clear_bit(); + // spe: enable the SPI bus + w.spe().set_bit() + }); + + Spi { + inner: SpiInner::new(spi), + pins, + } + } + + pub fn release(self) -> (SPI, SPI::MasterPins) { + (self.inner.spi, self.pins) } } -#[cfg(any(feature = "high", feature = "connectivity"))] -impl Spi { +impl SpiSlave { /** - Constructs an SPI instance using SPI3 in 8bit dataframe mode. + Constructs an SPI instance using SPI1 in 8bit dataframe mode. - The pin parameter tuple (sck, miso, mosi) should be `(PB3, PB4, PB5)` configured as `(Input<...>, Alternate<...>, Input<...>)`. + The pin parameter tuple (sck, miso, mosi) should be `(PA5, PA6, PA7)` or `(PB3, PB4, PB5)` configured as `(Input, Alternate<...>, Input<...>)`. You can also use `NoMiso` or `NoMosi` if you don't want to use the pins */ - #[cfg(not(feature = "connectivity"))] - pub fn spi3_slave(spi: pac::SPI3, pins: PINS, mode: Mode) -> Self - where - REMAP: Remap, - PINS: Pins, - { - Spi::::configure(spi, pins, mode) - } + pub fn new(spi: SPI, pins: impl Into>, mode: Mode) -> Self { + // enable or reset SPI + let rcc = unsafe { &(*RCC::ptr()) }; + SPI::enable(rcc); + SPI::reset(rcc); - /** - Constructs an SPI instance using SPI3 in 8bit dataframe mode. + // disable SS output + spi.cr2.write(|w| w.ssoe().clear_bit()); - The pin parameter tuple (sck, miso, mosi) should be `(PB3, PB4, PB5)` or `(PC10, PC11, PC12)` configured as `(Input<...>, Alternate<...>, Input<...>)`. + let pins = pins.into(); - You can also use `NoMiso` or `NoMosi` if you don't want to use the pins - */ - #[cfg(feature = "connectivity")] - pub fn spi3_slave(spi: pac::SPI3, pins: PINS, mapr: &mut MAPR, mode: Mode) -> Self - where - REMAP: Remap, - PINS: Pins, - { - mapr.modify_mapr(|_, w| w.spi3_remap().bit(REMAP::REMAP)); - Spi::::configure(spi, pins, mode) + spi.cr1.write(|w| { + // clock phase from config + w.cpha().bit(mode.phase == Phase::CaptureOnSecondTransition); + // clock polarity from config + w.cpol().bit(mode.polarity == Polarity::IdleHigh); + // mstr: slave configuration + w.mstr().clear_bit(); + // lsbfirst: MSB first + w.lsbfirst().clear_bit(); + // ssm: enable software slave management (NSS pin free for other uses) + w.ssm().set_bit(); + // ssi: set nss low = slave mode + w.ssi().clear_bit(); + // dff: 8 bit frames + w.dff().clear_bit(); + // bidimode: 2-line unidirectional + w.bidimode().clear_bit(); + // both TX and RX are used + w.rxonly().clear_bit(); + // spe: enable the SPI bus + w.spe().set_bit() + }); + + SpiSlave { + inner: SpiInner::new(spi), + pins, + } + } + + pub fn release(self) -> (SPI, SPI::SlavePins) { + (self.inner.spi, self.pins) } } @@ -370,12 +559,7 @@ pub trait SpiReadWrite { fn spi_write(&mut self, words: &[T]) -> Result<(), Error>; } -impl SpiReadWrite - for Spi -where - SPI: Instance, - FrameSize: Copy, -{ +impl SpiReadWrite for SpiInner { fn read_data_reg(&mut self) -> FrameSize { // NOTE(read_volatile) read only 1 byte (the svd2rust API only allows // reading a half-word) @@ -416,15 +600,7 @@ where } } -impl Spi -where - SPI: Instance, - FrameSize: Copy, -{ - pub fn release(self) -> (SPI, PINS) { - (self.spi, self.pins) - } - +impl SpiInner { /// Select which frame format is used for data transfers pub fn bit_format(&mut self, format: SpiBitFormat) { match format { @@ -480,159 +656,59 @@ where } } -impl Spi -where - SPI: Instance, -{ - fn configure( - spi: SPI, - pins: PINS, - mode: impl Into, - freq: Hertz, - clocks: &Clocks, - ) -> Self { - let mode = mode.into(); - // enable or reset SPI - let rcc = unsafe { &(*RCC::ptr()) }; - SPI::enable(rcc); - SPI::reset(rcc); +impl Spi { + /// Converts from 8bit dataframe to 16bit. + pub fn frame_size_16bit(self) -> Spi { + 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 { + inner: SpiInner::new(self.inner.spi), + pins: self.pins, + } + } +} - // disable SS output - spi.cr2.write(|w| w.ssoe().clear_bit()); +impl SpiSlave { + /// Converts from 8bit dataframe to 16bit. + pub fn frame_size_16bit(self) -> SpiSlave { + 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()); + SpiSlave { + inner: SpiInner::new(self.inner.spi), + pins: self.pins, + } + } +} - let br = match SPI::clock(&clocks) / 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, - }; - - spi.cr1.write(|w| { - // clock phase from config - w.cpha().bit(mode.phase == Phase::CaptureOnSecondTransition); - // clock polarity from config - w.cpol().bit(mode.polarity == Polarity::IdleHigh); - // mstr: master configuration - w.mstr().set_bit(); - // baudrate value - w.br().bits(br); - // lsbfirst: MSB first - w.lsbfirst().clear_bit(); - // ssm: enable software slave management (NSS pin free for other uses) - w.ssm().set_bit(); - // ssi: set nss high = master mode - w.ssi().set_bit(); - // dff: 8 bit frames - w.dff().clear_bit(); - // bidimode: 2-line unidirectional - w.bidimode().clear_bit(); - // both TX and RX are used - w.rxonly().clear_bit(); - // spe: enable the SPI bus - w.spe().set_bit() - }); - - Spi { - spi, - pins, - _remap: PhantomData, - _framesize: PhantomData, - _operation: PhantomData, - } - } -} - -impl Spi -where - SPI: Instance, -{ - fn configure(spi: SPI, pins: PINS, mode: impl Into) -> Self { - let mode = mode.into(); - // enable or reset SPI - let rcc = unsafe { &(*RCC::ptr()) }; - SPI::enable(rcc); - SPI::reset(rcc); - - // disable SS output - spi.cr2.write(|w| w.ssoe().clear_bit()); - - spi.cr1.write(|w| { - // clock phase from config - w.cpha().bit(mode.phase == Phase::CaptureOnSecondTransition); - // clock polarity from config - w.cpol().bit(mode.polarity == Polarity::IdleHigh); - // mstr: slave configuration - w.mstr().clear_bit(); - // lsbfirst: MSB first - w.lsbfirst().clear_bit(); - // ssm: enable software slave management (NSS pin free for other uses) - w.ssm().set_bit(); - // ssi: set nss low = slave mode - w.ssi().clear_bit(); - // dff: 8 bit frames - w.dff().clear_bit(); - // bidimode: 2-line unidirectional - w.bidimode().clear_bit(); - // both TX and RX are used - w.rxonly().clear_bit(); - // spe: enable the SPI bus - w.spe().set_bit() - }); - - Spi { - spi, - pins, - _remap: PhantomData, - _framesize: PhantomData, - _operation: PhantomData, - } - } -} - -impl Spi -where - SPI: Instance, -{ - /// Converts from 8bit dataframe to 16bit. - pub fn frame_size_16bit(self) -> Spi { +impl Spi { + /// Converts from 16bit dataframe to 8bit. + pub fn frame_size_8bit(self) -> Spi { self.spi.cr1.modify(|_, w| w.spe().clear_bit()); - self.spi.cr1.modify(|_, w| w.dff().set_bit()); + self.spi.cr1.modify(|_, w| w.dff().clear_bit()); self.spi.cr1.modify(|_, w| w.spe().set_bit()); Spi { - spi: self.spi, + inner: SpiInner::new(self.inner.spi), pins: self.pins, - _remap: PhantomData, - _framesize: PhantomData, - _operation: PhantomData, } } } -impl Spi -where - SPI: Instance, -{ +impl SpiSlave { /// Converts from 16bit dataframe to 8bit. - pub fn frame_size_8bit(self) -> Spi { + pub fn frame_size_8bit(self) -> SpiSlave { 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, + SpiSlave { + inner: SpiInner::new(self.inner.spi), pins: self.pins, - _remap: PhantomData, - _framesize: PhantomData, - _operation: PhantomData, } } } -impl Spi +impl SpiInner where SPI: Instance, FrameSize: Copy, @@ -677,46 +753,53 @@ where // DMA -pub type SpiTxDma = TxDma, CHANNEL>; -pub type SpiRxDma = RxDma, CHANNEL>; -pub type SpiRxTxDma = - RxTxDma, RXCHANNEL, TXCHANNEL>; +pub type SpiTxDma = TxDma, CHANNEL>; +pub type SpiRxDma = RxDma, CHANNEL>; +pub type SpiRxTxDma = + RxTxDma, RXCHANNEL, TXCHANNEL>; + +pub type SpiSlaveTxDma = + TxDma, CHANNEL>; +pub type SpiSlaveRxDma = + RxDma, CHANNEL>; +pub type SpiSlaveRxTxDma = + RxTxDma, RXCHANNEL, TXCHANNEL>; macro_rules! spi_dma { - ($SPIi:ty, $RCi:ty, $TCi:ty, $rxdma:ident, $txdma:ident, $rxtxdma:ident) => { - pub type $rxdma = SpiRxDma<$SPIi, REMAP, PINS, OP, $RCi>; - pub type $txdma = SpiTxDma<$SPIi, REMAP, PINS, OP, $TCi>; - pub type $rxtxdma = SpiRxTxDma<$SPIi, REMAP, PINS, OP, $RCi, $TCi>; + ($SPIi:ty, $RCi:ty, $TCi:ty, $rxdma:ident, $txdma:ident, $rxtxdma:ident, $slaverxdma:ident, $slavetxdma:ident, $slaverxtxdma:ident) => { + pub type $rxdma = SpiRxDma<$SPIi, $RCi, INMODE>; + pub type $txdma = SpiTxDma<$SPIi, $TCi, INMODE>; + pub type $rxtxdma = SpiRxTxDma<$SPIi, $RCi, $TCi, INMODE>; - impl Transmit for SpiTxDma<$SPIi, REMAP, PINS, OP, $TCi> { + impl Transmit for SpiTxDma<$SPIi, $TCi, INMODE> { type TxChannel = $TCi; type ReceivedWord = u8; } - impl Receive for SpiRxDma<$SPIi, REMAP, PINS, OP, $RCi> { + impl Receive for SpiRxDma<$SPIi, $RCi, INMODE> { type RxChannel = $RCi; type TransmittedWord = u8; } - impl Transmit for SpiRxTxDma<$SPIi, REMAP, PINS, OP, $RCi, $TCi> { + impl Transmit for SpiRxTxDma<$SPIi, $RCi, $TCi, INMODE> { type TxChannel = $TCi; type ReceivedWord = u8; } - impl Receive for SpiRxTxDma<$SPIi, REMAP, PINS, OP, $RCi, $TCi> { + impl Receive for SpiRxTxDma<$SPIi, $RCi, $TCi, INMODE> { type RxChannel = $RCi; type TransmittedWord = u8; } - impl Spi<$SPIi, REMAP, PINS, u8, OP> { - pub fn with_tx_dma(self, channel: $TCi) -> SpiTxDma<$SPIi, REMAP, PINS, OP, $TCi> { + impl Spi<$SPIi, u8, INMODE> { + pub fn with_tx_dma(self, channel: $TCi) -> SpiTxDma<$SPIi, $TCi, INMODE> { self.spi.cr2.modify(|_, w| w.txdmaen().set_bit()); SpiTxDma { payload: self, channel, } } - pub fn with_rx_dma(self, channel: $RCi) -> SpiRxDma<$SPIi, REMAP, PINS, OP, $RCi> { + pub fn with_rx_dma(self, channel: $RCi) -> SpiRxDma<$SPIi, $RCi, INMODE> { self.spi.cr2.modify(|_, w| w.rxdmaen().set_bit()); SpiRxDma { payload: self, @@ -727,7 +810,7 @@ macro_rules! spi_dma { self, rxchannel: $RCi, txchannel: $TCi, - ) -> SpiRxTxDma<$SPIi, REMAP, PINS, OP, $RCi, $TCi> { + ) -> SpiRxTxDma<$SPIi, $RCi, $TCi, INMODE> { self.spi .cr2 .modify(|_, w| w.rxdmaen().set_bit().txdmaen().set_bit()); @@ -739,24 +822,24 @@ macro_rules! spi_dma { } } - impl SpiTxDma<$SPIi, REMAP, PINS, OP, $TCi> { - pub fn release(self) -> (Spi<$SPIi, REMAP, PINS, u8, OP>, $TCi) { + impl SpiTxDma<$SPIi, $TCi, INMODE> { + pub fn release(self) -> (Spi<$SPIi, u8, INMODE>, $TCi) { let SpiTxDma { payload, channel } = self; payload.spi.cr2.modify(|_, w| w.txdmaen().clear_bit()); (payload, channel) } } - impl SpiRxDma<$SPIi, REMAP, PINS, OP, $RCi> { - pub fn release(self) -> (Spi<$SPIi, REMAP, PINS, u8, OP>, $RCi) { + impl SpiRxDma<$SPIi, $RCi, INMODE> { + pub fn release(self) -> (Spi<$SPIi, u8, INMODE>, $RCi) { let SpiRxDma { payload, channel } = self; payload.spi.cr2.modify(|_, w| w.rxdmaen().clear_bit()); (payload, channel) } } - impl SpiRxTxDma<$SPIi, REMAP, PINS, OP, $RCi, $TCi> { - pub fn release(self) -> (Spi<$SPIi, REMAP, PINS, u8, OP>, $RCi, $TCi) { + impl SpiRxTxDma<$SPIi, $RCi, $TCi, INMODE> { + pub fn release(self) -> (Spi<$SPIi, u8, INMODE>, $RCi, $TCi) { let SpiRxTxDma { payload, rxchannel, @@ -770,7 +853,266 @@ macro_rules! spi_dma { } } - impl TransferPayload for SpiTxDma<$SPIi, REMAP, PINS, OP, $TCi> { + impl TransferPayload for SpiTxDma<$SPIi, $TCi, INMODE> { + fn start(&mut self) { + self.channel.start(); + } + fn stop(&mut self) { + self.channel.stop(); + } + } + + impl TransferPayload for SpiRxDma<$SPIi, $RCi, INMODE> { + fn start(&mut self) { + self.channel.start(); + } + fn stop(&mut self) { + self.channel.stop(); + } + } + + impl TransferPayload for SpiRxTxDma<$SPIi, $RCi, $TCi, INMODE> { + fn start(&mut self) { + self.rxchannel.start(); + self.txchannel.start(); + } + fn stop(&mut self) { + self.txchannel.stop(); + self.rxchannel.stop(); + } + } + + impl crate::dma::ReadDma for SpiRxDma<$SPIi, $RCi, INMODE> + where + B: WriteBuffer, + { + fn read(mut self, mut buffer: B) -> Transfer { + // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it + // until the end of the transfer. + let (ptr, len) = unsafe { buffer.write_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| { + // memory to memory mode disabled + w.mem2mem().clear_bit(); + // medium channel priority level + w.pl().medium(); + // 8-bit memory size + w.msize().bits8(); + // 8-bit peripheral size + w.psize().bits8(); + // circular mode disabled + w.circ().clear_bit(); + // write to memory + w.dir().clear_bit() + }); + self.start(); + + Transfer::w(buffer, self) + } + } + + impl crate::dma::WriteDma for SpiTxDma<$SPIi, $TCi, INMODE> + where + B: ReadBuffer, + { + fn write(mut self, buffer: B) -> Transfer { + // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it + // until the end of the transfer. + let (ptr, len) = unsafe { buffer.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| { + // memory to memory mode disabled + w.mem2mem().clear_bit(); + // medium channel priority level + w.pl().medium(); + // 8-bit memory size + w.msize().bits8(); + // 8-bit peripheral size + w.psize().bits8(); + // circular mode disabled + w.circ().clear_bit(); + // read from memory + w.dir().set_bit() + }); + self.start(); + + Transfer::r(buffer, self) + } + } + + impl crate::dma::ReadWriteDma + for SpiRxTxDma<$SPIi, $RCi, $TCi, INMODE> + where + RXB: WriteBuffer, + TXB: ReadBuffer, + { + fn read_write( + mut self, + mut rxbuffer: RXB, + txbuffer: TXB, + ) -> Transfer { + // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it + // until the end of the transfer. + let (rxptr, rxlen) = unsafe { rxbuffer.write_buffer() }; + let (txptr, txlen) = unsafe { txbuffer.read_buffer() }; + + if rxlen != txlen { + panic!("receive and send buffer lengths do not match!"); + } + + self.rxchannel.set_peripheral_address( + unsafe { &(*<$SPIi>::ptr()).dr as *const _ as u32 }, + false, + ); + self.rxchannel.set_memory_address(rxptr as u32, true); + self.rxchannel.set_transfer_length(rxlen); + + self.txchannel.set_peripheral_address( + unsafe { &(*<$SPIi>::ptr()).dr as *const _ as u32 }, + false, + ); + self.txchannel.set_memory_address(txptr as u32, true); + self.txchannel.set_transfer_length(txlen); + + atomic::compiler_fence(Ordering::Release); + self.rxchannel.ch().cr.modify(|_, w| { + // memory to memory mode disabled + w.mem2mem().clear_bit(); + // medium channel priority level + w.pl().medium(); + // 8-bit memory size + w.msize().bits8(); + // 8-bit peripheral size + w.psize().bits8(); + // circular mode disabled + w.circ().clear_bit(); + // write to memory + w.dir().clear_bit() + }); + self.txchannel.ch().cr.modify(|_, w| { + // memory to memory mode disabled + w.mem2mem().clear_bit(); + // medium channel priority level + w.pl().medium(); + // 8-bit memory size + w.msize().bits8(); + // 8-bit peripheral size + w.psize().bits8(); + // circular mode disabled + w.circ().clear_bit(); + // read from memory + w.dir().set_bit() + }); + self.start(); + + Transfer::w((rxbuffer, txbuffer), self) + } + } + + pub type $slaverxdma = + SpiSlaveRxDma<$SPIi, $RCi, OUTMODE, INMODE>; + pub type $slavetxdma = + SpiSlaveTxDma<$SPIi, $TCi, OUTMODE, INMODE>; + pub type $slaverxtxdma = + SpiSlaveRxTxDma<$SPIi, $RCi, $TCi, OUTMODE, INMODE>; + + impl Transmit for SpiSlaveTxDma<$SPIi, $TCi, OUTMODE, INMODE> { + type TxChannel = $TCi; + type ReceivedWord = u8; + } + + impl Receive for SpiSlaveRxDma<$SPIi, $RCi, OUTMODE, INMODE> { + type RxChannel = $RCi; + type TransmittedWord = u8; + } + + impl Transmit for SpiSlaveRxTxDma<$SPIi, $RCi, $TCi, OUTMODE, INMODE> { + type TxChannel = $TCi; + type ReceivedWord = u8; + } + + impl Receive for SpiSlaveRxTxDma<$SPIi, $RCi, $TCi, OUTMODE, INMODE> { + type RxChannel = $RCi; + type TransmittedWord = u8; + } + + impl SpiSlave<$SPIi, u8, OUTMODE, INMODE> { + pub fn with_tx_dma(self, channel: $TCi) -> SpiSlaveTxDma<$SPIi, $TCi, OUTMODE, INMODE> { + self.spi.cr2.modify(|_, w| w.txdmaen().set_bit()); + SpiSlaveTxDma { + payload: self, + channel, + } + } + pub fn with_rx_dma(self, channel: $RCi) -> SpiSlaveRxDma<$SPIi, $RCi, OUTMODE, INMODE> { + self.spi.cr2.modify(|_, w| w.rxdmaen().set_bit()); + SpiSlaveRxDma { + payload: self, + channel, + } + } + pub fn with_rx_tx_dma( + self, + rxchannel: $RCi, + txchannel: $TCi, + ) -> SpiSlaveRxTxDma<$SPIi, $RCi, $TCi, OUTMODE, INMODE> { + self.spi + .cr2 + .modify(|_, w| w.rxdmaen().set_bit().txdmaen().set_bit()); + SpiSlaveRxTxDma { + payload: self, + rxchannel, + txchannel, + } + } + } + + impl SpiSlaveTxDma<$SPIi, $TCi, OUTMODE, INMODE> { + pub fn release(self) -> (SpiSlave<$SPIi, u8, OUTMODE, INMODE>, $TCi) { + let SpiSlaveTxDma { payload, channel } = self; + payload.spi.cr2.modify(|_, w| w.txdmaen().clear_bit()); + (payload, channel) + } + } + + impl SpiSlaveRxDma<$SPIi, $RCi, OUTMODE, INMODE> { + pub fn release(self) -> (SpiSlave<$SPIi, u8, OUTMODE, INMODE>, $RCi) { + let SpiSlaveRxDma { payload, channel } = self; + payload.spi.cr2.modify(|_, w| w.rxdmaen().clear_bit()); + (payload, channel) + } + } + + impl SpiSlaveRxTxDma<$SPIi, $RCi, $TCi, OUTMODE, INMODE> { + pub fn release(self) -> (SpiSlave<$SPIi, u8, OUTMODE, INMODE>, $RCi, $TCi) { + let SpiSlaveRxTxDma { + payload, + rxchannel, + txchannel, + } = self; + payload + .spi + .cr2 + .modify(|_, w| w.rxdmaen().clear_bit().txdmaen().clear_bit()); + (payload, rxchannel, txchannel) + } + } + + impl TransferPayload for SpiSlaveTxDma<$SPIi, $TCi, OUTMODE, INMODE> { fn start(&mut self) { self.channel.start(); } @@ -779,7 +1121,7 @@ macro_rules! spi_dma { } } - impl TransferPayload for SpiRxDma<$SPIi, REMAP, PINS, OP, $RCi> { + impl TransferPayload for SpiSlaveRxDma<$SPIi, $RCi, OUTMODE, INMODE> { fn start(&mut self) { self.channel.start(); } @@ -788,7 +1130,9 @@ macro_rules! spi_dma { } } - impl TransferPayload for SpiRxTxDma<$SPIi, REMAP, PINS, OP, $RCi, $TCi> { + impl TransferPayload + for SpiSlaveRxTxDma<$SPIi, $RCi, $TCi, OUTMODE, INMODE> + { fn start(&mut self) { self.rxchannel.start(); self.txchannel.start(); @@ -799,7 +1143,8 @@ macro_rules! spi_dma { } } - impl crate::dma::ReadDma for SpiRxDma<$SPIi, REMAP, PIN, OP, $RCi> + impl crate::dma::ReadDma + for SpiSlaveRxDma<$SPIi, $RCi, OUTMODE, INMODE> where B: WriteBuffer, { @@ -835,8 +1180,8 @@ macro_rules! spi_dma { } } - impl crate::dma::WriteDma - for SpiTxDma<$SPIi, REMAP, PIN, OP, $TCi> + impl crate::dma::WriteDma + for SpiSlaveTxDma<$SPIi, $TCi, OUTMODE, INMODE> where B: ReadBuffer, { @@ -872,8 +1217,8 @@ macro_rules! spi_dma { } } - impl crate::dma::ReadWriteDma - for SpiRxTxDma<$SPIi, REMAP, PIN, OP, $RCi, $TCi> + impl crate::dma::ReadWriteDma + for SpiSlaveRxTxDma<$SPIi, $RCi, $TCi, OUTMODE, INMODE> where RXB: WriteBuffer, TXB: ReadBuffer, @@ -949,7 +1294,10 @@ spi_dma!( dma1::C3, Spi1RxDma, Spi1TxDma, - Spi1RxTxDma + Spi1RxTxDma, + SpiSlave1RxDma, + SpiSlave1TxDma, + SpiSlave1RxTxDma ); spi_dma!( pac::SPI2, @@ -957,7 +1305,10 @@ spi_dma!( dma1::C5, Spi2RxDma, Spi2TxDma, - Spi2RxTxDma + Spi2RxTxDma, + SpiSlave2RxDma, + SpiSlave2TxDma, + SpiSlave2RxTxDma ); #[cfg(feature = "connectivity")] spi_dma!( @@ -966,5 +1317,8 @@ spi_dma!( dma2::C2, Spi3RxDma, Spi3TxDma, - Spi3RxTxDma + Spi3RxTxDma, + SpiSlave3RxDma, + SpiSlave3TxDma, + SpiSlave3RxTxDma ); diff --git a/src/spi/hal_02.rs b/src/spi/hal_02.rs index 84e5b967..de9f21a9 100644 --- a/src/spi/hal_02.rs +++ b/src/spi/hal_02.rs @@ -30,7 +30,7 @@ impl From for super::Mode { } } -impl spi::FullDuplex for Spi +impl spi::FullDuplex for Spi where SPI: Instance, W: Copy, @@ -46,25 +46,25 @@ where } } -impl blocking::transfer::Default for Spi +impl blocking::transfer::Default for Spi where SPI: Instance, W: Copy, { } -impl blocking::Write for Spi { +impl blocking::Write for Spi { type Error = Error; fn write(&mut self, words: &[u8]) -> Result<(), Error> { - self.write(words) + self.deref_mut().write(words) } } -impl blocking::Write for Spi { +impl blocking::Write for Spi { type Error = Error; fn write(&mut self, words: &[u16]) -> Result<(), Error> { - self.write(words) + self.deref_mut().write(words) } } diff --git a/src/spi/hal_1.rs b/src/spi/hal_1.rs index a398dac0..2d0b7781 100644 --- a/src/spi/hal_1.rs +++ b/src/spi/hal_1.rs @@ -38,7 +38,7 @@ impl embedded_hal::spi::Error for Error { } } -impl ErrorType for Spi { +impl ErrorType for Spi { type Error = Error; } @@ -46,7 +46,7 @@ mod nb { use super::{Error, Instance, Spi}; use embedded_hal_nb::spi::FullDuplex; - impl FullDuplex for Spi + impl FullDuplex for Spi where SPI: Instance, W: Copy, @@ -63,9 +63,10 @@ mod nb { mod blocking { use super::super::{Instance, Spi}; + use core::ops::DerefMut; use embedded_hal::spi::SpiBus; - impl SpiBus for Spi + impl SpiBus for Spi where SPI: Instance, W: Copy + 'static, @@ -83,7 +84,7 @@ mod blocking { } fn write(&mut self, words: &[W]) -> Result<(), Self::Error> { - self.write(words) + self.deref_mut().write(words) } fn flush(&mut self) -> Result<(), Self::Error> {