From 0f9680fbac7edc6e4451baee6649635035149f8d Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Tue, 11 Apr 2023 07:52:50 +0300 Subject: [PATCH] alternate pin enums, remap on From --- CHANGELOG.md | 11 +- examples/can-echo.rs | 28 +- examples/can-loopback.rs | 12 +- examples/can-rtic.rs | 23 +- examples/mfrc522.rs | 12 +- examples/serial-dma-circ.rs | 3 +- examples/serial-dma-peek.rs | 3 +- examples/serial-dma-rx.rs | 3 +- examples/serial-dma-tx.rs | 3 +- examples/serial-fmt.rs | 3 +- examples/serial-interrupt-idle.rs | 4 +- examples/serial.rs | 12 +- examples/serial_9bits.rs | 10 +- examples/serial_config.rs | 3 +- examples/serial_reconfigure.rs | 3 +- examples/spi-dma.rs | 2 +- examples/spi-slave.rs | 46 +- examples/spi.rs | 16 +- src/can.rs | 228 ++++-- src/gpio.rs | 12 +- src/i2c.rs | 162 ++-- src/i2c/blocking.rs | 85 +-- src/i2c/hal_02.rs | 11 +- src/i2c/hal_1.rs | 4 +- src/prelude.rs | 5 + src/serial.rs | 168 ++++- src/spi.rs | 1154 +++++++++++++++++++---------- src/spi/hal_02.rs | 12 +- src/spi/hal_1.rs | 9 +- src/timer/pwm.rs | 4 +- 30 files changed, 1256 insertions(+), 795 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 230faead..6cf1d6ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Breaking changes + +- Relax pin type generics for `Serial`, `I2c`, `Spi`, `Can`. [#462] + Use enums of pin tuples and `Enum::from<(tuple)>` for pin remap before passing to peripheral. + Remove `RemapStruct`s. [#462] +- Use independent `Spi` and `SpiSlave` structures instead of `OP` generic [#462] +- Take `&Clocks` instead of `Clocks` [#498] + ### Changed - PWM timer auto reload value is now preloaded/buffered [#453] @@ -14,7 +22,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Replace UB code by a legitimate pointer access [#480] - Fix flash error flag clearing [#489] - Clarify README for windows users [#496] -- Take `&Clocks` instead of `Clocks` ### Added @@ -28,6 +35,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). [#416]: https://github.com/stm32-rs/stm32f1xx-hal/pull/416 [#453]: https://github.com/stm32-rs/stm32f1xx-hal/pull/453 +[#462]: https://github.com/stm32-rs/stm32f1xx-hal/pull/462 [#467]: https://github.com/stm32-rs/stm32f1xx-hal/pull/467 [#479]: https://github.com/stm32-rs/stm32f1xx-hal/pull/479 [#480]: https://github.com/stm32-rs/stm32f1xx-hal/pull/480 @@ -36,6 +44,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). [#489]: https://github.com/stm32-rs/stm32f1xx-hal/pull/489 [#494]: https://github.com/stm32-rs/stm32f1xx-hal/pull/494 [#496]: https://github.com/stm32-rs/stm32f1xx-hal/pull/496 +[#498]: https://github.com/stm32-rs/stm32f1xx-hal/pull/498 ## [v0.10.0] - 2022-12-12 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..0543ab42 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, + PULL: 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, + PULL: 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 546f45cc..0d5ecebd 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -138,9 +138,7 @@ pub trait Active {} /// Input mode (type state) #[derive(Default)] -pub struct Input { - _mode: PhantomData, -} +pub struct Input(PhantomData); impl Active for Input {} @@ -162,9 +160,7 @@ pub struct PullUp; /// Output mode (type state) #[derive(Default)] -pub struct Output { - _mode: PhantomData, -} +pub struct Output(PhantomData); impl Active for Output {} @@ -184,9 +180,7 @@ impl Active for Analog {} /// Alternate function #[derive(Default)] -pub struct Alternate { - _mode: PhantomData, -} +pub struct Alternate(PhantomData); impl Active for Alternate {} diff --git a/src/i2c.rs b/src/i2c.rs index f62a25e7..51948159 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,94 @@ 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; + + #[allow(clippy::too_many_arguments)] + 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 +180,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 +209,7 @@ where let mut i2c = I2c { i2c, - pins, + pins: pins.into(), mode, pclk1, }; @@ -187,10 +218,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 +284,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 84eb16d9..27594d77 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, + PULL: 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, + PULL: 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() } @@ -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 diff --git a/src/spi.rs b/src/spi.rs index 2c92fb85..ae9ab445 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,394 +104,340 @@ 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 {} -impl Pins for (SCK, MISO, MOSI) -where - SCK: Sck, - MISO: Miso, - MOSI: Mosi, -{ -} +pub mod spi2 { + use super::*; -impl Pins for (SCK, MISO, MOSI) -where - SCK: Ssck, - MISO: So, - MOSI: Si, -{ -} - -pub struct Spi { - spi: SPI, - pins: PINS, - _remap: PhantomData, - _framesize: PhantomData, - _operation: PhantomData, + remap! { + MasterPins, SlavePins: [ + All, Nomosi, Nomiso, PB13, PB14, PB15; + ] + } } - -/// 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, +#[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) }; + ] + } } -/// 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; - -impl Sck for NoSck {} -impl Miso for NoMiso {} -impl Mosi for NoMosi {} -impl So for NoMiso {} -impl Si for NoMosi {} - 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> {} - }; -} + ($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 }, + )+ + } -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); + 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> }, + )+ + } -pub trait Instance: - crate::Sealed + Deref + Enable + Reset + BusClock -{ -} + $( + // For master mode -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::$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 } + } + } -impl Spi { - /** - Constructs an SPI instance using SPI1 in 8bit dataframe mode. + $(#[$attr])* + impl From<(gpio::$SCK, gpio::$MISO, gpio::$MOSI $(, &mut $MAPR)?)> for $master + where + Input: PinMode, + PULL: 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 } + } + } - 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> $(, &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 } + } + } - 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, - 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) - } -} + $(#[$attr])* + impl From<(gpio::$SCK, gpio::$MISO $(, &mut $MAPR)?)> for $master + where + Input: PinMode, + PULL: 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 Spi { - /** - Constructs an SPI instance using SPI1 in 8bit dataframe mode. + $(#[$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 } + } + } - The pin parameter tuple (sck, miso, mosi) should be `(PA5, PA6, PA7)` or `(PB3, PB4, PB5)` configured as `(Input<...>, Alternate<...>, Input<...>)`. + // For slave mode - 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) - } -} + $(#[$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 } + } + } -impl Spi { - /** - Constructs an SPI instance using SPI2 in 8bit dataframe mode. + $(#[$attr])* + impl From<(gpio::$SCK, gpio::$MISO, gpio::$MOSI $(, &mut $MAPR)?)> for $slave + where + Alternate: PinMode, + Input: PinMode, + PULL: 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 } + } + } - The pin parameter tuple (sck, miso, mosi) should be `(PB13, PB14, PB15)` configured as `(Alternate<...>, Input<...>, Alternate<...>)`. + $(#[$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 } + } + } - 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) + $(#[$attr])* + impl From<(gpio::$SCK, gpio::$MOSI $(, &mut $MAPR)?)> for $slave + where + Input: PinMode, + PULL: 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; -impl Spi { - /** - Constructs an SPI instance using SPI2 in 8bit dataframe mode. - - The pin parameter tuple (sck, miso, mosi) should be `(PB13, PB14, PB15)` configured as `(Input<...>, Alternate<...>, Input<...>)`. - - 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) +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, + ) -> Spi { + Self::spi(self, pins, mode, freq, clocks).frame_size_16bit() } -} - -#[cfg(any(feature = "high", feature = "connectivity"))] -impl Spi { - /** - Constructs an SPI instance using SPI3 in 8bit dataframe mode. - - The pin parameter tuple (sck, miso, mosi) should be `(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(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) + 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() } +} - /** - Constructs an SPI instance using SPI3 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<...>)`. - - 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, +impl SpiExt for SPI { + fn spi( + self, + 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) + ) -> Spi { + Spi::new(self, pins, mode, freq, clocks) } -} - -#[cfg(any(feature = "high", feature = "connectivity"))] -impl Spi { - /** - Constructs an SPI instance using SPI3 in 8bit dataframe mode. - - The pin parameter tuple (sck, miso, mosi) should be `(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) + fn spi_slave( + self, + pins: impl Into>, + mode: Mode, + ) -> SpiSlave { + SpiSlave::new(self, pins, mode) } +} - /** - Constructs an SPI instance using SPI3 in 8bit dataframe mode. - - The pin parameter tuple (sck, miso, mosi) should be `(PB3, PB4, PB5)` or `(PC10, PC11, PC12)` 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 - */ - #[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) +impl SpiInner { + fn new(spi: SPI) -> Self { + Self { + spi, + _framesize: PhantomData, + } } } -pub trait SpiReadWrite { - fn read_data_reg(&mut self) -> T; - fn write_data_reg(&mut self, data: T); - fn spi_write(&mut self, words: &[T]) -> Result<(), Error>; +/// Spi in Master mode +pub struct Spi { + inner: SpiInner, + pins: SPI::MasterPins, } -impl SpiReadWrite - for Spi -where - SPI: Instance, - FrameSize: Copy, -{ - fn read_data_reg(&mut self) -> FrameSize { - // NOTE(read_volatile) read only 1 byte (the svd2rust API only allows - // reading a half-word) - unsafe { ptr::read_volatile(ptr::addr_of!(self.spi.dr) as *const FrameSize) } - } +/// Spi in Slave mode +pub struct SpiSlave { + inner: SpiInner, + pins: SPI::SlavePins, +} - fn write_data_reg(&mut self, data: FrameSize) { - // NOTE(write_volatile) see note above - unsafe { ptr::write_volatile(ptr::addr_of!(self.spi.dr) as *mut FrameSize, data) } +impl Deref for Spi { + type Target = SpiInner; + fn deref(&self) -> &Self::Target { + &self.inner } +} - // Implement write as per the "Transmit only procedure" page 712 - // of RM0008 Rev 20. This is more than twice as fast as the - // default Write<> implementation (which reads and drops each - // received value) - fn spi_write(&mut self, words: &[FrameSize]) -> Result<(), Error> { - // Write each word when the tx buffer is empty - for word in words { - loop { - let sr = self.spi.sr.read(); - if sr.txe().bit_is_set() { - self.write_data_reg(*word); - if sr.modf().bit_is_set() { - return Err(Error::ModeFault); - } - break; - } - } - } - // Wait for final TXE - while !self.is_tx_empty() {} - // Wait for final !BSY - while self.is_busy() {} - // Clear OVR set due to dropped received values - let _ = self.read_data_reg(); - let _ = self.spi.sr.read(); - Ok(()) +impl DerefMut for Spi { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner } } -impl Spi -where - SPI: Instance, - FrameSize: Copy, -{ - pub fn release(self) -> (SPI, PINS) { - (self.spi, self.pins) +impl Deref for SpiSlave { + type Target = SpiInner; + fn deref(&self) -> &Self::Target { + &self.inner } +} - /// Select which frame format is used for data transfers - pub fn bit_format(&mut self, format: SpiBitFormat) { - match format { - SpiBitFormat::LsbFirst => self.spi.cr1.modify(|_, w| w.lsbfirst().set_bit()), - SpiBitFormat::MsbFirst => self.spi.cr1.modify(|_, w| w.lsbfirst().clear_bit()), - } - } - - /// Starts listening to the SPI by enabling the _Received data - /// ready to be read (RXNE)_ interrupt and _Transmit data - /// register empty (TXE)_ interrupt - pub fn listen(&mut self, event: Event) { - match event { - Event::Rxne => self.spi.cr2.modify(|_, w| w.rxneie().set_bit()), - Event::Txe => self.spi.cr2.modify(|_, w| w.txeie().set_bit()), - Event::Error => self.spi.cr2.modify(|_, w| w.errie().set_bit()), - } +impl DerefMut for SpiSlave { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner } +} - /// Stops listening to the SPI by disabling the _Received data - /// ready to be read (RXNE)_ interrupt and _Transmit data - /// register empty (TXE)_ interrupt - pub fn unlisten(&mut self, event: Event) { - match event { - Event::Rxne => self.spi.cr2.modify(|_, w| w.rxneie().clear_bit()), - Event::Txe => self.spi.cr2.modify(|_, w| w.txeie().clear_bit()), - Event::Error => self.spi.cr2.modify(|_, w| w.errie().clear_bit()), - } - } +/// 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, +} - /// Returns true if the tx register is empty (and can accept data) - #[inline] - pub fn is_tx_empty(&self) -> bool { - self.spi.sr.read().txe().bit_is_set() - } +pub trait Instance: + crate::Sealed + Deref + Enable + Reset + BusClock +{ + type MasterPins; + type SlavePins; +} - /// Returns true if the rx register is not empty (and can be read) - #[inline] - pub fn is_rx_not_empty(&self) -> bool { - self.spi.sr.read().rxne().bit_is_set() - } +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; +} - /// Returns true if the transfer is in progress - #[inline] - pub fn is_busy(&self) -> bool { - self.spi.sr.read().bsy().bit_is_set() - } +impl Spi { + /** + Constructs an SPI instance using SPI1 in 8bit dataframe mode. - /// Returns true if data are received and the previous data have not yet been read from SPI_DR. - #[inline] - pub fn is_overrun(&self) -> bool { - self.spi.sr.read().ovr().bit_is_set() - } -} + The pin parameter tuple (sck, miso, mosi) should be `(PA5, PA6, PA7)` or `(PB3, PB4, PB5)` configured as `(Alternate, Input<...>, Alternate)`. -impl Spi -where - SPI: Instance, -{ - fn configure( + You can also use `NoSck`, `NoMiso` or `NoMosi` if you don't want to use the pins + */ + pub fn new( spi: SPI, - pins: PINS, - mode: impl Into, + pins: impl Into>, + mode: Mode, freq: Hertz, clocks: &Clocks, ) -> Self { - let mode = mode.into(); // enable or reset SPI let rcc = unsafe { &(*RCC::ptr()) }; SPI::enable(rcc); @@ -512,6 +458,8 @@ where _ => 0b111, }; + let pins = pins.into(); + spi.cr1.write(|w| { // clock phase from config w.cpha().bit(mode.phase == Phase::CaptureOnSecondTransition); @@ -538,21 +486,25 @@ where }); Spi { - spi, + inner: SpiInner::new(spi), pins, - _remap: PhantomData, - _framesize: PhantomData, - _operation: PhantomData, } } + + pub fn release(self) -> (SPI, SPI::MasterPins) { + (self.inner.spi, self.pins) + } } -impl Spi -where - SPI: Instance, -{ - fn configure(spi: SPI, pins: PINS, mode: impl Into) -> Self { - let mode = mode.into(); +impl SpiSlave { + /** + Constructs an SPI instance using SPI1 in 8bit dataframe mode. + + 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 + */ + pub fn new(spi: SPI, pins: impl Into>, mode: Mode) -> Self { // enable or reset SPI let rcc = unsafe { &(*RCC::ptr()) }; SPI::enable(rcc); @@ -561,6 +513,8 @@ where // disable SS output spi.cr2.write(|w| w.ssoe().clear_bit()); + let pins = pins.into(); + spi.cr1.write(|w| { // clock phase from config w.cpha().bit(mode.phase == Phase::CaptureOnSecondTransition); @@ -584,60 +538,178 @@ where w.spe().set_bit() }); + SpiSlave { + inner: SpiInner::new(spi), + pins, + } + } + + pub fn release(self) -> (SPI, SPI::SlavePins) { + (self.inner.spi, self.pins) + } +} + +pub trait SpiReadWrite { + fn read_data_reg(&mut self) -> T; + fn write_data_reg(&mut self, data: T); + fn spi_write(&mut self, words: &[T]) -> Result<(), Error>; +} + +impl SpiReadWrite for SpiInner { + fn read_data_reg(&mut self) -> W { + // NOTE(read_volatile) read only 1 byte (the svd2rust API only allows + // reading a half-word) + unsafe { ptr::read_volatile(ptr::addr_of!(self.spi.dr) as *const W) } + } + + fn write_data_reg(&mut self, data: W) { + // NOTE(write_volatile) see note above + unsafe { ptr::write_volatile(ptr::addr_of!(self.spi.dr) as *mut W, data) } + } + + // Implement write as per the "Transmit only procedure" page 712 + // of RM0008 Rev 20. This is more than twice as fast as the + // default Write<> implementation (which reads and drops each + // received value) + fn spi_write(&mut self, words: &[W]) -> Result<(), Error> { + // Write each word when the tx buffer is empty + for word in words { + loop { + let sr = self.spi.sr.read(); + if sr.txe().bit_is_set() { + self.write_data_reg(*word); + if sr.modf().bit_is_set() { + return Err(Error::ModeFault); + } + break; + } + } + } + // Wait for final TXE + while !self.is_tx_empty() {} + // Wait for final !BSY + while self.is_busy() {} + // Clear OVR set due to dropped received values + let _ = self.read_data_reg(); + let _ = self.spi.sr.read(); + Ok(()) + } +} + +impl SpiInner { + /// Select which frame format is used for data transfers + pub fn bit_format(&mut self, format: SpiBitFormat) { + match format { + SpiBitFormat::LsbFirst => self.spi.cr1.modify(|_, w| w.lsbfirst().set_bit()), + SpiBitFormat::MsbFirst => self.spi.cr1.modify(|_, w| w.lsbfirst().clear_bit()), + } + } + + /// Starts listening to the SPI by enabling the _Received data + /// ready to be read (RXNE)_ interrupt and _Transmit data + /// register empty (TXE)_ interrupt + pub fn listen(&mut self, event: Event) { + match event { + Event::Rxne => self.spi.cr2.modify(|_, w| w.rxneie().set_bit()), + Event::Txe => self.spi.cr2.modify(|_, w| w.txeie().set_bit()), + Event::Error => self.spi.cr2.modify(|_, w| w.errie().set_bit()), + } + } + + /// Stops listening to the SPI by disabling the _Received data + /// ready to be read (RXNE)_ interrupt and _Transmit data + /// register empty (TXE)_ interrupt + pub fn unlisten(&mut self, event: Event) { + match event { + Event::Rxne => self.spi.cr2.modify(|_, w| w.rxneie().clear_bit()), + Event::Txe => self.spi.cr2.modify(|_, w| w.txeie().clear_bit()), + Event::Error => self.spi.cr2.modify(|_, w| w.errie().clear_bit()), + } + } + + /// Returns true if the tx register is empty (and can accept data) + #[inline] + pub fn is_tx_empty(&self) -> bool { + self.spi.sr.read().txe().bit_is_set() + } + + /// Returns true if the rx register is not empty (and can be read) + #[inline] + pub fn is_rx_not_empty(&self) -> bool { + self.spi.sr.read().rxne().bit_is_set() + } + + /// Returns true if the transfer is in progress + #[inline] + pub fn is_busy(&self) -> bool { + self.spi.sr.read().bsy().bit_is_set() + } + + /// Returns true if data are received and the previous data have not yet been read from SPI_DR. + #[inline] + pub fn is_overrun(&self) -> bool { + self.spi.sr.read().ovr().bit_is_set() + } +} + +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 { - spi, - pins, - _remap: PhantomData, - _framesize: PhantomData, - _operation: PhantomData, + inner: SpiInner::new(self.inner.spi), + pins: self.pins, } } } -impl Spi -where - SPI: Instance, -{ +impl SpiSlave { /// Converts from 8bit dataframe to 16bit. - pub fn frame_size_16bit(self) -> Spi { + 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()); - Spi { - spi: self.spi, + SpiSlave { + inner: SpiInner::new(self.inner.spi), pins: self.pins, - _remap: PhantomData, - _framesize: PhantomData, - _operation: PhantomData, } } } -impl Spi -where - SPI: Instance, -{ +impl Spi { /// Converts from 16bit dataframe to 8bit. - pub fn frame_size_8bit(self) -> Spi { + pub fn frame_size_8bit(self) -> Spi { 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, + inner: SpiInner::new(self.inner.spi), + pins: self.pins, + } + } +} + +impl SpiSlave { + /// Converts from 16bit dataframe to 8bit. + 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()); + 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, + W: Copy, { - pub fn read_nonblocking(&mut self) -> nb::Result { + pub fn read_nonblocking(&mut self) -> nb::Result { let sr = self.spi.sr.read(); Err(if sr.ovr().bit_is_set() { @@ -654,7 +726,7 @@ where nb::Error::WouldBlock }) } - pub fn write_nonblocking(&mut self, data: FrameSize) -> nb::Result<(), Error> { + pub fn write_nonblocking(&mut self, data: W) -> nb::Result<(), Error> { let sr = self.spi.sr.read(); // NOTE: Error::Overrun was deleted in #408. Need check @@ -670,53 +742,60 @@ where nb::Error::WouldBlock }) } - pub fn write(&mut self, words: &[FrameSize]) -> Result<(), Error> { + pub fn write(&mut self, words: &[W]) -> Result<(), Error> { self.spi_write(words) } } // 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, PULL>; + pub type $txdma = SpiTxDma<$SPIi, $TCi, PULL>; + pub type $rxtxdma = SpiRxTxDma<$SPIi, $RCi, $TCi, PULL>; - impl Transmit for SpiTxDma<$SPIi, REMAP, PINS, OP, $TCi> { + impl Transmit for SpiTxDma<$SPIi, $TCi, PULL> { type TxChannel = $TCi; type ReceivedWord = u8; } - impl Receive for SpiRxDma<$SPIi, REMAP, PINS, OP, $RCi> { + impl Receive for SpiRxDma<$SPIi, $RCi, PULL> { type RxChannel = $RCi; type TransmittedWord = u8; } - impl Transmit for SpiRxTxDma<$SPIi, REMAP, PINS, OP, $RCi, $TCi> { + impl Transmit for SpiRxTxDma<$SPIi, $RCi, $TCi, PULL> { type TxChannel = $TCi; type ReceivedWord = u8; } - impl Receive for SpiRxTxDma<$SPIi, REMAP, PINS, OP, $RCi, $TCi> { + impl Receive for SpiRxTxDma<$SPIi, $RCi, $TCi, PULL> { 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, PULL> { + pub fn with_tx_dma(self, channel: $TCi) -> SpiTxDma<$SPIi, $TCi, PULL> { 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, PULL> { self.spi.cr2.modify(|_, w| w.rxdmaen().set_bit()); SpiRxDma { payload: self, @@ -727,7 +806,7 @@ macro_rules! spi_dma { self, rxchannel: $RCi, txchannel: $TCi, - ) -> SpiRxTxDma<$SPIi, REMAP, PINS, OP, $RCi, $TCi> { + ) -> SpiRxTxDma<$SPIi, $RCi, $TCi, PULL> { self.spi .cr2 .modify(|_, w| w.rxdmaen().set_bit().txdmaen().set_bit()); @@ -739,24 +818,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, PULL> { + pub fn release(self) -> (Spi<$SPIi, u8, PULL>, $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, PULL> { + pub fn release(self) -> (Spi<$SPIi, u8, PULL>, $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, PULL> { + pub fn release(self) -> (Spi<$SPIi, u8, PULL>, $RCi, $TCi) { let SpiRxTxDma { payload, rxchannel, @@ -770,7 +849,7 @@ macro_rules! spi_dma { } } - impl TransferPayload for SpiTxDma<$SPIi, REMAP, PINS, OP, $TCi> { + impl TransferPayload for SpiTxDma<$SPIi, $TCi, PULL> { fn start(&mut self) { self.channel.start(); } @@ -779,7 +858,7 @@ macro_rules! spi_dma { } } - impl TransferPayload for SpiRxDma<$SPIi, REMAP, PINS, OP, $RCi> { + impl TransferPayload for SpiRxDma<$SPIi, $RCi, PULL> { fn start(&mut self) { self.channel.start(); } @@ -788,7 +867,7 @@ macro_rules! spi_dma { } } - impl TransferPayload for SpiRxTxDma<$SPIi, REMAP, PINS, OP, $RCi, $TCi> { + impl TransferPayload for SpiRxTxDma<$SPIi, $RCi, $TCi, PULL> { fn start(&mut self) { self.rxchannel.start(); self.txchannel.start(); @@ -799,7 +878,7 @@ macro_rules! spi_dma { } } - impl crate::dma::ReadDma for SpiRxDma<$SPIi, REMAP, PIN, OP, $RCi> + impl crate::dma::ReadDma for SpiRxDma<$SPIi, $RCi, PULL> where B: WriteBuffer, { @@ -833,8 +912,7 @@ macro_rules! spi_dma { } } - impl crate::dma::WriteDma - for SpiTxDma<$SPIi, REMAP, PIN, OP, $TCi> + impl crate::dma::WriteDma for SpiTxDma<$SPIi, $TCi, PULL> where B: ReadBuffer, { @@ -868,8 +946,267 @@ macro_rules! spi_dma { } } - impl crate::dma::ReadWriteDma - for SpiRxTxDma<$SPIi, REMAP, PIN, OP, $RCi, $TCi> + impl crate::dma::ReadWriteDma + for SpiRxTxDma<$SPIi, $RCi, $TCi, PULL> + 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, Otype, PULL>; + pub type $slavetxdma = + SpiSlaveTxDma<$SPIi, $TCi, Otype, PULL>; + pub type $slaverxtxdma = + SpiSlaveRxTxDma<$SPIi, $RCi, $TCi, Otype, PULL>; + + impl Transmit for SpiSlaveTxDma<$SPIi, $TCi, Otype, PULL> { + type TxChannel = $TCi; + type ReceivedWord = u8; + } + + impl Receive for SpiSlaveRxDma<$SPIi, $RCi, Otype, PULL> { + type RxChannel = $RCi; + type TransmittedWord = u8; + } + + impl Transmit for SpiSlaveRxTxDma<$SPIi, $RCi, $TCi, Otype, PULL> { + type TxChannel = $TCi; + type ReceivedWord = u8; + } + + impl Receive for SpiSlaveRxTxDma<$SPIi, $RCi, $TCi, Otype, PULL> { + type RxChannel = $RCi; + type TransmittedWord = u8; + } + + impl SpiSlave<$SPIi, u8, Otype, PULL> { + pub fn with_tx_dma(self, channel: $TCi) -> SpiSlaveTxDma<$SPIi, $TCi, Otype, PULL> { + self.spi.cr2.modify(|_, w| w.txdmaen().set_bit()); + SpiSlaveTxDma { + payload: self, + channel, + } + } + pub fn with_rx_dma(self, channel: $RCi) -> SpiSlaveRxDma<$SPIi, $RCi, Otype, PULL> { + 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, Otype, PULL> { + self.spi + .cr2 + .modify(|_, w| w.rxdmaen().set_bit().txdmaen().set_bit()); + SpiSlaveRxTxDma { + payload: self, + rxchannel, + txchannel, + } + } + } + + impl SpiSlaveTxDma<$SPIi, $TCi, Otype, PULL> { + pub fn release(self) -> (SpiSlave<$SPIi, u8, Otype, PULL>, $TCi) { + let SpiSlaveTxDma { payload, channel } = self; + payload.spi.cr2.modify(|_, w| w.txdmaen().clear_bit()); + (payload, channel) + } + } + + impl SpiSlaveRxDma<$SPIi, $RCi, Otype, PULL> { + pub fn release(self) -> (SpiSlave<$SPIi, u8, Otype, PULL>, $RCi) { + let SpiSlaveRxDma { payload, channel } = self; + payload.spi.cr2.modify(|_, w| w.rxdmaen().clear_bit()); + (payload, channel) + } + } + + impl SpiSlaveRxTxDma<$SPIi, $RCi, $TCi, Otype, PULL> { + pub fn release(self) -> (SpiSlave<$SPIi, u8, Otype, PULL>, $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, Otype, PULL> { + fn start(&mut self) { + self.channel.start(); + } + fn stop(&mut self) { + self.channel.stop(); + } + } + + impl TransferPayload for SpiSlaveRxDma<$SPIi, $RCi, Otype, PULL> { + fn start(&mut self) { + self.channel.start(); + } + fn stop(&mut self) { + self.channel.stop(); + } + } + + impl TransferPayload for SpiSlaveRxTxDma<$SPIi, $RCi, $TCi, Otype, PULL> { + 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 SpiSlaveRxDma<$SPIi, $RCi, Otype, PULL> + 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 SpiSlaveTxDma<$SPIi, $TCi, Otype, PULL> + 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 SpiSlaveRxTxDma<$SPIi, $RCi, $TCi, Otype, PULL> where RXB: WriteBuffer, TXB: ReadBuffer, @@ -941,7 +1278,10 @@ spi_dma!( dma1::C3, Spi1RxDma, Spi1TxDma, - Spi1RxTxDma + Spi1RxTxDma, + SpiSlave1RxDma, + SpiSlave1TxDma, + SpiSlave1RxTxDma ); spi_dma!( pac::SPI2, @@ -949,7 +1289,10 @@ spi_dma!( dma1::C5, Spi2RxDma, Spi2TxDma, - Spi2RxTxDma + Spi2RxTxDma, + SpiSlave2RxDma, + SpiSlave2TxDma, + SpiSlave2RxTxDma ); #[cfg(feature = "connectivity")] spi_dma!( @@ -958,5 +1301,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..2e410153 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..f038ac72 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> { diff --git a/src/timer/pwm.rs b/src/timer/pwm.rs index 36d59333..01fe1e61 100644 --- a/src/timer/pwm.rs +++ b/src/timer/pwm.rs @@ -94,11 +94,11 @@ macro_rules! pins_impl { ( $( ( $($PINX:ident),+ ), ( $($ENCHX:ident),+ ); )+ ) => { $( #[allow(unused_parens)] - impl Pins),+)> for ($($PINX),+) + impl Pins),+)> for ($($PINX),+) where TIM: Instance + WithPwm, REMAP: Remap, - $($PINX: CPin + gpio::PinExt>,)+ + $($PINX: CPin + gpio::PinExt>,)+ { $(const $ENCHX: bool = true;)+ type Channels = ($(PwmChannel),+);