Skip to content

Commit 3d0f18e

Browse files
authored
Merge pull request #262 from Sh3Rm4n/gpio-interrupt
Gpio interrupt
2 parents 17cbc88 + 35949ff commit 3d0f18e

File tree

5 files changed

+121
-55
lines changed

5 files changed

+121
-55
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
8989
- `clear_events()` was added to clear all events at once.
9090
- `is_event_triggered()` can check if an `Event` is triggered.
9191
- `triggered_events` returns an `EnumSet` of triggered events.
92+
- Change gpio interrupt API to be more in line with the new serial interrupt
93+
API. ([#262])
94+
- Move EXTI interrupt management to SysCfg. ([#262])
95+
- Becuase EXTI interrupt confiugration could cancel out, make it more obvious
96+
in that SysCfg manages the interrupts, not the pin itself.
97+
Change `make_interrupt_source()` to `SysCfg::select_exti_interrupt_source()`.
9298

9399
## [v0.7.0] - 2021-06-18
94100

@@ -414,6 +420,7 @@ let clocks = rcc
414420
[defmt]: https://github.com/knurling-rs/defmt
415421
[filter]: https://defmt.ferrous-systems.com/filtering.html
416422

423+
[#262]: https://github.com/stm32-rs/stm32f3xx-hal/pull/262
417424
[#260]: https://github.com/stm32-rs/stm32f3xx-hal/pull/260
418425
[#259]: https://github.com/stm32-rs/stm32f3xx-hal/pull/259
419426
[#257]: https://github.com/stm32-rs/stm32f3xx-hal/pull/257

examples/gpio_interrupts.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ fn main() -> ! {
4444
let mut user_button = gpioa
4545
.pa0
4646
.into_pull_down_input(&mut gpioa.moder, &mut gpioa.pupdr);
47-
user_button.make_interrupt_source(&mut syscfg);
47+
syscfg.select_exti_interrupt_source(&user_button);
4848
user_button.trigger_on_edge(&mut exti, Edge::Rising);
4949
user_button.enable_interrupt(&mut exti);
5050
let interrupt_num = user_button.nvic(); // hal::pac::Interrupt::EXTI0
@@ -82,6 +82,6 @@ fn EXTI0() {
8282
.borrow_mut()
8383
.as_mut()
8484
.unwrap()
85-
.clear_interrupt_pending_bit();
85+
.clear_interrupt();
8686
})
8787
}

src/gpio.rs

Lines changed: 56 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ use crate::{
6666
hal::digital::v2::OutputPin,
6767
pac::{Interrupt, EXTI},
6868
rcc::AHB,
69-
syscfg::SysCfg,
69+
Toggle,
7070
};
7171

7272
use crate::hal::digital::v2::{toggleable, InputPin, StatefulOutputPin};
@@ -209,6 +209,9 @@ impl private::Gpio for Gpiox {
209209
impl marker::Gpio for Gpiox {}
210210

211211
/// Runtime defined pin number (type state)
212+
// TODO(Sh3Rm4n): If the pin number wouldn't be runtime defined, the implementation for all
213+
// statically defined pins would be much easier (and withless overhead). What could be the
214+
// solution?
212215
pub struct Ux(u8);
213216

214217
impl marker::Index for Ux {
@@ -250,6 +253,8 @@ impl<Otype> marker::Active for Output<Otype> {}
250253
impl<Otype, const AF: u8> marker::Active for Alternate<Otype, AF> {}
251254

252255
/// Slew rate configuration
256+
#[derive(Copy, Clone, PartialEq, Eq)]
257+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
253258
pub enum Speed {
254259
/// Low speed
255260
Low,
@@ -260,6 +265,8 @@ pub enum Speed {
260265
}
261266

262267
/// Internal pull-up and pull-down resistor configuration
268+
#[derive(Copy, Clone, PartialEq, Eq)]
269+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
263270
pub enum Resistor {
264271
/// Floating
265272
Floating,
@@ -270,6 +277,8 @@ pub enum Resistor {
270277
}
271278

272279
/// GPIO interrupt trigger edge selection
280+
#[derive(Copy, Clone, PartialEq, Eq)]
281+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
273282
pub enum Edge {
274283
/// Rising edge of voltage
275284
Rising,
@@ -281,8 +290,8 @@ pub enum Edge {
281290

282291
/// Generic pin
283292
pub struct Pin<Gpio, Index, Mode> {
284-
gpio: Gpio,
285-
index: Index,
293+
pub(crate) gpio: Gpio,
294+
pub(crate) index: Index,
286295
_mode: PhantomData<Mode>,
287296
}
288297

@@ -300,17 +309,6 @@ impl<Gpio, Index, Mode> crate::private::Sealed for Pin<Gpio, Index, Mode> {}
300309
/// [examples/gpio_erased.rs]: https://github.com/stm32-rs/stm32f3xx-hal/blob/v0.7.0/examples/gpio_erased.rs
301310
pub type PXx<Mode> = Pin<Gpiox, Ux, Mode>;
302311

303-
/// Modify specific index of array-like register
304-
macro_rules! modify_at {
305-
($reg:expr, $bitwidth:expr, $index:expr, $value:expr) => {
306-
$reg.modify(|r, w| {
307-
let mask = !(u32::MAX >> (32 - $bitwidth) << ($bitwidth * $index));
308-
let value = $value << ($bitwidth * $index);
309-
w.bits(r.bits() & mask | value)
310-
});
311-
};
312-
}
313-
314312
impl<Gpio, Mode, const X: u8> Pin<Gpio, U<X>, Mode> {
315313
/// Erases the pin number from the type
316314
///
@@ -563,6 +561,11 @@ where
563561
Mode: marker::Active,
564562
{
565563
/// NVIC interrupt number of interrupt from this pin
564+
///
565+
/// Used to unmask / enable the interrupt with [`cortex_m::peripheral::NVIC::unmask()`].
566+
/// This is also useful for all other [`cortex_m::peripheral::NVIC`] functions.
567+
// TODO(Sh3rm4n): It would be cool to have this either const or have a const function.
568+
// But this is currenlty not possible, because index() is runtime defined.
566569
pub fn nvic(&self) -> Interrupt {
567570
match self.index.index() {
568571
0 => Interrupt::EXTI0,
@@ -578,62 +581,68 @@ where
578581
#[cfg(not(feature = "svd-f373"))]
579582
5..=9 => Interrupt::EXTI9_5,
580583
10..=15 => Interrupt::EXTI15_10,
581-
_ => unreachable!(),
584+
_ => crate::unreachable!(),
582585
}
583586
}
584587

585-
/// Make corresponding EXTI line sensitive to this pin
586-
pub fn make_interrupt_source(&mut self, syscfg: &mut SysCfg) {
587-
let bitwidth = 4;
588-
let index = self.index.index() % 4;
589-
let extigpionr = self.gpio.port_index() as u32;
590-
match self.index.index() {
591-
0..=3 => unsafe { modify_at!(syscfg.exticr1, bitwidth, index, extigpionr) },
592-
4..=7 => unsafe { modify_at!(syscfg.exticr2, bitwidth, index, extigpionr) },
593-
8..=11 => unsafe { modify_at!(syscfg.exticr3, bitwidth, index, extigpionr) },
594-
12..=15 => unsafe { modify_at!(syscfg.exticr4, bitwidth, index, extigpionr) },
595-
_ => unreachable!(),
596-
};
597-
}
598-
599588
/// Generate interrupt on rising edge, falling edge, or both
600589
pub fn trigger_on_edge(&mut self, exti: &mut EXTI, edge: Edge) {
601-
let bitwidth = 1;
590+
const BITWIDTH: u8 = 1;
602591
let index = self.index.index();
603592
let (rise, fall) = match edge {
604593
Edge::Rising => (true as u32, false as u32),
605594
Edge::Falling => (false as u32, true as u32),
606595
Edge::RisingFalling => (true as u32, true as u32),
607596
};
597+
// SAFETY: Unguarded write to the register, but behind a &mut
608598
unsafe {
609-
modify_at!(reg_for_cpu!(exti, rtsr), bitwidth, index, rise);
610-
modify_at!(reg_for_cpu!(exti, ftsr), bitwidth, index, fall);
599+
crate::modify_at!(reg_for_cpu!(exti, rtsr), BITWIDTH, index, rise);
600+
crate::modify_at!(reg_for_cpu!(exti, ftsr), BITWIDTH, index, fall);
611601
}
612602
}
613603

604+
/// Configure external interrupts from this pin
605+
///
606+
/// # Note
607+
///
608+
/// Remeber to also configure the interrupt pin on
609+
/// the SysCfg site, with [`crate::syscfg::SysCfg::select_exti_interrupt_source()`]
610+
pub fn configure_interrupt(&mut self, exti: &mut EXTI, enable: impl Into<Toggle>) {
611+
const BITWIDTH: u8 = 1;
612+
613+
let enable: Toggle = enable.into();
614+
let enable: bool = enable.into();
615+
616+
let index = self.index.index();
617+
let value = u32::from(enable);
618+
// SAFETY: Unguarded write to the register, but behind a &mut
619+
unsafe { crate::modify_at!(reg_for_cpu!(exti, imr), BITWIDTH, index, value) };
620+
}
621+
614622
/// Enable external interrupts from this pin
623+
///
624+
/// # Note
625+
///
626+
/// Remeber to also configure the interrupt pin on
627+
/// the SysCfg site, with [`crate::syscfg::SysCfg::select_exti_interrupt_source()`]
615628
pub fn enable_interrupt(&mut self, exti: &mut EXTI) {
616-
let bitwidth = 1;
617-
let index = self.index.index();
618-
let value = 1;
619-
unsafe { modify_at!(reg_for_cpu!(exti, imr), bitwidth, index, value) };
629+
self.configure_interrupt(exti, Toggle::On)
620630
}
621631

622632
/// Disable external interrupts from this pin
623633
pub fn disable_interrupt(&mut self, exti: &mut EXTI) {
624-
let bitwidth = 1;
625-
let index = self.index.index();
626-
let value = 0;
627-
unsafe { modify_at!(reg_for_cpu!(exti, imr), bitwidth, index, value) };
634+
self.configure_interrupt(exti, Toggle::Off)
628635
}
629636

630637
/// Clear the interrupt pending bit for this pin
631-
pub fn clear_interrupt_pending_bit(&mut self) {
638+
pub fn clear_interrupt(&mut self) {
639+
// SAFETY: Atomic write to register without side-effects.
632640
unsafe { reg_for_cpu!((*EXTI::ptr()), pr).write(|w| w.bits(1 << self.index.index())) };
633641
}
634642

635643
/// Reads the interrupt pending bit for this pin
636-
pub fn check_interrupt(&self) -> bool {
644+
pub fn is_interrupt_pending(&self) -> bool {
645+
// SAFETY: Atomic write to register without side-effects.
637646
unsafe { reg_for_cpu!((*EXTI::ptr()), pr).read().bits() & (1 << self.index.index()) != 0 }
638647
}
639648
}
@@ -735,7 +744,7 @@ macro_rules! r_trait {
735744
#[inline]
736745
fn $fn(&mut self, i: u8) {
737746
let value = $gpioy::$xr::$enum::$VARIANT as u32;
738-
unsafe { modify_at!((*$GPIOX::ptr()).$xr, $bitwidth, i, value) };
747+
unsafe { crate::modify_at!((*$GPIOX::ptr()).$xr, $bitwidth, i, value) };
739748
}
740749
)+
741750
}
@@ -875,8 +884,8 @@ macro_rules! gpio {
875884
impl Afr for AFRH {
876885
#[inline]
877886
fn afx(&mut self, i: u8, x: u8) {
878-
let bitwidth = 4;
879-
unsafe { modify_at!((*$GPIOX::ptr()).afrh, bitwidth, i - 8, x as u32) };
887+
const BITWIDTH: u8 = 4;
888+
unsafe { crate::modify_at!((*$GPIOX::ptr()).afrh, BITWIDTH, i - 8, x as u32) };
880889
}
881890
}
882891

@@ -886,8 +895,8 @@ macro_rules! gpio {
886895
impl Afr for AFRL {
887896
#[inline]
888897
fn afx(&mut self, i: u8, x: u8) {
889-
let bitwidth = 4;
890-
unsafe { modify_at!((*$GPIOX::ptr()).afrl, bitwidth, i, x as u32) };
898+
const BITWIDTH: u8 = 4;
899+
unsafe { crate::modify_at!((*$GPIOX::ptr()).afrl, BITWIDTH, i, x as u32) };
891900
}
892901
}
893902

src/lib.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,26 @@ pub use nb::block;
9191

9292
pub use embedded_time as time;
9393

94+
mod private {
95+
/// Private sealed trait to seal all GPIO implementations
96+
/// which do implement peripheral functionalities.
97+
pub trait Sealed {}
98+
99+
/// Modify specific index of array-like register
100+
macro_rules! modify_at {
101+
($reg:expr, $bitwidth:expr, $index:expr, $value:expr) => {
102+
$reg.modify(|r, w| {
103+
let mask = !(u32::MAX >> (32 - $bitwidth) << ($bitwidth * $index));
104+
let value = $value << ($bitwidth * $index);
105+
w.bits(r.bits() & mask | value)
106+
});
107+
};
108+
}
109+
pub(crate) use modify_at;
110+
}
111+
112+
pub(crate) use private::modify_at;
113+
94114
/// Peripheral access
95115
#[cfg(feature = "svd-f301")]
96116
pub use stm32f3::stm32f301 as pac;
@@ -196,12 +216,6 @@ cfg_if! {
196216
}
197217
}
198218

199-
mod private {
200-
/// Private sealed trait to seal all GPIO implementations
201-
/// which do implement peripheral functionalities.
202-
pub trait Sealed {}
203-
}
204-
205219
/// Toggle something on or off.
206220
///
207221
/// Convenience enum and wrapper around a bool, which more explicit about the intention to enable

src/syscfg.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use core::ops::Deref;
44

5+
use crate::gpio::{marker, Pin};
56
use crate::{pac::SYSCFG, rcc::APB2};
67

78
/// Extension trait that constrains the `SYSCFG` peripheral
@@ -39,3 +40,38 @@ impl Deref for SysCfg {
3940
&self.0
4041
}
4142
}
43+
44+
impl SysCfg {
45+
/// Make corresponding EXTI (external interrupt) line sensitive to the selected pin.
46+
///
47+
/// # Note
48+
///
49+
/// Only **one** Pin index of all banks can be activated
50+
/// for interrupts at the same time.
51+
///
52+
/// This means, that only on of `PA1`, `PB1`, `PC1`, ... can be activated.
53+
///
54+
/// For example, if first [`crate::gpio::gpioa::PA1`] and than [`crate::gpio::gpiob::PB1`]
55+
/// would be configured, the former configuration would be overwritten.
56+
///
57+
/// But configuring `PA1` and and `PB2` works!
58+
#[doc(alias = "enable_interrupt")]
59+
pub fn select_exti_interrupt_source<Gpio, Index, Mode>(&mut self, pin: &Pin<Gpio, Index, Mode>)
60+
where
61+
Gpio: marker::Gpio,
62+
Index: marker::Index,
63+
{
64+
const BITWIDTH: u8 = 4;
65+
let index = pin.index.index() % 4;
66+
let extigpionr = pin.gpio.port_index() as u32;
67+
match pin.index.index() {
68+
// SAFETY: These are all unguarded writes directly to the register,
69+
// without leveraging the safety of stm32f3 generated values.
70+
0..=3 => unsafe { crate::modify_at!(self.exticr1, BITWIDTH, index, extigpionr) },
71+
4..=7 => unsafe { crate::modify_at!(self.exticr2, BITWIDTH, index, extigpionr) },
72+
8..=11 => unsafe { crate::modify_at!(self.exticr3, BITWIDTH, index, extigpionr) },
73+
12..=15 => unsafe { crate::modify_at!(self.exticr4, BITWIDTH, index, extigpionr) },
74+
_ => crate::unreachable!(),
75+
};
76+
}
77+
}

0 commit comments

Comments
 (0)