概念先知道
- 断言(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/T与LOG_RF/RE/RW/RI/RD/RT:分级日志及其 Raw(不带额外格式)版本。- 该例程关闭了
CONFIG_BFLB_LOG,使用的是经典log.h日志体系。
注意
例程的 error_handler 仅为测试用途:断言失败后只打印一行就继续运行。正式项目请按注释说明放开死循环(while 停住),或改成保存现场、重启等符合产品策略的处理,避免带病运行造成数据损坏。
操作步骤
本页不需要额外接线。在终端进入 SDK 的调试断言例程目录(前提:已按快速开始(Linux)或Windows搭建好环境):
cd examples/log_dbg_assert执行编译命令。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)。程序先打印各类 DBG 调试信息(message、hexdump、数组、阶乘值、布尔表达式等),随后执行 12 个 _ASSERT_* 断言(全部为真,不会触发断言失败),最后每秒循环打印 F/E/W/I/D/T 及对应 Raw 版本的日志。把某一行断言改成假(如 _ASSERT_PARAM(1 == 0))重新烧录,会看到进入 error_handler 打印 error handler。
代码执行流程
例程从启动到输出的完整流程如下(图中的循环箭头表示反复执行):
例程调用的 API 介绍
DBG_HEXDUMP / DBG_ARRAY(expr, size / length)
DBG_HEXDUMP 以十六进制分块打印缓冲区;DBG_ARRAY 打印数组的每个元素。两者都会返回原表达式。
参数:
expr:缓冲区 / 数组size/length:字节数 / 元素个数
返回值:原表达式的值
_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 完整代码
#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

