调试

调试应用程序有以下两种方式。

  1. 使用log机制跟踪代码的执行和数据。

  2. 使用Keil MDK或J-Link Commander和SWD进行调试,增加/删除断点以及访问/追踪memory等。

Log机制

Debug analyzer是用来通过打印log的方式来追踪应用程序的执行情况,以及抓取BT的数据并进一步通过第三方BT分析工具来分析。详情请参考《Debug Analyzer User Manual》。

P0_3默认作为Log UART的输出引脚(也可以将Log功能重新配置到其它引脚),经UART-to-USB连接到PC,在PC端COM口接收Log数据。

Debug Analyzer

使用步骤如下。

  1. 打开 DebugAnalyzer.exe ,点击 Settings ‣ Start 即可。

../../../../_images/Main_Interface_of_Debug_Analyzer.png

Debug Analyzer主页面

  1. Settings 界面中,Serial Port 选择LOG对应的COM口。

  2. App Trace File 选择对应的 App.trace
    注:App.trace 在APP程序编译后和 App.bin 生成在同一个目录位置。
../../../../_images/Debug_Analyzer_Setting_Page.png

Debug Analyzer设置页面

Debug analyzer提供了一些高级功能。

  • Save Raw File:(建议勾选)保存Log到 xxx.bin 文件,此文件可提供给Realtek做进一步分析。

  • Log Filter:过滤显示,弹出新窗口,只显示满足过滤条件的Log。

  • Keyword Search:在Log打印窗口高亮显示关键字。

  • Clear Trace:清空显示框中的Log。

备注

Debug analyzer会自动保存Log文件,并按照端口号和生成时间命名,如 COM5_2015-06-12_18- 05-00.log ,保存位置在DebugAnalyzer安装路径 DebugAnalyzer\DataFile 中。

Log打印基本接口

硬件有特定的GDMA通道来打印Log,log接口声明如下。

DBG_BUFFER(T_LOG_TYPE type, T_LOG_SUBTYPE sub_type,..T_MODULE_ID module,uint8_t level, char* fmt, unit8_t param_num,...).

参数 type 固定是 TYPE_BEE4sub_type 固定是 SUBTYPE_FORMAT。只要关心参数 modulelevel。T_MODULE_ID类型中有预定义好几种module,这些module用于将log分类别。Debug Analyzer能够识别这些module,在每条打印的log前自动加到module名称。

还有一个概念是日志级别,表示log要打印的级别,定义了以下4种类别。

日志级别

日志级别

使用场景

LEVEL_ERROR

致命错误,程序无法继续运行(Log标识:!!!)

LEVE_WARN

异常情况发生,程序可以继续运行(Log标识:!!*)

LEVEL_INFO

重要信息(Log标识:!**)

LEVEL_TRACE

详细调试信息(Log标识:无)

DBG_BUFFER()接口灵活、功能丰富,但不方便使用。为此,RTL87x2G SDK在trace.h中提供了一些易读性更强的APIs来包装此接口。

Log打印封装接口

APIs通用语法如下。

{MODULE}_PRINT_{LEVEL}_{PARAMNUM}(...)

参数释义:

MODULE:

trace.h中定义——APP/GAP/USB/FLASH...

LEVEL:

四种level——ERROR/WARN/INFO/TRACE

PARAMNUM:

0到8——代表log打印参数的个数

例如,想要打印一条含2个参数的log。

APP_PRINT_WARN2("Test app: ID = %d, data = 0x%x", id, data);

Debug Analyzer显示如下这条Log。

0049410-13#17:06:45.994 087 02145 [APP] !!*Test app: ID = 3, data = 0xF0

备注

  1. 不要超过8个参数(如果使用DBG_BUFFER()接口,最多允许20个参数)。

  2. 尽量把所有参数放到一条打印语句中。

辅助打印接口

DBG_BUFFER()接口只能打印诸如(d,i,u,o,x)的整型、字符(c)和指针类型(p),当要打印string,binary array或者BT地址时,需用到以下辅助打印接口。

  1. TRACE_STRING(char* data)
    直接打印字符串,转换符%s。
  2. TRACE_BINARY(uint16_t length, uint8_t* data)
    以16进制格式打印二进制数据,转换符%b。
  3. TRACE_BDADDR(char* bd_addr)
    以BT地址格式打印二进制字符串,转换字符%s。
    例如:
    Hex Array: 0xaa 0xbb 0xcc 0xdd 0xee 0xff –> Literal String: FF::EE::DD::CC::BB::AA
    

备注

  1. TRACE_STRING:字符串的长度最大为240字节。

  2. TRACE_BINARY:二进制数据最大为240字节。

  3. TRACE_BDADDR:单条Log最多可打印4个BT地址。

Log打印示例

下面的例子展示了log APIs的一般用法,以及在Debug Analyzer上对应的输出。

uint32_t n = 77777;
uint8_t m = 0x5A;
uint8_t bd1[6] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
uint8_t bd2[6] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
char* c1[10] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
char* c2[8] = {'a', 'b', 'C', 'd', 'E', 'F', 'g', 'H'};
char* s1 = "Hello world!";
char* s2 = "Log Test";
ADC_PRINT_TRACE1("ADC value is %d", n);
UART_PRINT_INFO3("Serial data: 0x%x, c1[%b], s1[%s]",..m, TRACE_BINARY(10, c1), TRACE_STRING(s1));
GAP_PRINT_WARN6("n[%d], m[%c] bd1[%s], bd2[%s], c2[%b], s2[%s]", n, m,..TRACE_BDADDR(bd1), TRACE_BDADDR(bd2),TRACE_BINARY(8, c2), TRACE_STRING(s2));
APP_PRINT_ERROR0("APP ERROR OCCURED...");

