Timers, PWM and Non-blocking Scheduling
Use clock ticks to schedule work and control duty cycle without freezing the main loop.
A timer is a clock with a programmable alarm. PWM repeats the same timed on/off pattern while your program handles other work.
A timer increments from a peripheral clock, often after a prescaler divides it down. In a simple edge-aligned up-counting configuration, the counter runs from zero through a top value and restarts. The cycle therefore contains top plus one ticks. A compare register changes the output partway through that cycle to create PWM.
Duty cycle describes the fraction of a period spent high. A 25% duty cycle at 1 kHz is high for 0.25 ms and low for 0.75 ms. The pin still switches between logic levels; it does not become a true analog 25%-of-supply source. A load or filter responds to the pulses, and its electrical behavior determines the result.
For slower application scheduling, compare elapsed time with an interval and return to the main loop immediately when the interval has not passed. Unsigned subtraction handles timer wraparound when the types and intervals are appropriate and the check runs often enough. Comparing now with previous + interval can fail around rollover.
Choose between resetting the reference to now and advancing it by a fixed interval. Resetting to now avoids bursts of catch-up work but can accumulate timing drift. Advancing by the interval preserves the intended schedule, but you need an explicit policy for missed deadlines. Precise waveform edges or sampling triggers should normally use appropriate hardware timers.
Use clock ticks to schedule work and control duty cycle without freezing the main loop.
Core ideas
- Frequency and duty cycle are independent settings.
- Non-blocking scheduling keeps the main loop responsive.
- Timer equations depend on the actual counting mode.
Simple edge-aligned PWM: f = fclock / (prescaler × (TOP + 1))Duty = high time / periodWork through a practical example
1A 16 MHz timer clock divided by 64 becomes 250 kHz.
2Choose TOP = 249 to get 250 ticks per period.
3PWM frequency is 250000/250 = 1000 Hz.
4A 50% setting uses approximately half those ticks high; consult the timer's compare and endpoint behavior.
Using delay for every task or applying an edge-aligned frequency equation to a center-aligned timer.
Code example
Arduino C++ — complete sketch; LED toggles every 500 ms
// Arduino sketch: built-in LED, no delay()
unsigned long previous = 0;
bool on = false;
void setup() { pinMode(LED_BUILTIN, OUTPUT); }
void loop() {
const unsigned long now = millis();
if (now - previous >= 500UL) {
previous = now;
on = !on;
digitalWrite(LED_BUILTIN, on ? HIGH : LOW);
}
// Other short tasks can run here.
}Read and run this example in the stated environment. Code is displayed here; it does not execute in your browser.