Merge pull request #280 from 9names/alarm-ergonomics

Use atomic operations for altering Timer alarm interrupts
This commit is contained in:
Jonathan 'theJPster' Pallant 2022-02-03 19:47:40 +00:00 committed by GitHub
commit 16c9064c52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 32 additions and 23 deletions

View file

@ -54,7 +54,7 @@ mod app {
let mut timer = hal::Timer::new(c.device.TIMER, &mut resets); let mut timer = hal::Timer::new(c.device.TIMER, &mut resets);
let mut alarm = timer.alarm_0().unwrap(); let mut alarm = timer.alarm_0().unwrap();
let _ = alarm.schedule(SCAN_TIME_US.microseconds()); let _ = alarm.schedule(SCAN_TIME_US.microseconds());
alarm.enable_interrupt(&mut timer); alarm.enable_interrupt();
(Shared { timer, alarm, led }, Local {}, init::Monotonics()) (Shared { timer, alarm, led }, Local {}, init::Monotonics())
} }
@ -73,10 +73,9 @@ mod app {
} }
*c.local.tog = !*c.local.tog; *c.local.tog = !*c.local.tog;
let timer = c.shared.timer; let mut alarm = c.shared.alarm;
let alarm = c.shared.alarm; (alarm).lock(|a| {
(timer, alarm).lock(|t, a| { a.clear_interrupt();
a.clear_interrupt(t);
let _ = a.schedule(SCAN_TIME_US.microseconds()); let _ = a.schedule(SCAN_TIME_US.microseconds());
}); });
} }

View file

@ -3,6 +3,7 @@
use embedded_time::duration::Microseconds; use embedded_time::duration::Microseconds;
use crate::atomic_register_access::{write_bitmask_clear, write_bitmask_set};
use crate::pac::{RESETS, TIMER}; use crate::pac::{RESETS, TIMER};
use crate::resets::SubsystemReset; use crate::resets::SubsystemReset;
use core::marker::PhantomData; use core::marker::PhantomData;
@ -200,12 +201,15 @@ macro_rules! impl_alarm {
/// ` is called. /// ` is called.
/// ///
/// The interrupt is unable to trigger a 2nd time until this interrupt is cleared. /// The interrupt is unable to trigger a 2nd time until this interrupt is cleared.
pub fn clear_interrupt(&mut self, timer: &mut Timer) { pub fn clear_interrupt(&mut self) {
// safety: Because we have a mutable reference on `timer`, we have exclusive access to `TIMER::ptr()` // safety: TIMER.intr is a write-clear register, so we can atomically clear our interrupt
let _ = timer; // by writing its value to this field
let timer = unsafe { &*TIMER::ptr() }; // Only one instance of this alarm index can exist, and only this alarm interacts with this bit
// of the TIMER.inte register
timer.intr.modify(|_, w| w.$int_alarm().set_bit()); unsafe {
let timer = &(*pac::TIMER::ptr());
timer.intr.write_with_zero(|w| w.$int_alarm().set_bit());
}
} }
/// Enable this alarm to trigger an interrupt. This alarm will trigger ` /// Enable this alarm to trigger an interrupt. This alarm will trigger `
@ -215,21 +219,27 @@ macro_rules! impl_alarm {
/// After this interrupt is triggered, make sure to clear the interrupt with [clear_interrupt]. /// After this interrupt is triggered, make sure to clear the interrupt with [clear_interrupt].
/// ///
/// [clear_interrupt]: #method.clear_interrupt /// [clear_interrupt]: #method.clear_interrupt
pub fn enable_interrupt(&mut self, timer: &mut Timer) { pub fn enable_interrupt(&mut self) {
// safety: Because we have a mutable reference on `timer`, we have exclusive access to `TIMER::ptr()` // safety: using the atomic set alias means we can atomically set our interrupt enable bit.
let _ = timer; // Only one instance of this alarm can exist, and only this alarm interacts with this bit
let timer = unsafe { &*TIMER::ptr() }; // of the TIMER.inte register
unsafe {
timer.inte.modify(|_, w| w.$int_alarm().set_bit()); let timer = &(*pac::TIMER::ptr());
let reg = (&timer.inte).as_ptr();
write_bitmask_set(reg, $armed_bit_mask);
}
} }
/// Disable this alarm, preventing it from triggering an interrupt. /// Disable this alarm, preventing it from triggering an interrupt.
pub fn disable_interrupt(&mut self, timer: &mut Timer) { pub fn disable_interrupt(&mut self) {
// safety: Because we have a mutable reference on `timer`, we have exclusive access to `TIMER::ptr()` // safety: using the atomic set alias means we can atomically clear our interrupt enable bit.
let _ = timer; // Only one instance of this alarm can exist, and only this alarm interacts with this bit
let timer = unsafe { &*TIMER::ptr() }; // of the TIMER.inte register
unsafe {
timer.inte.modify(|_, w| w.$int_alarm().clear_bit()); let timer = &(*pac::TIMER::ptr());
let reg = (&timer.inte).as_ptr();
write_bitmask_clear(reg, $armed_bit_mask);
}
} }
/// Schedule the alarm to be finished after `countdown`. If [enable_interrupt] is called, this will trigger interrupt ` /// Schedule the alarm to be finished after `countdown`. If [enable_interrupt] is called, this will trigger interrupt `