mirror of
https://github.com/italicsjenga/rp-hal-boards.git
synced 2025-01-11 13:01:30 +11:00
Add rtic-monotonic support for timer & alarms (feature gated) (#459)
* Add rtic-monotonic support for timer & alarms * Force Alarm interrupt if timer is set too soon * timer: Remove non_exhaustive attribute from ScheduleAlarmError * timer: TooSoon is no longer emitted so it can be removed
This commit is contained in:
parent
82800fe370
commit
305dfc9d1f
|
@ -67,3 +67,7 @@ disable-intrinsics = ["rp2040-hal/disable-intrinsics"]
|
||||||
|
|
||||||
# This enables ROM functions for f64 math that were not present in the earliest RP2040s
|
# This enables ROM functions for f64 math that were not present in the earliest RP2040s
|
||||||
rom-v2-intrinsics = ["rp2040-hal/rom-v2-intrinsics"]
|
rom-v2-intrinsics = ["rp2040-hal/rom-v2-intrinsics"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "pico_rtic_monotonic"
|
||||||
|
required-features = ["rp2040-hal/rtic-monotonic"]
|
||||||
|
|
92
boards/rp-pico/examples/pico_rtic_monotonic.rs
Normal file
92
boards/rp-pico/examples/pico_rtic_monotonic.rs
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use panic_halt as _;
|
||||||
|
|
||||||
|
#[rtic::app(device = rp_pico::hal::pac, peripherals = true, dispatchers = [I2C0_IRQ])]
|
||||||
|
mod app {
|
||||||
|
|
||||||
|
use embedded_hal::digital::v2::OutputPin;
|
||||||
|
use fugit::ExtU64;
|
||||||
|
use fugit::MicrosDurationU32;
|
||||||
|
use rp_pico::{
|
||||||
|
hal::{
|
||||||
|
self,
|
||||||
|
clocks::init_clocks_and_plls,
|
||||||
|
timer::{monotonic::Monotonic, Alarm0},
|
||||||
|
watchdog::Watchdog,
|
||||||
|
Sio,
|
||||||
|
},
|
||||||
|
XOSC_CRYSTAL_FREQ,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SCAN_TIME_US: MicrosDurationU32 = MicrosDurationU32::secs(1);
|
||||||
|
|
||||||
|
#[shared]
|
||||||
|
struct Shared {
|
||||||
|
led: hal::gpio::Pin<hal::gpio::pin::bank0::Gpio25, hal::gpio::PushPullOutput>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[monotonic(binds = TIMER_IRQ_0, default = true)]
|
||||||
|
type MyMono = Monotonic<Alarm0>;
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct Local {}
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init(c: init::Context) -> (Shared, Local, init::Monotonics) {
|
||||||
|
// Soft-reset does not release the hardware spinlocks
|
||||||
|
// Release them now to avoid a deadlock after debug or watchdog reset
|
||||||
|
unsafe {
|
||||||
|
hal::sio::spinlock_reset();
|
||||||
|
}
|
||||||
|
let mut resets = c.device.RESETS;
|
||||||
|
let mut watchdog = Watchdog::new(c.device.WATCHDOG);
|
||||||
|
let _clocks = init_clocks_and_plls(
|
||||||
|
XOSC_CRYSTAL_FREQ,
|
||||||
|
c.device.XOSC,
|
||||||
|
c.device.CLOCKS,
|
||||||
|
c.device.PLL_SYS,
|
||||||
|
c.device.PLL_USB,
|
||||||
|
&mut resets,
|
||||||
|
&mut watchdog,
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let sio = Sio::new(c.device.SIO);
|
||||||
|
let pins = rp_pico::Pins::new(
|
||||||
|
c.device.IO_BANK0,
|
||||||
|
c.device.PADS_BANK0,
|
||||||
|
sio.gpio_bank0,
|
||||||
|
&mut resets,
|
||||||
|
);
|
||||||
|
let mut led = pins.led.into_push_pull_output();
|
||||||
|
led.set_low().unwrap();
|
||||||
|
|
||||||
|
let mut timer = hal::Timer::new(c.device.TIMER, &mut resets);
|
||||||
|
let alarm = timer.alarm_0().unwrap();
|
||||||
|
blink_led::spawn_after(500.millis()).unwrap();
|
||||||
|
|
||||||
|
(
|
||||||
|
Shared { led },
|
||||||
|
Local {},
|
||||||
|
init::Monotonics(Monotonic::new(timer, alarm)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(
|
||||||
|
shared = [led],
|
||||||
|
local = [tog: bool = true],
|
||||||
|
)]
|
||||||
|
fn blink_led(mut c: blink_led::Context) {
|
||||||
|
if *c.local.tog {
|
||||||
|
c.shared.led.lock(|l| l.set_high().unwrap());
|
||||||
|
} else {
|
||||||
|
c.shared.led.lock(|l| l.set_low().unwrap());
|
||||||
|
}
|
||||||
|
*c.local.tog = !*c.local.tog;
|
||||||
|
|
||||||
|
blink_led::spawn_after(500.millis()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,8 @@ chrono = { version = "0.4", default-features = false, optional = true }
|
||||||
|
|
||||||
defmt = { version = ">=0.2.0, <0.4", optional = true }
|
defmt = { version = ">=0.2.0, <0.4", optional = true }
|
||||||
|
|
||||||
|
rtic-monotonic = { version = "1.0.0", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
panic-halt = "0.2.0"
|
panic-halt = "0.2.0"
|
||||||
|
|
|
@ -237,8 +237,11 @@ macro_rules! impl_alarm {
|
||||||
// 1 instance of AlarmN so we can safely atomically clear this bit.
|
// 1 instance of AlarmN so we can safely atomically clear this bit.
|
||||||
unsafe {
|
unsafe {
|
||||||
timer.armed.write_with_zero(|w| w.bits($armed_bit_mask));
|
timer.armed.write_with_zero(|w| w.bits($armed_bit_mask));
|
||||||
|
crate::atomic_register_access::write_bitmask_set(
|
||||||
|
timer.intf.as_ptr(),
|
||||||
|
$armed_bit_mask,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return Err(ScheduleAlarmError::AlarmTooSoon);
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
@ -258,6 +261,10 @@ macro_rules! impl_alarm {
|
||||||
// of the TIMER.inte register
|
// of the TIMER.inte register
|
||||||
unsafe {
|
unsafe {
|
||||||
let timer = &(*pac::TIMER::ptr());
|
let timer = &(*pac::TIMER::ptr());
|
||||||
|
crate::atomic_register_access::write_bitmask_clear(
|
||||||
|
timer.intf.as_ptr(),
|
||||||
|
$armed_bit_mask,
|
||||||
|
);
|
||||||
timer.intr.write_with_zero(|w| w.$int_alarm().set_bit());
|
timer.intr.write_with_zero(|w| w.$int_alarm().set_bit());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,11 +352,8 @@ macro_rules! impl_alarm {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors that can be returned from any of the `AlarmX::schedule` methods.
|
/// Errors that can be returned from any of the `AlarmX::schedule` methods.
|
||||||
#[non_exhaustive]
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum ScheduleAlarmError {
|
pub enum ScheduleAlarmError {
|
||||||
/// Alarm time is too low. Should be at least 10 microseconds.
|
|
||||||
AlarmTooSoon,
|
|
||||||
/// Alarm time is too high. Should not be more than `u32::max_value()` in the future.
|
/// Alarm time is too high. Should not be more than `u32::max_value()` in the future.
|
||||||
AlarmTooLate,
|
AlarmTooLate,
|
||||||
}
|
}
|
||||||
|
@ -381,3 +385,50 @@ impl_alarm!(Alarm3 {
|
||||||
int_name: "TIMER_IRQ_3",
|
int_name: "TIMER_IRQ_3",
|
||||||
armed_bit_mask: 0b1000
|
armed_bit_mask: 0b1000
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Support for RTIC monotonic trait.
|
||||||
|
#[cfg(feature = "rtic-monotonic")]
|
||||||
|
pub mod monotonic {
|
||||||
|
use super::{Alarm, Instant, Timer};
|
||||||
|
use fugit::ExtU32;
|
||||||
|
|
||||||
|
/// RTIC Monotonic Implementation
|
||||||
|
pub struct Monotonic<A>(pub Timer, A);
|
||||||
|
impl<A: Alarm> Monotonic<A> {
|
||||||
|
/// Creates a new monotonic.
|
||||||
|
pub const fn new(timer: Timer, alarm: A) -> Self {
|
||||||
|
Self(timer, alarm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<A: Alarm> rtic_monotonic::Monotonic for Monotonic<A> {
|
||||||
|
type Instant = Instant;
|
||||||
|
type Duration = fugit::MicrosDurationU64;
|
||||||
|
|
||||||
|
const DISABLE_INTERRUPT_ON_EMPTY_QUEUE: bool = false;
|
||||||
|
|
||||||
|
fn now(&mut self) -> Instant {
|
||||||
|
self.0.get_counter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_compare(&mut self, instant: Instant) {
|
||||||
|
// The alarm can only trigger up to 2^32 - 1 ticks in the future.
|
||||||
|
// So, if `instant` is more than 2^32 - 2 in the future, we use `max_instant` instead.
|
||||||
|
let max_instant = self.0.get_counter() + 0xFFFF_FFFE.micros();
|
||||||
|
let wake_at = core::cmp::min(instant, max_instant);
|
||||||
|
|
||||||
|
// Cannot fail
|
||||||
|
let _ = self.1.schedule_at(wake_at);
|
||||||
|
self.1.enable_interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_compare_flag(&mut self) {
|
||||||
|
self.1.clear_interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zero() -> Self::Instant {
|
||||||
|
Instant::from_ticks(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn reset(&mut self) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue