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]);