Debug Analyzer上对应的输出结果。

00252 10-25#17:12:02.021 132 10241 [ADC] ADC value is 77777
00253 10-25#17:12:02.021 133 10241 [UART] !**Serial data:..0x5a, c1[31-32-33-34-35-36-37-38-39-30], s1[Hello world!]
00254 10-25#17:12:02.022 134 10241 [GAP] !!*n[77777], m[Z] bd1[55::44::33::22::11::00],bd2[FF::EE::DD::CC::BB::AA],c2[61-62-43-64-45-46-67-48], s2[Log Test]
00255 10-25#17:12:02.022 135 10241 [APP] !!!APP ERROR OCCURED...

Log控制接口

有时候需要控制某些log的打开和关闭。一种方式是通过设定MPTool中的Config File,将Config File烧录到RTL87x2G。如果需要频繁改变log打印level,这种方式就不够灵活了,因为每次重烧Config File也不方便,所以SDK中提供了3个APIs来控制特定module的特定level,分别为:

  • log_module_trace_init()

  • log_module_trace_set()

  • log_module_bitmap_trace_set_update()

这里通过一些场景来帮助用户理解如何在应用程序中使用log控制的APIs。假设log打印功能已经打开,并且Config File中将所有log trace mask置成1,意味着所有module的所有level的log都会打印出来。

场景1

关闭APP module的所有trace和info级别的log

int main(void)
{
    log_module_trace_set_update (MODULE_APP, LEVEL_INFO, false);
    log_module_trace_set_update(MODULE_APP, LEVEL_TRACE, false);
    ...
}
场景2

只打开PROFILE module的log

int main(void)
{
    uint64_t mask[LEVEL_NUM];4.
    memset(mask, 0, sizeof(mask));
    log_module_trace_init(mask);
    log_module_trace_set_update(MODULE_PROFILE, LEVEL_ERROR, true);
    log_module_trace_set_update(MODULE_PROFILE, LEVEL_WARN, true);
    log_module_trace_set_update(MODULE_PROFILE, LEVEL_INFO, true);
    log_module_trace_set_update(MODULE_PROFILE, LEVEL_TRACE, true);
    ...
}
场景3

关闭PROFILE/PROTOCOL/GAP/APP module的trace级别log

int main(void)
{
    log_module_bitmap_trace_set(MODULE_BIT_PROFILE | MODULE_BIT_PROTOCOL |
                               MODULE_BIT_GAP |MODULE_BIT_APP, LEVEL_TRACE, false);
    ...
}
场景4

关闭除了APP module以外的所有trace和info级别的log,包括关闭BT Snoop log

int main(void)
{
    for (uint8_t i = 0; i < MODULE_NUM; i++)
    {
        log_module_trace_set_update((T_MODULE_ID)i, LEVEL_TRACE, false);
        log_module_trace_set_update((T_MODULE_ID)i, LEVEL_INFO, false);
    }
    log_module_trace_set_update(MODULE_APP, LEVEL_INFO, true);
    log_module_trace_set_update(MODULE_APP, LEVEL_TRACE, true);
    log_module_trace_set_update(MODULE_SNOOP, LEVEL_ERROR, false);
    ...
}

备注

如果MODULE_SNOOP的LEVEL_ERROR log打开,Debug Analyzer会产生BT Snoop的log文件 *.cfa 。如果关闭MODULE_SNOOP的log,就不会产生BT Snoop log,如上 场景2场景4

DBG_DIRECT

通过调用基于DBG_BUFFER接口的函数打印log对系统性能的影响比较小,但是打印实时性不高,因为通过这些APIs打印的log会被缓存到buffer中,在系统空闲的时候发送给Log UART。

DBG_DIRECT用来打印实时log。这个API打印log对系统的影响比较大,因为会直接发送数据给Log UART,在Log打印完之前系统不能继续执行,所以 强烈建议只在以下一些特定的情况下使用该API

  1. 在系统重启前

  2. 实时性要求高的场景

  • 打印Log的示例代码

    DBG_DIRECT("ROM version: %s %s", __DATE__, __TIME__);
    
  • Debug Analyzer上对应的输出结果

    00002 12-22#19:19:32.573 004 00000 ROM version: Dec 22 2017 14:54:04
    

SWD调试

RTL87x2G调试接口 所示,RTL87x2G实现了3个基本的调试接口:run control,breakpoint和memory access。

../../../../_images/Debug_and_Trace_port_interface_in_RTL87x2G.png

RTL87x2G调试接口

备注

RTL87x2G支持SWD,不支持JTAG。

Keil MDK-ARM SWD调试

首先正确安装和配置Keil MDK-ARM和SWD debugger。RTL87x2G支持的主要调试特性如UI上展示。

  1. Running control按钮:Running/Reset/Step

  2. Breakpoints按钮:Add/Delete/Conditional

  3. 调试功能窗口

    1. Core registers window:监视/修改MCU寄存器值

    2. Disassembly window:显示程序执行的汇编代码,或者与源代码混合显示

    3. Source code window:查看C代码,支持断点以及变量的实时显示

    4. Variable watch window:查看加入监视的变量值

    5. Memory window:显示/修改待查的内存,支持直接的地址输入和可变的地址输入

    6. Call stack and local variable window:显示当前的调用栈,以及local变量(即stack上的变量)

备注

有时候可能会遇到image无法成功下载到flash上,或者即使与J-link成功连接,但依旧找不到SWD,这时可做如下检查:

  1. 更改debug clock到一个较小的值(例如从2MHz降到1MHz),或者换用更短的SWD调试线。

  2. IC可能已经进入了DLPS模式,reset IC,这样系统会保持处于active状态5秒钟。

  3. 应用程序中是否关闭了SWD feature。