diff --git a/Cargo.toml b/Cargo.toml index 429ab5e..37a0297 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,13 +3,14 @@ authors = ["Vitaly Domnikov "] categories = ["embedded", "hardware-support", "no-std"] description = "Peripheral access API for STM32C0 series microcontrollers" documentation = "https://docs.rs/stm32c0xx-hal" -edition = "2018" +edition = "2021" keywords = ["arm", "cortex-m", "stm32c0xx", "hal"] license = "MIT/Apache-2.0" name = "stm32c0xx-hal" readme = "README.md" repository = "https://github.com/stm32-rs/stm32c0xx-hal" version = "0.0.0" +rust-version = "1.65" [package.metadata.docs.rs] features = ["stm32c031", "rt"] @@ -46,8 +47,20 @@ panic-semihosting = "0.5.6" default = ["i2c-blocking"] device-selected = [] rt = ["stm32c0/rt"] -stm32c011 = ["stm32c0/stm32c011", "device-selected"] -stm32c031 = ["stm32c0/stm32c031", "device-selected"] +stm32c011 = ["stm32c0/stm32c011", "device-selected", "gpio-c0xx"] +stm32c031 = ["stm32c0/stm32c031", "device-selected", "gpio-c0xx_453"] + +gpio-c0xx = ["gpiof"] +gpio-c0xx_453 = ["gpiod", "gpiof"] +#gpio-c0xx = [ +# "debug", "gpioa", "gpiob", "gpioc", "gpiof", "i2c1", "i2s", "i2s1", "ir", "rcc", "spi1", "tim1", "tim14", "tim16", "tim17", "tim3", "usart1", "usart2", +#] +#gpio-c0xx_453 = [ +# "debug", "gpioa", "gpiob", "gpioc", "gpiod", "gpiof", "i2c1", "i2s", "i2s1", "ir", "rcc", "spi1", "tim1", "tim14", "tim16", "tim17", "tim3", "usart1", "usart2", +#] + +gpiod = [] +gpiof = [] i2c-blocking = [] i2c-nonblocking = [] @@ -60,3 +73,7 @@ debug = false codegen-units = 1 incremental = false lto = false + +[[example]] +name = "i2c" +required-features = ["gpio-c0xx_453"] diff --git a/examples/blinky.rs b/examples/blinky.rs index 644857d..76e9650 100644 --- a/examples/blinky.rs +++ b/examples/blinky.rs @@ -18,7 +18,7 @@ fn main() -> ! { let mut led = port_a.pa5.into_push_pull_output(); loop { - led.toggle().ok(); + led.toggle(); for _ in 0..1_000_000 { cortex_m::asm::nop(); } diff --git a/examples/blinky_delay.rs b/examples/blinky_delay.rs index 1a5045d..547fe11 100644 --- a/examples/blinky_delay.rs +++ b/examples/blinky_delay.rs @@ -22,6 +22,6 @@ fn main() -> ! { let mut delay = dp.TIM3.delay(&mut rcc); loop { delay.delay(500.millis()); - led.toggle().ok(); + led.toggle(); } } diff --git a/examples/blinky_timer.rs b/examples/blinky_timer.rs index 3471fa7..27b4ace 100644 --- a/examples/blinky_timer.rs +++ b/examples/blinky_timer.rs @@ -24,7 +24,7 @@ fn main() -> ! { timer.start(500.millis()); loop { - led.toggle().unwrap(); + led.toggle(); block!(timer.wait()).unwrap(); } } diff --git a/examples/button.rs b/examples/button.rs index 4b85c4a..fdd1992 100644 --- a/examples/button.rs +++ b/examples/button.rs @@ -22,10 +22,10 @@ fn main() -> ! { let mut led = port_a.pa5.into_push_pull_output(); loop { - if button.is_high().unwrap_or_default() { - led.set_low().ok(); + if button.is_high() { + led.set_low(); } else { - led.set_high().ok(); + led.set_high(); } } } diff --git a/examples/clockout.rs b/examples/clockout.rs index 223c53d..a67bbe2 100644 --- a/examples/clockout.rs +++ b/examples/clockout.rs @@ -20,11 +20,11 @@ fn main() -> ! { let mut rcc = dp.RCC.freeze(Config::hsi(Prescaler::NotDivided)); let gpioa = dp.GPIOA.split(&mut rcc); - let mut mco = - gpioa - .pa9 - .set_speed(Speed::VeryHigh) - .mco(MCOSrc::SysClk, Prescaler::Div2, &mut rcc); + let mut mco = gpioa.pa9.into_alternate().speed(Speed::VeryHigh).mco( + MCOSrc::SysClk, + Prescaler::Div2, + &mut rcc, + ); mco.enable(); let mut lsco = gpioa.pa2.lsco(LSCOSrc::LSE, &mut rcc); diff --git a/examples/i2c.rs b/examples/i2c.rs index b074ea9..c800f42 100644 --- a/examples/i2c.rs +++ b/examples/i2c.rs @@ -24,7 +24,7 @@ fn main() -> ! { let sda = gpiob.pb9.into_open_drain_output_in_state(PinState::High); let scl = gpiob.pb8.into_open_drain_output_in_state(PinState::High); - let mut i2c = dp.I2C.i2c(sda, scl, Config::new(400.kHz()), &mut rcc); + let mut i2c = dp.I2C.i2c((scl, sda), Config::new(400.kHz()), &mut rcc); i2c.write(0x2a, &[0x80, 0xff]).unwrap(); i2c.write(0x2a, &[0x01, 0x04, 0x00, 0x00]).unwrap(); diff --git a/examples/rtic.rs b/examples/rtic.rs index feaf72d..71dbd9d 100644 --- a/examples/rtic.rs +++ b/examples/rtic.rs @@ -55,7 +55,7 @@ mod app { #[task(binds = TIM17, shared = [timer], local = [led])] fn timer_tick(mut ctx: timer_tick::Context) { - ctx.local.led.toggle().ok(); + ctx.local.led.toggle(); ctx.shared.timer.lock(|tim| tim.clear_irq()); } diff --git a/examples/watchdog.rs b/examples/watchdog.rs index 55e026e..663fb60 100644 --- a/examples/watchdog.rs +++ b/examples/watchdog.rs @@ -25,7 +25,7 @@ fn main() -> ! { let mut watchdog = dp.WWDG.constrain(&mut rcc); // let mut watchdog = dp.IWDG.constrain(); - led.set_high().ok(); + led.set_high(); watchdog.start(20.millis()); loop {} diff --git a/src/analog/adc.rs b/src/analog/adc.rs index e6b954e..7ec6804 100644 --- a/src/analog/adc.rs +++ b/src/analog/adc.rs @@ -466,6 +466,9 @@ adc_pin! { Channel12: (PA12, 12u8), Channel13: (PA13, 13u8), Channel14: (PA14, 14u8), +} +#[cfg(feature = "gpio-c0xx_453")] +adc_pin! { Channel17: (PB0, 17u8), Channel18: (PB1, 18u8), Channel19: (PB2, 19u8), diff --git a/src/exti.rs b/src/exti.rs index dba5018..f0120f9 100644 --- a/src/exti.rs +++ b/src/exti.rs @@ -1,5 +1,5 @@ //! External interrupt controller -use crate::gpio::SignalEdge; +use crate::gpio::{Input, Pin, PinExt, PinMode, SignalEdge}; use crate::stm32::EXTI; /// EXTI trigger event @@ -27,6 +27,7 @@ pub enum Event { LSE_CSS = 31, } +#[allow(unused)] impl Event { pub(crate) fn from_code(n: u8) -> Event { match n { @@ -121,3 +122,30 @@ impl ExtiExt for EXTI { } } } + +impl Pin { + /// Configures the pin as external trigger + pub fn listen(self, edge: SignalEdge, exti: &mut EXTI) -> Pin { + let pin = self.into_mode(); + let offset = (N % 4) * 8; + let mask = (pin.port_id() as u32) << offset; + let reset = !(0xff << offset); + match N as u8 { + 0..=3 => exti + .exticr1 + .modify(|r, w| unsafe { w.bits(r.bits() & reset | mask) }), + 4..=7 => exti + .exticr2 + .modify(|r, w| unsafe { w.bits(r.bits() & reset | mask) }), + 8..=11 => exti + .exticr3 + .modify(|r, w| unsafe { w.bits(r.bits() & reset | mask) }), + 12..=16 => exti + .exticr4 + .modify(|r, w| unsafe { w.bits(r.bits() & reset | mask) }), + _ => unreachable!(), + } + exti.listen(Event::from_code(N), edge); + pin + } +} diff --git a/src/gpio.rs b/src/gpio.rs index 366a6d9..2c7cc8c 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -1,13 +1,87 @@ //! General Purpose Input / Output -use core::marker::PhantomData; +//! +//! The GPIO pins are organised into groups of 16 pins which can be accessed through the +//! `gpioa`, `gpiob`... modules. To get access to the pins, you first need to convert them into a +//! HAL designed struct from the `pac` struct using the [split](trait.GpioExt.html#tymethod.split) function. +//! ```rust +//! // Acquire the GPIOC peripheral +//! // NOTE: `dp` is the device peripherals from the `PAC` crate +//! let mut gpioa = dp.GPIOA.split(); +//! ``` +//! +//! This gives you a struct containing all the pins `px0..px15`. +//! By default pins are in floating input mode. You can change their modes. +//! For example, to set `pa5` high, you would call +//! +//! ```rust +//! let output = gpioa.pa5.into_push_pull_output(); +//! output.set_high(); +//! ``` +//! +//! ## Modes +//! +//! Each GPIO pin can be set to various modes: +//! +//! - **Alternate**: Pin mode required when the pin is driven by other peripherals +//! - **Analog**: Analog input to be used with ADC. +//! - **Dynamic**: Pin mode is selected at runtime. See changing configurations for more details +//! - Input +//! - **PullUp**: Input connected to high with a weak pull up resistor. Will be high when nothing +//! is connected +//! - **PullDown**: Input connected to high with a weak pull up resistor. Will be low when nothing +//! is connected +//! - **Floating**: Input not pulled to high or low. Will be undefined when nothing is connected +//! - Output +//! - **PushPull**: Output which either drives the pin high or low +//! - **OpenDrain**: Output which leaves the gate floating, or pulls it do ground in drain +//! mode. Can be used as an input in the `open` configuration +//! +//! ## Changing modes +//! The simplest way to change the pin mode is to use the `into_` functions. These return a +//! new struct with the correct mode that you can use the input or output functions on. +//! +//! If you need a more temporary mode change, and can not use the `into_` functions for +//! ownership reasons, you can use the closure based `with_` functions to temporarily change the pin type, do +//! some output or input, and then have it change back once done. +//! +//! ### Dynamic Mode Change +//! The above mode change methods guarantee that you can only call input functions when the pin is +//! in input mode, and output when in output modes, but can lead to some issues. Therefore, there +//! is also a mode where the state is kept track of at runtime, allowing you to change the mode +//! often, and without problems with ownership, or references, at the cost of some performance and +//! the risk of runtime errors. +//! +//! To make a pin dynamic, use the `into_dynamic` function, and then use the `make_` functions to +//! change the mode use crate::rcc::Rcc; -use core::convert::Infallible; -use embedded_hal::digital::v2::PinState; -use hal::digital::v2::{toggleable, InputPin, OutputPin, StatefulOutputPin}; +use core::marker::PhantomData; -/// Default pin mode -pub type DefaultMode = Analog; +pub mod alt; +mod convert; +pub use convert::PinMode; +mod partially_erased; +pub use partially_erased::{PEPin, PartiallyErasedPin}; +mod erased; +pub use erased::{EPin, ErasedPin}; +mod dynamic; +pub use dynamic::{Dynamic, DynamicPin}; +mod hal_02; +pub mod outport; + +pub use embedded_hal::digital::v2::PinState; + +use core::fmt; + +/// A filler pin type +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct NoPin(PhantomData); +impl NoPin { + pub fn new() -> Self { + Self(PhantomData) + } +} /// Extension trait to split a GPIO peripheral in independent pins and registers pub trait GpioExt { @@ -18,726 +92,537 @@ pub trait GpioExt { fn split(self, rcc: &mut Rcc) -> Self::Parts; } -trait GpioRegExt { - fn is_low(&self, pos: u8) -> bool; - fn is_set_low(&self, pos: u8) -> bool; - fn set_high(&self, pos: u8); - fn set_low(&self, pos: u8); +/// Id, port and mode for any pin +pub trait PinExt { + /// Current pin mode + type Mode; + /// Pin number + fn pin_id(&self) -> u8; + /// Port number starting from 0 + fn port_id(&self) -> u8; } +/// Some alternate mode (type state) +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Alternate(PhantomData); + /// Input mode (type state) -pub struct Input { - _mode: PhantomData, +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Input; + +/// Pull setting for an input. +#[derive(Debug, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Pull { + /// Floating + None = 0, + /// Pulled up + Up = 1, + /// Pulled down + Down = 2, } -/// Floating input (type state) -pub struct Floating; - -/// Pulled down input (type state) -pub struct PullDown; - -/// Pulled up input (type state) -pub struct PullUp; - /// Open drain input or output (type state) +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct OpenDrain; -/// Analog mode (type state) -pub struct Analog; - /// Output mode (type state) -pub struct Output { +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Output { _mode: PhantomData, } /// Push pull output (type state) +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PushPull; -/// Fully erased pin -pub struct Pin { - i: u8, - port: *const dyn GpioRegExt, - _mode: PhantomData, +/// Analog mode (type state) +#[derive(Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Analog; + +/// JTAG/SWD mote (type state) +pub type Debugger = Alternate<0, PushPull>; + +pub(crate) mod marker { + /// Marker trait that show if `ExtiPin` can be implemented + pub trait Interruptible {} + /// Marker trait for readable pin modes + pub trait Readable {} + /// Marker trait for slew rate configurable pin modes + pub trait OutputSpeed {} + /// Marker trait for active pin modes + pub trait Active {} + /// Marker trait for all pin modes except alternate + pub trait NotAlt {} + /// Marker trait for pins with alternate function `A` mapping + pub trait IntoAf {} } -macro_rules! gpio_trait { - ($gpiox:ident) => { - impl GpioRegExt for crate::stm32::$gpiox::RegisterBlock { - fn is_low(&self, pos: u8) -> bool { - // NOTE(unsafe) atomic read with no side effects - self.idr.read().bits() & (1 << pos) == 0 - } +impl marker::Interruptible for Output {} +impl marker::Interruptible for Input {} +impl marker::Readable for Input {} +impl marker::Readable for Output {} +impl marker::Interruptible for Alternate {} +impl marker::Readable for Alternate {} +impl marker::Active for Input {} +impl marker::OutputSpeed for Output {} +impl marker::OutputSpeed for Alternate {} +impl marker::Active for Output {} +impl marker::Active for Alternate {} +impl marker::NotAlt for Input {} +impl marker::NotAlt for Output {} +impl marker::NotAlt for Analog {} - fn is_set_low(&self, pos: u8) -> bool { - // NOTE(unsafe) atomic read with no side effects - self.odr.read().bits() & (1 << pos) == 0 - } +/// GPIO Pin speed selection +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Speed { + /// Low speed + Low = 0, + /// Medium speed + Medium = 1, + /// High speed + High = 2, + /// Very high speed + VeryHigh = 3, +} - fn set_high(&self, pos: u8) { - // NOTE(unsafe) atomic write to a stateless register - unsafe { self.bsrr.write(|w| w.bits(1 << pos)) } - } +/// GPIO interrupt trigger edge selection +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum SignalEdge { + /// Rising edge of voltage + Rising, + /// Falling edge of voltage + Falling, + /// Rising and falling edge of voltage + All, +} - fn set_low(&self, pos: u8) { - // NOTE(unsafe) atomic write to a stateless register - unsafe { self.bsrr.write(|w| w.bits(1 << (pos + 16))) } - } - } +macro_rules! af { + ($($i:literal: $AFi:ident),+) => { + $( + #[doc = concat!("Alternate function ", $i, " (type state)" )] + pub type $AFi = Alternate<$i, Otype>; + )+ }; } -gpio_trait!(gpioa); -gpio_trait!(gpiob); +af!( + 0: AF0, + 1: AF1, + 2: AF2, + 3: AF3, + 4: AF4, + 5: AF5, + 6: AF6, + 7: AF7, + 8: AF8, + 9: AF9, + 10: AF10, + 11: AF11, + 12: AF12, + 13: AF13, + 14: AF14, + 15: AF15 +); + +/// Generic pin type +/// +/// - `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section). +/// - `P` is port name: `A` for GPIOA, `B` for GPIOB, etc. +/// - `N` is pin number: from `0` to `15`. +pub struct Pin { + _mode: PhantomData, +} +impl Pin { + const fn new() -> Self { + Self { _mode: PhantomData } + } +} -// NOTE(unsafe) The only write acess is to BSRR, which is thread safe -unsafe impl Sync for Pin {} -// NOTE(unsafe) this only enables read access to the same pin from multiple -// threads -unsafe impl Send for Pin {} +impl fmt::Debug for Pin { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_fmt(format_args!( + "P{}{}<{}>", + P, + N, + crate::stripped_type_name::() + )) + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for Pin { + fn format(&self, f: defmt::Formatter) { + defmt::write!(f, "P{}{}<{}>", P, N, crate::stripped_type_name::()); + } +} -impl StatefulOutputPin for Pin> { +impl PinExt for Pin { + type Mode = MODE; + + #[inline(always)] + fn pin_id(&self) -> u8 { + N + } #[inline(always)] - fn is_set_high(&self) -> Result { - self.is_set_low().map(|v| !v) + fn port_id(&self) -> u8 { + P as u8 - b'A' } +} + +pub trait PinSpeed: Sized { + /// Set pin speed + fn set_speed(&mut self, speed: Speed); #[inline(always)] - fn is_set_low(&self) -> Result { - Ok(unsafe { (*self.port).is_set_low(self.i) }) + fn speed(mut self, speed: Speed) -> Self { + self.set_speed(speed); + self } } -impl OutputPin for Pin> { - type Error = Infallible; +pub trait PinPull: Sized { + /// Set the internal pull-up and pull-down resistor + fn set_internal_resistor(&mut self, resistor: Pull); #[inline(always)] - fn set_high(&mut self) -> Result<(), Self::Error> { - unsafe { (*self.port).set_high(self.i) }; - Ok(()) + fn internal_resistor(mut self, resistor: Pull) -> Self { + self.set_internal_resistor(resistor); + self } +} +impl PinSpeed for Pin +where + MODE: marker::OutputSpeed, +{ #[inline(always)] - fn set_low(&mut self) -> Result<(), Self::Error> { - unsafe { (*self.port).set_low(self.i) } - Ok(()) + fn set_speed(&mut self, speed: Speed) { + self.set_speed(speed) } } -impl toggleable::Default for Pin> {} +impl Pin +where + MODE: marker::OutputSpeed, +{ + /// Set pin speed + pub fn set_speed(&mut self, speed: Speed) { + let offset = 2 * { N }; + + unsafe { + (*Gpio::

::ptr()) + .ospeedr + .modify(|r, w| w.bits((r.bits() & !(0b11 << offset)) | ((speed as u32) << offset))); + } + } -impl InputPin for Pin> { - type Error = Infallible; + /// Set pin speed + pub fn speed(mut self, speed: Speed) -> Self { + self.set_speed(speed); + self + } +} +impl PinPull for Pin +where + MODE: marker::Active, +{ #[inline(always)] - fn is_high(&self) -> Result { - self.is_low().map(|v| !v) + fn set_internal_resistor(&mut self, resistor: Pull) { + self.set_internal_resistor(resistor) + } +} + +impl Pin +where + MODE: marker::Active, +{ + /// Set the internal pull-up and pull-down resistor + pub fn set_internal_resistor(&mut self, resistor: Pull) { + let offset = 2 * { N }; + let value = resistor as u32; + unsafe { + (*Gpio::

::ptr()) + .pupdr + .modify(|r, w| w.bits((r.bits() & !(0b11 << offset)) | (value << offset))); + } } + /// Set the internal pull-up and pull-down resistor + pub fn internal_resistor(mut self, resistor: Pull) -> Self { + self.set_internal_resistor(resistor); + self + } + + /// Enables / disables the internal pull up + pub fn internal_pull_up(self, on: bool) -> Self { + if on { + self.internal_resistor(Pull::Up) + } else { + self.internal_resistor(Pull::None) + } + } + + /// Enables / disables the internal pull down + pub fn internal_pull_down(self, on: bool) -> Self { + if on { + self.internal_resistor(Pull::Down) + } else { + self.internal_resistor(Pull::None) + } + } +} + +impl Pin { + /// Erases the pin number from the type + /// + /// This is useful when you want to collect the pins into an array where you + /// need all the elements to have the same type + pub fn erase_number(self) -> PartiallyErasedPin { + PartiallyErasedPin::new(N) + } + + /// Erases the pin number and the port from the type + /// + /// This is useful when you want to collect the pins into an array where you + /// need all the elements to have the same type + pub fn erase(self) -> ErasedPin { + ErasedPin::new(P as u8 - b'A', N) + } +} + +impl From> for PartiallyErasedPin { + /// Pin-to-partially erased pin conversion using the [`From`] trait. + /// + /// Note that [`From`] is the reciprocal of [`Into`]. + fn from(p: Pin) -> Self { + p.erase_number() + } +} + +impl From> for ErasedPin { + /// Pin-to-erased pin conversion using the [`From`] trait. + /// + /// Note that [`From`] is the reciprocal of [`Into`]. + fn from(p: Pin) -> Self { + p.erase() + } +} + +impl Pin { + /// Set the output of the pin regardless of its mode. + /// Primarily used to set the output value of the pin + /// before changing its mode to an output to avoid + /// a short spike of an incorrect value + #[inline(always)] + fn _set_state(&mut self, state: PinState) { + match state { + PinState::High => self._set_high(), + PinState::Low => self._set_low(), + } + } + #[inline(always)] + fn _set_high(&mut self) { + // NOTE(unsafe) atomic write to a stateless register + unsafe { (*Gpio::

::ptr()).bsrr.write(|w| w.bits(1 << N)) } + } + #[inline(always)] + fn _set_low(&mut self) { + // NOTE(unsafe) atomic write to a stateless register + unsafe { (*Gpio::

::ptr()).bsrr.write(|w| w.bits(1 << (16 + N))) } + } + #[inline(always)] + fn _is_set_low(&self) -> bool { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*Gpio::

::ptr()).odr.read().bits() & (1 << N) == 0 } + } #[inline(always)] - fn is_low(&self) -> Result { - Ok(unsafe { (*self.port).is_low(self.i) }) + fn _is_low(&self) -> bool { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*Gpio::

