SEGGER调试利器RTT,替代串口,高速数据上传

Posted 【ql君】qlexcel

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SEGGER调试利器RTT,替代串口,高速数据上传相关的知识,希望对你有一定的参考价值。

移植

下载RTT代码,地址: link
在工程目录里面新建一个 SEEGER 文件夹,将 RTT 组件内容全都添加进去,添加的内容

把RTT 文件夹中的2个c文件添加到 MDK 工程

添加RTT文件夹的路径到工程

移植完成!

替代串口使用

JLINK RTT Viewer

给板子供电,然后连接好SWD或JTAG。
打开电脑端软件 JLINK RTT Viewer(安装jlink驱动后就有,在开始菜单中找)
做如下配置

从log窗口可以看到已经连接上,没有成功连接会报错。

和普通串口助手一样,有数据接收窗口和发送窗口

接收窗口可以调出多个,程序中发送数据的时候可以选择发送到哪个窗口。

RTT缓冲区

  RTT不像串口发送数据的时候,必须等着一个字符一个字符传输,而是直接直接写入数据缓冲,接收数据的时候也是写入数据缓冲,然后程序去取。因此RTT需要上行和下行的数据缓冲区。
  RTT 上行缓冲区可以相对较小。 所需的最小缓冲区大小可以近似为一毫秒内写入的数据量或者一次写入操作中写入的最大值。 如果数据发送频率较低,那么缓冲区应该有足够的空间存储一次写入的数据。 如果频繁地发送数据,则缓冲区大小应满足在一毫秒内写入最大数据量。

RTT相关函数

设置上行缓冲区

int SEGGER_RTT_ConfigUpBuffer (unsigned BufferIndex, const char* sName, void* pBuffer, unsigned BufferSize, unsigned Flags)
BufferIndex:缓冲区的编号,可以设置多个缓冲区,当发送数据的时候可以选择放入哪个缓冲区
sName:缓冲区的名字
pBuffer:缓冲区的地址
BufferSize:缓冲区的大小
Flags:当缓冲区满的时候,对新放入的数据的处理方式

