Timers & PWM
The general-purpose timers drive three everyday jobs: PWM output for LEDs and motors, input capture for measuring incoming signals, and periodic callbacks at a fixed rate. core_pwm / core_timer sit on hal_timer and ll_tim. Use the Core / HAL / LL toggle at the top of the sidebar to switch.
Overview
A timer counts a prescaled clock up to a reload value. From that one mechanism you get a PWM frequency (the reload) and duty (a compare value), a capture timebase, or a periodic interrupt. You pick the timer instance (TIM1, TIM2, TIM15, …); the clock is resolved for you.
500 is 50%. Easy to misread as percent.Two Core surfaces share the hardware: core_pwm (including a Tier 2 core_pwm_duty(pad, …) that resolves the timer from config.json) and the lower-level core_timer for capture and explicit control.
PWM output
Init a timer at the PWM frequency, set a channel’s duty, start. The Tier 2 core_pwm_duty is even shorter when the pad is declared in config.json:
#include "core.h"
core_timer_t pwm;
core_pwm_init(&pwm, TIM2, 2000); // 2 kHz
core_pwm_set(&pwm, 1, 750); // channel 1, 75% (permil)
core_pwm_start(&pwm);
// Tier 2 — timer/channel resolved from config.json:
core_pwm_duty(7, 500); // pad 7, 50%Input capture
Capture latches the counter on each rising edge of an input — measure a pulse’s period or frequency. Init a timebase, arm a channel, read the latched value:
core_timer_t cap;
core_timer_init_freq(&cap, TIM2, 1000); // 1 kHz timebase
core_timer_capture_init(&cap, 1);
core_timer_start(&cap);
uint32_t edge = core_timer_capture_read(&cap, 1);Periodic callbacks
For a fixed-rate ISR — sampling, control loops — use the “every” helper or core_tick_init. The callback fires from the timer interrupt at the period you set:
static void on_tick(void *ctx) { /* runs every 1 ms */ }
core_timer_t t;
core_every_us(&t, TIM6, 1000, on_tick, NULL);
core_every_start(&t);Cross-architecture support
The timebase and PWM are the verified baseline across the family; capture, encoder, and low-power-timer modes vary. The core_timer / core_pwm contract is the same everywhere:
See the implementation status for the full matrix.
API reference
PWM (core_pwm)
void core_pwm_duty(uint8_t pad, uint16_t duty_permil);hal_status_t core_pwm_init(core_timer_t * h, TIM_TypeDef * instance, uint32_t freq_hz);hal_status_t core_pwm_init_clk(core_timer_t * h, TIM_TypeDef * instance, uint32_t pclk_hz, uint32_t freq_hz);void core_pwm_set(core_timer_t * h, uint8_t channel, uint16_t duty_permil);void core_pwm_set_freq(core_timer_t * h, uint32_t freq_hz);void core_pwm_start(core_timer_t * h);void core_pwm_stop(core_timer_t * h);hal_status_t core_pwm_init_pad(core_timer_t * h, uint8_t pad, uint32_t freq_hz);void core_pwm_set_pad(core_timer_t * h, uint8_t pad, uint16_t duty_permil);hal_status_t core_every_us(core_timer_t * h, TIM_TypeDef * instance, uint32_t period_us, hal_callback_t cb, void * ctx);hal_status_t core_every_us_clk(core_timer_t * h, TIM_TypeDef * instance, uint32_t pclk_hz, uint32_t period_us, hal_callback_t cb, void * ctx);void core_every_start(core_timer_t * h);void core_every_stop(core_timer_t * h);Generated from core_pwm.h — tiles@6bbf0d8.
Timer (core_timer)
hal_status_t core_timer_init_freq(core_timer_t * h, TIM_TypeDef * instance, uint32_t freq_hz);hal_status_t core_timer_init_tick(core_timer_t * h, TIM_TypeDef * instance, uint32_t tick_hz);void core_timer_start(core_timer_t * h);void core_timer_stop(core_timer_t * h);void core_timer_set_freq(core_timer_t * h, uint32_t freq_hz);void core_timer_pwm_set(core_timer_t * h, uint8_t channel, uint16_t duty_permil);void core_timer_capture_init(core_timer_t * h, uint8_t channel);uint32_t core_timer_capture_read(core_timer_t * h, uint8_t channel);hal_status_t core_timer_enable_tick(core_timer_t * h, core_callback_t cb, void * ctx);void core_timer_disable_tick(core_timer_t * h);hal_status_t core_tick_init(core_timer_t * h, TIM_TypeDef * instance, uint32_t period_us, core_callback_t cb, void * ctx);Generated from core_timer.h — tiles@6bbf0d8.