::ptr()).idr.read().bits() & (1 << N) == 0 } } } -impl InputPin for Pin> { - type Error = Infallible; +impl Pin> { + /// Drives the pin high + #[inline(always)] + pub fn set_high(&mut self) { + self._set_high() + } + + /// Drives the pin low + #[inline(always)] + pub fn set_low(&mut self) { + self._set_low() + } + + /// Is the pin in drive high or low mode? + #[inline(always)] + pub fn get_state(&self) -> PinState { + if self.is_set_low() { + PinState::Low + } else { + PinState::High + } + } + + /// Drives the pin high or low depending on the provided value + #[inline(always)] + pub fn set_state(&mut self, state: PinState) { + match state { + PinState::Low => self.set_low(), + PinState::High => self.set_high(), + } + } + + /// Is the pin in drive high mode? + #[inline(always)] + pub fn is_set_high(&self) -> bool { + !self.is_set_low() + } + /// Is the pin in drive low mode? #[inline(always)] - fn is_high(&self) -> Result { - self.is_low().map(|v| !v) + pub fn is_set_low(&self) -> bool { + self._is_set_low() } + /// Toggle pin output #[inline(always)] - fn is_low(&self) -> Result { - Ok(unsafe { (*self.port).is_low(self.i) }) + pub fn toggle(&mut self) { + if self.is_set_low() { + self.set_high() + } else { + self.set_low() + } } } -/// GPIO Pin speed selection -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum Speed { - Low = 0, - Medium = 1, - High = 2, - VeryHigh = 3, +pub trait ReadPin { + #[inline(always)] + fn is_high(&self) -> bool { + !self.is_low() + } + fn is_low(&self) -> bool; } -/// Trigger edge -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum SignalEdge { - Rising, - Falling, - All, +impl ReadPin for Pin +where + MODE: marker::Readable, +{ + #[inline(always)] + fn is_low(&self) -> bool { + self.is_low() + } } -#[allow(dead_code)] -pub(crate) enum AltFunction { - AF0 = 0, - AF1 = 1, - AF2 = 2, - AF3 = 3, - AF4 = 4, - AF5 = 5, - AF6 = 6, - AF7 = 7, - AF8 = 8, - AF9 = 9, - AF10 = 10, - AF11 = 11, - AF12 = 12, - AF13 = 13, - AF14 = 14, - AF15 = 15, +impl Pin +where + MODE: marker::Readable, +{ + /// Is the input pin high? + #[inline(always)] + pub fn is_high(&self) -> bool { + !self.is_low() + } + + /// Is the input pin low? + #[inline(always)] + pub fn is_low(&self) -> bool { + self._is_low() + } } macro_rules! gpio { - ($GPIOX:ident, $gpiox:ident, $PXx:ident, $Pxn:expr, [ - $($PXi:ident: ($pxi:ident, $i:expr),)+ + ($GPIOX:ident, $gpiox:ident, $PEPin:ident, $port_id:expr, $PXn:ident, [ + $($PXi:ident: ($pxi:ident, $i:expr, [$($A:literal),*] $(, $MODE:ty)?),)+ ]) => { /// GPIO pub mod $gpiox { - use core::convert::Infallible; - use core::marker::PhantomData; - use hal::digital::v2::{toggleable, InputPin, OutputPin, StatefulOutputPin}; - use crate::stm32::{EXTI, $GPIOX}; - use crate::exti::{ExtiExt, Event}; - use crate::rcc::{Enable, Rcc}; - use super::*; + use crate::pac::$GPIOX; + use crate::rcc::{Enable, Reset, Rcc}; /// GPIO parts pub struct Parts { $( - pub $pxi: $PXi, + /// Pin + pub $pxi: $PXi $(<$MODE>)?, )+ } - impl GpioExt for $GPIOX { + impl super::GpioExt for $GPIOX { type Parts = Parts; - fn split(self, rcc: &mut Rcc) -> Parts { + fn split(self, rcc: &mut Rcc) -> Self::Parts { <$GPIOX>::enable(rcc); + <$GPIOX>::reset(rcc); Parts { $( - $pxi: $PXi { _mode: PhantomData }, + $pxi: $PXi::new(), )+ } } } - /// Partially erased pin - pub struct $PXx { - i: u8, - _mode: PhantomData, - } - - impl OutputPin for $PXx> { - type Error = Infallible; - - fn set_high(&mut self) -> Result<(), Self::Error> { - // NOTE(unsafe) atomic write to a stateless register - unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << self.i)) }; - Ok(()) - } - - fn set_low(&mut self) -> Result<(), Self::Error> { - // NOTE(unsafe) atomic write to a stateless register - unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << (self.i + 16))) }; - Ok(()) - } - } - - impl StatefulOutputPin for $PXx> { - fn is_set_high(&self) -> Result { - let is_set_high = !self.is_set_low()?; - Ok(is_set_high) - } - - fn is_set_low(&self) -> Result { - // NOTE(unsafe) atomic read with no side effects - let is_set_low = unsafe { (*$GPIOX::ptr()).odr.read().bits() & (1 << self.i) == 0 }; - Ok(is_set_low) - } - } - - impl toggleable::Default for $PXx> { - } - - impl InputPin for $PXx> { - type Error = Infallible; - - fn is_high(&self) -> Result { - let is_high = !self.is_low()?; - Ok(is_high) - } - - fn is_low(&self) -> Result { - // NOTE(unsafe) atomic read with no side effects - let is_low = unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << self.i) == 0 }; - Ok(is_low) - } - } - - impl InputPin for $PXx> { - type Error = Infallible; - - fn is_high(&self) -> Result { - let is_high = !self.is_low()?; - Ok(is_high) - } - - fn is_low(&self) -> Result { - // NOTE(unsafe) atomic read with no side effects - let is_low = unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << self.i) == 0 }; - Ok(is_low) - } - } + #[doc="Common type for "] + #[doc=stringify!($GPIOX)] + #[doc=" related pins"] + pub type $PXn = super::PartiallyErasedPin<$port_id, MODE>; $( - pub struct $PXi { - _mode: PhantomData, - } - - #[allow(clippy::from_over_into)] - impl Into<$PXi>> for $PXi { - fn into(self) -> $PXi> { - self.into_pull_down_input() - } - } - - #[allow(clippy::from_over_into)] - impl Into<$PXi>> for $PXi { - fn into(self) -> $PXi> { - self.into_pull_up_input() - } - } - - #[allow(clippy::from_over_into)] - impl Into<$PXi>> for $PXi { - fn into(self) -> $PXi> { - self.into_floating_input() - } - } - - #[allow(clippy::from_over_into)] - impl Into<$PXi>> for $PXi { - fn into(self) -> $PXi> { - self.into_open_drain_output() - } - } - - #[allow(clippy::from_over_into)] - impl Into<$PXi>> for $PXi { - fn into(self) -> $PXi> { - self.into_push_pull_output() - } - } - - impl $PXi { - /// Configures the pin to operate as a floating input pin - pub fn into_floating_input(self) -> $PXi> { - let offset = 2 * $i; - unsafe { - let gpio = &(*$GPIOX::ptr()); - gpio.pupdr.modify(|r, w| { - w.bits(r.bits() & !(0b11 << offset)) - }); - gpio.moder.modify(|r, w| { - w.bits(r.bits() & !(0b11 << offset)) - }) - }; - $PXi { _mode: PhantomData } - } - - /// Configures the pin to operate as a pulled down input pin - pub fn into_pull_down_input(self) -> $PXi> { - let offset = 2 * $i; - unsafe { - let gpio = &(*$GPIOX::ptr()); - gpio.pupdr.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b10 << offset)) - }); - gpio.moder.modify(|r, w| { - w.bits(r.bits() & !(0b11 << offset)) - }) - }; - $PXi { _mode: PhantomData } - } - - /// Configures the pin to operate as a pulled up input pin - pub fn into_pull_up_input(self) -> $PXi> { - let offset = 2 * $i; - unsafe { - let gpio = &(*$GPIOX::ptr()); - gpio.pupdr.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset)) - }); - gpio.moder.modify(|r, w| { - w.bits(r.bits() & !(0b11 << offset)) - }) - }; - $PXi { _mode: PhantomData } - } + #[doc=stringify!($PXi)] + #[doc=" pin"] + pub type $PXi = super::Pin<$port_id, $i, MODE>; - /// Configures the pin to operate as an analog pin - pub fn into_analog(self) -> $PXi { - let offset = 2 * $i; - unsafe { - let gpio = &(*$GPIOX::ptr()); - gpio.pupdr.modify(|r, w| { - w.bits(r.bits() & !(0b11 << offset)) - }); - gpio.moder.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b11 << offset)) - }); - } - $PXi { _mode: PhantomData } - } - - /// Configures the pin to operate as an open drain output - /// pin with `initial_state` specifying whether the pin - /// should initially be high or low - pub fn into_open_drain_output_in_state(mut self, initial_state: PinState) -> $PXi> { - self.internal_set_state(initial_state); - self.into_open_drain_output() - } - - /// Configures the pin to operate as an open drain output pin - pub fn into_open_drain_output(self) -> $PXi> { - let offset = 2 * $i; - unsafe { - let gpio = &(*$GPIOX::ptr()); - gpio.pupdr.modify(|r, w| { - w.bits(r.bits() & !(0b11 << offset)) - }); - gpio.otyper.modify(|r, w| { - w.bits(r.bits() | (0b1 << $i)) - }); - gpio.moder.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset)) - }) - }; - $PXi { _mode: PhantomData } - } - - /// Configures the pin to operate as a push pull output pin - /// with `initial_state` specifying whether the pin should - /// initially be high or low - pub fn into_push_pull_output_in_state(mut self, initial_state: PinState) -> $PXi> { - self.internal_set_state(initial_state); - self.into_push_pull_output() - } - - /// Configures the pin to operate as a push pull output pin - pub fn into_push_pull_output(self) -> $PXi> { - let offset = 2 * $i; - unsafe { - let gpio = &(*$GPIOX::ptr()); - gpio.pupdr.modify(|r, w| { - w.bits(r.bits() & !(0b11 << offset)) - }); - gpio.otyper.modify(|r, w| { - w.bits(r.bits() & !(0b1 << $i)) - }); - gpio.moder.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset)) - }) - }; - $PXi { _mode: PhantomData } - } - - /// Configures the pin as external trigger - pub fn listen(self, edge: SignalEdge, exti: &mut EXTI) -> $PXi> { - let offset = 2 * $i; - unsafe { - let _ = &(*$GPIOX::ptr()).pupdr.modify(|r, w| { - w.bits(r.bits() & !(0b11 << offset)) - }); - &(*$GPIOX::ptr()).moder.modify(|r, w| { - w.bits(r.bits() & !(0b11 << offset)) - }) - }; - let offset = ($i % 4) * 8; - let mask = $Pxn << offset; - let reset = !(0xff << offset); - match $i as u8 { - 0..=3 => exti.exticr1.modify(|r, w| unsafe { - w.bits(r.bits() & reset | mask) - }), - 4..=7 => exti.exticr2.modify(|r, w| unsafe { - w.bits(r.bits() & reset | mask) - }), - 8..=11 => exti.exticr3.modify(|r, w| unsafe { - w.bits(r.bits() & reset | mask) - }), - 12..=16 => exti.exticr4.modify(|r, w| unsafe { - w.bits(r.bits() & reset | mask) - }), - _ => unreachable!(), - } - exti.listen(Event::from_code($i), edge); - $PXi { _mode: PhantomData } - } - - /// Set pin speed - pub fn set_speed(self, speed: Speed) -> Self { - let offset = 2 * $i; - unsafe { - &(*$GPIOX::ptr()).ospeedr.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | ((speed as u32) << offset)) - }) - }; - self - } - - #[allow(dead_code)] - pub(crate) fn set_alt_mode(&self, mode: AltFunction) { - let mode = mode as u32; - let offset = 2 * $i; - let offset2 = 4 * $i; - unsafe { - let gpio = &(*$GPIOX::ptr()); - if offset2 < 32 { - gpio.afrl.modify(|r, w| { - w.bits((r.bits() & !(0b1111 << offset2)) | (mode << offset2)) - }); - } else { - let offset2 = offset2 - 32; - gpio.afrh.modify(|r, w| { - w.bits((r.bits() & !(0b1111 << offset2)) | (mode << offset2)) - }); - } - gpio.moder.modify(|r, w| { - w.bits((r.bits() & !(0b11 << offset)) | (0b10 << offset)) - }); - } - } - - fn internal_set_state(&mut self, state: PinState) { - match state { - PinState::High => { - // NOTE(unsafe) atomic write to a stateless register - unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << $i)) }; - } - PinState::Low => { - // NOTE(unsafe) atomic write to a stateless register - unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << ($i + 16))) }; - } - } - } - } - - impl $PXi> { - /// Erases the pin number from the type - /// - /// This is useful when you want to collect the pins into an array where you - /// need all the elements to have the same type - pub fn downgrade(self) -> $PXx> { - $PXx { i: $i, _mode: self._mode } - } - } - - impl OutputPin for $PXi> { - type Error = Infallible; - - fn set_high(&mut self) -> Result<(), Self::Error> { - self.internal_set_state(PinState::High); - Ok(()) - } - - fn set_low(&mut self) -> Result<(), Self::Error>{ - self.internal_set_state(PinState::Low); - Ok(()) - } - } - - impl StatefulOutputPin for $PXi> { - fn is_set_high(&self) -> Result { - let is_set_high = !self.is_set_low()?; - Ok(is_set_high) - } - - fn is_set_low(&self) -> Result { - // NOTE(unsafe) atomic read with no side effects - let is_set_low = unsafe { (*$GPIOX::ptr()).odr.read().bits() & (1 << $i) == 0 }; - Ok(is_set_low) - } - } - - impl toggleable::Default for $PXi> { - } - - impl InputPin for $PXi> { - type Error = Infallible; - - fn is_high(&self) -> Result { - let is_high = !self.is_low()?; - Ok(is_high) - } - - fn is_low(&self) -> Result { - // NOTE(unsafe) atomic read with no side effects - let is_low = unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << $i) == 0 }; - Ok(is_low) - } - } - - impl $PXi> { - /// Erases the pin number from the type - /// - /// This is useful when you want to collect the pins into an array where you - /// need all the elements to have the same type - pub fn downgrade(self) -> $PXx> { - $PXx { i: $i, _mode: self._mode } - } - } - - impl InputPin for $PXi> { - type Error = Infallible; - - fn is_high(&self) -> Result { - let is_high = !self.is_low()?; - Ok(is_high) - } - - fn is_low(&self) -> Result { - // NOTE(unsafe) atomic read with no side effects - let is_low = unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << $i) == 0 }; - Ok(is_low) - } - } + $( + impl super::marker::IntoAf<$A> for $PXi { } + )* )+ - impl $PXx { - pub fn get_id (&self) -> u8 { - self.i - } - } - - // impl $PXx> { - // /// Erases the port number from the type - // /// - // /// This is useful when you want to collect the pins into an array where you - // /// need all the elements to have the same type - // pub fn downgrade(self) -> Pin> { - // Pin { - // i: self.get_id(), - // port: $GPIOX::ptr() as *const dyn GpioRegExt, - // _mode: self._mode, - // } - // } - // } - - // impl $PXx> { - // /// Erases the port number from the type - // /// - // /// This is useful when you want to collect the pins into an array where you - // /// need all the elements to have the same type - // pub fn downgrade(self) -> Pin> { - // Pin { - // i: self.get_id(), - // port: $GPIOX::ptr() as *const dyn GpioRegExt, - // _mode: self._mode, - // } - // } - // } } pub use $gpiox::{ $($PXi,)+ }; } } - -gpio!(GPIOA, gpioa, PA, 0, [ - PA0: (pa0, 0), - PA1: (pa1, 1), - PA2: (pa2, 2), - PA3: (pa3, 3), - PA4: (pa4, 4), - PA5: (pa5, 5), - PA6: (pa6, 6), - PA7: (pa7, 7), - PA8: (pa8, 8), - PA9: (pa9, 9), - PA10: (pa10, 10), - PA11: (pa11, 11), - PA12: (pa12, 12), - PA13: (pa13, 13), - PA14: (pa14, 14), - PA15: (pa15, 15), -]); - -gpio!(GPIOB, gpiob, PB, 1, [ - PB0: (pb0, 0), - PB1: (pb1, 1), - PB2: (pb2, 2), - PB3: (pb3, 3), - PB4: (pb4, 4), - PB5: (pb5, 5), - PB6: (pb6, 6), - PB7: (pb7, 7), - PB8: (pb8, 8), - PB9: (pb9, 9), - PB10: (pb10, 10), - PB11: (pb11, 11), - PB12: (pb12, 12), - PB13: (pb13, 13), - PB14: (pb14, 14), - PB15: (pb15, 15), -]); - -gpio!(GPIOC, gpioc, PC, 2, [ - PC0: (pc0, 0), - PC1: (pc1, 1), - PC2: (pc2, 2), - PC3: (pc3, 3), - PC4: (pc4, 4), - PC5: (pc5, 5), - PC6: (pc6, 6), - PC7: (pc7, 7), - PC8: (pc8, 8), - PC9: (pc9, 9), - PC10: (pc10, 10), - PC11: (pc11, 11), - PC12: (pc12, 12), - PC13: (pc13, 13), - PC14: (pc14, 14), - PC15: (pc15, 15), -]); - -gpio!(GPIOD, gpiod, PD, 3, [ - PD0: (pd0, 0), - PD1: (pd1, 1), - PD2: (pd2, 2), - PD3: (pd3, 3), - PD4: (pd4, 4), - PD5: (pd5, 5), - PD6: (pd6, 6), - PD7: (pd7, 7), - PD8: (pd8, 8), - PD9: (pd9, 9), - PD10: (pd10, 10), - PD11: (pd11, 11), - PD12: (pd12, 12), - PD13: (pd13, 13), - PD14: (pd14, 14), - PD15: (pd15, 15), -]); - -gpio!(GPIOF, gpiof, PF, 5, [ - PF0: (pf0, 0), - PF1: (pf1, 1), - PF2: (pf2, 2), - PF3: (pf3, 3), - PF4: (pf4, 4), - PF5: (pf5, 5), - PF6: (pf6, 6), - PF7: (pf7, 7), - PF8: (pf8, 8), - PF9: (pf9, 9), - PF10: (pf10, 10), - PF11: (pf11, 11), - PF12: (pf12, 12), - PF13: (pf13, 13), - PF14: (pf14, 14), - PF15: (pf15, 15), -]); +use gpio; + +mod c0; +pub use c0::*; + +struct Gpio; +impl Gpio

{ + const fn ptr() -> *const crate::pac::gpioa::RegisterBlock { + match P { + 'A' => crate::pac::GPIOA::ptr(), + 'B' => crate::pac::GPIOB::ptr() as _, + 'C' => crate::pac::GPIOC::ptr() as _, + #[cfg(feature = "gpiod")] + 'D' => crate::pac::GPIOD::ptr() as _, + #[cfg(feature = "gpioe")] + 'E' => crate::pac::GPIOE::ptr() as _, + #[cfg(feature = "gpiof")] + 'F' => crate::pac::GPIOF::ptr() as _, + #[cfg(feature = "gpiog")] + 'G' => crate::pac::GPIOG::ptr() as _, + #[cfg(feature = "gpioh")] + 'H' => crate::pac::GPIOH::ptr() as _, + #[cfg(feature = "gpioi")] + 'I' => crate::pac::GPIOI::ptr() as _, + #[cfg(feature = "gpioj")] + 'J' => crate::pac::GPIOJ::ptr() as _, + #[cfg(feature = "gpiok")] + 'K' => crate::pac::GPIOK::ptr() as _, + _ => panic!("Unknown GPIO port"), + } + } +} diff --git a/src/gpio/alt.rs b/src/gpio/alt.rs new file mode 100644 index 0000000..d1c3dbd --- /dev/null +++ b/src/gpio/alt.rs @@ -0,0 +1,371 @@ +mod c0; +pub use c0::*; + +macro_rules! pin { + ( $($(#[$docs:meta])* <$name:ident, $Otype:ident> for $(no: $NoPin:ident,)? [$( + $(#[$attr:meta])* $PX:ident<$A:literal $(, Speed::$Speed:ident)?>, + )*],)*) => { + $( + #[derive(Debug)] + $(#[$docs])* + pub enum $name { + $( + None($NoPin<$Otype>), + )? + + $( + $(#[$attr])* + $PX(gpio::$PX<$crate::gpio::Alternate<$A, $Otype>>), + )* + } + + impl crate::Sealed for $name { } + + #[allow(unreachable_patterns)] + impl $crate::gpio::ReadPin for $name { + fn is_low(&self) -> bool { + match self { + $( + $(#[$attr])* + Self::$PX(p) => p.is_low(), + )* + _ => false, + } + } + } + + #[allow(unreachable_patterns)] + impl $crate::gpio::PinSpeed for $name { + fn set_speed(&mut self, _speed: $crate::gpio::Speed) { + match self { + $( + $(#[$attr])* + Self::$PX(p) => p.set_speed(_speed), + )* + _ => {} + } + } + } + + #[allow(unreachable_patterns)] + impl $crate::gpio::PinPull for $name { + fn set_internal_resistor(&mut self, _pull: $crate::gpio::Pull) { + match self { + $( + $(#[$attr])* + Self::$PX(p) => p.set_internal_resistor(_pull), + )* + _ => {} + } + } + } + + /*#[allow(unreachable_patterns)] + impl $crate::gpio::ExtiPin for $name { + extipin! { $( $(#[$attr])* $PX, )* } + }*/ + + $( + impl From<$NoPin<$Otype>> for $name { + fn from(p: $NoPin<$Otype>) -> Self { + Self::None(p) + } + } + )? + + $( + $(#[$attr])* + impl From> for $name + where + MODE: $crate::gpio::marker::NotAlt + $crate::gpio::PinMode + { + fn from(p: gpio::$PX) -> Self { + Self::$PX(p.into_mode() $(.speed($crate::gpio::Speed::$Speed))?) + } + } + + $(#[$attr])* + impl From>> for $name { + fn from(p: gpio::$PX<$crate::gpio::Alternate<$A, $Otype>>) -> Self { + Self::$PX(p $(.speed($crate::gpio::Speed::$Speed))?) + } + } + + $(#[$attr])* + #[allow(irrefutable_let_patterns)] + impl TryFrom<$name> for gpio::$PX + where + MODE: $crate::gpio::PinMode, + $crate::gpio::Alternate<$A, $Otype>: $crate::gpio::PinMode, + { + type Error = (); + + fn try_from(a: $name) -> Result { + if let $name::$PX(p) = a { + Ok(p.into_mode()) + } else { + Err(()) + } + } + } + )* + )* + }; + + ( $($(#[$docs:meta])* <$name:ident> default:$DefaultOtype:ident for $(no: $NoPin:ident,)? [$( + $(#[$attr:meta])* $PX:ident<$A:literal>, + )*],)*) => { + $( + #[derive(Debug)] + $(#[$docs])* + pub enum $name { + $( + None($NoPin), + )? + + $( + $(#[$attr])* + $PX(gpio::$PX<$crate::gpio::Alternate<$A, Otype>>), + )* + } + + impl crate::Sealed for $name { } + + #[allow(unreachable_patterns)] + impl $crate::gpio::ReadPin for $name { + fn is_low(&self) -> bool { + match self { + $( + $(#[$attr])* + Self::$PX(p) => p.is_low(), + )* + _ => false, + } + } + } + + #[allow(unreachable_patterns)] + impl $crate::gpio::PinSpeed for $name { + fn set_speed(&mut self, _speed: $crate::gpio::Speed) { + match self { + $( + $(#[$attr])* + Self::$PX(p) => p.set_speed(_speed), + )* + _ => {} + } + } + } + + #[allow(unreachable_patterns)] + impl $crate::gpio::PinPull for $name { + fn set_internal_resistor(&mut self, _pull: $crate::gpio::Pull) { + match self { + $( + $(#[$attr])* + Self::$PX(p) => p.set_internal_resistor(_pull), + )* + _ => {} + } + } + } + + /*#[allow(unreachable_patterns)] + impl $crate::gpio::ExtiPin for $name { + extipin! { $( $(#[$attr])* $PX, )* } + }*/ + + $( + impl From<$NoPin> for $name { + fn from(p: $NoPin) -> Self { + Self::None(p) + } + } + )? + + $( + $(#[$attr])* + impl From> for $name + where + MODE: $crate::gpio::marker::NotAlt + $crate::gpio::PinMode, + $crate::gpio::Alternate<$A, Otype>: $crate::gpio::PinMode, + { + fn from(p: gpio::$PX) -> Self { + Self::$PX(p.into_mode()) + } + } + + $(#[$attr])* + impl From>> for $name { + fn from(p: gpio::$PX<$crate::gpio::Alternate<$A, Otype>>) -> Self { + Self::$PX(p) + } + } + + $(#[$attr])* + #[allow(irrefutable_let_patterns)] + impl TryFrom<$name> for gpio::$PX + where + MODE: $crate::gpio::PinMode, + $crate::gpio::Alternate<$A, Otype>: $crate::gpio::PinMode, + { + type Error = (); + + fn try_from(a: $name) -> Result { + if let $name::$PX(p) = a { + Ok(p.into_mode()) + } else { + Err(()) + } + } + } + )* + )* + }; +} +use pin; + +// CAN pins +#[cfg(feature = "can1")] +pub trait CanCommon { + type Rx; + type Tx; +} + +// DFSDM pins +#[cfg(feature = "dfsdm")] +pub trait DfsdmBasic { + type Ckin0; + type Ckin1; + type Ckout; + type Datin0; + type Datin1; +} +#[cfg(feature = "dfsdm")] +pub trait DfsdmGeneral: DfsdmBasic { + type Ckin2; + type Ckin3; + type Datin2; + type Datin3; +} +#[cfg(feature = "dfsdm")] +pub trait DfsdmAdvanced: DfsdmGeneral { + type Ckin4; + type Ckin5; + type Ckin6; + type Ckin7; + type Datin4; + type Datin5; + type Datin6; + type Datin7; +} + +// Serial pins +pub trait SerialAsync { + /// Receive + type Rx; + /// Transmit + type Tx; +} +/// Synchronous mode +pub trait SerialSync { + type Ck; +} +pub trait SerialRs485 { + type De; +} +/// Hardware flow control (RS232) +pub trait SerialRs232 { + /// Receive + type Cts; + /// Transmit + type Rts; +} + +// I2C pins +pub trait I2cCommon { + type Scl; + type Sda; + type Smba; +} + +// I2S pins +pub trait I2sCommon { + type Ck: crate::gpio::PinSpeed; + type Sd; + type Ws: crate::gpio::ReadPin; +} +pub trait I2sMaster { + type Mck; +} +pub trait I2sExtPin { + type ExtSd; +} + +// QuadSPI pins + +#[cfg(feature = "quadspi")] +pub trait QuadSpiBanks { + type Bank1; + type Bank2; +} +#[cfg(feature = "quadspi")] +pub trait QuadSpiBank { + type Io0: crate::gpio::PinSpeed; + type Io1: crate::gpio::PinSpeed; + type Io2: crate::gpio::PinSpeed; + type Io3: crate::gpio::PinSpeed; + type Ncs: crate::gpio::PinSpeed; +} + +// SAI pins + +#[cfg(feature = "sai1")] +pub trait SaiChannels { + type A; + type B; +} +#[cfg(feature = "sai1")] +pub trait SaiChannel { + type Fs; + type Mclk; + type Sck; + type Sd; +} + +// SPDIFRX pins + +#[cfg(feature = "spdifrx")] +pub trait SPdifIn { + type In; +} + +// SPI pins +pub trait SpiCommon { + type Miso; + type Mosi; + type Nss; + type Sck; +} + +// Timer pins + +/// Input capture / Output compare channel `C` +pub trait TimCPin { + type Ch; +} + +/// Complementary output channel `C` +pub trait TimNCPin { + type ChN; +} + +/// Break input +pub trait TimBkin { + type Bkin; +} + +/// External trigger timer input +pub trait TimEtr { + type Etr; +} diff --git a/src/gpio/alt/c0.rs b/src/gpio/alt/c0.rs new file mode 100644 index 0000000..452bbd2 --- /dev/null +++ b/src/gpio/alt/c0.rs @@ -0,0 +1,821 @@ +use super::*; +use crate::gpio::{self, NoPin, OpenDrain, PushPull}; + +pub mod debug { + use super::*; + + pin! { + for [ + PA14<0>, + ], + + for [ + PA13<0>, + ], + + } +} + +pub mod i2c1 { + use super::*; + + pin! { + for [ + PA9<6>, + + PB6<6>, + + PB7<14>, + + #[cfg(feature = "gpio-c0xx_453")] + PB8<6>, + ], + + for [ + PA10<6>, + + PB7<6>, + + #[cfg(feature = "gpio-c0xx_453")] + PB9<6>, + + PC14<14>, + ], + + for [ + PA1<6>, + + #[cfg(feature = "gpio-c0xx_453")] + PB5<6>, + + PB6<7>, + ], + + } + use crate::pac::I2C; + impl I2cCommon for I2C { + type Scl = Scl; + type Sda = Sda; + type Smba = Smba; + } +} + +pub mod i2s { + use super::*; + + pin! { + for [ + PA12<5>, + ], + + } +} + +pub mod i2s1 { + use super::*; + + pin! { + for [ + PA1<0>, + + PA5<0>, + + #[cfg(feature = "gpio-c0xx_453")] + PB3<0>, + + PB6<10>, + ], + + for no:NoPin, [ + PA6<0>, + + PA11<0>, + + #[cfg(feature = "gpio-c0xx_453")] + PB4<0>, + + PB6<9>, + ], + + for [ + PA2<0>, + + PA7<0>, + + PA12<0>, + + #[cfg(feature = "gpio-c0xx_453")] + PB5<0>, + + PB6<8>, + ], + + for [ + PA4<0>, + + PA8<8>, + + PA14<8>, + + #[cfg(feature = "gpio-c0xx_453")] + PA15<0>, + + #[cfg(feature = "gpio-c0xx_453")] + PB0<0>, + ], + + } +} + +pub mod ir { + use super::*; + + pin! { + default:PushPull for [ + PA13<1>, + + #[cfg(feature = "gpio-c0xx_453")] + PB9<0>, + + PC14<8>, + ], + + } +} + +pub mod rcc { + use super::*; + + pin! { + for [ + PA8<0>, + + PA9<0>, + + PF2<0>, + ], + + for [ + PA8<15>, + + PA10<3>, + + PA14<11>, + + #[cfg(feature = "gpio-c0xx_453")] + PA15<3>, + + #[cfg(feature = "gpio-c0xx_453")] + PB2<3>, + ], + + for [ + PC15<0>, + ], + + for [ + PC15<1>, + + #[cfg(feature = "gpio-c0xx_453")] + PF1<0>, + ], + + } +} + +pub mod spi1 { + use super::*; + + pin! { + for no:NoPin, [ + PA6<0>, + + PA11<0>, + + #[cfg(feature = "gpio-c0xx_453")] + PB4<0>, + + PB6<9>, + ], + + for no:NoPin, [ + PA2<0>, + + PA7<0>, + + PA12<0>, + + #[cfg(feature = "gpio-c0xx_453")] + PB5<0>, + + PB6<8>, + ], + + for [ + PA4<0>, + + PA8<8>, + + PA14<8>, + + #[cfg(feature = "gpio-c0xx_453")] + PA15<0>, + + #[cfg(feature = "gpio-c0xx_453")] + PB0<0>, + ], + + for no:NoPin, [ + PA1<0>, + + PA5<0>, + + #[cfg(feature = "gpio-c0xx_453")] + PB3<0>, + + PB6<10>, + ], + + } + impl SpiCommon for crate::pac::SPI { + type Miso = Miso; + type Mosi = Mosi; + type Nss = Nss; + type Sck = Sck; + } +} + +pub mod tim1 { + use super::*; + + pin! { + for [ + PA6<2>, + + #[cfg(feature = "gpio-c0xx_453")] + PB12<2>, + + #[cfg(feature = "gpio-c0xx_453")] + PC13<2>, + ], + + for [ + PA11<5>, + + #[cfg(feature = "gpio-c0xx_453")] + PB12<1>, + + PC14<2>, + ], + + for [ + PA12<2>, + + #[cfg(feature = "gpio-c0xx_453")] + PC13<1>, + + PC14<1>, + + PC15<2>, + ], + } + + pin! { + default:PushPull for [ + PA0<5>, + + PA5<5>, + + PA8<2>, + + PA14<10>, + + #[cfg(feature = "gpio-c0xx_453")] + PA15<2>, + ], + + default:PushPull for [ + PA3<2>, + + PA7<2>, + + #[cfg(feature = "gpio-c0xx_453")] + PB13<2>, + + #[cfg(feature = "gpio-c0xx_453")] + PD2<2>, + ], + + default:PushPull for [ + PA1<5>, + + PA9<2>, + + #[cfg(feature = "gpio-c0xx_453")] + PB3<1>, + + PB6<11>, + ], + + default:PushPull for [ + PA4<2>, + + PA8<9>, + + #[cfg(feature = "gpio-c0xx_453")] + PB0<2>, + + #[cfg(feature = "gpio-c0xx_453")] + PB1<5>, + + #[cfg(feature = "gpio-c0xx_453")] + PB14<2>, + + #[cfg(feature = "gpio-c0xx_453")] + PD3<2>, + ], + + default:PushPull for [ + PA2<5>, + + PA10<2>, + + PB6<1>, + ], + + default:PushPull for [ + PA5<2>, + + PA8<10>, + + #[cfg(feature = "gpio-c0xx_453")] + PB1<2>, + + #[cfg(feature = "gpio-c0xx_453")] + PB15<2>, + ], + + default:PushPull for [ + PA3<5>, + + PA11<2>, + + PB7<1>, + + PF2<1>, + ], + } + + use crate::pac::TIM1 as TIM; + impl TimCPin<0> for TIM { + type Ch = Ch1; + } + impl TimNCPin<0> for TIM { + type ChN = Ch1N; + } + impl TimCPin<1> for TIM { + type Ch = Ch2; + } + impl TimNCPin<1> for TIM { + type ChN = Ch2N; + } + impl TimCPin<2> for TIM { + type Ch = Ch3; + } + impl TimNCPin<2> for TIM { + type ChN = Ch3N; + } + impl TimCPin<3> for TIM { + type Ch = Ch4; + } + impl TimBkin for TIM { + type Bkin = Bkin; + } + impl TimEtr for TIM { + type Etr = Etr; + } +} + +pub mod tim3 { + use super::*; + + pin! { + default:PushPull for [ + PA6<1>, + + #[cfg(feature = "gpio-c0xx_453")] + PB4<1>, + + PB6<12>, + + PB7<11>, + + #[cfg(feature = "gpio-c0xx_453")] + PB8<3>, + + #[cfg(feature = "gpio-c0xx_453")] + PC6<1>, + ], + + default:PushPull for [ + PA7<1>, + + #[cfg(feature = "gpio-c0xx_453")] + PB3<3>, + + #[cfg(feature = "gpio-c0xx_453")] + PB5<1>, + + PB6<13>, + + #[cfg(feature = "gpio-c0xx_453")] + PB9<3>, + + #[cfg(feature = "gpio-c0xx_453")] + PC7<1>, + + PC14<11>, + ], + + default:PushPull for [ + PA8<11>, + + #[cfg(feature = "gpio-c0xx_453")] + PB0<1>, + + #[cfg(feature = "gpio-c0xx_453")] + PB5<3>, + + PB6<3>, + + PC15<3>, + ], + + default:PushPull for [ + PA8<12>, + + #[cfg(feature = "gpio-c0xx_453")] + PB1<1>, + + PB7<3>, + ], + } + + pin! { + for [ + PA2<3>, + + PA9<3>, + + PA13<3>, + + #[cfg(feature = "gpio-c0xx_453")] + PD2<1>, + ], + + } + + use crate::pac::TIM3 as TIM; + impl TimCPin<0> for TIM { + type Ch = Ch1; + } + impl TimCPin<1> for TIM { + type Ch = Ch2; + } + impl TimCPin<2> for TIM { + type Ch = Ch3; + } + impl TimCPin<3> for TIM { + type Ch = Ch4; + } + impl TimEtr for TIM { + type Etr = Etr; + } +} + +pub mod tim14 { + use super::*; + + pin! { + default:PushPull for [ + PA4<4>, + + PA7<4>, + + PA8<13>, + + #[cfg(feature = "gpio-c0xx_453")] + PB1<0>, + + #[cfg(feature = "gpio-c0xx_453")] + PF0<2>, + ], + + } + + use crate::pac::TIM14 as TIM; + impl TimCPin<0> for TIM { + type Ch = Ch1; + } +} + +pub mod tim16 { + use super::*; + + pin! { + for [ + #[cfg(feature = "gpio-c0xx_453")] + PB5<2>, + + PB6<14>, + ], + } + + pin! { + default:PushPull for [ + PA0<2>, + + PA6<5>, + + PB7<10>, + + #[cfg(feature = "gpio-c0xx_453")] + PB8<2>, + + #[cfg(feature = "gpio-c0xx_453")] + PD0<2>, + ], + + default:PushPull for [ + PA2<2>, + + PB6<2>, + ], + + } + + use crate::pac::TIM16 as TIM; + impl TimCPin<0> for TIM { + type Ch = Ch1; + } + impl TimNCPin<0> for TIM { + type ChN = Ch1N; + } + impl TimBkin for TIM { + type Bkin = Bkin; + } +} + +pub mod tim17 { + use super::*; + + pin! { + for [ + PA10<5>, + + #[cfg(feature = "gpio-c0xx_453")] + PB4<5>, + + PB6<15>, + ], + } + + pin! { + default:PushPull for [ + PA1<2>, + + PA7<5>, + + #[cfg(feature = "gpio-c0xx_453")] + PB9<2>, + + PC14<10>, + + #[cfg(feature = "gpio-c0xx_453")] + PD1<2>, + ], + + default:PushPull for [ + PA4<5>, + + PB7<2>, + ], + + } + + use crate::pac::TIM17 as TIM; + impl TimCPin<0> for TIM { + type Ch = Ch1; + } + impl TimNCPin<0> for TIM { + type ChN = Ch1N; + } + impl TimBkin for TIM { + type Bkin = Bkin; + } +} + +pub mod usart1 { + use super::*; + + pin! { + for [ + PA12<1>, + + PA14<12>, + + #[cfg(feature = "gpio-c0xx_453")] + PA15<4>, + + #[cfg(feature = "gpio-c0xx_453")] + PB3<4>, + + PB6<4>, + ], + + for [ + PA11<1>, + + #[cfg(feature = "gpio-c0xx_453")] + PB4<4>, + + PB6<5>, + ], + + for [ + PA12<1>, + + PA14<12>, + + #[cfg(feature = "gpio-c0xx_453")] + PA15<4>, + + #[cfg(feature = "gpio-c0xx_453")] + PB3<4>, + + PB6<4>, + ], + + for [ + PA11<1>, + + #[cfg(feature = "gpio-c0xx_453")] + PB4<4>, + + PB6<5>, + ], + + for [ + PA12<1>, + + PA14<12>, + + #[cfg(feature = "gpio-c0xx_453")] + PA15<4>, + + #[cfg(feature = "gpio-c0xx_453")] + PB3<4>, + + PB6<4>, + ], + } + + pin! { + default:PushPull for no:NoPin, [ + PA1<4>, + + PA8<14>, + + PA10<1>, + + #[cfg(feature = "gpio-c0xx_453")] + PB2<0>, + + PB7<0>, + ], + + default:PushPull for no:NoPin, [ + PA0<4>, + + PA9<1>, + + PB6<0>, + + PC14<0>, + ], + + } + use crate::pac::USART1 as USART; + impl SerialAsync for USART { + type Rx = Rx; + type Tx = Tx; + } + impl SerialRs485 for USART { + type De = De; + } + impl SerialSync for USART { + type Ck = Ck; + } + impl SerialRs232 for USART { + type Cts = Cts; + type Rts = Rts; + } +} + +pub mod usart2 { + use super::*; + + pin! { + for [ + PA1<1>, + + #[cfg(feature = "gpio-c0xx_453")] + PB9<1>, + + PC14<9>, + ], + + for [ + PA0<1>, + + PB7<9>, + + #[cfg(feature = "gpio-c0xx_453")] + PB8<1>, + + #[cfg(feature = "gpio-c0xx_453")] + PD3<0>, + ], + + for [ + PA1<1>, + + #[cfg(feature = "gpio-c0xx_453")] + PB9<1>, + + PC14<9>, + ], + + for [ + PA0<1>, + + PB7<9>, + + #[cfg(feature = "gpio-c0xx_453")] + PB8<1>, + + #[cfg(feature = "gpio-c0xx_453")] + PD3<0>, + ], + + for [ + PA1<1>, + + #[cfg(feature = "gpio-c0xx_453")] + PB9<1>, + + PC14<9>, + ], + } + + pin! { + default:PushPull for no:NoPin, [ + PA3<1>, + + PA5<1>, + + PA13<4>, + + PA14<9>, + + #[cfg(feature = "gpio-c0xx_453")] + PA15<1>, + ], + + default:PushPull for no:NoPin, [ + PA2<1>, + + PA4<1>, + + PA8<1>, + + PA14<1>, + ], + + } + use crate::pac::USART2 as USART; + impl SerialAsync for USART { + type Rx = Rx; + type Tx = Tx; + } + impl SerialRs485 for USART { + type De = De; + } + impl SerialSync for USART { + type Ck = Ck; + } + impl SerialRs232 for USART { + type Cts = Cts; + type Rts = Rts; + } +} diff --git a/src/gpio/c0.rs b/src/gpio/c0.rs new file mode 100644 index 0000000..fae2d4f --- /dev/null +++ b/src/gpio/c0.rs @@ -0,0 +1,105 @@ +use super::*; + +pub use super::Analog as DefaultMode; + +#[cfg(feature = "gpio-c0xx_453")] +gpio!(GPIOA, gpioa, PA, 'A', PAn, [ + PA0: (pa0, 0, [1, 2, 4, 5]), + PA1: (pa1, 1, [0, 1, 2, 4, 5, 6, 7]), + PA2: (pa2, 2, [0, 1, 2, 3, 5]), + PA3: (pa3, 3, [1, 2, 5, 7]), + PA4: (pa4, 4, [0, 1, 2, 4, 5, 7]), + PA5: (pa5, 5, [0, 1, 2, 5, 7]), + PA6: (pa6, 6, [0, 1, 2, 5]), + PA7: (pa7, 7, [0, 1, 2, 4, 5]), + PA8: (pa8, 8, [0, 1, 2, 7, 8, 9, 10, 11, 12, 13, 14, 15]), + PA9: (pa9, 9, [0, 1, 2, 3, 6, 7]), + PA10: (pa10, 10, [1, 2, 3, 5, 6, 7]), + PA11: (pa11, 11, [0, 1, 2, 5]), + PA12: (pa12, 12, [0, 1, 2, 5]), + PA13: (pa13, 13, [0, 1, 3, 4, 7], super::Debugger), + PA14: (pa14, 14, [0, 1, 7, 8, 9, 10, 11, 12], super::Debugger), + PA15: (pa15, 15, [0, 1, 2, 3, 4, 7], super::Debugger), +]); + +#[cfg(feature = "gpio-c0xx_453")] +gpio!(GPIOB, gpiob, PB, 'B', PBn, [ + PB0: (pb0, 0, [0, 1, 2]), + PB1: (pb1, 1, [0, 1, 2, 5, 7]), + PB2: (pb2, 2, [0, 3, 7]), + PB3: (pb3, 3, [0, 1, 3, 4, 7], super::Debugger), + PB4: (pb4, 4, [0, 1, 4, 5, 7], super::Debugger), + PB5: (pb5, 5, [0, 1, 2, 3, 6]), + PB6: (pb6, 6, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), + PB7: (pb7, 7, [0, 1, 2, 3, 6, 7, 9, 10, 11, 14]), + PB8: (pb8, 8, [1, 2, 3, 6, 7]), + PB9: (pb9, 9, [0, 1, 2, 3, 6, 7]), + PB10: (pb10, 10, []), + PB11: (pb11, 11, []), + PB12: (pb12, 12, [1, 2, 7]), + PB13: (pb13, 13, [2, 7]), + PB14: (pb14, 14, [2, 7]), + PB15: (pb15, 15, [2, 7]), +]); + +#[cfg(feature = "gpio-c0xx_453")] +gpio!(GPIOC, gpioc, PC, 'C', PCn, [ + PC6: (pc6, 6, [1]), + PC7: (pc7, 7, [1]), + PC13: (pc13, 13, [1, 2]), + PC14: (pc14, 14, [0, 1, 2, 8, 9, 10, 11, 14, 15]), + PC15: (pc15, 15, [0, 1, 2, 3]), +]); + +#[cfg(feature = "gpio-c0xx_453")] +gpio!(GPIOD, gpiod, PD, 'D', PDn, [ + PD0: (pd0, 0, [0, 2]), + PD1: (pd1, 1, [0, 2]), + PD2: (pd2, 2, [1, 2]), + PD3: (pd3, 3, [0, 2]), +]); + +#[cfg(feature = "gpio-c0xx_453")] +gpio!(GPIOF, gpiof, PF, 'F', PFn, [ + PF0: (pf0, 0, [2]), + PF1: (pf1, 1, [0]), + PF2: (pf2, 2, [0, 1]), + PF3: (pf3, 3, []), +]); + +#[cfg(feature = "gpio-c0xx")] +gpio!(GPIOA, gpioa, PA, 'A', PAn, [ + PA0: (pa0, 0, [1, 2, 4, 5]), + PA1: (pa1, 1, [0, 1, 2, 4, 5, 6, 7]), + PA2: (pa2, 2, [0, 1, 2, 3, 5]), + PA3: (pa3, 3, [1, 2, 5, 7]), + PA4: (pa4, 4, [0, 1, 2, 4, 5, 7]), + PA5: (pa5, 5, [0, 1, 2, 5, 7]), + PA6: (pa6, 6, [0, 1, 2, 5]), + PA7: (pa7, 7, [0, 1, 2, 4, 5]), + PA8: (pa8, 8, [0, 1, 2, 7, 8, 9, 10, 11, 12, 13, 14, 15]), + PA9: (pa9, 9, [0, 1, 2, 3, 6, 7]), + PA10: (pa10, 10, [1, 2, 3, 5, 6, 7]), + PA11: (pa11, 11, [0, 1, 2, 5]), + PA12: (pa12, 12, [0, 1, 2, 5]), + PA13: (pa13, 13, [0, 1, 3, 4, 7], super::Debugger), + PA14: (pa14, 14, [0, 1, 7, 8, 9, 10, 11, 12], super::Debugger), +]); + +#[cfg(feature = "gpio-c0xx")] +gpio!(GPIOB, gpiob, PB, 'B', PBn, [ + PB6: (pb6, 6, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), + PB7: (pb7, 7, [0, 1, 2, 3, 6, 7, 9, 10, 11, 14]), +]); + +#[cfg(feature = "gpio-c0xx")] +gpio!(GPIOC, gpioc, PC, 'C', PCn, [ + PC13: (pc13, 13, []), + PC14: (pc14, 14, [0, 1, 2, 8, 9, 10, 11, 14, 15]), + PC15: (pc15, 15, [0, 1, 2, 3]), +]); + +#[cfg(feature = "gpio-c0xx")] +gpio!(GPIOF, gpiof, PF, 'F', PFn, [ + PF2: (pf2, 2, [0, 1]), +]); diff --git a/src/gpio/convert.rs b/src/gpio/convert.rs new file mode 100644 index 0000000..0b056f7 --- /dev/null +++ b/src/gpio/convert.rs @@ -0,0 +1,342 @@ +use super::*; + +impl Pin> { + /// Turns pin alternate configuration pin into open drain + pub fn set_open_drain(self) -> Pin> { + self.into_mode() + } +} + +impl Pin { + /// Configures the pin to operate alternate mode + pub fn into_alternate(self) -> Pin> + where + Self: marker::IntoAf, + { + self.into_mode() + } + + /// Configures the pin to operate in alternate open drain mode + #[allow(path_statements)] + pub fn into_alternate_open_drain(self) -> Pin> + where + Self: marker::IntoAf, + { + self.into_mode() + } + + /// Configures the pin to operate as a input pin + pub fn into_input(self) -> Pin { + self.into_mode() + } + + /// Configures the pin to operate as a floating input pin + pub fn into_floating_input(self) -> Pin { + self.into_mode().internal_resistor(Pull::None) + } + + /// Configures the pin to operate as a pulled down input pin + pub fn into_pull_down_input(self) -> Pin { + self.into_mode().internal_resistor(Pull::Down) + } + + /// Configures the pin to operate as a pulled up input pin + pub fn into_pull_up_input(self) -> Pin { + self.into_mode().internal_resistor(Pull::Up) + } + + /// Configures the pin to operate as an open drain output pin + /// Initial state will be low. + pub fn into_open_drain_output(self) -> Pin> { + self.into_mode() + } + + /// Configures the pin to operate as an open-drain output pin. + /// `initial_state` specifies whether the pin should be initially high or low. + pub fn into_open_drain_output_in_state( + mut self, + initial_state: PinState, + ) -> Pin> { + self._set_state(initial_state); + self.into_mode() + } + + /// Configures the pin to operate as an push pull output pin + /// Initial state will be low. + pub fn into_push_pull_output(mut self) -> Pin> { + self._set_low(); + self.into_mode() + } + + /// Configures the pin to operate as an push-pull output pin. + /// `initial_state` specifies whether the pin should be initially high or low. + pub fn into_push_pull_output_in_state( + mut self, + initial_state: PinState, + ) -> Pin> { + self._set_state(initial_state); + self.into_mode() + } + + /// Configures the pin to operate as an analog input pin + pub fn into_analog(self) -> Pin { + self.into_mode() + } + + /// Configures the pin as a pin that can change between input + /// and output without changing the type. It starts out + /// as a floating input + pub fn into_dynamic(self) -> DynamicPin { + self.into_floating_input(); + DynamicPin::new(Dynamic::InputFloating) + } + + /// Puts `self` into mode `M`. + /// + /// This violates the type state constraints from `MODE`, so callers must + /// ensure they use this properly. + #[inline(always)] + pub(super) fn mode(&mut self) { + change_mode!((*Gpio::

::ptr()), N); + } + + #[inline(always)] + /// Converts pin into specified mode + pub fn into_mode(mut self) -> Pin { + self.mode::(); + Pin::new() + } +} + +macro_rules! change_mode { + ($block:expr, $N:ident) => { + let offset = 2 * $N; + unsafe { + if MODE::OTYPER != M::OTYPER { + if let Some(otyper) = M::OTYPER { + $block + .otyper + .modify(|r, w| w.bits(r.bits() & !(0b1 << $N) | (otyper << $N))); + } + } + + if MODE::AFR != M::AFR { + if let Some(afr) = M::AFR { + if $N < 8 { + let offset2 = 4 * { $N }; + $block.afrl.modify(|r, w| { + w.bits((r.bits() & !(0b1111 << offset2)) | (afr << offset2)) + }); + } else { + let offset2 = 4 * { $N - 8 }; + $block.afrh.modify(|r, w| { + w.bits((r.bits() & !(0b1111 << offset2)) | (afr << offset2)) + }); + } + } + } + + if MODE::MODER != M::MODER { + $block + .moder + .modify(|r, w| w.bits((r.bits() & !(0b11 << offset)) | (M::MODER << offset))); + } + } + }; +} +use change_mode; + +use super::ErasedPin; +impl ErasedPin { + #[inline(always)] + pub(super) fn mode(&mut self) { + let n = self.pin_id(); + change_mode!(self.block(), n); + } + + #[inline(always)] + /// Converts pin into specified mode + pub fn into_mode(mut self) -> ErasedPin { + self.mode::(); + ErasedPin::from_pin_port(self.into_pin_port()) + } +} + +use super::PartiallyErasedPin; +impl PartiallyErasedPin { + #[inline(always)] + pub(super) fn mode(&mut self) { + let n = self.pin_id(); + change_mode!((*Gpio::

::ptr()), n); + } + + #[inline(always)] + /// Converts pin into specified mode + pub fn into_mode(mut self) -> PartiallyErasedPin { + self.mode::(); + PartiallyErasedPin::new(self.i) + } +} + +impl Pin +where + MODE: PinMode, +{ + fn with_mode(&mut self, f: F) -> R + where + M: PinMode, + F: FnOnce(&mut Pin) -> R, + { + self.mode::(); // change physical mode, without changing typestate + + // This will reset the pin back to the original mode when dropped. + // (so either when `with_mode` returns or when `f` unwinds) + let mut resetti = ResetMode::::new(); + + f(&mut resetti.pin) + } + + /// Temporarily configures this pin as a input. + /// + /// The closure `f` is called with the reconfigured pin. After it returns, + /// the pin will be configured back. + pub fn with_input(&mut self, f: impl FnOnce(&mut Pin) -> R) -> R { + self.with_mode(f) + } + + /// Temporarily configures this pin as an analog pin. + /// + /// The closure `f` is called with the reconfigured pin. After it returns, + /// the pin will be configured back. + pub fn with_analog(&mut self, f: impl FnOnce(&mut Pin) -> R) -> R { + self.with_mode(f) + } + + /// Temporarily configures this pin as an open drain output. + /// + /// The closure `f` is called with the reconfigured pin. After it returns, + /// the pin will be configured back. + /// The value of the pin after conversion is undefined. If you + /// want to control it, use `with_open_drain_output_in_state` + pub fn with_open_drain_output( + &mut self, + f: impl FnOnce(&mut Pin>) -> R, + ) -> R { + self.with_mode(f) + } + + /// Temporarily configures this pin as an open drain output . + /// + /// The closure `f` is called with the reconfigured pin. After it returns, + /// the pin will be configured back. + /// Note that the new state is set slightly before conversion + /// happens. This can cause a short output glitch if switching + /// between output modes + pub fn with_open_drain_output_in_state( + &mut self, + state: PinState, + f: impl FnOnce(&mut Pin>) -> R, + ) -> R { + self._set_state(state); + self.with_mode(f) + } + + /// Temporarily configures this pin as a push-pull output. + /// + /// The closure `f` is called with the reconfigured pin. After it returns, + /// the pin will be configured back. + /// The value of the pin after conversion is undefined. If you + /// want to control it, use `with_push_pull_output_in_state` + pub fn with_push_pull_output( + &mut self, + f: impl FnOnce(&mut Pin>) -> R, + ) -> R { + self.with_mode(f) + } + + /// Temporarily configures this pin as a push-pull output. + /// + /// The closure `f` is called with the reconfigured pin. After it returns, + /// the pin will be configured back. + /// Note that the new state is set slightly before conversion + /// happens. This can cause a short output glitch if switching + /// between output modes + pub fn with_push_pull_output_in_state( + &mut self, + state: PinState, + f: impl FnOnce(&mut Pin>) -> R, + ) -> R { + self._set_state(state); + self.with_mode(f) + } +} + +/// Wrapper around a pin that transitions the pin to mode ORIG when dropped +struct ResetMode { + pub pin: Pin, + _mode: PhantomData, +} +impl ResetMode { + fn new() -> Self { + Self { + pin: Pin::new(), + _mode: PhantomData, + } + } +} +impl Drop + for ResetMode +{ + fn drop(&mut self) { + self.pin.mode::(); + } +} + +/// Marker trait for valid pin modes (type state). +/// +/// It can not be implemented by outside types. +pub trait PinMode: crate::Sealed { + // These constants are used to implement the pin configuration code. + // They are not part of public API. + + #[doc(hidden)] + const MODER: u32 = u32::MAX; + #[doc(hidden)] + const OTYPER: Option = None; + #[doc(hidden)] + const AFR: Option = None; +} + +impl crate::Sealed for Input {} +impl PinMode for Input { + const MODER: u32 = 0b00; +} + +impl crate::Sealed for Analog {} +impl PinMode for Analog { + const MODER: u32 = 0b11; +} + +impl crate::Sealed for Output {} +impl PinMode for Output { + const MODER: u32 = 0b01; + const OTYPER: Option = Some(0b1); +} + +impl PinMode for Output { + const MODER: u32 = 0b01; + const OTYPER: Option = Some(0b0); +} + +impl crate::Sealed for Alternate {} +impl PinMode for Alternate { + const MODER: u32 = 0b10; + const OTYPER: Option = Some(0b1); + const AFR: Option = Some(A as _); +} + +impl PinMode for Alternate { + const MODER: u32 = 0b10; + const OTYPER: Option = Some(0b0); + const AFR: Option = Some(A as _); +} diff --git a/src/gpio/dynamic.rs b/src/gpio/dynamic.rs new file mode 100644 index 0000000..d95c3b4 --- /dev/null +++ b/src/gpio/dynamic.rs @@ -0,0 +1,147 @@ +use super::*; + +/// Pin type with dynamic mode +/// +/// - `P` is port name: `A` for GPIOA, `B` for GPIOB, etc. +/// - `N` is pin number: from `0` to `15`. +pub struct DynamicPin { + /// Current pin mode + pub(crate) mode: Dynamic, +} + +/// Tracks the current pin state for dynamic pins +pub enum Dynamic { + /// Floating input mode + InputFloating, + /// Pull-up input mode + InputPullUp, + /// Pull-down input mode + InputPullDown, + /// Push-pull output mode + OutputPushPull, + /// Open-drain output mode + OutputOpenDrain, +} + +/// Error for [DynamicPin] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PinModeError { + /// For operations unsupported in current mode + IncorrectMode, +} + +impl Dynamic { + /// Is pin in readable mode + pub fn is_input(&self) -> bool { + use Dynamic::*; + match self { + InputFloating | InputPullUp | InputPullDown | OutputOpenDrain => true, + OutputPushPull => false, + } + } + + /// Is pin in writable mode + pub fn is_output(&self) -> bool { + use Dynamic::*; + match self { + InputFloating | InputPullUp | InputPullDown => false, + OutputPushPull | OutputOpenDrain => true, + } + } +} + +// For conversion simplify +struct Unknown; + +impl crate::Sealed for Unknown {} +impl PinMode for Unknown {} + +impl DynamicPin { + pub(super) const fn new(mode: Dynamic) -> Self { + Self { mode } + } + + /// Switch pin into pull-up input + #[inline] + pub fn make_pull_up_input(&mut self) { + // NOTE(unsafe), we have a mutable reference to the current pin + Pin::::new().into_pull_up_input(); + self.mode = Dynamic::InputPullUp; + } + /// Switch pin into pull-down input + #[inline] + pub fn make_pull_down_input(&mut self) { + // NOTE(unsafe), we have a mutable reference to the current pin + Pin::::new().into_pull_down_input(); + self.mode = Dynamic::InputPullDown; + } + /// Switch pin into floating input + #[inline] + pub fn make_floating_input(&mut self) { + // NOTE(unsafe), we have a mutable reference to the current pin + Pin::::new().into_floating_input(); + self.mode = Dynamic::InputFloating; + } + /// Switch pin into push-pull output + #[inline] + pub fn make_push_pull_output(&mut self) { + // NOTE(unsafe), we have a mutable reference to the current pin + Pin::::new().into_push_pull_output(); + self.mode = Dynamic::OutputPushPull; + } + /// Switch pin into push-pull output with required voltage state + #[inline] + pub fn make_push_pull_output_in_state(&mut self, state: PinState) { + // NOTE(unsafe), we have a mutable reference to the current pin + Pin::::new().into_push_pull_output_in_state(state); + self.mode = Dynamic::OutputPushPull; + } + /// Switch pin into open-drain output + #[inline] + pub fn make_open_drain_output(&mut self) { + // NOTE(unsafe), we have a mutable reference to the current pin + Pin::::new().into_open_drain_output(); + self.mode = Dynamic::OutputOpenDrain; + } + /// Switch pin into open-drain output with required voltage state + #[inline] + pub fn make_open_drain_output_in_state(&mut self, state: PinState) { + // NOTE(unsafe), we have a mutable reference to the current pin + Pin::::new().into_open_drain_output_in_state(state); + self.mode = Dynamic::OutputOpenDrain; + } + + /// Drives the pin high + pub fn set_high(&mut self) -> Result<(), PinModeError> { + if self.mode.is_output() { + Pin::::new()._set_state(PinState::High); + Ok(()) + } else { + Err(PinModeError::IncorrectMode) + } + } + + /// Drives the pin low + pub fn set_low(&mut self) -> Result<(), PinModeError> { + if self.mode.is_output() { + Pin::::new()._set_state(PinState::Low); + Ok(()) + } else { + Err(PinModeError::IncorrectMode) + } + } + + /// Is the input pin high? + pub fn is_high(&self) -> Result { + self.is_low().map(|b| !b) + } + + /// Is the input pin low? + pub fn is_low(&self) -> Result { + if self.mode.is_input() { + Ok(Pin::::new()._is_low()) + } else { + Err(PinModeError::IncorrectMode) + } + } +} diff --git a/src/gpio/erased.rs b/src/gpio/erased.rs new file mode 100644 index 0000000..9f24666 --- /dev/null +++ b/src/gpio/erased.rs @@ -0,0 +1,172 @@ +use super::*; + +pub use ErasedPin as EPin; + +/// Fully erased pin +/// +/// `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section). +pub struct ErasedPin { + // Bits 0-3: Pin, Bits 4-7: Port + pin_port: u8, + _mode: PhantomData, +} + +impl fmt::Debug for ErasedPin { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_fmt(format_args!( + "P({}{})<{}>", + self.port_id(), + self.pin_id(), + crate::stripped_type_name::() + )) + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for ErasedPin { + fn format(&self, f: defmt::Formatter) { + defmt::write!( + f, + "P({}{})<{}>", + self.port_id(), + self.pin_id(), + crate::stripped_type_name::() + ); + } +} + +impl PinExt for ErasedPin { + type Mode = MODE; + + #[inline(always)] + fn pin_id(&self) -> u8 { + self.pin_port & 0x0f + } + #[inline(always)] + fn port_id(&self) -> u8 { + self.pin_port >> 4 + } +} + +impl ErasedPin { + pub(crate) fn from_pin_port(pin_port: u8) -> Self { + Self { + pin_port, + _mode: PhantomData, + } + } + pub(crate) fn into_pin_port(self) -> u8 { + self.pin_port + } + pub(crate) fn new(port: u8, pin: u8) -> Self { + Self { + pin_port: port << 4 | pin, + _mode: PhantomData, + } + } + + /// Convert type erased pin to `Pin` with fixed type + pub fn restore(self) -> Pin { + assert_eq!(self.port_id(), P as u8 - b'A'); + assert_eq!(self.pin_id(), N); + Pin::new() + } + + #[inline] + pub(crate) fn block(&self) -> &crate::pac::gpioa::RegisterBlock { + // This function uses pointer arithmetic instead of branching to be more efficient + + // The logic relies on the following assumptions: + // - GPIOA register is available on all chips + // - all gpio register blocks have the same layout + // - consecutive gpio register blocks have the same offset between them, namely 0x0400 + // - ErasedPin::new was called with a valid port + + // FIXME could be calculated after const_raw_ptr_to_usize_cast stabilization #51910 + const GPIO_REGISTER_OFFSET: usize = 0x0400; + + let offset = GPIO_REGISTER_OFFSET * self.port_id() as usize; + let block_ptr = + (crate::pac::GPIOA::ptr() as usize + offset) as *const crate::pac::gpioa::RegisterBlock; + + unsafe { &*block_ptr } + } +} + +impl ErasedPin> { + /// Drives the pin high + #[inline(always)] + pub fn set_high(&mut self) { + // NOTE(unsafe) atomic write to a stateless register + unsafe { self.block().bsrr.write(|w| w.bits(1 << self.pin_id())) }; + } + + /// Drives the pin low + #[inline(always)] + pub fn set_low(&mut self) { + // NOTE(unsafe) atomic write to a stateless register + unsafe { + self.block() + .bsrr + .write(|w| w.bits(1 << (self.pin_id() + 16))) + }; + } + + /// Is the pin in drive high or low mode? + #[inline(always)] + pub fn get_state(&self) -> PinState { + if self.is_set_low() { + PinState::Low + } else { + PinState::High + } + } + + /// Drives the pin high or low depending on the provided value + #[inline(always)] + pub fn set_state(&mut self, state: PinState) { + match state { + PinState::Low => self.set_low(), + PinState::High => self.set_high(), + } + } + + /// Is the pin in drive high mode? + #[inline(always)] + pub fn is_set_high(&self) -> bool { + !self.is_set_low() + } + + /// Is the pin in drive low mode? + #[inline(always)] + pub fn is_set_low(&self) -> bool { + self.block().odr.read().bits() & (1 << self.pin_id()) == 0 + } + + /// Toggle pin output + #[inline(always)] + pub fn toggle(&mut self) { + if self.is_set_low() { + self.set_high() + } else { + self.set_low() + } + } +} + +impl ErasedPin +where + MODE: marker::Readable, +{ + /// Is the input pin high? + #[inline(always)] + pub fn is_high(&self) -> bool { + !self.is_low() + } + + /// Is the input pin low? + #[inline(always)] + pub fn is_low(&self) -> bool { + self.block().idr.read().bits() & (1 << self.pin_id()) == 0 + } +} diff --git a/src/gpio/hal_02.rs b/src/gpio/hal_02.rs new file mode 100644 index 0000000..9b98be7 --- /dev/null +++ b/src/gpio/hal_02.rs @@ -0,0 +1,242 @@ +use core::convert::Infallible; + +use super::{ + dynamic::PinModeError, marker, DynamicPin, ErasedPin, Input, OpenDrain, Output, + PartiallyErasedPin, Pin, PinMode, PinState, +}; + +use embedded_hal::digital::v2::{ + InputPin, IoPin, OutputPin, StatefulOutputPin, ToggleableOutputPin, +}; + +// Implementations for `Pin` + +impl OutputPin for Pin> { + type Error = Infallible; + + #[inline(always)] + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high(); + Ok(()) + } + + #[inline(always)] + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low(); + Ok(()) + } +} + +impl StatefulOutputPin for Pin> { + #[inline(always)] + fn is_set_high(&self) -> Result { + Ok(self.is_set_high()) + } + + #[inline(always)] + fn is_set_low(&self) -> Result { + Ok(self.is_set_low()) + } +} + +impl ToggleableOutputPin for Pin> { + type Error = Infallible; + + #[inline(always)] + fn toggle(&mut self) -> Result<(), Self::Error> { + self.toggle(); + Ok(()) + } +} + +impl InputPin for Pin +where + MODE: marker::Readable, +{ + type Error = Infallible; + + #[inline(always)] + fn is_high(&self) -> Result { + Ok(self.is_high()) + } + + #[inline(always)] + fn is_low(&self) -> Result { + Ok(self.is_low()) + } +} + +impl IoPin for Pin> { + type Error = Infallible; + fn into_input_pin(self) -> Result { + Ok(self) + } + fn into_output_pin(mut self, state: PinState) -> Result { + self.set_state(state); + Ok(self) + } +} + +impl IoPin, Self> for Pin> +where + Output: PinMode, +{ + type Error = Infallible; + fn into_input_pin(self) -> Result, Self::Error> { + Ok(self.into_input()) + } + fn into_output_pin(mut self, state: PinState) -> Result { + self.set_state(state); + Ok(self) + } +} + +impl IoPin>> for Pin +where + Output: PinMode, +{ + type Error = Infallible; + fn into_input_pin(self) -> Result { + Ok(self) + } + fn into_output_pin(mut self, state: PinState) -> Result>, Self::Error> { + self._set_state(state); + Ok(self.into_mode()) + } +} + +// Implementations for `ErasedPin` + +impl OutputPin for ErasedPin> { + type Error = core::convert::Infallible; + + #[inline(always)] + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high(); + Ok(()) + } + + #[inline(always)] + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low(); + Ok(()) + } +} + +impl StatefulOutputPin for ErasedPin> { + #[inline(always)] + fn is_set_high(&self) -> Result { + Ok(self.is_set_high()) + } + + #[inline(always)] + fn is_set_low(&self) -> Result { + Ok(self.is_set_low()) + } +} + +impl ToggleableOutputPin for ErasedPin> { + type Error = Infallible; + + #[inline(always)] + fn toggle(&mut self) -> Result<(), Self::Error> { + self.toggle(); + Ok(()) + } +} + +impl InputPin for ErasedPin +where + MODE: marker::Readable, +{ + type Error = core::convert::Infallible; + + #[inline(always)] + fn is_high(&self) -> Result { + Ok(self.is_high()) + } + + #[inline(always)] + fn is_low(&self) -> Result { + Ok(self.is_low()) + } +} + +// Implementations for `PartiallyErasedPin` + +impl OutputPin for PartiallyErasedPin> { + type Error = Infallible; + + #[inline(always)] + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high(); + Ok(()) + } + + #[inline(always)] + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low(); + Ok(()) + } +} + +impl StatefulOutputPin for PartiallyErasedPin> { + #[inline(always)] + fn is_set_high(&self) -> Result { + Ok(self.is_set_high()) + } + + #[inline(always)] + fn is_set_low(&self) -> Result { + Ok(self.is_set_low()) + } +} + +impl ToggleableOutputPin for PartiallyErasedPin> { + type Error = Infallible; + + #[inline(always)] + fn toggle(&mut self) -> Result<(), Self::Error> { + self.toggle(); + Ok(()) + } +} + +impl InputPin for PartiallyErasedPin +where + MODE: marker::Readable, +{ + type Error = Infallible; + + #[inline(always)] + fn is_high(&self) -> Result { + Ok(self.is_high()) + } + + #[inline(always)] + fn is_low(&self) -> Result { + Ok(self.is_low()) + } +} + +// Implementations for `DynamicPin` + +impl OutputPin for DynamicPin { + type Error = PinModeError; + fn set_high(&mut self) -> Result<(), Self::Error> { + self.set_high() + } + fn set_low(&mut self) -> Result<(), Self::Error> { + self.set_low() + } +} + +impl InputPin for DynamicPin { + type Error = PinModeError; + fn is_high(&self) -> Result { + self.is_high() + } + fn is_low(&self) -> Result { + self.is_low() + } +} diff --git a/src/gpio/outport.rs b/src/gpio/outport.rs new file mode 100644 index 0000000..5d8292a --- /dev/null +++ b/src/gpio/outport.rs @@ -0,0 +1,120 @@ +use super::*; + +/// Convert tuple or array of pins to output port +pub trait OutPort { + type Target; + fn outport(self) -> Self::Target; +} + +macro_rules! out_port { + ( $name:ident => $n:literal, ( $($i:tt),+ ), ( $($N:ident),+ )) => { + pub struct $name ( + $(pub Pin>,)+ + ); + + impl OutPort for ($(Pin>),+) { + type Target = $name

