MCU:STM32F407VE

MDK:5.29

IAR:8.32

目录--点击可快速直达

写在前面

本文介绍了J-Link RTT的部分使用内容,很多地方参考和使用了J-Link的官方资料,有的地方可能翻译的不太准确,请见谅。

如果想了解更加准确详细的内容,请点此处


什么是RTT?

RTT(Real Time Transfer)是一种用于嵌入式中与用户进行交互的技术,它结合了SWO和半主机的优点,具有极高的性能。

使用RTT可以从MCU非常快速输出调试信息和数据,且不影响MCU实时性。这个功能可以用于很多支持J-Link的设备和MCU,兼容性强。

RTT支持两个方向的多个通道,上到主机,下到目标,它可以用于不同的目的,为用户提供尽可能多的自由。默认实现每个方向使用一个通道,用于可打印终端输入和输出。

使用J-Link RTT Viewer,可用于“虚拟”终端,允许打印到多个窗口(例如,一个用于标准输出,一个对于错误输出,一个用于调试输出)。

RTT的工作原理

RTT在MCU的存储器中使用SEGGER RTT控制块结构管理数据读写。控制块对于每个可用的信道都在内存中包含了一个ID,通过J-Link或者环形缓冲结构区(链表)都可以通过ID找到对应的控制块。

可用信道的最大数目可以在编译时配置,并且每个缓冲区都可以在MCU运行时配置和使用。上下缓冲区可以分开处理。每个通道都可以配置为阻塞或非阻塞。

在阻塞模式下,应用程序将等待缓冲区写满,直到可以写入所有内存为止,这将导致应用程序处于阻塞状态,但可以防止数据丢失。

在非阻塞模式下,只会写入适合缓冲区的数据,或完全不写入缓冲区,其余的数据将被丢弃。这样即使没有连接调试器,也可以实时运行。开发人员不必创建特殊的调试版本,并且代码可以保留在发布应用程序中。

RTT的性能

RTT的性能明显高于其他任何用于将数据输出到主机PC的方式。平均一行文本可以在1微秒或更短的时间内输出。基本上相当于做一个memcopy()的时间。

RTT实现代码使用大约500字节的ROM和(n(通道数) * (24字节ID+24字节))的RAM。推荐的大小是1 kByte(上行信道)和16到32字节(下行信道),这取决于输入/输出的负载。

快速使用教程

1.首先安装J-Link的软件驱动

2.安装完成后,打开J-Link的安装目录(开始->SEGGR->J-Link RTT Viewer->右键打开文件所在位置->然后继续右键打开文件所在位置->此时就是安装目录了),

找到如下路径SEGGER\JLink_V632f\Samples\RTT,解压路径里面的压缩包SEGGER_RTT_V632f.zip(不同的版本,V后面的数字可能不一样)。

3.将解压完的文件拷贝到代码工程目录中。

4.在项目工程中加入SEGGER_RTT_V632f\RTT目录下的全部四个文件。工程添加文件方法请自行百度。

5.工程加入文件后,在想要用到RTT的文件中包含#include "SEGGER_RTT.h",然后直接调用SEGGER_RTT_printf()就好了,

