Skip to content

Commit 0b3ea14

Browse files
authored
Merge pull request #107 from jacobbarssbailey/fully-erasable-pins
add fully erasable pins: allow downgrading partially erased pins (pin…
2 parents b588115 + 2ea5221 commit 0b3ea14

File tree

1 file changed

+138
-0
lines changed

1 file changed

+138
-0
lines changed

src/gpio.rs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
use core::marker::PhantomData;
33

44
use crate::rcc::Rcc;
5+
use core::convert::Infallible;
56
use embedded_hal::digital::v2::PinState;
7+
use hal::digital::v2::{toggleable, InputPin, OutputPin, StatefulOutputPin};
68

79
/// Default pin mode
810
pub type DefaultMode = Analog;
@@ -16,6 +18,13 @@ pub trait GpioExt {
1618
fn split(self, rcc: &mut Rcc) -> Self::Parts;
1719
}
1820

21+
trait GpioRegExt {
22+
fn is_low(&self, pos: u8) -> bool;
23+
fn is_set_low(&self, pos: u8) -> bool;
24+
fn set_high(&self, pos: u8);
25+
fn set_low(&self, pos: u8);
26+
}
27+
1928
/// Input mode (type state)
2029
pub struct Input<MODE> {
2130
_mode: PhantomData<MODE>,
@@ -44,6 +53,106 @@ pub struct Output<MODE> {
4453
/// Push pull output (type state)
4554
pub struct PushPull;
4655

56+
/// Fully erased pin
57+
pub struct Pin<MODE> {
58+
i: u8,
59+
port: *const dyn GpioRegExt,
60+
_mode: PhantomData<MODE>,
61+
}
62+
63+
macro_rules! gpio_trait {
64+
($gpiox:ident) => {
65+
impl GpioRegExt for crate::stm32::$gpiox::RegisterBlock {
66+
fn is_low(&self, pos: u8) -> bool {
67+
// NOTE(unsafe) atomic read with no side effects
68+
self.idr.read().bits() & (1 << pos) == 0
69+
}
70+
71+
fn is_set_low(&self, pos: u8) -> bool {
72+
// NOTE(unsafe) atomic read with no side effects
73+
self.odr.read().bits() & (1 << pos) == 0
74+
}
75+
76+
fn set_high(&self, pos: u8) {
77+
// NOTE(unsafe) atomic write to a stateless register
78+
unsafe { self.bsrr.write(|w| w.bits(1 << pos)) }
79+
}
80+
81+
fn set_low(&self, pos: u8) {
82+
// NOTE(unsafe) atomic write to a stateless register
83+
unsafe { self.bsrr.write(|w| w.bits(1 << (pos + 16))) }
84+
}
85+
}
86+
};
87+
}
88+
89+
gpio_trait!(gpioa);
90+
gpio_trait!(gpiob);
91+
92+
// NOTE(unsafe) The only write acess is to BSRR, which is thread safe
93+
unsafe impl<MODE> Sync for Pin<MODE> {}
94+
// NOTE(unsafe) this only enables read access to the same pin from multiple
95+
// threads
96+
unsafe impl<MODE> Send for Pin<MODE> {}
97+
98+
impl<MODE> StatefulOutputPin for Pin<Output<MODE>> {
99+
#[inline(always)]
100+
fn is_set_high(&self) -> Result<bool, Self::Error> {
101+
self.is_set_low().map(|v| !v)
102+
}
103+
104+
#[inline(always)]
105+
fn is_set_low(&self) -> Result<bool, Self::Error> {
106+
Ok(unsafe { (*self.port).is_set_low(self.i) })
107+
}
108+
}
109+
110+
impl<MODE> OutputPin for Pin<Output<MODE>> {
111+
type Error = Infallible;
112+
113+
#[inline(always)]
114+
fn set_high(&mut self) -> Result<(), Self::Error> {
115+
unsafe { (*self.port).set_high(self.i) };
116+
Ok(())
117+
}
118+
119+
#[inline(always)]
120+
fn set_low(&mut self) -> Result<(), Self::Error> {
121+
unsafe { (*self.port).set_low(self.i) }
122+
Ok(())
123+
}
124+
}
125+
126+
impl<MODE> toggleable::Default for Pin<Output<MODE>> {}
127+
128+
impl InputPin for Pin<Output<OpenDrain>> {
129+
type Error = Infallible;
130+
131+
#[inline(always)]
132+
fn is_high(&self) -> Result<bool, Self::Error> {
133+
self.is_low().map(|v| !v)
134+
}
135+
136+
#[inline(always)]
137+
fn is_low(&self) -> Result<bool, Self::Error> {
138+
Ok(unsafe { (*self.port).is_low(self.i) })
139+
}
140+
}
141+
142+
impl<MODE> InputPin for Pin<Input<MODE>> {
143+
type Error = Infallible;
144+
145+
#[inline(always)]
146+
fn is_high(&self) -> Result<bool, Self::Error> {
147+
self.is_low().map(|v| !v)
148+
}
149+
150+
#[inline(always)]
151+
fn is_low(&self) -> Result<bool, Self::Error> {
152+
Ok(unsafe { (*self.port).is_low(self.i) })
153+
}
154+
}
155+
47156
/// GPIO Pin speed selection
48157
pub enum Speed {
49158
Low = 0,
@@ -494,6 +603,35 @@ macro_rules! gpio {
494603
self.i
495604
}
496605
}
606+
607+
impl<MODE> $PXx<Output<MODE>> {
608+
/// Erases the port number from the type
609+
///
610+
/// This is useful when you want to collect the pins into an array where you
611+
/// need all the elements to have the same type
612+
pub fn downgrade(self) -> Pin<Output<MODE>> {
613+
Pin {
614+
i: self.get_id(),
615+
port: $GPIOX::ptr() as *const dyn GpioRegExt,
616+
_mode: self._mode,
617+
}
618+
}
619+
}
620+
621+
impl<MODE> $PXx<Input<MODE>> {
622+
/// Erases the port number from the type
623+
///
624+
/// This is useful when you want to collect the pins into an array where you
625+
/// need all the elements to have the same type
626+
pub fn downgrade(self) -> Pin<Input<MODE>> {
627+
Pin {
628+
i: self.get_id(),
629+
port: $GPIOX::ptr() as *const dyn GpioRegExt,
630+
_mode: self._mode,
631+
}
632+
}
633+
}
634+
497635
}
498636
}
499637
}

0 commit comments

Comments
 (0)