Skip to content

Commit 59f16cd

Browse files
authored
Merge pull request #74 from jonas-schievink/pin-temp-mode
Add an API for temporary GPIO reconfiguration
2 parents e9363ca + 22c5849 commit 59f16cd

File tree

3 files changed

+200
-76
lines changed

3 files changed

+200
-76
lines changed

src/gpio.rs

Lines changed: 196 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,63 @@ pub struct Output<MODE> {
4141
/// Push pull output (type state)
4242
pub struct PushPull;
4343

44+
mod sealed {
45+
pub trait Sealed {}
46+
}
47+
48+
/// Marker trait for valid pin modes (type state).
49+
///
50+
/// It can not be implemented by outside types.
51+
pub trait PinMode: sealed::Sealed {
52+
// These constants are used to implement the pin configuration code.
53+
// They are not part of public API.
54+
55+
#[doc(hidden)]
56+
const PUPDR: u8;
57+
#[doc(hidden)]
58+
const MODER: u8;
59+
#[doc(hidden)]
60+
const OTYPER: Option<u8> = None;
61+
}
62+
63+
impl sealed::Sealed for Input<Floating> {}
64+
impl PinMode for Input<Floating> {
65+
const PUPDR: u8 = 0b00;
66+
const MODER: u8 = 0b00;
67+
}
68+
69+
impl sealed::Sealed for Input<PullDown> {}
70+
impl PinMode for Input<PullDown> {
71+
const PUPDR: u8 = 0b10;
72+
const MODER: u8 = 0b00;
73+
}
74+
75+
impl sealed::Sealed for Input<PullUp> {}
76+
impl PinMode for Input<PullUp> {
77+
const PUPDR: u8 = 0b01;
78+
const MODER: u8 = 0b00;
79+
}
80+
81+
impl sealed::Sealed for Analog {}
82+
impl PinMode for Analog {
83+
const PUPDR: u8 = 0b00;
84+
const MODER: u8 = 0b11;
85+
}
86+
87+
impl sealed::Sealed for Output<OpenDrain> {}
88+
impl PinMode for Output<OpenDrain> {
89+
const PUPDR: u8 = 0b00;
90+
const MODER: u8 = 0b01;
91+
const OTYPER: Option<u8> = Some(0b1);
92+
}
93+
94+
impl sealed::Sealed for Output<PushPull> {}
95+
impl PinMode for Output<PushPull> {
96+
const PUPDR: u8 = 0b00;
97+
const MODER: u8 = 0b01;
98+
const OTYPER: Option<u8> = Some(0b0);
99+
}
100+
44101
/// GPIO Pin speed selection
45102
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
46103
pub enum Speed {
@@ -85,7 +142,8 @@ macro_rules! gpio {
85142
use crate::rcc::Rcc;
86143
use super::{
87144
Floating, GpioExt, Input, OpenDrain, Output, Speed,
88-
PullDown, PullUp, PushPull, AltMode, Analog, Port
145+
PullDown, PullUp, PushPull, AltMode, Analog, Port,
146+
PinMode,
89147
};
90148

91149
/// GPIO parts
@@ -218,122 +276,188 @@ macro_rules! gpio {
218276
}
219277
}
220278

221-
impl<MODE> $PXi<MODE> {
222-
/// Configures the pin to operate as a floating input pin
223-
pub fn into_floating_input(
224-
self,
225-
) -> $PXi<Input<Floating>> {
279+
impl<MODE: PinMode> $PXi<MODE> {
280+
/// Puts `self` into mode `M`.
281+
///
282+
/// This violates the type state constraints from `MODE`, so callers must
283+
/// ensure they use this properly.
284+
fn mode<M: PinMode>(&mut self) {
226285
let offset = 2 * $i;
227286
unsafe {
228287
&(*$GPIOX::ptr()).pupdr.modify(|r, w| {
229-
w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset))
288+
w.bits((r.bits() & !(0b11 << offset)) | (u32::from(M::PUPDR) << offset))
230289
});
290+
291+
if let Some(otyper) = M::OTYPER {
292+
&(*$GPIOX::ptr()).otyper.modify(|r, w| {
293+
w.bits(r.bits() & !(0b1 << $i) | (u32::from(otyper) << $i))
294+
});
295+
}
296+
231297
&(*$GPIOX::ptr()).moder.modify(|r, w| {
232-
w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset))
233-
})
298+
w.bits((r.bits() & !(0b11 << offset)) | (u32::from(M::MODER) << offset))
299+
});
300+
}
301+
}
302+
303+
fn with_mode<M, F, R>(
304+
&mut self,
305+
f: F
306+
) -> R
307+
where
308+
M: PinMode,
309+
F: FnOnce(&mut $PXi<M>) -> R,
310+
{
311+
struct ResetMode<'a, ORIG: PinMode> {
312+
pin: &'a mut $PXi<ORIG>,
313+
}
314+
315+
impl<'a, ORIG: PinMode> Drop for ResetMode<'a, ORIG> {
316+
fn drop(&mut self) {
317+
self.pin.mode::<ORIG>();
318+
}
319+
}
320+
321+
self.mode::<M>();
322+
323+
// This will reset the pin back to the original mode when dropped.
324+
// (so either when `with_mode` returns or when `f` unwinds)
325+
let _resetti = ResetMode { pin: self };
326+
327+
let mut witness = $PXi {
328+
_mode: PhantomData
234329
};
330+
331+
f(&mut witness)
332+
}
333+
334+
/// Configures the pin to operate as a floating input pin.
335+
pub fn into_floating_input(
336+
mut self,
337+
) -> $PXi<Input<Floating>> {
338+
self.mode::<Input<Floating>>();
235339
$PXi {
236340
_mode: PhantomData
237341
}
238342
}
239343

