Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async timer interrupts #3616

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
54 changes: 6 additions & 48 deletions embassy-stm32/src/timer/input_capture.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
//! Input capture driver.

use core::future::Future;
use core::marker::PhantomData;
use core::pin::Pin;
use core::task::{Context, Poll};

use embassy_hal_internal::{into_ref, PeripheralRef};

use super::ll_async::TimerEventFuture;
use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer};
use super::{
CaptureCompareInterruptHandler, Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin,
GeneralInstance4Channel,
};
use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel, InterruptHandler};
use crate::gpio::{AfType, AnyPin, Pull};
use crate::interrupt::typelevel::{Binding, Interrupt};
use crate::time::Hertz;
Expand Down Expand Up @@ -68,7 +63,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> {
_ch2: Option<CapturePin<'d, T, Ch2>>,
_ch3: Option<CapturePin<'d, T, Ch3>>,
_ch4: Option<CapturePin<'d, T, Ch4>>,
_irq: impl Binding<T::CaptureCompareInterrupt, CaptureCompareInterruptHandler<T>> + 'd,
_irq: impl Binding<T::UpdateInterrupt, InterruptHandler<T>> + 'd,
freq: Hertz,
counting_mode: CountingMode,
) -> Self {
Expand Down Expand Up @@ -125,20 +120,18 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> {
self.inner.get_input_interrupt(channel)
}

fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> InputCaptureFuture<T> {
fn new_future(&self, channel: Channel, mode: InputCaptureMode, tisel: InputTISelection) -> TimerEventFuture<T> {
// Configuration steps from ST RM0390 (STM32F446) chapter 17.3.5
// or ST RM0008 (STM32F103) chapter 15.3.5 Input capture mode
self.inner.set_input_ti_selection(channel, tisel);
self.inner.set_input_capture_filter(channel, FilterValue::NOFILTER);
self.inner.set_input_capture_mode(channel, mode);
self.inner.set_input_capture_prescaler(channel, 0);
self.inner.enable_channel(channel, true);
self.inner.clear_input_interrupt(channel);
self.inner.enable_input_interrupt(channel, true);

InputCaptureFuture {
channel,
phantom: PhantomData,
}
TimerEventFuture::new(channel.into())
}

/// Asynchronously wait until the pin sees a rising edge.
Expand Down Expand Up @@ -177,38 +170,3 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> {
.await
}
}

#[must_use = "futures do nothing unless you `.await` or poll them"]
struct InputCaptureFuture<T: GeneralInstance4Channel> {
channel: Channel,
phantom: PhantomData<T>,
}

impl<T: GeneralInstance4Channel> Drop for InputCaptureFuture<T> {
fn drop(&mut self) {
critical_section::with(|_| {
let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) };

// disable interrupt enable
regs.dier().modify(|w| w.set_ccie(self.channel.index(), false));
});
}
}

impl<T: GeneralInstance4Channel> Future for InputCaptureFuture<T> {
type Output = u32;

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
T::state().cc_waker[self.channel.index()].register(cx.waker());

let regs = unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) };

let dier = regs.dier().read();
if !dier.ccie(self.channel.index()) {
let val = regs.ccr(self.channel.index()).read().0;
Poll::Ready(val)
} else {
Poll::Pending
}
}
}
95 changes: 95 additions & 0 deletions embassy-stm32/src/timer/ll_async.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//! Low level async timer driver.

use core::future::Future;
use core::marker::PhantomData;
use core::pin::Pin;
use core::task::{Context, Poll};

use super::{Channel, GeneralInstance4Channel};

/// All timer interrupts
#[derive(Clone, Copy)]
pub enum InterruptFlag {
/// Update
Update = 0,
/// Capture/compare 1
CaptureCompare1 = 1,
/// Capture/compare 2
CaptureCompare2 = 2,
/// Capture/compare 3
CaptureCompare3 = 3,
/// Capture/compare 4
CaptureCompare4 = 4,
/// COM event
ComEvent = 5,
/// Trigger
Trigger = 6,
/// Break
Break = 7,
}

/// Timer future
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct TimerEventFuture<T: GeneralInstance4Channel> {
flag: InterruptFlag,
phantom: PhantomData<T>,
}

impl<T: GeneralInstance4Channel> TimerEventFuture<T> {
/// Enable the interrupt source and returns a new instance of Future
pub fn new(flag: InterruptFlag) -> Self {
let this = Self {
flag,
phantom: PhantomData,
};

this
}

fn regs(&self) -> crate::pac::timer::TimGp16 {
unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) }
}
}

