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

Allow setting RTC time #1883

Merged
merged 35 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e3ab6ef
Initial WIP RTC set implementation
amsam0 Jul 30, 2024
461629b
Deprecate get_time_raw and add docs + some cleanup
amsam0 Aug 12, 2024
48075f2
Update rtc time example
amsam0 Aug 12, 2024
66759b2
Format
amsam0 Aug 12, 2024
1e19933
Update changelog
amsam0 Aug 12, 2024
c58310a
Add some comments linking the PR
amsam0 Aug 12, 2024
59f43de
Merge branch 'main' into rtc-set
amsam0 Aug 12, 2024
aa6b922
Small compilation fixes
amsam0 Aug 13, 2024
aef6d4b
C6 and H2 fixes
amsam0 Aug 14, 2024
9505691
Remove parantheses from if statement lol
amsam0 Aug 14, 2024
79f00ba
Merge branch 'main' into rtc-set
amsam0 Aug 14, 2024
bcd8629
Remove accidental changelog change
amsam0 Aug 14, 2024
974f683
Implement boot time wrapping to avoid overflows
amsam0 Aug 14, 2024
f972ba2
Remove unused get_rtc_time_ms and get_rtc_time_us functions
amsam0 Aug 14, 2024
358f2ed
Make get_rtc_time_us public and re-add get_rtc_time_ms as public
amsam0 Aug 14, 2024
e3899da
Update changelog
amsam0 Aug 14, 2024
b473d3a
Remove get_time_raw and replace with public get_rtc_time_raw
amsam0 Aug 14, 2024
e82f53e
Merge branch 'esp-rs:main' into rtc-set
amsam0 Aug 16, 2024
a9a1ace
Merge branch 'main' into rtc-set
amsam0 Aug 18, 2024
8c88764
Changelog reordering
amsam0 Aug 18, 2024
c7fd031
Function renaming
amsam0 Aug 18, 2024
68de22c
Use fugit and update changelog
amsam0 Aug 18, 2024
94830df
Small typo fix
amsam0 Aug 18, 2024
a7cb08a
Fix changelog addition from merging
amsam0 Aug 18, 2024
31309c7
Use chrono for current_time and set_current_time
amsam0 Sep 1, 2024
4dfdf40
Merge branch 'main' into rtc-set
amsam0 Sep 5, 2024
238bc4d
Fix changelog
amsam0 Sep 5, 2024
0b889f7
Update example
amsam0 Sep 5, 2024
7d4e3a7
Fix merge errors
amsam0 Sep 5, 2024
6e7fd88
Rename `time::current_time` to `time::uptime`
amsam0 Sep 5, 2024
f3e6692
Revert "Rename `time::current_time` to `time::uptime`"
amsam0 Sep 7, 2024
14948fe
Format
amsam0 Sep 7, 2024
f9d8751
Add info to migration guide
amsam0 Sep 7, 2024
296412a
Fix compilation for esp32c2
amsam0 Sep 7, 2024
ea51f02
Remove information about setting RTC time from migration guide since …
amsam0 Sep 7, 2024
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
3 changes: 3 additions & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added sleep and wakeup support for esp32c2 (#1922)
- `Input`, `Output`, `OutputOpenDrain` and `Flex` now implement `Peripheral`. (#2094)
- Previously unavailable memory is available via `.dram2_uninit` section (#2079)
- Added `Rtc::set_current_time` to allow setting RTC time, and `Rtc::current_time` to getting RTC time while taking into account boot time (#1883)

### Changed

Expand All @@ -31,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- To avoid confusion with the `Rtc::current_time` wall clock time APIs, we've renamed `esp_hal::time::current_time` to `esp_hal::time::now`. (#2091)
- Renamed `touch::Continous` to `touch::Continuous`. (#2094)
- The (previously undocumented) `ErasedPin` enum has been replaced with the `ErasedPin` struct. (#2094)
- Renamed and merged `Rtc::get_time_us` and `Rtc::get_time_ms` into `Rtc::time_since_boot` (#1883)

### Fixed

Expand All @@ -48,6 +50,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Removed `Peripherals::take`. Use `esp_hal::init` to obtain `Peripherals` (#1999)
- Removed `AnyInputOnlyPin` in favour of `AnyPin`. (#2071)
- Removed the following functions from `GpioPin`: `is_high`, `is_low`, `set_high`, `set_low`, `set_state`, `is_set_high`, `is_set_low`, `toggle`. (#2094)
- Removed `Rtc::get_time_raw` (#1883)

## [0.20.1] - 2024-08-30

Expand Down
1 change: 1 addition & 0 deletions esp-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ bitflags = "2.6.0"
bytemuck = "1.17.1"
bitfield = "0.16.1"
cfg-if = "1.0.0"
chrono = { version = "0.4.38", default-features = false }
critical-section = "1.1.3"
defmt = { version = "0.3.8", optional = true }
delegate = "0.12.0"
Expand Down
10 changes: 10 additions & 0 deletions esp-hal/MIGRATING-0.20.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,13 @@ let dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap();
.map_err(|e| e.0)
.unwrap();
```

## RTC Wall Clock APIs

Instead of the `get_time_ms`, `get_time_us`, and `get_time_raw` functions, the `Rtc` struct now provides the `current_time` function, using `chrono`'s `NaiveDateTime` struct.

```diff
let rtc = Rtc::new(peripherals.LPWR);
- let current_time_ms = rtc.get_time_ms();
+ let current_time_ms = rtc.current_time().and_utc().timestamp_millis(); // assuming UTC
```
117 changes: 108 additions & 9 deletions esp-hal/src/rtc_cntl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
//! }
//! ```

use chrono::{DateTime, NaiveDateTime};
#[cfg(not(any(esp32c6, esp32h2)))]
use fugit::HertzU32;
use fugit::MicrosDurationU64;
Expand All @@ -78,7 +79,7 @@ use crate::efuse::Efuse;
#[cfg(not(any(esp32c6, esp32h2)))]
use crate::peripherals::{LPWR, TIMG0};
#[cfg(any(esp32c6, esp32h2))]
use crate::peripherals::{LP_TIMER, LP_WDT};
use crate::peripherals::{LP_AON, LP_TIMER, LP_WDT};
#[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))]
use crate::rtc_cntl::sleep::{RtcSleepConfig, WakeSource, WakeTriggers};
use crate::{
Expand Down Expand Up @@ -214,8 +215,8 @@ impl<'d> Rtc<'d> {
RtcClock::estimate_xtal_frequency()
}

/// Read the current value of the rtc time registers.
pub fn get_time_raw(&self) -> u64 {
/// Get the time since boot in the raw register units.
fn time_since_boot_raw(&self) -> u64 {
#[cfg(not(any(esp32c6, esp32h2)))]
let rtc_cntl = unsafe { &*LPWR::ptr() };
#[cfg(any(esp32c6, esp32h2))]
Expand Down Expand Up @@ -253,14 +254,112 @@ impl<'d> Rtc<'d> {
((h as u64) << 32) | (l as u64)
}

/// Read the current value of the rtc time registers in microseconds.
pub fn get_time_us(&self) -> u64 {
self.get_time_raw() * 1_000_000 / RtcClock::get_slow_freq().frequency().to_Hz() as u64
/// Get the time since boot.
pub fn time_since_boot(&self) -> MicrosDurationU64 {
MicrosDurationU64::micros(
self.time_since_boot_raw() * 1_000_000
/ RtcClock::get_slow_freq().frequency().to_Hz() as u64,
)
}

/// Read the current value of the rtc time registers in milliseconds.
pub fn get_time_ms(&self) -> u64 {
self.get_time_raw() * 1_000 / RtcClock::get_slow_freq().frequency().to_Hz() as u64
/// Read the current value of the boot time registers in microseconds.
fn boot_time_us(&self) -> u64 {
// For more info on about how RTC setting works and what it has to do with boot time, see https://github.com/esp-rs/esp-hal/pull/1883

// In terms of registers, STORE2 and STORE3 are used on all current chips
// (esp32, esp32p4, esp32h2, esp32c2, esp32c3, esp32c5, esp32c6, esp32c61,
// esp32s2, esp32s3)

// In terms of peripherals:

// - LPWR is used on the following chips: esp32, esp32p4, esp32c2, esp32c3,
// esp32s2, esp32s3

// - LP_AON is used on the following chips: esp32c5, esp32c6, esp32c61, esp32h2

// For registers and peripherals used in esp-idf, see https://github.com/search?q=repo%3Aespressif%2Fesp-idf+RTC_BOOT_TIME_LOW_REG+RTC_BOOT_TIME_HIGH_REG+path%3A**%2Frtc.h&type=code

#[cfg(not(any(esp32c6, esp32h2)))]
let rtc_cntl = unsafe { &*LPWR::ptr() };
#[cfg(any(esp32c6, esp32h2))]
let rtc_cntl = unsafe { &*LP_AON::ptr() };

let (l, h) = (rtc_cntl.store2(), rtc_cntl.store3());

let l = l.read().bits() as u64;
let h = h.read().bits() as u64;

// https://github.com/espressif/esp-idf/blob/23e4823f17a8349b5e03536ff7653e3e584c9351/components/newlib/port/esp_time_impl.c#L115
l + (h << 32)
}

/// Set the current value of the boot time registers in microseconds.
fn set_boot_time_us(&self, boot_time_us: u64) {
// Please see `boot_time_us` for documentation on registers and peripherals
// used for certain SOCs.

#[cfg(not(any(esp32c6, esp32h2)))]
let rtc_cntl = unsafe { &*LPWR::ptr() };
#[cfg(any(esp32c6, esp32h2))]
let rtc_cntl = unsafe { &*LP_AON::ptr() };

let (l, h) = (rtc_cntl.store2(), rtc_cntl.store3());

// https://github.com/espressif/esp-idf/blob/23e4823f17a8349b5e03536ff7653e3e584c9351/components/newlib/port/esp_time_impl.c#L102-L103
l.write(|w| unsafe { w.bits((boot_time_us & 0xffffffff) as u32) });
h.write(|w| unsafe { w.bits((boot_time_us >> 32) as u32) });
}

/// Get the current time.
pub fn current_time(&self) -> NaiveDateTime {
// Current time is boot time + time since boot

let rtc_time_us = self.time_since_boot().to_micros();
let boot_time_us = self.boot_time_us();
let wrapped_boot_time_us = u64::MAX - boot_time_us;

// We can detect if we wrapped the boot time by checking if rtc time is greater
// than the amount of time we would've wrapped.
let current_time_us = if rtc_time_us > wrapped_boot_time_us {
// We also just checked that this won't overflow
rtc_time_us - wrapped_boot_time_us
} else {
boot_time_us + rtc_time_us
};

DateTime::from_timestamp_micros(current_time_us as i64)
.unwrap()
.naive_utc()
}

/// Set the current time.
///
/// # Panics
///
/// Panics if `current_time` is before the Unix epoch (meaning the
/// underlying timestamp is negative).
pub fn set_current_time(&self, current_time: NaiveDateTime) {
let current_time_us: u64 = current_time
.and_utc()
.timestamp_micros()
.try_into()
.expect("current_time is negative");

// Current time is boot time + time since boot (rtc time)
// So boot time = current time - time since boot (rtc time)

let rtc_time_us = self.time_since_boot().to_micros();
if current_time_us < rtc_time_us {
// An overflow would happen if we subtracted rtc_time_us from current_time_us.
// To work around this, we can wrap around u64::MAX by subtracting the
// difference between the current time and the time since boot.
// Subtracting time since boot and adding current new time is equivalent and
// avoids overflow. We just checked that rtc_time_us is less than time_us
// so this won't overflow.
self.set_boot_time_us(u64::MAX - rtc_time_us + current_time_us)
} else {
self.set_boot_time_us(current_time_us - rtc_time_us)
}
}

/// Enter deep sleep and wake with the provided `wake_sources`.
Expand Down
2 changes: 1 addition & 1 deletion esp-hal/src/rtc_cntl/sleep/esp32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl WakeSource for TimerWakeupSource {
let clock_hz = clock_freq.frequency().to_Hz() as u64;
let ticks = self.duration.as_micros() as u64 * clock_hz / 1_000_000u64;
// "alarm" time in slow rtc ticks
let now = rtc.get_time_raw();
let now = rtc.time_since_boot_raw();
let time_in_ticks = now + ticks;
unsafe {
rtc_cntl
Expand Down
2 changes: 1 addition & 1 deletion esp-hal/src/rtc_cntl/sleep/esp32c2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ impl WakeSource for TimerWakeupSource {
let clock_hz = clock_freq.frequency().to_Hz() as u64;
let ticks = self.duration.as_micros() as u64 * clock_hz / 1_000_000u64;
// "alarm" time in slow rtc ticks
let now = rtc.get_time_raw();
let now = rtc.time_since_boot_raw();
let time_in_ticks = now + ticks;
unsafe {
rtc_cntl
Expand Down
2 changes: 1 addition & 1 deletion esp-hal/src/rtc_cntl/sleep/esp32c3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ impl WakeSource for TimerWakeupSource {
let clock_hz = clock_freq.frequency().to_Hz() as u64;
let ticks = self.duration.as_micros() as u64 * clock_hz / 1_000_000u64;
// "alarm" time in slow rtc ticks
let now = rtc.get_time_raw();
let now = rtc.time_since_boot_raw();
let time_in_ticks = now + ticks;
unsafe {
rtc_cntl
Expand Down
2 changes: 1 addition & 1 deletion esp-hal/src/rtc_cntl/sleep/esp32c6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl WakeSource for TimerWakeupSource {
let clock_hz = clock_freq.frequency().to_Hz() as u64;
let ticks = self.duration.as_micros() as u64 * clock_hz / 1_000_000u64;
// "alarm" time in slow rtc ticks
let now = rtc.get_time_raw();
let now = rtc.time_since_boot_raw();
let time_in_ticks = now + ticks;
unsafe {
lp_timer.tar0_high().write(|w| {
Expand Down
2 changes: 1 addition & 1 deletion esp-hal/src/rtc_cntl/sleep/esp32s3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ impl WakeSource for TimerWakeupSource {
let clock_hz = clock_freq.frequency().to_Hz() as u64;
let ticks = self.duration.as_micros() as u64 * clock_hz / 1_000_000u64;
// "alarm" time in slow rtc ticks
let now = rtc.get_time_raw();
let now = rtc.time_since_boot_raw();
let time_in_ticks = now + ticks;
unsafe {
rtc_cntl
Expand Down
15 changes: 14 additions & 1 deletion examples/src/bin/rtc_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#![no_std]
#![no_main]

use core::time::Duration;
MabezDev marked this conversation as resolved.
Show resolved Hide resolved

use esp_backtrace as _;
use esp_hal::{delay::Delay, prelude::*, rtc_cntl::Rtc};

Expand All @@ -16,7 +18,18 @@ fn main() -> ! {
let delay = Delay::new();

loop {
esp_println::println!("rtc time in milliseconds is {}", rtc.get_time_ms());
esp_println::println!(
"rtc time in milliseconds is {}",
rtc.current_time().and_utc().timestamp_millis()
);
delay.delay_millis(1000);

// Set the time to half a second in the past
let new_time = rtc.current_time() - Duration::from_millis(500);
esp_println::println!(
"setting rtc time to {} milliseconds",
new_time.and_utc().timestamp_millis()
);
rtc.set_current_time(new_time);
}
}