I have just found and fixed a nasty bug that was slowing down main program loop when using Arduino Zero.
The problem was in the watchdog system. If you don't have experience with processor watchdog, I'll make a short intro: A watchdog is a countdown timer that once it reaches zero causes a processor reset. That means you have to periodically reset the counter so it never reaches zero. And periodically resetting means calling it in some kind of a loop. If this loop freezes, the watchdog timer will eventually reach zero and reset the processor, which is what it is supposed to do – an automatic reset when your program freezes or enters an infinite loop.
A watchdog can use a dedicated oscillator. In the case of Arduino Zero and my sketch, it is the ultra-low power internal oscillator running at 32.768 kHz. And because it is very slow, any write to a system that uses it – such as the reset of the watchdog timer – needs a synchronization with the main CPU clock, which runs at 48 MHz on Arduino Zero.
I was wrongly assuming this synchronization was fast, whereas it was not, governed by the speed of the slower clock. My code made a blocking wait for this synchronization:
WDT->CLEAR.reg = WDT_CLEAR_CLEAR_KEY; /* reset watchdog timer */
while(WDT->STATUS.bit.SYNCBUSY){} /* wait for sync */
A stupid mistake that was, fortunately, easy to find and fix to a non-blocking call:
if(WDT->STATUS.bit.SYNCBUSY == false) /* synchronization is not busy, meaning it is synchronized */
{
WDT->CLEAR.reg = WDT_CLEAR_CLEAR_KEY; /* reset watchdog timer */
}
This call will only reset timer if the previous reset was already synchronized. There. Fixed.
The performance gain? Tremendous. Previously, I was getting around 100 measurements per second and 400 turnarounds of the main loop, now I am at 237 measurements per second with 12500 (!) main loops per second.
So today's lesson is clear: Don't make blocking calls if you don't have to.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.
Hi, I have a question. how do I reset the watchdog timer in the loop(). I am using the wtachdog timer to check if the code gets stuck for x sec and then reset the micro. If not stuck, then reset the watchdog timer to 0. Can you please let me know how to do that?
Thank you!
Are you sure? yes | no
Well, there is Watchdog_Reset function directly in MightyWattR3.ino and it is already called in the loop(). If you change the static linkage to external linkage, you can then use this function anywhere in the code. Maybe put it in a separate file (Watchdog.cpp / Watchdog.h) to keep it tidy.
Are you sure? yes | no