一、RTT是什么
J-Link RTT – Real Time Transfer: SEGGER’s Real Time Transfer (RTT) is the proven technology for system monitoring and interactive user I/O in embedded applications. It combines the advantages of SWO and semihosting at very high performance.
Bi-directional communication with the target application
Very high transfer speed without affecting real time behavior
Uses debug channel for communication
No additional hardware or pin on target required
Supported by any J-Link model
Supported by ARM Cortex-M0/M0+/M1/M3/M4/M7/M23/M33 and Renesas RX100/200/600
Complete implementation code providing functionality and freedom
简而言之,RTT是SEGGER基于J-Link开发的一款可使用SWO接口(仅SWDIO、SWCLK和GND三线即可)与嵌入式系统进行输入输出交互的组件,不影响、不占用系统中的任何硬件资源。
二、为什么要使用RTT
在一些项目中,由于成本考量等原因,选用了硬件资源较为紧张的STM32,譬如只有两个串口。如果这仅有的两个串口都被系统占用了的话,我们在调试的时候就没有办法使用串口来对接控制台以及打印调试信息了。
令人欣慰的是,RT-Thread(Nano)的FinSH(shell)组件并非只局限于串口,且它的输入输出接口都被剥离了出来,方便我们将控制台重定向到其它硬件上如以太网、USB以及本文的J-Link RTT 等。
三、RTT怎么用
IDE: Windows + MDK + LL Libraries for STM32
需要下载安装:J-Link / J-Trace
移植RTT到MDK工程中
举例说明:
①找到JLink安装目录
我的安装目录:
②将..\SEGGER\JLink\Samples\RTT目录中的SEGGER_RTT_Vxxxx.zip拷贝到工程目录中解压,创建相关文件夹管理,并加入工程,添加头文件目录。
我的工程结构:
修改代码与配置(LL库)
①修改SEGGER_RTT_Conf.h
按需修改上、下行以及缓冲区大小
//
// Most common case:
// Down-channel 0: RTT
// Down-channel 1: SystemView
//
#ifndef SEGGER_RTT_MAX_NUM_DOWN_BUFFERS
#define SEGGER_RTT_MAX_NUM_DOWN_BUFFERS (3) // Max. number of down-buffers (H->T) available on this target (Default: 3)
#endif
#ifndef BUFFER_SIZE_UP
#define BUFFER_SIZE_UP (1024) // Size of the buffer for terminal output of target, up to host (Default: 1k)
#endif
#ifndef BUFFER_SIZE_DOWN
#define BUFFER_SIZE_DOWN (16) // Size of the buffer for terminal input to target from host (Usually keyboard input) (Default: 16)
#endif
#ifndef SEGGER_RTT_PRINTF_BUFFER_SIZE
#define SEGGER_RTT_PRINTF_BUFFER_SIZE (64u) // Size of buffer for RTT printf to bulk-send chars via RTT (Default: 64)
#endif
#ifndef SEGGER_RTT_MODE_DEFAULT
#define SEGGER_RTT_MODE_DEFAULT SEGGER_RTT_MODE_NO_BLOCK_SKIP // Mode for pre-initialized terminal channel (buffer 0)
#endif
②修改RT-Thread Nano -> board.c
我使用了一个宏来管理该模块和串口的互斥,方便调试的时候相互切换:
#define USART_SHELL USART1
#ifndef USART_SHELL
#include "SEGGER_RTT.h"
#endif
使用如下代码来导出至RT-Thread Nano的自动初始化功能:
#ifndef USART_SHELL
int RTT_init()
{
SEGGER_RTT_ConfigUpBuffer(0, "RTTUP", NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);
SEGGER_RTT_ConfigDownBuffer(0, "RTTDOWN", NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);
SEGGER_RTT_SetTerminal(0);
return RT_EOK;
}
INIT_COMPONENT_EXPORT(RTT_init);
#endif
使用如下代码来对接RT-Thread Nano的输入输出函数(LL库):
void rt_hw_console_output(const char str)
{
#ifdef USART_SHELL
rt_size_t i = 0, size = 0;
char a = '\r';
size = rt_strlen(str);
for (i = 0; i < size; i++)
{
while(!LL_USART_IsActiveFlag_TXE(USART_SHELL))
{
}
if ((str + i) == '\n')
{
LL_USART_TransmitData8(USART_SHELL, a);
while(!LL_USART_IsActiveFlag_TXE(USART_SHELL))
{
}
}
LL_USART_TransmitData8(USART_SHELL, *(str + i));
}
#else
SEGGER_RTT_printf(0, str);
#endif
}
char rt_hw_console_getchar()
{
int ch = -1;
#ifdef USART_SHELL
if(LL_USART_IsActiveFlag_RXNE(USART_SHELL))
{
ch = LL_USART_ReceiveData8(USART_SHELL);
}
else
{
if(LL_USART_IsActiveFlag_ORE(USART_SHELL))
{
LL_USART_ClearFlag_ORE(USART_SHELL);
}
rt_thread_mdelay(10);
}
#else
if(SEGGER_RTT_HasKey())
ch = SEGGER_RTT_GetKey();
else
rt_thread_mdelay(10);
#endif
return ch;
}
使用J-Link RTT Viewer
选择芯片型号并连接:
输出功能:
输入功能:
问题与总结
本文简述了如何将RTT对接到RT-Thread Nano中来代替常用的串口打印。RT-Thread(Nano)的模块化、接口抽象和面向对象设计能够让我们很轻松地做功能移植,灵活且方便。然而在使用过程中发现有如下问题有待解决,时间原因我暂时不去追根溯源,欢迎大家研究并留言:
原作者:CharlesX