Skip to content

概念先知道

  • 看门狗(WDG):像“监工”——程序必须周期性“喂狗”(清零计数器);如果程序卡死没喂,看门狗就复位系统。
  • 计数器/比较值:看门狗时钟持续计数,计到比较值(comp_val)还没喂狗就触发中断或复位。
  • 时钟源:看门狗可以用不同时钟(BCLK/32K/1K/XTAL),例程遍历它们校验实际走时。

例程功能简介

本页对应博流官方 SDK 的 wdg_clksource_check 例程(examples/peripherals/wdg/wdg_clksource_check),演示看门狗定时器与时钟源验证:

  • 看门狗时钟 = 时钟源 /(分频 + 1),例程遍历 BCLK、32K、1K、XTAL 四种时钟源;
  • 对每种时钟源:配置 → 清零计数器 → 启动 → 延时 200ms → 读取计数器值 → 停止,重复 10 次取计数;
  • 通过计数差值可以校验看门狗实际走时是否与理论频率一致。
  • 同族例程examples/peripherals/wdg/):wdg_int(中断模式)、wdg_reset(复位模式)。

操作步骤

1
进入例程目录

本页不需要额外接线。在终端进入 SDK 的看门狗例程目录(前提:已按快速开始(Linux)Windows搭建好环境):

cd examples/peripherals/wdg/wdg_clksource_check
2
编译工程

执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可:

make CHIP=bl616 BOARD=bl616dk
3
烧录固件

用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):

make flash CHIP=bl616 COMX=/dev/ttyUSB0
4
运行验证

打开串口助手(波特率 2000000)。例程依次用 BCLK/32K/1K/XTAL 四种时钟源启动看门狗,每次延时 200ms 后读取计数器值并打印 Delay 200ms, div = x, test x times, cnt: xxx,最后打印 case success.

代码执行流程

例程从启动到运行的完整流程如下(图中的循环箭头表示反复执行):

例程调用的 API 介绍

bflb_device_get_by_name("watchdog0")

获取看门狗外设的设备句柄,BL616/BL618 上名称为 "watchdog0"

参数

  • name:外设名称,固定 "watchdog0"

返回值struct bflb_device_s * 设备句柄(获取失败为 NULL

bflb_wdg_init(wdt, config)

初始化看门狗。struct bflb_wdg_config_s 中:

  • clock_source:时钟源,WDG_CLKSRC_BCLK / WDG_CLKSRC_32K / WDG_CLKSRC_1K / WDG_CLKSRC_XTAL
  • clock_div:分频值,计数时钟 = 时钟源 /(div + 1)
  • comp_val:比较值,例程 0xffff
  • modeWDG_MODE_INTERRUPT 中断模式(超时先产生中断)

参数

  • wdt:看门狗设备句柄
  • config:配置结构体指针

返回值:成功返回 0;失败返回负值错误码

bflb_wdg_reset_countervalue(wdt)

把看门狗计数器清零(即“喂狗”)。

参数

  • wdt:看门狗设备句柄

返回值:无

bflb_wdg_start / bflb_wdg_stop(wdt)

启动/停止看门狗。启动后必须在比较值超时前喂狗,否则触发复位或中断。

参数

  • wdt:看门狗设备句柄

返回值:无

bflb_wdg_get_countervalue(wdt)

读取当前计数器值,用于验证走时。

参数

  • wdt:看门狗设备句柄

返回值:计数器值

完整代码

以下为 wdg_clksource_check/main.c 完整源码,与官方示例(examples/peripherals/wdg/wdg_clksource_check)一致;点灯引脚已适配 Ai-M61/62-32S-Kit 板载 RGB 灯,默认折叠,点击展开:

📜 点击展开 wdg_clksource_check/main.c 完整代码
c
#include "bflb_mtimer.h"
#include "bflb_wdg.h"
#include "board.h"
#include <assert.h>

#if !defined(BL702L)
uint8_t wdg_clk_src_type[] = {
    WDG_CLKSRC_BCLK,
    WDG_CLKSRC_32K,
    WDG_CLKSRC_1K,
    WDG_CLKSRC_XTAL,
};
#else
uint8_t wdg_clk_src_type[] = {
    WDG_CLKSRC_32K,
    WDG_CLKSRC_1K,
    WDG_CLKSRC_XTAL,
};
#endif

int main(void)
{
    uint32_t i = 0;
    uint32_t j = 0;
    uint32_t cnt = 0;

    board_init();
    printf("Timer clksource check\n");

    struct bflb_device_s *wdt;
    wdt = bflb_device_get_by_name("watchdog0");
    assert(wdt != NULL);

    /* watchdog clk = clock_source/(div + 1)*/
    struct bflb_wdg_config_s cfg;
    cfg.clock_source = WDG_CLKSRC_32K;
    cfg.clock_div = 0;
    cfg.comp_val = 0xffff;
    cfg.mode = WDG_MODE_INTERRUPT;

    for (i = 0; i < sizeof(wdg_clk_src_type) / sizeof(wdg_clk_src_type[0]); i++) {
        cnt = 0;

        if (wdg_clk_src_type[i] == WDG_CLKSRC_XTAL) {
            printf("Watchdog Src Clk is XTAL\r\n");
            cfg.clock_div = 199;
        } else if (wdg_clk_src_type[i] == WDG_CLKSRC_32K) {
            printf("Watchdog Src Clk is 32K\r\n");
            cfg.clock_div = 1;
        } else if (wdg_clk_src_type[i] == WDG_CLKSRC_1K) {
            printf("Watchdog Src Clk is 1K\r\n");
            cfg.clock_div = 1;
        }
#if !defined(BL702L)
        else if (wdg_clk_src_type[i] == WDG_CLKSRC_BCLK) {
            printf("Watchdog Src Clk is BCLK\r\n");
            cfg.clock_div = 249;
        }
#endif
        else {
            printf("Other clock, not test.\r\n");
            continue;
        }

        cfg.clock_source = wdg_clk_src_type[i];
        bflb_wdg_init(wdt, &cfg);

        for (j = 0; j < 10; j++) {
            bflb_wdg_reset_countervalue(wdt);
            bflb_wdg_start(wdt);
            bflb_mtimer_delay_ms(200);
            cnt = bflb_wdg_get_countervalue(wdt);
            bflb_wdg_stop(wdt);
            bflb_mtimer_delay_ms(10);
            printf("Delay 200ms, div = %lu, test %lu times, cnt: %lu\r\n", cfg.clock_div + 1, j, cnt);
        }
    }
    printf("case success.\r\n");
    while (1) {
        bflb_mtimer_delay_ms(1500);
    }
}

FAQ

程序反复复位

看门狗启动后没有及时喂狗就会复位。例程是验证用途(启停模式),实际项目要在主循环或空闲任务中周期性调用 bflb_wdg_reset_countervalue 喂狗。

计数器值与理论值不符

计数 = 时钟源频率 /(div + 1)× 时间。先确认对应时钟源实际频率(如 XTAL=40MHz、32K=32768Hz),再核对 clock_div 配置。

如何让看门狗真正复位系统

mode 改为复位模式(如 WDG_MODE_RESET,具体枚举见 bflb_wdg.h),并配置合适的 comp_val 超时时间。

遇到问题?

如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

Released under the MIT License. Build Time 2026-09-11 14:52:23