1. Today’s topic
We examine:
timer clock
prescaler and auto-reload
Input Capture
Output Compare
PWM
One-Pulse mode
counter overflow
hardware ADC triggering
timer synchronization
period and pulse-duration measurementThe central idea: when edge timing or pulse duration matters, a hardware timer should capture or generate the event. A FreeRTOS task should process an already recorded timestamp.
2. Why this matters
Traffic-light phases
Input Capture records the edge time in hardware:
input edge
→ CNT copied into CCR
→ ISR receives the saved timestamp
→ input_task analyzes intervalsEven if the ISR runs later, the capture value corresponds to the edge time.
HDR illumination and sync
Poor approach:
gpio_set_level(IR_GPIO, 1);
vTaskDelay(pdMS_TO_TICKS(duration_ms));
gpio_set_level(IR_GPIO, 0);Correct approach:
camera sync edge
→ Input Capture / timer trigger
→ One-Pulse or Output Compare
→ hardware illumination pulseADC + DMA
TIM update/TRGO
→ ADC conversion
→ DMA bufferThis keeps the sample period uniform and independent of the scheduler.
3. Theory
Main registers
timer clock
↓
Prescaler PSC
↓
Counter CNT
├── comparison with ARR → update/overflow
└── comparison with CCRx → Output Compare/PWMFormulae:
f_counter = f_timer / (PSC + 1)
T_tick = 1 / f_counter
f_update = f_timer / ((PSC + 1) × (ARR + 1))A 1 microsecond tick
If f_counter = 1 MHz, then 1 tick = 1 us. This is convenient for measuring 50/100 Hz periods and illumination pulses. But for a 16-bit timer:
65536 us ≈ 65.5 msBeyond that, overflow handling or a 32-bit timer is required.
Input Capture
At an edge:
CCRx = CNTFor the period between rising edges:
uint32_t delta_ticks = current_capture - previous_capture;Unsigned arithmetic works correctly across one wraparound for a 32-bit counter.
Duty cycle
rising1 → falling → rising2
period = rising2 - rising1
high_time = falling - rising1
duty = high_time / periodWithout float:
uint32_t duty_permille = ((uint64_t)high_ticks * 1000ULL) / period_ticks;Input filter
The timer’s digital filter helps against glitches, but adds delay and limits the maximum frequency. It complements circuit design rather than replacing it.
Output Compare
Output Compare generates a hardware event when:
CNT == CCRxIt can set/reset/toggle an output, raise an interrupt, or request DMA. For periodic events, calculate the next compare from the previous compare rather than the current CNT to avoid accumulating drift.
PWM
frequency = f_counter / (ARR + 1)
duty ≈ CCR / (ARR + 1)PWM is generated in hardware without scheduler jitter.
One-Pulse mode
trigger
→ programmable delay
→ output active
→ programmable duration
→ output inactive
→ timer stopsSuitable for an IR flash, reset pulse, camera trigger, or strobe.
Preload
Preload applies new CCR/ARR values only at the update event. This prevents a single incorrect PWM pulse when duty/duration changes in the middle of a period.
4. Common mistakes
- Measuring an edge with a timestamp inside ISR rather than the Capture Register.
- Not checking the timer clock.
- Forgetting PSC + 1 and ARR + 1.
- Not accounting for overflow.
- Using software PWM through vTaskDelay().
- Switching capture polarity too slowly.
- Logging or doing heavy mathematics in a capture callback.
- Treating the digital filter as a substitute for circuit design.
- Changing CCR mid-PWM without preload.
- Scheduling compare from the current CNT and accumulating drift.
5. Practical assignment
Generate PWM and measure it through Input Capture.
- Configure PWM:
frequency = 1 kHz
duty = 30 %- Connect PWM output → capture input.
- Configure the capture timer:
resolution ≈ 1 MHz
capture rising and falling edges- Expected values:
period ≈ 1000 us
high time ≈ 300 us
duty ≈ 30 %
frequency ≈ 1000 Hz- The ISR passes only:
typedef struct {
uint32_t capture;
uint8_t edge;
} timer_capture_event_t;The task calculates period/duty/frequency.
- Add a capture CLI:
capture:
resolution=1000000 Hz
edges=120340
measurements=60170
period=999..1001 us
frequency=1000000 mHz
duty=300 permille
invalid=0
timeouts=06. What to try next
- Add a missing-signal timeout.
- Test 100 Hz, 1 kHz, 10 kHz, and different duty values.
- Inject a glitch and inspect the counter.
- Create HIL: Raspberry Pi/a second MCU generates the signal, ESP32/STM32 measures it.
- Extract the calculation into pulse_meter_core and cover it with host unit tests.
Final checklist for lessons 21-30
Reliability
□ Every fault has source, class, action, and counter. □ Degraded mode is visible through CLI/MQTT/CAN. □ Watchdog is not the first recovery mechanism. □ A fault snapshot is saved before reset.
Configuration and OTA
□ Config has magic/version/size/CRC/generation. □ Defaults and validation exist. □ Old versions can be migrated. □ A/B config exists. □ OTA is confirmed only after self-test. □ LTE/MQTT unavailability does not automatically roll back healthy firmware.
CI/HIL
□ Builds use a pinned Docker/toolchain. □ ELF/MAP/sdkconfig/manifest/SHA256 are retained. □ OTA slot size is controlled. □ Firmware signing is separate from the PR-job. □ HIL tests not only the happy path but also power-cut, rollback, watchdog, and fault paths.
Testability
□ Core modules do not depend on ESP-IDF/STM32 HAL. □ Time is passed as an argument. □ Infinite tasks use a testable step() function. □ The AT parser is tested for fragmentation invariance. □ Fuzzing crashes become regression tests.
RTOS and interrupts
□ A peripheral has a single owner task. □ ISR does not wait for a mutex. □ ISR calls only ...FromISR() API. □ NVIC priorities agree with configMAX_SYSCALL_INTERRUPT_PRIORITY. □ ESP32 interrupts have deliberate core affinity. □ Long processing moves from ISR into a task.
DMA, buffers, and timers
□ A continuous stream has an owner, overrun detection, and CLI stats. □ DMA buffers have explicit ownership. □ STM32H7/F7 cache clean/invalidate are accounted for. □ Timer clock is checked against the clock tree. □ Precise edges are measured through Input Capture. □ PWM/One-Pulse are generated in hardware, not through vTaskDelay().
Exercise
Explain why an ISR-entry timestamp is not equivalent to an Input Capture register value. Describe a test that exposes the difference under interrupt load.
Self-check criteria: Separate edge time from handling time and use a known signal/reference; do not claim accuracy from scheduler timing alone.
Show the supplied answer
The capture register latches the hardware edge time, while an ISR timestamp includes interrupt latency. Apply a known signal and additional interrupt load, compare recorded capture intervals with ISR-entry timing, and inspect variation without heavy logging inside ISR.
Exercise
For the source’s 1 kHz, 30 % PWM exercise, state the expected period and high time, then explain how preload prevents an output glitch during a duty update.
Self-check criteria: Keep the source units and values; verify both measurement results and the update boundary rather than merely observing that output toggles.
Show the supplied answer
The expected period is approximately 1000 us and high time approximately 300 us. Preload defers the new CCR/ARR values until the update event, preventing a partial-period change from producing one malformed pulse.