嵌入式系统深度调试:FreeRTOS 上的 SEGGER RTT 与 SystemView 应用
嵌入式系统的有效调试和性能分析是微控制器开发的关键基石。SEGGER RTT(实时传输)和 SystemView 技术为这些任务提供了强大的工具,使开发人员能够在不中断目标设备运行的情况下,获取详细的调试信息并对 FreeRTOS 等实时操作系统(RTOS)进行深入监控。本文将探讨 RTT Viewer 用于日志输出和 SystemView 用于 RTOS 事件可视化的工作原理和实际配置方面,这对于诊断嵌入式软件中复杂的行为模式至关重要。
嵌入式系统调试基础与 SEGGER RTT 的作用
调试嵌入式系统需要专门的方法,以最大限度地减少对目标设备行为的影响。传统的调试方法,例如使用断点的 JTAG/SWD 调试,可能会引入不必要的延迟。在这种情况下,DAP(调试访问端口)、ITM(指令跟踪宏单元)和 RTT(实时传输)等机制应运而生。
- DAP (Debug Access Port): 一个硬件模块,提供对微控制器总线和内核的调试访问。
- ITM (Instrumentation Trace Macrocell): Cortex-M 内核(从 M3 开始)中的一个专用模块,旨在以最小的时间开销高速输出调试消息。
- RTT (Real-Time Transfer): SEGGER 的一项专有技术,它利用微控制器 RAM 中的循环缓冲区与主机计算机进行双向数据交换。RTT 的关键优势在于其非侵入性:微控制器的 CPU 不会暂停,调试信息以极高的速度传输,使其成为实时日志输出(
printf)的理想选择。
要将 RTT 集成到项目中,您需要添加 SEGGER RTT 库源文件。此过程不依赖于特定的集成开发环境(IDE),无论是 Keil uVision 还是其他。编译并执行后,J-Link 调试器会自动在微控制器的 RAM 中搜索 RTT 控制块(CB)结构。该控制块包含一个标识符、通道数量以及读写循环缓冲区的描述,提供了数据交换的入口点。在大多数情况下,搜索是自动进行的,但也可以手动配置地址。
RTT 连接和使用示例:
包含头文件:
#include <stdio.h>
#include "SEGGER_RTT.h"
类型化消息的封装(可选,为了方便):
#define LOG_INFO(msg, ...) { SEGGER_RTT_TerminalOut(0, "INFO: "); SEGGER_RTT_printf(0, msg, ##__VA_ARGS__); }
#define LOG_ERR(msg, ...) { SEGGER_RTT_TerminalOut(0, "ERROR: "); SEGGER_RTT_printf(0, msg, ##__VA_ARGS__); }
void LOG_FLOAT(const char* prefix, float value, const char* suffix) {
char buf[64];
snprintf(buf, sizeof(buf), "%s%.2f%s", prefix, value, suffix);
SEGGER_RTT_TerminalOut(0, buf);
}
初始化控制块并输出日志:
SEGGER_RTT_Init();
// ...
LOG_INFO("Central Heating: %s\r\n", Boiler.isCentralHeatingActive(response) ? "on" : "off");
LOG_ERR("Error: OpenTherm is not initialized");
J-Link 驱动程序包中包含的 RTT Viewer 工具允许查看这些日志。它支持将信息输出到不同的选项卡(终端),并使用多个 RTT 通道进行更高级的自定义数据传输。RTT 的优点包括能够通过 SWD 输出 printf 而无需使用额外的微控制器引脚(例如 SWO)、与 Cortex-M0/M0+ 的兼容性、易于设置以及与其他方法相比极高的运行速度。
SystemView:深入的实时 RTOS 性能分析
SystemView 是 SEGGER 强大的工具,用于实时分析和可视化嵌入式系统操作,使用 RTT 作为其传输层。它允许开发人员通过跟踪任务切换、中断处理和其他系统事件,更深入地了解 FreeRTOS 的行为。SystemView 的配置比 RTT 更复杂,涉及几个关键步骤:
- 包含源文件: 将必要的 SystemView 文件添加到您的项目中。
- FreeRTOS 配置: SystemView 通过拦截 FreeRTOS 跟踪宏来工作,这些宏位于内核中的关键点(例如,在任务创建或上下文切换期间)。要在
FreeRTOSConfig.h文件中激活此机制,您必须:
* 设置 #define configUSE_TRACE_FACILITY 1。
* 对于 FreeRTOS V10+,添加 #define INCLUDE_xTaskGetIdleTaskHandle 1。
* 在 FreeRTOSConfig.h 文件的末尾,包含 SEGGER_SYSVIEW_FreeRTOS.h。
跟踪宏示例:
```c
#define traceTASK_SWITCHED_IN() SEGGER_SYSVIEW_OnTaskStartExec((U32)pxCurrentTCB)
#define traceTASK_SWITCHED_OUT() SEGGER_SYSVIEW_OnTaskStopExec()
#define traceISR_ENTER() SEGGER_SYSVIEW_RecordEnterISR()
// etc.
```
- 使用 DWT_CYCCNT 进行精确计时: Cortex-M3/M4 内核包含一个内置的 DWT(数据观察点和跟踪)模块,带有一个 32 位循环计数器(CYCCNT)。SystemView 将其用作高精度时间参考。在
SEGGER_SYSVIEW_Conf()函数中,您需要激活此计数器:
```c
void SEGGER_SYSVIEW_Conf(void) {
// ---- Setup DWT (for Cortex-M3/M4) ----
#define DEMCR ((volatile U32)0xE000EDFCu)
#define DWT_CTRL ((volatile U32)0xE0001000u)
#define DWT_CYCCNT ((volatile U32)0xE0001004u)
#define DEMCR_TRCENA (1u << 24)
#define DWT_CTRL_CYCCNTENA (1u << 0)
DEMCR |= DEMCR_TRCENA; // Enable access to TRCENA
DWT_CYCCNT = 0; // Clear counter
DWT_CTRL |= DWT_CTRL_CYCCNTENA; // Start DWT count
SEGGER_SYSVIEW_Init(SYSVIEW_TIMESTAMP_FREQ, SYSVIEW_CPU_FREQ,
&SYSVIEW_X_OS_TraceAPI, _cbSendSystemDesc);
SEGGER_SYSVIEW_SetRAMBase(SYSVIEW_RAM_BASE);
}
```
此外,请确保 SYSVIEW_RAM_BASE 宏与微控制器的 RAM 地址匹配。
- 中断服务例程(ISR)处理: 与 SystemView 通过 FreeRTOS 宏拦截的任务切换不同,中断需要显式调用。您必须将
SEGGER_SYSVIEW_RecordEnterISR()和SEGGER_SYSVIEW_RecordExitISR()函数添加到您的中断处理程序中:
```c
void EINT15_10_IRQHandler()
{
SEGGER_SYSVIEW_RecordEnterISR();
if(EINT->IPEND & EINT_IPEND_11) {
// Your code
}
SEGGER_SYSVIEW_RecordExitISR();
}
```
- 命名中断: 为了 SystemView 中的清晰度,您需要通过扩展
_cbSendSystemDesc()函数(位于SEGGER_SYSVIEW_Config_FreeRTOS.c中)来告知工具中断名称:
```c
static void _cbSendSystemDesc(void) {
SEGGER_SYSVIEW_SendSysDesc("N="SYSVIEW_APP_NAME",D="SYSVIEW_DEVICE_NAME",O=FreeRTOS");
SEGGER_SYSVIEW_SendSysDesc("I#15=SysTickIRQ");
SEGGER_SYSVIEW_SendSysDesc("I#46=ModbusIRQ");
//...
}
```
- 获取中断 ID 和时间戳的函数: 将 SystemView 正常运行所需的两个函数添加到
main(或其他方便的位置):
```c
// 一个获取当前活动中断(ISR)编号的函数
U32 SEGGER_SYSVIEW_X_GetInterruptId(void) {
// 在 Cortex-M 内核中,中断号存储在 IPSR 寄存器的低 9 位。
return (((volatile U32)(0xE000ED04u)) & 0x1FFu);
}
// 在 Cortex-M3/M4 中,我们使用 DWT_CYCCNT 内核时钟计数器寄存器
U32 SEGGER_SYSVIEW_X_GetTimestamp(void) {
return ((volatile U32)(0xE0001004u));
}
```
- 最终初始化: 在
main函数中初始化硬件和外设后,调用SEGGER_SYSVIEW_Conf(),然后启动任务调度器vTaskStartScheduler():
```c
InitPhy();
SEGGER_SYSVIEW_Conf(); // Segger SystemView 初始化
CreateDeviceTasks();
vTaskStartScheduler(); // 启动实时调度器
```
成功配置后,SystemView 工具将提供系统行为的详细可视化。SystemView 最有价值的优势之一是,即使目标微控制器不在调试模式下,它也能运行。这允许在实际运行条件下进行监控,根据需要连接 J-Link 进行诊断,从而显著简化了复杂、时间相关错误的识别。
集成与工具选择:RTT Viewer vs. SystemView
SEGGER RTT Viewer 和 SystemView 是互补工具,各自解决嵌入式软件开发中的特定任务。RTT Viewer 非常适合输出文本日志和调试信息,它取代了传统的 printf,同时提供了显著更高的性能和非侵入性。SystemView 则通过提供 FreeRTOS 事件(如上下文切换、任务执行和中断处理)的图形化表示,将分析提升到一个新的水平,这对于性能优化和识别调度问题至关重要。
SEGGER_RTT_printf 和 SEGGER_SYSVIEW_Print(如果用于 SystemView 中的文本消息)之间的选择取决于具体情况:SEGGER_RTT_printf 用于通用日志,而 SystemView 函数则用于需要集成到分析时间线中的事件。值得注意的是,SystemView 使用其内部 RTT 通道进行跟踪数据传输,该通道通常不用于直接用户交互。尽管 SystemView 可以在没有 RTOS 的情况下运行,但其功能将受到严重限制,因为它的许多功能都依赖于操作系统钩子。
SystemView 中使用 DWT_CYCCNT 而非 SysTick 作为时间戳的原因有几个。DWT_CYCCNT 是一个专用的硬件处理器周期计数器,它提供最高的精度,并且独立于 RTOS 使用的系统定时器设置。这确保 SystemView 中的时间戳尽可能可靠,并且不会因操作系统的自身运行而失真。
其他 RTOS 跟踪和分析工具也存在,例如 Percepio 的 Tracealyzer,它们也提供丰富的功能来可视化和分析嵌入式系统行为。然而,SEGGER RTT 和 SystemView 因其与 J-Link 生态系统的深度集成和高性能而脱颖而出,成为使用 Cortex-M 微控制器的开发人员的首选解决方案。
重要提示:
- SEGGER RTT 通过 RAM 中的循环缓冲区提供高速、非侵入性的调试日志(
printf)输出。 - SystemView 是一个强大的工具,用于实时 FreeRTOS 可视化和分析,使用 RTT 作为其传输方式。
- 为了精确的时间线,SystemView 依赖于硬件 DWT_CYCCNT 计数器(Cortex-M3/M4)。
- 这两种工具都通过允许在不暂停 CPU 的情况下进行系统监控,显著简化了复杂问题的诊断。
- SystemView 甚至可以在非调试模式下进行系统行为分析,只需按需连接 J-Link。
— Editorial Team
暂无评论。