Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,9 @@ embedded-hal = "1.0.0"
defmt = { version = "1.0.0", optional = true }
paste = "1.0.15"
log = { version = "0.4.20", optional = true}
futures-util = { version = "0.3", default-features = false, features = ["async-await-macro"], optional = true}
futures-util = { version = "0.3", default-features = false, features = ["async-await-macro"], optional = true }
stm32-usbd = "0.8.0"
embedded-hal-async = "1.0.0"

[dev-dependencies]
log = { version = "0.4.20"}
Expand All @@ -95,6 +96,7 @@ panic-probe = "0.3.2"
panic-semihosting = "0.6"
usbd-serial = "0.2.2"
usb-device = { version = "0.3.2", features = ["defmt", "log"] }
rtic = { version = "2.2", features = ["thumbv8main-backend"] }

[profile.release]
codegen-units = 1 # better optimizations
Expand All @@ -116,3 +118,7 @@ required-features = ["stm32h503"]
[[example]]
name = "i2c_target_manual_ack"
required-features = ["stm32h503"]

[[example]]
name = "await-pins"
required-features = ["futures"]
60 changes: 60 additions & 0 deletions examples/await-pins.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#![deny(warnings)]
#![no_main]
#![no_std]

mod utilities;
use embedded_hal_async::digital::Wait;
use stm32h5xx_hal::{
gpio::{ExtiExt, ExtiPin},
pac,
prelude::*,
};
use utilities::logger::info;

#[rtic::app(device = pac, dispatchers = [USART1, USART2], peripherals = true)]
mod app {
use stm32h5xx_hal::gpio;

use super::*;

#[shared]
struct Shared {}

#[local]
struct Local {
pin: gpio::PA5<gpio::Input, true>,
}

#[init]
fn init(ctx: init::Context) -> (Shared, Local) {
let dp = ctx.device;

let pwr = dp.PWR.constrain();
let pwrcfg = pwr.vos0().freeze();

// Constrain and Freeze clock
let rcc = dp.RCC.constrain();
let ccdr = rcc.sys_ck(250.MHz()).freeze(pwrcfg, &dp.SBS);

let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA);

let (mut exti, exti_channels) = dp.EXTI.split();

let pin = gpioa
.pa5
.into_input()
.make_interrupt_source(exti_channels.ch5, &mut exti);

(Shared {}, Local { pin })
}

#[task(local = [pin])]
async fn foo(ctx: foo::Context) {
loop {
ctx.local.pin.wait_for_high().await.unwrap();
info!("On");
ctx.local.pin.wait_for_low().await.unwrap();
info!("Off");
}
}
}
17 changes: 13 additions & 4 deletions src/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ mod convert;
mod dynamic;
mod erased;
mod exti;

#[cfg(feature = "futures")]
mod future;
mod gpio_def;
mod hal;
mod partially_erased;
Expand All @@ -71,7 +74,7 @@ pub use dynamic::{Dynamic, DynamicPin};
pub use embedded_hal::digital::PinState;

pub use erased::{EPin, ErasedPin};
pub use exti::ExtiPin;
pub use exti::{ExtiExt, ExtiPin, ExtiedPin};
pub use gpio_def::*;
pub use partially_erased::{PEPin, PartiallyErasedPin};