240-
/// Configures the pin to operate as a pulled down input pin
344+
/// Temporarily configures this pin as a floating input.
345+
///
346+
/// The closure `f` is called with the reconfigured pin. After it returns,
347+
/// the pin will be configured back.
348+
pub fn with_floating_input<R>(
349+
&mut self,
350+
f: impl FnOnce(&mut $PXi<Input<Floating>>) -> R,
351+
) -> R {
352+
self.with_mode(f)
353+
}
354+
355+
/// Configures the pin to operate as a pulled-down input pin.
241356
pub fn into_pull_down_input(
242-
self,
243-
) -> $PXi<Input<PullDown>> {
244-
let offset = 2 * $i;
245-
unsafe {
246-
&(*$GPIOX::ptr()).pupdr.modify(|r, w| {
247-
w.bits((r.bits() & !(0b11 << offset)) | (0b10 << offset))
248-
});
249-
&(*$GPIOX::ptr()).moder.modify(|r, w| {
250-
w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset))
251-
})
252-
};
357+
mut self,
358+
) -> $PXi<Input<PullDown>> {
359+
self.mode::<Input<Floating>>();
253360
$PXi {
254361
_mode: PhantomData
255362
}
256363
}
257364

258-
/// Configures the pin to operate as a pulled up input pin
365+
/// Temporarily configures this pin as a pulled-down input.
366+
///
367+
/// The closure `f` is called with the reconfigured pin. After it returns,
368+
/// the pin will be configured back.
369+
pub fn with_pull_down_input<R>(
370+
&mut self,
371+
f: impl FnOnce(&mut $PXi<Input<PullDown>>) -> R,
372+
) -> R {
373+
self.with_mode(f)
374+
}
375+
376+
/// Configures the pin to operate as a pulled-up input pin.
259377
pub fn into_pull_up_input(
260-
self,
378+
mut self,
261379
) -> $PXi<Input<PullUp>> {
262-
let offset = 2 * $i;
263-
unsafe {
264-
&(*$GPIOX::ptr()).pupdr.modify(|r, w| {
265-
w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset))
266-
});
267-
&(*$GPIOX::ptr()).moder.modify(|r, w| {
268-
w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset))
269-
})
270-
};
380+
self.mode::<Input<PullUp>>();
271381
$PXi {
272382
_mode: PhantomData
273383
}
274384
}
275385

276-
/// Configures the pin to operate as an analog pin
386+
/// Temporarily configures this pin as a pulled-up input.
387+
///
388+
/// The closure `f` is called with the reconfigured pin. After it returns,
389+
/// the pin will be configured back.
390+
pub fn with_pull_up_input<R>(
391+
&mut self,
392+
f: impl FnOnce(&mut $PXi<Input<PullUp>>) -> R,
393+
) -> R {
394+
self.with_mode(f)
395+
}
396+
397+
/// Configures the pin to operate as an analog pin.
277398
pub fn into_analog(
278-
self,
399+
mut self,
279400
) -> $PXi<Analog> {
280-
let offset = 2 * $i;
281-
unsafe {
282-
&(*$GPIOX::ptr()).pupdr.modify(|r, w| {
283-
w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset))
284-
});
285-
&(*$GPIOX::ptr()).moder.modify(|r, w| {
286-
w.bits((r.bits() & !(0b11 << offset)) | (0b11 << offset))
287-
});
288-
}
401+
self.mode::<Analog>();
289402
$PXi {
290403
_mode: PhantomData
291404
}
292405
}
293406

