SystemTimer fixups (#1455)

* Resolve TODOs, add wait_until to target alarm, add &mut self to public API

* changelog
This commit is contained in:
Scott Mabin 2024-04-17 16:09:48 +01:00 committed by GitHub
parent 71db9fa55c
commit 111dcda103
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 78 additions and 55 deletions

View File

@ -65,6 +65,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- PCNT: Runtime ISR binding (#1396)
- Runtime ISR binding for RTC (#1405)
- Improve MCPWM DeadTimeCfg API (#1378)
- `SystemTimer`'s `Alarm` methods now require `&mut self` (#1455)
### Removed

View File

@ -52,13 +52,13 @@ xtensa-lx = { version = "0.9.0", optional = true }
# IMPORTANT:
# Each supported device MUST have its PAC included below along with a
# corresponding feature.
esp32 = { git = "https://github.com/esp-rs/esp-pacs", rev = "1d58d95", features = ["critical-section", "rt"], optional = true }
esp32c2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "1d58d95", features = ["critical-section", "rt"], optional = true }
esp32c3 = { git = "https://github.com/esp-rs/esp-pacs", rev = "1d58d95", features = ["critical-section", "rt"], optional = true }
esp32c6 = { git = "https://github.com/esp-rs/esp-pacs", rev = "1d58d95", features = ["critical-section", "rt"], optional = true }
esp32h2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "1d58d95", features = ["critical-section", "rt"], optional = true }
esp32s2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "1d58d95", features = ["critical-section", "rt"], optional = true }
esp32s3 = { git = "https://github.com/esp-rs/esp-pacs", rev = "1d58d95", features = ["critical-section", "rt"], optional = true }
esp32 = { git = "https://github.com/esp-rs/esp-pacs", rev = "f6b5e6b", features = ["critical-section", "rt"], optional = true }
esp32c2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "f6b5e6b", features = ["critical-section", "rt"], optional = true }
esp32c3 = { git = "https://github.com/esp-rs/esp-pacs", rev = "f6b5e6b", features = ["critical-section", "rt"], optional = true }
esp32c6 = { git = "https://github.com/esp-rs/esp-pacs", rev = "f6b5e6b", features = ["critical-section", "rt"], optional = true }
esp32h2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "f6b5e6b", features = ["critical-section", "rt"], optional = true }
esp32s2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "f6b5e6b", features = ["critical-section", "rt"], optional = true }
esp32s3 = { git = "https://github.com/esp-rs/esp-pacs", rev = "f6b5e6b", features = ["critical-section", "rt"], optional = true }
[target.'cfg(target_arch = "riscv32")'.dependencies]
esp-riscv-rt = { version = "0.7.0", path = "../esp-riscv-rt" }

View File