; + fn outport(self) -> Self::Target { + $name($(self.$i),+) + } + } + + /// Wrapper for tuple of `Pin`s + impl $name

{ + const fn mask() -> u32 { + 0 $( | (1 << { $N }))+ + } + const fn value_for_write_bsrr(val: u32) -> u32 { + 0 $( | (1 << (if val & (1 << $i) != 0 { $N } else { $N + 16 })))+ + } + + #[doc=concat!("Set/reset pins according to `", $n, "` lower bits")] + #[inline(never)] + pub fn write(&mut self, word: u32) { + unsafe { + (*Gpio::

::ptr()) + .bsrr + .write(|w| w.bits(Self::value_for_write_bsrr(word))) + } + } + + /// Set all pins to `PinState::High` + pub fn all_high(&mut self) { + unsafe { + (*Gpio::

::ptr()) + .bsrr + .write(|w| w.bits(Self::mask())) + } + } + + /// Reset all pins to `PinState::Low` + pub fn all_low(&mut self) { + unsafe { + (*Gpio::

::ptr()) + .bsrr + .write(|w| w.bits(Self::mask() << 16)) + } + } + } + } +} + +out_port!(OutPort2 => 2, (0, 1), (N0, N1)); +out_port!(OutPort3 => 3, (0, 1, 2), (N0, N1, N2)); +out_port!(OutPort4 => 4, (0, 1, 2, 3), (N0, N1, N2, N3)); +out_port!(OutPort5 => 5, (0, 1, 2, 3, 4), (N0, N1, N2, N3, N4)); +out_port!(OutPort6 => 6, (0, 1, 2, 3, 4, 5), (N0, N1, N2, N3, N4, N5)); +out_port!(OutPort7 => 7, (0, 1, 2, 3, 4, 5, 6), (N0, N1, N2, N3, N4, N5, N6)); +out_port!(OutPort8 => 8, (0, 1, 2, 3, 4, 5, 6, 7), (N0, N1, N2, N3, N4, N5, N6, N7)); + +/// Wrapper for array of `PartiallyErasedPin`s +pub struct OutPortArray(pub [PEPin>; SIZE]); + +impl OutPort for [PEPin>; SIZE] { + type Target = OutPortArray; + fn outport(self) -> Self::Target { + OutPortArray(self) + } +} + +impl OutPortArray { + fn mask(&self) -> u32 { + let mut msk = 0; + for pin in self.0.iter() { + msk |= 1 << pin.i; + } + msk + } + fn value_for_write_bsrr(&self, val: u32) -> u32 { + let mut msk = 0; + for (idx, pin) in self.0.iter().enumerate() { + let n = pin.i; + msk |= 1 << (if val & (1 << idx) != 0 { n } else { n + 16 }); + } + msk + } + + /// Set/reset pins according to `SIZE` lower bits + #[inline(never)] + pub fn write(&mut self, word: u32) { + unsafe { + (*Gpio::

