【嵌入式】如何使用JLINK RTT打印log日志

 

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

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

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

SEGGER_RTT_Write(0, "0123456789", 10);

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

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

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

由于需要获取芯片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的位置:

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

运行结果:

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");

运行结果:

有点不方便的是每次在data段添加变量的时候,数据的内存地址可能会改变,那么_SEGGER_RTT的地址就会变化,这样每次都要查看_SEGGER_RTT的地址,比较麻烦,可以使用attribute关键字指定_SEGGER_RTT变量存放在内存的位置,注意要避开启动文件中默认分配的stack的地址区域和heap地址区域,例如这里我改成:

//SEGGER_RTT_PUT_CB_SECTION(SEGGER_RTT_CB_ALIGN(SEGGER_RTT_CB _SEGGER_RTT));
SEGGER_RTT_CB _SEGGER_RTT __attribute__ ((at(0x1FFE4000)));

### STM32F103 使用 J-Link 实现实时打印 为了实现STM32F103通过J-Link进行实时打印,通常采用SWO(Single Wire Output)功能来传输调试信息。此方法允许开发者在不占用串口资源的情况下输出调试信息。 #### 配置 SWO 输出 要启用SWO输出并配置相应的设置: 1. **初始化 ITM 和 DWT 模块** 在程序启动阶段需初始化ITM (Instrumentation Trace Macrocell) 和DWT (Data Watchpoint and Trace) 模块以准备数据追踪和时间戳记录[^1]。 ```c #include "stm32f1xx_hal.h" void ITM_Init(void){ CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; ITM->LAR = 0xC5ACCE55; /* 解锁ITM寄存器 */ } ``` 2. **定义日志函数** 创建一个自定义的日志发送函数用于向PC端发送字符串消息: ```c #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) PUTCHAR_PROTOTYPE { while (ITM_SendChar(ch) == 0); return ch; } ``` 上述代码片段重定向标准库中的`printf()`函数至ITM接口,使得所有调用`printf()`的地方都能经由SWO通道输出字符流到电脑终端显示出来. 3. **编写测试代码** 下面是一段简单的应用程序示例,在主循环里周期性的打印计数值: ```c int main(void) { HAL_Init(); SystemClock_Config(); // 初始化系统时钟 ITM_Init(); printf("Start Debugging with SWO...\r\n"); uint32_t counter = 0; while(1){ printf("Counter Value: %lu\r\n",counter++); HAL_Delay(1000); // 延迟一秒 } } ``` 当运行这段代码时,每秒钟会在连接的IDE控制台窗口中看到更新后的计数器值被打印出来。 #### 设置 J-Link 调试工具 确保已安装最新版本的SEGGER Ozone 或者其他支持SWO解码的软件,并按照如下步骤操作: - 打开目标设备属性对话框; - 寻找Trace选项卡下的Source选项; - 将其设为SWO模式; - 启动跟踪会话即可查看来自MCU的信息流。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值