embassy-rs / embassy

Modern embedded framework, using Rust and async.
https://embassy.dev
Apache License 2.0
5.15k stars 713 forks source link

embassy-stm32: Expose RTC `instant` #2377

Open esden opened 8 months ago

esden commented 8 months ago

I require access to the sub second resolution RTC time. The data is available through the instant method but that method is currently private.

https://github.com/embassy-rs/embassy/blob/430696802e31701f23d2c8e9d5fc8c796cf277e1/embassy-stm32/src/rtc/mod.rs#L294-L298

I can't rely on the embassy-time as my application spends most of the time in shutdown.

What would be required to allow us to make the method public? For platforms that don't provide the sub second resolution data I would propose to change RtcInstant.subsecond field to be Option and return None when not available. Alternatively subsecond could default to 0 when not available, but that might be bad and hide the reality.

Dirbaio commented 8 months ago

It's not public because it's an implementation detail of the low-power executor. We could add it to DateTime instead.

For platforms that don't provide the sub second resolution data I would propose to change RtcInstant.subsecond field to be Option and return None when not available.

it's better to make public API conditionally available with cfg's. If your chip doesn't have it, it's better if you get a compilation error.

esden commented 8 months ago

After further investigation and after implementing my own code to generate ms resolution timestamps I think it is better to output subsecond DateTime when available. I agree that just exposing instant is probably not the right thing to do.

I implemented it for my purposes directly accessing the RTC registers using NaiveTime::from_hms_milli. It looks something like this here:

pub(crate) fn get_timestamp() -> Option<i64> {
    let raw_subsecond = pac::RTC.ssr().read().ss() as u32;
    let tr = pac::RTC.tr().read();
    let dr = pac::RTC.dr().read();
    let prediv_s = pac::RTC.prer().read().prediv_s() as u32;

    let millis = ((prediv_s - raw_subsecond) * 1000) / (prediv_s + 1);
    let second = bcd2_to_byte((tr.st(), tr.su())) as u32;
    let minute = bcd2_to_byte((tr.mnt(), tr.mnu())) as u32;
    let hour = bcd2_to_byte((tr.ht(), tr.hu())) as u32;

    let day = bcd2_to_byte((dr.dt(), dr.du())) as u32;
    let month = bcd2_to_byte((dr.mt() as u8, dr.mu())) as u32;
    let year = bcd2_to_byte((dr.yt(), dr.yu())) as i32 + 1970_i32;

    let date = NaiveDate::from_ymd_opt(year, month, day);
    let time = NaiveTime::from_hms_milli_opt(hour, minute, second, millis);
    let date_time = if date.is_some() && time.is_some() {
        Some(NaiveDateTime::new(
            date.unwrap(),
            time.unwrap()))
    } else {
        None
    };

    info!("pdi {} raw {} sub {}", prediv_s, raw_subsecond, millis);

    date_time.and_then(|dt| Some(dt.timestamp_millis()))
}

Let me know what your thoughts are.

Dirbaio commented 8 months ago

yeah, a PR adding subseconds to DateTime would be welcome!