# JTAG 调试

[English]

本文将介绍如何安装 ESP32 的 OpenOCD 调试环境，以及如何使用 GDB 来调试 ESP32 的应用程序。

备注

也可以使用 `idf.py monitor` 来调试 ESP32，免于设置 JTAG 或 OpenOCD。请参阅 IDF 监视器 和 CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME。

本文档结构如下：

引言

  介绍本指南主旨。

工作原理

  介绍 ESP32、JTAG (Joint Test Action Group) 接口、OpenOCD 和 GDB 如何相互连接，从而实现 ESP32 的调试功能。

选择 JTAG 适配器

  介绍有关 JTAG 硬件适配器的选择及参照标准。

安装 OpenOCD

  介绍如何安装官方预编译好的 OpenOCD 软件包并验证是否安装成功。

配置 ESP32 目标板

  介绍如何设置 OpenOCD 软件并安装 JTAG 硬件，两项共同构成调试目标.

启动调试器

  介绍如何从 Eclipse 集成开发环境 和 命令行终端 启动 GDB 调试会话。

调试范例

  如果你不熟悉 GDB，请查看此小节以获取 Eclipse 集成开发环境 以及 命令行终端 提供的调试示例。

从源码构建 OpenOCD

  OpenOCD 源码构建流程参考。

注意事项和补充内容

  介绍使用 OpenOCD 和 GDB 通过 JTAG 接口调试 ESP32 时的注意事项和补充内容。

## 引言

ESP32 具有两个强大的 Xtensa 内核，支持多种程序架构。ESP-IDF 自带的 FreeRTOS 操作系统支持多核抢占式调度，允许用户以更加直观的方式编写软件。

与此相对地，由于缺乏合适的工具，简便的编程方式也会给程序的调试带来困难，比如找出由两个线程引起的错误，并且这两个线程在单独的 CPU 核上同时运行，那么仅凭 `printf` 语句会花费很长时间来定位该错误。调试此类问题更好（往往也更快）的方法是使用调试器，将其连接到处理器的调试端口。

乐鑫已完成 OpenOCD 移植，以支持 ESP32 处理器和多核 FreeRTOS 架构（大多数 ESP32 应用程序的基础）。此外，乐鑫还提供了一些 OpenOCD 本身并不支持的工具，以进一步丰富调试功能。

本文将介绍如何在 Linux、Windows 和 macOS 环境下为 ESP32 安装 OpenOCD，并使用 GDB 进行软件调试。除部分安装流程有所不同外，所有操作系统的软件用户界面和使用流程都是相同的。

备注

本文使用的图片素材来自于 Ubuntu 16.04 LTS 上 Eclipse Neon 3 软件的截图，不同的操作系统（Windows、macOS 或 Linux）或不同的 Eclipse 软件版本在用户界面上可能会有细微差别。

## 工作原理

通过 JTAG (Joint Test Action Group) 接口使用 OpenOCD 调试 ESP32 时所需要的关键软件和硬件包括 xtensa-esp32-elf-gdb 调试器、OpenOCD 片上调试器 和连接到 ESP32 目标的 JTAG 适配器，如下图 “Application Loading and Monitoring” 标志所示。

JTAG 调试 - 概述图

“Application Loading and Monitoring” 标志显示一组关键的软件和硬件组件，可用于编译、构建和烧写应用程序到 ESP32 上，以及监视来自 ESP32 的运行诊断信息。

Eclipse 环境集成了 JTAG 调试和应用程序加载、监视的功能，使得软件从编写、编译、加载到调试的迭代过程变得更加快速简单。Eclipse IDE 及其集成的调试软件均适用于 Windows、Linux 和 macOS 平台。根据用户喜好，除了使用 Eclipse 集成开发环境，还可以直接在命令行终端运行 debugger 和 idf.py build。

若使用 ESP-WROVER-KIT 开发板，由于其板载 FT232H 芯片，仅需一根 USB 线即可连接 PC 与 ESP32。FT232H 提供了两路 USB 通道，一路连接到 JTAG，另一路连接到 UART。

