Project 5.06 Section 5 ⚡ Embedded Relevance: Critical Hardware Timers SysTick vTaskDelay Busy-Wait Low Power WFI

5.06 Software Busy-Wait Delays vs Hardware SysTick Timers & RTOS vTaskDelay

Executive Summary: Exploring loop countdowns and delays. We demonstrate why software busy-wait loops (for(volatile int i=0...)) waste battery power and jitter across compiler optimization levels, and replace them with hardware SysTick timers and RTOS sleep delays (WFI).

💻 1. Annotated Source Code

#include <iostream>
using namespace std;

void countDownFrom(int num);
int sumValues(int num);

int main() {

	//countDownFrom(10);

	//for (int i = 10; i >= 0; i--) {
	//	cout << i << endl;
	//}

	int totalSum = sumValues(10);
	cout << "The sum is " << totalSum << endl;

	return 0;
}

void countDownFrom(int num) {
	if (num >= 0) {
		cout << num << endl;
		countDownFrom(num - 1);
	}
}//end countDownFrom

int sumValues(int num) {
	if (num > 1) {
		return num + sumValues(num - 1);
	}
	return num;   //base case
}

📐 2. Architecture & UML Class Model

📐 Recursive Countdown vs Iterative SysTick Loop Model
+ Public - Private # Protected
<<compilation-unit>> CountdownEngine Timer Pipeline
(none / stateless)
+countDownRecursive(num: int32_t) : void
+countDownIterative(num: int32_t) : void

📚 3. Core C++ Concepts Deep-Dive

1. Count-Down Loop Structures

Loops counting down to zero (while (n > 0) --n;) often generate more efficient assembly on ARM processors because comparing against zero is handled automatically by CPU condition flags (SUBS instruction).

⚡ 4. Embedded Systems & Hardware Reality

1. The Evils of Software Busy-Wait Loops

Software spin loops (for (int i=0; i<100000; ++i);):

  • Are Optimized Away: Without volatile, the compiler deletes empty loops entirely under -O2 or -O3.
  • Waste Battery: The CPU burns maximum active current (e.g. 20mA) instead of sleeping.
  • Are Non-Deterministic: Delay duration changes drastically if CPU clock frequency or compiler flags change.

2. Hardware SysTick & RTOS vTaskDelay()

Production firmware uses hardware timer interrupts (SysTick) and puts the CPU to sleep using Wait For Interrupt (WFI), reducing current draw by 99%.

💡 5. Production-Ready Embedded Refactoring

Non-blocking hardware timer delay:

💡 Production-Ready Refactor
#include <cstdint>

extern volatile uint32_t g_system_ticks_ms; // Incremented by SysTick_Handler

void delay_ms(uint32_t ms) noexcept {
    uint32_t start = g_system_ticks_ms;
    while ((g_system_ticks_ms - start) < ms) {
        __asm volatile("wfi"); // Wait For Interrupt: Sleep CPU until next timer tick!
    }
}

📝 Knowledge Verification Quiz

Test your understanding of the C++ concepts and embedded microcontroller trade-offs covered in this guide. Click any option for instant feedback.

Q1. Why is an empty software delay loop (for(int i=0; i<10000; i++)) dangerous in production code?
A The optimizing compiler will delete the loop entirely, resulting in zero delay
B It causes an immediate memory leak
C It permanently disables interrupts
D It reboots the microcontroller
Detailed Explanation: Because the loop has no observable side effects, modern optimizing compilers (-O2/-O3) completely remove empty loops.
Q2. What does the ARM assembly instruction 'WFI' (Wait For Interrupt) do?
A Puts the CPU core into a low-power sleep state until the next hardware interrupt fires, drastically reducing current draw
B Resets the CPU stack pointer
C Waits for a serial character from UART
D Disables all hardware timers
Detailed Explanation: WFI suspends CPU execution and clocks until an interrupt arrives, dropping current consumption to microamps.
Q3. Why does counting down to zero (while(n-- > 0)) often produce smaller assembly code on ARM processors than counting up?
A ARM arithmetic instructions (SUBS) update the zero flag (Z) automatically, eliminating separate comparison (CMP) instructions
B ARM cannot count upwards
C The stack only allows decrements
D Down loops use 16-bit registers
Detailed Explanation: The SUBS instruction subtracts and sets condition flags simultaneously; a branch instruction (BNE) can immediately test the zero flag without an extra CMP instruction.
Q4. In FreeRTOS, what is the advantage of vTaskDelay(pdMS_TO_TICKS(100)) over a busy loop?
A It yields the CPU to lower-priority tasks and puts the current task into the Blocked state until the delay expires
B It overclock the CPU
C It disables task scheduling
D It formats the heap
Detailed Explanation: vTaskDelay blocks the calling task, allowing other application tasks to execute while consuming zero CPU cycles.