之前的文章在讲到调试的时候,一般都是大家熟悉的调试方法:通过打断点,让程序运行到某一个地方停下来,查看某些变量、寄存器等的状态;单步运行,看程序的执行、跳转是否跟预期一致;比较高级的是设置软断点,比如在读写某一个变量,或是某一变量值到达某种状态时停下。这些方法有一个共同的特征,就是程序要停下来,让我们观察各种信息。
有没有一种方法,程序在持续的运行,某些变量的值能够持续的输出给我们,甚至是通过图表的形式给我们观察,甚至是后期保存下来。这样在某些情况下是更直观的、更有效的一种调试方法。
这种方法当然是存在的,比如通常会利用单片机的某一个串口向外发送数据,再通过上位机串口调试软件接收并显示。除此之外,还有另一种方法,通过STM32系列单片机的SWV(Serial Wire Viewer)实时跟踪技术来实现。
接下来我们通过一个简单的例子,看看在CubeIDE下怎么通过单片机的SWV功能结合CubeIDE的ITM(Instrumentation Trace Macrocell)功能实现参数的实时跟踪。需要说明的是,下面关于SWV实时跟踪的功能介绍来源于CubeIDE的用户手册,觉得自己英文水平还行的可以直接去看手册,更权威。
1. 端口配置
我们还是基于正点原子的F767开发板,在CubeMX里新建一个工程,具体设置见下图。默认的Debug模式为JTAG,这里我们改为Trace Asynchronous Sw,调试模式改为SWD。这个时候PB3就会被使能,它的一个复用功能是SWO(Serial Wire Output),这个引脚配合SWD输出实时跟踪数据,也即实现了SWV功能。
2. 调试配置
用CubeIDE打开生成的工程,点击菜单Run->Debug Configurations,新建一个调试配置文件,在调试器标签页里,接口选SWD,使能SWV,Core Clock处填内核实际运行频率,这里是216 MHz。
接下来,连接开发板,进入调试模式。点击菜单Window->Show View->SWV->SWV Trace Log,会在窗口下方打开SWV Trace Log标签,如下图。点击Configure Trace图标进行配置。
在配置窗口里我们使能PC Sampling,对内核PC(Program Counter)值进行采样,分辨率16384 Cycles/sample的含义是每次采样间隔16384个时钟周期。这个值是默认的,考虑到SWO端口异步工作频率为2 MHz,这个分辨率不能太小,否则数据量太大会造成堵塞。Timestamps必须被使能,好知道有事件发生了。后边的预分频值同样与SWO数据量大小有关。
点击SWV Trace Log标签页右方的Start Trace图标,见下图,并启动调试,则会看到SWV Trace Log标签页会持续输出PC Sample数据。
当然,除了输出PC Sample数据之外,还可以配置输出Configure Trace窗口里的各种事件发生的情况,也可以输出某些变量值,甚至是把输出的变量以图表的形式输出。具体配置方法可参考用户手册去研究。
3. 代码利用printf重定向输出消息
我们知道printf是C语言标准库的函数,可以向屏幕打印字符串,嵌入式里一般被重定向到串口。在利用SWV进行调试时,我们可以把printf重定向到ITM的0通道,调试时,用户代码可以利用printf向外发送易读的消息。
首先,Configure Trace里勾选0通道,见下图。然后点击CubeIDE菜单Window->Show View->SWV->SWV ITM Data Console,窗口下方会出现SWV ITM Data Console标签页,可以显示ITM各通道输出的信息。
接下来修改代码,打开工程文件夹->Src->syscall.c文件,这个源文件里的函数配合C库实现系统调用。找到_write函数,完成printf到ITM的重定向。修改如下:
__attribute__((weak)) int _write(int file, char *ptr, int len)
{
int DataIdx;
for (DataIdx = 0; DataIdx < len; DataIdx++)
{
//__io_putchar(*ptr++);
ITM_SendChar(*ptr++);
}
return len;
}
当然,把注释掉的那一行修改成串口发送的话,就能把printf重定向到串口。
在main.c的while循环里添加如下代码:
while (1)
{
HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_SET);
printf("LED0 is offn");
HAL_Delay(500);
HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET);
printf("LED0 is onn");
printf("%dn",counter++);
}
重新编译,进入调试模式,则可以在SWV ITM Data Console标签页里看到如下打印信息。
4. 小结
基于STM32Cube生态的SWV实时跟踪调试方法就介绍到这里。需要说明的是,此方法在硬件上需要留出SWD接口和SWO引脚,正点原子STM32F767核心板上的SWD接口没有引出SWO引脚,所以连接核心板的SWD无法用到SWV功能,需要通过ST-Link连接底板上的20针JTAG接口实现。