调试与日志工具
AC792N SDK 的调试与日志(debug & dlog)子系统,提供从串口(UART)、蓝牙 SPP 等通道输出与合并日志、外置 Flash 日志存储、在线调试数据库等能力,是嵌入式开发阶段定位问题的核心基础设施。
Purpose and Scope
本文档介绍 AC792N_SDK_V3 中与调试与日志相关的完整机制,涵盖:
- dlog 日志框架的配置入口(
dlog_config.c)与输出通道管理(dlog_output_config.c); - 日志输出通道(UART / SPP)、合并发送(merge)、环形缓冲(lbuf)与锁同步机制;
- 外置 Flash 日志存储区(
TCFG_DEBUG_DLOG_FLASH_SEL)及 SPI Flash 底层驱动; debug/目录下其他调试组件(debug.c、debug_user.c、debug_lite.c、put_float.c、online_db/)的职责定位;- 相关配置宏、API、失败模式与扩展方式。
以下内容不在本文档范围,请参考对应目录页:音频通路与编解码调试、蓝牙协议栈调试、功耗测量工具(如 ram_timer 等)以及各产品 demo 的板级调试配置。
概述
在 AC792N 这类资源受限的嵌入式 SoC 上,日志系统是开发与量产阶段最重要的观测手段。SDK 将日志功能组织为两层:
- dlog 框架(generic/dlog.h):提供
dlog_printf等日志写入原语,以及日志数据的缓冲、合并、多通道分发能力; - 应用层调试配置(apps/common/debug/):把 dlog 框架绑定到具体硬件(串口 DMA、外置 SPI Flash、蓝牙 SPP),并提供在线调试数据库(online_db)与浮点打印等辅助工具。
设计上,日志系统必须满足嵌入式环境的三个约束:低中断延迟(日志写入不能长时间关中断)、零丢失(高频日志需要缓冲与背压控制)、可配置(量产固件可裁剪日志以节省 RAM/Flash)。
架构
flowchart TD
subgraph sg_App["应用层 apps/common/debug"]
Dbg["debug.c / debug_user.c / debug_lite.c"]
DlogCfg["dlog_config.c<br/>(配置 + Flash 存储)"]
DlogOut["dlog_output_config.c<br/>(输出通道管理)"]
Float["put_float.c"]
OnDb["online_db/<br/>online_db_deal.c + spp_online_db.c"]
end
subgraph sg_Core["dlog 框架(generic/dlog.h)"]
DlogApi["dlog_printf / log_print"]
Lbuf["lbuf 环形缓冲"]
ChList["dlog_output_channel_s 通道链表"]
end
subgraph sg_Channels["输出通道"]
Uart["UART(DMA)"]
Spp["蓝牙 SPP"]
Flash["外置 SPI Flash 日志区"]
end
Dbg --> DlogCfg
DlogCfg --> DlogApi
DlogOut --> DlogApi
DlogApi --> Lbuf
Lbuf --> ChList
ChList --> Uart
ChList --> Spp
DlogCfg --> Flash
OnDb --> Spp
Float --> DlogApi
各组件职责:
- dlog_config.c:dlog 系统的总配置与初始化入口。在
TCFG_DEBUG_DLOG_ENABLE宏使能下编译;定义弱符号dlog_output_direct()作为直通输出钩子;当TCFG_DEBUG_DLOG_FLASH_SEL打开时,通过 SPI 操作外置 Winbond 类 Flash,把日志写入自定义区域(见 dlog_config.c)。 - dlog_output_config.c:输出通道的缓冲与调度。用
DLOG_OUT_CHANNEL宏选择 UART/SPP 通道,用 lbuf 环形缓冲暂存日志,由定时器触发dlog_output_resume()将缓冲刷出;支持DLOG_OUT_MERGE_EN合并发送以降低 SPP 发送频率(见 dlog_output_config.c)。 - debug.c / debug_user.c / debug_lite.c:用户态调试命令与通用调试入口,通常绑定串口命令解析。
- put_float.c:在无浮点打印支持的环境下输出浮点数的辅助实现。
- online_db/:通过蓝牙 SPP 提供在线读写设备数据库(db)的能力,用于上位机调试工具实时查看/修改参数。
dlog 核心机制
使能与裁剪:TCFG_DEBUG_DLOG_ENABLE
整个 dlog 功能由编译宏 TCFG_DEBUG_DLOG_ENABLE 门控。dlog_config.c 与 dlog_output_config.c 的代码主体都包裹在 #if TCFG_DEBUG_DLOG_ENABLE 中,关闭该宏即可把日志框架整体裁剪出固件,节省 RAM 与 Flash。这是量产固件与开发固件的关键差异点:
#if TCFG_DEBUG_DLOG_ENABLE
extern u32 __attribute__((weak)) dlog_log_data_start_addr[];
struct sys_time dlog_sys_time = {0};
__attribute__((weak))
int dlog_output_direct(void *buf, u16 len)
{
return 0;
}
这里三个弱符号/全局量各有用途:
dlog_log_data_start_addr[]:指向日志数据区的起始地址(通常由链接脚本提供),供 Flash 存储布局使用;dlog_sys_time:日志时间戳的全局时间结构,由 RTC 相关代码维护;dlog_output_direct():直通输出钩子。默认实现返回 0(不处理),用户可在别处提供强符号实现,将日志绕过缓冲直接写出——适用于极低延迟的调试场景(如中断上下文)。
输出通道抽象:dlog_output_channel_s
dlog_output_config.c 通过一个链接器段数组管理输出通道。所有通道实例被链接进 dlog_out_channel_begin 到 dlog_out_channel_end 之间,遍历时用宏:
extern const struct dlog_output_channel_s dlog_out_channel_begin[];
extern const struct dlog_output_channel_s dlog_out_channel_end[];
#define list_for_each_dlog_channel(p) \
for (p = dlog_out_channel_begin; p < dlog_out_channel_end; p++)
这种"段内数组"设计避免了链表节点在运行期的分配开销,并且新通道只需在源码中定义 const struct dlog_output_channel_s 实例即可自动被遍历到,是嵌入式环境常用的零成本注册机制。通道结构至少包含 is_busy() 回调,供调度器判断该通道是否空闲。
输出缓冲与调度
日志输出使用一个独立分配的 lbuf 环形缓冲,大小由 DLOG_OUTPUT_LBUF_MAX_SIZE 决定(默认 20KB)。初始化代码如下:
int dlog_output_channel_init()
{
//串口DMA要求4 byte对齐,且为连续ram,因此用dma_malloc和lbuf
printf("%s", __func__);
g_dlog_output.lbuf_head = malloc(DLOG_OUTPUT_LBUF_MAX_SIZE);
if (g_dlog_output.lbuf_head) {
lbuf_init(g_dlog_output.lbuf_head, DLOG_OUTPUT_LBUF_MAX_SIZE, 4, 0);
}
return 0;
}
注释揭示了关键设计约束:串口 DMA 要求 4 字节对齐且为连续 RAM,因此缓冲用 malloc 而非零散的小块。lbuf_init(..., 4, 0) 中的对齐参数 4 正对应这一 DMA 要求。
刷出(flush)流程由 dlog_output_resume() 驱动,它的三个关键行为:
- 忙检查:遍历所有通道,若任一通道
is_busy()为真则立即返回(背压),防止数据覆盖; - 释放上一帧:
lbuf_free(g_dlog_output.last_log)归还上一次发送的缓冲区; - 弹出并发送:从 lbuf 头部取出一条
struct logbuf,其buf_len字段记录长度,随后按合并策略发送。
void dlog_output_resume(u8 flush)
{
if (NULL == g_dlog_output.lbuf_head) {
return;
}
if (g_dlog_output.timer) {
return;
}
const struct dlog_output_channel_s *p = NULL;
spin_lock(&g_dlog_output_lock);
// flush = 1 则不检查busy标记
if (!flush) {
list_for_each_dlog_channel(p) { //等所有通道都发送完成后再继续发
if (p->is_busy && p->is_busy()) {
spin_unlock(&g_dlog_output_lock);
return;
}
}
}
...
}
flush 参数语义明确:flush = 1 时不检查 busy 标记,强制把缓存数据全部清空——用于系统重启前或进入低功耗前的兜底落盘。g_dlog_output.timer 作为节流标志,防止在定时器回调尚未复位时被重复触发;DEFINE_SPINLOCK(g_dlog_output_lock) 保护整个刷出流程,因为该函数可能在任务上下文与定时器上下文中被并发调用。
外置 Flash 日志存储(TCFG_DEBUG_DLOG_FLASH_SEL)
当 TCFG_DEBUG_DLOG_FLASH_SEL 打开时,dlog 支持把日志写入外置 SPI Flash 的自定义区域,用于记录掉电后仍可回溯的运行日志(例如崩溃现场)。dlog_config.c 中为此实现了精简的 Winbond(W25X 系列)SPI Flash 驱动。
硬件抽象
struct _dlog_ext_flash_ {
void *spi;
int cs_gpio;
};
static struct _dlog_ext_flash_ hdl = {0};
#define FLASH_PAGE_SIZE 256
#define WINBOND_READ_DATA 0x03
#define WINBOND_DUAL_READ_DATA 0x3b
#define WINBOND_DUAL_READ 0xbb
#define WINBOND_READ_SR1 0x05
#define DUMMY_BYTE 0xff
#define WINBOND_CHIP_ERASE 0xC7
#define W25X_JedecDeviceID 0x9f
#define WINBOND_WRITE_ENABLE 0x06
#define WINBOND_PAGE_PROGRAM_QUAD 0x32
#define W25X_SectorErase 0x20
#define W25X_PageProgram 0x02
该驱动通过 hdl.spi(SPI 设备句柄)与 hdl.cs_gpio(片选 GPIO)操作 Flash,命令集覆盖:读(0x03/双线 0x3b/0xbb)、写使能(0x06)、页编程(0x02/四线 0x32)、扇区擦除(0x20)、整片擦除(0xC7)、读 ID(0x9f)、读状态寄存器(0x05)。页大小按标准 256 字节定义。
操作原语
所有 Flash 操作都遵循"拉低片选 → 发命令 → 传输数据 → 拉高片选"的时序。写使能与读 ID 是典型示例:
static void spiflash_send_write_enable(void)
{
cs_gpio(0);
dev_ioctl(hdl.spi, IOCTL_SPI_SEND_BYTE, WINBOND_WRITE_ENABLE);
cs_gpio(1);
}
static int W25X_GetChipID(void)
{
u8 id[0x3] = {0, 0, 0};
int id_mub = 0;
int err = 0;
cs_gpio(0);
dev_ioctl(hdl.spi, IOCTL_SPI_SEND_BYTE, W25X_JedecDeviceID);
for (u8 i = 0; i < sizeof(id); i++) {
err = dev_ioctl(hdl.spi, IOCTL_SPI_READ_BYTE, (u32)&id[i]);
printf(">>>>>>>>>id = 0x%x", id[i]);
if (err) {
printf(">>>>>>>>>>ERR");
id[0] = id[1] = id[2] = 0xff;
break;
}
}
cs_gpio(1);
...
}
设计要点:
- 自包含驱动:dlog 的 Flash 日志存储不依赖 SDK 通用的 norflash 驱动栈(
asm/sfc_norflash_api.h被包含但真正的 Flash 访问走dev_ioctl(hdl.spi, ...)),从而可以在日志系统早期(甚至 Flash 驱动未初始化时)独立工作; - 错误容错:读 ID 失败时把 ID 填充为
0xff并继续,避免初始化流程被单点错误卡死,同时通过printf打印错误信息辅助定位; - CS 时序由 GPIO 控制:
cs_gpio()用gpio_direction_output()手动拉片选,适合挂在通用 SPI 控制器之外的软件模拟场景。
日志数据区
日志在 Flash 中的落盘地址依赖链接脚本符号 dlog_log_data_start_addr(弱符号声明,见上文),实际地址由链接脚本在编译期确定,保证日志区与代码/数据区隔离,避免误擦除。
核心流程
日志从产生到输出的完整链路
sequenceDiagram
participant Task as 业务任务
participant Dlog as dlog 框架
participant Lbuf as lbuf 环形缓冲
participant Timer as 定时器
participant Out as dlog_output_resume
participant Chan as 输出通道(UART/SPP)
participant Flash as 外置Flash(可选)
Task->>Dlog: dlog_printf(...) / log_print(...)
Dlog->>Lbuf: 写入日志帧(含时间戳)
Note over Lbuf: 超过 DLOG_OUTPUT_LBUF_MAX_SIZE 时<br/>丢弃最旧数据或阻塞
Timer->>Out: dlog_output_timeout_handler()
Out->>Out: 检查 timer 节流标志
Out->>Chan: is_busy() == 空闲?
alt 所有通道空闲
Out->>Lbuf: lbuf_pop 取出一条 logbuf
Out->>Chan: 按通道发送(buf_len 字节)
Chan-->>Out: 发送完成
else 有通道忙
Out-->>Timer: 直接返回,等待下次触发
end
opt TCFG_DEBUG_DLOG_FLASH_SEL 使能
Dlog->>Flash: 写使能→页编程→扇区擦除
end
合并发送(DLOG_OUT_MERGE_EN)
当 DLOG_OUT_MERGE_EN 打开时,短时间内的多条日志会被合并为一条再发送,其目的在于降低 SPP 等慢通道的发送频率——注释明确说明"SPP 发送太频繁,uart 可以不开"。合并由三个宏控制:
| 宏 | 含义 |
|---|---|
DLOG_OUT_MERGE_EN | 是否启用合并(0 关闭 / 1 打开) |
DLOG_OUT_MERGE_TIME | 合并时间窗,单位 ms(默认 100),窗口内日志攒成一条 |
DLOG_OUT_MERGE_SIZE | 合并缓冲上限(默认 512 字节),超过立即发送 |
合并逻辑的核心判断出现在 dlog_output_resume() 中:若当前取出的 log->buf_len 小于 g_dlog_output.total_size(累计待发长度),则继续累积而不是立即发送(见 dlog_output_config.c)。total_size 因此充当"已合并未发送"的水位计数。
生命周期
flowchart LR
A["上电/编译期<br/>TCFG_DEBUG_DLOG_ENABLE"] --> B["dlog_output_channel_init()<br/>malloc 20KB lbuf + lbuf_init"]
B --> C["运行期<br/>dlog_printf 写入 + 定时刷出"]
C --> D["重启/低功耗前<br/>dlog_output_resume(flush=1)"]
D --> E["dlog_output_channel_deinit()<br/>free lbuf"]
初始化与反初始化函数对称:dlog_output_channel_init() 分配缓冲,dlog_output_channel_deinit() 释放并置空指针(dlog_output_config.c)。注意 late_initcall(dlog_output_channel_init) 在源码中被注释掉,说明实际初始化时机由应用层决定(通常放在 app_core 任务启动早期)。
使用示例
在业务代码中输出日志
dlog 提供与 printf 兼容的 dlog_printf,并支持时间前缀与任务名。dlog_config.c 中的测试代码展示了标准用法:
void dlog_print_test1(void *priv)
{
static int cnt[3] = {0};
char time[15] = {0};
int index = (int)priv;
extern int log_print_time_to_buf(char *time);
log_print_time_to_buf(time);
/* printf("%stask:%s, cnt:%d\n", time, os_current_task(), cnt[index]); */
dlog_printf("%stask:%s, cnt:%d\n", time, os_current_task(), cnt[index]);
cnt[index]++;
}
要点:
- 先用
log_print_time_to_buf()把 RTC 时间格式化进time缓冲区,再随日志一起输出,实现带时间戳的日志; - 通过
os_current_task()打印当前任务名,便于多任务环境下快速定位日志来源; - 示例代码中
printf被注释、dlog_printf生效——这反映了 SDK 的迁移策略:调试期统一走 dlog 通道,便于集中收集。
跨任务注入日志测试
dlog 可以借助系统任务队列在任意任务上下文中打印,验证多任务并发下的日志正确性:
void dlog_print_test(void *priv)
{
extern void dlog_uart_init();
dlog_uart_init();
sys_timer_add((void *)0, dlog_print_test1, 200);
#if 1
int msg[] = {(int)dlog_print_in_bttask, 0};
os_taskq_post_type("btstack", Q_CALLBACK, ARRAY_SIZE(msg), msg);
int msg2[] = {(int)dlog_print_in_btctrler, 0};
os_taskq_post_type("btctrler", Q_CALLBACK, ARRAY_SIZE(msg2), msg2);
#endif
}
这段代码展示了三个能力:dlog_uart_init() 手动初始化 UART 通道;sys_timer_add() 注册周期定时器持续打印;os_taskq_post_type("btstack"/"btctrler", Q_CALLBACK, ...) 把打印函数投递到蓝牙协议栈任务,验证日志系统在非当前任务上下文中也能安全输出。
重定向 printf 到 dlog
dlog_output_config.c 展示了将标准 printf/put_buf 重定向到 dlog 的宏方案(当前被注释,说明默认保持库自带 printf):
#ifdef printf
#undef printf
#endif
#ifdef put_buf
#undef put_buf
#endif
/* #define printf(format, ...) log_print(3, NULL, format, ##__VA_ARGS__) */
若取消注释,所有 printf 调用都会走 log_print(3, NULL, ...) 进入 dlog 框架——3 为日志等级参数。这是"全局日志接管"的开关,量产裁剪时可直接移除。
配置选项
dlog_output_config.c 输出通道配置
| 宏 | 类型 | 默认值 | 说明 |
|---|---|---|---|
DLOG_OUTPUT_LBUF_MAX_SIZE | 整数 | 20 * 1024 | 日志环形缓冲总大小(字节)。串口 DMA 要求连续 4 字节对齐 RAM,故用 malloc 一次性分配 |
DLOG_OUTPUT_BY_UART | 位标志 | (1 << 0) | UART 输出通道使能位 |
DLOG_OUTPUT_BY_SPP | 位标志 | (1 << 1) | 蓝牙 SPP 输出通道使能位 |
DLOG_OUT_CHANNEL | 位组合 | DLOG_OUTPUT_BY_UART | 实际启用的输出通道(可组合 UART | SPP,0 表示关闭输出) |
DLOG_OUT_MERGE_EN | 布尔 | 0 | 是否启用短时日志合并(用于降低 SPP 发送频率,可关 UART) |
DLOG_OUT_MERGE_TIME | 整数(ms) | 100 | 合并时间窗:窗口内日志合并成一条发送 |
DLOG_OUT_MERGE_SIZE | 整数(字节) | 512 | 合并缓冲上限,超过立即发送 |
dlog_config.c 功能开关
| 宏 | 说明 |
|---|---|
TCFG_DEBUG_DLOG_ENABLE | dlog 总开关。关闭后 dlog_config.c 与 dlog_output_config.c 主体代码全部不编译 |
TCFG_DEBUG_DLOG_FLASH_SEL | 外置 Flash 日志存储开关。打开后启用 W25X 系列 SPI Flash 驱动,把日志写入自定义区域 |
这两个宏通常在 app_config.h 或板级配置头文件中定义,属于编译期配置,改动后需重新编译固件。
API 参考
以下 API 均在 TCFG_DEBUG_DLOG_ENABLE 使能时可用,来自 dlog_config.c 与 dlog_output_config.c。
int dlog_output_direct(void *buf, u16 len)
弱符号直通输出钩子。默认返回 0;用户提供强符号实现后,日志可绕过缓冲直接写出。
参数:
buf(void *):待输出的日志数据指针len(u16):数据长度(字节)
返回: 默认实现返回 0;自定义实现返回实际处理结果(如写入字节数或错误码)。
设计意图: 用于中断上下文或极低延迟场景,避免依赖 lbuf 缓冲与定时器调度的延迟。
void dlog_output_resume(u8 flush)
刷出环形缓冲中的日志到所有输出通道。
参数:
flush(u8):1表示不检查通道 busy 标记、强制清空缓存;0表示仅当所有通道空闲时才发送。
行为:
lbuf_head为空或timer节流标志置位时直接返回;- 持自旋锁
g_dlog_output_lock; flush = 0时遍历通道检查is_busy(),任一忙则解锁返回;- 释放上一帧
last_log,从 lbuf 弹出下一条logbuf,按合并策略发送。
int dlog_output_channel_init(void)
分配并初始化输出缓冲。
行为: malloc(DLOG_OUTPUT_LBUF_MAX_SIZE) 分配 20KB 连续 RAM,调用 lbuf_init(head, size, 4, 0) 建立 4 字节对齐的环形缓冲。分配失败时 lbuf_head 为 NULL,后续刷出流程自动跳过。
void dlog_output_channel_deinit(void)
释放输出缓冲。free(lbuf_head) 后置 NULL。用于系统关闭日志功能或低功耗前回收内存。
static void dlog_output_timeout_handler(void)
定时器回调。清除 timer 节流标志后调用 dlog_output_resume(0)。注释掉的代码展示了备选方案:把 dlog_output_resume 投递到 app_core 任务队列执行(避免在定时器上下文做重活)。
dlog_printf(format, ...)
dlog 框架提供的格式化日志输出(generic/dlog.h),用法与 printf 一致。实际输出路径为:写入 lbuf → 定时器触发 → 通道发送。
失败模式、边界情况与并发
通道忙导致的背压丢弃
dlog_output_resume() 在任一通道 is_busy() 时直接返回,不阻塞调用方。这意味着当 UART/SPP 发送速度跟不上日志产生速度时,日志会在 lbuf 中积压;若积压超过 DLOG_OUTPUT_LBUF_MAX_SIZE(20KB),lbuf 会按环形缓冲策略覆盖最旧数据。这是有意的取舍:日志系统优先保证系统实时性,允许在高频日志下丢失部分旧日志,而不是拖慢业务任务。
合并模式下的发送延迟
DLOG_OUT_MERGE_EN = 1 时,日志最多延迟 DLOG_OUT_MERGE_TIME(100ms)才发送(除非累积超过 DLOG_OUT_MERGE_SIZE 提前触发)。调试低延迟事件(如中断时序)时应关闭合并,避免日志时间戳与实际发生时刻偏差。
并发与锁
g_dlog_output_lock 自旋锁保护 dlog_output_resume() 的整个临界区。可能的竞争来源:
- 定时器上下文调用
dlog_output_timeout_handler()→dlog_output_resume(0); - 任务上下文(如 SPP 数据回调或用户主动 flush)调用
dlog_output_resume(1)。
若两者同时进入,自旋锁保证串行化。注意在持锁期间调用 lbuf_free/lbuf_pop 与通道 is_busy() 回调,这些函数必须是可重入且快速的,否则会拉长关中断/自旋时间。源码中把 dlog_output_resume 投递到 app_core 的备选方案(被注释)正是为规避在定时器上下文长时间持锁的权衡。
分配失败降级
dlog_output_channel_init() 中 malloc 失败时 lbuf_head = NULL,后续所有刷出路径都以 if (NULL == g_dlog_output.lbuf_head) return; 优雅退出,日志退化为"直接丢弃",不会引起空指针崩溃。代价是 20KB 连续 RAM 在低内存场景可能分配失败,需在系统内存预算中评估。
Flash 读写错误
外置 Flash 日志存储依赖 dev_ioctl(hdl.spi, ...) 的返回码。W25X_GetChipID() 在 err 非零时把 ID 置为 0xff 并打印 ERR,继续后续流程而非死等。生产环境应关注:Flash 写磨损(页编程前需扇区擦除)、掉电时写入中断(可能产生半页数据,读取端需容错)、以及日志区与文件系统区地址重叠(需与链接脚本核对 dlog_log_data_start_addr)。
性能与运维考量
- 缓冲即性能边界:20KB lbuf + 定时器节流决定了日志吞吐上限。高频日志(每任务每 tick 打印)会快速填满缓冲并触发覆盖;应通过日志等级或宏裁剪高频路径。
- DMA 对齐:lbuf 按 4 字节对齐初始化,正是为满足串口 DMA 搬运要求;若改用非对齐缓冲,需确保发送端处理非对齐头。
- SPP 通道的代价:SPP 走蓝牙协议栈,吞吐远低于 UART。合并发送(
DLOG_OUT_MERGE_EN)是 SPP 场景的推荐配置;纯 UART 调试可关闭合并以降低延迟。 - 量产裁剪:量产固件应关闭
TCFG_DEBUG_DLOG_ENABLE或把DLOG_OUT_CHANNEL置 0,释放 20KB RAM 与相关代码段。 - 在线调试数据库(online_db):
sdk/apps/common/debug/online_db/下的online_db_deal.c与spp_online_db.c通过 SPP 提供设备数据库在线读写能力,供上位机(如杰理调试工具)实时查看/修改参数。其实现细节属于在线调试工具范畴,本文档仅作入口定位。
扩展点
- 新增输出通道:定义
const struct dlog_output_channel_s实例(实现is_busy()等回调),放入dlog_out_channel_begin/end段区间,即被list_for_each_dlog_channel自动遍历——无需修改调度代码。 - 自定义直通输出:提供
dlog_output_direct()强符号实现,接管低延迟输出路径。 - 日志全局重定向:取消
printf → log_print(3, NULL, ...)宏的注释,让全工程printf统一进入 dlog 通道。 - Flash 日志区定制:通过链接脚本定义
dlog_log_data_start_addr决定日志区位置;TCFG_DEBUG_DLOG_FLASH_SEL控制该功能编译。 - 其它调试组件:
debug.c(通用调试命令)、debug_user.c(用户自定义调试接口)、debug_lite.c(精简版调试)、put_float.c(浮点打印辅助)均可按产品需求增删;在线调试数据库(online_db)可扩展为独立调试通道。
相关链接
- dlog_config.c — dlog 配置与外置 Flash 日志存储
- dlog_output_config.c — 日志输出通道与合并调度
- debug.c / debug_user.c / debug_lite.c — 通用调试入口
- online_db/ — 在线调试数据库(SPP)
- put_float.c — 浮点打印辅助
- dlog 使用说明文档(PDF)
- dlog 调试文档(RST 源)
相关目录页:功耗与定时器调试工具(ram_timer)、音频/蓝牙协议栈调试、各产品 demo 板级调试配置。