首页 > 其他分享 >ESP32-S3 通过USB Serial更新固件

ESP32-S3 通过USB Serial更新固件

时间:2024-01-25 15:56:13浏览次数:27  
标签:HWSerial USB CDC S3 ESP32 ARDUINO data event

本文主要介绍ESP32-S3在ubuntu20.04下通过ESP32-S3的USB Serial更新固件的方法以及遇到的问题的解决方法。

现在市面上ESP32-S3的开发板,大多都是ESP32-S3搭配一个USB-To-Serial桥芯片(CP210X、CH340等)来使用,硬件上再设计一个自动烧录电路(如下所示),以达到通过串口实现固件更新的目的。

在实际的产品中,使用USB-To-Serial桥芯片不仅增加成本,也浪费PCB上面积。ESP32-S3是支持USB OTG的,可以使用这个USB口来进行固件更新吗?

当然可以!

除 USB OTG 之外,ESP32-S3 还内置一个 USB 串行/JTAG 控制器。当只使用内部收发器时,USB OTG 和 USB 串行/JTAG 外设共用这个收发器。默认情况下,内部收发器与USB 串行/JTAG 外设相连。当RTC_CNTL_SW_HW_USB_PHY_SEL_CFG 为 0 时,eFuse 中的 EFUSE_USB_PHY_SEL 位决定内部收发器与哪个外设相连。若该位为 0,内部收发器与 USB 串行/JTAG 外设相连;若该位为 1,内部收发器与 USB OTG 外设相连。当 RTC_CNTL_SW_HW_USB_PHY_SEL_CFG 为 1 时,由 RTC_CNTL_SW_USB_PHY_SEL_CFG控制内部收发器与哪个外设相连(与 EFUSE_USB_PHY_SEL 位的使用方式相同)。 

CDC-ACM 接口遵循标准 USB CDC-ACM 类别进行虚拟串口通信,包含一个虚拟中断端点(不会发送任何事件,无使用需求)以及一个批量输入端点 (Bulk IN) 和批量输出端点 (Bulk OUT) 进行数据接收和发送。这些端点一次可以处理最高 64 字节的数据包,实现高吞吐量。CDC-ACM 为标准的 USB 设备类型,主机一般无需任何特殊安装程序就能正常工作,也就是说,当一个 USB 调试设备正确连接至主机时,操作系统应能在片刻后显示新的串口信息。 除了通用的通信之外,CDC-ACM 接口还可以复位 ESP32-S3 并选择使其进入下载模式,从而烧录新的固件。这一功能可通过设置虚拟串口的 RTS 和 DTR 线来实现。   当我们的程序没有操作USB,完全按照eFuse的配置让USB工作以后,在Linux主机下看到的设备枚举信息如下:
usb 1-4: new full-speed USB device number 114 using xhci_hcd
usb 1-4: New USB device found, idVendor=303a, idProduct=1001, bcdDevice= 1.01
usb 1-4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 1-4: Product: USB JTAG/serial debug unit
usb 1-4: Manufacturer: Espressif
usb 1-4: SerialNumber: 48:27:E2:E1:E5:30
cdc_acm 1-4:1.0: ttyACM1: USB ACM device

这时我们使用esptool来更新固件是可以的,命令如下:

esptool --chip esp32s3 --port /dev/ttyACM1 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 8MB 0x10000 test_firmware.bin 

注意:ubuntu20.04使用apt下载的esptool版本太低,不支持ESP32-S3的固件更新,需要到https://github.com/espressif/esptool下载最新的esptool。

但是当我们实现了ESP32-S3的USB Serial功能以后,就不能再使用esptool自动更新固件了,会一直打印connecting......消息。需要先通过BOOT和Reset引脚让ESP32-S3进入下载模式,才能使用esptool更新固件成功。

USB Serial代码demo如下(基于Arduino IDE):

#if ARDUINO_USB_MODE
#warning This sketch should be used when USB is in OTG mode
void setup(){}
void loop(){}
#else
#include "USB.h"

#if ARDUINO_USB_CDC_ON_BOOT
#define HWSerial Serial0
#define USBSerial Serial
#else
#define HWSerial Serial
USBCDC USBSerial;
#endif