例如SEGGER_RTT_printf(0,"hello world!")这个和C语言的printf的格式差不多,就是前面加了一个端口0的参数。(详细信息请看高级使用教程

6.然后点击开始->SEGGR->J-Link RTT Viewer,打开J-Link RTT Viewer 选择好你的芯片型号后,点击确认。

7.然后就能看到我们打印的内容了。

高级使用教程

1.部分函数介绍:

(1)void SEGGER_RTT_Init (void) RTT初始化函数,应放于程序开始阶段。


(2)int SEGGER_RTT_GetKey (void); 从RTT终端获取一个按键字符。

Return Value

Value Meaning
>=0 返回按键字符(0-255)
< 0 缓存区中没有有效的字符

示例代码:

    int c;
c = SEGGER_RTT_GetKey();
if (c == 'q') {
exit();
}

(3)int SEGGER_RTT_HasKey (void);检测缓存区中是否还有字符。

Return Value

Value Meaning
1 缓存区中至少有一个字符是有效的
0 缓存区中没有有效的字符

示例代码:

   if (SEGGER_RTT_HasKey()) {
int c = SEGGER_RTT_GetKey();
}

(4)int SEGGER_RTT_printf (unsigned BufferIndex, const char * sFormat, …)格式化输出字符串

Return Value

Value Meaning
>=0 已经发送的字符数
< 0 发生错误

附加信息:

 转换规范具有以下语法:

 %[标志][字段宽度][.精度]转换指定程序

 支持的标志:

 -:在字段宽度内左对齐

 +:始终打印有符号转换的符号扩展名

 0:用0代替空格。使用“-”标志或精度时忽略

 支持的转换说明符:

 c:将参数打印为一个字符

 d:将参数打印为有符号整数

 u:将参数打印为无符号整数

 x:将参数打印为十六进制整数

 s:打印参数指向的字符串

 p:将参数打印为8位十六进制整数。

 ps.似乎官方没有给float类型格式化输出方式。

示例代码:

SEGGER_RTT_printf(0, "SEGGER RTT Sample. Uptime: %.10dms.", /*OS_Time*/ 890912);

同时,可以使用SEGGER_RTT_printf()来设置字体颜色还背景颜色:

例如:

SEGGER_RTT_printf(0,RTT_CTRL_BG_WHITE);
SEGGER_RTT_printf(0,RTT_CTRL_TEXT_BLUE);

(5)void SEGGER_RTT_SetTerminal(char TerminalId);设置虚拟终端ID。

Return Value

Parameter Meaning
TerminalId 虚拟终端的ID

示例代码:

//
// Send a string to terminal 1 which is used as error out.
//
SEGGER_RTT_SetTerminal(1); // Select terminal 1
SEGGER_RTT_WriteString(0, "ERROR: Buffer overflow");
SEGGER_RTT_SetTerminal(0); // Reset to standard terminal

SEGGER_RTT_WriteString中的0参数,是通道号,不是终端号。


(6)int SEGGER_RTT_WaitKey (void);检测缓存区中是否还有字符。

Return Value

Value Meaning
≥0 等待返回一个按键值

示例代码:

   int c = 0;
do {
c = SEGGER_RTT_WaitKey();
} while (c != 'c');

附上测试代码

/*terminal 0: if you press any key in the keyboard ,terminal 0 will show the key value witch you press.
terminal 1: show the date
terminal 2: show the time
*/
if (SEGGER_RTT_HasKey())
{
int c = SEGGER_RTT_GetKey();
SEGGER_RTT_SetTerminal(0);
SEGGER_RTT_Write (0, &c, 1);
SEGGER_RTT_printf(0,"\n");
}
//GET DATA
HAL_RTC_GetTime(&hrtc,&_current_time,RTC_FORMAT_BIN);
//GET TIME
HAL_RTC_GetDate(&hrtc,&_current_date,RTC_FORMAT_BIN);
//Printf
SEGGER_RTT_SetTerminal(1);
SEGGER_RTT_printf(0,"%d . %d . %d \n",_current_date.Year,_current_date.Month,_current_date.Date);
SEGGER_RTT_SetTerminal(2);
SEGGER_RTT_printf(0,"%d : %d : %d \n\n",_current_time.Hours,_current_time.Minutes,_current_time.Seconds);

代码的下载链接:https://download.csdn.net/download/xue745146527/12045381 (工程包含了Keil 和 IAR )

2019年12月27日更新--增加打印float的功能

因为官方的RTT View不能打印出float类型的数据,因此我简单写了个float转字符串的函数。

unsigned char *out_float(double value, unsigned char decimal_digit, unsigned char *output_length)
{
unsigned char _output[20];
unsigned long integer;
unsigned long decimal;
unsigned char _output_length = 0;
unsigned char _length_buff = 0;
static unsigned char *return_pointer;
unsigned char signal_flag;
if (value < 0)
signal_flag = 1;
else
signal_flag = 0;
value = fabs(value);
integer = (unsigned long)value;
decimal = (unsigned long)((value - integer) * pow(10, decimal_digit)); unsigned long integer_buff = integer;
unsigned long decimal_buff = decimal; while (1)
{
if (integer / 10 != 0)
_length_buff++;
else
{
_length_buff++;
break;
}
integer = integer / 10;
}
for (int i = 0; i < _length_buff; i++)
{
if (i == _length_buff - 1)
_output[_output_length] = integer_buff % 10 + 0x30;
else
{
//_output[_output_length] = integer_buff / 10 % 10 + 0x30;
_output[_output_length] = integer_buff / (unsigned long)pow(10, _length_buff - i - 1) % 10 + 0x30;
integer_buff = integer_buff % (unsigned long)pow(10, _length_buff - i - 1);
//integer_buff = integer_buff % 10;
}
_output_length++;
}
_output[_output_length] = '.';
_output_length++;
_length_buff = 0;
while (1)
{
if (decimal / 10 != 0)
_length_buff++;
else
{
_length_buff++;
break;
}
decimal = decimal / 10;
}
for (int i = 0; i < _length_buff; i++)
{
if (i == _length_buff - 1)
_output[_output_length] = decimal_buff % 10 + 0x30;
else
{
_output[_output_length] = decimal_buff / (unsigned long)pow(10, _length_buff-i-1) % 10 + 0x30;
decimal_buff = decimal_buff % (unsigned long)pow(10, _length_buff - i - 1);
} _output_length++;
}
_output[_output_length] = 0x00;
_output_length++;
return_pointer = (unsigned char *)realloc(return_pointer,_output_length); *output_length = _output_length - 1;
if (return_pointer == 0)
return 0;
else
{
if (signal_flag == 1)
{
return_pointer[0] = '-';
memcpy(return_pointer+1, _output, _output_length);
}
else
memcpy(return_pointer, _output, _output_length);
}
return return_pointer;
}

Parameter

Value Meaning
value 想要打印的数据
decimal_digit 数字小数部分的位数
_output_length 输出字符串的长度

Return Value

Value Meaning
unsigned char* 返回一个字符串指针

示例代码:

   float value = 3.1415;
unsigned char length;
SEGGER_RTT_printf(0,"value = %s \n",out_float(value,4,&length));

调试备忘录-J-Link RTT的使用(原理 + 教程 + 应用 + 代码)的更多相关文章

  1. 调试备忘录-NTC电阻的使用(教程 + 代码)

    软件环境:CodeWarrior 11.1 硬件环境:NXP S9KEAZ64A 传感器参数:NTC热敏电阻(R25 = 50k,B25-50 3950) 写在前面 最近做小项目需要用到NTC电阻,因 ...

  2. 最短路径A*算法原理及java代码实现(看不懂是我的失败)

    算法仅仅要懂原理了,代码都是小问题,先看以下理论,尤其是红色标注的(要源代码请留下邮箱,有測试用例,直接执行就可以) A*算法 百度上的解释: A*[1](A-Star)算法是一种静态路网中求解最短路 ...

  3. 编译原理-词法分析04-NFA & 代码实现

    编译原理-词法分析04-NFA & 代码实现 0.术语 NFA 非确定性有穷自动机nondeterministic finite automation. ε-转换ε-transition 是无 ...

  4. 对象部分初始化:原理以及验证代码(双重检查锁与volatile相关)

    对象部分初始化:原理以及验证代码(双重检查锁与volatile相关) 对象部分初始化被称为 Partially initialized objects / Partially constructed ...

  5. 华农oj Problem J: 幻化【贪心/抽屉原理】

    Problem J: 幻化 Time Limit: 2 Sec Memory Limit: 128 MB Submit: 18 Solved: 3 [Submit][Status][Web Board ...

  6. 调试备忘录-nRF24L01P的使用(教程 + 源码)

    目录--点击可快速直达 MCU:KEAZ64A MDK:CodeWarrior 11.0 目录 写在前面 什么是nRF24L01P? nRF24L01P模块的简单介绍 nRF24L01P的工作模式 n ...

  7. Dom4j工具j解析XML原理和示例代码

    import java.io.File; import java.util.ArrayList; import java.util.Iterator; import java.util.List; i ...

  8. 调试备忘录-SWD协议解析

    目录--点击可快速直达 目录 写在前面 1  SWD协议简介 2  SWD物理层协议解析 2.1  SWD通信时序分析 2.2  SWD 寄存器简介 2.2.1  DP寄存器 2.2.2  AP寄存器 ...

  9. 调试备忘录-RS485 MODBUS RTU协议简述

    目录--点击可快速直达 目录 写在前面 先简单说下什么是MODBUS? 参考文章 写在前面 最近在做和物联网有关的小项目,有一个传感器通讯用到了RS485 MODBUS RTU协议,所以就写个随笔记录 ...

随机推荐

  1. Windows File Recovery - 微软官方文件恢复工具

    假如你不小心误删除了文件或因各种意外情况丢失数据后,你可以通过 微软这款工具 这个工具来尝试恢复它们.WinFR 工具支持读取本机硬盘.移动硬盘.U 盘,或者连接相机.手机.使用读卡器来恢复 SD.T ...

  2. 03 AMD规范的基础使用详解

    AMD模块规范 1.1 AMD规范说明 AMD规范专门用来实现浏览器端的模块化,并且模块的加载是异步的:引入一个第三方的require.js脚本用来解析AMD规范编写的模块 1.2 基本语法 使用de ...

  3. Python学习周期 学习Python要多久?

    学习python编程需要多长时间?首先我们需要明确一点,在互联网技术领域,技术始终在不断的迭代升级,只要进入IT行业就要时刻保持学习的状态,才能不被技术进步的车轮碾压.我们目前讨论的python学习周 ...

  4. iOS倒计时button闪烁

    v _button.titleLabel.text = [NSString stringWithFormat:@"%d后重发",t]; [_button setTitle:[NSS ...

  5. MySQL之高级操作

     新增数据: 基本语法: insert into 表名 [(字段列表)] values(列表值) 在数据插入的时候,假设主键对应的值已经存在,插入一定会失败 主键冲突: 当主键存在冲突的时候(Dupl ...

  6. Java方法(函数)

    4.1方法简介 方法是语句的集合,他们在一起执行一个功能: 1.方法是解决一类问题的步骤的有序组合(功能块) 2.方法包含于类与对象中 3.方法在程序中创建,在其它地方引用 4.原子性:单一职能原则( ...

  7. Redis的各种数据类型到底能玩出什么花儿?

    https://mp.weixin.qq.com/s/ZSQ9vCkWXYuLrKS0UJ4RIg 两个星期终于肝了出来,Redis相关问题脑图,终于整理完了!!! 文末无套路分享~~附获取方式 Re ...

  8. sed打印包含一个字符串的行到包含另一个字符串的行解答

    sed -n '/字符串1/,/字符串2/p' filename  这个命令为什么有时候打印不出来想要的东西,例如:sed -n '/root/,/adm/p'  /etc/passwd      我 ...

  9. CentOS7.3 ffmpeg安装

    ffmpeg安装笔记 ======================== 一.安装依赖 yum -y install yum-utils yum-config-manager --add-repo ht ...

  10. sql数据管理语句

    一.数据管理 1.增加数据 INSERT INTO student VALUES(1,'张三','男',20); -- 插入所有字段.一定依次按顺序插入 -- 注意不能少或多字段值 如只需要插入部分字 ...