kea_hal/
port.rs

1//! Port peripheral module.
2//!
3//! This module contains only the IO Filter part of the port peripheral.
4//! The high current driver and pull-up resist peripherals are implemented as
5//! part of the GPIO module.
6
7use crate::pac::PORT;
8use core::marker::PhantomData;
9
10/// Default Divisor for each port
11pub type DefaultDiv = DivNone;
12
13/// No Divisor (Filter runs a busclk, or disabled)
14pub struct DivNone;
15
16/// Use Divisor 1
17pub struct Div1;
18
19/// Use Divisor 2
20pub struct Div2;
21
22/// Use Divisor 3
23pub struct Div3;
24
25/// A struct that represents one of the divisors in the filter module
26pub struct Divisor<Offset, Size> {
27    _offset: PhantomData<Offset>,
28    _size: PhantomData<Size>,
29}
30
31fn set_port_divisor(divisor: u32, offset: usize) {
32    unsafe {
33        let port = &(*PORT::ptr());
34        port.ioflt
35            .modify(|r, w| w.bits((r.bits() & !(0b11 << offset)) | (divisor << offset)));
36    }
37}
38
39macro_rules! port_filter {
40    ([ $($PORT:ident: ($port:ident, $PORTOffset:expr),)+ ],
41     [ $($DIV:ident: ($div:ident, $DIVOffset:expr, $DIVSize:expr), )+ ]) => {
42        /// Input Filter control
43        ///
44        /// per 11.4 of KEA64 ref man, filter acts as lowpass with adjustable
45        /// timing. holds the pin in previous state until new state exists for
46        /// longer than adjustable time. This feature is effectively a lowpass
47        /// filter.
48
49        // @TODO This should probably have some knowledge of the clocks involved
50        // in order to abstract the divisors into a cut-off frequency or
51        // something.
52        // FLTDIV3 -> LPOCLK, FLTDIV2 -> BUSCLK, FLTDIV1 -> BUSCLK
53        pub struct Filter {
54            $(
55                /// Divisor Controller
56                pub $div: $DIV,
57            )+
58            $(
59                /// Filterable Port
60                pub $port: $PORT<DefaultDiv>,
61            )+
62        }
63
64        impl Filter {
65            /// Get the Filter interface.
66            pub fn get() -> Self {
67                Filter {
68                    $(
69                        $div: $DIV {},
70                    )+
71                    $(
72                        $port: $PORT { _div: PhantomData },
73                    )+
74                }
75            }
76        }
77
78        $(
79            /// Divisor Controller
80            pub struct $DIV;
81
82
83            impl $DIV {
84
85                /// Sets the divisor the filtered port.
86                ///
87                /// See the PORT_IOFLT documentation in the manual for values.
88                /// @TODO Use (cutoff) frequency or the actual value of the
89                /// divisor. In either case match it to one of the available
90                /// hardware divisors
91                pub fn set_divisor(self, divisor: u8) {
92                    assert!(divisor & !($DIVSize) == 0);
93                    unsafe {
94                        (*PORT::ptr()).ioflt.modify(|r, w| {
95                            w.bits((r.bits() & !($DIVSize << $DIVOffset)) | ((divisor as u32) << $DIVOffset))
96                        })
97                    }
98                }
99            }
100        )+
101        $(
102            /// A Filterable Port type
103            pub struct $PORT<DIV> {
104                _div: PhantomData<DIV>,
105            }
106
107            impl<DIV> $PORT<DIV> {
108                /// Configure $PORT to use no divisor (or disable filter)
109                pub fn into_no_div(self) -> $PORT<DivNone> {
110                    set_port_divisor(0b00, $PORTOffset);
111                    $PORT {_div: PhantomData}
112                }
113
114                /// Configure $PORT to use Div1
115                pub fn into_div1(self) -> $PORT<Div1> {
116                    set_port_divisor(0b01, $PORTOffset);
117                    $PORT {_div: PhantomData}
118                }
119
120                /// Configure $PORT to use Div2
121                pub fn into_div2(self) -> $PORT<Div2> {
122                    set_port_divisor(0b10, $PORTOffset);
123                    $PORT {_div: PhantomData}
124                }
125
126                /// Configure $PORT to use Div3
127                pub fn into_div3(self) -> $PORT<Div3> {
128                    set_port_divisor(0b11, $PORTOffset);
129                    $PORT {_div: PhantomData}
130                }
131            }
132        )+
133    }
134}
135
136port_filter!([
137    PORTA: (porta, 0),
138    PORTB: (portb, 2),
139    PORTC: (portc, 4),
140    PORTD: (portd, 8),
141    PORTE: (porte, 10),
142    PORTF: (portf, 12),
143    PORTG: (portg, 14),
144], [
145    DIV1: (div1, 29, 7),
146    DIV2: (div2, 26, 7),
147    DIV3: (div3, 24, 3),
148]);