Add current_time (#1503)

* Add `current_time`

* CHANGELOG.md

* Add HIL test for `current_time`

* Fix after rebase
This commit is contained in:
Björn Quentin 2024-04-23 16:59:28 +02:00 committed by GitHub
parent ad1176865e
commit 373735f96a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 136 additions and 1 deletions

View File

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- ESP32-PICO-V3-02: Initial support (#1155)
- `time::current_time` API (#1503)
### Fixed

View File

@ -153,6 +153,7 @@ pub mod spi;
pub mod system;
#[cfg(systimer)]
pub mod systimer;
pub mod time;
#[cfg(any(timg0, timg1))]
pub mod timer;
#[cfg(trace0)]

View File

@ -77,6 +77,7 @@ pub unsafe extern "C" fn ESP32Reset() -> ! {
}
crate::interrupt::setup_interrupts();
crate::time::time_init();
// continue with default reset handler
xtensa_lx_rt::Reset();

76
esp-hal/src/time.rs Normal file
View File

@ -0,0 +1,76 @@
//! The `time` module offers a way to get the system uptime.
//!
//! ### Example
//! ```no_run
//! let time = time::current_time();
//! ```
#![warn(missing_docs)]
/// Provides time since system start in microseconds precision
///
/// The counter wont measure time in sleep-mode.
///
/// The timer will wrap after:
/// - ESP32 = 36_558 years,
/// - ESP32-S2 = 7_311 years,
/// - others = > 7 years
pub fn current_time() -> fugit::Instant<u64, 1, 1_000_000> {
#[cfg(esp32)]
let (ticks, div) = {
// on ESP32 use LACT
let tg0 = unsafe { crate::peripherals::TIMG0::steal() };
tg0.lactupdate().write(|w| unsafe { w.update().bits(1) });
// The peripheral doesn't have a bit to indicate that the update is done, so we
// poll the lower 32 bit part of the counter until it changes, or a timeout
// expires.
let lo_initial = tg0.lactlo().read().bits();
let mut div = tg0.lactconfig().read().divider().bits();
let lo = loop {
let lo = tg0.lactlo().read().bits();
if lo != lo_initial || div == 0 {
break lo;
}
div -= 1;
};
let hi = tg0.lacthi().read().bits();
let ticks = (hi as u64) << 32u64 | lo as u64;
(ticks, 16)
};
#[cfg(not(esp32))]
let (ticks, div) = {
// otherwise use SYSTIMER
let ticks = crate::systimer::SystemTimer::now();
(
ticks,
(crate::systimer::SystemTimer::TICKS_PER_SECOND / 1_000_000),
)
};
fugit::Instant::<u64, 1, 1_000_000>::from_ticks(ticks / div)
}
#[cfg(esp32)]
pub(crate) fn time_init() {
// we assume 80MHz APB clock source - there is no way to configure it in a
// different way currently
const APB_FREQUENCY: u32 = 80_000_000u32;
let tg0 = unsafe { crate::peripherals::TIMG0::steal() };
tg0.lactconfig().write(|w| unsafe { w.bits(0) });
tg0.lactalarmhi().write(|w| unsafe { w.bits(u32::MAX) });
tg0.lactalarmlo().write(|w| unsafe { w.bits(u32::MAX) });
tg0.lactload().write(|w| unsafe { w.load().bits(1) });
// 16 MHz counter
tg0.lactconfig()
.modify(|_, w| unsafe { w.divider().bits((APB_FREQUENCY / 16_000_000u32) as u16) });
tg0.lactconfig().modify(|_, w| {
w.increase().bit(true);
w.autoreload().bit(true);
w.en().bit(true)
});
}

View File

@ -53,7 +53,12 @@ fn main() -> ! {
#[handler]
fn tg0_t0_level() {
critical_section::with(|cs| {
esp_println::println!("Interrupt 1");
esp_println::println!(
"Interrupt at {} ms",
esp_hal::time::current_time()
.duration_since_epoch()
.to_millis()
);
let mut timer0 = TIMER0.borrow_ref_mut(cs);
let timer0 = timer0.as_mut().unwrap();

View File

@ -53,6 +53,10 @@ required-features = ["async", "embassy"]
name = "ecc"
harness = false
[[test]]
name = "get_time"
harness = false
[dependencies]
cfg-if = "1.0.0"
critical-section = "1.1.2"

View File

@ -0,0 +1,47 @@
//! current_time Test
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
#![no_std]
#![no_main]
use defmt_rtt as _;
use esp_backtrace as _;
use esp_hal::{clock::ClockControl, delay::Delay, peripherals::Peripherals, system::SystemControl};
struct Context {
delay: Delay,
}
impl Context {
pub fn init() -> Self {
let peripherals = Peripherals::take();
let system = SystemControl::new(peripherals.SYSTEM);
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
let delay = Delay::new(&clocks);
Context { delay }
}
}
#[cfg(test)]
#[embedded_test::tests]
mod tests {
use super::*;
#[init]
fn init() -> Context {
Context::init()
}
#[test]
fn test_current_time(ctx: Context) {
let t1 = esp_hal::time::current_time();
ctx.delay.delay_millis(500);
let t2 = esp_hal::time::current_time();
assert!(t2 > t1);
assert!((t2 - t1).to_millis() >= 500u64);
}
}