当第 1 个参数 BufferIndex = 0 的时候, RTT 组件已经为其配置了默认的缓冲和默认的大小(大小配置是在 SEGGER_RTT_Conf.h 文件里面通过如下宏定义进行:
#define BUFFER_SIZE_UP (1024)
所以使用缓冲区 0 的时候, 配置比较简单(第 3 个参数缓冲区地址设置为0, 第 4 个参数缓冲区大小设置为 0 即可) :

SEGGER_RTT_ConfigUpBuffer(0, "RTTUP", 0, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);

还有两个与此函数相关的宏定义:
⚫ #define SEGGER_RTT_MAX_NUM_UP_BUFFERS (3)
用于配置最大的上行缓冲数, 也就是参数 BufferIndex 的范围。
⚫ #define SEGGER_RTT_MODE_DEFAULT SEGGER_RTT_MODE_NO_BLOCK_SKIP
用于配置 RTT 的默认工作模式, 也就是此函数的最后一个参数, 这里是表示如果上行缓冲区不够
存储要发送的数据, 将放弃写入缓冲区。 除了这种配置还有如下两种:
SEGGER_RTT_MODE_NO_BLOCK_TRIM
表示如果上行缓冲区不够存储这些数据, 能写多少写多少, 无法写入的将丢弃。
◼ SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL
表示如果上行缓冲区满,将被阻塞, 等待有空间可用。

设置下行缓冲区

 int SEGGER_RTT_ConfigDownBuffer (unsigned BufferIndex, const char* sName, char* pBuffer,
int BufferSize, int Flags);

同上面的函数一样, 仅仅是方向不同,这里是从电脑端向开发板发送数据。

其他操作函数

int SEGGER_RTT_HasKey (void)

此函数用于判断接收缓冲区中是否有数据。返回 1 表示至少 1 个数据,返回 0 表示没有。

int SEGGER_RTT_GetKey (void)

从接收缓冲区 buffer 0 中接收一个字符。

void SEGGER_RTT_SetTerminal(char TerminalId)

用于设置在 RTT Viewer 的那个终端窗口显示。

 unsigned SEGGER_RTT_WriteSting (unsigned BufferIndex, const char* s)

用于显示字符串,此函数比较简单。

int SEGGER_RTT_printf (unsigned BufferIndex, const char * sFormat,)

这个函数跟 C 库中 printf 一样,区别是不支持浮点数。

使用方式

首先,在程序初始化的时候设置好上行、下行缓冲区

	/* 配置通道0,上行配置*/
	SEGGER_RTT_ConfigUpBuffer(0, "RTTUP", 0, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);
	
	/* 配置通道0,下行配置*/	
	SEGGER_RTT_ConfigDownBuffer(0, "RTTDOWN", 0, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);

然后在需要发送数据的地方,像串口一样调用SEGGER_RTT_printf 函数发送即可。

SEGGER_RTT_SetTerminal(0);
SEGGER_RTT_printf(0, "hello\\r\\n");
SEGGER_RTT_printf(0, "SEGGER_RTT_GetKey = %c\\r\\n", GetKey);

SEGGER_RTT_SetTerminal(1);
SEGGER_RTT_WriteString(0,
					   RTT_CTRL_RESET"Red: " \\
					   RTT_CTRL_TEXT_RED"This text is red. " \\
					   RTT_CTRL_TEXT_BLACK"" \\
					   RTT_CTRL_BG_BRIGHT_GREEN"This background is green. " \\
					   RTT_CTRL_RESET"Normal text again.\\r\\n");

SEGGER_RTT_WriteString函数可以指定颜色和背景色,上面的效果:

支持

#define RTT_CTRL_RESET "[0m" // Reset to default colors
#define RTT_CTRL_CLEAR "[2J" // Clear screen, reposition cursor to top left
#define RTT_CTRL_TEXT_BLACK "[2;30m"
#define RTT_CTRL_TEXT_RED "[2;31m"
#define RTT_CTRL_TEXT_GREEN "[2;32m"
#define RTT_CTRL_TEXT_YELLOW "[2;33m"
#define RTT_CTRL_TEXT_BLUE "[2;34m"
#define RTT_CTRL_TEXT_MAGENTA "[2;35m"
#define RTT_CTRL_TEXT_CYAN "[2;36m"
#define RTT_CTRL_TEXT_WHITE "[2;37m"
#define RTT_CTRL_TEXT_BRIGHT_BLACK "[1;30m"
#define RTT_CTRL_TEXT_BRIGHT_RED "[1;31m"
#define RTT_CTRL_TEXT_BRIGHT_GREEN "[1;32m"
#define RTT_CTRL_TEXT_BRIGHT_YELLOW "[1;33m"
#define RTT_CTRL_TEXT_BRIGHT_BLUE "[1;34m"
#define RTT_CTRL_TEXT_BRIGHT_MAGENTA "[1;35m"
#define RTT_CTRL_TEXT_BRIGHT_CYAN "[1;36m"
#define RTT_CTRL_TEXT_BRIGHT_WHITE "[1;37m"
#define RTT_CTRL_BG_BLACK "[24;40m"
#define RTT_CTRL_BG_RED "[24;41m"
#define RTT_CTRL_BG_GREEN "[24;42m"
#define RTT_CTRL_BG_YELLOW "[24;43m"
#define RTT_CTRL_BG_BLUE "[24;44m"
#define RTT_CTRL_BG_MAGENTA "[24;45m"
#define RTT_CTRL_BG_CYAN "[24;46m"
#define RTT_CTRL_BG_WHITE "[24;47m"
#define RTT_CTRL_BG_BRIGHT_BLACK "[4;40m"
#define RTT_CTRL_BG_BRIGHT_RED "[4;41m"
#define RTT_CTRL_BG_BRIGHT_GREEN "[4;42m"
#define RTT_CTRL_BG_BRIGHT_YELLOW "[4;43m"
#define RTT_CTRL_BG_BRIGHT_BLUE "[4;44m"
#define RTT_CTRL_BG_BRIGHT_MAGENTA "[4;45m"
#define RTT_CTRL_BG_BRIGHT_CYAN "[4;46m"
#define RTT_CTRL_BG_BRIGHT_WHITE "[4;47m"

int main(void)

	uint8_t ucKeyCode;		/* 按键代码 */
	uint32_t i = 0;
	int GetKey;

	/*
		由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
		启动文件 startup_stm32f4xx.s 会调用 system_stm32f4xx.c 中的 void SystemInit(void)。
		SystemInit()函数配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。

		安富莱STM32-V5开发板主晶振是25MHz, 内部PLL倍频到168MHz。如果需要更改主频,可以修改下面的文件:
		\\User\\bsp_stm32f4xx\\system_stm32f4xx.c
		文件开头的几个宏是PLL倍频参数,修改这些宏就可以修改主频,无需更改硬件。
	*/

	bsp_Init();		/* 硬件初始化 */

	bsp_StartAutoTimer(0, 100);	/* 启动1个100ms的自动重装的定时器 */
	
	/* 配置通道0,上行配置*/
	SEGGER_RTT_ConfigUpBuffer(0, "RTTUP", NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);
	
	/* 配置通道0,下行配置*/	
	SEGGER_RTT_ConfigDownBuffer(0, "RTTDOWN", NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);

	/* 进入主程序循环体 */
	while (1)
	
		bsp_Idle();		/* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */

		/* 判断定时器超时时间 */
		if (bsp_CheckTimer(0))	
		
			bsp_LedToggle(4);	
		
		
		/* 做一个简单的回环功能 */
		if (SEGGER_RTT_HasKey()) 
		
			GetKey = SEGGER_RTT_GetKey();
			SEGGER_RTT_SetTerminal(0);
			SEGGER_RTT_printf(0, "SEGGER_RTT_GetKey = %c\\r\\n", GetKey);
		

		/* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
		ucKeyCode = bsp_GetKey();	/* 读取键值, 无键按下时返回 KEY_NONE = 0 */
		if (ucKeyCode != KEY_NONE)
		
			switch (ucKeyCode)
			
				case KEY_DOWN_K1:			/* K1键按下 */
					SEGGER_RTT_SetTerminal(1);
					SEGGER_RTT_WriteString(0,
										   RTT_CTRL_RESET"Red: " \\
										   RTT_CTRL_TEXT_RED"This text is red. " \\
										   RTT_CTRL_TEXT_BLACK"" \\
										   RTT_CTRL_BG_BRIGHT_GREEN"This background is green. " \\
										   RTT_CTRL_RESET"Normal text again.\\r\\n");
				
					break;

				case KEY_DOWN_K2:			/* K2键按下 */
					SEGGER_RTT_SetTerminal(2);
					SEGGER_RTT_WriteString(0, RTT_CTRL_TEXT_BRIGHT_GREEN"KEY_DOWN_K2, ArmFly Real-Time-Terminal Sample\\r\\n");
					break;

				case KEY_DOWN_K3:			/* K3键按下 */
					SEGGER_RTT_SetTerminal(3);
					SEGGER_RTT_printf(0, "KEY_DOWN_K3, i = %d\\r\\n", i++);
					break;

				default:
					/* 其它的键值不处理 */
					break;
			
		
	

配合J-scope使用,高速数据图形化

J-scope支持HSS模式和RTT模式。
HSS 模式
HSS 模式比较简单,仅需大家将 MDK 生成的可执行文件 xxx.axf 或者 IAR 生成的可执行文件 xxx.out文件加载到 JScope 软件里面即可。
 优势
 随时随地都可以连接目标板,不影响目标板的正常功能,不需要额外资源。 无需用户写目标板代码。
 不需要用到 SWO 引脚, 使用标准的下载接口即可。 以我们的开发板为例,用到 VCC, GND,SWDIO, SWCLK 和 NRST。 大家使用三线 JLINK-OB 也是没问题的, 仅需用到 GND, SWDIO和 SWCLK。
 劣势
 相对 RTT 模式,速度慢。
 采样速度基本固定在 1KHz 左右,速度较慢, 仅适合采样变量变化速度低于 1KHz 的情况。

RTT 模式获取数据
这种方式类似串口上传数据,只是换成了 SWD 接口。
 且随时随地都可以连接目标板,不影响目标板的正常功能。
 允许比 HSS 更高的数据吞吐量。高达 2 MB / s 可以实现。即使目标上有 512 字节的小缓冲区,也可以达到 1 MB / s。
 J-Scope 数据采集与目标板应用程序的执行同步,因为应用程序决定何时以及如何采样数据。
 J-Scope 不需要知道变量的位置。 RTT 缓冲区的位置由 J-Scope 自动检测。
时间戳等数据可以被添加到数据样本中。
 不需要用到 SWO 引脚, 使用标准的下载接口即可

第 1 步:需要按照上面方法移植 SEGGER 的 RTT 组件
第 2 步:配置上行缓冲区。
这点比较重要, 比如本期教程配套例子的配置如下:

SEGGER_RTT_ConfigUpBuffer(1, "JScope_u2", buf, 2048, SEGGER_RTT_MODE_NO_BLOCK_SKIP);

这个函数在上面有详细说明。我们这里重点说下 J-Scope 使用 RTT 模式时,此函数的注意事项。
 第 1 个参数要填通道 1,实际测试通道 0 不可用。
 第 2 个参数非常重要,要使用固定的格式,字符串 JScope_是固定的,主要是下划线后面的字符。
支持的格式如下:

比如:
JScope_t4u1i4: 表示数据包格式为 32 位时间戳, 8 位无符号数和 32 位有符号数组成。并且数据顺序也是按照这个排列。
JScope_u2 : 表示数据包仅包含 16 位无符号数。

使用 RTT 模式的话,最后一个参数仅支持 SEGGER_RTT_MODE_NO_BLOCK_SKIP 和
SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL
第 3 步: 发送数据。
发送数据就比较简单了,调用函数 SEGGER_RTT_Write 即可。

int main(void)
            
	bsp_Init();

	/* 配置通道1,上行配置*/	
	SEGGER_RTT_ConfigUpBuffer(1, "JScope_i2", buf, 4096, SEGGER_RTT_MODE_NO_BLOCK_SKIP);

    while(1)
    
	    debug_enc++;
		SEGGER_RTT_Write(1, &debug_enc, 2);
	

打开J-scope配置如下:

右下角看采样率


以上是关于SEGGER调试利器RTT,替代串口,高速数据上传的主要内容,如果未能解决你的问题,请参考以下文章

安富莱专题教程第5期工程调试利器RTT实时数据传输组件,替代串口调试,速度飞快,可以在中断和多任务中随意调用

Segger RTT使用注意事项

安富莱专题教程第6期SEGGER的J-Scope波形上位机软件,RTT模式波形上传速度可狂飙到500KB/S左右

RTT 查看器未识别 Segger RTT 控制块

嵌入式芯片调试神器-J-Link RTT详解

嵌入式芯片调试神器-J-Link RTT详解