nrf52_hal_common/
gpio.rs

1// TODO - clean these up
2#![allow(unused_imports)]
3#![allow(non_camel_case_types)]
4
5use core::marker::PhantomData;
6
7/// Input mode (type state)
8pub struct Input<MODE> {
9    _mode: PhantomData<MODE>,
10}
11
12/// Floating input (type state)
13pub struct Floating;
14/// Pulled down input (type state)
15pub struct PullDown;
16/// Pulled up input (type state)
17pub struct PullUp;
18
19/// Output mode (type state)
20pub struct Output<MODE> {
21    _mode: PhantomData<MODE>,
22}
23
24/// Extension trait to split a GPIO peripheral in independent pins and registers
25pub trait GpioExt {
26    /// The to split the GPIO into
27    type Parts;
28
29    /// Splits the GPIO block into independent pins and registers
30    fn split(
31        self,
32        // apb2: &mut APB2
33    ) -> Self::Parts;
34}
35
36/// Push pull output (type state)
37pub struct PushPull;
38/// Open drain output (type state)
39pub struct OpenDrain;
40
41// /// Alternate function
42// pub struct Alternate<MODE> {
43//     _mode: PhantomData<MODE>,
44// }
45
46
47/// Represents a digital input or output level
48pub enum Level {
49    Low,
50    High,
51}
52
53
54// ===============================================================
55// Implement Generic Pins for this port, which allows you to use
56// other peripherals without having to be completely rust-generic
57// across all of the possible pins
58// ===============================================================
59/// Generic $PX pin
60pub struct Pin<MODE> {
61    pub pin: u8,
62    #[cfg(feature = "52840")]
63    pub port: bool,
64    _mode: PhantomData<MODE>,
65}
66
67use crate::target::P0;
68
69#[cfg(feature = "52840")]
70use crate::target::{ P1 };
71
72use crate::hal::digital::{OutputPin, StatefulOutputPin, InputPin};
73
74impl<MODE> Pin<MODE> {
75    /// Convert the pin to be a floating input
76    pub fn into_floating_input(self) -> Pin<Input<Floating>> {
77        unsafe {
78            &(*{
79                #[cfg(any(feature = "52810", feature = "52832"))]
80                { P0::ptr() }
81                #[cfg(feature = "52840")]
82                { if !self.port { P0::ptr() } else { P1::ptr() } }
83            }).pin_cnf[self.pin as usize]
84        }
85        .write(|w| {
86            w.dir().input()
87                .input().connect()
88                .pull().disabled()
89                .drive().s0s1()
90                .sense().disabled()
91        });
92
93        Pin {
94            _mode: PhantomData,
95            #[cfg(feature = "52840")]
96            port: self.port,
97            pin: self.pin
98        }
99    }
100    pub fn into_pullup_input(self) -> Pin<Input<PullUp>> {
101        unsafe {
102            &(*{
103                #[cfg(any(feature = "52810", feature = "52832"))]
104                { P0::ptr() }
105                #[cfg(feature = "52840")]
106                { if !self.port { P0::ptr() } else { P1::ptr() } }
107            }).pin_cnf[self.pin as usize]
108        }
109        .write(|w| {
110            w.dir().input()
111                .input().connect()
112                .pull().pullup()
113                .drive().s0s1()
114                .sense().disabled()
115        });
116
117        Pin {
118            _mode: PhantomData,
119            #[cfg(feature = "52840")]
120            port: self.port,
121            pin: self.pin
122        }
123    }
124    pub fn into_pulldown_input(self) -> Pin<Input<PullDown>> {
125        unsafe {
126            &(*{
127                #[cfg(any(feature = "52810", feature = "52832"))]
128                { P0::ptr() }
129                #[cfg(feature = "52840")]
130                { if !self.port { P0::ptr() } else { P1::ptr() } }
131            }).pin_cnf[self.pin as usize]
132        }
133        .write(|w| {
134            w.dir().input()
135                .input().connect()
136                .pull().pulldown()
137                .drive().s0s1()
138                .sense().disabled()
139        });
140
141        Pin {
142            _mode: PhantomData,
143            #[cfg(feature = "52840")]
144            port: self.port,
145            pin: self.pin
146        }
147    }
148
149    /// Convert the pin to be a push-pull output with normal drive
150    pub fn into_push_pull_output(self, initial_output: Level)
151        -> Pin<Output<PushPull>>
152    {
153        let mut pin = Pin {
154            _mode: PhantomData,
155            #[cfg(feature = "52840")]
156            port: self.port,
157            pin: self.pin
158        };
159
160        match initial_output {
161            Level::Low  => pin.set_low(),
162            Level::High => pin.set_high(),
163        }
164
165        unsafe {
166            &(*{
167                #[cfg(any(feature = "52810", feature = "52832"))]
168                { P0::ptr() }
169                #[cfg(feature = "52840")]
170                { if !self.port { P0::ptr() } else { P1::ptr() } }
171            }).pin_cnf[self.pin as usize]
172        }
173        .write(|w| {
174            w.dir().output()
175                .input().connect() // AJM - hack for SPI
176                .pull().disabled()
177                .drive().s0s1()
178                .sense().disabled()
179        });
180
181        pin
182    }
183
184    /// Convert the pin to be an open-drain output
185    ///
186    /// This method currently does not support configuring an
187    /// internal pull-up or pull-down resistor.
188    pub fn into_open_drain_output(self,
189        config:         OpenDrainConfig,
190        initial_output: Level,
191    )
192        -> Pin<Output<OpenDrain>>
193    {
194        let mut pin = Pin {
195            _mode: PhantomData,
196            #[cfg(feature = "52840")]
197            port: self.port,
198            pin: self.pin
199        };
200
201        match initial_output {
202            Level::Low  => pin.set_low(),
203            Level::High => pin.set_high(),
204        }
205
206        // This is safe, as we restrict our access to the dedicated
207        // register for this pin.
208        let pin_cnf = unsafe {
209            &(*{
210                #[cfg(any(feature = "52810", feature = "52832"))]
211                { P0::ptr() }
212                #[cfg(feature = "52840")]
213                { if !self.port { P0::ptr() } else { P1::ptr() } }
214            }).pin_cnf[self.pin as usize]
215        };
216        pin_cnf.write(|w| {
217            w
218                .dir().output()
219                .input().disconnect()
220                .pull().disabled()
221                .drive().variant(config.variant())
222                .sense().disabled()
223        });
224
225        pin
226    }
227}
228
229impl<MODE> InputPin for Pin<Input<MODE>> {
230    fn is_high(&self) -> bool {
231        !self.is_low()
232    }
233
234    fn is_low(&self) -> bool {
235        unsafe { (
236            (*{
237                #[cfg(any(feature = "52810", feature = "52832"))]
238                { P0::ptr() }
239                #[cfg(feature = "52840")]
240                { if !self.port { P0::ptr() } else { P1::ptr() } }
241            }).in_.read().bits() & (1 << self.pin)
242        ) == 0 }
243    }
244}
245
246impl<MODE> OutputPin for Pin<Output<MODE>> {
247    /// Set the output as high
248    fn set_high(&mut self) {
249        // NOTE(unsafe) atomic write to a stateless register - TODO(AJM) verify?
250        // TODO - I wish I could do something like `.pins$i()`...
251        unsafe {
252            (*{
253                #[cfg(any(feature = "52810", feature = "52832"))]
254                { P0::ptr() }
255                #[cfg(feature = "52840")]
256                { if !self.port { P0::ptr() } else { P1::ptr() } }
257            }).outset.write(|w| w.bits(1u32 << self.pin));
258        }
259    }
260
261    /// Set the output as low
262    fn set_low(&mut self) {
263        // NOTE(unsafe) atomic write to a stateless register - TODO(AJM) verify?
264        // TODO - I wish I could do something like `.pins$i()`...
265        unsafe {
266            (*{
267                #[cfg(any(feature = "52810", feature = "52832"))]
268                { P0::ptr() }
269                #[cfg(feature = "52840")]
270                { if !self.port { P0::ptr() } else { P1::ptr() } }
271            }).outclr.write(|w| w.bits(1u32 << self.pin));
272        }
273    }
274}
275
276impl<MODE> StatefulOutputPin for Pin<Output<MODE>> {
277    /// Is the output pin set as high?
278    fn is_set_high(&self) -> bool {
279        !self.is_set_low()
280    }
281
282    /// Is the output pin set as low?
283    fn is_set_low(&self) -> bool {
284        // NOTE(unsafe) atomic read with no side effects - TODO(AJM) verify?
285        // TODO - I wish I could do something like `.pins$i()`...
286        unsafe { (
287            (*{
288                #[cfg(any(feature = "52810", feature = "52832"))]
289                { P0::ptr() }
290                #[cfg(feature = "52840")]
291                { if !self.port { P0::ptr() } else { P1::ptr() } }
292            }).out.read().bits() & (1 << self.pin)
293        ) == 0 }
294    }
295}
296
297/// Pin configuration for open-drain mode
298pub enum OpenDrainConfig {
299    Disconnect0Standard1,
300    Disconnect0HighDrive1,
301    Standard0Disconnect1,
302    HighDrive0Disconnect1,
303}
304
305use crate::target::p0::{
306    pin_cnf,
307    PIN_CNF,
308};
309
310impl OpenDrainConfig {
311    fn variant(self) -> pin_cnf::DRIVEW {
312        use self::OpenDrainConfig::*;
313
314        match self {
315            Disconnect0Standard1  => pin_cnf::DRIVEW::D0S1,
316            Disconnect0HighDrive1 => pin_cnf::DRIVEW::D0H1,
317            Standard0Disconnect1  => pin_cnf::DRIVEW::S0D1,
318            HighDrive0Disconnect1 => pin_cnf::DRIVEW::H0D1,
319        }
320    }
321}
322
323macro_rules! gpio {
324    (
325        $PX:ident, $pxsvd:ident, $px:ident, $port_value:expr, [
326            $($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty),)+
327        ]
328    ) => {
329        /// GPIO
330        pub mod $px {
331            use super::{
332                Pin,
333
334                // Alternate,
335                Floating,
336                GpioExt,
337                Input,
338                Level,
339                OpenDrain,
340                OpenDrainConfig,
341                Output,
342                PullDown,
343                PullUp,
344                PushPull,
345
346                PhantomData,
347            };
348
349            use crate::target;
350            use crate::target::$PX;
351            use crate::hal::digital::{OutputPin, StatefulOutputPin, InputPin};
352
353            
354
355            // ===============================================================
356            // This chunk allows you to obtain an nrf52-hal gpio from the
357            // upstream nrf52 gpio definitions by defining a trait
358            // ===============================================================
359            /// GPIO parts
360            pub struct Parts {
361                $(
362                    /// Pin
363                    pub $pxi: $PXi<$MODE>,
364                )+
365            }
366
367            impl GpioExt for $PX {
368                type Parts = Parts;
369
370                fn split(self) -> Parts {
371                    Parts {
372                        $(
373                            $pxi: $PXi {
374                                _mode: PhantomData,
375                            },
376                        )+
377                    }
378                }
379            }
380
381            // ===============================================================
382            // Implement each of the typed pins usable through the nrf52-hal
383            // defined interface
384            // ===============================================================
385            $(
386                pub struct $PXi<MODE> {
387                    _mode: PhantomData<MODE>,
388                }
389
390
391                impl<MODE> $PXi<MODE> {
392                    /// Convert the pin to be a floating input
393                    pub fn into_floating_input(self) -> $PXi<Input<Floating>> {
394                        unsafe { &(*$PX::ptr()).pin_cnf[$i] }.write(|w| {
395                            w.dir().input()
396                             .input().connect()
397                             .pull().disabled()
398                             .drive().s0s1()
399                             .sense().disabled()
400                        });
401
402                        $PXi {
403                            _mode: PhantomData,
404                        }
405                    }
406                    pub fn into_pulldown_input(self) -> $PXi<Input<PullDown>> {
407                        unsafe { &(*$PX::ptr()).pin_cnf[$i] }.write(|w| {
408                            w.dir().input()
409                             .input().connect()
410                             .pull().pulldown()
411                             .drive().s0s1()
412                             .sense().disabled()
413                        });
414
415                        $PXi {
416                            _mode: PhantomData,
417                        }
418                    }
419                    pub fn into_pullup_input(self) -> $PXi<Input<PullUp>> {
420                        unsafe { &(*$PX::ptr()).pin_cnf[$i] }.write(|w| {
421                            w.dir().input()
422                             .input().connect()
423                             .pull().pullup()
424                             .drive().s0s1()
425                             .sense().disabled()
426                        });
427
428                        $PXi {
429                            _mode: PhantomData,
430                        }
431                    }
432
433                    /// Convert the pin to bepin a push-pull output with normal drive
434                    pub fn into_push_pull_output(self, initial_output: Level)
435                        -> $PXi<Output<PushPull>>
436                    {
437                        let mut pin = $PXi {
438                            _mode: PhantomData,
439                        };
440
441                        match initial_output {
442                            Level::Low  => pin.set_low(),
443                            Level::High => pin.set_high(),
444                        }
445
446                        unsafe { &(*$PX::ptr()).pin_cnf[$i] }.write(|w| {
447                            w.dir().output()
448                             .input().disconnect()
449                             .pull().disabled()
450                             .drive().s0s1()
451                             .sense().disabled()
452                        });
453
454                        pin
455                    }
456
457                    /// Convert the pin to be an open-drain output
458                    ///
459                    /// This method currently does not support configuring an
460                    /// internal pull-up or pull-down resistor.
461                    pub fn into_open_drain_output(self,
462                        config:         OpenDrainConfig,
463                        initial_output: Level,
464                    )
465                        -> $PXi<Output<OpenDrain>>
466                    {
467                        let mut pin = $PXi {
468                            _mode: PhantomData,
469                        };
470
471                        match initial_output {
472                            Level::Low  => pin.set_low(),
473                            Level::High => pin.set_high(),
474                        }
475
476                        // This is safe, as we restrict our access to the
477                        // dedicated register for this pin.
478                        let pin_cnf = unsafe {
479                            &(*$PX::ptr()).pin_cnf[$i]
480                        };
481                        pin_cnf.write(|w| {
482                            w
483                                .dir().output()
484                                .input().disconnect()
485                                .pull().disabled()
486                                .drive().variant(config.variant())
487                                .sense().disabled()
488                        });
489
490                        pin
491                    }
492
493                    /// Degrade to a generic pin struct, which can be used with peripherals
494                    pub fn degrade(self) -> Pin<MODE> {
495                        Pin {
496                            _mode: PhantomData,
497                            #[cfg(feature = "52840")]
498                            port: $port_value,
499                            pin: $i
500                        }
501                    }
502                }
503
504                impl<MODE> InputPin for $PXi<Input<MODE>> {
505                    fn is_high(&self) -> bool {
506                        !self.is_low()
507                    }
508
509                    fn is_low(&self) -> bool {
510                        unsafe { ((*$PX::ptr()).in_.read().bits() & (1 << $i)) == 0 }
511                    }
512                }
513
514                impl<MODE> OutputPin for $PXi<Output<MODE>> {
515                    /// Set the output as high
516                    fn set_high(&mut self) {
517                        // NOTE(unsafe) atomic write to a stateless register - TODO(AJM) verify?
518                        // TODO - I wish I could do something like `.pins$i()`...
519                        unsafe { (*$PX::ptr()).outset.write(|w| w.bits(1u32 << $i)); }
520                    }
521
522                    /// Set the output as low
523                    fn set_low(&mut self) {
524                        // NOTE(unsafe) atomic write to a stateless register - TODO(AJM) verify?
525                        // TODO - I wish I could do something like `.pins$i()`...
526                        unsafe { (*$PX::ptr()).outclr.write(|w| w.bits(1u32 << $i)); }
527                    }
528                }
529
530                impl<MODE> StatefulOutputPin for $PXi<Output<MODE>> {
531                    /// Is the output pin set as high?
532                    fn is_set_high(&self) -> bool {
533                        !self.is_set_low()
534                    }
535
536                    /// Is the output pin set as low?
537                    fn is_set_low(&self) -> bool {
538                        // NOTE(unsafe) atomic read with no side effects - TODO(AJM) verify?
539                        // TODO - I wish I could do something like `.pins$i()`...
540                        unsafe { ((*$PX::ptr()).out.read().bits() & (1 << $i)) == 0 }
541                    }
542                }
543            )+
544        }
545    }
546}
547
548// ===========================================================================
549// Definition of all the items used by the macros above.
550//
551// For now, it is a little repetitive, especially as the nrf52 only has one
552// 32-bit GPIO port (P0)
553// ===========================================================================
554gpio!(P0, p0, p0, false, [
555    P0_00: (p0_00,  0, Input<Floating>),
556    P0_01: (p0_01,  1, Input<Floating>),
557    P0_02: (p0_02,  2, Input<Floating>),
558    P0_03: (p0_03,  3, Input<Floating>),
559    P0_04: (p0_04,  4, Input<Floating>),
560    P0_05: (p0_05,  5, Input<Floating>),
561    P0_06: (p0_06,  6, Input<Floating>),
562    P0_07: (p0_07,  7, Input<Floating>),
563    P0_08: (p0_08,  8, Input<Floating>),
564    P0_09: (p0_09,  9, Input<Floating>),
565    P0_10: (p0_10, 10, Input<Floating>),
566    P0_11: (p0_11, 11, Input<Floating>),
567    P0_12: (p0_12, 12, Input<Floating>),
568    P0_13: (p0_13, 13, Input<Floating>),
569    P0_14: (p0_14, 14, Input<Floating>),
570    P0_15: (p0_15, 15, Input<Floating>),
571    P0_16: (p0_16, 16, Input<Floating>),
572    P0_17: (p0_17, 17, Input<Floating>),
573    P0_18: (p0_18, 18, Input<Floating>),
574    P0_19: (p0_19, 19, Input<Floating>),
575    P0_20: (p0_20, 20, Input<Floating>),
576    P0_21: (p0_21, 21, Input<Floating>),
577    P0_22: (p0_22, 22, Input<Floating>),
578    P0_23: (p0_23, 23, Input<Floating>),
579    P0_24: (p0_24, 24, Input<Floating>),
580    P0_25: (p0_25, 25, Input<Floating>),
581    P0_26: (p0_26, 26, Input<Floating>),
582    P0_27: (p0_27, 27, Input<Floating>),
583    P0_28: (p0_28, 28, Input<Floating>),
584    P0_29: (p0_29, 29, Input<Floating>),
585    P0_30: (p0_30, 30, Input<Floating>),
586    P0_31: (p0_31, 31, Input<Floating>),
587]);
588
589// The p1 types are present in the p0 module generated from the
590// svd, but we want to export them in a p1 module from this crate.
591#[cfg(feature = "52840")]
592gpio!(P1, p0, p1, true, [
593    P1_00: (p1_00,  0, Input<Floating>),
594    P1_01: (p1_01,  1, Input<Floating>),
595    P1_02: (p1_02,  2, Input<Floating>),
596    P1_03: (p1_03,  3, Input<Floating>),
597    P1_04: (p1_04,  4, Input<Floating>),
598    P1_05: (p1_05,  5, Input<Floating>),
599    P1_06: (p1_06,  6, Input<Floating>),
600    P1_07: (p1_07,  7, Input<Floating>),
601    P1_08: (p1_08,  8, Input<Floating>),
602    P1_09: (p1_09,  9, Input<Floating>),
603    P1_10: (p1_10, 10, Input<Floating>),
604    P1_11: (p1_11, 11, Input<Floating>),
605    P1_12: (p1_12, 12, Input<Floating>),
606    P1_13: (p1_13, 13, Input<Floating>),
607    P1_14: (p1_14, 14, Input<Floating>),
608    P1_15: (p1_15, 15, Input<Floating>),
609]);