Skip to content

Commit

Permalink
gpio: add EXTI support
Browse files Browse the repository at this point in the history
  • Loading branch information
astapleton committed Oct 8, 2024
1 parent 526ceba commit ad94594
Show file tree
Hide file tree
Showing 2 changed files with 166 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
//! ownership reasons, you can use the closure based `with_<mode>` functions to temporarily change the pin type, do
//! some output or input, and then have it change back once done.

mod exti;
mod gpio_def;

use core::{fmt, marker::PhantomData};
Expand All @@ -52,6 +53,7 @@ pub use embedded_hal::digital::PinState;

use crate::rcc::ResetEnable;

pub use exti::ExtiPin;
pub use gpio_def::*;

/// A filler pin type
Expand Down Expand Up @@ -152,7 +154,6 @@ pub type Debugger = Alternate<0, PushPull>;

mod marker {
/// Marker trait that show if `ExtiPin` can be implemented
#[allow(dead_code)] // TODO: Remove when EXTI is implemented
pub trait Interruptable {}
/// Marker trait for readable pin modes
pub trait Readable {}
Expand Down
164 changes: 164 additions & 0 deletions src/gpio/exti.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
use super::{marker, Edge, Pin, PinExt};
use crate::pac::{Interrupt, EXTI};

impl<const P: char, const N: u8, MODE> Pin<P, N, MODE> {
/// NVIC interrupt number of interrupt from this pin
///
/// Used to unmask / enable the interrupt with [`cortex_m::peripheral::NVIC::unmask()`].
/// This is also useful for all other [`cortex_m::peripheral::NVIC`] functions.
pub const fn interrupt(&self) -> Interrupt {
match N {
0 => Interrupt::EXTI0,
1 => Interrupt::EXTI1,
2 => Interrupt::EXTI2,
3 => Interrupt::EXTI3,
4 => Interrupt::EXTI4,
5 => Interrupt::EXTI5,
6 => Interrupt::EXTI6,
7 => Interrupt::EXTI7,
8 => Interrupt::EXTI8,
9 => Interrupt::EXTI9,
10 => Interrupt::EXTI10,
11 => Interrupt::EXTI11,
12 => Interrupt::EXTI12,
13 => Interrupt::EXTI13,
14 => Interrupt::EXTI14,
15 => Interrupt::EXTI15,
_ => panic!("Unsupported pin number"),
}
}
}

/// External Interrupt Pin
pub trait ExtiPin {
fn make_interrupt_source(&mut self, exti: &mut EXTI);
fn trigger_on_edge(&mut self, exti: &mut EXTI, level: Edge);
fn enable_event(&mut self, exti: &mut EXTI);
fn disable_event(&mut self, exti: &mut EXTI);
fn enable_interrupt(&mut self, exti: &mut EXTI);
fn disable_interrupt(&mut self, exti: &mut EXTI);
fn clear_interrupt_pending_bit(&mut self, edge: Edge);
fn check_interrupt(&self, edge: Edge) -> bool;
}

impl<PIN> ExtiPin for PIN
where
PIN: PinExt,
PIN::Mode: marker::Interruptable,
{
/// Make corresponding EXTI line sensitive to this pin
#[inline(always)]
fn make_interrupt_source(&mut self, exti: &mut EXTI) {
let i = self.pin_id();
let port = self.port_id() as u32;
let offset = 8 * (i % 4);
match i {
0..=3 => {
exti.exticr1().modify(|r, w| unsafe {
w.bits((r.bits() & !(0xf << offset)) | (port << offset))
});
}
4..=7 => {
exti.exticr2().modify(|r, w| unsafe {
w.bits((r.bits() & !(0xf << offset)) | (port << offset))
});
}
8..=11 => {
exti.exticr3().modify(|r, w| unsafe {
w.bits((r.bits() & !(0xf << offset)) | (port << offset))
});
}
12..=15 => {
exti.exticr4().modify(|r, w| unsafe {
w.bits((r.bits() & !(0xf << offset)) | (port << offset))
});
}
_ => unreachable!(),
}
}

/// Generate interrupt on rising edge, falling edge or both
#[inline(always)]
fn trigger_on_edge(&mut self, exti: &mut EXTI, edge: Edge) {
let i = self.pin_id();
match edge {
Edge::Rising => {
exti.rtsr1()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << i)) });
exti.ftsr1()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << i)) });
}
Edge::Falling => {
exti.ftsr1()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << i)) });
exti.rtsr1()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << i)) });
}
Edge::RisingFalling => {
exti.rtsr1()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << i)) });
exti.ftsr1()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << i)) });
}
}
}

/// Enable external interrupts from this pin.
#[inline(always)]
fn enable_event(&mut self, exti: &mut EXTI) {
exti.emr1()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.pin_id())) });
}

/// Disable external interrupts from this pin
#[inline(always)]
fn disable_event(&mut self, exti: &mut EXTI) {
exti.emr1()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin_id())) });
}

/// Enable external interrupts from this pin.
#[inline(always)]
fn enable_interrupt(&mut self, exti: &mut EXTI) {
exti.imr1()
.modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.pin_id())) });
}

/// Disable external interrupts from this pin
#[inline(always)]
fn disable_interrupt(&mut self, exti: &mut EXTI) {
exti.imr1()
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.pin_id())) });
}

/// Clear the interrupt pending bit for this pin
#[inline(always)]
fn clear_interrupt_pending_bit(&mut self, edge: Edge) {
unsafe {
let exti = &(*EXTI::ptr());

let mask = 1 << self.pin_id();
match edge {
Edge::Rising => exti.rpr1().write(|w| w.bits(mask)),
Edge::Falling => exti.fpr1().write(|w| w.bits(mask)),
_ => panic!("Must choose a rising or falling edge"),
}
}
}

/// Reads the interrupt pending bit for this pin
#[inline(always)]
fn check_interrupt(&self, edge: Edge) -> bool {
unsafe {
let exti = &(*EXTI::ptr());

let bits = match edge {
Edge::Rising => exti.rpr1().read().bits(),
Edge::Falling => exti.fpr1().read().bits(),
_ => panic!("Must choose a rising or falling edge"),
};

bits & (1 << self.pin_id()) != 0
}
}
}

0 comments on commit ad94594

Please sign in to comment.