Skip to content

概念先知道

  • 断言(Assert):程序里“我认为这里一定成立”的检查;如果条件为假,说明逻辑出问题了,程序会进入错误处理。
  • 调试宏(DBG_*):打印变量名和值、数组、hexdump、布尔结果的宏,会返回传入的表达式结果,可以包在任何表达式的外面,不影响原逻辑。
  • 参数 / 函数断言_ASSERT_*_PARAM 面向参数校验,_ASSERT_*_FUNC 面向内部逻辑检查;失败时都会调用 error_handler
  • error_handler:断言失败后的处理函数,SDK 提供弱默认实现,例程里重定义它打印 error handler(测试用途,正式代码一般让它停住,避免带病运行)。

例程功能简介

本页对应博流官方 SDK 的 log_dbg_assert 例程(examples/log_dbg_assert),演示调试打印宏与断言宏的用法

  • DBG_VALUE:自动按类型打印变量名与值(int、字符串、指针等);
  • DBG_HEXDUMP / DBG_ARRAY:hexdump 打印 / 数组打印;
  • DBG_BOOL:把表达式当布尔值打印真/假;
  • _ASSERT_PARAM / _ASSERT_FUNC / _ASSERT_TRUE / _ASSERT_FALSE / _ASSERT_ZERO / _ASSERT_EQUAL(各有 _PARAM_FUNC 版本):断言表达式为真/假/零/相等;
  • LOG_F/E/W/I/D/TLOG_RF/RE/RW/RI/RD/RT:分级日志及其 Raw(不带额外格式)版本。
  • 该例程关闭了 CONFIG_BFLB_LOG,使用的是经典 log.h 日志体系。

注意

例程的 error_handler 仅为测试用途:断言失败后只打印一行就继续运行。正式项目请按注释说明放开死循环(while 停住),或改成保存现场、重启等符合产品策略的处理,避免带病运行造成数据损坏。

操作步骤

1
进入例程目录

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

cd examples/log_dbg_assert
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)。程序先打印各类 DBG 调试信息(message、hexdump、数组、阶乘值、布尔表达式等),随后执行 12 个 _ASSERT_* 断言(全部为真,不会触发断言失败),最后每秒循环打印 F/E/W/I/D/T 及对应 Raw 版本的日志。把某一行断言改成假(如 _ASSERT_PARAM(1 == 0))重新烧录,会看到进入 error_handler 打印 error handler

代码执行流程

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

例程调用的 API 介绍

DBG_VALUE(expr)

打印表达式的变量名与值(按类型自适应格式),并返回表达式结果,可直接包在其他表达式外层。

参数

  • expr:任意表达式

返回值:表达式的值

DBG_HEXDUMP / DBG_ARRAY(expr, size / length)

DBG_HEXDUMP 以十六进制分块打印缓冲区;DBG_ARRAY 打印数组的每个元素。两者都会返回原表达式。

参数

  • expr:缓冲区 / 数组
  • size / length:字节数 / 元素个数

返回值:原表达式的值

DBG_BOOL(expr)

强制以布尔形式打印表达式结果(真/假),返回表达式值。

参数

  • expr:任意表达式

返回值:表达式的值

_ASSERT_PARAM / _ASSERT_FUNC(expr)

断言表达式为真(非零);为假时打印断言信息并进入 error_handler_PARAM 用于参数检查,_FUNC 用于函数内部逻辑检查。

参数

  • expr:必须为真的表达式