## 选择 JTAG 适配器

上手 JTAG 最快速便捷的方式是使用 ESP-WROVER-KIT 开发板，因为它板载了 JTAG 调试接口，无需使用外部 JTAG 硬件适配器和额外线缆来连接 JTAG 与 ESP32。ESP-WROVER-KIT 采用 FT2232H 提供的 JTAG 接口，可以稳定运行在 20 MHz 的时钟频率，外接的适配器很难达到这个速度。

如果想使用单独的 JTAG 适配器，请确保其与 ESP32 的电平电压和 OpenOCD 软件都兼容。ESP32 使用的是业界标准的 JTAG 接口，它未使用（实际上也并不需要）TRST 信号脚。JTAG 使用的 IO 管脚由 VDD_3P3_RTC 电源管脚供电（通常连接到外部 3.3 V 的电源轨），因此 JTAG 硬件适配器的管脚需要能够在该电压范围内正常工作。

在软件方面，OpenOCD 支持相当多数量的 JTAG 适配器，请参阅 OpenOCD 支持的适配器列表 （请注意这一列表并不完整），其中还列出了兼容 SWD 接口的适配器，但请注意，ESP32 目前并不支持 SWD。此外，硬编码为只支持特定产品线的 JTAG 适配器也无法在 ESP32 上工作，例如仅针对 STM32 系列产品的 ST-LINK 适配器。

保证 JTAG 正常工作需要连接的信号线包括：TDI、TDO、TCK、TMS 和 GND。一些 JTAG 适配器还需要 ESP32 提供一路电源到适配器的某个管脚上（比如 Vtar），用于设置适配器的工作电压。也可以选择将 SRST 信号线连接到 ESP32 的 CH_PD 管脚上，但请注意，目前 OpenOCD 对该信号线提供的支持相当有限。

ESP-Prog 中展示了使用外部电路板进行调试的实例，方法是将其连接到 ESP32 的 JTAG 管脚上。

## 安装 OpenOCD

如果已经按照 快速入门 完成了 ESP-IDF 及其 CMake 构建系统的安装，那么 OpenOCD 已经被默认安装到了你的开发系统中。在 Windows、Linux 或 macOS 设置开发环境 结束后，应该能够在终端中运行如下 OpenOCD 命令：

    openocd --version

终端会输出以下信息（实际版本号可能会更新）：

    Open On-Chip Debugger v0.12.0-esp32-20240318 (2024-03-18-18:25)
    Licensed under GNU GPL v2
    For bug reports, read
            http://openocd.org/doc/doxygen/bugs.html

你还可以检查 `OPENOCD_SCRIPTS` 环境变量的值，以确认 OpenOCD 配置文件的路径，Linux 和 macOS 用户可以在终端输入 `echo $OPENOCD_SCRIPTS`，Windows 用户需要输入 `echo %OPENOCD_SCRIPTS%`。如果终端输出了有效路径，则表明已经正确安装 OpenOCD。

如果无法执行上述步骤，请再次阅读快速入门的 安装 章节。

备注

另外也可以从源代码编译 OpenOCD 工具，详细信息请参阅 从源码构建 OpenOCD 章节。

## 配置 ESP32 目标板

OpenOCD 安装完成后就可以配置 ESP32 目标（即带 JTAG 接口的 ESP32 板），具体分为以下三个步骤：

- 配置并连接 JTAG 接口

- 运行 OpenOCD

- 上传待调试的应用程序

### 配置并连接 JTAG 接口

此步骤取决于使用的 JTAG 和 ESP32 板，请参考以下两种情况。

- 配置 ESP-WROVER-KIT 上的 JTAG 接口
- 配置其他 JTAG 接口

### 运行 OpenOCD

配置完目标并将其连接到电脑后，即可启动 OpenOCD。

