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

stm32_common/periph/rtc: current implementation broken/poor accuracy #8746

Open
MichelRottleuthner opened this issue Mar 6, 2018 · 5 comments
Labels
Area: cpu Area: CPU/MCU ports Area: pm Area: (Low) power management Area: tests Area: tests and testing framework Area: timers Area: timer subsystems Discussion: RFC The issue/PR is used as a discussion starting point about the item of the issue/PR Platform: ARM Platform: This PR/issue effects ARM-based platforms Type: bug The issue reports a bug / The PR fixes a bug (including spelling errors)

Comments

@MichelRottleuthner
Copy link
Contributor

MichelRottleuthner commented Mar 6, 2018

Description

In the current implementation rtc_unlock() doesn't, like the name indicates, only unlocks the rtc but also enters init mode. Looking at the datasheet this is not a really good idea. Refering to "Calendar initialization and configuration":

1. Set INIT bit to 1 in the RTC_ISR register to enter initialization mode. In this mode, the
calendar counter is stopped
and its value can be updated.
(...)
5. (...) Exit the initialization mode by clearing the INIT bit. The actual calendar counter value is
then automatically loaded and the counting restarts after 4 RTCCLK clock cycles.

So every operation that calls rtc_unlock() will delay the rtc by the time the operation itself takes, plus at minimum the 4 RTCCLK cycles. This is for example true for rtc_set_alarm(). So if you happen to use the alarm feature to wake up every few seconds your time reference will become borked up pretty quickly.

The next problem comes up when using the rtc in combination with pm. This is only visible when your application makes use of power modes that issue a system reset when exiting this mode (namely Shutdown mode in my case). On reset rtc_init(), which is only conditioned by MODULE_PERIPH_RTC, is always called if the rtc is available. Since this is part of periph_init() it also doesn't help to disable auto_init.

One thing that was really nasty to debug here, came from the fact that rtc_init() isn't actually resetting the RTC_DR and RTC_TR registers. So testing if the rtc works together with pm looks "working ok" if you only observe the system over a short period of time, because the rtc_init() will effectively only loose the SSR (sub second regiser).
What do you think about using the INITS flag in RTC_ISR to only init the rtc if it wasn't already initialized before?

The fact that the current tests/periph_rtc doesn't highlight any of the problems brings me to the conclusion that the test needs improvement.

I'd like to exchange opinions on the following thoughts:
IMO It's absolutely necessary to add some quantifiable output on the accuracy. For example sntp could be used as a reference here.
This should also be tested with pm&rtc - particularly since rtc is a feature that will often be combined with low power modes.
Another thing that came to my mind: is it intentional that the rtc api doesn't provide functions to perform calibration?

Steps to reproduce the issue

taking tests/periph_rtc/ as starting point:
add USEMODULE += pm_layered to tests/periph_rtc/Makefile
add #include "pm_layered.h" to main.c
replace main() with this:

int main(void)
{
    struct tm time;
    rtc_get_time(&time);

    print_time("\nwoke up at: ", &time);

    time_t time_sec = mktime(&time);
    time_sec += 1;
    struct tm *alarm_time = gmtime(&time_sec);

    rtc_set_alarm(alarm_time, cb, NULL);

    print_time("setting alarm to: ", alarm_time);

    pm_unblock(0);
    pm_unblock(1);
    pm_unblock(2);
    pm_unblock(3);

    return 0;
}

Expected results

Accuracy near the oscillator specifications (e.g. <20 ppm on nucleo-l476 in my case).

Actual results

Accuracy dependent on application beaviour (duty cycle etc.) and for some use cases in fact abysmal.
Watching pyterms time stamps for the above code gives an error of ~220ms per minute. This translates to 3666 ppm.
Fun fact: this is more than seven times greater than the maximum this chips calibration feature could compensate, if it were implemented ;)

Versions

current master

@MichelRottleuthner MichelRottleuthner added Type: bug The issue reports a bug / The PR fixes a bug (including spelling errors) Area: tests Area: tests and testing framework Discussion: RFC The issue/PR is used as a discussion starting point about the item of the issue/PR Area: timers Area: timer subsystems Area: pm Area: (Low) power management labels Mar 6, 2018
@vincent-d
Copy link
Member

