我正在使用相同的工具链来编程和调试 STM32F107 板。以下是我在此工具链下对 STM32Fxxx 芯片进行编程和调试的观察。
初始起点
所以在这一点上,你已经有了一个有效的 OpenOCD 到 ARM-USB-OCD 的连接,所以你应该为此做好准备。现在的工作是让 Eclipse/Zylin/Yagarto GDB 组合通过 OpenOCD/Olimex 连接正确地与 STM32Fxxx 通信。要记住的一件事是,要发出的所有 OpenOCD 命令都是运行模式命令。调用 OpenOCD 服务器的配置脚本和命令行选项是配置模式命令。一旦你发出init命令,服务器就会进入运行模式,这会打开你接下来需要的命令集。您可能已经在其他地方完成了它,但是当我像这样调用 OpenOCD 服务器时,我添加了一个 '-c "init"' 选项:
openocd -f /path to scripts/olimex-arm-usb-ocd-h.cfg -f /path to targets/stm32f107.cfg -c "init"
我接下来发出的以下命令由 Eclipse调试配置对话框完成。在Zylin Embedded debug (Native)部分下,我创建了一个新配置,为其命名、项目(可选)和我想要编程的二进制文件的绝对路径。在 Debugger 选项卡下,我将调试器设置为Embedded GDB,指向 Yagarto GDB 二进制路径,不要设置 GDB 命令文件,将 GDB 命令集设置为Standard,并将协议设置为mi。
命令选项卡 - 将 GDB 连接到 OpenOCD
所以下一个选项卡是命令选项卡,这就是问题的关键所在。您有两个空格Initialize和Run。除了猜测它们发生在 GDB 调用之前和之后,不知道究竟有什么区别。无论哪种方式,我都没有注意到我的命令运行方式有什么不同。
但无论如何,按照我在网上找到的示例,我用以下命令填充了初始化框:
set remote hardware-breakpoint limit 6
set remote hardware-watchoint-limit 4
target remote localhost:3333
monitor halt
monitor poll
前两行告诉 GDB 你有多少断点和观察点。打开 OCD 手册第 20.3 节说 GDB 无法查询该信息,所以我自己告诉它。下一行命令 GDB 通过 3333 端口连接到 localhost 上的远程目标。最后一行是一个监视命令,它告诉 GDB 将命令传递给目标,而无需自行执行任何操作。在这种情况下,目标是 OpenOCD,我给它的命令是halt。之后我告诉 OpenOCD 切换到异步操作模式。由于以下某些操作需要一段时间,因此不要让 OpenOCD 阻塞并等待每个操作。
旁注#1:如果您对 GDB 或 OpenOCD 的状态有疑问,那么您可以在调用此调试配置后使用 Eclipse 调试控制台将命令发送到 GDB 或 OpenOCD(通过 GDB 监视器命令)。
命令选项卡 - 设置用户闪存
接下来是我在运行命令部分给出的命令:
monitor flash probe 0
monitor flash protect 0 0 127 off
monitor reset halt
monitor stm32x mass_erase 0
monitor flash write_image STM3210CTest/test_rom.elf
monitor flash protect 0 0 127 on
disconnect
target remote localhost:3333
monitor soft_reset_halt
将在以下部分中解释...
设置对用户闪存的访问
首先我发出一个 OpenOCD 查询,看看它是否可以找到闪存模块并报告正确的地址。如果它响应它在地址 0x08000000 处找到闪存,那么我们很好。末尾的 0 指定获取有关 flash bank 0 的信息。
旁注#2: STM32Fxxx 部件特定数据表在第 4 节中有一个存储器映射。在您使用芯片时随身携带非常有用。此外,由于所有内容都作为内存地址进行访问,经过一点编程时间,您就会像手背一样了解这种布局!
因此,在确认闪存已正确配置后,我们调用命令关闭对闪存组的写保护。PM0075描述了您需要了解的有关对闪存进行编程的所有信息。对于这个命令,你需要知道的是 flash bank、起始扇区、结束扇区,以及是否启用或禁用写保护。闪存库是在您传递给 OpenOCD 的配置文件中定义的,并由前面的命令确认。因为我想禁用对整个闪存空间的保护,所以我指定扇区 0 到 127。PM0075 解释了我是如何得到这个数字的,因为它指的是闪存如何组织成我(和你的)设备的 2KB 页面。我的设备有 256KB 的闪存,这意味着我有 128 页。您的设备有 512KB 的闪存,因此您将拥有 256 页。要确认您的设备的写保护已被正确禁用,您可以使用 OpenOCD 命令检查地址 0x40022020 的 FLASH_WRPR 寄存器:
monitor mdw 0x40022020
它打印的结果字将是 0xffffffff,这意味着所有页面都禁用了写保护。0x00000000 表示所有页面都启用了写保护。
旁注#3:关于内存命令的主题,当我在地址 0x1ffff800 开始的块处弄乱选项字节时,我将芯片变砖了两次。第一次我在闪存上设置了读保护(如果你这样做,很难弄清楚你在做什么),第二次我设置了硬件看门狗,这阻止了我之后做任何事情,因为看门狗一直在触发!通过使用 OpenOCD 内存访问命令修复它。故事的寓意是:权力越大,责任越大…… 或者另一种看法是,如果我在脚上开枪,我仍然可以通过 JTAG 修复问题。
旁注#4:如果您尝试写入受保护的闪存,将会发生的一件事是 FLASH_SR:WRPRTERR 位将被设置。OpenOCD 将报告一个更加用户友好的错误消息。
擦除闪存
所以在禁用写保护之后,我们需要擦除你想要编程的内存。我进行了一次批量擦除,它会擦除所有内容,您还可以选择按扇区或地址擦除(我认为)。无论哪种方式,您都需要在编程之前先擦除,因为硬件会在允许写入之前先检查擦除。如果 FLASH_SR:PGERR 位 (0x4002200c) 在编程过程中被设置,那么你就知道你还没有擦除那块内存。
旁注#5:擦除闪存中的位意味着将其设置为 1。
编程你的二进制文件
擦除后的下两行将二进制映像写入闪存并重新启用写保护。PM0075 没有涵盖更多内容。基本上,当您发出flash write_image时发生的任何错误都可能与未禁用 flash 保护有关。它可能不是OpenOCD,但如果您好奇,您可以启用调试输出并遵循它的功能。
GDB 调试
所以最后在编程之后,我将 GDB 与远程连接断开,然后将其重新连接到目标,进行软重置,我的 GDB 现在可以调试了。最后一部分是我昨晚才弄清楚的,因为我试图弄清楚为什么在编程之后,GDB 在重置后不会正确地停止在 main() 处。它不断地进入杂草并炸毁。
我目前的想法以及我在 OpenOCD 和 GDB 手册中阅读的内容是,远程连接首先是要在 GDB 和已经配置和运行的目标之间使用。好吧,我在运行之前使用 GDB 进行配置,所以我认为符号表或其他一些重要信息在编程过程中会变得混乱。OpenOCD 手册说,当 GDB 连接时,服务器会自动报告内存和符号,但是当芯片被编程时,所有这些信息可能会变得无效。断开连接并重新连接我认为刷新 GDB 需要正确调试的信息。所以这导致我创建了另一个调试配置,这个只是连接并重置目标,因为我不一定需要在每次我想使用 GDB 时对芯片进行编程。
哇!完毕!有点长,但这花了我 3 个周末才弄清楚,所以我认为这并不算太糟糕......
最后的旁注:在我调试期间,我发现 OpenOCD 调试输出对于我理解 OpenOCD 在幕后所做的事情非常宝贵。要对 STM32x 芯片进行编程,您需要解锁闪存寄存器,翻转正确的位,并且一次只能写入一个半字。有一段时间我一直在质疑 OpenOCD 是否正确执行此操作,但在查看 OpenOCD 调试输出并将其与 PM0075 指令进行比较后,我能够确认它确实遵循了正确的步骤来执行每个操作。我还发现我正在重复 OpenOCD 已经在执行的步骤,因此我能够删除没有帮助的指令!故事的寓意:调试输出是你的朋友!