没有串口的情况下可以使用JLINK的RTT即 Real Time Transfer功能。RTT的工作原理大致就是在内存里面创建一个RTT控制块RTT Control Block,即SEGGER_RTT_CB结构体。这个结构体里面存放了若干个上行通道和下行通道,每个通道都有一个数据buffer以及读写指针,构成了数据队列。向RTT控制块里面的任意一个上行通道的数据队列中写入log信息,然后JLINK能够通过读写RAM内容,获取到队列的数据buffer和写指针的内容,并更新读指针。如果写入的数据太快,JLINK的刷新速率来不及读取队列内容的话,可能导致队列满,数据丢失情况。

在JLINK的安装目录里面有RTT的代码:

【嵌入式】使用JLINK RTT打印log_字符串

可以直接使用SEGGER_RTT_Write函数写字符串:

SEGGER_RTT_Write(0, "0123456789", 10);

或者使用SEGGER_RTT_printf进行格式化打印:

SEGGER_RTT_printf(0, "%s\n", "test");

使用RTTViewer工具可以通过JLINK实时读取RAM内容获取上传通道的数据内容:

【嵌入式】使用JLINK RTT打印log_字符串_02

由于需要获取芯片RAM中的数据,那就得要知道要读取的RAM地址,也就是RTT Control Block的地址。RTT Control Block的结构体定义为:

typedef struct {
char acID[16]; // Initialized to "SEGGER RTT"
int MaxNumUpBuffers; // Initialized to SEGGER_RTT_MAX_NUM_UP_BUFFERS (type. 2)
int MaxNumDownBuffers; // Initialized to SEGGER_RTT_MAX_NUM_DOWN_BUFFERS (type. 2)
SEGGER_RTT_BUFFER_UP aUp[SEGGER_RTT_MAX_NUM_UP_BUFFERS]; // Up buffers, transferring information up from target via debug probe to host
SEGGER_RTT_BUFFER_DOWN aDown[SEGGER_RTT_MAX_NUM_DOWN_BUFFERS]; // Down buffers, transferring information down from host via debug probe to target
} SEGGER_RTT_CB;

程序定义了一个RTT Control Block变量:_SEGGER_RTT

SEGGER_RTT_PUT_CB_SECTION(SEGGER_RTT_CB_ALIGN(SEGGER_RTT_CB _SEGGER_RTT));

其中acID会被初始化为固定字符串值:“SEGGER RTT”,可以用这个字符串做为Magic Number,在整个RAM空间里面搜索到RTT Control Block的位置,但是有点傻。找到编译生成的map文件,查找_SEGGER_RTT的位置:

【嵌入式】使用JLINK RTT打印log_单片机_03

 然后在RTT Viewer里面connect的时候设置RTT control block地址为_SEGGER_RTT的地址:

【嵌入式】使用JLINK RTT打印log_单片机_04

运行结果:

【嵌入式】使用JLINK RTT打印log_数据_05

RTT Viewer终端支持显示不同字符颜色以及清空屏幕指令,SEGGER_RTT.h中有控制指令定义,使用方式示例:

SEGGER_RTT_Write(0, RTT_CTRL_BG_BLACK RTT_CTRL_TEXT_RED "0123456789", 20);

SEGGER_RTT_printf(0, RTT_CTRL_BG_GREEN RTT_CTRL_TEXT_YELLOW "%s\n", "test");

运行结果:

【嵌入式】使用JLINK RTT打印log_数据_06