static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){
  if(event_base == ARDUINO_USB_EVENTS){
    arduino_usb_event_data_t * data = (arduino_usb_event_data_t*)event_data;
    switch (event_id){
      case ARDUINO_USB_STARTED_EVENT:
        HWSerial.println("USB PLUGGED");
        break;
      case ARDUINO_USB_STOPPED_EVENT:
        //HWSerial.println("USB UNPLUGGED");
        break;
      case ARDUINO_USB_SUSPEND_EVENT:
        HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en);
        break;
      case ARDUINO_USB_RESUME_EVENT:
        HWSerial.println("USB RESUMED");
        break;

      default:
        break;
    }
  } else if(event_base == ARDUINO_USB_CDC_EVENTS){
    arduino_usb_cdc_event_data_t * data = (arduino_usb_cdc_event_data_t*)event_data;
    switch (event_id){
      case ARDUINO_USB_CDC_CONNECTED_EVENT:
        HWSerial.println("CDC CONNECTED");
        break;
      case ARDUINO_USB_CDC_DISCONNECTED_EVENT:
        HWSerial.println("CDC DISCONNECTED");
        break;
      case ARDUINO_USB_CDC_LINE_STATE_EVENT:
        HWSerial.printf("CDC LINE STATE: dtr: %u, rts: %u\n", data->line_state.dtr, data->line_state.rts);
        break;
      case ARDUINO_USB_CDC_LINE_CODING_EVENT:
        HWSerial.printf("CDC LINE CODING: bit_rate: %u, data_bits: %u, stop_bits: %u, parity: %u\n", data->line_coding.bit_rate, data->line_coding.data_bits, data->line_coding.stop_bits, data->line_coding.parity);
        break;
      case ARDUINO_USB_CDC_RX_EVENT:
        HWSerial.printf("CDC RX [%u]:", data->rx.len);
        {
            uint8_t buf[data->rx.len];
            size_t len = USBSerial.read(buf, data->rx.len);
            USBSerial.write(buf, data->rx.len); //write back to usb host
            HWSerial.write(buf, len);
        }
        HWSerial.println();
        break;
       case ARDUINO_USB_CDC_RX_OVERFLOW_EVENT:
        HWSerial.printf("CDC RX Overflow of %d bytes", data->rx_overflow.dropped_bytes);
        break;

      default:
        break;
    }
  }
}

void setup() {
  HWSerial.begin(115200);
  HWSerial.setDebugOutput(true);

  USB.onEvent(usbEventCallback);
  USBSerial.onEvent(usbEventCallback);

  USBSerial.begin();
  USB.begin();
}

void loop() {
  while(HWSerial.available()){
    size_t l = HWSerial.available();
    uint8_t b[l];
    l = HWSerial.read(b, l);
    USBSerial.write(b, l);
  }
}
#endif /* ARDUINO_USB_MODE */

Arduino IDE里Tools->Board我们选择的是“ESP32S3 Dev Module”,编译下载完之后,把USB接到ubuntu20.04主机上,枚举信息如下:

usb 1-4: new full-speed USB device number 104 using xhci_hcd
usb 1-4: New USB device found, idVendor=303a, idProduct=1001, bcdDevice= 1.00
usb 1-4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 1-4: Product: ESP32S3_DEV
usb 1-4: Manufacturer: Espressif Systems
usb 1-4: SerialNumber: 4827E2E1E530
cdc_acm 1-4:1.0: ttyACM1: USB ACM device

解决办法

根据esp32-s3_technical_reference_manual_cn.pdf,有如下信息:

 

RTC_CNTL_SW_USB_PHY_SEL 置 1 允许 USB OTG 使用内部 USB 接收器。清 0 允许USB-Serial-JTAG 使用内部 USB 接收器。本字段仅在 RTC_CNTL_SW_HW_USB_PHY_SEL 时生效。(R/W) RTC_CNTL_SW_HW_USB_PHY_SEL 置 1 软件分配内部 USB 接收器的使用(RTC_CNTL_SW_USB_PHY_SEL);清 0 硬件分配内部 USB 接收器的使用。(R/W) 所以,当我们想通过ubuntu20.04主机更新ESP32-S3的固件时,先通过CDC-ACM设备发送一个命令给ESP32-S3,ESP32-S3收到这个命令后,从当前USB的工作模式(CDC-ACM切换到USB Serial/JTAG模式),然后就可以用esptool直接进行固件更新了,省去了按BOOT和Reset引脚进入ESP32-S3固件下载模式的步骤。 完整代码如下:
#if ARDUINO_USB_MODE
#warning This sketch should be used when USB is in OTG mode
void setup(){}
void loop(){}
#else
#include "USB.h"

