2023-10-31 06:43 AM
I have three STM32WB55 applications:
A) is a debugging application that's only ever flashed directly and standalone using the STM32CubeProgrammer CLI.
B) is a bootloader with integrated FUOTA functionality.
C) is my "actual" application. It can be compiled in two configurations: as standalone (which I will refer to as "Ca", as in "C alone"), or to be started by B (referred to as "Cb", as in "C w/ bootloader")
C has a sophisticated debug interface, where I can send commands as bytes via UART and get a reply back via UART. One of those commands is "Echo your current RTC time every 500 milliseconds for 5 seconds" (timing the 500ms is done by FreeRTOS/osDelay).
But the debug command exposes a weird behavior:
Flash and run A, then flash and run B+Cb, and the debug command works as intended. It prints the 10 values in 500ms intervals, and the RTC time progresses by 500ms for every timestamp received.
But then I tell C to launch B in FUOTA mode. I let the update time out, which means nothing is changed about the flashed application binaries, and Cb is started again. When I repeat the debug command, the results are still printed in 500ms intervals (so FreeRTOS ticks are still accurate), but the timestamp only progresses by ~31ms.
So depending on which other application last ran, the RTC seems to be going at the correct speed, or only 1/16th the speed. No combination of flashing Ca, or B+Cb, or stopping/resetting the device fixes this. Only if I flash and run A once, it returns back to normal. Thereafter (as before) flashing and simply launching B+Cb poses no problem.
All that said, I assume that A contains some setup code that configures the RTC in the way C expects and needs in order to function properly, while B has some code that changes that setup, but only the in FUOAT part, not the common launch path itself. And C has incomplete setup code, so unlike A it doesn't change the config according to its needs.
Unfortunately, neither A nor B were developed by me, and B didn't even use CubeMX and therefore it has no clock configuration diagram to look at and compare. A does use CubeMX, but A and C have identical clock configuration diagrams, apart from the RNG bit which C uses but A doesn't (marked red). That's also why I assumed C's clock configuration would be correct, but apparently it isn't.
Clock map A:
Clock map C:
I guess my only chance to find out what A does right and B does "wrong" and C does not do at all would be to look at the code, or certain registers or variables at run time. Can someone point me to the part of the setup code and the variable names that I should check, given the behavior described above?
2023-11-13 09:10 AM - edited 2023-11-13 09:11 AM
I think I have narrowed down the issue to the AsynchPrediv and SynchPrediv values in MX_RTC_Init:
Before calling HAL_RTC_Init, the hrtc->Instance.PRER value is 1015807 (0x000F7FFF). This is probably the "default" value when the predividers have not been configured. The value is equivalent to AsynchPrediv being 15 (0x000F) and SynchPrediv being 32767 (0x7FFF, maximum).
Application "A" sets AsynchPrediv to 127 and SynchPrediv to 255, then calls HAL_RTC_Init. Afterwards, PRER is 8323327 (0x007F00FF), which is correct.
Application "C" sets AsynchPrediv to 15 and SynchPrediv to 2047, but HAL_RTC_Init does not change the value of PRER.
The reason appears to be that A still uses STM32Cube FW 1.10.0, whereas C uses version 1.15.0+.
In version 1.12.0, the following change was made: "Improve HAL_RTC_Init function to avoid initialization if already done".
Upon debugging into HAL_RTC_Init, I can see that the function skips setting the PRE register precisely because of that added check. The check reads if (__HAL_RTC_IS_CALENDAR_INITIALIZED(hrtc) == 0U) which resolves to looking up 0x10 in hrtc->Instance.ISR which appears to be 23 (0x10111) before entering MX_RTC_Init.
In other words - the PRER value is wrong, both applications A and C adjust them, but only A succeeds because in C the newer STM32Cube version means initialization is skipped.
What can I do to fix that?
2023-12-06 01:34 AM
Hello @LWChris ,
The __HAL_RTC_IS_CALENDAR_INITIALIZED macro is used to check if the RTC has already been configured. The RTC registers retain their values while powered up, even on a reset (while Vbat or Vdd are up).
One point you mentioned is the reset state of PRER register, on WB series, it should be 0x007F 00FF.
-> Asynchronous prescaler factor = 0x7F
-> Synchronous prescaler factor = 0xFF
Which allow a 1 Hz calendar update (with LSE as input clock of RTC at 32768 Hz) (32768 / ( (Asynchronous prescaler +1) * (Synchronous prescaler +1)).
I would say that you obtain the 0x000F7FFF at the start, because you already have init the RTC.
If you want to reconfigure the RTC, you can use the __HAL_RTC_IS_CALENDAR_INITIALIZED macro to check if it has already been initialized. If it has been initialized, you can use the HAL_RTC_DeInit function to reset all the RTC registers (except the backup register) to their default values before calling HAL_RTC_Init again to reconfigure the RTC.
Best regards
2023-12-06 02:57 AM
Hello @Victor_D
I am afraid the problem is in the bootloader/OTA updater (B):
since B doesn't use the calendar at all, its HAL_RTC_Init configures PRER to 0x000F7FFFF. As I take it from the source code comments, this should be the configuration with the least energy consumption and is probably why it was chosen that way.
The tricky bit is that under normal circumstances, this would be okay:
B would set the PRER "incorrectly" but not set the DR/TR. Later, C could change PRER, and then receive DR/TR and everything was working. If went into the OTA of B, its code would run through B's HAL_RTC_Init, but after the update, C's HAL_RTC_Init would run and override the wrong PRER values, even if the calendar was still initialized.
Now, as the newer version of C uses the latest STM32Cube FW, we come from a correctly configured PRER into the updater B which uses the old Cube FW. So B can "destroy" PRER even if the calendar was initialized, but newer versions of C can't "fix" PRER again because now changing PRER while date/time is set is prohibited. Tough luck.
I have found a workaround though:
I check ISR and PRER, and if PRER is wrong but the calendar is initialized, I do all the steps manually that would've been done inside the guarded section of HAL_RTC_Init. This isn't great since I basically duplicate the code of HAL_RTC_Init in the user code, but it fixes the problem.
One question I have, though: you say the default value of PRER is 0x007F00FF, so A 127, S 255, which enables 1s calendar and should work (128*256 = 32768). C is requesting the current time (including SSR) multiple times per second, so it is setting PRER to 0x000F7FFFF (A 15, S 2047, 16*2048 = 32768). It was my understanding that this should provide the best accuracy/resolution for SSR, at the expense of power consumption. Is my understanding of the trade-off correct here, or did I get that wrong and 127/255 should be used in all cases?
2023-12-06 03:13 AM
@LWChris ,
Ok I didn't understand your set up. Good to hear you found a workaround.
For the power consumption, accordingly to the AN4759 (Using the hardware real-time clock (RTC) and the tamper management unit (TAMP) with STM32 microcontrollers - Application note) :
STM32WB serie have a RTC2.
Have a good day.
2023-12-06 03:34 AM
Yes, that's what I found, but also section 2.1.4 says:
But how do I chose PREDIV_A? I understand that (PREDIV_A + 1) * (PREDIV_S + 1) must equal RTCCLK. That's why 127/255 of A works, and 15/2047 of C does as well, but obviously not 15/32767 of B.
But what condition does PREDIV_A need to meet in order to have an RTC accuracy of 1ms, 10ms, 500ms, etc.? How do I calculate PREDIV_A from the needed accuracy?
2023-12-06 05:05 AM
The RTC accuracy depends on your input clock and his own precision. The RTC allow you to do a smooth calibration, if needed. (ref section 29.4.11 "RTC smooth digital calibration" of rm0434).
To obtain the 1 Hz input to the calendar, you can use PREDIV_A = 127 and PREDIV_S = 255. Alternatively, you can use PREDIV_A = 15 and PREDIV_S = 2047, but this will impact consumption and RTC_SSR.
The sub second register (SSR) is updated using clk_apre = 32768 / (PREDIV_A+1).
With PREDIV_A = 127 you add 1 to the sub second register each 3.9 ms.
With PREDIV_A = 15 you add 1 to the sub second register each 0.5 ms. This allows a better SSR precision.
(And with PREDIV_A = 31 you add 1 to the sub second register each 0.97 ms. Which is the config the closer to the ms).
The RTC_SSR is used to configure alarm with the millisecond precision.
Victor
2023-12-06 08:09 AM
Thank you, @Victor_D. So I assume these are all the valid configurations for PREDIV_A and PREDIV_S then, alongside the update intervals of SSR.
So I could theoretically use 31/1023, and would still have a 1 ms precision.
Do you know how power consumption scales with PREDIV_A? Is it linear? Does going from 15 to 31 effectively halve the consumption of the RTC component?
Chris
2023-12-07 06:54 AM
Hello @LWChris,
Yes, the 31/1023 configuration is particularly useful when SSR register is used.
After conducting some tests on my STM32WB55RG board using a basic example that sets an alarm to wake it up from standby mode, I found that the difference between the 0x7F/0xFF and 0x00/0X7FFF configurations can result in a power consumption difference of 250-300 nA (at 3.3 V). From my tests, I observed that this difference is not linear, with a difference of approximately 100 nA between the 0X01/0X3FFF and 0X00/0X7FFF configurations.
Victor