nrf5x/gpio.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! GPIO and GPIOTE (task and events), nRF5x-family
6//!
7//! ### Author
8//! * Philip Levis <pal@cs.stanford.edu>
9//! * Date: August 18, 2016
10
11use core::ops::{Index, IndexMut};
12use enum_primitive::cast::FromPrimitive;
13use enum_primitive::enum_from_primitive;
14use kernel::debug;
15use kernel::hil;
16use kernel::utilities::cells::OptionalCell;
17use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
18use kernel::utilities::registers::{register_bitfields, ReadWrite};
19use kernel::utilities::StaticRef;
20
21#[cfg(feature = "nrf51")]
22const NUM_GPIOTE: usize = 4;
23#[cfg(feature = "nrf52")]
24const NUM_GPIOTE: usize = 8;
25// Dummy value for testing on Travis-CI.
26#[cfg(all(
27 not(all(target_arch = "arm", target_os = "none")),
28 not(feature = "nrf51"),
29 not(feature = "nrf52"),
30))]
31const NUM_GPIOTE: usize = 4;
32
33const GPIO_PER_PORT: usize = 32;
34
35const GPIOTE_BASE: StaticRef<GpioteRegisters> =
36 unsafe { StaticRef::new(0x40006000 as *const GpioteRegisters) };
37
38const GPIO_BASE_ADDRESS: usize = 0x50000000;
39const GPIO_SIZE: usize = 0x300;
40
41/// The nRF5x doesn't automatically provide GPIO interrupts. Instead, to receive
42/// interrupts from a GPIO line, you must allocate a GPIOTE (GPIO Task and
43/// Event) channel, and bind the channel to the desired pin. There are 4
44/// channels for the nrf51 and 8 channels for the nrf52. This means that
45/// requesting an interrupt can fail, if they are all already allocated.
46#[repr(C)]
47struct GpioteRegisters {
48 /// Task for writing to pin specified in CONFIG\[n\].PSEL.
49 /// Action on pin is configured in CONFIG\[n\].POLARITY
50 ///
51 /// - Address: 0x000 - 0x010 (nRF51)
52 /// - Address: 0x000 - 0x020 (nRF52)
53 task_out: [ReadWrite<u32, TasksOut::Register>; NUM_GPIOTE],
54 /// Reserved
55 // task_set and task_clear are not used on nRF52
56 _reserved0: [u8; 0x100 - (0x0 + NUM_GPIOTE * 4)],
57 /// Event generated from pin specified in CONFIG\[n\].PSEL
58 ///
59 /// - Address: 0x100 - 0x110 (nRF51)
60 /// - Address: 0x100 - 0x120 (nRF52)
61 event_in: [ReadWrite<u32, EventsIn::Register>; NUM_GPIOTE],
62 /// Reserved
63 _reserved1: [u8; 0x17C - (0x100 + NUM_GPIOTE * 4)],
64 /// Event generated from multiple input GPIO pins
65 /// - Address: 0x17C - 0x180
66 event_port: ReadWrite<u32, EventsPort::Register>,
67 /// Reserved
68 // inten on nRF51 is ignored because intenset and intenclr provides the same functionality
69 _reserved2: [u8; 0x184],
70 /// Enable interrupt
71 /// - Address: 0x304 - 0x308
72 intenset: ReadWrite<u32, Intenset::Register>,
73 /// Disable interrupt
74 /// - Address: 0x308 - 0x30C
75 intenclr: ReadWrite<u32, Intenclr::Register>,
76 /// Reserved
77 _reserved3: [u8; 0x204],
78 /// Configuration for OUT\[n\], SET\[n\] and CLR\[n\] tasks and IN\[n\] event
79 ///
80 /// - Adress: 0x510 - 0x520 (nRF51)
81 /// - Adress: 0x510 - 0x530 (nRF52)
82 // Note, only IN\[n\] and OUT\[n\] are used in Tock
83 config: [ReadWrite<u32, Config::Register>; NUM_GPIOTE],
84}
85
86#[repr(C)]
87struct GpioRegisters {
88 /// Reserved
89 _reserved1: [u32; 321],
90 /// Write GPIO port
91 /// - Address: 0x504 - 0x508
92 out: ReadWrite<u32, Out::Register>,
93 /// Set individual bits in GPIO port
94 /// - Address: 0x508 - 0x50C
95 outset: ReadWrite<u32, OutSet::Register>,
96 /// Clear individual bits in GPIO port
97 /// - Address: 0x50C - 0x510
98 outclr: ReadWrite<u32, OutClr::Register>,
99 /// Read GPIO Port
100 /// - Address: 0x510 - 0x514
101 in_: ReadWrite<u32, In::Register>,
102 /// Direction of GPIO pins
103 /// - Address: 0x514 - 0x518
104 dir: ReadWrite<u32, Dir::Register>,
105 /// DIR set register
106 /// - Address: 0x518 - 0x51C
107 dirset: ReadWrite<u32, DirSet::Register>,
108 /// DIR clear register
109 /// - Address: 0x51C - 0x520
110 dirclr: ReadWrite<u32, DirClr::Register>,
111 #[cfg(feature = "nrf51")]
112 /// Reserved
113 _reserved2: [u32; 120],
114 /// Latch register indicating what GPIO pins that have met the criteria set in the
115 /// PIN_CNF\[n\].SENSE
116 /// - Address: 0x520 - 0x524
117 #[cfg(feature = "nrf52")]
118 latch: ReadWrite<u32, Latch::Register>,
119 /// Select between default DETECT signal behaviour and LDETECT mode
120 /// - Address: 0x524 - 0x528
121 #[cfg(feature = "nrf52")]
122 detect_mode: ReadWrite<u32, DetectMode::Register>,
123 /// Reserved
124 #[cfg(feature = "nrf52")]
125 _reserved2: [u32; 118],
126 /// Configuration of GPIO pins
127 pin_cnf: [ReadWrite<u32, PinConfig::Register>; 32],
128}
129
130// Gpio
131register_bitfields! [u32,
132 /// Write GPIO port
133 Out [
134 /// Pin\[n\], each bit correspond to a pin 0 to 31
135 /// 0 - Low, Pin driver is low
136 /// 1 - High, Pin driver is high
137 PIN OFFSET(0) NUMBITS(32)
138 ],
139 /// Set individual bits in GPIO port
140 OutSet [
141 /// Pin\[n\], each bit correspond to a pin 0 to 31
142 /// 0 - Low
143 /// 1 - High
144 /// Writing a '1' sets the pin high
145 /// Writing a '0' has no effect
146 PIN OFFSET(0) NUMBITS(32)
147 ],
148 /// Clear individual bits in GPIO port
149 OutClr [
150 /// Pin\[n\], each bit correspond to a pin 0 to 31
151 /// 0 - Low
152 /// 1 - High
153 /// Writing a '1' sets the pin low
154 /// Writing a '0' has no effect
155 PIN OFFSET(0) NUMBITS(32)
156 ],
157 /// Read GPIO port
158 In [
159 /// Pin\[n\], each bit correspond to a pin 0 to 31
160 /// 0 - Low
161 /// 1 - High
162 PIN OFFSET(0) NUMBITS(32)
163 ],
164 /// Direction of GPIO pins
165 Dir [
166 /// 0 - Pin set as input
167 /// 1 - Pin set as output
168 PIN OFFSET(0) NUMBITS(32)
169 ],
170 /// Configure direction of individual GPIO pins as output
171 DirSet [
172 /// Pin\[n\], each bit correspond to a pin 0 to 31
173 /// 0 - Pin set as input
174 /// 1 - Pin set as output
175 /// Write: writing a '1' sets pin to output
176 /// Writing a '0' has no effect
177 PIN OFFSET(0) NUMBITS(32)
178 ],
179 /// Configure direction of individual GPIO pins as input
180 DirClr [
181 /// Pin\[n\], each bit correspond to a pin 0 to 31
182 /// 0 - Pin set as input
183 /// 1 - Pin set as output
184 /// Write: writing a '1' sets pin to input
185 /// Writing a '0' has no effect
186 PIN OFFSET(0) NUMBITS(32)
187 ],
188 /// Latch register indicating what GPIO pins that have met the criteria set in the
189 /// PIN_CNF\[n\].SENSE registers
190 Latch [
191 /// Pin\[n\], each bit correspond to a pin 0 to 31
192 /// 0 - NotLatched
193 /// 1 - Latched
194 PIN OFFSET(0) NUMBITS(32)
195 ],
196 /// Select between default DETECT signal behaviour and LDETECT mode
197 DetectMode [
198 /// 0 - NotLatched
199 /// 1 - Latched
200 DETECTMODE OFFSET(0) NUMBITS(1) [
201 DEFAULT = 0,
202 LDDETECT = 1
203 ]
204 ],
205 /// Configuration of GPIO pins
206 /// Pin\[n\], each bit correspond to a pin 0 to 31
207 PinConfig [
208 /// Pin direction. Same physical register as DIR register
209 DIR OFFSET(0) NUMBITS(1) [
210 Input = 0,
211 Output = 1
212 ],
213 /// Connect or disconnect input buffer
214 INPUT OFFSET(1) NUMBITS(1) [
215 Connect = 0,
216 Disconnect = 1
217 ],
218 /// Pull configuration
219 PULL OFFSET(2) NUMBITS(2) [
220 Disabled = 0,
221 Pulldown = 1,
222 Pullup = 3
223 ],
224 /// Drive configuration
225 DRIVE OFFSET(8) NUMBITS(3) [
226 /// Standard '0', standard '1'
227 S0S1 = 0,
228 /// High drive '0', standard '1'
229 H0S1 = 1,
230 /// Standard '0', high drive '1
231 S0H1 = 2,
232 /// High drive '0', high 'drive '1'
233 H0H1 = 3,
234 /// Disconnect '0' standard '1' (normally used for wired-or connections)
235 D0S1 = 4,
236 /// Disconnect '0', high drive '1' (normally used for wired-or connections)
237 D0H1 = 5,
238 /// Standard '0'. disconnect '1' (normally used for wired-and connections)
239 S0D1 = 6,
240 /// High drive '0', disconnect '1' (normally used for wired-and connections)
241 H0D1 = 7
242 ],
243 /// Pin sensing mechanism
244 SENSE OFFSET(16) NUMBITS(2) [
245 /// Disabled
246 Disabled = 0,
247 /// Sense for high level
248 High = 2,
249 /// Sense for low level
250 Low = 3
251 ]
252 ]
253];
254
255// GpioTe
256register_bitfields! [u32,
257 /// Task for writing to pin specified in CONFIG\[n\].PSEL.
258 /// Action on pin is configured in CONFIG\[n\].POLARITY
259 TasksOut [
260 TASK OFFSET(0) NUMBITS(1) [
261 Disable = 0,
262 Enable = 1
263 ]
264 ],
265
266 /// Event generated from pin specified in CONFIG\[n\].PSEL
267 EventsIn [
268 EVENT OFFSET(0) NUMBITS(1) [
269 NotReady = 0,
270 Ready = 1
271 ]
272 ],
273
274 /// Event generated from multiple input pins
275 EventsPort [
276 PINS OFFSET(0) NUMBITS(1) [
277 NotReady = 0,
278 Ready = 1
279 ]
280 ],
281
282 /// Enable interrupt
283 Intenset [
284 // nRF51 has only 4 inputs
285 IN OFFSET(0) NUMBITS(8),
286 PORT OFFSET(31) NUMBITS(1)
287 ],
288
289 /// Disable interrupt
290 Intenclr [
291 // nRF51 has only 4 inputs
292 IN OFFSET(0) NUMBITS(8),
293 PORT OFFSET(31) NUMBITS(1)
294 ],
295
296 /// Configuration for OUT\[n\], SET\[n\] and CLR\[n\] tasks and IN\[n\] event
297 Config [
298 /// Mode
299 MODE OFFSET(0) NUMBITS(2) [
300 /// Disabled. Pin specified by PSEL will not be acquired by the
301 /// GPIOTE module
302 Disabled = 0,
303 /// The pin specified by PSEL will be configured as an input and the
304 /// IN\[n\] event will be generated if operation specified in POLARITY
305 /// occurs on the pin.
306 Event = 1,
307 ///The GPIO specified by PSEL will be configured as an output and
308 /// triggering the SET\[n\], CLR\[n\] or OUT\[n\] task will perform the
309 /// operation specified by POLARITY on the pin. When enabled as a
310 /// task the GPIOTE module will acquire the pin and the pin can no
311 /// longer be written as a regular output pin from the GPIO module.
312 Task = 3
313 ],
314 /// GPIO number associated with SET\[n\], CLR\[n\] and OUT\[n\] tasks
315 /// and IN\[n\] event. Only 5 bits are used but they are followed by 1 bit
316 /// indicating the port. This allows us to abstract the port away as each port
317 /// is defined for 32 pins.
318 PSEL OFFSET(8) NUMBITS(6) [],
319 /// When In task mode: Operation to be performed on output
320 /// when OUT\[n\] task is triggered. When In event mode: Operation
321 /// on input that shall trigger IN\[n\] event
322 POLARITY OFFSET(16) NUMBITS(2) [
323 /// Task mode: No effect on pin from OUT\[n\] task. Event mode: no
324 /// IN\[n\] event generated on pin activity
325 Disabled = 0,
326 /// Task mode: Set pin from OUT\[n\] task. Event mode: Generate
327 /// IN\[n\] event when rising edge on pin
328 LoToHi = 1,
329 /// Task mode: Clear pin from OUT\[n\] task. Event mode: Generate
330 /// IN\[n\] event when falling edge on pin
331 HiToLo = 2,
332 /// Task mode: Toggle pin from OUT\[n\]. Event mode: Generate
333 /// IN\[n\] when any change on pin
334 Toggle = 3
335 ],
336 /// When in task mode: Initial value of the output when the GPIOTE
337 /// channel is configured. When in event mode: No effect
338 OUTINIT OFFSET(20) NUMBITS(1) [
339 /// Task mode: Initial value of pin before task triggering is low
340 Low = 0,
341 /// Task mode: Initial value of pin before task triggering is high
342 High = 1
343 ]
344 ]
345];
346
347enum_from_primitive! {
348 #[derive(Copy, Clone, Debug, PartialEq)]
349 #[rustfmt::skip]
350 pub enum Pin {
351 P0_00, P0_01, P0_02, P0_03, P0_04, P0_05, P0_06, P0_07,
352 P0_08, P0_09, P0_10, P0_11, P0_12, P0_13, P0_14, P0_15,
353 P0_16, P0_17, P0_18, P0_19, P0_20, P0_21, P0_22, P0_23,
354 P0_24, P0_25, P0_26, P0_27, P0_28, P0_29, P0_30, P0_31,
355 // Pins only on nrf52840.
356 P1_00, P1_01, P1_02, P1_03, P1_04, P1_05, P1_06, P1_07,
357 P1_08, P1_09, P1_10, P1_11, P1_12, P1_13, P1_14, P1_15,
358 }
359}
360
361pub struct GPIOPin<'a> {
362 pin: u8,
363 port: u8,
364 client: OptionalCell<&'a dyn hil::gpio::Client>,
365 gpiote_registers: StaticRef<GpioteRegisters>,
366 gpio_registers: StaticRef<GpioRegisters>,
367 allocated_channel: OptionalCell<usize>,
368}
369
370impl<'a> GPIOPin<'a> {
371 pub const fn new(pin: Pin) -> GPIOPin<'a> {
372 GPIOPin {
373 pin: ((pin as usize) % GPIO_PER_PORT) as u8,
374 port: ((pin as usize) / GPIO_PER_PORT) as u8,
375 client: OptionalCell::empty(),
376 gpio_registers: unsafe {
377 StaticRef::new(
378 (GPIO_BASE_ADDRESS + ((pin as usize) / GPIO_PER_PORT) * GPIO_SIZE)
379 as *const GpioRegisters,
380 )
381 },
382 gpiote_registers: GPIOTE_BASE,
383 allocated_channel: OptionalCell::empty(),
384 }
385 }
386
387 pub fn set_high_drive(&self, high_drive: bool) {
388 self.gpio_registers.pin_cnf[self.pin as usize].modify(if high_drive {
389 PinConfig::DRIVE::H0H1
390 } else {
391 PinConfig::DRIVE::S0S1
392 });
393 }
394
395 // This sets the specified pin cfg as per the TRM for i2c pin usage.
396 pub fn set_i2c_pin_cfg(&self) {
397 self.gpio_registers.pin_cnf[self.pin as usize].modify(
398 PinConfig::DIR::Input
399 + PinConfig::INPUT::Disconnect
400 + PinConfig::DRIVE::S0D1
401 + PinConfig::SENSE::Disabled,
402 );
403 }
404}
405
406impl hil::gpio::Configure for GPIOPin<'_> {
407 fn set_floating_state(&self, mode: hil::gpio::FloatingState) {
408 let pin_config = match mode {
409 hil::gpio::FloatingState::PullUp => PinConfig::PULL::Pullup,
410 hil::gpio::FloatingState::PullDown => PinConfig::PULL::Pulldown,
411 hil::gpio::FloatingState::PullNone => PinConfig::PULL::Disabled,
412 };
413 // PIN_CNF also holds the direction and the pin driving mode, settings we don't
414 // want to overwrite!
415 self.gpio_registers.pin_cnf[self.pin as usize].modify(pin_config);
416 }
417
418 fn floating_state(&self) -> hil::gpio::FloatingState {
419 match self.gpio_registers.pin_cnf[self.pin as usize].read_as_enum(PinConfig::PULL) {
420 Some(PinConfig::PULL::Value::Pullup) => hil::gpio::FloatingState::PullUp,
421 Some(PinConfig::PULL::Value::Pulldown) => hil::gpio::FloatingState::PullDown,
422 Some(PinConfig::PULL::Value::Disabled) => hil::gpio::FloatingState::PullNone,
423 None => hil::gpio::FloatingState::PullNone,
424 }
425 }
426
427 fn make_output(&self) -> hil::gpio::Configuration {
428 self.gpio_registers.pin_cnf[self.pin as usize].modify(PinConfig::DIR::Output);
429 hil::gpio::Configuration::Output
430 }
431
432 fn disable_output(&self) -> hil::gpio::Configuration {
433 self.make_input()
434 }
435
436 fn make_input(&self) -> hil::gpio::Configuration {
437 self.gpio_registers.pin_cnf[self.pin as usize]
438 .modify(PinConfig::DIR::Input + PinConfig::INPUT::Connect);
439 hil::gpio::Configuration::Input
440 }
441
442 fn disable_input(&self) -> hil::gpio::Configuration {
443 // GPIOs are either inputs or outputs on this chip. To "disable" input
444 // would cause this pin to start driving, which is likely undesired, so
445 // this function is a no-op.
446 self.configuration()
447 }
448
449 fn configuration(&self) -> hil::gpio::Configuration {
450 let dir = self.gpio_registers.pin_cnf[self.pin as usize].read_as_enum(PinConfig::DIR);
451 let connected =
452 self.gpio_registers.pin_cnf[self.pin as usize].read_as_enum(PinConfig::INPUT);
453 match (dir, connected) {
454 (Some(PinConfig::DIR::Value::Input), Some(PinConfig::INPUT::Value::Connect)) => {
455 hil::gpio::Configuration::Input
456 }
457 (Some(PinConfig::DIR::Value::Input), Some(PinConfig::INPUT::Value::Disconnect)) => {
458 hil::gpio::Configuration::LowPower
459 }
460 (Some(PinConfig::DIR::Value::Output), _) => hil::gpio::Configuration::Output,
461 _ => hil::gpio::Configuration::Other,
462 }
463 }
464
465 fn deactivate_to_low_power(&self) {
466 self.gpio_registers.pin_cnf[self.pin as usize].write(
467 PinConfig::DIR::Input + PinConfig::INPUT::Disconnect + PinConfig::PULL::Disabled,
468 );
469 }
470}
471
472impl hil::gpio::Input for GPIOPin<'_> {
473 fn read(&self) -> bool {
474 self.gpio_registers.in_.get() & (1 << self.pin) != 0
475 }
476}
477
478impl hil::gpio::Output for GPIOPin<'_> {
479 fn set(&self) {
480 self.gpio_registers.outset.set(1 << self.pin);
481 }
482
483 fn clear(&self) {
484 self.gpio_registers.outclr.set(1 << self.pin);
485 }
486
487 fn toggle(&self) -> bool {
488 let result = (1 << self.pin) ^ self.gpio_registers.out.get();
489 self.gpio_registers.out.set(result);
490 result & (1 << self.pin) != 0
491 }
492}
493
494impl<'a> hil::gpio::Interrupt<'a> for GPIOPin<'a> {
495 fn set_client(&self, client: &'a dyn hil::gpio::Client) {
496 self.client.set(client);
497 }
498
499 fn is_pending(&self) -> bool {
500 if let Some(channel) = self.allocated_channel.get() {
501 let ev = &self.gpiote_registers.event_in[channel];
502 ev.any_matching_bits_set(EventsIn::EVENT::Ready)
503 } else {
504 false
505 }
506 }
507
508 fn enable_interrupts(&self, mode: hil::gpio::InterruptEdge) {
509 let channel = if let Some(chan) = self.allocated_channel.get() {
510 // We only support one interrupt mode per pin, despite the
511 // hardware supporting multiple. This is to comply with
512 // expectations in other Tock components, such as the button
513 // driver which re-registers interrupts for a restarted app,
514 // assuming the old ones to be overwritten.
515 chan
516 } else if let Ok(chan) = self.allocate_channel() {
517 // Don't have a channel yet, got a new one:
518 chan
519 } else {
520 debug!("No available GPIOTE interrupt channels");
521 return;
522 };
523
524 // Remember that we have allocated this channel for this pin:
525 self.allocated_channel.set(channel);
526
527 let polarity = match mode {
528 hil::gpio::InterruptEdge::EitherEdge => Config::POLARITY::Toggle,
529 hil::gpio::InterruptEdge::RisingEdge => Config::POLARITY::LoToHi,
530 hil::gpio::InterruptEdge::FallingEdge => Config::POLARITY::HiToLo,
531 };
532 let pin: u32 = (GPIO_PER_PORT as u32 * self.port as u32) + self.pin as u32;
533 self.gpiote_registers.config[channel]
534 .write(Config::MODE::Event + Config::PSEL.val(pin) + polarity);
535 self.gpiote_registers.intenset.set(1 << channel);
536 }
537
538 fn disable_interrupts(&self) {
539 if let Some(channel) = self.allocated_channel.get() {
540 self.gpiote_registers.config[channel]
541 .write(Config::MODE::CLEAR + Config::PSEL::CLEAR + Config::POLARITY::CLEAR);
542 self.gpiote_registers.intenclr.set(1 << channel);
543 self.allocated_channel.clear();
544 }
545 }
546}
547
548impl GPIOPin<'_> {
549 /// Allocate a GPIOTE channel
550 /// If the channel couldn't be allocated return error instead
551 fn allocate_channel(&self) -> Result<usize, ()> {
552 for (i, ch) in self.gpiote_registers.config.iter().enumerate() {
553 if ch.matches_all(Config::MODE::Disabled) {
554 return Ok(i);
555 }
556 }
557 Err(())
558 }
559
560 fn handle_interrupt(&self) {
561 self.client.map(|client| {
562 client.fired();
563 });
564 }
565}
566
567pub struct Port<'a, const N: usize> {
568 pub pins: [GPIOPin<'a>; N],
569}
570
571impl<'a, const N: usize> Index<Pin> for Port<'a, N> {
572 type Output = GPIOPin<'a>;
573
574 fn index(&self, index: Pin) -> &GPIOPin<'a> {
575 &self.pins[index as usize]
576 }
577}
578
579impl<'a, const N: usize> IndexMut<Pin> for Port<'a, N> {
580 fn index_mut(&mut self, index: Pin) -> &mut GPIOPin<'a> {
581 &mut self.pins[index as usize]
582 }
583}
584
585impl<'a, const N: usize> Port<'a, N> {
586 pub const fn new(pins: [GPIOPin<'a>; N]) -> Self {
587 Self { pins }
588 }
589
590 /// GPIOTE interrupt: check each GPIOTE channel, if any has
591 /// fired then trigger its corresponding pin's interrupt handler.
592 pub fn handle_interrupt(&self) {
593 // do this just to get a pointer the memory map
594 // doesn't matter which pin is used because it is the same
595 let pin_registers = self.pins[0].gpiote_registers;
596
597 for (i, ev) in pin_registers.event_in.iter().enumerate() {
598 if ev.any_matching_bits_set(EventsIn::EVENT::Ready) {
599 ev.write(EventsIn::EVENT::NotReady);
600 // Get pin number for the event and `trigger` an interrupt manually on that pin
601 let pin = pin_registers.config[i].read(Config::PSEL) as usize;
602 self.pins[pin].handle_interrupt();
603 }
604 }
605 }
606}