#if ARDUINO_USB_CDC_ON_BOOT
#define HWSerial Serial0
#define USBSerial Serial
#else
#define HWSerial Serial
USBCDC USBSerial;
#endif

#define DR_REG_RTCCNTL_BASE                     0x60008000
#define RTC_CNTL_USB_CONF_REG          (DR_REG_RTCCNTL_BASE + 0x120)

//write value to register
#define REG_WRITE(_r, _v)  do {                                                                                        \
             (*(volatile uint32_t *)(_r)) = (_v);                                                                       \
         } while(0)

//read value from register
#define REG_READ(_r) ({                                                                                                \
             (*(volatile uint32_t *)(_r));                                                                              \
         })


void switch_to_download_mode(uint32_t ms)
{// 切换到USB Serial/JTAG控制器
REG_WRITE(RTC_CNTL_USB_CONF_REG, 0x0); // 拉低D+和D-持续100ms,触发USB复位重新枚举 REG_WRITE(RTC_CNTL_USB_CONF_REG, 0xa8); delay(ms); REG_WRITE(RTC_CNTL_USB_CONF_REG, 0x0); } static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){ if(event_base == ARDUINO_USB_EVENTS){ arduino_usb_event_data_t * data = (arduino_usb_event_data_t*)event_data; switch (event_id){ case ARDUINO_USB_STARTED_EVENT: HWSerial.println("USB PLUGGED"); break; case ARDUINO_USB_STOPPED_EVENT: //HWSerial.println("USB UNPLUGGED"); break; case ARDUINO_USB_SUSPEND_EVENT: HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en); break; case ARDUINO_USB_RESUME_EVENT: HWSerial.println("USB RESUMED"); break; default: break; } } else if(event_base == ARDUINO_USB_CDC_EVENTS){ arduino_usb_cdc_event_data_t * data = (arduino_usb_cdc_event_data_t*)event_data; switch (event_id){ case ARDUINO_USB_CDC_CONNECTED_EVENT: HWSerial.println("CDC CONNECTED"); break; case ARDUINO_USB_CDC_DISCONNECTED_EVENT: HWSerial.println("CDC DISCONNECTED"); break; case ARDUINO_USB_CDC_LINE_STATE_EVENT: HWSerial.printf("CDC LINE STATE: dtr: %u, rts: %u\n", data->line_state.dtr, data->line_state.rts); break; case ARDUINO_USB_CDC_LINE_CODING_EVENT: HWSerial.printf("CDC LINE CODING: bit_rate: %u, data_bits: %u, stop_bits: %u, parity: %u\n", data->line_coding.bit_rate, data->line_coding.data_bits, data->line_coding.stop_bits, data->line_coding.parity); break; case ARDUINO_USB_CDC_RX_EVENT: HWSerial.printf("CDC RX [%u]:", data->rx.len); { uint8_t buf[data->rx.len]; size_t len = USBSerial.read(buf, data->rx.len); USBSerial.write(buf, data->rx.len); //write back to usb host HWSerial.write(buf, len); if (buf[0] == 'U') { //如果收到'U'字符,切换到USB Serial/JTAG模式 switch_to_download_mode(100); } } HWSerial.println(); break; case ARDUINO_USB_CDC_RX_OVERFLOW_EVENT: HWSerial.printf("CDC RX Overflow of %d bytes", data->rx_overflow.dropped_bytes); break; default: break; } } } void setup() { HWSerial.begin(115200); HWSerial.setDebugOutput(true); USB.onEvent(usbEventCallback); USBSerial.onEvent(usbEventCallback); USBSerial.begin(); USB.begin(); } void loop() { while(HWSerial.available()){ size_t l = HWSerial.available(); uint8_t b[l]; l = HWSerial.read(b, l); USBSerial.write(b, l); } } #endif /* ARDUINO_USB_MODE */

如上述代码所示,当ESP32-S3 USB 收到’U‘命令后,把USB切换到USB Serial/JTAG控制器,然后拉低D+和D-触发USB复位,重新枚举后即可直接使用esptool通过主机下CDC-ACM设备更新ESP32-S3的固件。

 

标签:HWSerial,USB,CDC,S3,ESP32,ARDUINO,data,event
From: https://www.cnblogs.com/wanglouxiaozi/p/17987323

相关文章

  • NanoFramework操作ESP32(一)_基础元器件篇(二十二)_DHT11温湿度传感器
    一、元器件介绍1、针脚用途编号名称功能1VCC电源正2TRIG触发控制信号输入3ECHO回响信号输出4GND电源地2、电气参数 二、示例代码1、代码:元器件的针脚ESP32模块的针脚VCC;供电脚+5VTRIG;发送脚IO17ECHO;接收脚IO16GND......
  • 微雪ESP32-S3-Zreo学习笔记之USB串口
    软件下载ESP32-S3-Zero没有板载USB转串口,无法实现自动下载。下载软件时要按住Boot按键再上电,此时电脑会识别到一个USB模拟的COM口,可用于下载软件。开发环境编程环境是使用的esp-idf-4.4.2;值得注意的是在此之前安装了esp-idf-5.0.2、esp-idf-5.1.2都不能正常使用,安装好后使......
  • 无涯教程-CSS3 - 用户界面
    用户界面属性允许您将任何元素更改为几个标准用户界面元素之一。CSS3用户界面中使用的一些常用属性。Sr.No.Value&Remark1appearance用于允许用户将元素制作为用户界面元素。2box-sizing允许用户以清晰的方式将元素固定在区域上。3icon用于在区域上提供图标。......
  • 无涯教程-CSS3 - 多列布局
    CSS3可以将文本内容设计成像报纸一样的多列布局。一些最常用的多列属性,如下所示-Sr.No.Value&Remark1column-count 指定元素应该被分割的列数。2column-fill指定如何填充列3column-gap 指定列与列之间的间隙4column-rule所有column-rule-*属性的简......
  • 无涯教程-CSS3 - 字体属性(Font)
    Web字体用于允许CSS中的字体,这些字体未在本地系统上安装。Sr.No.Font&Remark1TrueTypeFonts(TTF)TrueType是Apple和Microsoft在1980年代后期开发的轮廓字体标准,它成为Windows和MAC操作系统最常用的字体。2OpenTypeFonts(OTF)OpenType是Microsoft开发的可缩放计......
  • 无涯教程-CSS3 - 渐变属性(Gradients)
    渐变显示两种或更多种颜色的组合,如下所示-线性渐变线性渐变用于以线性格式(如从上到下)排列两种或多种颜色。Toptobottom(从上到下)<html><head><style>#grad1{height:100px;background:-webkit-linear-gradient(pink,......
  • 无涯教程-CSS3 - 颜色属性(Color)
    CSS3支持以下其他颜色属性-RGBA颜色HSL颜色HSLA颜色Opacity透明RGBA代表RedGreenBlueAlpha。它是CSS2的扩展,Alpha指定颜色的透明度,参数是0.0到1.0之间的数字。RGBA的示例语法如下所示-#d1{background-color:rgba(255,0,0,0.5);}#d2{background-colo......
  • 无涯教程-CSS3 - boarder-image属性
    CSSBorderimage属性用于向某些元素添加图像边界。边界图像的示例语法如下-#borderimg{border:10pxsolidtransparent;padding:15px;}最常用的值如下所示-Sr.No.Value&Remark1border-image-source用于设置图像路径2border-image-slice用于切片边......
  • 无涯教程-CSS3 - border-radius属性
    CSS3圆角用于通过使用border-radius 属性为正文或文本添加特殊的彩色圆角,语法如下-#rcorners7{border-radius:60px/15px;background:#FF0000;padding:20px;width:200px;height:150px;}下表显示了圆角的可能值,如下所示:Sr.No.Value&Remark1......
  • 解决centos7修改网卡名为eth0仍显示ens33的问题
    1.进入/etc/sysconfig/network-scripts修改网卡配置文件中的DEVICE=与NAME=参数为eth0保存退出后再修改网卡配置文件名mvifcfg-ens33ifcfg-eth02.重新生成grub2文件编辑/etc/default/grub配置文件,在GRUB_CMDLINE_LINUX这个参数后面加入:net.ifnames=0biosdevnam......