本篇文章主要介绍了如何使用VisualGDB构建、调试和探索无线STM32WL55 微控制器的示例项目。我们将展示如何克隆STM32WL55 SDK中包含的LocalNetwork示例,对其进行调整以启用调试,并修复导致其开箱即用的崩溃问题。
LocalNetwork示例由2个项目组成:发送周期性信标事件的集中器和响应它们的传感器。为了学习本教程,您需要两块NUCLEO-WL55JC1开发板,因为STM32WL55 SDK 1.0仅包含此类开发板的示例。
1. 启动Visual Studio,然后打开VisualGDB Embedded Project Wizard:
2. 为第一个项目选择一个名称和位置。请注意,由于我们使用两个开发板,因此需要创建两个单独的项目:
3. 在下一页上,选择“Create a new project -> Embedded Application -> Advanced CMake”:
4. 选择ARM工具链并选择STM32WL55JC器件。点击“Install”,自动下载安装STM32WL55 SDK:
5. 现在SDK已安装,您可以继续使用器件的默认设置:
6. STM32WL55 BSP包含大量从原始SDK衍生的示例。您可以通过选择下一页顶部的“STM32CubeMX Samples”来查看它们。当我们第一次创建集中器项目时,选择LocalNetwork_Concentrator并按“下一步”继续:
7. 将您的开发板连接到USB端口并等待VisualGDB识别它。请注意,编程到板上的默认固件通常会进入低功耗模式,从而阻止调试器连接到它。为了避免这种情况,请确保选中“connect under reset”复选框:
8. 点击“Finish”创建项目。创建完成后,您可以通过按Ctrl-Shift-B来构建它:
9. 尝试按F5运行项目。 VisualGDB将在main()中显示“Signal 0”,OpenOCD将报告JTAG状态错误。这实际上意味着与开发板的JTAG/SWD通信在main()中的初始断点之后丢失:
10. 发生这种情况是因为开发板进入睡眠模式,禁用了调试器相关的逻辑。您可以通过将代码资源管理器切换到全局视图来跟踪此代码的位置,将详细信息配置为仅显示Outgoing Calls,多次按下“全部展开”按钮,然后搜索“sleep”:
这将指向DBG_Init ()函数检查DEBUGGER_ON宏。 11. 按F12进入DEBUGGER_ON的定义并将其从0更改为1。请注意,这会使编译的固件变大,并会大大增加功耗:
12. 除非您通过COM端口明确请求,否则Concentrator项目不会开始发送信标数据包。为此,请将原始终端(需要自定义版本)配置为以9600位/秒的速度连接到ST-Link端口,或使用任何其他终端程序:
13. 现在您可以再次运行该项目,通过COM端口发送“AT+BEACON_ON”命令并确认板子响应“OK”:
警告:为了符合您当地的射频频谱规定,您可能需要设置设备区域通过单独的AT命令。有关详细信息,请参阅项目目录中的 readme.txt 文件。 14. 现在我们将为第二块开发板创建另一个项目。让第一个项目保持运行并启动另一个Visual Studio实例。选择第二个项目的名称和位置:
15. 选择相同的项目类型和工具链,但这次在Sample Selection页面上选择LocalNetwork_Sensor:
16. 在Debug Method页面记下第一块板的序列号。然后,插入第二个,并选择新出现的ST-Link作为调试方法:
不要忘记勾选“Connect under reset”复选框,否则无法调试! 17. 按照与之前相同的步骤启用DEBUGGER_ON宏,然后构建并运行项目:
18. 集中器项目将显示部分AT+RCV消息并停止响应。按Debug->Break All可以看到代码实际上卡在了HardFault_Handler()中:
19. 调用堆栈不会很有意义,表明内存损坏错误。要追踪它,请在源代码中搜索“AT+RCV”,并在输出它的行处设置断点。然后,重新启动集中器,发出初始AT命令,最后重新启动传感器开发板:
如果您单步执行输出AT+RCV消息的CONC_Report_RCV()函数,您会注意到它调用memcpy()来设置数据变量不验证有效载荷大小。从SDK 1.0版本开始,它会导致尝试将20个字节复制到一个3字节变量中并覆盖堆栈中保存的寄存器地址。 20. 您可以使用调用堆栈视图发现20的值是通过从通过SUBGRF_GetPayload()接收的有效负载中减去6得出的:
21. 在传感器方面,这对应于设置为默认值的data_lim字段,并且永远不会被覆盖。您可以使用Find References命令 (Shift-F12) 以及Find Symbol Results窗口中的工具栏按钮来快速显示分配了变量的所有代码位置:
22. 如果您在分配data_lim的两行都设置断点,第一个将在传感器准备好发送时立即触发,而第二个将永远不会触发:
23. 修复集中器崩溃的最简单方法是在错误的memcpy()调用之前添加大小检查: - if (DataLen > sizeof(data))
- DataLen = sizeof(data);
复制代码现在您可以再次重新启动集中器,运行AT命令,启动传感器并确认成功接收到多个传感器数据包:
24. 请注意,VisualGDB建议的默认项目参数不支持sprintf()中的“%hhu”说明符。您可以通过VisualGDB项目属性将C库类型从Newlib-Nano更改为Default来修复它:
25. 这将纠正AT+RCV的输出,但会增加FLASH和RAM的使用:
请注意,许多无线应用程序对时序至关重要,因此在断点处停止代码可能会中断通信。作为断点的替代方法,请考虑以下非侵入式调试技术: ◾ Live Watch 用于观察和绘制变量值(例如数据包计数器) ◾ 带有自定义事件的实时手表,用于跟踪精确事件 ◾ Live Code Coverage 用于实时查看正在运行的代码路径 ◾ 快速半主机作为COM端口的更快替代方案 ◾ 测试资源管理器,用于直接从STM32代码在开发计算机上创建二进制转储文件
您可以在我们的GitHub存储库中找到本文中显示的源代码。 |