diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c5e7543..b46ca237 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [Unreleased] + +### Added + +- Added support for `ExtiPin` pin traits + ## [v0.5.0] - 2019-12-03 ### Added diff --git a/Cargo.toml b/Cargo.toml index 4b306602..b9860c47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,9 @@ features = ["stm32f103", "rt", "stm32-usbd"] [[example]] name = "timer-interrupt-rtfm" required-features = ["rt"] +[[example]] +name = "exti" +required-features = ["rt"] [dependencies] cortex-m = "0.6.0" diff --git a/examples/exti.rs b/examples/exti.rs new file mode 100644 index 00000000..43a6eef1 --- /dev/null +++ b/examples/exti.rs @@ -0,0 +1,68 @@ +//! Turns the user LED on +//! +//! Listens for interrupts on the pa7 pin. On any rising or falling edge, toggles +//! the pc13 pin (which is connected to the LED on the blue pill board, hence the `led` name). + +#![no_main] +#![no_std] + +use panic_halt as _; + +use stm32f1xx_hal::{ + prelude::*, + pac +}; +use cortex_m_rt::entry; +use pac::interrupt; +use core::mem::MaybeUninit; +use embedded_hal::digital::v2::OutputPin; +use stm32f1xx_hal::gpio::*; + +// These two are owned by the ISR. main() may only access them during the initialization phase, +// where the interrupt is not yet enabled (i.e. no concurrent accesses can occur). +// After enabling the interrupt, main() may not have any references to these objects any more. +// For the sake of minimalism, we do not use RTFM here, which would be the better way. +static mut LED : MaybeUninit>> = MaybeUninit::uninit(); +static mut EXTI : MaybeUninit>> = MaybeUninit::uninit(); + +#[interrupt] +fn EXTI9_5() { + let led = unsafe { &mut *LED.as_mut_ptr()}; + let exti = unsafe { &mut *EXTI.as_mut_ptr()}; + + if exti.check_interrupt() { + led.toggle(); + + // if we don't clear this bit, the ISR would trigger indefinitely + exti.clear_interrupt_pending_bit(); + } +} + +#[entry] +fn main() -> ! { + // initialization phase + let p = pac::Peripherals::take().unwrap(); + let cp = cortex_m::peripheral::Peripherals::take().unwrap(); + { + // the scope ensures that the exti reference is dropped before the first ISR can be executed. + + let mut rcc = p.RCC.constrain(); + let mut gpioa = p.GPIOA.split(&mut rcc.apb2); + let mut gpioc = p.GPIOC.split(&mut rcc.apb2); + let mut afio = p.AFIO.constrain(&mut rcc.apb2); + + let led = unsafe { &mut *LED.as_mut_ptr()}; + *led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh); + + let exti = unsafe { &mut *EXTI.as_mut_ptr()}; + *exti = gpioa.pa7.into_floating_input(&mut gpioa.crl); + exti.make_interrupt_source(&mut afio); + exti.trigger_on_edge(&p.EXTI, Edge::RISING_FALLING); + exti.enable_interrupt(&p.EXTI); + } // initialization ends here + + let mut nvic = cp.NVIC; + nvic.enable(pac::Interrupt::EXTI9_5); + + loop {} +} diff --git a/src/gpio.rs b/src/gpio.rs index 7207f8a3..b80435a5 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -17,6 +17,8 @@ use core::marker::PhantomData; use crate::rcc::APB2; +use crate::stm32::EXTI; +use crate::afio; /// Extension trait to split a GPIO peripheral in independent pins and registers pub trait GpioExt { @@ -75,9 +77,26 @@ pub enum State { Low, } +#[derive(Debug, PartialEq)] +pub enum Edge { + RISING, + FALLING, + RISING_FALLING, +} + +/// External Interrupt Pin +pub trait ExtiPin { + fn make_interrupt_source(&mut self, afio: &mut afio::Parts); + fn trigger_on_edge(&mut self, exti: &EXTI, level: Edge); + fn enable_interrupt(&mut self, exti: &EXTI); + fn disable_interrupt(&mut self, exti: &EXTI); + fn clear_interrupt_pending_bit(&mut self); + fn check_interrupt(&mut self) -> bool; +} + macro_rules! gpio { - ($GPIOX:ident, $gpiox:ident, $gpioy:ident, $PXx:ident, [ - $($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty, $CR:ident),)+ + ($GPIOX:ident, $gpiox:ident, $gpioy:ident, $PXx:ident, $extigpionr:expr, [ + $($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty, $CR:ident, $exticri:ident),)+ ]) => { /// GPIO pub mod $gpiox { @@ -86,6 +105,8 @@ macro_rules! gpio { use crate::hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, toggleable}; use crate::pac::{$gpioy, $GPIOX}; + use crate::stm32::EXTI; + use crate::afio; use crate::rcc::{APB2, Enable, Reset}; use super::{ @@ -101,6 +122,8 @@ macro_rules! gpio { Debugger, Pxx, Mode, + Edge, + ExtiPin }; /// GPIO parts @@ -193,6 +216,74 @@ macro_rules! gpio { } } + impl ExtiPin for Generic> { + /// Make corresponding EXTI line sensitive to this pin + fn make_interrupt_source(&mut self, afio: &mut afio::Parts) { + let offset = 4 * (self.i % 4); + match self.i { + 0..=3 => { + afio.exticr1.exticr1().modify(|r, w| unsafe { + w.bits((r.bits() & !(0xf << offset)) | ($extigpionr << offset)) + }); + }, + 4..=7 => { + afio.exticr2.exticr2().modify(|r, w| unsafe { + w.bits((r.bits() & !(0xf << offset)) | ($extigpionr << offset)) + }); + }, + 8..=11 => { + afio.exticr3.exticr3().modify(|r, w| unsafe { + w.bits((r.bits() & !(0xf << offset)) | ($extigpionr << offset)) + }); + }, + 12..=15 => { + afio.exticr4.exticr4().modify(|r, w| unsafe { + w.bits((r.bits() & !(0xf << offset)) | ($extigpionr << offset)) + }); + }, + _ => unreachable!(), + } + } + + /// Generate interrupt on rising edge, falling edge or both + fn trigger_on_edge(&mut self, exti: &EXTI, edge: Edge) { + match edge { + Edge::RISING => { + exti.rtsr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.i)) }); + exti.ftsr.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.i)) }); + }, + Edge::FALLING => { + exti.ftsr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.i)) }); + exti.rtsr.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.i)) }); + }, + Edge::RISING_FALLING => { + exti.rtsr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.i)) }); + exti.ftsr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.i)) }); + } + } + } + + /// Enable external interrupts from this pin. + fn enable_interrupt(&mut self, exti: &EXTI) { + exti.imr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.i)) }); + } + + /// Disable external interrupts from this pin + fn disable_interrupt(&mut self, exti: &EXTI) { + exti.imr.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.i)) }); + } + + /// Clear the interrupt pending bit for this pin + fn clear_interrupt_pending_bit(&mut self) { + unsafe { (*EXTI::ptr()).pr.write(|w| w.bits(1 << self.i) ) }; + } + + /// Reads the interrupt pending bit for this pin + fn check_interrupt(&mut self) -> bool { + unsafe { ((*EXTI::ptr()).pr.read().bits() & (1 << self.i)) != 0 } + } + } + impl StatefulOutputPin for Generic> { fn is_set_high(&self) -> Result { self.is_set_low().map(|b| !b) @@ -527,6 +618,56 @@ macro_rules! gpio { Ok(unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << $i) == 0 }) } } + + impl ExtiPin for $PXi> { + /// Configure EXTI Line $i to trigger from this pin. + fn make_interrupt_source(&mut self, afio: &mut afio::Parts) { + let offset = 4 * ($i % 4); + afio.$exticri.$exticri().modify(|r, w| unsafe { + let mut exticr = r.bits(); + exticr = (exticr & !(0xf << offset)) | ($extigpionr << offset); + w.bits(exticr) + }); + } + + /// Generate interrupt on rising edge, falling edge or both + fn trigger_on_edge(&mut self, exti: &EXTI, edge: Edge) { + match edge { + Edge::RISING => { + exti.rtsr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) }); + exti.ftsr.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << $i)) }); + }, + Edge::FALLING => { + exti.ftsr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) }); + exti.rtsr.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << $i)) }); + }, + Edge::RISING_FALLING => { + exti.rtsr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) }); + exti.ftsr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) }); + } + } + } + + /// Enable external interrupts from this pin. + fn enable_interrupt(&mut self, exti: &EXTI) { + exti.imr.modify(|r, w| unsafe { w.bits(r.bits() | (1 << $i)) }); + } + + /// Disable external interrupts from this pin + fn disable_interrupt(&mut self, exti: &EXTI) { + exti.imr.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << $i)) }); + } + + /// Clear the interrupt pending bit for this pin + fn clear_interrupt_pending_bit(&mut self) { + unsafe { (*EXTI::ptr()).pr.write(|w| w.bits(1 << $i) ) }; + } + + /// Reads the interrupt pending bit for this pin + fn check_interrupt(&mut self) -> bool { + unsafe { ((*EXTI::ptr()).pr.read().bits() & (1 << $i)) != 0 } + } + } )+ } } @@ -586,6 +727,44 @@ macro_rules! impl_pxx { } } } + + impl ExtiPin for Pxx> { + fn make_interrupt_source(&mut self, afio: &mut afio::Parts) { + match self { + $(Pxx::$pin(pin) => pin.make_interrupt_source(afio)),* + } + } + + fn trigger_on_edge(&mut self, exti: &EXTI, level: Edge) { + match self { + $(Pxx::$pin(pin) => pin.trigger_on_edge(exti, level)),* + } + } + + fn enable_interrupt(&mut self, exti: &EXTI) { + match self { + $(Pxx::$pin(pin) => pin.enable_interrupt(exti)),* + } + } + + fn disable_interrupt(&mut self, exti: &EXTI) { + match self { + $(Pxx::$pin(pin) => pin.disable_interrupt(exti)),* + } + } + + fn clear_interrupt_pending_bit(&mut self) { + match self { + $(Pxx::$pin(pin) => pin.clear_interrupt_pending_bit()),* + } + } + + fn check_interrupt(&mut self) -> bool { + match self { + $(Pxx::$pin(pin) => pin.check_interrupt()),* + } + } + } } } @@ -597,97 +776,97 @@ impl_pxx!{ (gpioe::PEx) } -gpio!(GPIOA, gpioa, gpioa, PAx, [ - PA0: (pa0, 0, Input, CRL), - PA1: (pa1, 1, Input, CRL), - PA2: (pa2, 2, Input, CRL), - PA3: (pa3, 3, Input, CRL), - PA4: (pa4, 4, Input, CRL), - PA5: (pa5, 5, Input, CRL), - PA6: (pa6, 6, Input, CRL), - PA7: (pa7, 7, Input, CRL), - PA8: (pa8, 8, Input, CRH), - PA9: (pa9, 9, Input, CRH), - PA10: (pa10, 10, Input, CRH), - PA11: (pa11, 11, Input, CRH), - PA12: (pa12, 12, Input, CRH), - PA13: (pa13, 13, Debugger, CRH), - PA14: (pa14, 14, Debugger, CRH), - PA15: (pa15, 15, Debugger, CRH), +gpio!(GPIOA, gpioa, gpioa, PAx, 0, [ + PA0: (pa0, 0, Input, CRL, exticr1), + PA1: (pa1, 1, Input, CRL, exticr1), + PA2: (pa2, 2, Input, CRL, exticr1), + PA3: (pa3, 3, Input, CRL, exticr1), + PA4: (pa4, 4, Input, CRL, exticr2), + PA5: (pa5, 5, Input, CRL, exticr2), + PA6: (pa6, 6, Input, CRL, exticr2), + PA7: (pa7, 7, Input, CRL, exticr2), + PA8: (pa8, 8, Input, CRH, exticr3), + PA9: (pa9, 9, Input, CRH, exticr3), + PA10: (pa10, 10, Input, CRH, exticr3), + PA11: (pa11, 11, Input, CRH, exticr3), + PA12: (pa12, 12, Input, CRH, exticr4), + PA13: (pa13, 13, Debugger, CRH, exticr4), + PA14: (pa14, 14, Debugger, CRH, exticr4), + PA15: (pa15, 15, Debugger, CRH, exticr4), ]); -gpio!(GPIOB, gpiob, gpioa, PBx, [ - PB0: (pb0, 0, Input, CRL), - PB1: (pb1, 1, Input, CRL), - PB2: (pb2, 2, Input, CRL), - PB3: (pb3, 3, Debugger, CRL), - PB4: (pb4, 4, Debugger, CRL), - PB5: (pb5, 5, Input, CRL), - PB6: (pb6, 6, Input, CRL), - PB7: (pb7, 7, Input, CRL), - PB8: (pb8, 8, Input, CRH), - PB9: (pb9, 9, Input, CRH), - PB10: (pb10, 10, Input, CRH), - PB11: (pb11, 11, Input, CRH), - PB12: (pb12, 12, Input, CRH), - PB13: (pb13, 13, Input, CRH), - PB14: (pb14, 14, Input, CRH), - PB15: (pb15, 15, Input, CRH), +gpio!(GPIOB, gpiob, gpioa, PBx, 1, [ + PB0: (pb0, 0, Input, CRL, exticr1), + PB1: (pb1, 1, Input, CRL, exticr1), + PB2: (pb2, 2, Input, CRL, exticr1), + PB3: (pb3, 3, Debugger, CRL, exticr1), + PB4: (pb4, 4, Debugger, CRL, exticr2), + PB5: (pb5, 5, Input, CRL, exticr2), + PB6: (pb6, 6, Input, CRL, exticr2), + PB7: (pb7, 7, Input, CRL, exticr2), + PB8: (pb8, 8, Input, CRH, exticr3), + PB9: (pb9, 9, Input, CRH, exticr3), + PB10: (pb10, 10, Input, CRH, exticr3), + PB11: (pb11, 11, Input, CRH, exticr3), + PB12: (pb12, 12, Input, CRH, exticr4), + PB13: (pb13, 13, Input, CRH, exticr4), + PB14: (pb14, 14, Input, CRH, exticr4), + PB15: (pb15, 15, Input, CRH, exticr4), ]); -gpio!(GPIOC, gpioc, gpioa, PCx, [ - PC0: (pc0, 0, Input, CRL), - PC1: (pc1, 1, Input, CRL), - PC2: (pc2, 2, Input, CRL), - PC3: (pc3, 3, Input, CRL), - PC4: (pc4, 4, Input, CRL), - PC5: (pc5, 5, Input, CRL), - PC6: (pc6, 6, Input, CRL), - PC7: (pc7, 7, Input, CRL), - PC8: (pc8, 8, Input, CRH), - PC9: (pc9, 9, Input, CRH), - PC10: (pc10, 10, Input, CRH), - PC11: (pc11, 11, Input, CRH), - PC12: (pc12, 12, Input, CRH), - PC13: (pc13, 13, Input, CRH), - PC14: (pc14, 14, Input, CRH), - PC15: (pc15, 15, Input, CRH), +gpio!(GPIOC, gpioc, gpioa, PCx, 2, [ + PC0: (pc0, 0, Input, CRL, exticr1), + PC1: (pc1, 1, Input, CRL, exticr1), + PC2: (pc2, 2, Input, CRL, exticr1), + PC3: (pc3, 3, Input, CRL, exticr1), + PC4: (pc4, 4, Input, CRL, exticr2), + PC5: (pc5, 5, Input, CRL, exticr2), + PC6: (pc6, 6, Input, CRL, exticr2), + PC7: (pc7, 7, Input, CRL, exticr2), + PC8: (pc8, 8, Input, CRH, exticr3), + PC9: (pc9, 9, Input, CRH, exticr3), + PC10: (pc10, 10, Input, CRH, exticr3), + PC11: (pc11, 11, Input, CRH, exticr3), + PC12: (pc12, 12, Input, CRH, exticr4), + PC13: (pc13, 13, Input, CRH, exticr4), + PC14: (pc14, 14, Input, CRH, exticr4), + PC15: (pc15, 15, Input, CRH, exticr4), ]); -gpio!(GPIOD, gpiod, gpioa, PDx, [ - PD0: (pd0, 0, Input, CRL), - PD1: (pd1, 1, Input, CRL), - PD2: (pd2, 2, Input, CRL), - PD3: (pd3, 3, Input, CRL), - PD4: (pd4, 4, Input, CRL), - PD5: (pd5, 5, Input, CRL), - PD6: (pd6, 6, Input, CRL), - PD7: (pd7, 7, Input, CRL), - PD8: (pd8, 8, Input, CRH), - PD9: (pd9, 9, Input, CRH), - PD10: (pd10, 10, Input, CRH), - PD11: (pd11, 11, Input, CRH), - PD12: (pd12, 12, Input, CRH), - PD13: (pd13, 13, Input, CRH), - PD14: (pd14, 14, Input, CRH), - PD15: (pd15, 15, Input, CRH), +gpio!(GPIOD, gpiod, gpioa, PDx, 3, [ + PD0: (pd0, 0, Input, CRL, exticr1), + PD1: (pd1, 1, Input, CRL, exticr1), + PD2: (pd2, 2, Input, CRL, exticr1), + PD3: (pd3, 3, Input, CRL, exticr1), + PD4: (pd4, 4, Input, CRL, exticr2), + PD5: (pd5, 5, Input, CRL, exticr2), + PD6: (pd6, 6, Input, CRL, exticr2), + PD7: (pd7, 7, Input, CRL, exticr2), + PD8: (pd8, 8, Input, CRH, exticr3), + PD9: (pd9, 9, Input, CRH, exticr3), + PD10: (pd10, 10, Input, CRH, exticr3), + PD11: (pd11, 11, Input, CRH, exticr3), + PD12: (pd12, 12, Input, CRH, exticr4), + PD13: (pd13, 13, Input, CRH, exticr4), + PD14: (pd14, 14, Input, CRH, exticr4), + PD15: (pd15, 15, Input, CRH, exticr4), ]); -gpio!(GPIOE, gpioe, gpioa, PEx, [ - PE0: (pe0, 0, Input, CRL), - PE1: (pe1, 1, Input, CRL), - PE2: (pe2, 2, Input, CRL), - PE3: (pe3, 3, Input, CRL), - PE4: (pe4, 4, Input, CRL), - PE5: (pe5, 5, Input, CRL), - PE6: (pe6, 6, Input, CRL), - PE7: (pe7, 7, Input, CRL), - PE8: (pe8, 8, Input, CRH), - PE9: (pe9, 9, Input, CRH), - PE10: (pe10, 10, Input, CRH), - PE11: (pe11, 11, Input, CRH), - PE12: (pe12, 12, Input, CRH), - PE13: (pe13, 13, Input, CRH), - PE14: (pe14, 14, Input, CRH), - PE15: (pe15, 15, Input, CRH), +gpio!(GPIOE, gpioe, gpioa, PEx, 4, [ + PE0: (pe0, 0, Input, CRL, exticr1), + PE1: (pe1, 1, Input, CRL, exticr1), + PE2: (pe2, 2, Input, CRL, exticr1), + PE3: (pe3, 3, Input, CRL, exticr1), + PE4: (pe4, 4, Input, CRL, exticr2), + PE5: (pe5, 5, Input, CRL, exticr2), + PE6: (pe6, 6, Input, CRL, exticr2), + PE7: (pe7, 7, Input, CRL, exticr2), + PE8: (pe8, 8, Input, CRH, exticr3), + PE9: (pe9, 9, Input, CRH, exticr3), + PE10: (pe10, 10, Input, CRH, exticr3), + PE11: (pe11, 11, Input, CRH, exticr3), + PE12: (pe12, 12, Input, CRH, exticr4), + PE13: (pe13, 13, Input, CRH, exticr4), + PE14: (pe14, 14, Input, CRH, exticr4), + PE15: (pe15, 15, Input, CRH, exticr4), ]);