Skip to content

概念先知道

  • 日志(Log):程序运行时打印到串口的文字,用来观察运行状态、定位 bug,相当于程序的“日记”。
  • 分级日志:按严重程度分级,例程用到的级别从高到低为 F(fatal 致命)、E(error 错误)、W(warning 警告)、I(info 信息)、D(debug 调试)、T(trace 跟踪)。
  • 等级颜色:不同级别用不同颜色区分,一眼就能看出严重程度。颜色由 BFLB_LOG_COLOR_* 宏实际定义(bflb_log_conf.h,例程目录的 bflog_conf_user.h 是参考副本):F 品红、E 红、W 黄、I 无色(BFLB_LOG_SGR_RESET)、D 白、T 暗淡白(BFLB_LOG_SGR_FAINT)。例程配置中绿色 INFO 那行被注释掉了,所以实际输出 INFO 不带颜色。
  • 日志标签(Tag):给日志分组起名字(如 MAINTEST),可以按标签整体开启或关闭过滤,方便只看某一部分日志。
  • 同步 / 异步模式:同步模式直接立即输出;异步模式(同目录的 freertos_async 例程)先把日志放入队列,由专门线程输出,适合日志量大的场景。

例程功能简介

本页对应博流官方 SDK 的 barebone_sync 例程(examples/bflog/barebone_sync),演示基于 BFLB_LOG 组件的日志打印、分级与标签过滤

  • 创建 4096 字节内存池的日志记录器,模式为同步(BFLB_LOG_MODE_SYNC);
  • 创建 UART 流式输出通道(带颜色),并把输出函数接到 uart0
  • 主循环每秒打印 F/E/W/I/D/T 六级日志,并循环切换三种过滤状态:全部开启 → 只开 MAIN → 全部关闭;
  • log_test.c 里定义了 TEST 标签,演示多文件下标签的定义与过滤效果。
  • 同族例程examples/bflog/):freertos_async(FreeRTOS 多线程异步日志)。

操作步骤

1
进入例程目录

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

cd examples/bflog/barebone_sync
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)。程序每秒循环一轮:先打印 enable all output(MAIN 与 TEST 两组日志都显示),再打印 enable only MAIN output(只显示 MAIN 组),最后打印 disable all output(两组都不显示),如此循环。每轮都会输出 F/E/W/I/D/T 六级日志(hello world this is ...hello test this is ...)。

代码执行流程

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

例程调用的 API 介绍

bflb_log_create(log, pool, size, mode)

创建日志记录器(recorder),负责接收日志并分发给输出通道。

