ESP32-USB Serial/JTAG Controller使用

概述

ESP32-c3内部带有1个USB Serial/JTAG控制器,可用于下载flash程序、JTAG调试、虚拟串口通信等功能。硬件框图如下:
在这里插入图片描述
其主要特性如下:

  • 支持usb全速设备(12Mbps),不支持高速(480Mbps)
  • 固定为CDC-ACM设备(Communication Device Class - Abstract Control Model)
  • 64byte硬件缓冲区
  • 大多数操作系统即插即用

CDC-ACM功能描述

由下表可见,ESP32-C3只能响应主机设置RTS/DTR指令,以实现芯片复位和程序下载,设置波特率无效。
在这里插入图片描述
RTS/DTR状态说明:
在这里插入图片描述
esp32-c3通过内部APB总线与CDC-ACM连接,cpu通过读写寄存器判断USB_SERIAL_JTAG_SERIAL_OUT_EP_DATA_AVAIL1(表示接收buf有数据)接收主机发来的数据;并可通过判断USB_REG_SERIAL_IN_EP_DATA_FREE1(表示发送buf未满)可向发送buf写入数据,随后设置USB_SERIAL_JTAG_WR_DONE==1,以发送buf数据到主机。

环境说明

  • 软件采用IDF4.4.2版本
  • 硬件采用esp32-c3芯片
  • 硬件需具备usb和uart接口,uart配置用于控制台打印信息

注意: 使用该功能前需要禁止控制台打印到USB,否则控制台打印信息会和usb打印信息混在一起,配置如下:
在这里插入图片描述

硬件查询方式使用

关键函数说明

#include "hal/usb_serial_jtag_ll.h"

//检测接收buf是否有数据
static inline int usb_serial_jtag_ll_rxfifo_data_available(void)//返回:0-没数据  1-有数据

//从接收buf读取数据,返回值:实际接收的字节数
static inline int usb_serial_jtag_ll_read_rxfifo(
		uint8_t *buf, //接收数据指针
		uint32_t rd_len)//读取长度,最大64,当设置为最大值64时,会将接收buf读空,并返回读取的实际字节数

//写数据到发送buf
static inline uint32_t usb_serial_jtag_ll_write_txfifo(
		const uint8_t *buf, //发送数据指针
		uint32_t wr_len)//写入长度,最大64,如果写入过程中,发送buf满了,则函数退出并返回写入的实际字节数

//刷新发送buf
static inline void usb_serial_jtag_ll_txfifo_flush(void)//即发送buf数据到主机

示例代码

本示例代码采用uart0输出调试信息,基本功能是将usb发来的任意数据进行环回发出。该代码采用单任务循环检查接收buf,占用cpu资源,后期改成中断形式更佳,代码如下:

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"

#include "string.h"
#include "hal/usb_serial_jtag_ll.h"

void mytask1(void *pvParameter)
{
    uint8_t *rxbuf;
    int cnt;
    rxbuf = (uint8_t *)malloc(64);
    int rxcnt;
    while (1)
    {
        if (usb_serial_jtag_ll_rxfifo_data_available())//等待接收buf非空
        {
            rxcnt = usb_serial_jtag_ll_read_rxfifo(rxbuf, 64);//接收buf中的所有数据
            cnt = (int)usb_serial_jtag_ll_write_txfifo((const uint8_t *)rxbuf, rxcnt);//将接收数据回环发送
            usb_serial_jtag_ll_txfifo_flush();//刷新发送buf
            printf("Send %d characters to host \n", cnt);//在调试串口打印发送的数量
        }
        vTaskDelay(pdMS_TO_TICKS(10));
    }
    free(rxbuf);//与malloc成对使用
    vTaskDelete(NULL);
}
void app_main(void)
{
    xTaskCreate(mytask1, "mytask1", 1024 * 5, NULL, 1, NULL);

}

效果:
在调试接口打印信息:

Send 11 characters to host
Send 11 characters to host
Send 11 characters to host
Send 11 characters to host
Send 11 characters to host
Send 11 characters to host

在usb接口打印信息如下:

[21:28:37.884]发→◇hello world□
[21:28:37.884]收←◆hello world
[21:28:38.888]发→◇hello world□
[21:28:38.888]收←◆hello world
[21:28:39.886]发→◇hello world□
[21:28:39.886]收←◆hello world

官方中断方式使用

关键函数说明

包含头文件

#include "driver/usb_serial_jtag.h"

安装卸载驱动

由于硬件buf为64byte,环形buf至少应能容纳主机一次发送的最大64byte数据,因此其大小应大于64字节。

//环形buf配置结构体
usb_serial_jtag_driver_config_t usb_cdc = {
    .rx_buffer_size = 65, //环形buf应大于64
    .tx_buffer_size = 65  //环形buf应大于64
};
//安装usb_serial_jtag驱动
esp_err_t usb_serial_jtag_driver_install(usb_serial_jtag_driver_config_t *usb_serial_jtag_config)//参数为环形buf配置的结构体地址

