SEGGER RTT 使用指南
简介
SEGGER的实时传输(RTT)技术用于嵌入式应用中的交互式用户输入输出。它结合了SWO和半托管(semihosting)的优点,同时实现了极高的性能。通过RTT,可以在不影响目标微控制器实时行为的情况下,以非常高的速度从目标微控制器输出信息并发送输入到应用。
SEGGER RTT可以与任何J-Link型号和任何支持背景内存访问的目标处理器(如Cortex-M和RX目标)配合使用。RTT支持多个双向通道,可以用于不同的目的,为用户提供最大的灵活性。默认实现中,每个方向使用一个通道,用于可打印的终端输入和输出。通过J-Link RTT Viewer,这个通道可以用于多个“虚拟”终端,允许在多个窗口中打印(例如,一个用于标准输出,一个用于错误输出,一个用于调试输出),只需一个目标缓冲区。此外,还可以使用一个额外的上行(到主机)通道,例如用于发送性能分析或事件跟踪数据。
在硬件没有预留调试串口的情况下,我们可以使用 RTT 来进行调试打印。
使用步骤
引入驱动
1、在 SEGGER 官网选择合适版本的 JLINK 进行下载安装
2、找到对应 JLINK 版本的 RTT 驱动
3、将驱动文件添加到工程
接口使用
在工程中添加头文件 SEGGER_RTT.h
,在打印前调用 SEGGER_RTT_Init()
初始化 RTT,打印时使用 SEGGER_RTT_printf
函数。
这里我们将 SEGGER_RTT_printf
定义为 printf_dbg
,并将打印通道指向 0
,后面就可以使用 printf_dbg
宏进行打印了。
可能遇到的问题
坑1
现象:单片机程序分为 BLD、APP 两个部分,两边都使用 RTT 进行打印时,打开 J-Link RTT Viewer
发现 BLD 部分的打印丢失。
原因:BLD 与 APP 中都调用了 SEGGER_RTT_Init()
,这个函数在调用后会重置 RTT 控制块结构体,在打开 J-Link RTT Viewer
后只记录了 APP 内的打印。
措施:只在 BLD 程序中调用 SEGGER_RTT_Init()
,去掉 APP 中的初始化调用。
坑2
现象:按上面修改后,打开 J-Link RTT Viewer
发现 BLD 中的打印正常了,但 APP 的打印缺失。
原因:APP 中 RTT 控制块结构体在内存中的地址可能与 BLD 中该结构体地址不一致,打开 J-Link RTT Viewer
后软件从扫描到的内存地址读取打印,但 APP 中的打印地址与 BLD 中不一致,导致 APP 内的打印丢失。
措施:固定 RTT 控制块的内存地址
1、用宏指定 RTT 控制块所在的段
2、指定使用自定义分散加载文件
3、修改分散加载文件,指定 RTT 控制块所在段的内存位置
注意事项
1、在 map 文件中确认 RTT 控制块的内存地址
2、BLD 中 RTT 控制块初始时应该用 0 填充,而 APP 中不对 RTT 控制块所在内存做任何操作,所以 APP 对应的分散加载文件中,RTT 控制块段的内存描述中使用 UNINIT
修饰
3、使用 Arm Compiler 6
时,设置非零初始化变量的方式与 Arm Compiler 5
不同
Arm Compiler 5 属性 | Arm Compiler 6 属性 | 描述 |
---|---|---|
__attribute__((at(address))) | __attribute__((section(".ARM.__at_address"))) | Arm Compiler 6 中的 armlink 仍然支持以 .ARM.__at_address 的形式放置段 |
__attribute__((at(address), zero_init)) | __attribute__((section(".bss.ARM.__at_address"))) | Arm Compiler 6 中的 armlink 支持以 .bss.ARM.__at_address 的形式放置零初始化段。 .bss 前缀区分大小写,并且必须全部小写。 |
__attribute__((section(name), zero_init)) | __attribute__((section(".bss.name"))) | name 是你选择的名字。 .bss 前缀区分大小写,并且必须全部小写 |
__attribute__((zero_init)) | 不支持默认将零初始化变量放在.bss 段。 | 如果变量具有初始值设定项,则 Arm Compiler 5 会生成错误。 否则,它将零初始化变量放在 .bss段。 |
RTT 控制块所在段属性应当设置为 ZI
,使用 Arm Compiler 6
编译时,定义变量使用 __attribute__((section(".bss.RTT")))
来进行修饰。
4、使用 GCC 进行编译时,修改 .s 启动文件和 .ld 链接文件
BLD:
- 添加自定义段的起始地址与结束地址
/* start address for the .custom_ram section. defined in linker script */
.word _scustom
/* end address for the .custom_ram section. defined in linker script */
.word _ecustom
- 用 0 初始化自定义段
/* Zero fill the custom_ram segment. */
ldr r2, =_scustom
b LoopFillZeroCustom
FillZeroCustom:
movs r3, #0
str r3, [r2]
adds r2, r2, #4
LoopFillZeroCustom:
ldr r3, =_ecustom
cmp r2, r3
bcc FillZeroCustom
BLD && APP:
- 修改链接文件
/* Specify the memory areas */
MEMORY
{
RAM_CUSTOM (xrw): ORIGIN = 0x20000000, LENGTH = 0x4b8
RAM (xrw) : ORIGIN = 0x20000000 + 0x4b8, LENGTH = 0x2000 - 0x4b8
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 15K
}
/* Uninitialized data section */
. = ALIGN(4);
.custom_ram :
{
_scustom = .;
*(.bss.RTT)
. = ALIGN(4);
_ecustom = .;
} >RAM_CUSTOM
- 添加宏指定 RTT 控制块所在段
-DSEGGER_RTT_SECTION=\".bss.RTT\"
5、使用 SEGGER Embedded Studio
编译时,修改.icf 链接脚本
APP:
- APP 中不初始化 RTT 控制块
do not initialize { section .RTT };
BLD && APP:
- 将 RTT 控制块所在段放到 RAM1 开头
place in RAM1 { section .RTT, section .RAM1, section .RAM1.* };
- Preprocessor Definitions 添加宏指定 RTT 控制块所在段
SEGGER_RTT_SECTION=".RTT"