atsamd_hal/gpio/
reg.rs

1use atsamd_hal_macros::{hal_cfg, hal_macro_helper};
2
3#[hal_cfg(any("port-d11", "port-d21"))]
4use crate::pac::port::{
5    Ctrl, Dir, Dirclr, Dirset, Dirtgl, In, Out, Outclr, Outset, Outtgl, Pincfg0_ as Pincfg,
6    Pmux0_ as Pmux, Wrconfig,
7};
8
9#[hal_cfg("port-d5x")]
10use crate::pac::port::group::{
11    Ctrl, Dir, Dirclr, Dirset, Dirtgl, In, Out, Outclr, Outset, Outtgl, Pincfg, Pmux, Wrconfig,
12};
13
14use crate::pac::Port;
15
16use super::dynpin::*;
17
18//==============================================================================
19//  ModeFields
20//==============================================================================
21
22/// Collect all fields needed to set the [`PinMode`](super::PinMode)
23#[derive(Default)]
24struct ModeFields {
25    dir: bool,
26    inen: bool,
27    pullen: bool,
28    out: bool,
29    pmuxen: bool,
30    pmux: u8,
31}
32
33impl From<DynPinMode> for ModeFields {
34    #[inline]
35    #[hal_macro_helper]
36    fn from(mode: DynPinMode) -> Self {
37        let mut fields = Self::default();
38        use DynPinMode::*;
39        match mode {
40            Disabled(config) => {
41                use DynDisabled::*;
42                match config {
43                    Floating => {
44                        fields.pullen = false;
45                        fields.out = false;
46                    }
47                    PullDown => {
48                        fields.pullen = true;
49                        fields.out = false;
50                    }
51                    PullUp => {
52                        fields.pullen = true;
53                        fields.out = true;
54                    }
55                }
56            }
57            Input(config) => {
58                fields.inen = true;
59                use DynInput::*;
60                match config {
61                    Floating => {
62                        fields.pullen = false;
63                        fields.out = false;
64                    }
65                    PullDown => {
66                        fields.pullen = true;
67                        fields.out = false;
68                    }
69                    PullUp => {
70                        fields.pullen = true;
71                        fields.out = true;
72                    }
73                }
74            }
75            Interrupt(config) => {
76                fields.pmuxen = true;
77                fields.pmux = 0;
78                use DynInterrupt::*;
79                match config {
80                    Floating => {
81                        fields.pullen = false;
82                        fields.out = false;
83                    }
84                    PullDown => {
85                        fields.pullen = true;
86                        fields.out = false;
87                    }
88                    PullUp => {
89                        fields.pullen = true;
90                        fields.out = true;
91                    }
92                }
93            }
94            Output(config) => {
95                fields.dir = true;
96                use DynOutput::*;
97                match config {
98                    PushPull => {
99                        fields.inen = false;
100                    }
101                    Readable => {
102                        fields.inen = true;
103                    }
104                }
105            }
106            Alternate(config) => {
107                fields.pmuxen = true;
108                use DynAlternate::*;
109                match config {
110                    B => {
111                        fields.pmux = 1;
112                    }
113                    C => {
114                        fields.pmux = 2;
115                    }
116                    D => {
117                        fields.pmux = 3;
118                    }
119                    E => {
120                        fields.pmux = 4;
121                    }
122                    F => {
123                        fields.pmux = 5;
124                    }
125                    G => {
126                        fields.pmux = 6;
127                    }
128                    #[hal_cfg(any("port-d21", "port-d5x"))]
129                    H => {
130                        fields.pmux = 7;
131                    }
132                    #[hal_cfg("port-d5x")]
133                    I => {
134                        fields.pmux = 8;
135                    }
136                    #[hal_cfg("port-d5x")]
137                    J => {
138                        fields.pmux = 9;
139                    }
140                    #[hal_cfg("port-d5x")]
141                    K => {
142                        fields.pmux = 10;
143                    }
144                    #[hal_cfg("port-d5x")]
145                    L => {
146                        fields.pmux = 11;
147                    }
148                    #[hal_cfg("port-d5x")]
149                    M => {
150                        fields.pmux = 12;
151                    }
152                    #[hal_cfg("port-d5x")]
153                    N => {
154                        fields.pmux = 13;
155                    }
156                }
157            }
158        };
159        fields
160    }
161}
162
163//==============================================================================
164//  GROUP
165//==============================================================================
166
167/// Represent the [`Port`] register block
168///
169/// The SAMx5x PACs have a GROUP type to represent each [`Port`] group, but the
170/// SAMD11 and SAMD21 PACs do not. Manually re-implement it here.
171#[repr(C)]
172#[allow(clippy::upper_case_acronyms)]
173pub(super) struct GROUP {
174    dir: Dir,
175    dirclr: Dirclr,
176    dirset: Dirset,
177    dirtgl: Dirtgl,
178    out: Out,
179    outclr: Outclr,
180    outset: Outset,
181    outtgl: Outtgl,
182    in_: In,
183    ctrl: Ctrl,
184    wrconfig: Wrconfig,
185    _padding1: [u8; 4],
186    pmux: [Pmux; 16],
187    pincfg: [Pincfg; 32],
188    _padding2: [u8; 32],
189}
190
191//==============================================================================
192//  RegisterInterface
193//==============================================================================
194
195/// Provide a safe register interface for pin objects
196///
197/// [`Port`], like every PAC `struct`, is [`Send`] but not [`Sync`], because it
198/// points to a `RegisterBlock` of `VolatileCell`s. Unfortunately, such an
199/// interface is quite restrictive. Instead, it would be ideal if we could split
200/// the [`Port`] into independent pins that are both [`Send`] and [`Sync`].
201///
202/// [`Port`] is a single, zero-sized marker `struct` that provides access to
203/// every [`Port`] register. Instead, we would like to create zero-sized marker
204/// `struct`s for every pin, where each pin is only allowed to control its own
205/// registers. Furthermore, each pin `struct` should be a singleton, so that
206/// exclusive access to the `struct` also guarantees exclusive access to the
207/// corresponding registers. Finally, the pin `struct`s should not have any
208/// interior mutability. Together, these requirements would allow the pin
209/// `struct`s to be both [`Send`] and [`Sync`].
210///
211/// This trait creates a safe API for accomplishing these goals. Implementers
212/// supply a pin ID through the [`id`] function. The remaining functions provide
213/// a safe API for accessing the registers associated with that pin ID. Any
214/// modification of the registers requires `&mut self`, which destroys interior
215/// mutability.
216///
217/// # Safety
218///
219/// Users should only implement the [`id`] function. No default function
220/// implementations should be overridden. The implementing type must also have
221/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
222/// pin ID is a singleton.
223///
224/// [`id`]: Self::id
225pub(super) unsafe trait RegisterInterface {
226    /// Provide a [`DynPinId`] identifying the set of registers controlled by
227    /// this type.
228    fn id(&self) -> DynPinId;
229
230    /// Pointer to the array of [`GROUP`] register blocks
231    const GROUPS: *const GROUP = Port::ptr() as *const _;
232
233    #[inline]
234    #[hal_macro_helper]
235    fn group(&self) -> &GROUP {
236        let offset = match self.id().group {
237            DynGroup::A => 0,
238            #[hal_cfg("pin-group-b")]
239            DynGroup::B => 1,
240            #[hal_cfg("pin-group-c")]
241            DynGroup::C => 2,
242            #[hal_cfg("pin-group-d")]
243            DynGroup::D => 3,
244        };
245        // Safety: It is safe to create shared references to each PAC register
246        // or register block, because all registers are wrapped in
247        // `UnsafeCell`s. We should never create unique references to the
248        // registers, to prevent any risk of UB.
249        unsafe { &*Self::GROUPS.add(offset) }
250    }
251
252    #[inline]
253    fn mask_32(&self) -> u32 {
254        1 << self.id().num
255    }
256
257    #[inline]
258    fn mask_16(&self) -> u16 {
259        1 << (self.id().num & 0xF)
260    }
261
262    #[inline]
263    fn hwsel(&self) -> bool {
264        self.id().num & 0x10 != 0
265    }
266
267    #[inline]
268    fn pincfg(&self) -> &Pincfg {
269        &self.group().pincfg[self.id().num as usize]
270    }
271
272    /// Change the pin mode
273    ///
274    /// We use the Wrconfig register to avoid using the Pmux register. Each Pmux
275    /// register stores the Pmux values for two different pins.  Changing the
276    /// Pmux value for one pin would require a read/modify/write operation that
277    /// could be preempted by the other pin. This is fundamentally unsound. The
278    /// Wrconfig register lets us modify *only* the fields corresponding to this
279    /// particular PinId/DynPinId.
280    #[inline]
281    fn change_mode(&mut self, mode: DynPinMode) {
282        let ModeFields {
283            dir,
284            inen,
285            pullen,
286            out,
287            pmuxen,
288            pmux,
289        } = mode.into();
290        // The bit patterns here are guaranteed to be safe, because they can
291        // ultimately be traced back to associated constants defined on the
292        // `PinId` and `PinMode` traits, which are guaranteed to be correct.
293        self.group().wrconfig.write(|w| unsafe {
294            w.hwsel().bit(self.hwsel());
295            w.wrpincfg().set_bit();
296            w.wrpmux().set_bit();
297            w.pmux().bits(pmux);
298            w.pullen().bit(pullen);
299            w.inen().bit(inen);
300            w.pmuxen().bit(pmuxen);
301            w.pinmask().bits(self.mask_16())
302        });
303        self.set_dir(dir);
304        if pullen {
305            self.write_pin(out)
306        };
307    }
308
309    /// Set the direction of a pin
310    #[inline]
311    fn set_dir(&mut self, bit: bool) {
312        let mask = self.mask_32();
313        // Safety: Dirset & Dirclr are "mask" registers, and we only write the
314        // bit for this pin ID
315        unsafe {
316            if bit {
317                self.group().dirset.write(|w| w.bits(mask));
318            } else {
319                self.group().dirclr.write(|w| w.bits(mask));
320            }
321        }
322    }
323
324    /// Read the logic level of an input put
325    #[inline]
326    #[allow(dead_code)]
327    fn read_pin(&self) -> bool {
328        let mask = self.mask_32();
329        self.group().in_.read().bits() & mask != 0
330    }
331
332    /// Write the logic level of an output pin
333    #[inline]
334    fn write_pin(&mut self, bit: bool) {
335        let mask = self.mask_32();
336        // Safety: Outset & Outclr are "mask" registers, and we only write the
337        // bit for this pin ID
338        unsafe {
339            if bit {
340                self.group().outset.write(|w| w.bits(mask));
341            } else {
342                self.group().outclr.write(|w| w.bits(mask));
343            }
344        }
345    }
346
347    /// Toggle the logic level of an output pin
348    #[inline]
349    fn toggle_pin(&mut self) {
350        let mask = self.mask_32();
351        // Safety: Outtgl is a "mask" register, and we only write the bit for
352        // this pin ID
353        unsafe { self.group().outtgl.write(|w| w.bits(mask)) };
354    }
355
356    /// Read back the logic level of an output pin
357    #[inline]
358    #[allow(dead_code)]
359    fn read_out_pin(&self) -> bool {
360        let mask = self.mask_32();
361        self.group().out.read().bits() & mask != 0
362    }
363
364    /// Read the drive strength of a pin
365    #[inline]
366    fn read_drive_strength(&self) -> bool {
367        self.pincfg().read().drvstr().bit()
368    }
369
370    /// Write the drive strength of a pin
371    #[inline]
372    fn write_drive_strength(&mut self, bit: bool) {
373        self.pincfg().modify(|_, w| w.drvstr().bit(bit));
374    }
375}