@ -135,9 +135,9 @@ impl EmbassyTimer {
fn arm(&self, id: usize, timestamp: u64) {
match id {
0 => self.alarm0.set_target(timestamp),
1 => self.alarm1.set_target(timestamp),
2 => self.alarm2.set_target(timestamp),
0 => self.alarm0.set_target_internal(timestamp),
1 => self.alarm1.set_target_internal(timestamp),
2 => self.alarm2.set_target_internal(timestamp),
_ => {}
}
}

View File

@ -1,29 +1,27 @@
//! # System Timer peripheral driver
//!
//! ## Overview
//! This software module provides an interface to interact with the system timer
//! (SYSTIMER) peripheral on ESP microcontroller chips.
//! The System Timer is a
#![cfg_attr(esp32s2, doc = "`64-bit`")]
#![cfg_attr(not(esp32s2), doc = "`54-bit`")]
//! timer with three comparators capable of raising an alarm interupt on each.
//!
//! Each ESP chip provides a timer (`52-bit` or `64-bit`, depends on chip),
//! which can be used to generate tick interrupts for operating system, or be
//! used as a general timer to generate periodic interrupts or one-time
//! interrupts. With the help of the RTC timer, system timer can be kept up to
//! date after Light-sleep or Deep-sleep.
//! To obtain the current timer value, call [`SystemTimer::now`].
//!
//! The driver supports features such as retrieving the current system time,
//! setting alarms for specific time points or periodic intervals, enabling and
//! clearing interrupts, configuring various settings of the system timer.
//! [`Alarm`]'s can be configured into two different modes:
//!
//! By using the SYSTIMER peripheral driver, you can leverage the system timer
//! functionality of ESP chips for accurate timing measurements, event
//! triggering and synchronization in your applications.
//! - [`Target`], for one-shot timer behaviour.
//! - [`Periodic`], for alarm triggers at a repeated interval.
//!
//! ## Example
//! ```no_run
//! let peripherals = Peripherals::take();
//!
//! let syst = SystemTimer::new(peripherals.SYSTIMER);
//! let systimer = SystemTimer::new(peripherals.SYSTIMER);
//! println!("SYSTIMER Current value = {}", SystemTimer::now());
//!
//! let mut alarm0 = systimer.alarm0;
//! // Block for a second
//! alarm0.wait_until(SystemTimer::now().wrapping_add(SystemTimer::TICKS_PER_SECOND));
//! ```
use core::{marker::PhantomData, mem::transmute};
@ -50,8 +48,6 @@ use crate::{
},
};
// TODO this only handles unit0 of the systimer
/// The SystemTimer
pub struct SystemTimer<'d, DM: crate::Mode> {
pub alarm0: Alarm<Target, DM, 0>,
@ -69,7 +65,7 @@ impl<'d> SystemTimer<'d, crate::Blocking> {
/// The ticks per second the underlying peripheral uses
#[cfg(esp32s2)]
pub const TICKS_PER_SECOND: u64 = 80_000_000; // TODO this can change when we have support for changing APB frequency
pub const TICKS_PER_SECOND: u64 = 80_000_000;
#[cfg(not(esp32s2))]
pub const TICKS_PER_SECOND: u64 = 16_000_000;
@ -86,8 +82,7 @@ impl<'d> SystemTimer<'d, crate::Blocking> {
}
}
// TODO use fugit types
/// Get the current count of the system-timer.
/// Get the current count of unit 0 in the system timer.
pub fn now() -> u64 {
// This should be safe to access from multiple contexts
// worst case scenario the second accessor ends up reading
@ -132,7 +127,7 @@ pub struct Target;
/// A marker for a [Alarm] in periodic mode.
#[derive(Debug)]
pub struct Periodic; // TODO, also impl e-h timer traits
pub struct Periodic;
/// A single alarm.
#[derive(Debug)]
@ -252,11 +247,32 @@ impl<T, DM: crate::Mode, const CHANNEL: u8> Alarm<T, DM, CHANNEL> {
_ => unreachable!(),
}
}
pub(crate) fn set_target_internal(&self, timestamp: u64) {
self.configure(|tconf, hi, lo| unsafe {
tconf.write(|w| w.target0_period_mode().clear_bit()); // target mode
hi.write(|w| w.timer_target0_hi().bits((timestamp >> 32) as u32));
lo.write(|w| w.timer_target0_lo().bits((timestamp & 0xFFFF_FFFF) as u32));
})
}
pub(crate) fn set_period_internal(&self, ticks: u32) {
self.configure(|tconf, hi, lo| unsafe {
tconf.write(|w| {
w.target0_period_mode()
.set_bit()
.target0_period()
.bits(ticks)
});
hi.write(|w| w.timer_target0_hi().bits(0));
lo.write(|w| w.timer_target0_lo().bits(0));
});
}
}
impl<T, const CHANNEL: u8> Alarm<T, crate::Blocking, CHANNEL> {
/// Set the interrupt handler for this alarm.
pub fn set_interrupt_handler(&self, handler: InterruptHandler) {
pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
match CHANNEL {
0 => unsafe {
crate::interrupt::bind_interrupt(
@ -293,23 +309,39 @@ impl<T, const CHANNEL: u8> Alarm<T, crate::Blocking, CHANNEL> {
}
/// Enable the interrupt for this alarm.
pub fn enable_interrupt(&self, val: bool) {
pub fn enable_interrupt(&mut self, val: bool) {
self.enable_interrupt_internal(val);
}
/// Enable the interrupt pending status for this alarm.
pub fn clear_interrupt(&self) {
pub fn clear_interrupt(&mut self) {
self.clear_interrupt_internal();
}
}
impl<DM: crate::Mode, const CHANNEL: u8> Alarm<Target, DM, CHANNEL> {
/// Set the target value of this [Alarm]
pub fn set_target(&self, timestamp: u64) {
self.configure(|tconf, hi, lo| unsafe {
tconf.write(|w| w.target0_period_mode().clear_bit()); // target mode
hi.write(|w| w.timer_target0_hi().bits((timestamp >> 32) as u32));
lo.write(|w| w.timer_target0_lo().bits((timestamp & 0xFFFF_FFFF) as u32));
})
pub fn set_target(&mut self, timestamp: u64) {
self.set_target_internal(timestamp);
}
/// Block waiting until the timer reaches the `timestamp`
pub fn wait_until(&mut self, timestamp: u64) {
self.clear_interrupt_internal();
self.set_target(timestamp);
loop {
let r = unsafe { &*crate::peripherals::SYSTIMER::PTR }
.int_raw()
.read();
if match CHANNEL {
0 => r.target0().bit_is_set(),
1 => r.target1().bit_is_set(),
2 => r.target2().bit_is_set(),
_ => unreachable!(),
} {
break;
}
}
}
/// Converts this [Alarm] into [Periodic] mode
@ -320,20 +352,11 @@ impl<DM: crate::Mode, const CHANNEL: u8> Alarm<Target, DM, CHANNEL> {
impl<DM: crate::Mode, const CHANNEL: u8> Alarm<Periodic, DM, CHANNEL> {
/// Set the period of this [Alarm]
pub fn set_period(&self, period: MicrosDurationU32) {
pub fn set_period(&mut self, period: MicrosDurationU32) {
let us = period.ticks();
let ticks = us * (SystemTimer::TICKS_PER_SECOND / 1_000_000) as u32;
self.configure(|tconf, hi, lo| unsafe {
tconf.write(|w| {
w.target0_period_mode()
.set_bit()
.target0_period()
.bits(ticks)
});
hi.write(|w| w.timer_target0_hi().bits(0));
lo.write(|w| w.timer_target0_lo().bits(0));
});
self.set_period_internal(ticks);
}
/// Converts this [Alarm] into [Target] mode

View File

@ -35,21 +35,20 @@ fn main() -> ! {
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
let systimer = SystemTimer::new(peripherals.SYSTIMER);
println!("SYSTIMER Current value = {}", SystemTimer::now());
critical_section::with(|cs| {
let alarm0 = systimer.alarm0.into_periodic();
let mut alarm0 = systimer.alarm0.into_periodic();
alarm0.set_interrupt_handler(systimer_target0);
alarm0.set_period(1u32.secs());
alarm0.enable_interrupt(true);
let alarm1 = systimer.alarm1;
let mut alarm1 = systimer.alarm1;
alarm1.set_interrupt_handler(systimer_target1);
alarm1.set_target(SystemTimer::now() + (SystemTimer::TICKS_PER_SECOND * 2));
alarm1.enable_interrupt(true);
let alarm2 = systimer.alarm2;
let mut alarm2 = systimer.alarm2;
alarm2.set_interrupt_handler(systimer_target2);
alarm2.set_target(SystemTimer::now() + (SystemTimer::TICKS_PER_SECOND * 3));
alarm2.enable_interrupt(true);