概念先知道
- 定时器(TIMER):芯片里的“秒表”,按设定频率计数,计到设定值(比较值)就触发一次中断。
- 分频(clock_div):把高频时钟“减速”后再计数。例程用 40MHz 时钟分频 40(39+1)得到 1MHz,比较值 64000 → 每 64ms 触发一次。
- 比较中断:计数器到达比较通道(comp0/1/2)的设定值时产生的中断,常用于周期任务、PWM 时基、计时。
例程功能简介
本页对应博流官方 SDK 的 timer_int 例程(examples/peripherals/timer/timer_int),演示硬件定时器的比较中断功能:
- 定时器时钟 = XCLK /(分频 + 1),BL616/BL618 上 XCLK 为 40MHz,例程分频 39,得到 1MHz 计数时钟;
- 比较值
comp0_val = 64000,即每计数 64000 次(约 64 毫秒)触发一次比较中断; - 中断回调中打印
timer0 comp0 trigger,同时主循环每 1.5 秒打印hello表示程序仍在运行。 - 同族例程(
examples/peripherals/timer/):timer_capture(输入捕获)、timer_clksource_check(时钟源校验)、timer_gpio_clock(GPIO 时钟输出)。
操作步骤
本页不需要额外接线,直接使用板载资源。在终端进入 SDK 的定时器例程目录(前提:已按快速开始(Linux)或Windows搭建好环境):
cd examples/peripherals/timer/timer_int执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可:
make CHIP=bl616 BOARD=bl616dk用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):
make flash CHIP=bl616 COMX=/dev/ttyUSB0打开串口助手(波特率 2000000),可以看到:串口大约每 64 毫秒打印一次 timer0 comp0 trigger(定时器比较中断),同时主循环每 1.5 秒打印一次 hello。
代码执行流程
例程从启动到运行的完整流程如下(图中的循环箭头表示反复执行):
例程调用的 API 介绍
bflb_device_get_by_name("timer0")
获取定时器外设的设备句柄,本例使用 timer0。
参数:
name:外设名称字符串,如"timer0"
返回值:struct bflb_device_s * 设备句柄
bflb_timer_init(timer, config)
按配置初始化定时器。struct bflb_timer_config_s 中关键字段:
counter_mode:计数模式,TIMER_COUNTER_MODE_PROLOAD表示匹配后自动重载clock_source:时钟源,TIMER_CLKSRC_XTAL使用晶振时钟clock_div:分频值,计数时钟 = XCLK /(clock_div + 1),BL616/BL618 填39trigger_comp_id:触发比较通道,TIMER_COMP_ID_0comp0_val/comp1_val/comp2_val:三个比较通道的匹配值,本列程只使用 comp0preload_val:重载初值,0
参数:
timer:定时器设备句柄config:struct bflb_timer_config_s配置结构体指针
返回值:成功返回 0;失败返回负值错误码
bflb_irq_attach(timer->irq_num, isr, NULL)
注册定时器中断回调函数,并把中断使能(bflb_irq_enable)。
参数:
irq_num:定时器中断号,使用timer->irq_numisr:回调函数,形如void isr(int irq, void *arg)
返回值:无
bflb_timer_get_compint_status(timer, comp_id)
查询指定比较通道的中断状态,用于判断是哪个通道触发了中断。
参数:
timer:定时器设备句柄comp_id:比较通道,TIMER_COMP_ID_0/TIMER_COMP_ID_1/TIMER_COMP_ID_2
返回值:true 该通道已触发;false 未触发
bflb_timer_compint_clear(timer, comp_id)
清除指定比较通道的中断标志,中断回调中必须调用,否则中断会反复触发。
参数:
timer:定时器设备句柄comp_id:比较通道号
返回值:无
完整代码
以下为 timer_int/main.c 完整源码,与官方示例(examples/peripherals/timer/timer_int)一致;点灯引脚已适配 Ai-M61/62-32S-Kit 板载 RGB 灯,默认折叠,点击展开:
📜 点击展开 timer_int/main.c 完整代码
#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
打印的 timer0 comp0 trigger 频率不对
触发周期由分频与比较值决定:周期 =(clock_div + 1)× comp0_val / XCLK。例程为 40 × 64000 / 40MHz ≈ 64ms;修改 comp0_val 即可改变周期。
只打印 hello 没有 comp 触发信息
确认 bflb_timer_start(timer0) 已调用、中断已使能(bflb_irq_enable);如果修改过 clock_div,注意 BL616/BL618 上 XCLK 为 40MHz,不要照搬其他芯片的 32 分频示例。
中断里没有清除标志导致异常
在中断回调中,对每个触发的比较通道都要调用 bflb_timer_compint_clear 清除标志,否则会反复进入中断。
遇到问题?
如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