//卸载usb_serial_jtag驱动
esp_err_t usb_serial_jtag_driver_uninstall(void)

收发数据

收发数据函数实际读写环形buf,该驱动安装后会启用中断,对接收来说,当有数据到来时触发中断,并从硬件buf拷贝数据到环形buf。
当环形buf溢出时,主机仍能够发送数据,但会导致数据丢失。

//接收主机发送的数据,返回值为接收到的数据量
int usb_serial_jtag_read_bytes(
		void* buf, //接收数据指针
		uint32_t length, //接收数据长度
		TickType_t ticks_to_wait)//超时时间,portMAX_DELAY表示永久阻塞

//向主机发送数据,返回值为实际发送的数据量
int usb_serial_jtag_write_bytes(
		const void* src, //发送数据指针
		size_t size, //发送数据大小
		TickType_t ticks_to_wait)//超时时间,portMAX_DELAY表示永久阻塞

示例程序

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"

#include "string.h"
#include "driver/usb_serial_jtag.h"

void mytask1(void *pvParameter)
{
    uint8_t *rxbuf;
    int sendcnt;
    rxbuf = (uint8_t *)malloc(64);
    int rxcnt;
    while (1)
    {
        rxcnt = usb_serial_jtag_read_bytes((void *)rxbuf, 10, portMAX_DELAY); //阻塞当前任务,直到接收数据
        sendcnt = (int)usb_serial_jtag_write_bytes((const void *)rxbuf, rxcnt, portMAX_DELAY);
        printf("task1: Send %d characters to host \n", sendcnt);
        //vTaskDelay(pdMS_TO_TICKS(1000));
    }
    free(rxbuf); //释放缓冲区
}

void app_main(void)
{
    usb_serial_jtag_driver_config_t usb_cdc = {
        .rx_buffer_size = 65, //环形buf应大于64
        .tx_buffer_size = 65  //环形buf应大于64
    };
    TaskHandle_t task1_handler;
    ESP_ERROR_CHECK(usb_serial_jtag_driver_install(&usb_cdc)); //安装usb_serial驱动
    xTaskCreate(mytask1, "mytask1", 1024 * 5, NULL, 1, &task1_handler);
    printf("start loop back \n ");
    vTaskDelay(pdMS_TO_TICKS(30000)); //运行30s后退出任务1
    vTaskDelete(task1_handler);
    usb_serial_jtag_driver_uninstall(); //卸载usb_serial驱动
    printf("stop loop back \n ");
}

该程序在任务1中对usb接口进行了数据回环,启动后,主机可通过usb虚拟串口收发数据。

为验证环形buf溢出的情况,将任务1中的1s延迟打开,模拟处理时间,主机循环每10ms发1包数据,结果如下:

在这里插入图片描述
环形buf只开了65个字节,因此返回大量数据丢失。调试接口打印信息为:

start loop back
task1: Send 10 characters to host
task1: Send 10 characters to host
task1: Send 10 characters to host
task1: Send 10 characters to host
task1: Send 10 characters to host
task1: Send 10 characters to host
task1: Send 5 characters to host
task1: Send 7 characters to host
stop loop back