::ptr()) + .bsrr + .write(|w| w.bits(self.value_for_write_bsrr(word))) + } + } + + /// Set all pins to `PinState::High` + pub fn all_high(&mut self) { + unsafe { (*Gpio::

::ptr()).bsrr.write(|w| w.bits(self.mask())) } + } + + /// Reset all pins to `PinState::Low` + pub fn all_low(&mut self) { + unsafe { + (*Gpio::

::ptr()) + .bsrr + .write(|w| w.bits(self.mask() << 16)) + } + } +} diff --git a/src/gpio/partially_erased.rs b/src/gpio/partially_erased.rs new file mode 100644 index 0000000..8483f0b --- /dev/null +++ b/src/gpio/partially_erased.rs @@ -0,0 +1,153 @@ +use super::*; + +pub use PartiallyErasedPin as PEPin; + +/// Partially erased pin +/// +/// - `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section). +/// - `P` is port name: `A` for GPIOA, `B` for GPIOB, etc. +pub struct PartiallyErasedPin { + pub(crate) i: u8, + _mode: PhantomData, +} + +impl PartiallyErasedPin { + pub(crate) fn new(i: u8) -> Self { + Self { + i, + _mode: PhantomData, + } + } + + /// Convert partially type erased pin to `Pin` with fixed type + pub fn restore(self) -> Pin { + assert_eq!(self.i, N); + Pin::new() + } +} + +impl fmt::Debug for PartiallyErasedPin { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_fmt(format_args!( + "P{}({})<{}>", + P, + self.i, + crate::stripped_type_name::() + )) + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for PartiallyErasedPin { + fn format(&self, f: defmt::Formatter) { + defmt::write!( + f, + "P{}({})<{}>", + P, + self.i, + crate::stripped_type_name::() + ); + } +} + +impl PinExt for PartiallyErasedPin { + type Mode = MODE; + + #[inline(always)] + fn pin_id(&self) -> u8 { + self.i + } + #[inline(always)] + fn port_id(&self) -> u8 { + P as u8 - b'A' + } +} + +impl PartiallyErasedPin> { + /// Drives the pin high + #[inline(always)] + pub fn set_high(&mut self) { + // NOTE(unsafe) atomic write to a stateless register + unsafe { (*Gpio::

::ptr()).bsrr.write(|w| w.bits(1 << self.i)) } + } + + /// Drives the pin low + #[inline(always)] + pub fn set_low(&mut self) { + // NOTE(unsafe) atomic write to a stateless register + unsafe { + (*Gpio::

::ptr()) + .bsrr + .write(|w| w.bits(1 << (self.i + 16))) + } + } + + /// Is the pin in drive high or low mode? + #[inline(always)] + pub fn get_state(&self) -> PinState { + if self.is_set_low() { + PinState::Low + } else { + PinState::High + } + } + + /// Drives the pin high or low depending on the provided value + #[inline(always)] + pub fn set_state(&mut self, state: PinState) { + match state { + PinState::Low => self.set_low(), + PinState::High => self.set_high(), + } + } + + /// Is the pin in drive high mode? + #[inline(always)] + pub fn is_set_high(&self) -> bool { + !self.is_set_low() + } + + /// Is the pin in drive low mode? + #[inline(always)] + pub fn is_set_low(&self) -> bool { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*Gpio::

::ptr()).odr.read().bits() & (1 << self.i) == 0 } + } + + /// Toggle pin output + #[inline(always)] + pub fn toggle(&mut self) { + if self.is_set_low() { + self.set_high() + } else { + self.set_low() + } + } +} + +impl PartiallyErasedPin +where + MODE: marker::Readable, +{ + /// Is the input pin high? + #[inline(always)] + pub fn is_high(&self) -> bool { + !self.is_low() + } + + /// Is the input pin low? + #[inline(always)] + pub fn is_low(&self) -> bool { + // NOTE(unsafe) atomic read with no side effects + unsafe { (*Gpio::

