本帖最后由 hawk2003454 于 2024-5-20 16:44 编辑
RTT全称是Real Time Transmit(实时传输)是Segger公司推出的调试手段之一。它是一种用于嵌入式中与用户进行交互的技术。使用RTT可以从MCU快速输出调试信息和数据,且不影响MCU的实时性。只要支持J-Link的MCU就可使用RTT功能,兼容性非常强。RTT支持两个方向的多个通道,上到主机,下到目标,它可以用于不同的目的,为用户提供尽可能多的自由。默认实现每个方向使用一个通道,用户可在在调试终端输入和输出。使用J-Link RTT Viewer,可用于“虚拟”终端,允许打印到多个窗口(例如,一个用于标准输出,一个对于错误输出,一个用于调试输出)。
本演示基于官方固件库的模板工程。
移植RTT代码
1.安装Jlink软件包,相关的下载以及安装方式就不做过多的介绍。
2.在安装完成J-Link全家桶以后,在电脑安装路径下的D:\Program Files\SEGGER\JLink\Samples\RTT文件夹下面存放的就是RTT_Viewer的源代码。将此文件夹下面的SEGGER_RTT_Vxxxx.zip文件解压。
得到如下文件:
其中RTT文件夹下面存放的即为我们需要的源码,将RTT整个文件夹拷贝到我们的工程路径下面。
3.在工程中创建一个RTT的分组,添加两个源文件SEGGER_RTT.c和SEGGER_RTT_printf.c
4.将工程目录中的RTT文件夹包含进来:
在这里插入图片描述
5.在用到了RTT函数的文件添加头文件:
#include “SEGGER_RTT.h”
6.在main函数添加SEGGER_RTT_printf(0, “hello world\r\n”);
如果打印某个变量:SEGGER_RTT_printf(0, “n=%d\r\n”, n);
第一个参数0表示终端号。
编译成功后烧录。
连接RTT Viewer
在文件路径D:\Program Files \SEGGER\JLink找到JLinkRTTViewer.exe打开
(也可能在D:\Program Files(x86),具体情况可以自己去找找)
注意要先用Jlink连接MCU,打开File->Connect
选择对应的MCU型号,RTT控制块默认选择自动检测即可
如果没有对应型号选择,可以选择对应内核,然后手动填写控制块的起始地址
(为什么只有选择型号才能自动检测,因为选择型号就等于选择了芯片RAM的大小和起始地址,RTT Viewer就能在RAM区域自动搜索RTT控制块)
打开map文件,找到如下字段,该地址就是RTT控制块的地址,填入上面的地址栏中。
连接成功后:
这里要注意RTT与串口的不同,前面已经了解到RTT是MCU先把log缓存在一段RAM区,然后当我们用RTT Viewer连接上时,才会一次性读出来全部的数据,因为必须MCU先上电才能用jlink连接上,如果有一些上电立即打印的log也不会丢失,当连接后还是能读取到。
浮点数处理
修改SEGGER_RTT_printf.c,调用关系SEGGER_RTT_printf–>SEGGER_RTT_vprintf, 固在SEGGER_RTT_vprintf中添加如下代码:
int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList) {
char c;
SEGGER_RTT_PRINTF_DESC BufferDesc;
int v;
unsigned NumDigits;
unsigned FormatFlags;
unsigned FieldWidth;
char acBuffer[SEGGER_RTT_PRINTF_BUFFER_SIZE];
BufferDesc.pBuffer = acBuffer;
BufferDesc.BufferSize = SEGGER_RTT_PRINTF_BUFFER_SIZE;
BufferDesc.Cnt = 0u;
BufferDesc.RTTBufferIndex = BufferIndex;
BufferDesc.ReturnValue = 0;
do {
c = *sFormat;
sFormat++;
if (c == 0u) {
break;
}
if (c == '%') {
//
// Filter out flags
//
FormatFlags = 0u;
v = 1;
do {
c = *sFormat;
switch (c) {
case '-': FormatFlags |= FORMAT_FLAG_LEFT_JUSTIFY; sFormat++; break;
case '0': FormatFlags |= FORMAT_FLAG_PAD_ZERO; sFormat++; break;
case '+': FormatFlags |= FORMAT_FLAG_PRINT_SIGN; sFormat++; break;
case '#': FormatFlags |= FORMAT_FLAG_ALTERNATE; sFormat++; break;
default: v = 0; break;
}
} while (v);
//
// filter out field with
//
FieldWidth = 0u;
do {
c = *sFormat;
if ((c < '0') || (c > '9')) {
break;
}
sFormat++;
FieldWidth = (FieldWidth * 10u) + ((unsigned)c - '0');
} while (1);
//
// Filter out precision (number of digits to display)
//
NumDigits = 0u;
c = *sFormat;
if (c == '.') {
sFormat++;
do {
c = *sFormat;
if ((c < '0') || (c > '9')) {
break;
}
sFormat++;
NumDigits = NumDigits * 10u + ((unsigned)c - '0');
} while (1);
}
//
// Filter out length modifier
//
c = *sFormat;
do {
if ((c == 'l') || (c == 'h')) {
sFormat++;
c = *sFormat;
} else {
break;
}
} while (1);
#if 0
//
// Handle specifiers
//
switch (c) {
case 'c': {
char c0;
v = va_arg(*pParamList, int);
c0 = (char)v;
_StoreChar(&BufferDesc, c0);
break;
}
case 'd':
v = va_arg(*pParamList, int);
_PrintInt(&BufferDesc, v, 10u, NumDigits, FieldWidth, FormatFlags);
break;
case 'u':
v = va_arg(*pParamList, int);
_PrintUnsigned(&BufferDesc, (unsigned)v, 10u, NumDigits, FieldWidth, FormatFlags);
break;
case 'x':
case 'X':
v = va_arg(*pParamList, int);
_PrintUnsigned(&BufferDesc, (unsigned)v, 16u, NumDigits, FieldWidth, FormatFlags);
break;
case 's':
{
const char * s = va_arg(*pParamList, const char *);
do {
c = *s;
s++;
if (c == '\0') {
break;
}
_StoreChar(&BufferDesc, c);
} while (BufferDesc.ReturnValue >= 0);
}
break;
case 'p':
v = va_arg(*pParamList, int);
_PrintUnsigned(&BufferDesc, (unsigned)v, 16u, 8u, 8u, 0u);
break;
case '%':
_StoreChar(&BufferDesc, '%');
break;
default:
break;
}
#else
//
// Handle specifiers
//
switch (c) {
case 'c': {
char c0;
v = va_arg(*pParamList, int);
c0 = (char)v;
_StoreChar(&BufferDesc, c0);
break;
}
case 'd':
v = va_arg(*pParamList, int);
_PrintInt(&BufferDesc, v, 10u, NumDigits, FieldWidth, FormatFlags);
break;
case 'u':
v = va_arg(*pParamList, int);
_PrintUnsigned(&BufferDesc, (unsigned)v, 10u, NumDigits, FieldWidth, FormatFlags);
break;
case 'x':
case 'X':
v = va_arg(*pParamList, int);
_PrintUnsigned(&BufferDesc, (unsigned)v, 16u, NumDigits, FieldWidth, FormatFlags);
break;
case 's':
{
const char * s = va_arg(*pParamList, const char *);
do {
c = *s;
s++;
if (c == '\0') {
break;
}
_StoreChar(&BufferDesc, c);
} while (BufferDesc.ReturnValue >= 0);
}
break;
case 'p':
v = va_arg(*pParamList, int);
_PrintUnsigned(&BufferDesc, (unsigned)v, 16u, 8u, 8u, 0u);
break;
case '%':
_StoreChar(&BufferDesc, '%');
break;
// 添加输出浮点数的功能。默认带两位小数。
case 'f':
case 'F': {
float fv = (float)va_arg(*pParamList, double); // 取出输入的浮点数值
if(fv < 0) _StoreChar(&BufferDesc, '-'); // 判断正负号
v = abs((int)fv); // 取正整数部分
_PrintInt(&BufferDesc, v, 10u, NumDigits, FieldWidth, FormatFlags); //显示整数
_StoreChar(&BufferDesc, '.'); //显示小数点
v = abs((int)(fv * 100));
v = v % 100;
_PrintInt(&BufferDesc, v, 10u, 2, FieldWidth, FormatFlags); //显示小数点后两位
break;
}
default:
break;
}
#endif
sFormat++;
} else {
_StoreChar(&BufferDesc, c);
}
} while (BufferDesc.ReturnValue >= 0);
if (BufferDesc.ReturnValue > 0) {
//
// Write remaining data, if any
//
if (BufferDesc.Cnt != 0u) {
SEGGER_RTT_Write(BufferIndex, acBuffer, BufferDesc.Cnt);
}
BufferDesc.ReturnValue += (int)BufferDesc.Cnt;
}
return BufferDesc.ReturnValue;
}
至于Jlink RTT Viewer增加时间戳+打印不同颜色优先级LOG,均可参考网上的各位大神的教程,本人在此抛转引玉
另比较好用的上位机参考:https://gitee.com/bds123/rtt_t2
|
共1人点赞
|