打开终端，按照快速入门中:ref:Windows <get-started-set-up-env>、Linux 或 macOS 设置开发环境 章节进行操作。运行 OpenOCD 时，需要提供与目标开发板相关的配置文件。构建项目后，ESP-IDF 会生成 `build/project_description.json` 文件，其中 `debug_arguments_openocd` 字段保存了默认的 OpenOCD 配置信息。请运行如下命令，以启动 OpenOCD（该命令适用于 Windows、Linux 和 macOS）：

    openocd -f board/esp32-wrover-kit-3.3v.cfg

备注

上述命令中 `-f` 选项后跟的配置文件专用于 板载 ESP32-WROOM-32 模组的 ESP-WROVER-KIT 开发板。基于具体使用的硬件，你可能需要选择不同的配置文件，具体内容请参阅 根据目标芯片配置 OpenOCD。

现在应该可以看到如下输出（此日志来自 板载 ESP32-WROOM-32 模组的 ESP-WROVER-KIT 开发板）:

    user-name@computer-name:~/esp/esp-idf$ openocd -f board/esp32-wrover-kit-3.3v.cfg
    Open On-Chip Debugger  v0.10.0-esp32-20190708 (2019-07-08-11:04)
    Licensed under GNU GPL v2
    For bug reports, read
            http://openocd.org/doc/doxygen/bugs.html
    none separate
    adapter speed: 20000 kHz
    force hard breakpoints
    Info : ftdi: if you experience problems at higher adapter clocks, try the command "ftdi_tdo_sample_edge falling"
    Info : clock speed 20000 kHz
    Info : JTAG tap: esp32.cpu0 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
    Info : JTAG tap: esp32.cpu1 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
    Info : esp32: Debug controller was reset (pwrstat=0x5F, after clear 0x0F).
    Info : esp32: Core was reset (pwrstat=0x5F, after clear 0x0F).

- 如果出现指示权限问题的错误，请打开 `~/esp/openocd-esp32` 目录，参阅 OpenOCD README 文件中关于 “Permissions delegation” 的说明。

- 如果遇到无法找到配置文件的错误，例如 `Can't find board/esp32-wrover-kit-3.3v.cfg`，请检查 `OPENOCD_SCRIPTS` 环境变量是否设置正确，OpenOCD 根据此变量来查找 `-f` 指定的文件，详见 安装 OpenOCD。此外，还需要检查配置文件是否确实位于该路径下。

- 如果出现 JTAG 错误（例如输出为 `...all ones` 或 `...all zeroes`），请检查硬件连接是否正确，除了 ESP32 的管脚之外是否还有其他信号连接到了 JTAG，并查看是否所有器件都已经上电。

### 上传待调试的应用程序

按照正常步骤构建并上传 ESP32 应用程序，具体请参阅 构建首个项目 章节。

除此以外，还可以使用 OpenOCD 通过 JTAG 接口将应用程序镜像烧写到 flash 中，命令如下:

    openocd -f board/esp32-wrover-kit-3.3v.cfg -c "program_esp filename.bin 0x10000 verify exit"

其中 OpenOCD 的烧写命令 `program_esp` 格式如下：

`program_esp <image_file> <offset> [verify] [reset] [exit] [compress] [encrypt] [no_clock_boost] [restore_clock] [skip_loaded]`

- `image_file` - 程序镜像文件存放的路径

- `offset` - 镜像烧写到 flash 中的偏移地址

- `verify` - 烧写完成后校验 flash 中的内容（可选）

- `reset` - 烧写完成后重启目标（可选）

- `exit` - 烧写完成后退出 OpenOCD（可选）

- `compress` - 烧写开始前压缩镜像文件（可选）

- `encrypt` - 烧写到 flash 前加密二进制文件，与 `idf.py encrypted-flash` 功能相同（可选）

- `no_clock_boost` - 禁用在烧写前将目标时钟频率设置为其最大可能值（可选）。默认情况下禁用该选项，即默认启用时钟提升。

- `restore_clock` - 可选。烧写完成后将时钟频率恢复到初始值。默认情况下不启用。

- `skip_loaded` - 可选。如果二进制文件已加载，则跳过烧录。默认情况下不启用。

#### 替代方法：使用 `program_esp_bins`

