跳转至

JTAG调试ESP32

JTAG调试ESP32

硬件:jlink、esp32、杜邦线

软件:jlinkzadig、opencod-esp32

先把正常的 jlink 驱动装好,插上 jlink 检查一下是不是识别到了设备

1671615661586-3eea8fe6-bd72-4ae8-b85b-a3db2ce3beb7.png

然后运行 zadig,选择 Options

1671615703006-96cf9e9f-3dc7-4953-8c23-df8d21fec37c.png

List All Devices 列出所有驱动

1671615735171-eaeabd21-9372-4eb9-bd1d-2c278c1ca76b.png

此时效果如下,虽然显示的驱动时 jlink 的,但是这个是串口的,不是我们要的,不要改这个!

1671615774525-9d6d2cef-4cd2-497d-bcc5-ce8a4481aa8a.png

选择 BULK 这一项,点击 Replace Driver

1671615810945-04b95ab0-54a1-4642-8b40-0a85791da7cc.png

找到 esp32 开发环境中自带的 opencod.exe 所在的路径:C:\Espressif\tools\openocd-esp32\v0.11.0-esp32-20220706\openocd-esp32\bin

1671617267222-1b561813-fd6d-4e1a-abbb-7bd0407d01ea.png

然后把 esp32.cfg(C:\Espressif\tools\openocd-esp32\v0.11.0-esp32-20220706\openocd-esp32\share\openocd\scripts\target\esp32.cfg)复制到当前 openocd 目录

1671617974988-e36ba8ac-34e3-4613-8115-479e02305cb3.png

前面加上句:source [find interface/jlink.cfg]

1671618057956-d713e659-4bec-4b19-994e-7cd2c1c38fe9.png

运行:openocd.exe -f interface/jlink.cfg -f ./esp32.cfg 发现报错:JTAG I/O operation failed: 0x1.

1671618115693-0b6ff0a1-52da-4385-964d-d8b9e208d2c9.png

找一下 jlink.cfg(C:\Espressif\tools\openocd-esp32\v0.11.0-esp32-20220706\openocd-esp32\share\openocd\scripts\interface\jlink.cfg)编辑上一行 adapter speed 9600

1671618216051-f3b6b6bc-0bcc-4ac6-a7e8-198cf1feaf96.png

在执行 openocd.exe -f interface/jlink.cfg -f ./esp32.cfg 就没问题了

1671618234191-33bb0b76-aa85-4baf-9ee8-998e294b5c06.png

我们以 esp32 的示例程序 hello world 为例,找到例子中的 hello world 位置 C:\Espressif\frameworks\esp-idf-v4.4.3\examples\get-started\hello_world

打开 ESP-IDF 切换到 hello_world 路径,使用 idf.py build 将 hello_world 编译出来,然后将 ESP32 插入电脑,使用 idf.py flash 将编译成功的 hello_world 烧录到 ESP32 中

按照 ESP32 的引脚定义连接好 ESP32 的板子和 Jlink 设备上的针脚

1678257977628-249df505-b61b-460a-b0ed-966bc7a6b7a9.png

在 hello_world 文件夹创建一个 gdbinit 文件,内容为:

target remote :3333
set remote hardware-watchpoint-limit 2
mon reset halt
flushregs
thb app_main
c

对 gdbinit 每条指令做一个解释:

target remote :3333 指定了连接的端口

set remote hardware-watchpoint-limit 2 限制 GDB 仅使用 ESP32 支持的两个硬件观察点

mon reset halt 复位芯片并使 CPU 停止运行

flushregs 一种强制 GDB 从目标获取最新状态的方法

thb app_main 在 app_main 处插入一个临时的硬件断点

c 恢复程序运行,它将会在 app_main 的断点处停止运行

使用 xtensa-esp32-elf-gdb -x gdbinit .\build\hello_world.elf 调试程序,运行起来之后输入 c 就会断在源码文件的第 17 行

1678259452228-e894debc-2174-49b2-a637-fd58d1d3ad45.png

1678259388771-cc3cdfcd-bb6e-4321-937f-dd44ae1c0bde.png

我们可以通过串口工具观察整个过程,先使用 b 22 给源码的第 22 行下一个断点

1678259772748-ed831b2c-ef96-41ac-a79a-984c28bee713.png

然后输入 c 将会断到第 22 行,这时串口应该输出 Hello world! 这个串口信息,但不应该打印 This is xxx 这一段话,我们运行看看实际情况

1678259906941-63bd54c9-3e45-47af-80f0-25897d787c99.png

到此调试是能调试了,怎么通过 opencode 提取固件呢,想要提取固件首先得知道你要提取的东西在内存中的位置与大小(其实大小也无所谓,你可以尽量大一点哈哈哈),我们看烧录过程中的日志:

1678933719172-e6ad4d0f-8a46-456f-97cc-66958f77859c.png

然后使用命令将这一块的内存提取到 opencod 目录的 output.bin:

openocd.exe -f interface/jlink.cfg -f ./esp32.cfg -c init -c "reset halt" -c "flash read_bank 0 output.bin 0x10000 0x29c40" -c "reset" -c shutdown

1678933882925-643f0fae-eae1-47c4-9aca-9c70313279c4.png

我们将提取出来的与 build 编译的固件计算一下 md5 值,可以看到两者 hash 相同,证明我们完美的提取了 hello_world.bin

certutil -hashfile <filename> MD5

1678933904790-1f013858-5c04-4fae-ae06-ad58169b5133.png

最后:esp32官方是有 esptool.py 能够直接提取固件的,命令如下

esptool.py --baud 115200  read_flash 0x10000 0x29c40 output.bin

效果是一样的

1678934233167-ce5a4a5c-8fb4-415c-b7fb-a47d5a090809.png

原文: https://www.yuque.com/hxfqg9/iot/kn7rl91t9e3k1zwy