Blog:

The story of an embedded lullaby

Published on Mar 04, 2019
By Terry / Senior System Design Engineer

Some time ago, a thought occurred to me: to save power on an embedded microcontroller, we try to let it sleep whenever possible – but it is rather hard to find out what percentage of time (per second) it is asleep. Surely there must be an easy way to find out.

First it would require a means to measure time, regardless of the chosen microcontroller clock frequency. A likely candidate is a ‘cycle count’ register, in here the microcontroller counts the amount of clock cycles which have passed since start-up on any given moment in time. This usually is an unsigned integer value which wraps around. On an ARM Cortex-M4 it is in the DWT (Data Watchpoint and Trace) unit. From this we can use the CYCCNT (Cycle Count) register.

To let the microcontroller sleep a common method: WaitForInterrupt is used. Essentially this puts the microcontroller in a light sleep mode – waking when an interrupt occurs. Then the microcontroller resumes processing. This instruction is usually put at the end of a main processing loop. The general idea here is that the microcontroller processes its work, then enters the WFI (WaitForInterrupt or sleep) mode. When new work to process arrives, the microcontroller is to wake up, process the work and return to sleep.

By creating a wrapper for the WaitForInterrup method, we can measure the CYCCNT register right before entering sleep and right after waking, meaning we can measure the time spent awake – and with this the time spent asleep. The only caveat here is that when the microcontroller is awoken by an interrupt, it gets processed before control is handed back to our measurement code which resides right after the sleep method. This in effect counts the time spend in the interrupt(s) as sleep time – not as what is wanted.

To count this time as wake time, we perform a little trick by disabling interrupts globally right before entering sleep, and restore them after waking, after measuring the CYCCNT register. Only then are global interrupts restored. The effect is that the microcontroller still wakes due to the interrupt(s) – but now the interrupt(s) are not yet handled – control remains in the measurement function until the global interrupts are restored. After they are restored the interrupt routine(s) executes. By delaying the restore of global interrupts shortly, this small window of time can be used to correct our measurement administration, allowing the interrupt execution time to be measured as time spend awake – exactly as we want.

To complete the functionality, the wrapping of the CYCCNT value is taken into account. If we use the SystemClock (a constant value indicating the actual processor operating frequency) it is possible to calculate the time passed in seconds. By capturing the measured data into a structure and flagging an update is available each second, the main loop can check for an update and make use of the calculated values in a more meaningful way. Assert when the percentage awake reaches a maximum threshold, print the load to log file, etc.

To summarize, by wrapping the WaitForInterrupt functionality into a custom function we can add a little bit of measurement code which calculates the percentage the microcontroller is awake (regardless of the processor operating frequency). The calculated numbers can be used in the main loop for various purposes. The overhead introduced is rather small. In addition, since we measure this per seconds, the number of times the main loop iterates can be counted as well.

A working example for a STM32F407G discovery kit can be found here:
https://github.com/tlouwers/STM32F4-DISCOVERY/tree/master/utility/CpuWakeCounter

ARM – DWT functional description:
http://infocenter.arm.com/help/topic/com.arm.doc.ddi0439b/BIIFBHIF.html

ARM – Wait For Interrupt – WFI description:
http://infocenter.arm.com/help/topic/com.arm.doc.ddi0360e/CHDEEIJB.html