294-
/// Configures the pin to operate as an open drain output pin
407+
/// Temporarily configures this pin as an analog pin.
408+
///
409+
/// The closure `f` is called with the reconfigured pin. After it returns,
410+
/// the pin will be configured back.
411+
pub fn with_analog<R>(
412+
&mut self,
413+
f: impl FnOnce(&mut $PXi<Analog>) -> R,
414+
) -> R {
415+
self.with_mode(f)
416+
}
417+
418+
/// Configures the pin to operate as an open drain output pin.
295419
pub fn into_open_drain_output(
296-
self,
420+
mut self,
297421
) -> $PXi<Output<OpenDrain>> {
298-
let offset = 2 * $i;
299-
unsafe {
300-
&(*$GPIOX::ptr()).pupdr.modify(|r, w| {
301-
w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset))
302-
});
303-
&(*$GPIOX::ptr()).otyper.modify(|r, w| {
304-
w.bits(r.bits() | (0b1 << $i))
305-
});
306-
&(*$GPIOX::ptr()).moder.modify(|r, w| {
307-
w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset))
308-
})
309-
};
422+
self.mode::<Output<OpenDrain>>();
310423
$PXi {
311424
_mode: PhantomData
312425
}
313426
}
314427

315-
/// Configures the pin to operate as an push pull output pin
428+
/// Temporarily configures this pin as an open drain output.
429+
///
430+
/// The closure `f` is called with the reconfigured pin. After it returns,
431+
/// the pin will be configured back.
432+
pub fn with_open_drain_output<R>(
433+
&mut self,
434+
f: impl FnOnce(&mut $PXi<Output<OpenDrain>>) -> R,
435+
) -> R {
436+
self.with_mode(f)
437+
}
438+
439+
/// Configures the pin to operate as an push-pull output pin.
316440
pub fn into_push_pull_output(
317-
self,
441+
mut self,
318442
) -> $PXi<Output<PushPull>> {
319-
let offset = 2 * $i;
320-
unsafe {
321-
&(*$GPIOX::ptr()).pupdr.modify(|r, w| {
322-
w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset))
323-
});
324-
&(*$GPIOX::ptr()).otyper.modify(|r, w| {
325-
w.bits(r.bits() & !(0b1 << $i))
326-
});
327-
&(*$GPIOX::ptr()).moder.modify(|r, w| {
328-
w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset))
329-
})
330-
};
443+
self.mode::<Output<PushPull>>();
331444
$PXi {
332445
_mode: PhantomData
333446
}
334447
}
335448

336-
/// Set pin speed
449+
/// Temporarily configures this pin as a push-pull output.
450+
///
451+
/// The closure `f` is called with the reconfigured pin. After it returns,
452+
/// the pin will be configured back.
453+
pub fn with_push_pull_output<R>(
454+
&mut self,
455+
f: impl FnOnce(&mut $PXi<Output<PushPull>>) -> R,
456+
) -> R {
457+
self.with_mode(f)
458+
}
459+
460+
/// Set pin speed.
337461
pub fn set_speed(self, speed: Speed) -> Self {
338462
let offset = 2 * $i;
339463
unsafe {

src/pwm.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use core::ops::Deref;
44
use cortex_m::interrupt;
55

66
use crate::gpio::gpioa::{PA0, PA1, PA2, PA3};
7-
use crate::gpio::AltMode;
7+
use crate::gpio::{AltMode, PinMode};
88
use crate::hal;
99
use crate::pac::{tim2, TIM2, TIM3};
1010
use crate::rcc::Rcc;
@@ -251,7 +251,7 @@ macro_rules! impl_pin {
251251
) => {
252252
$(
253253
$(
254-
impl<State> Pin<$instance, $channel> for $name<State> {
254+
impl<State: PinMode> Pin<$instance, $channel> for $name<State> {
255255
fn setup(&self) {
256256
self.set_alt_mode(AltMode::$alternate_function);
257257
}

src/serial.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use core::marker::PhantomData;
33
use core::ptr;
44

55
use crate::gpio::gpioa::*;
6-
use crate::gpio::AltMode;
6+
use crate::gpio::{PinMode, AltMode};
77
use crate::hal;
88
use crate::hal::prelude::*;
99
pub use crate::pac::USART2;
@@ -159,7 +159,7 @@ pub trait Pins<USART> {
159159
macro_rules! impl_pins {
160160
($($instance:ty, $tx:ident, $rx:ident, $alt:ident;)*) => {
161161
$(
162-
impl<Tx, Rx> Pins<$instance> for ($tx<Tx>, $rx<Rx>) {
162+
impl<Tx: PinMode, Rx: PinMode> Pins<$instance> for ($tx<Tx>, $rx<Rx>) {
163163
fn setup(&self) {
164164
self.0.set_alt_mode(AltMode::$alt);
165165
self.1.set_alt_mode(AltMode::$alt);

0 commit comments

Comments
 (0)