Expand Down Expand Up @@ -264,10 +267,16 @@ pub type AF15<Otype = PushPull> = Alternate<15, Otype>;
/// - `MODE` is one of the pin modes (see [Modes](crate::gpio#modes) section).
/// - `P` is port name: `A` for GPIOA, `B` for GPIOB, etc.
/// - `N` is pin number: from `0` to `15`.
pub struct Pin<const P: char, const N: u8, MODE = Analog> {
/// - `INT` is true of the pin is configured for EXTI interrupt
pub struct Pin<
const P: char,
const N: u8,
MODE = Analog,
const INT: bool = false,
> {
_mode: PhantomData<MODE>,
}
impl<const P: char, const N: u8, MODE> Pin<P, N, MODE> {
impl<const P: char, const N: u8, MODE, const INT: bool> Pin<P, N, MODE, INT> {
const fn new() -> Self {
Self { _mode: PhantomData }
}
Expand Down Expand Up @@ -412,7 +421,7 @@ impl<const P: char, const N: u8, MODE> From<Pin<P, N, MODE>>
}
}

impl<const P: char, const N: u8, MODE> Pin<P, N, MODE> {
impl<const P: char, const N: u8, MODE, const INT: bool> Pin<P, N, MODE, INT> {
/// Set the output of the pin regardless of its mode.
/// Primarily used to set the output value of the pin
/// before changing its mode to an output to avoid
Expand Down
132 changes: 108 additions & 24 deletions src/gpio/exti.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,72 @@
use core::{marker::PhantomData, ops::Deref};

use super::{marker, Edge, Pin, PinExt};
use crate::pac::{Interrupt, EXTI};
use crate::{
gpio,
pac::{Interrupt, EXTI},
};

pub trait ExtiExt {
fn split(self) -> (Exti, ExtiChannels);
}

impl ExtiExt for EXTI {
fn split(self) -> (Exti, ExtiChannels) {
(
Exti(self),
ExtiChannels {
ch0: ExtiChannel,
ch1: ExtiChannel,
ch2: ExtiChannel,
ch3: ExtiChannel,
ch4: ExtiChannel,
ch5: ExtiChannel,
ch6: ExtiChannel,
ch7: ExtiChannel,
ch8: ExtiChannel,
ch9: ExtiChannel,
ch10: ExtiChannel,
ch11: ExtiChannel,
ch12: ExtiChannel,
ch13: ExtiChannel,
ch14: ExtiChannel,
ch15: ExtiChannel,
},
)
}
}

pub struct Exti(pub(crate) EXTI);

impl Deref for Exti {
type Target = EXTI;

fn deref(&self) -> &Self::Target {
&self.0
}
}

#[non_exhaustive]
pub struct ExtiChannel<const N: u8>;

pub struct ExtiChannels {
pub ch0: ExtiChannel<0>,
pub ch1: ExtiChannel<1>,
pub ch2: ExtiChannel<2>,
pub ch3: ExtiChannel<3>,
pub ch4: ExtiChannel<4>,
pub ch5: ExtiChannel<5>,
pub ch6: ExtiChannel<6>,
pub ch7: ExtiChannel<7>,
pub ch8: ExtiChannel<8>,
pub ch9: ExtiChannel<9>,
pub ch10: ExtiChannel<10>,
pub ch11: ExtiChannel<11>,
pub ch12: ExtiChannel<12>,
pub ch13: ExtiChannel<13>,
pub ch14: ExtiChannel<14>,
pub ch15: ExtiChannel<15>,
}

impl<const P: char, const N: u8, MODE> Pin<P, N, MODE> {
/// NVIC interrupt number of interrupt from this pin
Expand Down Expand Up @@ -30,25 +97,38 @@ impl<const P: char, const N: u8, MODE> Pin<P, N, MODE> {
}

/// 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);
pub trait ExtiPin<const P: char, const N: u8, M> {
fn make_interrupt_source(
self,
_ch: ExtiChannel<N>,
ch: &mut Exti,
) -> Pin<P, N, M, true>;
}

// TODO: Find better name
/// Only available on pins where interrupts have been enabled by the user
pub trait ExtiedPin<const N: u8> {
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
impl<const P: char, const N: u8, M> ExtiPin<P, N, M>
for gpio::Pin<P, N, M, false>
where
PIN: PinExt,
PIN::Mode: marker::Interruptable,
M: marker::Interruptable,
{
/// Make corresponding EXTI line sensitive to this pin
#[inline(always)]
fn make_interrupt_source(&mut self, exti: &mut EXTI) {
fn make_interrupt_source(
self,
_ch: ExtiChannel<N>,
exti: &mut Exti,
) -> Pin<P, N, M, true> {
let i = self.pin_id();
let port = self.port_id() as u32;
let offset = 8 * (i % 4);
Expand All @@ -75,12 +155,16 @@ where
}
_ => unreachable!(),
}

Pin { _mode: PhantomData }
}
}

impl<const P: char, const N: u8, M> ExtiedPin<N> for gpio::Pin<P, N, M, true> {
/// 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();
fn trigger_on_edge(&mut self, exti: &mut Exti, edge: Edge) {
let i = N;
match edge {
Edge::Rising => {
exti.rtsr1()
Expand All @@ -105,30 +189,30 @@ where

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

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

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

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

/// Clear the interrupt pending bit for this pin
Expand All @@ -137,7 +221,7 @@ where
unsafe {
let exti = &(*EXTI::ptr());

let mask = 1 << self.pin_id();
let mask = 1 << N;
match edge {
Edge::Rising => exti.rpr1().write(|w| w.bits(mask)),
Edge::Falling => exti.fpr1().write(|w| w.bits(mask)),
Expand All @@ -158,7 +242,7 @@ where
_ => panic!("Must choose a rising or falling edge"),
};

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