::ptr()).idr.read().bits() & (1 << self.i) == 0 } + } +} + +impl From> for ErasedPin { + /// Partially erased pin-to-erased pin conversion using the [`From`] trait. + /// + /// Note that [`From`] is the reciprocal of [`Into`]. + fn from(p: PartiallyErasedPin) -> Self { + ErasedPin::new(P as u8 - b'A', p.i) + } +} diff --git a/src/i2c/blocking.rs b/src/i2c/blocking.rs index 8babf09..106d940 100644 --- a/src/i2c/blocking.rs +++ b/src/i2c/blocking.rs @@ -1,9 +1,7 @@ //! I2C -use crate::gpio::*; use crate::i2c::config::Config; -use crate::i2c::{self, Error, I2c, I2cDirection, I2cExt, SCLPin, SDAPin}; +use crate::i2c::{self, Error, I2c, I2cDirection, I2cExt, Instance}; use crate::rcc::*; -use crate::stm32::I2C; use hal::blocking::i2c::{Read, Write, WriteRead}; pub trait I2cSlave { @@ -110,414 +108,349 @@ macro_rules! busy_wait { }; } -macro_rules! i2c { - ($I2CX:ident, $i2cx:ident, - sda: [ $(($PSDA:ty, $AFSDA:expr),)+ ], - scl: [ $(($PSCL:ty, $AFSCL:expr),)+ ], - ) => { - $( - impl SDAPin<$I2CX> for $PSDA { - fn setup(&self) { - self.set_alt_mode($AFSDA) - } - - fn release(self) -> Self { - self.into_open_drain_output() - } - } - )+ - - $( - impl SCLPin<$I2CX> for $PSCL { - fn setup(&self) { - self.set_alt_mode($AFSCL) - } +impl I2cExt for I2C { + fn i2c( + self, + pins: (impl Into, impl Into), + config: impl Into, + rcc: &mut Rcc, + ) -> I2c { + I2c::new(self, pins, config, rcc) + } +} - fn release(self) -> Self { - self.into_open_drain_output() - } - } - )+ - - impl I2cExt<$I2CX> for $I2CX { - fn i2c( - self, - sda: SDA, - scl: SCL, - config: impl Into, - rcc: &mut Rcc, - ) -> I2c<$I2CX, SDA, SCL> - where - SDA: SDAPin<$I2CX>, - SCL: SCLPin<$I2CX>, - { - I2c::$i2cx(self, sda, scl, config, rcc) - } +impl I2c { + pub fn new( + i2c: I2C, + pins: (impl Into, impl Into), + config: impl Into, + rcc: &mut Rcc, + ) -> Self { + let config = config.into(); + I2C::enable(rcc); + I2C::reset(rcc); + + // Make sure the I2C unit is disabled so we can configure it + i2c.cr1.modify(|_, w| w.pe().clear_bit()); + + // Setup protocol timings + let timing_bits = config.timing_bits(rcc.clocks.apb_clk); + i2c.timingr.write(|w| unsafe { w.bits(timing_bits) }); + + // Enable the I2C processing + i2c.cr1.modify(|_, w| unsafe { + w.pe().set_bit(); + w.dnf().bits(config.digital_filter); + w.anfoff().bit(!config.analog_filter) + }); + + if config.slave_address_1 > 0 { + i2c.oar1.write(|w| unsafe { + w.oa1().bits(config.slave_address_1); + w.oa1mode().bit(config.address_11bits); + w.oa1en().set_bit() + }); + // Enable acknowlidge control + i2c.cr1.modify(|_, w| w.sbc().set_bit()); } - impl I2c<$I2CX, SDA, SCL> where - SDA: SDAPin<$I2CX>, - SCL: SCLPin<$I2CX> - { - pub fn $i2cx(i2c: $I2CX, sda: SDA, scl: SCL, config: impl Into, rcc: &mut Rcc) -> Self - where - SDA: SDAPin<$I2CX>, - SCL: SCLPin<$I2CX>, - { - let config = config.into(); - $I2CX::enable(rcc); - $I2CX::reset(rcc); - - // Make sure the I2C unit is disabled so we can configure it - i2c.cr1.modify(|_, w| w.pe().clear_bit()); - - // Setup protocol timings - let timing_bits = config.timing_bits(rcc.clocks.apb_clk); - i2c.timingr.write(|w| unsafe { w.bits(timing_bits) }); - - // Enable the I2C processing - i2c.cr1.modify(|_, w| unsafe { - w.pe() - .set_bit() - .dnf() - .bits(config.digital_filter) - .anfoff() - .bit(!config.analog_filter) - }); + if config.slave_address_2 > 0 { + i2c.oar2.write(|w| unsafe { + w.oa2msk().bits(config.slave_address_mask as u8); + w.oa2().bits(config.slave_address_2); + w.oa2en().set_bit() + }); + // Enable acknowlidge control + i2c.cr1.modify(|_, w| w.sbc().set_bit()); + } - if config.slave_address_1 > 0 { - i2c.oar1.write(|w| unsafe { - w.oa1().bits(config.slave_address_1) - .oa1mode().bit(config.address_11bits) - .oa1en().set_bit() - }); - // Enable acknowlidge control - i2c.cr1.modify(|_, w| w.sbc().set_bit() ); - } + // Enable pins + let pins = (pins.0.into(), pins.1.into()); - if config.slave_address_2 > 0 { - i2c.oar2.write( |w| unsafe { - w.oa2msk().bits( config.slave_address_mask as u8) - .oa2().bits(config.slave_address_2) - .oa2en().set_bit() - }); - // Enable acknowlidge control - i2c.cr1.modify(|_, w| w.sbc().set_bit() ); - } + I2c { i2c, pins } + } - // Enable pins - sda.setup(); - scl.setup(); + pub fn listen(&mut self, ev: i2c::Event) { + match ev { + i2c::Event::AddressMatch => self.i2c.cr1.modify(|_, w| w.addrie().set_bit()), + i2c::Event::Rxne => self.i2c.cr1.modify(|_, w| w.rxie().set_bit()), + } + } - I2c { i2c, sda, scl } - } + pub fn unlisten(&mut self, ev: i2c::Event) { + match ev { + i2c::Event::AddressMatch => self.i2c.cr1.modify(|_, w| w.addrie().clear_bit()), + i2c::Event::Rxne => self.i2c.cr1.modify(|_, w| w.rxie().clear_bit()), + } + } - pub fn listen(&mut self, ev: i2c::Event) { - match ev { - i2c::Event::AddressMatch => self.i2c.cr1.modify(|_, w| w.addrie().set_bit()), - i2c::Event::Rxne => self.i2c.cr1.modify(|_, w| w.rxie().set_bit()), - } - } + pub fn clear_irq(&mut self, ev: i2c::Event) { + match ev { + i2c::Event::AddressMatch => self.i2c.icr.write(|w| w.addrcf().set_bit()), + _ => {} + } + } - pub fn unlisten(&mut self, ev: i2c::Event) { - match ev { - i2c::Event::AddressMatch => self.i2c.cr1.modify(|_, w| w.addrie().clear_bit()), - i2c::Event::Rxne => self.i2c.cr1.modify(|_, w| w.rxie().clear_bit()), - } - } + pub fn release(self) -> (I2C, (I2C::Scl, I2C::Sda)) { + (self.i2c, self.pins) + } +} - pub fn clear_irq(&mut self, ev: i2c::Event) { - match ev { - i2c::Event::AddressMatch => self.i2c.icr.write(|w| w.addrcf().set_bit()), - _ => {}, - } +impl WriteRead for I2c { + type Error = Error; + + fn write_read( + &mut self, + addr: u8, + snd_buffer: &[u8], + rcv_buffer: &mut [u8], + ) -> Result<(), Self::Error> { + let sndlen = snd_buffer.len(); + let rcvlen = rcv_buffer.len(); + assert!(sndlen < 256 && sndlen > 0); + assert!(rcvlen < 256 && rcvlen > 0); + + // Wait for any previous address sequence to end automatically. + // This could be up to 50% of a bus cycle (ie. up to 0.5/freq) + while self.i2c.cr2.read().start().bit_is_set() {} + + // flush i2c tx register + self.i2c.isr.write(|w| w.txe().set_bit()); + + // Set START and prepare to send `bytes`. + // The START bit can be set even if the bus is BUSY or + // I2C is in slave mode. + self.i2c.cr2.write(|w| unsafe { + // Set number of bytes to transfer + w.nbytes().bits(sndlen as u8); + // Set address to transfer to/from + w.sadd().bits((addr << 1) as u16); + // 7-bit addressing mode + w.add10().clear_bit(); + // Set transfer direction to write + w.rd_wrn().clear_bit(); + // Software end mode + w.autoend().clear_bit(); + w.reload().clear_bit(); + // Start transfer + w.start().set_bit() + }); + let mut idx = 0; + // Wait until we are allowed to send data + // (START has been ACKed or last byte went through) + // macro will return false when the tc bit is set + for byte in snd_buffer { + busy_wait!(self.i2c, txis, bit_is_set, idx, sndlen); + // Put byte on the wire + self.i2c.txdr.write(|w| unsafe { w.txdata().bits(*byte) }); + idx += 1; + } + // Wait until the write finishes before beginning to read. + let dummy = 0xFE; + busy_wait!(self.i2c, tc, bit_is_set, idx, dummy); + + // reSTART and prepare to receive bytes into `rcv_buffer` + self.i2c.cr2.write(|w| unsafe { + // Set number of bytes to transfer + w.nbytes().bits(rcvlen as u8); + // Set address to transfer to/from + w.sadd().bits((addr << 1) as u16); + // 7-bit addressing mode + w.add10().clear_bit(); + // Set transfer direction to read + w.rd_wrn().set_bit(); + // Automatic end mode + w.autoend().set_bit(); + w.reload().clear_bit(); + // Start transfer + w.start().set_bit() + }); + + idx = 0; + loop { + // Wait until we have received something. Handle all state in busy_wait macro + busy_wait!(self.i2c, rxne, bit_is_set, idx, rcvlen); + if idx < rcvlen { + rcv_buffer[idx] = self.i2c.rxdr.read().rxdata().bits(); + idx += 1; } + } + } +} - pub fn release(self) -> ($I2CX, SDA, SCL) { - (self.i2c, self.sda.release(), self.scl.release()) +impl Write for I2c { + type Error = Error; + + fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { + let buflen = bytes.len(); + assert!(buflen < 256 && buflen > 0); + + // Wait for any previous address sequence to end automatically. + // This could be up to 50% of a bus cycle (ie. up to 0.5/freq) + while self.i2c.cr2.read().start().bit_is_set() {} + + self.i2c.cr2.modify(|_, w| unsafe { + // Start transfer + w.start().set_bit(); + // Set number of bytes to transfer + w.nbytes().bits(buflen as u8); + // Set address to transfer to/from + w.sadd().bits((addr << 1) as u16); + // Set transfer direction to write + w.rd_wrn().clear_bit(); + // Automatic end mode + w.autoend().set_bit(); + w.reload().clear_bit() + }); + + let mut idx = 0; + loop { + // Wait until we are allowed to send data, handle all state in busy_wait macro + busy_wait!(self.i2c, txis, bit_is_set, idx, buflen); + + // Put byte on the wire + if idx < buflen { + self.i2c + .txdr + .write(|w| unsafe { w.txdata().bits(bytes[idx]) }); + idx += 1; } } + } +} - impl WriteRead for I2c<$I2CX, SDA, SCL> { - type Error = Error; - - fn write_read( - &mut self, - addr: u8, - snd_buffer: &[u8], - rcv_buffer: &mut [u8], - ) -> Result<(), Self::Error> { - let sndlen = snd_buffer.len(); - let rcvlen = rcv_buffer.len(); - assert!(sndlen < 256 && sndlen > 0); - assert!(rcvlen < 256 && rcvlen > 0); - - // Wait for any previous address sequence to end automatically. - // This could be up to 50% of a bus cycle (ie. up to 0.5/freq) - while self.i2c.cr2.read().start().bit_is_set() {}; - - // flush i2c tx register - self.i2c.isr.write(|w| w.txe().set_bit()); - - // Set START and prepare to send `bytes`. - // The START bit can be set even if the bus is BUSY or - // I2C is in slave mode. - self.i2c.cr2.write(|w| unsafe { - w - // Set number of bytes to transfer - .nbytes().bits(sndlen as u8) - // Set address to transfer to/from - .sadd().bits((addr << 1) as u16) - // 7-bit addressing mode - .add10().clear_bit() - // Set transfer direction to write - .rd_wrn().clear_bit() - // Software end mode - .autoend().clear_bit() - .reload().clear_bit() - // Start transfer - .start().set_bit() - }); - let mut idx = 0; - // Wait until we are allowed to send data - // (START has been ACKed or last byte went through) - // macro will return false when the tc bit is set - for byte in snd_buffer { - busy_wait!(self.i2c, txis, bit_is_set, idx, sndlen); - // Put byte on the wire - self.i2c.txdr.write(|w| unsafe { w.txdata().bits(*byte) }); - idx += 1; - } - // Wait until the write finishes before beginning to read. - let dummy = 0xFE; - busy_wait!(self.i2c, tc, bit_is_set, idx, dummy ); - - // reSTART and prepare to receive bytes into `rcv_buffer` - self.i2c.cr2.write(|w| unsafe { - w - // Set number of bytes to transfer - .nbytes().bits(rcvlen as u8) - // Set address to transfer to/from - .sadd().bits((addr << 1) as u16) - // 7-bit addressing mode - .add10().clear_bit() - // Set transfer direction to read - .rd_wrn().set_bit() - // Automatic end mode - .autoend().set_bit() - .reload().clear_bit() - // Start transfer - .start().set_bit() - }); - - idx = 0; - loop { - // Wait until we have received something. Handle all state in busy_wait macro - busy_wait!(self.i2c, rxne, bit_is_set, idx, rcvlen); - if idx < rcvlen { - rcv_buffer[idx] = self.i2c.rxdr.read().rxdata().bits(); - idx +=1; - } - } +impl Read for I2c { + type Error = Error; + + fn read(&mut self, addr: u8, bytes: &mut [u8]) -> Result<(), Self::Error> { + let buflen = bytes.len(); + assert!(buflen < 256 && buflen > 0); + + // Wait for any previous address sequence to end automatically. + // This could be up to 50% of a bus cycle (ie. up to 0.5/freq) + while self.i2c.cr2.read().start().bit_is_set() {} + // Flush rxdr register + let _ = self.i2c.rxdr.read().rxdata().bits(); + + // Set START and prepare to receive bytes into `buffer`. + // The START bit can be set even if the bus + // is BUSY or I2C is in slave mode. + self.i2c.cr2.modify(|_, w| unsafe { + // Start transfer + w.start().set_bit(); + // Set number of bytes to transfer + w.nbytes().bits(buflen as u8); + // Set address to transfer to/from + w.sadd().bits((addr << 1) as u16); + // Set transfer direction to read + w.rd_wrn().set_bit(); + // automatic end mode + w.autoend().set_bit(); + w.reload().clear_bit() + }); + let mut idx = 0; + loop { + // Wait until we have received something + busy_wait!(self.i2c, rxne, bit_is_set, idx, buflen); + if idx < buflen { + bytes[idx] = self.i2c.rxdr.read().rxdata().bits(); + idx += 1; } } + } +} - impl Write for I2c<$I2CX, SDA, SCL> { - type Error = Error; - - fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { - let buflen = bytes.len(); - assert!(buflen < 256 && buflen > 0); - - // Wait for any previous address sequence to end automatically. - // This could be up to 50% of a bus cycle (ie. up to 0.5/freq) - while self.i2c.cr2.read().start().bit_is_set() {}; - - self.i2c.cr2.modify(|_, w| unsafe { - w - // Start transfer - .start().set_bit() - // Set number of bytes to transfer - .nbytes().bits(buflen as u8) - // Set address to transfer to/from - .sadd().bits((addr << 1) as u16) - // Set transfer direction to write - .rd_wrn().clear_bit() - // Automatic end mode - .autoend().set_bit() - .reload().clear_bit() - }); - - let mut idx = 0; - loop { - // Wait until we are allowed to send data, handle all state in busy_wait macro - busy_wait!(self.i2c, txis, bit_is_set, idx, buflen); +impl I2cSlave for I2c { + fn slave_sbc(&mut self, sbc_enabled: bool) { + // Enable Slave byte control + self.i2c.cr1.modify(|_, w| w.sbc().bit(sbc_enabled)); + } - // Put byte on the wire - if idx < buflen { - self.i2c.txdr.write(|w| unsafe { w.txdata().bits(bytes[idx]) }); - idx += 1; - } - } - } + fn slave_addressed(&mut self) -> Result, Error> { + if self.i2c.isr.read().addr().bit_is_set() { + let isr = self.i2c.isr.read(); + let current_address = isr.addcode().bits() as u16; + + // if the dir bit is set it is a master write slave read operation + let direction = if isr.dir().bit_is_set() { + I2cDirection::MasterReadSlaveWrite + } else { + I2cDirection::MasterWriteSlaveRead + }; + // do not yet release the clock stretching here. + // In the slave read function the nbytes is send, for this the addr bit must be set + Ok(Some((current_address, direction))) + } else { + Ok(None) } + } - impl Read for I2c<$I2CX, SDA, SCL> { - type Error = Error; - - fn read(&mut self, addr: u8, bytes: &mut [u8]) -> Result<(), Self::Error> { - let buflen = bytes.len(); - assert!(buflen < 256 && buflen > 0); - - // Wait for any previous address sequence to end automatically. - // This could be up to 50% of a bus cycle (ie. up to 0.5/freq) - while self.i2c.cr2.read().start().bit_is_set() {}; - // Flush rxdr register - let _ = self.i2c.rxdr.read().rxdata().bits(); - - // Set START and prepare to receive bytes into `buffer`. - // The START bit can be set even if the bus - // is BUSY or I2C is in slave mode. - self.i2c.cr2.modify(|_, w| unsafe { - w - // Start transfer - .start().set_bit() - // Set number of bytes to transfer - .nbytes().bits(buflen as u8) - // Set address to transfer to/from - .sadd().bits((addr << 1) as u16) - // Set transfer direction to read - .rd_wrn().set_bit() - // automatic end mode - .autoend().set_bit() - .reload().clear_bit() - }); - let mut idx = 0; - loop { - // Wait until we have received something - busy_wait!(self.i2c, rxne, bit_is_set, idx, buflen); - if idx < buflen { - bytes[idx] = self.i2c.rxdr.read().rxdata().bits(); - idx +=1; - } - } + fn slave_wait_addressed(&mut self) -> Result<(u16, I2cDirection), Error> { + loop { + if let Some(res) = self.slave_addressed()? { + return Ok(res); } } + } - impl I2cSlave for I2c<$I2CX, SDA, SCL> { - - fn slave_sbc(&mut self, sbc_enabled: bool) { - // Enable Slave byte control - self.i2c.cr1.modify(|_, w| w.sbc().bit(sbc_enabled) ); - } + fn slave_write(&mut self, bytes: &[u8]) -> Result<(), Error> { + let buflen = bytes.len(); + assert!(buflen < 256 && buflen > 0); - fn slave_addressed(&mut self) -> Result, Error> { - if self.i2c.isr.read().addr().bit_is_set() { - let isr = self.i2c.isr.read(); - let current_address = isr.addcode().bits() as u16; - - // if the dir bit is set it is a master write slave read operation - let direction = if isr.dir().bit_is_set() { - I2cDirection::MasterReadSlaveWrite - } else { - I2cDirection::MasterWriteSlaveRead - }; - // do not yet release the clock stretching here. - // In the slave read function the nbytes is send, for this the addr bit must be set - Ok(Some((current_address, direction))) + // Set the nbytes and prepare to send bytes into `buffer`. + self.i2c + .cr2 + .modify(|_, w| unsafe { w.nbytes().bits(buflen as u8).reload().clear_bit() }); + // flush i2c tx register + self.i2c.isr.write(|w| w.txe().set_bit()); + // end address phase, release clock stretching + self.i2c.icr.write(|w| w.addrcf().set_bit()); - } else { - Ok(None) - } - } - - fn slave_wait_addressed(&mut self) -> Result<(u16, I2cDirection), Error> { - loop { - if let Some(res) = self.slave_addressed()? { - return Ok(res) - } - } + let mut idx = 0; + loop { + // wait until we are allowed to send the byte. Handle all state in macro + busy_wait!(self.i2c, txis, bit_is_set, idx, buflen); + + // Put byte on the wire + if idx < buflen { + self.i2c + .txdr + .write(|w| unsafe { w.txdata().bits(bytes[idx]) }); + idx += 1; + } else { + // we will never reach here. In case the master wants to read more than buflen + // the hardware will send 0xFF + // Also means that on slave side we cannot detect this error case + self.i2c.txdr.write(|w| unsafe { w.txdata().bits(0x21) }); } + } + } - fn slave_write(&mut self, bytes: &[u8]) -> Result<(), Error> { - let buflen = bytes.len(); - assert!(buflen < 256 && buflen > 0); - - // Set the nbytes and prepare to send bytes into `buffer`. - self.i2c.cr2.modify(|_, w| unsafe { - w.nbytes().bits( buflen as u8) - .reload().clear_bit() - }); - // flush i2c tx register - self.i2c.isr.write(|w| w.txe().set_bit()); - // end address phase, release clock stretching - self.i2c.icr.write(|w| w.addrcf().set_bit() ); - - let mut idx = 0; - loop { - // wait until we are allowed to send the byte. Handle all state in macro - busy_wait!(self.i2c, txis, bit_is_set, idx, buflen); - - // Put byte on the wire - if idx < buflen { - self.i2c.txdr.write(|w| unsafe { w.txdata().bits(bytes[idx]) }); - idx += 1; - } else { - // we will never reach here. In case the master wants to read more than buflen - // the hardware will send 0xFF - // Also means that on slave side we cannot detect this error case - self.i2c.txdr.write(|w| unsafe { w.txdata().bits(0x21) }); - } - } - } + fn slave_read(&mut self, bytes: &mut [u8]) -> Result<(), Error> { + let buflen = bytes.len(); + assert!(buflen < 256 && buflen > 0); + + // Set the nbytes START and prepare to receive bytes into `buffer`. + self.i2c.cr2.modify(|_, w| unsafe { + // Set number of bytes to transfer: maximum as all incoming bytes will be ACK'ed + w.nbytes().bits(buflen as u8); + // during sending nbytes automatically send a ACK, stretch clock after last byte + w.reload().set_bit() + }); + // end address phase, release clock stretching + self.i2c.icr.write(|w| w.addrcf().set_bit()); + flush_rxdr!(self.i2c); + + let mut idx = 0; + loop { + // Wait until we have received something. + busy_wait!(self.i2c, rxne, bit_is_set, idx, buflen); - fn slave_read(&mut self, bytes: &mut [u8]) -> Result<(), Error> { - let buflen = bytes.len(); - assert!(buflen < 256 && buflen > 0); - - // Set the nbytes START and prepare to receive bytes into `buffer`. - self.i2c.cr2.modify(|_, w| unsafe { - w - // Set number of bytes to transfer: maximum as all incoming bytes will be ACK'ed - .nbytes().bits(buflen as u8) - // during sending nbytes automatically send a ACK, stretch clock after last byte - .reload().set_bit() - }); - // end address phase, release clock stretching - self.i2c.icr.write(|w| - w.addrcf().set_bit() - ); - flush_rxdr!(self.i2c); - - let mut idx = 0; - loop { - // Wait until we have received something. - busy_wait!(self.i2c, rxne, bit_is_set, idx, buflen); - - // read byte from wire - if idx < buflen { - bytes[idx] = self.i2c.rxdr.read().rxdata().bits(); - idx += 1; - } - } + // read byte from wire + if idx < buflen { + bytes[idx] = self.i2c.rxdr.read().rxdata().bits(); + idx += 1; } } } } - -i2c!( - I2C, - i2c1, - sda: [ - (PA10>, AltFunction::AF6), - (PB7>, AltFunction::AF6), - (PB9>, AltFunction::AF6), - (PC14>, AltFunction::AF14), - ], - scl: [ - (PA9>, AltFunction::AF6), - (PB6>, AltFunction::AF6), - (PB8>, AltFunction::AF6), - (PB7>, AltFunction::AF14), - ], -); diff --git a/src/i2c/mod.rs b/src/i2c/mod.rs index 204026a..17a66ba 100644 --- a/src/i2c/mod.rs +++ b/src/i2c/mod.rs @@ -4,11 +4,15 @@ pub mod blocking; #[cfg(feature = "i2c-nonblocking")] pub mod nonblocking; +use core::ops::Deref; + #[cfg(feature = "i2c-nonblocking")] pub use nonblocking::*; pub mod config; +use crate::gpio; +use crate::pac::{self, i2c as i2c1}; use crate::rcc::*; pub use config::Config; @@ -53,44 +57,48 @@ pub enum Error { IncorrectFrameSize(usize), } -/// I2C SDA pin -pub trait SDAPin { - fn setup(&self); - fn release(self) -> Self; +pub trait Instance: + crate::Sealed + Deref + Enable + Reset + gpio::alt::I2cCommon +{ + #[doc(hidden)] + fn ptr() -> *const i2c1::RegisterBlock; } -/// I2C SCL pin -pub trait SCLPin { - fn setup(&self); - fn release(self) -> Self; +// Implemented by all I2C instances +macro_rules! i2c { + ($I2C:ty: $I2c:ident) => { + pub type $I2c = I2c<$I2C>; + + impl Instance for $I2C { + fn ptr() -> *const i2c1::RegisterBlock { + <$I2C>::ptr() as *const _ + } + } + }; } -pub trait I2cExt { - fn i2c( +i2c! { pac::I2C: I2c1 } + +pub trait I2cExt: Sized + Instance { + fn i2c( self, - sda: SDA, - scl: SCL, + pins: (impl Into, impl Into), config: impl Into, rcc: &mut Rcc, - ) -> I2c - where - SDA: SDAPin, - SCL: SCLPin; + ) -> I2c; } /// I2C abstraction #[cfg(feature = "i2c-blocking")] -pub struct I2c { +pub struct I2c { i2c: I2C, - sda: SDA, - scl: SCL, + pins: (I2C::Scl, I2C::Sda), } #[cfg(feature = "i2c-nonblocking")] -pub struct I2c { +pub struct I2c { i2c: I2C, - sda: SDA, - scl: SCL, + pins: (I2C::Scl, I2C::Sda), address: u16, watchdog: u16, // on each start set to 10, on each stop set to 0 index: usize, diff --git a/src/i2c/nonblocking.rs b/src/i2c/nonblocking.rs index c7f9c1b..7718afd 100644 --- a/src/i2c/nonblocking.rs +++ b/src/i2c/nonblocking.rs @@ -1,12 +1,11 @@ //! I2C -use crate::gpio::*; -use crate::gpio::{AltFunction, OpenDrain, Output}; use crate::i2c::config::Config; -use crate::i2c::{Error, I2c, I2cDirection, I2cExt, I2cResult, SCLPin, SDAPin}; +use crate::i2c::{Error, I2c, I2cDirection, I2cExt, I2cResult}; use crate::rcc::*; -use crate::stm32::I2C; use nb::Error::{Other, WouldBlock}; +use super::Instance; + pub trait I2cControl { /// Start listening for an interrupt event, will also enable non_blocking mode fn listen(&mut self); @@ -99,508 +98,429 @@ macro_rules! flush_rxdr { }; } -macro_rules! i2c { - ($I2CX:ident, $i2cx:ident, - sda: [ $(($PSDA:ty, $AFSDA:expr),)+ ], - scl: [ $(($PSCL:ty, $AFSCL:expr),)+ ], - ) => { - $( - impl SDAPin<$I2CX> for $PSDA { - fn setup(&self) { - self.set_alt_mode($AFSDA) - } - - fn release(self) -> Self { - self.into_open_drain_output() - } - } - )+ +impl I2cExt for I2C { + fn i2c( + self, + pins: (impl Into, impl Into), + config: impl Into, + rcc: &mut Rcc, + ) -> I2c { + I2c::new(self, pins, config, rcc) + } +} - $( - impl SCLPin<$I2CX> for $PSCL { - fn setup(&self) { - self.set_alt_mode($AFSCL) - } +impl I2c { + pub fn new( + i2c: I2C, + pins: (impl Into, impl Into), + config: impl Into, + rcc: &mut Rcc, + ) -> Self { + let config = config.into(); + I2C::enable(rcc); + I2C::reset(rcc); + + // Make sure the I2C unit is disabled so we can configure it + i2c.cr1.modify(|_, w| w.pe().clear_bit()); + + // Setup protocol timings + let timing_bits = config.timing_bits(rcc.clocks.apb_clk); + i2c.timingr.write(|w| unsafe { w.bits(timing_bits) }); + + // Enable the I2C processing + i2c.cr1.modify(|_, w| unsafe { + w.pe().set_bit(); + w.dnf().bits(config.digital_filter); + w.anfoff().bit(!config.analog_filter) + }); + + if config.slave_address_1 > 0 { + i2c.oar1.write(|w| unsafe { + w.oa1().bits(config.slave_address_1); + w.oa1mode().bit(config.address_11bits); + w.oa1en().set_bit() + }); + // Enable acknowlidge control + i2c.cr1.modify(|_, w| w.sbc().set_bit()); + } - fn release(self) -> Self { - self.into_open_drain_output() - } - } - )+ - - impl I2cExt<$I2CX> for $I2CX { - fn i2c( - self, - sda: SDA, - scl: SCL, - config: impl Into, - rcc: &mut Rcc, - ) -> I2c<$I2CX, SDA, SCL> - where - SDA: SDAPin<$I2CX>, - SCL: SCLPin<$I2CX>, - { - I2c::$i2cx(self, sda, scl, config, rcc) - } + if config.slave_address_2 > 0 { + i2c.oar2.write(|w| unsafe { + w.oa2msk().bits(config.slave_address_mask as u8); + w.oa2().bits(config.slave_address_2); + w.oa2en().set_bit() + }); + // Enable acknowlidge control + i2c.cr1.modify(|_, w| w.sbc().set_bit()); } - impl I2c<$I2CX, SDA, SCL> where - SDA: SDAPin<$I2CX>, - SCL: SCLPin<$I2CX> - { - pub fn $i2cx(i2c: $I2CX, sda: SDA, scl: SCL, config: impl Into, rcc: &mut Rcc) -> Self - where - SDA: SDAPin<$I2CX>, - SCL: SCLPin<$I2CX>, - { - let config = config.into(); - $I2CX::enable(rcc); - $I2CX::reset(rcc); - - // Make sure the I2C unit is disabled so we can configure it - i2c.cr1.modify(|_, w| w.pe().clear_bit()); - - // Setup protocol timings - let timing_bits = config.timing_bits(rcc.clocks.apb_clk); - i2c.timingr.write(|w| unsafe { w.bits(timing_bits) }); - - // Enable the I2C processing - i2c.cr1.modify(|_, w| unsafe { - w.pe() - .set_bit() - .dnf() - .bits(config.digital_filter) - .anfoff() - .bit(!config.analog_filter) - }); + // Enable pins + let pins = (pins.0.into(), pins.1.into()); + I2c { + i2c, + pins, + address: 0, + watchdog: 0, + index: 0, + length: 0, + errors: 0, + length_write_read: 0, + data: [0_u8; 255], + } + } + pub fn release(self) -> (I2C, (I2C::Sda, I2C::Scl)) { + (self.i2c, self.pins) + } +} // I2c + +impl I2cControl for I2c { + /// Starts listening for an interrupt event + fn listen(&mut self) { + self.i2c.cr1.modify(|_, w| { + w.txie().set_bit(); + w.addrie().set_bit(); + w.rxie().set_bit(); + w.nackie().set_bit(); + w.stopie().set_bit(); + w.errie().set_bit(); + w.tcie().set_bit() + }); + } - if config.slave_address_1 > 0 { - i2c.oar1.write(|w| unsafe { - w.oa1().bits(config.slave_address_1) - .oa1mode().bit(config.address_11bits) - .oa1en().set_bit() - }); - // Enable acknowlidge control - i2c.cr1.modify(|_, w| w.sbc().set_bit() ); - } - - if config.slave_address_2 > 0 { - i2c.oar2.write( |w| unsafe { - w.oa2msk().bits( config.slave_address_mask as u8) - .oa2().bits(config.slave_address_2) - .oa2en().set_bit() - }); - // Enable acknowlidge control - i2c.cr1.modify(|_, w| w.sbc().set_bit() ); - } - - // Enable pins - sda.setup(); - scl.setup(); - I2c { i2c, sda, scl, - address:0, - watchdog:0, - index: 0, - length:0, - errors:0, - length_write_read:0, - data:[0_u8;255] - } - } - pub fn release(self) -> ($I2CX, SDA, SCL) { - (self.i2c, self.sda.release(), self.scl.release()) - } - } // I2c - - impl I2cControl for I2c<$I2CX, SDA, SCL> { - /// Starts listening for an interrupt event - fn listen(&mut self) { - self.i2c.cr1.modify(|_, w| - w.txie().set_bit() - .addrie().set_bit() - .rxie().set_bit() - .nackie().set_bit() - .stopie().set_bit() - .errie().set_bit() - .tcie().set_bit() - ); - } + /// Stop listening for an interrupt event + fn unlisten(&mut self) { + self.i2c.cr1.modify(|_, w| { + w.txie().clear_bit(); + w.rxie().clear_bit(); + w.addrie().clear_bit(); + w.nackie().clear_bit(); + w.stopie().clear_bit(); + w.tcie().clear_bit(); + w.errie().clear_bit() + }); + } - /// Stop listening for an interrupt event - fn unlisten(&mut self) { - self.i2c.cr1.modify(|_, w| - w.txie().clear_bit() - .rxie().clear_bit() - .addrie().clear_bit() - .nackie().clear_bit() - .stopie().clear_bit() - .tcie().clear_bit() - .errie().clear_bit() - ); - } + /// get the global error counter. Reset to 0 after read + fn get_errors_reset(&mut self) -> usize { + let result = self.errors; + self.errors = 0; + result + } - /// get the global error counter. Reset to 0 after read - fn get_errors_reset(&mut self) -> usize { - let result = self.errors; - self.errors = 0; - result + /// optional function + /// If used call this function once per 10th second. After 10 calls (after a second) + /// i2c will be forcefully reset, if the watchdog counter is still greater than zero + fn execute_watchdog(&mut self) { + match self.watchdog { + 0 => return, + 1 => { + self.errors += 1; + self.watchdog = 0; + // Disable I2C processing, resetting all hardware state machines + self.i2c.cr1.modify(|_, w| w.pe().clear_bit()); + // force enough wait states for the pe clear + let _ = self.i2c.cr1.read(); + // Enable the I2C processing again + self.i2c.cr1.modify(|_, w| w.pe().set_bit()); } + _ => self.watchdog -= 1, + } + } - - /// optional function - /// If used call this function once per 10th second. After 10 calls (after a second) - /// i2c will be forcefully reset, if the watchdog counter is still greater than zero - fn execute_watchdog(&mut self) { - match self.watchdog { - 0 => return, - 1 => { - self.errors += 1; - self.watchdog = 0; - // Disable I2C processing, resetting all hardware state machines - self.i2c.cr1.modify(|_, w| w.pe().clear_bit()); - // force enough wait states for the pe clear - let _ = self.i2c.cr1.read(); - // Enable the I2C processing again - self.i2c.cr1.modify(|_, w| w.pe().set_bit()); - }, - _ => {self.watchdog -= 1}, - } + /// Check the isr flags. If the transaction still is not finished + /// This funcion can be called inside the block! macro for blocking mode, + /// or inside an I2C interrupt, in case the isr is enalbed + fn check_isr_flags(&mut self) -> nb::Result { + let isr = self.i2c.isr.read(); + + if isr.berr().bit_is_set() { + self.i2c.icr.write(|w| w.berrcf().set_bit()); + self.errors += 1; + return Err(Other(Error::BusError)); + } else if isr.arlo().bit_is_set() { + self.i2c.icr.write(|w| w.arlocf().set_bit()); + return Err(Other(Error::ArbitrationLost)); + } else if isr.nackf().bit_is_set() { + self.i2c.icr.write(|w| w.nackcf().set_bit()); + // Make one extra loop to wait on the stop condition + return Err(WouldBlock); + } else if isr.txis().bit_is_set() { + // Put byte on the wire + if self.index < self.length { + self.i2c + .txdr + .write(|w| unsafe { w.txdata().bits(self.data[self.index]) }); + self.index += 1; // ok previous byte is send now } - - /// Check the isr flags. If the transaction still is not finished - /// This funcion can be called inside the block! macro for blocking mode, - /// or inside an I2C interrupt, in case the isr is enalbed - fn check_isr_flags(&mut self) -> nb::Result< I2cResult, Error>{ - let isr = self.i2c.isr.read(); - - if isr.berr().bit_is_set() { - self.i2c.icr.write(|w| w.berrcf().set_bit()); - self.errors += 1; - return Err( Other(Error::BusError)) - } else - if isr.arlo().bit_is_set() { - self.i2c.icr.write(|w| w.arlocf().set_bit()); - return Err( Other(Error::ArbitrationLost)) - }else - if isr.nackf().bit_is_set() { - self.i2c.icr.write(|w| w.nackcf().set_bit()); - // Make one extra loop to wait on the stop condition - return Err( WouldBlock) - } else - if isr.txis().bit_is_set() { - // Put byte on the wire - if self.index < self.length { - self.i2c.txdr.write(|w| unsafe { w.txdata().bits(self.data[self.index]) }); - self.index += 1; // ok previous byte is send now - } - return Err( WouldBlock) - } else - if isr.rxne().bit_is_set() { - // read byte from the wire - if self.index < self.length { - self.data[self.index] = self.i2c.rxdr.read().rxdata().bits(); - self.index += 1; - }else { - // anyway read the result to clear the rxne flag - flush_rxdr!(self.i2c); - } - return Err( WouldBlock) - } else - if isr.stopf().bit_is_set() { - // Clear the stop condition flag - self.i2c.icr.write(|w| w.stopcf().set_bit()); - // Disable the watchdog - self.watchdog = 0; - if self.index == 0 { - self.errors += 1; - return Err( Other(Error::Nack)) - } else - { - // figure out the direction - let direction = if isr.dir().bit_is_set() - { - I2cDirection::MasterReadSlaveWrite - } else { - I2cDirection::MasterWriteSlaveRead - }; - // return the actual amount of data (self.index), not the requested (self.length) - // application must evaluate the size of the frame - return Ok( I2cResult::Data(self.address, direction, &self.data[0..self.index]) ) - } - }else - if isr.tc().bit_is_set() { - // This condition Will only happen when autoend is 0 in master mode (write with subb addressing) - // Flag is reset by a start or stop condition. - // no stop condition will be generated in this transaction so evaluate the result here - if self.index < self.length { - self.index += 1; // ok previous byte is send now - } - if self.index == self.length { - // ok start the second part of the transaction - // reSTART and prepare to receive bytes into `rcv_buffer` - self.length = self.length_write_read; - self.length_write_read = 0; - self.index = 0; - self.i2c.cr2.write(|w| unsafe { - w - // Set number of bytes to transfer - .nbytes().bits(self.length as u8) - // Set address to transfer to/from - .sadd().bits((self.address << 1) as u16) - // 7-bit addressing mode - .add10().clear_bit() - // Set transfer direction to read - .rd_wrn().set_bit() - // Automatic end mode - .autoend().set_bit() - .reload().clear_bit() - // Start transfer - .start().set_bit() - }); - // not yet ready here - return Err( WouldBlock) - } else - if self.index == 0 { - self.i2c.cr2.modify(|_, w| { - w.stop().set_bit() - }); - self.errors += 1; - return Err( Other(Error::Nack)) - } else - { - self.i2c.cr2.modify(|_, w| { - w.stop().set_bit() - }); - self.errors += 1; - return Err(Other(Error::IncorrectFrameSize(self.index))) - } - } else - if isr.tcr().bit_is_set() { - // This condition Will only happen when reload == 1 and sbr == 1 (slave) and nbytes was written. - // Send a NACK, set nbytes to clear tcr flag - self.i2c.cr2.modify(|_, w| unsafe { - w.nack().set_bit().nbytes().bits( 1 as u8) - }); - // Make one extra loop here to wait on the stop condition - return Err( WouldBlock) - - } else - if isr.addr().bit_is_set() { - // handle the slave device case, addressed by a master - let current_address = isr.addcode().bits() as u16; - self.address = current_address; - // guard against misbehavior - self.watchdog = 10; - - // figure out the direction. - let direction = if isr.dir().bit_is_set() - { - I2cDirection::MasterReadSlaveWrite - } else { - // Start the master write slave read transaction fully automatically here - // Set the nbytes to the max size and prepare to receive bytes into `buffer`. - self.length = self.data.len(); - self.index = 0; - self.i2c.cr2.modify(|_, w| unsafe { - // Set number of bytes to transfer: as many as internal buffer - w.nbytes().bits(self.length as u8) - // during sending nbytes automatically send a ACK, stretch clock after last byte - .reload().set_bit() - }); - // end address phase, release clock stretching - self.i2c.icr.write(|w| w.addrcf().set_bit()); - // return result - I2cDirection::MasterWriteSlaveRead - }; - - // do not yet release the clock stretching here - return Ok(I2cResult::Addressed(current_address, direction)) - } - return Err( WouldBlock) - } // check_isr_flags - } // i2c - - impl I2cMaster for I2c<$I2CX, SDA, SCL> { - - - fn master_write(&mut self, addr: u16, data: &[u8]) -> nb::Result<(), Error>{ - // Check if the bus is free - if self.i2c.cr2.read().start().bit_is_set() { - return Err(nb::Error::WouldBlock) - }; - self.watchdog = 10; - let buflen = data.len(); - assert!(buflen < 256 && buflen > 0); - self.length = buflen; - self.data[..buflen].copy_from_slice(data); - self.index = 0; - self.address = addr; - self.length_write_read = 0; - - self.i2c.cr2.modify(|_, w| unsafe { - w - // Start transfer - .start().set_bit() - // Set number of bytes to transfer - .nbytes().bits(buflen as u8) - // Set address to transfer to/from - .sadd().bits((addr << 1) as u16) - // Set transfer direction to write - .rd_wrn().clear_bit() - // Automatic end mode - .autoend().bit(true) - .reload().clear_bit() - }); - // in non-blocking mode the result is not yet available - Ok (()) + return Err(WouldBlock); + } else if isr.rxne().bit_is_set() { + // read byte from the wire + if self.index < self.length { + self.data[self.index] = self.i2c.rxdr.read().rxdata().bits(); + self.index += 1; + } else { + // anyway read the result to clear the rxne flag + flush_rxdr!(self.i2c); } - fn master_write_read(&mut self, addr: u16, data: &[u8], read_len:u8) -> nb::Result<(), Error>{ - // Check if the bus is free - if self.i2c.cr2.read().start().bit_is_set() { - return Err(nb::Error::WouldBlock) + return Err(WouldBlock); + } else if isr.stopf().bit_is_set() { + // Clear the stop condition flag + self.i2c.icr.write(|w| w.stopcf().set_bit()); + // Disable the watchdog + self.watchdog = 0; + if self.index == 0 { + self.errors += 1; + return Err(Other(Error::Nack)); + } else { + // figure out the direction + let direction = if isr.dir().bit_is_set() { + I2cDirection::MasterReadSlaveWrite + } else { + I2cDirection::MasterWriteSlaveRead }; - self.watchdog = 10; - let buflen = data.len(); - assert!(buflen < 256 && buflen > 0); - self.length = buflen; - self.data[..buflen].copy_from_slice(data); + // return the actual amount of data (self.index), not the requested (self.length) + // application must evaluate the size of the frame + return Ok(I2cResult::Data( + self.address, + direction, + &self.data[0..self.index], + )); + } + } else if isr.tc().bit_is_set() { + // This condition Will only happen when autoend is 0 in master mode (write with subb addressing) + // Flag is reset by a start or stop condition. + // no stop condition will be generated in this transaction so evaluate the result here + if self.index < self.length { + self.index += 1; // ok previous byte is send now + } + if self.index == self.length { + // ok start the second part of the transaction + // reSTART and prepare to receive bytes into `rcv_buffer` + self.length = self.length_write_read; + self.length_write_read = 0; self.index = 0; - self.address = addr; - self.length_write_read = read_len as usize; - - self.i2c.cr2.modify(|_, w| unsafe { - w - // Start transfer - .start().set_bit() - // Set number of bytes to transfer - .nbytes().bits(buflen as u8) - // Set address to transfer to/from - .sadd().bits((addr << 1) as u16) - // Set transfer direction to write - .rd_wrn().clear_bit() - // Automatic end mode - .autoend().bit(false) - .reload().clear_bit() + self.i2c.cr2.write(|w| unsafe { + // Set number of bytes to transfer + w.nbytes().bits(self.length as u8); + // Set address to transfer to/from + w.sadd().bits((self.address << 1) as u16); + // 7-bit addressing mode + w.add10().clear_bit(); + // Set transfer direction to read + w.rd_wrn().set_bit(); + // Automatic end mode + w.autoend().set_bit(); + w.reload().clear_bit(); + // Start transfer + w.start().set_bit() }); - // in non-blocking mode the result is not yet available - Ok (()) + // not yet ready here + return Err(WouldBlock); + } else if self.index == 0 { + self.i2c.cr2.modify(|_, w| w.stop().set_bit()); + self.errors += 1; + return Err(Other(Error::Nack)); + } else { + self.i2c.cr2.modify(|_, w| w.stop().set_bit()); + self.errors += 1; + return Err(Other(Error::IncorrectFrameSize(self.index))); } - - - fn master_read(&mut self, addr: u16, length: u8) -> nb::Result<(), Error>{ - // Wait for any previous address sequence to end automatically. - // This could be up to 50% of a bus cycle (ie. up to 0.5/freq) - if self.i2c.cr2.read().start().bit_is_set() { - return Err(nb::Error::WouldBlock) - }; - // Flush rxdr register - self.watchdog = 10; - self.i2c.rxdr.read().rxdata().bits(); - self.length = length as usize; + } else if isr.tcr().bit_is_set() { + // This condition Will only happen when reload == 1 and sbr == 1 (slave) and nbytes was written. + // Send a NACK, set nbytes to clear tcr flag + self.i2c + .cr2 + .modify(|_, w| unsafe { w.nack().set_bit().nbytes().bits(1 as u8) }); + // Make one extra loop here to wait on the stop condition + return Err(WouldBlock); + } else if isr.addr().bit_is_set() { + // handle the slave device case, addressed by a master + let current_address = isr.addcode().bits() as u16; + self.address = current_address; + // guard against misbehavior + self.watchdog = 10; + + // figure out the direction. + let direction = if isr.dir().bit_is_set() { + I2cDirection::MasterReadSlaveWrite + } else { + // Start the master write slave read transaction fully automatically here + // Set the nbytes to the max size and prepare to receive bytes into `buffer`. + self.length = self.data.len(); self.index = 0; - self.address = addr; - - for i in 0.. length as usize { - self.data[i] = 0; - } - - // Set START and prepare to receive bytes into `buffer`. - // The START bit can be set even if the bus - // is BUSY or I2C is in slave mode. self.i2c.cr2.modify(|_, w| unsafe { - w - // Start transfer - .start().set_bit() - // Set number of bytes to transfer - .nbytes().bits(length as u8) - // Set address to transfer to/from - .sadd().bits((addr << 1) as u16) - // Set transfer direction to read - .rd_wrn().set_bit() - // automatic end mode - .autoend().set_bit() - .reload().clear_bit() + // Set number of bytes to transfer: as many as internal buffer + w.nbytes().bits(self.length as u8); + // during sending nbytes automatically send a ACK, stretch clock after last byte + w.reload().set_bit() }); - // in non-blocking mode the result is not yet available - Ok (()) - } - - fn get_address(&self) -> u16 { - self.address - } + // end address phase, release clock stretching + self.i2c.icr.write(|w| w.addrcf().set_bit()); + // return result + I2cDirection::MasterWriteSlaveRead + }; - /// return a non mutable slice to the internal data, with the size of the last transaction - fn get_data(&self) -> &[u8] { - &self.data[0..self.length] - } + // do not yet release the clock stretching here + return Ok(I2cResult::Addressed(current_address, direction)); } + return Err(WouldBlock); + } // check_isr_flags +} // i2c + +impl I2cMaster for I2c { + fn master_write(&mut self, addr: u16, data: &[u8]) -> nb::Result<(), Error> { + // Check if the bus is free + if self.i2c.cr2.read().start().bit_is_set() { + return Err(nb::Error::WouldBlock); + }; + self.watchdog = 10; + let buflen = data.len(); + assert!(buflen < 256 && buflen > 0); + self.length = buflen; + self.data[..buflen].copy_from_slice(data); + self.index = 0; + self.address = addr; + self.length_write_read = 0; + + self.i2c.cr2.modify(|_, w| unsafe { + // Start transfer + w.start().set_bit(); + // Set number of bytes to transfer + w.nbytes().bits(buflen as u8); + // Set address to transfer to/from + w.sadd().bits((addr << 1) as u16); + // Set transfer direction to write + w.rd_wrn().clear_bit(); + // Automatic end mode + w.autoend().bit(true); + w.reload().clear_bit() + }); + // in non-blocking mode the result is not yet available + Ok(()) + } + fn master_write_read(&mut self, addr: u16, data: &[u8], read_len: u8) -> nb::Result<(), Error> { + // Check if the bus is free + if self.i2c.cr2.read().start().bit_is_set() { + return Err(nb::Error::WouldBlock); + }; + self.watchdog = 10; + let buflen = data.len(); + assert!(buflen < 256 && buflen > 0); + self.length = buflen; + self.data[..buflen].copy_from_slice(data); + self.index = 0; + self.address = addr; + self.length_write_read = read_len as usize; + + self.i2c.cr2.modify(|_, w| unsafe { + // Start transfer + w.start().set_bit(); + // Set number of bytes to transfer + w.nbytes().bits(buflen as u8); + // Set address to transfer to/from + w.sadd().bits((addr << 1) as u16); + // Set transfer direction to write + w.rd_wrn().clear_bit(); + // Automatic end mode + w.autoend().bit(false); + w.reload().clear_bit() + }); + // in non-blocking mode the result is not yet available + Ok(()) + } - impl I2cSlave for I2c<$I2CX, SDA, SCL> { - - fn slave_sbc(&mut self, sbc_enabled: bool) { - // enable acknowlidge control - self.i2c.cr1.modify(|_, w| w.sbc().bit(sbc_enabled) ); - } + fn master_read(&mut self, addr: u16, length: u8) -> nb::Result<(), Error> { + // Wait for any previous address sequence to end automatically. + // This could be up to 50% of a bus cycle (ie. up to 0.5/freq) + if self.i2c.cr2.read().start().bit_is_set() { + return Err(nb::Error::WouldBlock); + }; + // Flush rxdr register + self.watchdog = 10; + self.i2c.rxdr.read().rxdata().bits(); + self.length = length as usize; + self.index = 0; + self.address = addr; + + for i in 0..length as usize { + self.data[i] = 0; + } - fn set_address(&mut self, address:u16) { - self.i2c.oar1.write(|w| unsafe { - w.oa1().bits(address as _) - .oa1en().clear_bit() - }); - // set the 7 bits address - self.i2c.oar1.write(|w| unsafe { - w.oa1().bits(address as _) - .oa1mode().clear_bit() - .oa1en().set_bit() - }); - } + // Set START and prepare to receive bytes into `buffer`. + // The START bit can be set even if the bus + // is BUSY or I2C is in slave mode. + self.i2c.cr2.modify(|_, w| unsafe { + // Start transfer + w.start().set_bit(); + // Set number of bytes to transfer + w.nbytes().bits(length as u8); + // Set address to transfer to/from + w.sadd().bits((addr << 1) as u16); + // Set transfer direction to read + w.rd_wrn().set_bit(); + // automatic end mode + w.autoend().set_bit(); + w.reload().clear_bit() + }); + // in non-blocking mode the result is not yet available + Ok(()) + } - fn slave_write(&mut self, bytes: &[u8]) -> Result<(), Error> { - let buflen = bytes.len(); - assert!(buflen < 256 && buflen > 0); + fn get_address(&self) -> u16 { + self.address + } - self.length = buflen; - self.data[..buflen].copy_from_slice(bytes); - self.index = 0; + /// return a non mutable slice to the internal data, with the size of the last transaction + fn get_data(&self) -> &[u8] { + &self.data[0..self.length] + } +} - // Set the nbytes and prepare to send bytes into `buffer`. - self.i2c.cr2.modify(|_, w| unsafe { - w.nbytes().bits( buflen as u8) - .reload().clear_bit() - }); - // flush i2c tx register - self.i2c.isr.write(|w| w.txe().set_bit()); - // end address phase, release clock stretching - self.i2c.icr.write(|w| w.addrcf().set_bit() ); +impl I2cSlave for I2c { + fn slave_sbc(&mut self, sbc_enabled: bool) { + // enable acknowlidge control + self.i2c.cr1.modify(|_, w| w.sbc().bit(sbc_enabled)); + } - // in non-blocking mode the result is not yet available - Ok (()) - } - fn get_address(&self) -> u16 { - self.address - } - /// return a non mutable slice to the internal data, with the size of the last transaction - fn get_data(&self) -> &[u8] { - &self.data[0..self.index] - } - } + fn set_address(&mut self, address: u16) { + self.i2c.oar1.write(|w| unsafe { + w.oa1().bits(address as _); + w.oa1en().clear_bit() + }); + // set the 7 bits address + self.i2c.oar1.write(|w| unsafe { + w.oa1().bits(address as _); + w.oa1mode().clear_bit(); + w.oa1en().set_bit() + }); + } + fn slave_write(&mut self, bytes: &[u8]) -> Result<(), Error> { + let buflen = bytes.len(); + assert!(buflen < 256 && buflen > 0); + + self.length = buflen; + self.data[..buflen].copy_from_slice(bytes); + self.index = 0; + + // Set the nbytes and prepare to send bytes into `buffer`. + self.i2c.cr2.modify(|_, w| unsafe { + w.nbytes().bits(buflen as u8); + w.reload().clear_bit() + }); + // flush i2c tx register + self.i2c.isr.write(|w| w.txe().set_bit()); + // end address phase, release clock stretching + self.i2c.icr.write(|w| w.addrcf().set_bit()); + + // in non-blocking mode the result is not yet available + Ok(()) + } + fn get_address(&self) -> u16 { + self.address + } + /// return a non mutable slice to the internal data, with the size of the last transaction + fn get_data(&self) -> &[u8] { + &self.data[0..self.index] } } - -i2c!( - I2C, - i2c1, - sda: [ - (PA10>, AltFunction::AF6), - (PB7>, AltFunction::AF6), - (PB9>, AltFunction::AF6), - (PC14>, AltFunction::AF14), - ], - scl: [ - (PA9>, AltFunction::AF6), - (PB6>, AltFunction::AF6), - (PB8>, AltFunction::AF6), - (PB7>, AltFunction::AF14), - ], -); diff --git a/src/lib.rs b/src/lib.rs index c0fe198..2087464 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,3 +48,9 @@ mod sealed { #[cfg(feature = "device-selected")] pub(crate) use sealed::Sealed; + +fn stripped_type_name() -> &'static str { + let s = core::any::type_name::(); + let p = s.split("::"); + p.last().unwrap() +} diff --git a/src/rcc/clockout.rs b/src/rcc/clockout.rs index f613d28..74f60e1 100644 --- a/src/rcc/clockout.rs +++ b/src/rcc/clockout.rs @@ -1,11 +1,11 @@ -use crate::gpio::*; +use crate::gpio::{self, alt::rcc as alt}; use crate::rcc::*; use crate::stm32::RCC; -pub type LscoPin = PA2; +pub type LscoPin = gpio::PA2; pub struct Lsco { - pin: LscoPin, + pin: gpio::PA2>, } impl Lsco { @@ -20,7 +20,7 @@ impl Lsco { } pub fn release(self) -> LscoPin { - self.pin.into_analog() + self.pin.into_mode() } } @@ -40,21 +40,18 @@ impl LSCOExt for LscoPin { false } }; - self.set_alt_mode(AltFunction::AF0); + let pin = self.into_mode(); rcc.csr1.modify(|_, w| w.lscosel().bit(src_select_bit)); - Lsco { pin: self } + Lsco { pin } } } -pub struct Mco { - pin: PIN, +pub struct Mco { + pin: alt::Mco, src_bits: u8, } -impl Mco -where - PIN: MCOExt, -{ +impl Mco { pub fn enable(&mut self) { let rcc = unsafe { &(*RCC::ptr()) }; rcc.cfgr @@ -66,66 +63,61 @@ where rcc.cfgr.modify(|_, w| unsafe { w.mcosel().bits(0) }); } - pub fn release(self) -> PIN { - self.pin.release() + pub fn release(self) -> alt::Mco { + self.pin } } -pub trait MCOExt { - fn mco(self, src: MCOSrc, psc: Prescaler, rcc: &mut Rcc) -> Mco; - fn release(self) -> PIN; +pub trait MCOExt { + fn mco(self, src: MCOSrc, psc: Prescaler, rcc: &mut Rcc) -> Mco; + fn release(self) -> Self; } -macro_rules! mco { - ($($PIN:ty),+) => { - $( - impl MCOExt<$PIN> for $PIN { - fn mco(self, src: MCOSrc, psc: Prescaler, rcc: &mut Rcc) -> Mco<$PIN> { - let psc_bits = match psc { - Prescaler::NotDivided => 0b000, - Prescaler::Div2 => 0b001, - Prescaler::Div4 => 0b010, - Prescaler::Div8 => 0b011, - Prescaler::Div16 => 0b100, - Prescaler::Div32 => 0b101, - Prescaler::Div64 => 0b110, - _ => 0b111, - }; - - rcc.cfgr.modify(|_, w| unsafe { - w.mcopre().bits(psc_bits) - }); - - let src_bits = match src { - MCOSrc::SysClk => 0b001, - MCOSrc::HSI => { - rcc.enable_hsi(); - 0b011 - }, - MCOSrc::HSE => { - rcc.enable_hse(false); - 0b100 - }, - MCOSrc::LSI => { - rcc.enable_lsi(); - 0b110 - }, - MCOSrc::LSE => { - rcc.enable_lse(false); - 0b111 - }, - }; - - self.set_alt_mode(AltFunction::AF0); - Mco { src_bits, pin: self } - } - - fn release(self) -> $PIN { - self.into_analog() - } +impl MCOExt for PIN +where + PIN: Into + TryFrom, +{ + fn mco(self, src: MCOSrc, psc: Prescaler, rcc: &mut Rcc) -> Mco { + let psc_bits = match psc { + Prescaler::NotDivided => 0b000, + Prescaler::Div2 => 0b001, + Prescaler::Div4 => 0b010, + Prescaler::Div8 => 0b011, + Prescaler::Div16 => 0b100, + Prescaler::Div32 => 0b101, + Prescaler::Div64 => 0b110, + _ => 0b111, + }; + + rcc.cfgr.modify(|_, w| unsafe { w.mcopre().bits(psc_bits) }); + + let src_bits = match src { + MCOSrc::SysClk => 0b001, + MCOSrc::HSI => { + rcc.enable_hsi(); + 0b011 } - )+ - }; -} + MCOSrc::HSE => { + rcc.enable_hse(false); + 0b100 + } + MCOSrc::LSI => { + rcc.enable_lsi(); + 0b110 + } + MCOSrc::LSE => { + rcc.enable_lse(false); + 0b111 + } + }; -mco!(PA8, PA9, PF2); + Mco { + src_bits, + pin: self.into(), + } + } + + fn release(self) -> Self { + self.try_into().unwrap() + } +} diff --git a/src/rcc/enable.rs b/src/rcc/enable.rs index 33d7a4a..6594d26 100644 --- a/src/rcc/enable.rs +++ b/src/rcc/enable.rs @@ -99,7 +99,7 @@ macro_rules! bus_reset { macro_rules! bus { ($($PER:ident => ($busX:ty, $($en:ident)?, $($smen:ident)?, $($rst:ident)?),)+) => { $( - impl crate::Sealed for crate::stm32::$PER {} + impl crate::Sealed for crate::pac::$PER {} impl RccBus for crate::stm32::$PER { type Bus = $busX; } diff --git a/src/rtc.rs b/src/rtc.rs index d48fb5f..27c6dfd 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -125,20 +125,13 @@ impl Rtc { self.modify(|rb| { rb.dr.write(|w| unsafe { - w.dt() - .bits(dt) - .du() - .bits(du) - .mt() - .bit(mt > 0) - .mu() - .bits(mu) - .yt() - .bits(yt) - .yu() - .bits(yu) - .wdu() - .bits(date.day as u8) + w.dt().bits(dt); + w.du().bits(du); + w.mt().bit(mt > 0); + w.mu().bits(mu); + w.yt().bits(yt); + w.yu().bits(yu); + w.wdu().bits(date.day as u8) }); }); } @@ -149,20 +142,13 @@ impl Rtc { let (st, su) = bcd2_encode(time.seconds); self.modify(|rb| { rb.tr.write(|w| unsafe { - w.ht() - .bits(ht) - .hu() - .bits(hu) - .mnt() - .bits(mnt) - .mnu() - .bits(mnu) - .st() - .bits(st) - .su() - .bits(su) - .pm() - .clear_bit() + w.ht().bits(ht); + w.hu().bits(hu); + w.mnt().bits(mnt); + w.mnu().bits(mnu); + w.st().bits(st); + w.su().bits(su); + w.pm().clear_bit() }); rb.cr.modify(|_, w| w.fmt().bit(time.daylight_savings)); }); @@ -200,10 +186,8 @@ impl Rtc { self.modify(|rb| { rb.alrmassr.write(|w| unsafe { - w.maskss() - .bits(alarm.subseconds_mask_bits) - .ss() - .bits(alarm.subseconds) + w.maskss().bits(alarm.subseconds_mask_bits); + w.ss().bits(alarm.subseconds) }); rb.alrmar.write(|w| unsafe { w.wdsel().bit(alarm.use_weekday); @@ -233,10 +217,8 @@ impl Rtc { // self.modify(|rb| { // rb.alrmbssr.write(|w| unsafe { - // w.maskss() - // .bits(alarm.subseconds_mask_bits) - // .ss() - // .bits(alarm.subseconds) + // w.maskss().bits(alarm.subseconds_mask_bits); + // w.ss().bits(alarm.subseconds) // }); // rb.alrmbr.write(|w| unsafe { // w.wdsel().bit(alarm.use_weekday); @@ -304,17 +286,14 @@ impl Rtc { pin: PIN, freq: RtcCalibrationFrequency, ) { - pin.setup(); + let channel = pin.channel(); + let _pin = pin.setup(); self.modify(|rb| { rb.cr.modify(|_, w| unsafe { - w.osel() - .bits(0b0) - .out2en() - .bit(pin.channel()) - .cosel() - .bit(freq == RtcCalibrationFrequency::F1Hz) - .coe() - .set_bit() + w.osel().bits(0b0); + w.out2en().bit(channel); + w.cosel().bit(freq == RtcCalibrationFrequency::F1Hz); + w.coe().set_bit() }); }); todo!(); @@ -354,25 +333,27 @@ impl RtcExt for RTC { } pub trait RtcOutputPin { - fn setup(&self); + type AFPin; + fn setup(self) -> Self::AFPin; fn channel(&self) -> bool; - fn release(self) -> Self; + fn release(pin: Self::AFPin) -> Self; } macro_rules! rtc_out_pins { - ($($pin:ty: ($af_mode:expr, $ch:expr),)+) => { + ($($pin:ty: ($afpin:ty, $ch:expr),)+) => { $( impl RtcOutputPin for $pin { - fn setup(&self) { - self.set_alt_mode($af_mode); + type AFPin = $afpin; + fn setup(self) -> Self::AFPin { + self.into_mode() } fn channel(&self) -> bool { $ch } - fn release(self) -> Self { - self.into_analog() + fn release(pin: Self::AFPin) -> Self { + pin.into_mode() } } )+ @@ -380,8 +361,8 @@ macro_rules! rtc_out_pins { } rtc_out_pins! { - PA4: (AltFunction::AF3, true), - PC13: (AltFunction::AF3, false), + PA4: (PA4, true), + PC13: (PC13, false), } fn bcd2_encode(word: u32) -> (u8, u8) { diff --git a/src/serial/usart.rs b/src/serial/usart.rs index 6499bfc..5b94d60 100644 --- a/src/serial/usart.rs +++ b/src/serial/usart.rs @@ -1,12 +1,14 @@ -use core::fmt; use core::marker::PhantomData; +use core::{fmt, ops::Deref}; -use crate::gpio::{AltFunction, *}; -use crate::prelude::*; -use crate::rcc::*; +use crate::gpio::{ + alt::{SerialAsync as CommonPins, SerialRs485 as Rs485}, + PushPull, +}; +use crate::rcc::{self, *}; use crate::serial; use crate::serial::config::*; -use crate::stm32::*; +use crate::{gpio, pac, prelude::*}; use nb::block; @@ -70,518 +72,400 @@ impl Event { } } +impl Instance for pac::USART1 { + fn ptr() -> *const pac::usart1::RegisterBlock { + pac::USART1::ptr() + } +} +impl Instance for pac::USART2 { + fn ptr() -> *const pac::usart1::RegisterBlock { + pac::USART2::ptr() + } +} + +pub trait Instance: + crate::Sealed + + rcc::Enable + + rcc::Reset + + CommonPins + + Rs485 + + Deref +{ + #[doc(hidden)] + fn ptr() -> *const pac::usart1::RegisterBlock; +} + /// Serial receiver -pub struct Rx { +pub struct Rx { _usart: PhantomData, + _pin: USART::Rx, } /// Serial transmitter -pub struct Tx { +pub struct Tx { _usart: PhantomData, + _pin: USART::Tx, } /// Serial abstraction -pub struct Serial { +pub struct Serial { tx: Tx, rx: Rx, usart: USART, + _depin: Option, } -// Serial TX pin -pub trait TxPin { - fn setup(&self); - fn release(self) -> Self; -} +/// A filler type for when the Tx pin is unnecessary +pub use gpio::NoPin as NoTx; +/// A filler type for when the Rx pin is unnecessary +pub use gpio::NoPin as NoRx; -// Serial RX pin -pub trait RxPin { - fn setup(&self); - fn release(self) -> Self; +pub trait SerialExt: Sized + Instance { + fn usart( + self, + pins: (impl Into>, impl Into>), + config: serial::Config, + rcc: &mut Rcc, + ) -> Result, InvalidConfig>; + fn rs485( + self, + pins: ( + impl Into>, + impl Into>, + impl Into, + ), + config: serial::Config, + rcc: &mut Rcc, + ) -> Result, InvalidConfig>; } -pub struct NoTx; +impl SerialExt for USART { + fn usart( + self, + pins: (impl Into>, impl Into>), + config: serial::Config, + rcc: &mut Rcc, + ) -> Result, InvalidConfig> { + Serial::new(self, pins, config, rcc) + } + fn rs485( + self, + pins: ( + impl Into>, + impl Into>, + impl Into, + ), + config: serial::Config, + rcc: &mut Rcc, + ) -> Result, InvalidConfig> { + Serial::rs485(self, pins, config, rcc) + } +} -impl TxPin for NoTx { - fn setup(&self) {} +impl fmt::Write for Serial +where + Serial: hal::serial::Write, +{ + fn write_str(&mut self, s: &str) -> fmt::Result { + let _ = s.as_bytes().iter().map(|c| block!(self.write(*c))).last(); + Ok(()) + } +} - fn release(self) -> Self { - self +impl fmt::Write for Tx +where + Tx: hal::serial::Write, +{ + fn write_str(&mut self, s: &str) -> fmt::Result { + let _ = s.as_bytes().iter().map(|c| block!(self.write(*c))).last(); + Ok(()) } } -pub struct NoRx; -impl RxPin for NoRx { - fn setup(&self) {} +impl Rx { + pub fn listen(&mut self) { + let usart = unsafe { &(*USART::ptr()) }; + usart.cr1_disabled().modify(|_, w| w.rxneie().set_bit()); + } + + /// Stop listening for an interrupt event + pub fn unlisten(&mut self) { + let usart = unsafe { &(*USART::ptr()) }; + usart.cr1_disabled().modify(|_, w| w.rxneie().clear_bit()); + } - fn release(self) -> Self { - self + /// Return true if the rx register is not empty (and can be read) + pub fn is_rxne(&self) -> bool { + let usart = unsafe { &(*USART::ptr()) }; + usart.isr_disabled().read().rxne().bit_is_set() } } -// Driver enable pin -pub trait DriverEnablePin { - fn setup(&self); - fn release(self) -> Self; +impl hal::serial::Read for Rx { + type Error = Error; + + fn read(&mut self) -> nb::Result { + let usart = unsafe { &(*USART::ptr()) }; + let isr = usart.isr_enabled().read(); + + Err(if isr.pe().bit_is_set() { + usart.icr.write(|w| w.pecf().set_bit()); + nb::Error::Other(Error::Parity) + } else if isr.fe().bit_is_set() { + usart.icr.write(|w| w.fecf().set_bit()); + nb::Error::Other(Error::Framing) + } else if isr.ne().bit_is_set() { + usart.icr.write(|w| w.necf().set_bit()); + nb::Error::Other(Error::Noise) + } else if isr.ore().bit_is_set() { + usart.icr.write(|w| w.orecf().set_bit()); + nb::Error::Other(Error::Overrun) + } else if isr.rxfne().bit_is_set() { + return Ok(usart.rdr.read().bits() as u8); + } else { + nb::Error::WouldBlock + }) + } } -// Serial pins -pub trait Pins { - const DRIVER_ENABLE: bool; +impl hal::serial::Read for Serial { + type Error = Error; - fn setup(&self); - fn release(self) -> Self; + fn read(&mut self) -> nb::Result { + self.rx.read() + } } -// Duplex mode -impl Pins for (TX, RX) -where - TX: TxPin, - RX: RxPin, -{ - const DRIVER_ENABLE: bool = false; +impl Tx { + /// Starts listening for an interrupt event + pub fn listen(&mut self) { + let usart = unsafe { &(*USART::ptr()) }; + usart.cr1_disabled().modify(|_, w| w.txeie().set_bit()); + } - fn setup(&self) { - self.0.setup(); - self.1.setup(); + /// Stop listening for an interrupt event + pub fn unlisten(&mut self) { + let usart = unsafe { &(*USART::ptr()) }; + usart.cr1_disabled().modify(|_, w| w.txeie().clear_bit()); } - fn release(self) -> Self { - (self.0.release(), self.1.release()) + /// Return true if the tx register is empty (and can accept data) + pub fn is_txe(&self) -> bool { + let usart = unsafe { &(*USART::ptr()) }; + usart.isr_disabled().read().txe().bit_is_set() } } -// Duplex mode with driver enabled -impl Pins for (TX, RX, DE) -where - TX: TxPin, - RX: RxPin, - DE: DriverEnablePin, -{ - const DRIVER_ENABLE: bool = true; +impl hal::serial::Write for Tx { + type Error = Error; - fn setup(&self) { - self.0.setup(); - self.1.setup(); - self.2.setup(); + fn flush(&mut self) -> nb::Result<(), Self::Error> { + let usart = unsafe { &(*USART::ptr()) }; + if usart.isr_disabled().read().tc().bit_is_set() { + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } } - fn release(self) -> Self { - (self.0.release(), self.1.release(), self.2.release()) + fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { + let usart = unsafe { &(*USART::ptr()) }; + if usart.isr_disabled().read().txe().bit_is_set() { + usart.tdr.write(|w| unsafe { w.bits(byte as u32) }); + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } } } -pub trait SerialExt { - fn usart>( - self, - pins: PINS, - config: serial::Config, - rcc: &mut Rcc, - ) -> Result, InvalidConfig>; -} +impl hal::serial::Write for Serial { + type Error = Error; -impl fmt::Write for Serial -where - Serial: hal::serial::Write, -{ - fn write_str(&mut self, s: &str) -> fmt::Result { - let _ = s.as_bytes().iter().map(|c| block!(self.write(*c))).last(); - Ok(()) + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.tx.flush() } -} -impl fmt::Write for Tx -where - Tx: hal::serial::Write, -{ - fn write_str(&mut self, s: &str) -> fmt::Result { - let _ = s.as_bytes().iter().map(|c| block!(self.write(*c))).last(); - Ok(()) + fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { + self.tx.write(byte) } } -macro_rules! uart_shared { - ($USARTX:ident, $dmamux_rx:ident, $dmamux_tx:ident, - tx: [ $(($PTX:ident, $TAF:expr),)+ ], - rx: [ $(($PRX:ident, $RAF:expr),)+ ], - de: [ $(($PDE:ident, $DAF:expr),)+ ]) => { - - $( - impl TxPin<$USARTX> for $PTX { - fn setup(&self) { - self.set_alt_mode($TAF) - } - - fn release(self) -> Self { - self - } - } - )+ - - $( - impl RxPin<$USARTX> for $PRX { - fn setup(&self) { - self.set_alt_mode($RAF) - } - - fn release(self) -> Self { - self - } - } - )+ - - $( - impl DriverEnablePin<$USARTX> for $PDE { - fn setup(&self) { - self.set_alt_mode($DAF) - } - - fn release(self) -> Self { - self - } - } - )+ - - impl Rx<$USARTX> { - pub fn listen(&mut self) { - let usart = unsafe { &(*$USARTX::ptr()) }; - usart.cr1_disabled().modify(|_, w| w.rxneie().set_bit()); - } - - /// Stop listening for an interrupt event - pub fn unlisten(&mut self) { - let usart = unsafe { &(*$USARTX::ptr()) }; - usart.cr1_disabled().modify(|_, w| w.rxneie().clear_bit()); - } - - /// Return true if the rx register is not empty (and can be read) - pub fn is_rxne(&self) -> bool { - let usart = unsafe { &(*$USARTX::ptr()) }; - usart.isr_disabled().read().rxne().bit_is_set() - } - } - - impl hal::serial::Read for Rx<$USARTX> { - type Error = Error; - - fn read(&mut self) -> nb::Result { - let usart = unsafe { &(*$USARTX::ptr()) }; - let isr = usart.isr_enabled().read(); - - Err( - if isr.pe().bit_is_set() { - usart.icr.write(|w| w.pecf().set_bit()); - nb::Error::Other(Error::Parity) - } else if isr.fe().bit_is_set() { - usart.icr.write(|w| w.fecf().set_bit()); - nb::Error::Other(Error::Framing) - } else if isr.ne().bit_is_set() { - usart.icr.write(|w| w.necf().set_bit()); - nb::Error::Other(Error::Noise) - } else if isr.ore().bit_is_set() { - usart.icr.write(|w| w.orecf().set_bit()); - nb::Error::Other(Error::Overrun) - } else if isr.rxfne().bit_is_set() { - return Ok(usart.rdr.read().bits() as u8) - } else { - nb::Error::WouldBlock - } - ) - } - } - - impl hal::serial::Read for Serial<$USARTX> { - type Error = Error; +impl Serial { + /// Separates the serial struct into separate channel objects for sending (Tx) and + /// receiving (Rx) + pub fn split(self) -> (Tx, Rx) { + (self.tx, self.rx) + } +} - fn read(&mut self) -> nb::Result { - self.rx.read() - } +impl Serial { + pub fn new( + usart: USART, + pins: ( + impl Into>, + impl Into>, + ), + config: serial::Config, + rcc: &mut Rcc, + ) -> Result { + Self::_new(usart, pins, Option::::None, config, rcc) + } + fn rs485( + usart: USART, + pins: ( + impl Into>, + impl Into>, + impl Into, + ), + config: serial::Config, + rcc: &mut Rcc, + ) -> Result { + Self::_new(usart, (pins.0, pins.1), Some(pins.2), config, rcc) + } + fn _new( + usart: USART, + pins: ( + impl Into>, + impl Into>, + ), + depin: Option>, + config: serial::Config, + rcc: &mut Rcc, + ) -> Result { + // Enable clock for USART + USART::enable(rcc); + + let clk = rcc.clocks.apb_clk.raw() as u64; + let bdr = config.baudrate.0 as u64; + let clk_mul = 1; + let div = (clk_mul * clk) / bdr; + usart.brr.write(|w| unsafe { w.bits(div as u32) }); + + // usart.cr1.reset(); + usart.cr2.reset(); + usart.cr3.reset(); + + usart.cr2.write(|w| unsafe { + w.stop().bits(config.stopbits.bits()); + w.swap().bit(config.swap) + }); + + if let Some(timeout) = config.receiver_timeout { + usart.cr1_enabled().write(|w| w.rtoie().set_bit()); + usart.cr2.modify(|_, w| w.rtoen().set_bit()); + usart.rtor.write(|w| unsafe { w.rto().bits(timeout) }); } - impl Tx<$USARTX> { - /// Starts listening for an interrupt event - pub fn listen(&mut self) { - let usart = unsafe { &(*$USARTX::ptr()) }; - usart.cr1_disabled().modify(|_, w| w.txeie().set_bit()); - } - - /// Stop listening for an interrupt event - pub fn unlisten(&mut self) { - let usart = unsafe { &(*$USARTX::ptr()) }; - usart.cr1_disabled().modify(|_, w| w.txeie().clear_bit()); - } - - /// Return true if the tx register is empty (and can accept data) - pub fn is_txe(&self) -> bool { - let usart = unsafe { &(*$USARTX::ptr()) }; - usart.isr_disabled().read().txe().bit_is_set() - } - } + usart.cr3.write(|w| unsafe { + w.txftcfg().bits(config.tx_fifo_threshold.bits()); + w.rxftcfg().bits(config.rx_fifo_threshold.bits()); + w.txftie().bit(config.tx_fifo_interrupt); + w.rxftie().bit(config.rx_fifo_interrupt) + }); + + usart.cr1_enabled().modify(|_, w| { + w.ue().set_bit(); + w.te().set_bit(); + w.re().set_bit(); + w.m0().bit(config.wordlength == WordLength::DataBits7); + w.m1().bit(config.wordlength == WordLength::DataBits9); + w.pce().bit(config.parity != Parity::ParityNone); + w.ps().bit(config.parity == Parity::ParityOdd); + w.fifoen().bit(config.fifo_enable) + }); + + usart.cr3.write(|w| w.dem().bit(depin.is_some())); + + Ok(Serial { + tx: Tx { + _usart: PhantomData, + _pin: pins.0.into(), + }, + rx: Rx { + _usart: PhantomData, + _pin: pins.1.into(), + }, + usart, + _depin: depin.map(Into::into), + }) + } - impl hal::serial::Write for Tx<$USARTX> { - type Error = Error; - - fn flush(&mut self) -> nb::Result<(), Self::Error> { - let usart = unsafe { &(*$USARTX::ptr()) }; - if usart.isr_disabled().read().tc().bit_is_set() { - Ok(()) - } else { - Err(nb::Error::WouldBlock) - } - } - - fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { - let usart = unsafe { &(*$USARTX::ptr()) }; - if usart.isr_disabled().read().txe().bit_is_set() { - usart.tdr.write(|w| unsafe { w.bits(byte as u32) }); - Ok(()) - } else { - Err(nb::Error::WouldBlock) - } - } + /// Starts listening for an interrupt event + pub fn listen(&mut self, event: Event) { + match event { + Event::Rxne => self + .usart + .cr1_disabled() + .modify(|_, w| w.rxneie().set_bit()), + Event::Txe => self.usart.cr1_disabled().modify(|_, w| w.txeie().set_bit()), + Event::Idle => self + .usart + .cr1_disabled() + .modify(|_, w| w.idleie().set_bit()), + _ => {} } + } - impl hal::serial::Write for Serial<$USARTX> { - type Error = Error; - - fn flush(&mut self) -> nb::Result<(), Self::Error> { - self.tx.flush() - } - - fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { - self.tx.write(byte) - } + /// Stop listening for an interrupt event + pub fn unlisten(&mut self, event: Event) { + match event { + Event::Rxne => self + .usart + .cr1_disabled() + .modify(|_, w| w.rxneie().clear_bit()), + Event::Txe => self + .usart + .cr1_disabled() + .modify(|_, w| w.txeie().clear_bit()), + Event::Idle => self + .usart + .cr1_disabled() + .modify(|_, w| w.idleie().clear_bit()), + _ => {} } + } - impl Serial<$USARTX> { - - /// Separates the serial struct into separate channel objects for sending (Tx) and - /// receiving (Rx) - pub fn split(self) -> (Tx<$USARTX>, Rx<$USARTX>) { - (self.tx, self.rx) - } + /// Check if interrupt event is pending + pub fn is_pending(&mut self, event: Event) -> bool { + (self.usart.isr_enabled().read().bits() & event.val()) != 0 + } - } + /// Clear pending interrupt + pub fn unpend(&mut self, event: Event) { + // mask the allowed bits + let mask: u32 = 0x123BFF; + self.usart + .icr + .write(|w| unsafe { w.bits(event.val() & mask) }); } } -macro_rules! uart { - ($USARTX:ident, - $usartX:ident, $clk_mul:expr - ) => { - impl SerialExt<$USARTX> for $USARTX { - fn usart>( - self, - pins: PINS, - config: serial::Config, - rcc: &mut Rcc, - ) -> Result, InvalidConfig> { - Serial::$usartX(self, pins, config, rcc) - } - } +impl Tx { + /// Returns true if the tx fifo threshold has been reached. + pub fn fifo_threshold_reached(&self) -> bool { + let usart = unsafe { &(*USART::ptr()) }; + usart.isr_enabled().read().txft().bit_is_set() + } +} - impl Serial<$USARTX> { - pub fn $usartX>( - usart: $USARTX, - pins: PINS, - config: serial::Config, - rcc: &mut Rcc, - ) -> Result { - // Enable clock for USART - $USARTX::enable(rcc); - - let clk = rcc.clocks.apb_clk.raw() as u64; - let bdr = config.baudrate.0 as u64; - let clk_mul = 1; - let div = (clk_mul * clk) / bdr; - usart.brr.write(|w| unsafe { w.bits(div as u32) }); - - // usart.cr1.reset(); - usart.cr2.reset(); - usart.cr3.reset(); - - usart.cr2.write(|w| unsafe { - w.stop() - .bits(config.stopbits.bits()) - .swap() - .bit(config.swap) - }); - - if let Some(timeout) = config.receiver_timeout { - usart.cr1_enabled().write(|w| w.rtoie().set_bit()); - usart.cr2.modify(|_, w| w.rtoen().set_bit()); - usart.rtor.write(|w| unsafe { w.rto().bits(timeout) }); - } - - usart.cr3.write(|w| unsafe { - w.txftcfg() - .bits(config.tx_fifo_threshold.bits()) - .rxftcfg() - .bits(config.rx_fifo_threshold.bits()) - .txftie() - .bit(config.tx_fifo_interrupt) - .rxftie() - .bit(config.rx_fifo_interrupt) - }); - - usart.cr1_enabled().modify(|_, w| { - w.ue() - .set_bit() - .te() - .set_bit() - .re() - .set_bit() - .m0() - .bit(config.wordlength == WordLength::DataBits7) - .m1() - .bit(config.wordlength == WordLength::DataBits9) - .pce() - .bit(config.parity != Parity::ParityNone) - .ps() - .bit(config.parity == Parity::ParityOdd) - .fifoen() - .bit(config.fifo_enable) - }); - - usart.cr3.write(|w| w.dem().bit(PINS::DRIVER_ENABLE)); - - // Enable pins - pins.setup(); - - Ok(Serial { - tx: Tx { - _usart: PhantomData, - }, - rx: Rx { - _usart: PhantomData, - }, - usart, - }) - } - - /// Starts listening for an interrupt event - pub fn listen(&mut self, event: Event) { - match event { - Event::Rxne => self - .usart - .cr1_disabled() - .modify(|_, w| w.rxneie().set_bit()), - Event::Txe => self.usart.cr1_disabled().modify(|_, w| w.txeie().set_bit()), - Event::Idle => self - .usart - .cr1_disabled() - .modify(|_, w| w.idleie().set_bit()), - _ => {} - } - } - - /// Stop listening for an interrupt event - pub fn unlisten(&mut self, event: Event) { - match event { - Event::Rxne => self - .usart - .cr1_disabled() - .modify(|_, w| w.rxneie().clear_bit()), - Event::Txe => self - .usart - .cr1_disabled() - .modify(|_, w| w.txeie().clear_bit()), - Event::Idle => self - .usart - .cr1_disabled() - .modify(|_, w| w.idleie().clear_bit()), - _ => {} - } - } - - /// Check if interrupt event is pending - pub fn is_pending(&mut self, event: Event) -> bool { - (self.usart.isr_enabled().read().bits() & event.val()) != 0 - } - - /// Clear pending interrupt - pub fn unpend(&mut self, event: Event) { - // mask the allowed bits - let mask: u32 = 0x123BFF; - self.usart - .icr - .write(|w| unsafe { w.bits(event.val() & mask) }); - } - } +impl Rx { + /// Check if receiver timeout has lapsed + /// Returns the current state of the ISR RTOF bit + pub fn timeout_lapsed(&self) -> bool { + let usart = unsafe { &(*USART::ptr()) }; + usart.isr_enabled().read().rtof().bit_is_set() + } - impl Tx<$USARTX> { - /// Returns true if the tx fifo threshold has been reached. - pub fn fifo_threshold_reached(&self) -> bool { - let usart = unsafe { &(*$USARTX::ptr()) }; - usart.isr_enabled().read().txft().bit_is_set() - } - } + /// Clear pending receiver timeout interrupt + pub fn clear_timeout(&mut self) { + let usart = unsafe { &(*USART::ptr()) }; + usart.icr.write(|w| w.rtocf().set_bit()); + } - impl Rx<$USARTX> { - /// Check if receiver timeout has lapsed - /// Returns the current state of the ISR RTOF bit - pub fn timeout_lapsed(&self) -> bool { - let usart = unsafe { &(*$USARTX::ptr()) }; - usart.isr_enabled().read().rtof().bit_is_set() - } - - /// Clear pending receiver timeout interrupt - pub fn clear_timeout(&mut self) { - let usart = unsafe { &(*$USARTX::ptr()) }; - usart.icr.write(|w| w.rtocf().set_bit()); - } - - /// Returns true if the rx fifo threshold has been reached. - pub fn fifo_threshold_reached(&self) -> bool { - let usart = unsafe { &(*$USARTX::ptr()) }; - usart.isr_enabled().read().rxft().bit_is_set() - } - } - }; + /// Returns true if the rx fifo threshold has been reached. + pub fn fifo_threshold_reached(&self) -> bool { + let usart = unsafe { &(*USART::ptr()) }; + usart.isr_enabled().read().rxft().bit_is_set() + } } - -uart_shared!(USART1, USART1_RX, USART1_TX, - tx: [ - (PA0, AltFunction::AF4), - (PA9, AltFunction::AF1), - (PB6, AltFunction::AF0), - (PC14, AltFunction::AF0), - ], - rx: [ - (PA1, AltFunction::AF4), - (PA8, AltFunction::AF14), - (PA10, AltFunction::AF1), - (PB2, AltFunction::AF0), - (PB7, AltFunction::AF0), - ], - de: [ - (PA12, AltFunction::AF1), - (PA14, AltFunction::AF12), - (PA15, AltFunction::AF4), - (PB3, AltFunction::AF4), - (PB6, AltFunction::AF4), - ] -); - -uart_shared!(USART2, USART2_RX, USART2_TX, - tx: [ - (PA2, AltFunction::AF1), - (PA4, AltFunction::AF1), - (PA8, AltFunction::AF1), - (PA14, AltFunction::AF1), - ], - rx: [ - (PA3, AltFunction::AF1), - (PA5, AltFunction::AF1), - (PA13, AltFunction::AF4), - (PA14, AltFunction::AF9), - (PA15, AltFunction::AF1), - ], - de: [ - (PA1, AltFunction::AF1), - (PB9, AltFunction::AF1), - (PC14, AltFunction::AF9), - ] -); - -uart!(USART1, usart1, 1); -uart!(USART2, usart2, 1); diff --git a/src/spi.rs b/src/spi.rs index 1544ff0..93246a1 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -1,7 +1,8 @@ -use crate::gpio::*; -use crate::rcc::*; -use crate::stm32::SPI; +use crate::gpio; +use crate::pac::spi as spi1; +use crate::rcc::{self, Rcc}; use crate::time::Hertz; +use core::ops::Deref; use core::ptr; pub use hal::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; @@ -17,286 +18,172 @@ pub enum Error { } /// A filler type for when the SCK pin is unnecessary -pub struct NoSck; +pub use gpio::NoPin as NoSck; /// A filler type for when the Miso pin is unnecessary -pub struct NoMiso; +pub use gpio::NoPin as NoMiso; /// A filler type for when the Mosi pin is unnecessary -pub struct NoMosi; - -pub trait Pins { - fn setup(&self); - fn release(self) -> Self; -} - -pub trait PinSck { - fn setup(&self); - fn release(self) -> Self; -} - -pub trait PinMiso { - fn setup(&self); - fn release(self) -> Self; -} - -pub trait PinMosi { - fn setup(&self); - fn release(self) -> Self; -} - -impl Pins for (SCK, MISO, MOSI) -where - SCK: PinSck, - MISO: PinMiso, - MOSI: PinMosi, +pub use gpio::NoPin as NoMosi; + +// Implemented by all SPI instances +pub trait Instance: + crate::Sealed + + Deref + + rcc::Enable + + rcc::Reset + + gpio::alt::SpiCommon { - fn setup(&self) { - self.0.setup(); - self.1.setup(); - self.2.setup(); - } - - fn release(self) -> Self { - (self.0.release(), self.1.release(), self.2.release()) - } + #[doc(hidden)] + fn ptr() -> *const spi1::RegisterBlock; } #[derive(Debug)] -pub struct Spi { +pub struct Spi { spi: SPI, - pins: PINS, + pins: (SPI::Sck, SPI::Miso, SPI::Mosi), } -pub trait SpiExt: Sized { - fn spi(self, pins: PINS, mode: Mode, freq: Hertz, rcc: &mut Rcc) -> Spi - where - PINS: Pins; +pub trait SpiExt: Sized + Instance { + fn spi( + self, + pins: ( + impl Into, + impl Into, + impl Into, + ), + mode: Mode, + freq: Hertz, + rcc: &mut Rcc, + ) -> Spi; } -macro_rules! spi { - ($SPIX:ident, $spiX:ident, - sck: [ $(($SCK:ty, $SCK_AF:expr),)+ ], - miso: [ $(($MISO:ty, $MISO_AF:expr),)+ ], - mosi: [ $(($MOSI:ty, $MOSI_AF:expr),)+ ], - ) => { - impl PinSck<$SPIX> for NoSck { - fn setup(&self) {} - - fn release(self) -> Self { - self - } - } - - impl PinMiso<$SPIX> for NoMiso { - fn setup(&self) {} - - fn release(self) -> Self { - self - } - } - - impl PinMosi<$SPIX> for NoMosi { - fn setup(&self) {} - - fn release(self) -> Self { - self - } - } - - $( - impl PinSck<$SPIX> for $SCK { - fn setup(&self) { - self.set_alt_mode($SCK_AF); - } - - fn release(self) -> Self { - self.into_analog() - } - } - )* - $( - impl PinMiso<$SPIX> for $MISO { - fn setup(&self) { - self.set_alt_mode($MISO_AF); - } - - fn release(self) -> Self { - self.into_analog() - } - } - )* - $( - impl PinMosi<$SPIX> for $MOSI { - fn setup(&self) { - self.set_alt_mode($MOSI_AF); - } - - fn release(self) -> Self { - self.into_analog() - } - } - )* - - impl> Spi<$SPIX, PINS> { - pub fn $spiX( - spi: $SPIX, - pins: PINS, - mode: Mode, - speed: Hertz, - rcc: &mut Rcc - ) -> Self { - $SPIX::enable(rcc); - $SPIX::reset(rcc); - - // disable SS output - spi.cr2.write(|w| w.ssoe().clear_bit()); - - let br = match rcc.clocks.apb_clk / speed { - 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.cr2.write(|w| unsafe { - w.frxth().set_bit().ds().bits(0b111).ssoe().clear_bit() - }); - - // Enable pins - pins.setup(); - - spi.cr1.write(|w| unsafe { - w.cpha() - .bit(mode.phase == Phase::CaptureOnSecondTransition) - .cpol() - .bit(mode.polarity == Polarity::IdleHigh) - .mstr() - .set_bit() - .br() - .bits(br) - .lsbfirst() - .clear_bit() - .ssm() - .set_bit() - .ssi() - .set_bit() - .rxonly() - .clear_bit() - .bidimode() - .clear_bit() - .ssi() - .set_bit() - .spe() - .set_bit() - }); - - Spi { spi, pins } - } - - pub fn data_size(&mut self, nr_bits: u8) { - self.spi.cr2.modify(|_, w| unsafe { - w.ds().bits(nr_bits-1) - }); - } - - pub fn half_duplex_enable(&mut self, enable: bool) { - self.spi.cr1.modify(|_, w| - w.bidimode().bit(enable) - ); - } - - pub fn half_duplex_output_enable(&mut self, enable: bool) { - self.spi.cr1.modify(|_, w| - w.bidioe().bit(enable) - ); - } - - pub fn release(self) -> ($SPIX, PINS) { - (self.spi, self.pins.release()) - } - } - - impl SpiExt for $SPIX { - fn spi(self, pins: PINS, mode: Mode, freq: Hertz, rcc: &mut Rcc) -> Spi<$SPIX, PINS> - where - PINS: Pins<$SPIX>, - { - Spi::$spiX(self, pins, mode, freq, rcc) - } - } +impl SpiExt for SPI { + fn spi( + self, + pins: ( + impl Into, + impl Into, + impl Into, + ), + mode: Mode, + freq: Hertz, + rcc: &mut Rcc, + ) -> Spi { + Spi::new(self, pins, mode, freq, rcc) + } +} - impl hal::spi::FullDuplex for Spi<$SPIX, PINS> { - type Error = Error; +impl Spi { + pub fn new( + spi: SPI, + pins: ( + impl Into, + impl Into, + impl Into, + ), + mode: Mode, + speed: Hertz, + rcc: &mut Rcc, + ) -> Self { + SPI::enable(rcc); + SPI::reset(rcc); + + // disable SS output + spi.cr2.write(|w| w.ssoe().clear_bit()); + + let br = match rcc.clocks.apb_clk / speed { + 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.cr2 + .write(|w| unsafe { w.frxth().set_bit().ds().bits(0b111).ssoe().clear_bit() }); + + // Enable pins + let pins = (pins.0.into(), pins.1.into(), pins.2.into()); + + spi.cr1.write(|w| unsafe { + w.cpha().bit(mode.phase == Phase::CaptureOnSecondTransition); + w.cpol().bit(mode.polarity == Polarity::IdleHigh); + w.mstr().set_bit(); + w.br().bits(br); + w.lsbfirst().clear_bit(); + w.ssm().set_bit(); + w.ssi().set_bit(); + w.rxonly().clear_bit(); + w.bidimode().clear_bit(); + w.ssi().set_bit(); + w.spe().set_bit() + }); + + Spi { spi, pins } + } - fn read(&mut self) -> nb::Result { - let sr = self.spi.sr.read(); + pub fn data_size(&mut self, nr_bits: u8) { + self.spi + .cr2 + .modify(|_, w| unsafe { w.ds().bits(nr_bits - 1) }); + } - Err(if sr.ovr().bit_is_set() { - nb::Error::Other(Error::Overrun) - } else if sr.modf().bit_is_set() { - nb::Error::Other(Error::ModeFault) - } else if sr.crcerr().bit_is_set() { - nb::Error::Other(Error::Crc) - } else if sr.rxne().bit_is_set() { - // NOTE(read_volatile) read only 1 byte (the svd2rust API only allows - // reading a half-word) - return Ok(unsafe { - ptr::read_volatile(&self.spi.dr as *const _ as *const u8) - }); - } else { - nb::Error::WouldBlock - }) - } + pub fn half_duplex_enable(&mut self, enable: bool) { + self.spi.cr1.modify(|_, w| w.bidimode().bit(enable)); + } - fn send(&mut self, byte: u8) -> nb::Result<(), Error> { - let sr = self.spi.sr.read(); + pub fn half_duplex_output_enable(&mut self, enable: bool) { + self.spi.cr1.modify(|_, w| w.bidioe().bit(enable)); + } - Err(if sr.ovr().bit_is_set() { - nb::Error::Other(Error::Overrun) - } else if sr.modf().bit_is_set() { - nb::Error::Other(Error::ModeFault) - } else if sr.crcerr().bit_is_set() { - nb::Error::Other(Error::Crc) - } else if sr.txe().bit_is_set() { - // NOTE(write_volatile) see note above - unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut u8, byte) } - return Ok(()); - } else { - nb::Error::WouldBlock - }) - } - } + pub fn release(self) -> (SPI, (SPI::Sck, SPI::Miso, SPI::Mosi)) { + (self.spi, self.pins) + } +} - impl ::hal::blocking::spi::transfer::Default for Spi<$SPIX, PINS> {} +impl hal::spi::FullDuplex for Spi { + type Error = Error; + + fn read(&mut self) -> nb::Result { + let sr = self.spi.sr.read(); + + Err(if sr.ovr().bit_is_set() { + nb::Error::Other(Error::Overrun) + } else if sr.modf().bit_is_set() { + nb::Error::Other(Error::ModeFault) + } else if sr.crcerr().bit_is_set() { + nb::Error::Other(Error::Crc) + } else if sr.rxne().bit_is_set() { + // NOTE(read_volatile) read only 1 byte (the svd2rust API only allows + // reading a half-word) + return Ok(unsafe { ptr::read_volatile(&self.spi.dr as *const _ as *const u8) }); + } else { + nb::Error::WouldBlock + }) + } - impl ::hal::blocking::spi::write::Default for Spi<$SPIX, PINS> {} + fn send(&mut self, byte: u8) -> nb::Result<(), Error> { + let sr = self.spi.sr.read(); + + Err(if sr.ovr().bit_is_set() { + nb::Error::Other(Error::Overrun) + } else if sr.modf().bit_is_set() { + nb::Error::Other(Error::ModeFault) + } else if sr.crcerr().bit_is_set() { + nb::Error::Other(Error::Crc) + } else if sr.txe().bit_is_set() { + // NOTE(write_volatile) see note above + unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut u8, byte) } + return Ok(()); + } else { + nb::Error::WouldBlock + }) } } -spi!( - SPI, - spi1, - sck: [ - (PA1, AltFunction::AF0), - (PA5, AltFunction::AF0), - (PB3, AltFunction::AF0), - (PB6, AltFunction::AF10), - ], - miso: [ - (PA6, AltFunction::AF0), - (PA11, AltFunction::AF0), - (PB4, AltFunction::AF0), - (PB6, AltFunction::AF9), - ], - mosi: [ - (PA2, AltFunction::AF0), - (PA7, AltFunction::AF0), - (PA12, AltFunction::AF0), - (PB5, AltFunction::AF0), - (PB6, AltFunction::AF8), - ], -); +impl ::hal::blocking::spi::transfer::Default for Spi {} + +impl ::hal::blocking::spi::write::Default for Spi {} diff --git a/src/timer/mod.rs b/src/timer/mod.rs index 905966a..2f353d6 100644 --- a/src/timer/mod.rs +++ b/src/timer/mod.rs @@ -2,7 +2,6 @@ use crate::rcc::*; use crate::stm32::*; use crate::time::{Hertz, MicroSecond}; -use core::marker::PhantomData; use cortex_m::peripheral::syst::SystClkSource; use cortex_m::peripheral::SYST; use hal::timer::{CountDown, Periodic}; @@ -21,10 +20,14 @@ pub struct Timer { tim: TIM, } -pub struct Channel1; -pub struct Channel2; -pub struct Channel3; -pub struct Channel4; +#[allow(non_upper_case_globals)] +pub const Channel1: u8 = 0; +#[allow(non_upper_case_globals)] +pub const Channel2: u8 = 1; +#[allow(non_upper_case_globals)] +pub const Channel3: u8 = 2; +#[allow(non_upper_case_globals)] +pub const Channel4: u8 = 3; /// System timer impl Timer { diff --git a/src/timer/opm.rs b/src/timer/opm.rs index e8fdde3..3cbdcce 100644 --- a/src/timer/opm.rs +++ b/src/timer/opm.rs @@ -1,8 +1,9 @@ //! # One-pulse Mode +use crate::gpio::alt::TimCPin; +use crate::gpio::PushPull; use crate::rcc::*; use crate::stm32::*; use crate::time::{Hertz, MicroSecond}; -use crate::timer::pins::TimerPin; use crate::timer::*; use core::marker::PhantomData; use fugit::RateExtU32; @@ -11,9 +12,8 @@ pub trait OpmExt: Sized { fn opm(self, period: MicroSecond, rcc: &mut Rcc) -> Opm; } -pub struct OpmPin { +pub struct OpmPin { tim: PhantomData, - channel: PhantomData, delay: u32, } @@ -23,14 +23,13 @@ pub struct Opm { } impl Opm { - pub fn bind_pin(&self, pin: PIN) -> OpmPin + pub fn bind_pin(&self, pin: impl Into) -> OpmPin where - PIN: TimerPin, + TIM: TimCPin = PIN>, { - pin.setup(); + let _ = pin.into(); OpmPin { tim: PhantomData, - channel: PhantomData, delay: 1, } } diff --git a/src/timer/pins.rs b/src/timer/pins.rs index 1b68b40..d855fe0 100644 --- a/src/timer/pins.rs +++ b/src/timer/pins.rs @@ -1,10 +1,12 @@ +/* use crate::gpio::*; use crate::gpio::{AltFunction, DefaultMode}; use crate::stm32::*; use crate::timer::*; pub trait TimerPin { - type Channel; + #[allow(non_upper_case_globals)] + const Channel: u8; fn setup(&self); fn release(self) -> Self; @@ -28,10 +30,11 @@ impl> TriggerPin { } macro_rules! timer_pins { - ($TIMX:ident, [ $(($ch:ty, $pin:ty, $af_mode:expr),)+ ]) => { + ($TIMX:ident, [ $(($ch:ident, $pin:ty, $af_mode:expr),)+ ]) => { $( impl TimerPin<$TIMX> for $pin { - type Channel = $ch; + #[allow(non_upper_case_globals)] + const Channel: u8 = $ch; fn setup(&self) { self.set_alt_mode($af_mode); @@ -173,3 +176,4 @@ timer_pins!(TIM17, [ timer_pins!(TIM17, [ (Channel1, PB7, AltFunction::AF2), ]); +*/ diff --git a/src/timer/pwm.rs b/src/timer/pwm.rs index c6131bf..665fa28 100644 --- a/src/timer/pwm.rs +++ b/src/timer/pwm.rs @@ -1,10 +1,11 @@ //! # Pulse Width Modulation use core::marker::PhantomData; +use crate::gpio::alt::TimCPin; +use crate::gpio::PushPull; use crate::rcc::*; use crate::stm32::*; use crate::time::Hertz; -use crate::timer::pins::TimerPin; use crate::timer::*; pub enum OutputCompareMode { @@ -29,9 +30,8 @@ pub struct Pwm { tim: TIM, } -pub struct PwmPin { +pub struct PwmPin { tim: PhantomData, - channel: PhantomData, } pub trait PwmExt: Sized { @@ -43,15 +43,12 @@ pub trait PwmPinMode { } impl Pwm { - pub fn bind_pin(&self, pin: PIN) -> PwmPin + pub fn bind_pin(&self, pin: impl Into) -> PwmPin where - PIN: TimerPin, + TIM: TimCPin = PIN>, { - pin.setup(); - PwmPin { - tim: PhantomData, - channel: PhantomData, - } + let _ = pin.into(); + PwmPin { tim: PhantomData } } } @@ -140,7 +137,7 @@ macro_rules! pwm_q { macro_rules! pwm_hal { ($($TIMX:ident: - ($CH:ty, $ccxe:ident, $ccmrx_output:ident, $ocxpe:ident, $ocxm:ident, $ccrx:ident, $ccrx_l:ident, $ccrx_h:ident),)+ + ($CH:ident, $ccxe:ident, $ccmrx_output:ident, $ocxpe:ident, $ocxm:ident, $ccrx:ident, $ccrx_l:ident, $ccrx_h:ident),)+ ) => { $( impl hal::PwmPin for PwmPin<$TIMX, $CH> { @@ -178,7 +175,7 @@ macro_rules! pwm_hal { macro_rules! pwm_advanced_hal { ($($TIMX:ident: ( - $CH:ty, + $CH:ident, $ccxe:ident $(: $ccxne:ident)*, $ccmrx_output:ident, $ocxpe:ident, diff --git a/src/timer/qei.rs b/src/timer/qei.rs index 00539fe..0af2237 100644 --- a/src/timer/qei.rs +++ b/src/timer/qei.rs @@ -1,110 +1,131 @@ //! Quadrature Encoder Interface -use crate::hal::{self, Direction}; -use crate::rcc::*; - -use crate::timer::pins::TimerPin; -use crate::timer::*; +use crate::gpio::{alt::TimCPin as CPin, PushPull}; +use crate::pac; +use crate::rcc::{self, Rcc}; + +pub trait QeiExt: Sized + Instance { + fn qei( + self, + pins: ( + impl Into<>::Ch>, + impl Into<>::Ch>, + ), + rcc: &mut Rcc, + ) -> Qei; +} -pub struct Qei { - tim: TIM, - pins: PINS, +impl QeiExt for TIM { + fn qei( + self, + pins: ( + impl Into<>::Ch>, + impl Into<>::Ch>, + ), + rcc: &mut Rcc, + ) -> Qei { + Qei::new(self, pins, rcc) + } } -pub trait QeiPins { - fn setup(&self); - fn release(self) -> Self; +/// Hardware quadrature encoder interface peripheral +pub struct Qei { + tim: TIM, + pins: ( + >::Ch, + >::Ch, + ), } -impl QeiPins for (P1, P2) -where - P1: TimerPin, - P2: TimerPin, -{ - fn setup(&self) { - self.0.setup(); - self.1.setup(); +impl Qei { + /// Configures a TIM peripheral as a quadrature encoder interface input + pub fn new( + mut tim: TIM, + pins: ( + impl Into<>::Ch>, + impl Into<>::Ch>, + ), + rcc: &mut Rcc, + ) -> Self { + // enable and reset peripheral to a clean slate state + TIM::enable(rcc); + TIM::reset(rcc); + + tim.setup_qei(); + let pins = (pins.0.into(), pins.1.into()); + tim.start(); + + Qei { tim, pins } } - fn release(self) -> Self { - (self.0.release(), self.1.release()) + /// Releases the TIM peripheral and QEI pins + #[allow(clippy::type_complexity)] + pub fn release( + self, + ) -> ( + TIM, + ( + >::Ch, + >::Ch, + ), + ) { + (self.tim, self.pins) } } -pub trait QeiExt -where - PINS: QeiPins, -{ - fn qei(self, pins: PINS, rcc: &mut Rcc) -> Qei; +pub trait Instance: crate::Sealed + rcc::Enable + rcc::Reset + CPin<0> + CPin<1> { + fn setup_qei(&mut self); + fn start(&mut self); + fn read_direction(&self) -> bool; } -macro_rules! qei { - ($($TIMX:ident: ($tim:ident, $arr:ident, $cnt:ident),)+) => { - $( - impl Qei<$TIMX, PINS> where PINS: QeiPins<$TIMX> { - fn $tim(tim: $TIMX, pins: PINS, rcc: &mut Rcc) -> Self { - // enable and reset peripheral to a clean slate state - $TIMX::enable(rcc); - $TIMX::reset(rcc); - - // Configure TxC1 and TxC2 as captures - tim.ccmr1_output().write(|w| unsafe { w.cc1s().bits(0b01).cc2s().bits(0b01) }); - - // Encoder mode 2. - tim.smcr.write(|w| unsafe { w.sms1().bits(0b010) }); - - // Enable and configure to capture on rising edge - tim.ccer.write(|w| { - w.cc1e() - .set_bit() - .cc2e() - .set_bit() - .cc1p() - .clear_bit() - .cc2p() - .clear_bit() - .cc1np() - .clear_bit() - .cc2np() - .clear_bit() - }); - - pins.setup(); - - tim.cr1.write(|w| w.cen().set_bit()); - Qei { tim, pins } - } +macro_rules! hal { + ($TIM:ty) => { + impl embedded_hal::Qei for Qei<$TIM> { + type Count = u16; - pub fn release(self) -> ($TIMX, PINS) { - (self.tim, self.pins.release()) - } + fn count(&self) -> u16 { + self.tim.cnt.read().bits() as u16 } - impl hal::Qei for Qei<$TIMX, PINS> { - type Count = u16; - - fn count(&self) -> u16 { - self.tim.cnt.read().$cnt().bits() + fn direction(&self) -> embedded_hal::Direction { + if self.tim.read_direction() { + embedded_hal::Direction::Upcounting + } else { + embedded_hal::Direction::Downcounting } + } + } + + impl Instance for $TIM { + fn setup_qei(&mut self) { + // Configure TxC1 and TxC2 as captures + self.ccmr1_output() + .write(|w| unsafe { w.cc1s().bits(0b01).cc2s().bits(0b01) }); + + // Encoder mode 2. + self.smcr.write(|w| unsafe { w.sms1().bits(0b010) }); + + // Enable and configure to capture on rising edge + self.ccer.write(|w| { + w.cc1e().set_bit(); + w.cc2e().set_bit(); + w.cc1p().clear_bit(); + w.cc2p().clear_bit(); + w.cc1np().clear_bit(); + w.cc2np().clear_bit() + }); + } - fn direction(&self) -> Direction { - if self.tim.cr1.read().dir().bit_is_clear() { - hal::Direction::Upcounting - } else { - hal::Direction::Downcounting - } - } + fn start(&mut self) { + self.cr1.write(|w| w.cen().set_bit()); } - impl QeiExt<$TIMX, PINS> for $TIMX where PINS: QeiPins<$TIMX> { - fn qei(self, pins: PINS, rcc: &mut Rcc) -> Qei<$TIMX, PINS> { - Qei::$tim(self, pins, rcc) - } + fn read_direction(&self) -> bool { + self.cr1.read().dir().bit_is_clear() } - )+ - } + } + }; } -qei! { - TIM1: (tim1, arr, cnt), - TIM3: (tim3, arr, cnt), -} +hal! { pac::TIM1 } +hal! { pac::TIM3 }