vscode esp idf debug模式。[Build] Project build complete. To flash, run: ESP-IDF: Flash your project in the ESP-IDF Visual Studio Code Extension or in a ESP-IDF Terminal: idf.py flash or idf.py -p PORT flash or python -m esptool --chip esp32 -b 460800 --before default_reset --after hard_reset --port /dev/ttyUSB0 write_flash --flash_mode dio --flash_size 2MB --flash_freq 40m 0x1000 bootloader/bootloader.bin 0x10000 pca9685_test.bin 0x8000 partition_table/partition-table.bin or from the "/home/cm/9_Robot/SpotMicroAI/esp/0_test/pca9685_test/build" directory python -m esptool --chip esp32 -b 460800 --before default_reset --after hard_reset write_flash "@flash_args" /home/cm/.espressif/tools/openocd-esp32/v0.12.0-esp32-20241016/openocd-esp32/bin/openocd Open On-Chip Debugger v0.12.0-esp32-20241016 (2024-10-16-14:17) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html /home/cm/.espressif/tools/openocd-esp32/v0.12.0-esp32-20241016/openocd-esp32/bin/openocd [/Build] [OpenOCD] Open On-Chip Debugger v0.12.0-esp32-20241016 (2024-10-16-14:17) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html debug_level: 2 Info : Listening on port 6666 for tcl connections Info : Listening on port 4444 for telnet connections ❌ Error: unable to open ftdi device with description &#39;*&#39;, serial &#39;*&#39; at bus location &#39;*&#39; ❌ /home/cm/.espressif/tools/openocd-esp32/v0.12.0-esp32-20241016/openocd-esp32/share/openocd/scripts/target/esp_common.cfg:9: Error: at file "/home/cm/.espressif/tools/openocd-esp32/v0.12.0-esp32-20241016/openocd-esp32/share/openocd/scripts/target/esp_common.cfg", line 9 For assistance with OpenOCD errors, please refer to our Troubleshooting FAQ: https://github.com/espressif/openocd-esp32/wiki/Troubleshooting-FAQ OpenOCD Exit with non-zero error code 1 [Stopped] : OpenOCD Server [/OpenOCD] [Flash] Can&#39;t perform JTag flash, because OpenOCD server is not running!
03-12
<think>好的,我现在需要解决用户在VSCode中使用ESP-IDF进行调试时遇到的OpenOCD无法打开FTDI设备的问题。用户提到错误发生在启动OpenOCD后,导致JTAG闪存失败,设备描述符和序列号显示为星号。我需要根据提供的引用资料和我的知识库来寻找解决方案。 首先,我需要确认用户的环境是否正确配置。根据引用[1],安装ESP-IDF需要Python 3.7以上,并且正确添加到PATH中。可能用户的环境变量没有正确设置,或者依赖项缺失。另外,引用[4]提到了在VSCode中配置ESP-IDF扩展,可能需要检查扩展的配置是否正确。 接下来,用户的问题涉及FTDI设备的访问权限。在Linux系统中,通常需要将用户添加到dialout组,或者设置udev规则。引用[1]提到安装过程中的操作系统要求,可能用户没有正确配置这些权限,导致OpenOCD无法访问设备。此外,Windows系统可能需要安装FTDI驱动程序,如引用[1]中提到的ESP-IDF安装工具可能包含这些驱动,但用户是否安装了正确的版本? 检查OpenOCD的配置也很重要。用户可能在调试配置中使用了错误的接口或参数。引用[3]提到参考VSCode配置ESP-IDF的博客,可能需要确认openocd.cfg文件是否正确指定了板型,例如esp32s3.cfg等。如果板型不匹配,可能导致通信失败。 另外,硬件连接问题可能导致此错误。检查JTAG连接是否稳固,线缆是否损坏,或者是否选择了正确的USB端口。引用[2]提到的sdkconfig文件可能包含JTAG相关的配置,需要确认是否正确启用。 还有可能是其他程序占用了FTDI设备,比如串口监视工具或之前未正确关闭的调试会话。需要确保没有其他进程在使用该设备。 最后,如果以上步骤都无效,可能需要更新或重新安装ESP-IDF工具链,包括OpenOCD,确保使用的是最新版本,避免已知的兼容性问题。引用[1]中的安装工具可能提供了一键安装,但用户可能需要手动更新组件。 总结可能的解决步骤:检查权限、安装驱动、验证硬件连接、检查OpenOCD配置、关闭占用进程、更新工具链。需要逐步排除可能的原因,并参考提供的引用资料中的安装和配置指南。</think>针对VSCode中ESP-IDF调试模式下OpenOCD无法打开FTDI设备的问题,请按照以下步骤排查解决: ### 一、检查设备权限(Linux/macOS) 1. **添加用户到`dialout`组**: ```bash sudo usermod -aG dialout $USER sudo reboot ``` Linux系统需要通过此命令允许普通用户访问串口设备[^1] 2. **配置udev规则**: 创建文件`/etc/udev/rules.d/99-esp32.rules`,添加: ```bash # ESP32 UART SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", MODE="0666" # ESP32 JTAG SUBSYSTEM=="usb", ATTRS{idVendor}=="0403", MODE="0666" ``` 执行`sudo udevadm control --reload`后重新插拔设备 ### 二、Windows系统驱动处理 1. 卸载现有FTDI驱动 - 设备管理器中找到`Universal Serial Bus devices`下的`USB Serial Converter` - 右键选择"卸载设备",勾选"删除此设备的驱动程序软件" 2. 安装**Zadig**工具重新配置驱动 - 下载地址:https://zadig.akeo.ie/ - 选择设备后安装`WinUSB`或`libusb-win32`驱动 ### 三、验证硬件连接 1. 检查JTAG接口连接: - 确认接线符合ESP32开发板定义(TCK/TMS/TDI/TDO) - 典型连接方式: ``` ESP32 JTAG适配器 GPIO12 —— TCK GPIO14 —— TMS GPIO13 —— TDO GPIO15 —— TDI GND —— GND ``` 2. 使用`lsusb`(Linux)或设备管理器(Windows)确认设备识别 ### 四、配置调试参数 1. 修改`.vscode/launch.json`: ```json { "configurations": [ { "openocdConfigs": [ "interface/ftdi/esp32_devkitj_v1.cfg", "board/esp32-wrover-kit-3.3v.cfg" ] } ] } ``` 根据实际使用的调试器型号选择配置文件[^3] ### 五、关闭冲突进程 执行以下命令终止可能占用设备的进程: ```bash # Linux sudo systemctl stop ModemManager # Windows taskkill /F /IM putty.exe /IM screen.exe ``` ### 六、更新工具链 通过ESP-IDF命令提示符执行: ```bash export IDF_TOOLS_PATH=~/.espressif python $IDF_PATH/tools/idf_tools.py install openocd-esp32 python $IDF_PATH/tools/idf_tools.py --prefer-system install cmake ninja ```
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值