返回值:无(失败进入 error_handler

_ASSERT_TRUE / _ASSERT_FALSE / _ASSERT_ZERO / _ASSERT_EQUAL(...)

更语义化的断言:_ASSERT_TRUE 断言为真、_ASSERT_FALSE 断言为假、_ASSERT_ZERO 断言为 0、_ASSERT_EQUAL(val, expr) 断言两者相等;各有 _PARAM_FUNC 版本。

参数

  • val:期望值(仅 _ASSERT_EQUAL
  • expr:被检查的表达式

返回值:无(失败进入 error_handler

LOG_F / E / W / I / D / T 与 LOG_RF ... LOG_RT(fmt, ...)

分级日志宏:F 致命、E 错误、W 警告、I 信息、D 调试、T 跟踪;LOG_R* 为 Raw 版本,不附加额外格式。

参数

  • fmt, ...:printf 风格格式串与参数

返回值:无

完整代码

以下为 log_dbg_assert/main.c 完整源码,与官方示例(examples/log_dbg_assert)逐字一致,默认折叠,点击展开:

📜 点击展开 log_dbg_assert/main.c 完整代码
c
#include "bflb_mtimer.h"
#include "bflb_irq.h"
#include "board.h"

#define DBG_TAG "MAIN"
#include "log.h"

static int factorial(int n)
{
    if (DBG_BOOL(n <= 1)) {
        return DBG_VALUE(1);
    } else {
        return DBG_VALUE(n * factorial(n - 1));
    }
}

int main(void)
{
    board_init();

    char message[] = "hellokszhdfoiasjdkjnskjxnvuiolashdfoinaskjldfnvklasjhdfi213489o71234589073298dfl;asjdlfnkjzxncvhasdlkfljhasjkldjhnvjkaslndfvkljashdlifhui";

    /*!< All DBG macros will return the variables passed in */

    /*!< Print various types of variables using DBG_VALUE, type adaptive */
    DBG_VALUE(message);
    /*!< Use DBG_HEXDUMP to print data in canonical format, type adaptive */
    DBG_HEXDUMP(message, sizeof(message));

    const int a = 2;
    const int b = DBG_VALUE(3 * a) + 1;

    /*!< Use DBG_ARRAY to print arrays, type adaptive */
    int numbers[512] = { b, 13 };
    DBG_ARRAY(numbers, 512);

    DBG_VALUE(factorial(4));

    /*!< Printing Boolean expressions using DBG_BOOL */
    DBG_BOOL(1 == factorial(4));
    DBG_BOOL(24 == factorial(4));
    DBG_BOOL(1 == 0);
    DBG_BOOL(1 == 1);

    /*!< Assert whether the expression is true or not */
    _ASSERT_PARAM(1 == factorial(4));
    _ASSERT_FUNC(1 == factorial(4));
    /*!< Assert whether the expression is true or not */
    _ASSERT_TRUE_PARAM(1 == factorial(4));
    _ASSERT_TRUE_FUNC(1 == factorial(4));
    /*!< Assert whether the expression is false or not */
    _ASSERT_FALSE_PARAM(24 == factorial(4));
    _ASSERT_FALSE_FUNC(24 == factorial(4));
    /*!< Assert whether the expression is 0 or not */
    _ASSERT_ZERO_PARAM(factorial(4));
    _ASSERT_ZERO_FUNC(factorial(4));
    /*!< Assert whether two values are equal */
    _ASSERT_EQUAL_PARAM(1, factorial(4));
    _ASSERT_EQUAL_FUNC(1, factorial(4));

    while (1) {
        LOG_F("hello world fatal\r\n");
        LOG_E("hello world error\r\n");
        LOG_W("hello world warning\r\n");
        LOG_I("hello world information\r\n");
        LOG_D("hello world debug\r\n");
        LOG_T("hello world trace\r\n");
        LOG_RF("hello world fatal raw\r\n");
        LOG_RE("hello world error raw\r\n");
        LOG_RW("hello world warning raw\r\n");
        LOG_RI("hello world information raw\r\n");
        LOG_RD("hello world debug raw\r\n");
        LOG_RT("hello world trace raw\r\n");
        bflb_mtimer_delay_ms(1000);
    }
}

/*!< You can leave this function undefined */
/*!< and use the weak default handler */
void error_handler(void)
{
    /*!< assertion faild handler */
    printf("error handler\r\n");

    /*!< For testing purposes only, the following  */
    /*!< comments should be uncommented under normal  */
    /*!< circumstances, so that the MCU is stuck */

    // uintptr_t irq = bflb_irq_save();
    // volatile unsigned char dummy = 0;
    // while (dummy == 0) {
    // }
    // bflb_irq_restore(irq);
}

FAQ

断言失败后程序没有停住

例程的 error_handler 只打印一行是测试用途。正式项目请按例程注释放开 while (dummy == 0) 死循环,或改为保存崩溃现场后复位,避免带病运行。

DBG 宏会不会改变程序逻辑

不会:所有 DBG 宏都会返回原表达式的结果(DBG_VALUE(x) 等价于 x),可以直接包在表达式外层。CONFIG_LOG_LEVEL < 3 时宏退化为 ((void)(expr)),也不改变值。

LOG_* 与 BFLB_LOG_* 有什么区别

本页例程用的是经典 log.h 体系(LOG_*),并关闭了 CONFIG_BFLB_LOG;「blog 日志」页用的是 BFLB_LOG 体系(BFLB_LOG_*,支持标签过滤)。两者选其一即可,混用容易造成输出重复或配置冲突。

遇到问题?

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

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