One thing that was really nasty to debug here, came from the fact that rtc_init() isn't actually resetting the RTC_DR and RTC_TR registers. (...) What do you think about using the INITS flag in RTC_ISR to only init the rtc if it wasn't already initialized before?

+1. Resetting RTC_DR and RTC_TR at boot time is not a good idea, but checking if the RTC has been initialized or not before going through rtc_init sounds like a gound idea. Though I don't know if it should be done inside rtc_init or outside (i.e. in cpu_init)

Did you already work on fixes for these issues?

@MichelRottleuthner
Copy link
Contributor Author

MichelRottleuthner commented Mar 6, 2018

Did you already work on fixes for these issues?

Currently I've fixed it by guarding everything in rtc_init with if(!(RTC->ISR & RTC_ISR_INITS)) exept for the EXTI configuration which is lost after shutdown. And additionally replacing rtc_unlock() in rtc_set_alarm with

stmclk_dbp_unlock();
RTC->WPR = WPK1;
RTC->WPR = WPK2;

There may be more (an improved test would show), but with this I can at least get ~12ppm, with calibration even below that.
But as you already said, I also thought that simply adding it to rtc_init() is a thing to discuss.

@stale
Copy link

stale bot commented Aug 10, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want me to ignore this issue, please mark it with the "State: don't stale" label. Thank you for your contributions.

@stale stale bot added the State: stale State: The issue / PR has no activity for >185 days label Aug 10, 2019
@stale stale bot closed this as completed Sep 10, 2019
@aabadie aabadie reopened this Sep 21, 2019
@stale stale bot removed the State: stale State: The issue / PR has no activity for >185 days label Sep 21, 2019
@smlng
Copy link
Member

smlng commented Nov 1, 2019

This sound related to a problem I currently have when using rtc_set_alarm, i.e. it does not fire anymore after a longer interval. In my setup I basically use an extended version of our examples/lorawan to periodically send data via LoRaWAN, for the interval I use rtc_set_alarm as in the example. However, after a couple of minutes or hours the application stops and no data is send anymore.

I added some LED toggling to see where my app is and according to this it hangs right in or after rtc_set_alarm. Same as in the example the alarm callback should send a message to the main thread, which never arrives.

@MichelRottleuthner do you plan to open a PR with your fixes, or how do we proceed - to me this seems a severe problem. Specifically for use-cases as you described, i.e. RTC with low power stuff.

@MichelRottleuthner
Copy link
Contributor Author

I almost forgot this one, good to have that verbose description^^ Yeah we should just open a PR.
@smlng what is your opinion on how to handle the "don't init if already initialized"-case discussed above?

For handling it from within the init function I see the following points:

+no interface change needed (the current documentation is not violated)
+kind of what one would expect from an RTC peripheral
-doesn't allow you to force-init the RTC anymore (not sure why you would need that with our currently
very limited API though)

Meumeu added a commit to Meumeu/RIOT that referenced this issue May 8, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue May 11, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue May 11, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue May 12, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue May 14, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue May 15, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue May 17, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue May 19, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue May 21, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue May 25, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue May 25, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue May 26, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue May 29, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue May 30, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue Jun 2, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue Jun 2, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue Jun 2, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue Jun 5, 2020
Meumeu added a commit to Meumeu/RIOT that referenced this issue Jun 11, 2020
@aabadie aabadie added Platform: ARM Platform: This PR/issue effects ARM-based platforms Area: cpu Area: CPU/MCU ports labels May 20, 2021
@MrKevinWeiss MrKevinWeiss added this to the Release 2021.07 milestone Jun 22, 2021
@MrKevinWeiss MrKevinWeiss removed this from the Release 2021.07 milestone Jul 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: cpu Area: CPU/MCU ports Area: pm Area: (Low) power management Area: tests Area: tests and testing framework Area: timers Area: timer subsystems Discussion: RFC The issue/PR is used as a discussion starting point about the item of the issue/PR Platform: ARM Platform: This PR/issue effects ARM-based platforms Type: bug The issue reports a bug / The PR fixes a bug (including spelling errors)
Projects
None yet
Development

No branches or pull requests

5 participants