aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2021-05-05 19:49:21 +0200
committerGitHub <[email protected]>2021-05-05 19:49:21 +0200
commit58b4909fa07d21190e86ab1a1424e2c164f596bd (patch)
tree35cd572abb6b776f9761c4e1c904f32a9a301d5e
parente40346a54495e2cf2de6cf45c8c6e87bb634165f (diff)
parent9d427a1ba4489487bb58387e1b809bb8d7fe8bc8 (diff)
Merge pull request #157 from derekdreery/flex-pin
Add a pin variant that can change between disconnected/input/output.
-rw-r--r--embassy-nrf/src/gpio.rs191
1 files changed, 154 insertions, 37 deletions
diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs
index a06ee6053..14ac61822 100644
--- a/embassy-nrf/src/gpio.rs
+++ b/embassy-nrf/src/gpio.rs
@@ -41,24 +41,7 @@ impl<'d, T: Pin> Input<'d, T> {
41 pub fn new(pin: impl Unborrow<Target = T> + 'd, pull: Pull) -> Self { 41 pub fn new(pin: impl Unborrow<Target = T> + 'd, pull: Pull) -> Self {
42 unborrow!(pin); 42 unborrow!(pin);
43 43
44 pin.conf().write(|w| { 44 init_input(&pin, pull);
45 w.dir().input();
46 w.input().connect();
47 match pull {
48 Pull::None => {
49 w.pull().disabled();
50 }
51 Pull::Up => {
52 w.pull().pullup();
53 }
54 Pull::Down => {
55 w.pull().pulldown();
56 }
57 }
58 w.drive().s0s1();
59 w.sense().disabled();
60 w
61 });
62 45
63 Self { 46 Self {
64 pin, 47 pin,
@@ -93,6 +76,7 @@ pub enum Level {
93 High, 76 High,
94} 77}
95 78
79// These numbers match DRIVE_A exactly so hopefully the compiler will unify them.
96#[derive(Clone, Copy, Debug, PartialEq)] 80#[derive(Clone, Copy, Debug, PartialEq)]
97#[cfg_attr(feature = "defmt", derive(defmt::Format))] 81#[cfg_attr(feature = "defmt", derive(defmt::Format))]
98#[repr(u8)] 82#[repr(u8)]
@@ -134,25 +118,7 @@ impl<'d, T: Pin> Output<'d, T> {
134 Level::Low => pin.set_low(), 118 Level::Low => pin.set_low(),
135 } 119 }
136 120
137 let drive = match drive { 121 init_output(&pin, drive);
138 OutputDrive::Standard => DRIVE_A::S0S1,
139 OutputDrive::HighDrive0Standard1 => DRIVE_A::H0S1,
140 OutputDrive::Standard0HighDrive1 => DRIVE_A::S0H1,
141 OutputDrive::HighDrive => DRIVE_A::H0H1,
142 OutputDrive::Disconnect0Standard1 => DRIVE_A::D0S1,
143 OutputDrive::Disconnect0HighDrive1 => DRIVE_A::D0H1,
144 OutputDrive::Standard0Disconnect1 => DRIVE_A::S0D1,
145 OutputDrive::HighDrive0Disconnect1 => DRIVE_A::H0D1,
146 };
147
148 pin.conf().write(|w| {
149 w.dir().output();
150 w.input().disconnect();
151 w.pull().disabled();
152 w.drive().variant(drive);
153 w.sense().disabled();
154 w
155 });
156 122
157 Self { 123 Self {
158 pin, 124 pin,
@@ -205,6 +171,108 @@ impl<'d, T: Pin> StatefulOutputPin for Output<'d, T> {
205 } 171 }
206} 172}
207 173
174/// GPIO flexible pin.
175///
176/// This pin can either be a disconnected, input, or output pin. The level register bit will remain
177/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output
178/// mode.
179pub struct FlexPin<'d, T: Pin> {
180 pub(crate) pin: T,
181 phantom: PhantomData<&'d mut T>,
182}
183
184impl<'d, T: Pin> FlexPin<'d, T> {
185 /// Wrap the pin in a `FlexPin`.
186 ///
187 /// The pin remains disconnected. The initial output level is unspecified, but can be changed
188 /// before the pin is put into output mode.
189 pub fn new(pin: impl Unborrow<Target = T> + 'd) -> Self {
190 unborrow!(pin);
191 // Pin will be in disconnected state.
192 Self {
193 pin,
194 phantom: PhantomData,
195 }
196 }
197
198 /// Put the pin into input mode.
199 pub fn set_as_input(&mut self, pull: Pull) {
200 init_input(&self.pin, pull);
201 }
202
203 /// Put the pin into output mode.
204 ///
205 /// The pin level will be whatever was set before (or low by default). If you want it to begin
206 /// at a specific level, call `set_high`/`set_low` on the pin first.
207 pub fn set_as_output(&mut self, drive: OutputDrive) {
208 init_output(&self.pin, drive);
209 }
210
211 /// Put the pin into disconnected mode.
212 pub fn set_as_disconnected(&mut self) {
213 self.pin.conf().reset();
214 }
215}
216
217impl<'d, T: Pin> Drop for FlexPin<'d, T> {
218 fn drop(&mut self) {
219 self.pin.conf().reset();
220 }
221}
222
223/// Implement [`InputPin`] for [`FlexPin`];
224///
225/// If the pin is not in input mode the result is unspecified.
226impl<'d, T: Pin> InputPin for FlexPin<'d, T> {
227 type Error = Infallible;
228
229 fn is_high(&self) -> Result<bool, Self::Error> {
230 self.is_low().map(|v| !v)
231 }
232
233 fn is_low(&self) -> Result<bool, Self::Error> {
234 Ok(self.pin.block().in_.read().bits() & (1 << self.pin.pin()) == 0)
235 }
236}
237
238impl<'d, T: Pin> OutputPin for FlexPin<'d, T> {
239 type Error = Infallible;
240
241 /// Set the output as high.
242 fn set_high(&mut self) -> Result<(), Self::Error> {
243 unsafe {
244 self.pin
245 .block()
246 .outset
247 .write(|w| w.bits(1u32 << self.pin.pin()));
248 }
249 Ok(())
250 }
251
252 /// Set the output as low.
253 fn set_low(&mut self) -> Result<(), Self::Error> {
254 unsafe {
255 self.pin
256 .block()
257 .outclr
258 .write(|w| w.bits(1u32 << self.pin.pin()));
259 }
260 Ok(())
261 }
262}
263
264impl<'d, T: Pin> StatefulOutputPin for FlexPin<'d, T> {
265 /// Is the output pin set as high?
266 fn is_set_high(&self) -> Result<bool, Self::Error> {
267 self.is_set_low().map(|v| !v)
268 }
269
270 /// Is the output pin set as low?
271 fn is_set_low(&self) -> Result<bool, Self::Error> {
272 Ok(self.pin.block().out.read().bits() & (1 << self.pin.pin()) == 0)
273 }
274}
275
208pub(crate) mod sealed { 276pub(crate) mod sealed {
209 use super::*; 277 use super::*;
210 278
@@ -314,6 +382,55 @@ impl sealed::Pin for AnyPin {
314 } 382 }
315} 383}
316 384
385// =====================
386
387/// Set up a pin for input
388#[inline]
389fn init_input<T: Pin>(pin: &T, pull: Pull) {
390 pin.conf().write(|w| {
391 w.dir().input();
392 w.input().connect();
393 match pull {
394 Pull::None => {
395 w.pull().disabled();
396 }
397 Pull::Up => {
398 w.pull().pullup();
399 }
400 Pull::Down => {
401 w.pull().pulldown();
402 }
403 }
404 w.drive().s0s1();
405 w.sense().disabled();
406 w
407 });
408}
409
410/// Set up a pin for output
411#[inline]
412fn init_output<T: Pin>(pin: &T, drive: OutputDrive) {
413 let drive = match drive {
414 OutputDrive::Standard => DRIVE_A::S0S1,
415 OutputDrive::HighDrive0Standard1 => DRIVE_A::H0S1,
416 OutputDrive::Standard0HighDrive1 => DRIVE_A::S0H1,
417 OutputDrive::HighDrive => DRIVE_A::H0H1,
418 OutputDrive::Disconnect0Standard1 => DRIVE_A::D0S1,
419 OutputDrive::Disconnect0HighDrive1 => DRIVE_A::D0H1,
420 OutputDrive::Standard0Disconnect1 => DRIVE_A::S0D1,
421 OutputDrive::HighDrive0Disconnect1 => DRIVE_A::H0D1,
422 };
423
424 pin.conf().write(|w| {
425 w.dir().output();
426 w.input().disconnect();
427 w.pull().disabled();
428 w.drive().variant(drive);
429 w.sense().disabled();
430 w
431 });
432}
433
317// ==================== 434// ====================
318 435
319pub trait OptionalPin: sealed::OptionalPin + Sized { 436pub trait OptionalPin: sealed::OptionalPin + Sized {