Skip to content

Commit 1d73f62

Browse files
bors[bot]burrbull
andauthored
Merge #372
372: DynamicPin r=therealprof a=burrbull Co-authored-by: Andrey Zgarbul <[email protected]>
2 parents 77c2265 + a5e42e4 commit 1d73f62

File tree

7 files changed

+218
-20
lines changed

7 files changed

+218
-20
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1212
- Added `Counter` with `CountDown<Time=fugit::TimerDuration>` and `atat::Clock` implementations [#381]
1313
- `Into<serial::Config>` for `Bps` [#387]
1414
- Added the missing DMA implementations for USART3 [#373]
15+
- `DynamicPin` with dynamically changed mode, remove `AF` constants [#372]
1516
- `count_down` constructor for `Timer` -> `CountDownTimer` without start [#382]
1617
- Implementation of RTIC Monotonic for TIM2 & TIM5 under `rtic` feature [#380] [#390]
1718
- `IoPin` for `Output<OpenDrain>> <-> Input<Floating>>` [#374]

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,3 +443,8 @@ required-features = ["device-selected"]
443443
[[example]]
444444
name = "spi_dma"
445445
required-features = ["rt", "stm32f411"]
446+
447+
[[example]]
448+
name = "dynamic_gpio"
449+
required-features = ["device-selected"]
450+

examples/dynamic_gpio.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#![deny(unsafe_code)]
2+
#![no_std]
3+
#![no_main]
4+
5+
use panic_halt as _;
6+
7+
use nb::block;
8+
9+
use cortex_m_rt::entry;
10+
use cortex_m_semihosting::hprintln;
11+
use stm32f4xx_hal::{gpio::PinState, pac, prelude::*, timer::Timer};
12+
13+
#[entry]
14+
fn main() -> ! {
15+
// Get access to the core peripherals from the cortex-m crate
16+
let cp = cortex_m::Peripherals::take().unwrap();
17+
// Get access to the device specific peripherals from the peripheral access crate
18+
let dp = pac::Peripherals::take().unwrap();
19+
20+
// Take ownership over raw device and convert it into the corresponding HAL struct
21+
let rcc = dp.RCC.constrain();
22+
23+
// Freeze the configuration of all the clocks in the system and store the frozen frequencies in
24+
// `clocks`
25+
let clocks = rcc.cfgr.freeze();
26+
27+
// Acquire the GPIOC peripheral
28+
let gpioc = dp.GPIOC.split();
29+
30+
let mut pin = gpioc.pc13.into_dynamic();
31+
// Configure the syst timer to trigger an update every second
32+
let mut timer = Timer::syst(cp.SYST, &clocks).counter();
33+
timer.start(1.secs()).unwrap();
34+
35+
// Wait for the timer to trigger an update and change the state of the LED
36+
loop {
37+
pin.make_floating_input();
38+
block!(timer.wait()).unwrap();
39+
hprintln!("{}", pin.is_high().unwrap()).unwrap();
40+
41+
pin.make_push_pull_output_in_state(PinState::High);
42+
block!(timer.wait()).unwrap();
43+
pin.set_low().unwrap();
44+
block!(timer.wait()).unwrap();
45+
}
46+
}

src/gpio.rs

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
//!
2525
//! - **Alternate**: Pin mode required when the pin is driven by other peripherals
2626
//! - **Analog**: Analog input to be used with ADC.
27+
//! - **Dynamic**: Pin mode is selected at runtime. See changing configurations for more details
2728
//! - Input
2829
//! - **PullUp**: Input connected to high with a weak pull up resistor. Will be high when nothing
2930
//! is connected
@@ -42,6 +43,16 @@
4243
//! If you need a more temporary mode change, and can not use the `into_<mode>` functions for
4344
//! ownership reasons, you can use the closure based `with_<mode>` functions to temporarily change the pin type, do
4445
//! some output or input, and then have it change back once done.
46+
//!
47+
//! ### Dynamic Mode Change
48+
//! The above mode change methods guarantee that you can only call input functions when the pin is
49+
//! in input mode, and output when in output modes, but can lead to some issues. Therefore, there
50+
//! is also a mode where the state is kept track of at runtime, allowing you to change the mode
51+
//! often, and without problems with ownership, or references, at the cost of some performance and
52+
//! the risk of runtime errors.
53+
//!
54+
//! To make a pin dynamic, use the `into_dynamic` function, and then use the `make_<mode>` functions to
55+
//! change the mode
4556
4657
use core::marker::PhantomData;
4758

@@ -56,6 +67,8 @@ mod partially_erased;
5667
pub use partially_erased::{PEPin, PartiallyErasedPin};
5768
mod erased;
5869
pub use erased::{EPin, ErasedPin};
70+
mod dynamic;
71+
pub use dynamic::{Dynamic, DynamicPin};
5972
mod hal_02;
6073

6174
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
@@ -98,24 +111,6 @@ pub trait PinExt {
98111
/// Some alternate mode (type state)
99112
pub struct Alternate<Otype, const A: u8>(PhantomData<Otype>);
100113

101-
// Compatibility constants
102-
pub const AF0: u8 = 0;
103-
pub const AF1: u8 = 1;
104-
pub const AF2: u8 = 2;
105-
pub const AF3: u8 = 3;
106-
pub const AF4: u8 = 4;
107-
pub const AF5: u8 = 5;
108-
pub const AF6: u8 = 6;
109-
pub const AF7: u8 = 7;
110-
pub const AF8: u8 = 8;
111-
pub const AF9: u8 = 9;
112-
pub const AF10: u8 = 10;
113-
pub const AF11: u8 = 11;
114-
pub const AF12: u8 = 12;
115-
pub const AF13: u8 = 13;
116-
pub const AF14: u8 = 14;
117-
pub const AF15: u8 = 15;
118-
119114
/// Input mode (type state)
120115
pub struct Input<MODE> {
121116
_mode: PhantomData<MODE>,

src/gpio/convert.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,14 @@ impl<MODE, const P: char, const N: u8> Pin<MODE, P, N> {
436436
Pin::new()
437437
}
438438

439+
/// Configures the pin as a pin that can change between input
440+
/// and output without changing the type. It starts out
441+
/// as a floating input
442+
pub fn into_dynamic(self) -> DynamicPin<P, N> {
443+
self.into_floating_input();
444+
DynamicPin::new(Dynamic::InputFloating)
445+
}
446+
439447
/// Puts `self` into mode `M`.
440448
///
441449
/// This violates the type state constraints from `MODE`, so callers must

src/gpio/dynamic.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
use super::*;
2+
3+
/// Pin type with dynamic mode
4+
///
5+
/// - `P` is port name: `A` for GPIOA, `B` for GPIOB, etc.
6+
/// - `N` is pin number: from `0` to `15`.
7+
pub struct DynamicPin<const P: char, const N: u8> {
8+
/// Current pin mode
9+
pub(crate) mode: Dynamic,
10+
}
11+
12+
/// Tracks the current pin state for dynamic pins
13+
pub enum Dynamic {
14+
InputFloating,
15+
InputPullUp,
16+
InputPullDown,
17+
OutputPushPull,
18+
OutputOpenDrain,
19+
}
20+
21+
#[derive(Debug, PartialEq)]
22+
pub enum PinModeError {
23+
IncorrectMode,
24+
}
25+
26+
impl Dynamic {
27+
pub fn is_input(&self) -> bool {
28+
use Dynamic::*;
29+
match self {
30+
InputFloating | InputPullUp | InputPullDown | OutputOpenDrain => true,
31+
OutputPushPull => false,
32+
}
33+
}
34+
pub fn is_output(&self) -> bool {
35+
use Dynamic::*;
36+
match self {
37+
InputFloating | InputPullUp | InputPullDown => false,
38+
OutputPushPull | OutputOpenDrain => true,
39+
}
40+
}
41+
}
42+
43+
// For convertion simplify
44+
struct Unknown;
45+
46+
impl<const P: char, const N: u8> DynamicPin<P, N> {
47+
pub const fn new(mode: Dynamic) -> Self {
48+
Self { mode }
49+
}
50+
51+
#[inline]
52+
pub fn make_pull_up_input(&mut self) {
53+
// NOTE(unsafe), we have a mutable reference to the current pin
54+
Pin::<Unknown, P, N>::new().into_pull_up_input();
55+
self.mode = Dynamic::InputPullUp;
56+
}
57+
#[inline]
58+
pub fn make_pull_down_input(&mut self) {
59+
// NOTE(unsafe), we have a mutable reference to the current pin
60+
Pin::<Unknown, P, N>::new().into_pull_down_input();
61+
self.mode = Dynamic::InputPullDown;
62+
}
63+
#[inline]
64+
pub fn make_floating_input(&mut self) {
65+
// NOTE(unsafe), we have a mutable reference to the current pin
66+
Pin::<Unknown, P, N>::new().into_floating_input();
67+
self.mode = Dynamic::InputFloating;
68+
}
69+
#[inline]
70+
pub fn make_push_pull_output(&mut self) {
71+
// NOTE(unsafe), we have a mutable reference to the current pin
72+
Pin::<Unknown, P, N>::new().into_push_pull_output();
73+
self.mode = Dynamic::OutputPushPull;
74+
}
75+
#[inline]
76+
pub fn make_push_pull_output_in_state(&mut self, state: PinState) {
77+
// NOTE(unsafe), we have a mutable reference to the current pin
78+
Pin::<Unknown, P, N>::new().into_push_pull_output_in_state(state);
79+
self.mode = Dynamic::OutputPushPull;
80+
}
81+
#[inline]
82+
pub fn make_open_drain_output(&mut self) {
83+
// NOTE(unsafe), we have a mutable reference to the current pin
84+
Pin::<Unknown, P, N>::new().into_open_drain_output();
85+
self.mode = Dynamic::OutputOpenDrain;
86+
}
87+
#[inline]
88+
pub fn make_open_drain_output_in_state(&mut self, state: PinState) {
89+
// NOTE(unsafe), we have a mutable reference to the current pin
90+
Pin::<Unknown, P, N>::new().into_open_drain_output_in_state(state);
91+
self.mode = Dynamic::OutputOpenDrain;
92+
}
93+
94+
pub fn set_high(&mut self) -> Result<(), PinModeError> {
95+
if self.mode.is_output() {
96+
Pin::<Unknown, P, N>::new()._set_state(PinState::High);
97+
Ok(())
98+
} else {
99+
Err(PinModeError::IncorrectMode)
100+
}
101+
}
102+
pub fn set_low(&mut self) -> Result<(), PinModeError> {
103+
if self.mode.is_output() {
104+
Pin::<Unknown, P, N>::new()._set_state(PinState::Low);
105+
Ok(())
106+
} else {
107+
Err(PinModeError::IncorrectMode)
108+
}
109+
}
110+
111+
pub fn is_high(&self) -> Result<bool, PinModeError> {
112+
self.is_low().map(|b| !b)
113+
}
114+
pub fn is_low(&self) -> Result<bool, PinModeError> {
115+
if self.mode.is_input() {
116+
Ok(Pin::<Unknown, P, N>::new()._is_low())
117+
} else {
118+
Err(PinModeError::IncorrectMode)
119+
}
120+
}
121+
}

src/gpio/hal_02.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use core::convert::Infallible;
22

33
use super::{
4-
ErasedPin, Floating, Input, OpenDrain, Output, PartiallyErasedPin, Pin, PullDown, PullUp,
5-
PushPull,
4+
dynamic::PinModeError, DynamicPin, ErasedPin, Floating, Input, OpenDrain, Output,
5+
PartiallyErasedPin, Pin, PullDown, PullUp, PushPull,
66
};
77

88
pub use embedded_hal::digital::v2::PinState;
@@ -333,3 +333,25 @@ impl<MODE, const P: char> InputPin for PartiallyErasedPin<Input<MODE>, P> {
333333
Ok(self.is_low())
334334
}
335335
}
336+
337+
// Implementations for `DynamicPin`
338+
339+
impl<const P: char, const N: u8> OutputPin for DynamicPin<P, N> {
340+
type Error = PinModeError;
341+
fn set_high(&mut self) -> Result<(), Self::Error> {
342+
self.set_high()
343+
}
344+
fn set_low(&mut self) -> Result<(), Self::Error> {
345+
self.set_low()
346+
}
347+
}
348+
349+
impl<const P: char, const N: u8> InputPin for DynamicPin<P, N> {
350+
type Error = PinModeError;
351+
fn is_high(&self) -> Result<bool, Self::Error> {
352+
self.is_high()
353+
}
354+
fn is_low(&self) -> Result<bool, Self::Error> {
355+
self.is_low()
356+
}
357+
}

0 commit comments

Comments
 (0)