参数

  • logbflb_log_t 记录器对象(例程为 example_recorder
  • pool:日志内存池指针,例程为 4KB 对齐数组 example_pool
  • size:内存池大小(字节),例程 EXAMPLE_LOG_POOL_SIZE = 4096
  • modeBFLB_LOG_MODE_SYNC 同步模式 / 异步模式

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

bflb_log_direct_create(direct, type, color, lock, unlock)

创建日志输出通道(direct),例程为流式输出并开启颜色。

参数

  • directbflb_log_direct_t 输出通道对象
  • type:输出类型,例程 BFLB_LOG_DIRECT_TYPE_STREAM(流式)
  • colorBFLB_LOG_DIRECT_COLOR_ENABLE 开启颜色
  • lock / unlock:互斥锁回调,例程为 NULL

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

bflb_log_direct_init_stream(direct, stream_output)

绑定流式输出的实际输出函数(例程逐个调用 bflb_uart_putchar 写到 uart0)。

参数

  • direct:流式输出通道对象
  • stream_output:输出回调,形如 uint16_t (*)(void *, uint16_t)

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

bflb_log_append(log, direct)

把输出通道添加到记录器,之后日志才会被转发到该通道。

参数

  • log:记录器对象
  • direct:输出通道对象

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

bflb_log_direct_resume / bflb_log_resume(direct / log)

把输出通道 / 记录器恢复为工作状态(创建后默认挂起,必须 resume 才能输出)。

参数

  • direct / log:对应对象指针

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

bflb_log_global_filter("MAIN", enable)

按标签全局开启 / 关闭某组日志输出。

参数

  • tag_string:标签名,例程 "MAIN" / "TEST"
  • enabletrue 开启 / false 关闭

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

BFLB_LOG_F / E / W / I / D / T(recorder, fmt, ...)

分级日志宏:F 致命、E 错误、W 警告、I 信息、D 调试、T 跟踪。

参数

  • recorder:记录器对象(例程 &example_recorder
  • fmt, ...:printf 风格格式串与参数

返回值:无

BFLB_LOG_DEFINE_TAG / BFLB_LOG_GET_TAG(name, string, enable)

定义日志标签:BFLB_LOG_DEFINE_TAG(MAIN, "MAIN", true) 把标签放进专门的 section,之后用 BFLB_LOG_GET_TAG(MAIN) 取出并赋给 BFLB_LOG_TAG,本文件中的日志宏就会带上该标签。

参数

  • name:标签变量名
  • string:标签字符串(打印与过滤时使用)
  • enable:初始是否使能

返回值:无

完整代码

以下为 barebone_sync 例程完整源码,与官方示例(examples/bflog/barebone_sync)一致,默认折叠,点击展开:

📜 点击展开 barebone_sync/main.c 完整代码
c
#define DBG_TAG "MAIN"

#include "bflb_mtimer.h"
#include "bflb_uart.h"
#include "bflb_rtc.h"
#include "bflb_clock.h"
#include "board.h"
#include "bflb_log.h"

/*!< Adding the BFLB_LOG tag allows the use of tag filtering functionality */
/*!< If not added, the tag can still be displayed, but tag filtering will not work */

/*!< Define a BFLB_LOG tag */
BFLB_LOG_DEFINE_TAG(MAIN, DBG_TAG, true);

/*!< Cancel the previous BFLB_LOG tag */
#undef BFLB_LOG_TAG

/*!< Define the BFLB_LOG tag as a new tag */
#define BFLB_LOG_TAG BFLB_LOG_GET_TAG(MAIN)

/*!< Bflog bare-metal synchronization routine */
/*!< bflb_log barebone sync example */

struct bflb_device_s *uart0 = NULL;
static struct bflb_device_s *rtc = NULL;

/*!< Current UTC Timestamp 2022-12-16 17:52 */
uint32_t timestamp_base = 1671184300;

/** @defgroup   example_bflb_log_port port
-----------------------------------------------------------------------------
* @{
----------------------------------------------------------------------------*/
uint64_t bflb_log_clock(void)
{
    return bflb_mtimer_get_time_us();
}

uint32_t bflb_log_time(void)
{
    return BFLB_RTC_TIME2SEC(bflb_rtc_get_time(rtc)) + timestamp_base;
}

char *bflb_log_thread(void)
{
    return "";
}
/*---------------------------------------------------------------------------
* @}            example_bflb_log_port port
----------------------------------------------------------------------------*/

#define EXAMPLE_LOG_POOL_SIZE 4096

bflb_log_t example_recorder;
static uint32_t example_pool[EXAMPLE_LOG_POOL_SIZE / 4];
bflb_log_direct_stream_t example_uart_stream;

uint16_t example_uart_stream_output(void *ptr, uint16_t size)
{
    for (size_t i = 0; i < size; i++) {
        bflb_uart_putchar(uart0, ((char *)ptr)[i]);
    }
    return size;
}

void example_log_init(void)
{
    void *record = (void *)&example_recorder;
    void *direct = (void *)&example_uart_stream;

    /*!< Create a logger, configure the memory pool, set the memory pool size, and set the mode to synchronous.*/
    /*!< create recorder */
    if (0 != bflb_log_create(record, example_pool, EXAMPLE_LOG_POOL_SIZE, BFLB_LOG_MODE_SYNC)) {
        printf("bflb_log_create faild\r\n");
    }

    /*!< Create an outputter, of the stream outputter type, enable color output, and set the mutex to NULL. */
    /*!< create stream direct */
    bflb_log_direct_create(direct, BFLB_LOG_DIRECT_TYPE_STREAM, BFLB_LOG_DIRECT_COLOR_ENABLE, NULL, NULL);
    /*!< Configure the output function of the stream output */
    bflb_log_direct_init_stream((void *)direct, example_uart_stream_output);

    /*!< Add the outputter to the logger */
    /*!< connect direct and recorder */
    bflb_log_append(record, direct);

    /*!< Restore the outputter to working mode*/
    /*!< resume direct */
    bflb_log_direct_resume(direct);

    /*!< Restore the logger to working mode */
    /*!< resume record */
    bflb_log_resume(record);
}

extern void test_log(void);

int main(void)
{
    board_init();

    /*!< uart0 already initialized in bsp/board */
    uart0 = bflb_device_get_by_name("uart0");
    rtc = bflb_device_get_by_name("rtc");
    bflb_rtc_set_time(rtc, 0);

    example_log_init();

    uint8_t test = 0;

    while (1) {
        if (test == 0) {
            bflb_log_global_filter("MAIN", true);
            bflb_log_global_filter("TEST", true);
            printf("\r\n============================== enable all output\r\n\r\n");
        } else if (test == 1) {
            bflb_log_global_filter("MAIN", false);
            bflb_log_global_filter("TEST", true);
            printf("\r\n============================== enable only MAIN output\r\n\r\n");
        } else if (test == 2) {
            bflb_log_global_filter("MAIN", false);
            bflb_log_global_filter("TEST", false);
            printf("\r\n============================== disable all output\r\n\r\n");
        }

        if (++test >= 3) {
            test = 0;
        }

        BFLB_LOG_F(&example_recorder, "hello world this is fatal error\r\n");
        BFLB_LOG_E(&example_recorder, "hello world this is error\r\n");
        BFLB_LOG_W(&example_recorder, "hello world this is warning\r\n");
        BFLB_LOG_I(&example_recorder, "hello world this is information\r\n");
        BFLB_LOG_D(&example_recorder, "hello world this is degug information\r\n");
        BFLB_LOG_T(&example_recorder, "hello world this is trace information\r\n");
        test_log();
        bflb_mtimer_delay_ms(1000);
    }
}
📜 点击展开 barebone_sync/log_test.c 完整代码
c
#define DBG_TAG "TEST"

#include "bflb_log.h"

BFLB_LOG_DEFINE_TAG(TEST, DBG_TAG, true);
#undef BFLB_LOG_TAG
#define BFLB_LOG_TAG BFLB_LOG_GET_TAG(TEST)

extern bflb_log_t example_recorder;

void test_log(void)
{
    BFLB_LOG_F(&example_recorder, "hello test this is fatal error\r\n");
    BFLB_LOG_E(&example_recorder, "hello test this is error\r\n");
    BFLB_LOG_W(&example_recorder, "hello test this is warning\r\n");
    BFLB_LOG_I(&example_recorder, "hello test this is information\r\n");
    BFLB_LOG_D(&example_recorder, "hello test this is degug information\r\n");
    BFLB_LOG_T(&example_recorder, "hello test this is trace information\r\n");
}

FAQ

怎么开启 blog 日志(BFLB_LOG)

分三层开启:

  1. 编译期(工程配置):在工程 defconfig 中设置 CONFIG_BFLB_LOG =y,SDK 才会把 bflb_log 组件编进来(本页例程已默认开启)。未开启时 LOG_* 宏会退化为空操作,看不到日志。
  2. 头文件(日志级别)components/utils/log/bflb_log/bflb_log_conf.h 是默认配置(例程目录里的 bflog_conf_user.h 是它的参考副本),通过宏控制编译范围与输出:
    • BFLB_LOG_ENABLE:总开关,注释掉后日志代码不参与编译;
    • BFLB_LOG_LEVEL_ENABLE:编译进固件的最高级别(例程为 BFLB_LOG_LEVEL_TRACE,即 F/E/W/I/D/T 全量);
    • BFLB_LOG_LEVEL_DEFAULT / BFLB_LOG_DIRECT_LEVEL_DEFAULT:记录器/输出通道默认过滤级别,低于该级别的日志不记录/不输出。
  3. 运行期(标签过滤)BFLB_LOG_DEFINE_TAG(name, tag, enable)enable 决定该标签初始是否输出;运行中用 bflb_log_global_filter("MAIN", true/false) 按标签动态开关(本页例程就是每秒切换三种过滤状态)。

自建工程可复制例程的 defconfigbflog_conf_user.h 起步,确认第 1、2 层开启后,日志就会从 uart0 输出。

日志没有任何输出

先确认串口波特率是 2000000;再检查记录器与输出通道是否都执行了 resume(挂起状态下不会输出),以及过滤状态是否被全部关闭(bflb_log_global_filter 两个标签都 false 时无输出)。

串口看不到彩色日志,只有普通文字

颜色依赖串口工具支持 ANSI 转义序列:请使用支持 ANSI 颜色显示的串口/终端工具(如 MobaXterm、Xshell、Windows Terminal 等),并确认创建输出通道时传入了 BFLB_LOG_DIRECT_COLOR_ENABLE(例程已开启)。颜色以代码中的 BFLB_LOG_COLOR_* 宏为准(F 品红 / E 红 / W 黄 / I 无色 / D 白 / T 暗淡白);例程参考配置里 INFO 的绿色定义被注释,如需绿色把该行取消注释并重新编译即可。

看不到 TEST 标签的日志

过滤状态为 enable only MAIN output 时 TEST 被关掉了,这是例程演示的标签过滤效果。想同时看两组,保持两个标签都 true 即可。

日志时间显示不对

日志头的时间来自 bflb_log_time(),例程以 RTC 时间加固定 timestamp_base 计算。RTC 未校时或基值不符合当前时间时,显示会偏离真实时间,但不影响功能验证。

遇到问题?

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

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