为了便于在 ESP-IDF 项目中使用，OpenOCD 提供了一个替代命令 `program_esp_bins`，该命令可以通过读取 ESP-IDF 构建过程中生成的 `flasher_args.json` 文件中的构建配置，在单个命令中烧录多个二进制文件。

此方案有如下几点优势：

- 自动从构建输出中读取所有二进制文件及其 flash 地址。

- 根据项目配置自动处理加密分区。

- 无需手动指定每个二进制文件（引导加载程序、分区表、应用程序等）的地址。

基本用法：

    openocd -f board/esp32-wrover-kit-3.3v.cfg -c "program_esp_bins build flasher_args.json verify exit"

##### 命令格式

OpenOCD 烧录命令 `program_esp_bins` 格式如下：

`program_esp_bins <build_dir> <json_file> [verify] [reset] [exit] [compress] [no_clock_boost] [restore_clock] [skip_loaded]`

> - `build_dir` - 包含 `flasher_args.json` 文件的构建目录路径。

> - `json_file` - 包含 flash 配置的 JSON 文件名称（通常为 `flasher_args.json`）。

> - 其他可选参数的使用方式与 `program_esp` 命令相同。详情请参阅 上传待调试的应用程序 章节。

现在可以调试应用程序了，请按照以下章节中的步骤进行操作。

## 启动调试器

ESP32 的工具链中带有 GNU 调试器（简称 GDB），它和其它工具链软件共同存放于 xtensa-esp32-elf-gdb 中。除了直接在命令行终端中调用并操作 GDB 外，也可以在 IDE（例如 Eclipse、Visual Studio Code 等）中进行调用，使用图形用户界面间接操作 GDB，这一方法无需在终端中输入任何命令。

关于调试器的使用方法，详见以下链接：

- 使用 Eclipse 调试

- 使用命令行调试

- 使用 VS Code 调试

建议首先检查调试器能否在 命令行终端 下正常工作，然后再使用 Eclipse 集成开发环境 进行调试工作。

## 调试范例

本节适用于不熟悉 GDB 的用户，下文将使用 get-started/blink 下简单的应用程序来演示 调试会话的工作流程，同时会介绍以下常用的调试操作：

1. 浏览代码，查看堆栈和线程

2. 设置和清除断点

3. 手动暂停目标

4. 单步执行代码

5. 查看并设置内存

6. 观察和设置程序变量

7. 设置条件断点

此外还会提供在 在命令行终端进行调试 下使用 GDB 调试的案例。

备注

调试 FreeRTOS 对象 目前仅适用于命令行调试。

在演示之前，请完成 ESP32 目标板设置并加载 get-started/blink 至 ESP32 中。

## 从源码构建 OpenOCD

本文档在演示中所使用的 OpenOCD 是预编译好的二进制发行版，在 安装 OpenOCD 章节中有所介绍。

如需根据特定需求从源码构建 OpenOCD，请参考 OpenOCD 构建工作流程。该工作流演示了如何在不同平台 (Windows, Linux, macOS) 上构建 OpenOCD。

## 注意事项和补充内容

本节列出了上文中提到的所有注意事项和补充内容的链接。

- 注意事项和补充内容
  - 可用的断点和观察点
  - 关于断点的补充知识
  - flash 映射 vs 软件 flash 断点
  - “next” 命令无法跳过子程序的原因
  - OpenOCD 支持的编译时的选项
  - 支持 FreeRTOS
  - 在 OpenOCD 的配置文件中设置 SPI flash 的工作电压
  - 优化 JTAG 的速度
  - 调试器的启动命令的含义
  - 根据目标芯片配置 OpenOCD
  - 复位 ESP32
  - JTAG 管脚是否能用于其他功能
  - JTAG 与 flash 加密和安全引导
  - JTAG 和 ESP32-WROOM-32 AT 固件兼容性问题
  - 报告 OpenOCD/GDB 的问题

## 相关文档

- 使用调试器

- 调试示例

- 注意事项和补充内容

- 应用层跟踪库

- ESP-Prog 调试板介绍

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。