impl From<Channel> for InterruptFlag {
fn from(value: Channel) -> Self {
match value {
Channel::Ch1 => InterruptFlag::CaptureCompare1,
Channel::Ch2 => InterruptFlag::CaptureCompare2,
Channel::Ch3 => InterruptFlag::CaptureCompare3,
Channel::Ch4 => InterruptFlag::CaptureCompare4,
}
}
}

impl<T: GeneralInstance4Channel> Drop for TimerEventFuture<T> {
fn drop(&mut self) {
critical_section::with(|_| {
// clear interrupt enable
self.regs().dier().modify(|w| w.0 &= !(1u32 << self.flag as u32));
});
}
}

impl<T: GeneralInstance4Channel> Future for TimerEventFuture<T> {
type Output = u32;

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
T::state().wakers[self.flag as usize].register(cx.waker());

// if interrupt enable is cleared, this means the interrupt handler executed, thus we can return the value
let dier = self.regs().dier().read();
if (dier.0 & (1u32 << self.flag as u32)) == 0 {
let val = match self.flag {
InterruptFlag::CaptureCompare1 => self.regs().ccr(Channel::Ch1.index()).read().0,
InterruptFlag::CaptureCompare2 => self.regs().ccr(Channel::Ch2.index()).read().0,
InterruptFlag::CaptureCompare3 => self.regs().ccr(Channel::Ch3.index()).read().0,
InterruptFlag::CaptureCompare4 => self.regs().ccr(Channel::Ch4.index()).read().0,
_ => self.regs().cnt().read().0, // return the counter value
};
Poll::Ready(val)
} else {
Poll::Pending
}
}
}
55 changes: 19 additions & 36 deletions embassy-stm32/src/timer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use embassy_sync::waitqueue::AtomicWaker;
#[cfg(not(stm32l0))]
pub mod complementary_pwm;
pub mod input_capture;
pub mod ll_async;
pub mod low_level;
pub mod pwm_input;
pub mod qei;
Expand Down Expand Up @@ -53,15 +54,13 @@ pub enum TimerBits {
}

struct State {
up_waker: AtomicWaker,
cc_waker: [AtomicWaker; 4],
wakers: [AtomicWaker; 8],
}

impl State {
const fn new() -> Self {
Self {
up_waker: AtomicWaker::new(),
cc_waker: [const { AtomicWaker::new() }; 4],
wakers: [const { AtomicWaker::new() }; 8],
}
}
}
Expand Down Expand Up @@ -320,12 +319,12 @@ foreach_interrupt! {
};
}

/// Update interrupt handler.
pub struct UpdateInterruptHandler<T: CoreInstance> {
/// Global interrupt handler.
pub struct InterruptHandler<T: CoreInstance> {
_phantom: PhantomData<T>,
}

impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for UpdateInterruptHandler<T> {
impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
#[cfg(feature = "low-power")]
crate::low_power::on_wakeup_irq();
Expand All @@ -335,46 +334,30 @@ impl<T: CoreInstance> interrupt::typelevel::Handler<T::UpdateInterrupt> for Upda
// Read TIM interrupt flags.
let sr = regs.sr().read();

// Mask relevant interrupts (UIE).
let bits = sr.0 & 0x00000001;
// Mask relevant interrupts (bits 0..7).
let bits = sr.0 & 0x000000FF;

// Mask all the channels that fired.
regs.dier().modify(|w| w.0 &= !bits);

// Wake the tasks
if sr.uif() {
T::state().up_waker.wake();
for pin in BitIter(bits) {
T::state().wakers[pin as usize].wake();
}
}
}

/// Capture/Compare interrupt handler.
pub struct CaptureCompareInterruptHandler<T: GeneralInstance1Channel> {
_phantom: PhantomData<T>,
}

impl<T: GeneralInstance1Channel> interrupt::typelevel::Handler<T::CaptureCompareInterrupt>
for CaptureCompareInterruptHandler<T>
{
unsafe fn on_interrupt() {
#[cfg(feature = "low-power")]
crate::low_power::on_wakeup_irq();

let regs = crate::pac::timer::TimGp16::from_ptr(T::regs());
struct BitIter(u32);

// Read TIM interrupt flags.
let sr = regs.sr().read();
impl Iterator for BitIter {
type Item = u32;

// Mask relevant interrupts (CCIE).
let bits = sr.0 & 0x0000001E;

// Mask all the channels that fired.
regs.dier().modify(|w| w.0 &= !bits);

// Wake the tasks
for ch in 0..4 {
if sr.ccif(ch) {
T::state().cc_waker[ch].wake();
fn next(&mut self) -> Option<Self::Item> {
match self.0.trailing_zeros() {
32 => None,
b => {
self.0 &= !(1 << b);
Some(b)
}
}
}
Expand Down
Loading
Loading