Concepts First
- Timer (TIMER): the chip's "stopwatch". It counts at a configured rate and fires an interrupt when it reaches a compare value.
- Divider (clock_div): slows the high-frequency clock before counting. The example divides 40 MHz by 40 (39+1) to get 1 MHz; compare value 64000 → one interrupt every 64 ms.
- Compare interrupt: generated when the counter reaches a compare channel (comp0/1/2) value — used for periodic tasks, PWM time bases, and timing.
Example Overview
This page is based on the timer_int example in the official Bouffalo SDK (examples/peripherals/timer/timer_int), which demonstrates the hardware timer compare interrupt:
- Timer clock = XCLK / (divider + 1); on BL616/BL618 the XCLK is 40 MHz and the example uses divider 39, giving a 1 MHz counting clock;
- With
comp0_val = 64000, the compare interrupt fires every 64000 counts (about 64 ms); - The ISR prints
timer0 comp0 trigger, while the main loop printshelloevery 1.5 s to show the program is still running. - Sibling examples (
examples/peripherals/timer/):timer_capture(input capture),timer_clksource_check(clock-source check),timer_gpio_clock(GPIO clock output).
Operation Steps
No external wiring is needed for this page. Open a terminal and enter the timer example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):
cd examples/peripherals/timer/timer_intRun the build command. The Ai-M62 (BL616) and Ai-M61 (BL618) belong to the same series, so both use bl616:
make CHIP=bl616 BOARD=bl616dkConnect the board with a USB cable, hold the BOOT button (IO2 on the Ai-M61-32S-Kit), briefly press EN/RST to enter download mode, then flash (replace the serial port with the one on your computer):
make flash CHIP=bl616 COMX=/dev/ttyUSB0Open a serial tool (baud rate 2000000). The serial port prints timer0 comp0 trigger about every 64 ms (timer compare interrupt), and the main loop prints hello every 1.5 s.
Code Execution Flow
The complete execution flow from startup to running is shown below (loop arrows mean repeated execution):
APIs Used by the Example
bflb_device_get_by_name("timer0")
Gets the timer device handle; this example uses timer0.
Parameters:
name: device name string, e.g."timer0"
Return: struct bflb_device_s * device handle
bflb_timer_init(timer, config)
Initializes the timer. Key fields of struct bflb_timer_config_s:
counter_mode: counting mode,TIMER_COUNTER_MODE_PROLOADreloads after a matchclock_source: clock source,TIMER_CLKSRC_XTALuses the crystal clockclock_div: divider; counting clock = XCLK / (clock_div + 1), use39on BL616/BL618trigger_comp_id: trigger compare channel,TIMER_COMP_ID_0comp0_val/comp1_val/comp2_val: match values of the three compare channels (this example only uses comp0)preload_val: reload value,0
Parameters:
timer: timer device handleconfig: pointer to thestruct bflb_timer_config_sconfig
Return: 0 on success; negative error code on failure
bflb_irq_attach(timer->irq_num, isr, NULL)
Registers the timer interrupt callback and enables the interrupt (bflb_irq_enable).
Parameters:
irq_num: timer interrupt number,timer->irq_numisr: callback function, e.g.void isr(int irq, void *arg)
Return: none
bflb_timer_start(timer)
Starts the timer. The timer only runs after this function is called.
Parameters:
timer: timer device handle
Return: 0 on success; negative error code on failure
bflb_timer_get_compint_status(timer, comp_id)
Reads the interrupt status of a compare channel to know which channel fired.
Parameters:
timer: timer device handlecomp_id: compare channel,TIMER_COMP_ID_0/1/2
Return: true fired; false not fired
bflb_timer_compint_clear(timer, comp_id)
Clears the interrupt flag of a compare channel. It must be called in the ISR, otherwise the interrupt keeps firing.
Parameters:
timer: timer device handlecomp_id: compare channel number
Return: none
Complete Code
The complete source below matches the effect described on this page. It is based on the official example (examples/peripherals/timer/timer_int); the LED pins are adapted to the Ai-M61/62-32S-Kit onboard RGB LED. Collapsed by default, click to expand:
📜 Click to expand timer_int/main.c full code
#include "bflb_mtimer.h"
#include "bflb_timer.h"
#include "board.h"
#define TEST_TIMER_COMP_ID TIMER_COMP_ID_0
struct bflb_device_s *timer0;
struct bflb_device_s *timer1;
void timer0_isr(int irq, void *arg)
{
bool status = bflb_timer_get_compint_status(timer0, TIMER_COMP_ID_0);
if (status) {
bflb_timer_compint_clear(timer0, TIMER_COMP_ID_0);
printf("timer0 comp0 trigger\r\n");
}
status = bflb_timer_get_compint_status(timer0, TIMER_COMP_ID_1);
if (status) {
bflb_timer_compint_clear(timer0, TIMER_COMP_ID_1);
printf("timer0 comp1 trigger\r\n");
}
status = bflb_timer_get_compint_status(timer0, TIMER_COMP_ID_2);
if (status) {
bflb_timer_compint_clear(timer0, TIMER_COMP_ID_2);
printf("timer0 comp2 trigger\r\n");
}
}
void timer1_isr(int irq, void *arg)
{
bool status = bflb_timer_get_compint_status(timer1, TIMER_COMP_ID_0);
if (status) {
bflb_timer_compint_clear(timer1, TIMER_COMP_ID_0);
printf("timer1 comp0 trigger\r\n");
}
status = bflb_timer_get_compint_status(timer1, TIMER_COMP_ID_1);
if (status) {
bflb_timer_compint_clear(timer1, TIMER_COMP_ID_1);
printf("timer1 comp1 trigger\r\n");
}
status = bflb_timer_get_compint_status(timer1, TIMER_COMP_ID_2);
if (status) {
bflb_timer_compint_clear(timer1, TIMER_COMP_ID_2);
printf("timer1 comp2 trigger\r\n");
}
}
int main(void)
{
board_init();
printf("Timer basic test\n");
/* timer clk = XCLK/(div + 1 )*/
struct bflb_timer_config_s cfg0;
cfg0.counter_mode = TIMER_COUNTER_MODE_PROLOAD; /* preload when match occur */
cfg0.clock_source = TIMER_CLKSRC_XTAL;
cfg0.clock_div = 39; /* for bl616/bl618dg/bl618dg is 39, for bl702 is 31 */
cfg0.trigger_comp_id = TEST_TIMER_COMP_ID;
cfg0.comp0_val = 64000; /* match value 0 */
cfg0.comp1_val = 0xFFFFFFFF; /* match value 1 */
cfg0.comp2_val = 0xFFFFFFFF; /* match value 2 */
cfg0.preload_val = 0; /* preload value */
#if 0
struct bflb_timer_config_s cfg1;
cfg1.counter_mode = TIMER_COUNTER_MODE_PROLOAD;
cfg1.clock_source = TIMER_CLKSRC_XTAL;
cfg1.clock_div = 39; /* for bl616/bl618dg/bl618dg is 39, for bl702 is 31 */
cfg1.trigger_comp_id = TEST_TIMER_COMP_ID;
cfg1.comp0_val = 1000000; /* match value 0 */
cfg1.comp1_val = 1500000; /* match value 1 */
cfg1.comp2_val = 2500000; /* match value 2 */
cfg1.preload_val = 0; /* preload value */
#endif
timer0 = bflb_device_get_by_name("timer0");
// timer1 = bflb_device_get_by_name("timer1");
/* Timer init with default configuration */
bflb_timer_init(timer0, &cfg0);
// bflb_timer_init(timer1, &cfg1);
bflb_irq_attach(timer0->irq_num, timer0_isr, NULL);
// bflb_irq_attach(timer1->irq_num, timer1_isr, NULL);
bflb_irq_enable(timer0->irq_num);
// bflb_irq_enable(timer1->irq_num);
/* Enable timer */
bflb_timer_start(timer0);
// bflb_timer_start(timer1);
printf("case success.\r\n");
while (1) {
bflb_mtimer_delay_ms(1500);
printf("hello\n\r");
}
}FAQ
The timer0 comp0 trigger rate is wrong
The period is determined by the divider and compare value: period = (clock_div + 1) × comp0_val / XCLK. The example gives 40 × 64000 / 40 MHz ≈ 64 ms; change comp0_val to change the period.
Only hello is printed, no comp trigger
Make sure bflb_timer_start(timer0) is called and the interrupt is enabled (bflb_irq_enable); if you changed clock_div, note that XCLK is 40 MHz on BL616/BL618 — do not copy the 32-divider example for other chips.
Abnormal behavior because the flag is not cleared
Call bflb_timer_compint_clear for every fired compare channel in the ISR, otherwise the interrupt re-enters repeatedly.
Have questions?
For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

