ESP32S2应用开发——USB通信(CDC类)


目录

  • ESP32S2应用开发——USB通信(CDC类)
  • 前言
  • 1 硬件介绍
  • 1.1 硬件连接
  • 2 软件开发
  • 2.1 安装开发板
  • 2.2 安装库
  • 2.3 运行示例代码
  • 2.4 USB传输速度测试
  • 结束语


前言

ESP32-S2是继ESP32之后新出的一款的MCU,而USB接口是ESP32-S2的一大特色,虽然使用的只是USB1.1协议,但是相比于串口而言传输速度还是要快很多的。对于音频或者视频等数据的传输,使用usb明显是比串口有优势的。
因为前段时间项目需求,需要用到ESP32-S2的USB,于是就花了些时间研究了一下,发现网上关于ESP32-S2 USB的介绍很少而且大多资料都过时了,于是就有了这篇博客。
好了,废话不多说了,马上开始讲解。

1 硬件介绍

本文的硬件配置如下:

模块

型号

说明

ESP32-S2

ESP32-S2-WROVER

这是乐鑫的一款模组,内部主要是用乐鑫的ESP32-S2再加上一个4M FLASH和2M PSRAM组成,开发板用的是乐鑫的ESP32-S2-SAOLA

ESP32-S2的引脚很多我就不一一介绍了,这一讲主要用到的UART0和USB(GPIO19,GPIO20)。

1.1 硬件连接

我这里用的是开发板,硬件连接比较简单。
UART0通过USB转TTL芯片连接到PC端。
USB通过GPIO19和GPIO20直连PC端的USB接口。

引脚

描述

说明

GPIO19

USB D-

USB信号线,直连PC端即可,不需要接转换芯片

GPIO20

USB D+

USB信号线,直连PC端即可,不需要接转换芯片

U0TXD

串口TX

方便调试使用,需要接USB转换TTL才能连接到PC端

U0RXD

串口RX

方便调试使用,需要接USB转换TTL才能连接到PC端

2 软件开发

2.1 安装开发板

关于ESP32-S2 Arduino的环境搭建我之前出过教程了,这里就不多说了,不懂的同学可以先看下我之前的博客。

2.2 安装库

打开Arduino IDE,依次打开 工具 -> 管理库…
在搜索框输入需要安装的库名称,找到对应的库,点击安装即可。

本文需要使用的Arduino库如下:

Arduino库

版本

说明

ESP32TinyUSB

1.3.4

USB相关库,使用该库要确保ESP32库版本在2.0.0以上

esp32

2.0.1

建议使用该版本,v2.0.2有个usb相关的结构体定义改了,跟ESP32TinyUSB库不兼容。

如果非要用2.0.2以上版本就需要在ESP32TinyUSB和esp32两者之前选择一个把client_event_callback的定义改掉

esp32 蓝牙 mesh ESP32 蓝牙音频转USB声卡_示例代码

2.3 运行示例代码

ESP32TinyUSB库自带很多examples,我们打开一个cdc的示例代码先测试一下USB通讯。
示例代码如下:

/**
 * Simple CDC device connect with putty to use it
 * author: chegewara
 * Serial - used only for logging
 * Serial1 - can be used to control GPS or any other device, may be replaced with Serial
 */
#include "cdcusb.h"
#if CFG_TUD_CDC
CDCusb USBSerial;

class MyUSBCallbacks : public CDCCallbacks {
    void onCodingChange(cdc_line_coding_t const* p_line_coding)
    {
        int bitrate = USBSerial.getBitrate();
        Serial.printf("new bitrate: %d\n", bitrate);
    }

    bool onConnect(bool dtr, bool rts)
    {
        Serial.printf("connection state changed, dtr: %d, rts: %d\n", dtr, rts);
        return true;  // allow to persist reset, when Arduino IDE is trying to enter bootloader mode
    }

    void onData()
    {
        int len = USBSerial.available();
        Serial.printf("\nnew data, len %d\n", len);
        uint8_t buf[len] = {};
        USBSerial.read(buf, len);
        Serial.write(buf, len);
    }

    void onWantedChar(char c)
    {
        Serial.printf("wanted char: %c\n", c);
    }
};


void setup()
{
    Serial.begin(115200);
    USBSerial.setCallbacks(new MyUSBCallbacks());
    USBSerial.setWantedChar('x');

    if (!USBSerial.begin())
        Serial.println("Failed to start CDC USB stack");

}

void loop()
{
    while (Serial.available())
    {
        int len = Serial.available();
        char buf1[len];
        Serial.read(buf1, len);
        int a = USBSerial.write((uint8_t*)buf1, len);
    }
}

#endif

esp32 蓝牙 mesh ESP32 蓝牙音频转USB声卡_arduino_02


运行结果如下:

设备管理器能看到两个com口(一个是串口转换芯片,一个是ESP32-S2的USB)。

esp32 蓝牙 mesh ESP32 蓝牙音频转USB声卡_esp32_03


用串口助手先打开UART对应的端口,波特率115200。再打开一个串口助手,连接USB虚拟串口对应的com口,此时能看到UART会出现一些log。

esp32 蓝牙 mesh ESP32 蓝牙音频转USB声卡_示例代码_04


USB连接上之后,两个串口助手之间可以互发数据,说明USB通讯是没问题的。

esp32 蓝牙 mesh ESP32 蓝牙音频转USB声卡_示例代码_05


提示:如果烧录程序之后出现一直重启的现象,可能是因为MCU原本出厂的固件有一部分没有被擦除导致的,可以使用乐鑫的flash烧录工具对整个MCU进行擦除之后再烧录Arduino的程序。

2.4 USB传输速度测试

简单写一个测试代码用来测试USB数据传输的速度。
示例代码如下:

#include "cdcusb.h"
#include "Arduino.h"
#include <esp_heap_caps.h>

#define FILE_SIZE 971240   // 测试文件的大小
uint8_t *rx_buf;
uint32_t rx_num = 0;
uint8_t first_time_flag = 1;
long lTime;

CDCusb CDCUSBSerial;

class MyCDCCallbacks : public CDCCallbacks {
    void onCodingChange(cdc_line_coding_t const* p_line_coding)
    {
        int bitrate = CDCUSBSerial.getBitrate();
        Serial.printf("new bitrate: %d\n", bitrate);
    }

    bool onConnect(bool dtr, bool rts)
    {
        Serial.printf("connection state changed, dtr: %d, rts: %d\n", dtr, rts);
        return true;  // allow to persist reset, when Arduino IDE is trying to enter bootloader mode
    }

    void onData()
    {
        if(first_time_flag)
        {
            first_time_flag = 0;
            lTime = micros();
        }

        int len = CDCUSBSerial.available();
        CDCUSBSerial.read(&rx_buf[rx_num], len);
        rx_num += len;

        if(rx_num >= FILE_SIZE)
        {
            lTime = micros() - lTime;
            Serial.printf("time: %f s \n", lTime / 1000000.0);
            Serial.printf("speed:%f kb/s", ((float)FILE_SIZE / 1024.0) / (lTime / 1000000.0));
            first_time_flag = 1;
            rx_num = 0;
        }
    }
};

void setup()
{
    Serial.begin(115200);

    if (!CDCUSBSerial.begin())
        Serial.println("Failed to start CDC USB stack");

    CDCUSBSerial.setCallbacks(new MyCDCCallbacks());

    rx_buf = (uint8_t*)ps_malloc(FILE_SIZE);
}

void loop()
{

}

通过串口助手往USB发送一个大文件(971240字节,约948.5kb)。

提示:这里串口助手打开文件时显示的时间是按当前波特率估算出来的,但是实际上我们用的是虚拟串口,USB传输是没有波特率这个参数的,这里波特率不管设置为多少,实际的速度都一样,都是以USB传输速度为准。

esp32 蓝牙 mesh ESP32 蓝牙音频转USB声卡_串口_06

通过ESP32-S2的串口0打印实际的传输的时间和速度。

esp32 蓝牙 mesh ESP32 蓝牙音频转USB声卡_arduino_07


经过sscom这个串口助手传输文件测试,ESP32-S2 USB的最大传输速度在190kb/s左右,实际上加上一些应用代码之后,速度会有所下降,约160kb/s左右(这个速度跟具体的应用有关)。

因为USB接收是中断处理的,MCU如果一直处于闲置状态,那USB的数据传输速度可以达到最大。反之,MCU如果一直在运行其他应用代码,那么在接收USB数据时只能通过频繁的中断来完成数据的读取,此时接收的效率明显是要下降一些的。另外,传输速度跟MCU的接收方式也有关系,USB1.1最大支持一次接收64字节,所以MCU在进入回调函数时,应该根据把当前收到的所有数据一次性读取完,而不是每次回调只读一个字节。

后期测试补充:
在使用sscom这个串口助手时,文件的传输速度跟设置的波特率无关,但是后来用另外一个串口助手(UartAssist)时发现用这个工具设置的波特率跟实际传输速度有关联,这就很奇怪了,因为这个只是虚拟串口,实际上是按照USB1.1协议来传输数据的,理论上不应该出现这种情况的。然后我测试了多种不同的波特率,发现波特率较小时,实际传输速度与波特率基本一致,波特率越大速度越快,当波特率增大到2M时,速度与之前sscom串口助手测试的速度接近。继续增大波特率到某个值之后,不管波特率设置多少,速度都不再增大了。最大传输速度在270kb/s左右。
从目前的现象来看,USB的传输速度跟串口工具本身也有关系,具体的原因还没搞清楚,有懂的老哥解答一下吗?

结束语

好了,关于ESP32-S2 USB的使用就介绍到这里。如果这篇文章对你有帮助,可以点赞收藏,如果还有什么问题,欢迎在评论区留言或者私信给我。

补充说明:
最近我经常收到一些私信,这是不是真的USB?这是串口转USB吧?
所以我这里统一补充说明一下,如何区分USB CDC和串口。
CDC类USB和HID不同,它枚举出来的设备确实是一个串行设备,看着是很像串口,但实际上是不一样的。
主要有以下几个区别:

区别

USB

串口

1

两个USB之间是直连的,不需要转换电平

串口是TTL电平的,如果没有转换芯片,是没法直接连接PC端的USB接口的。常用的转换IC如:CH340、CP2102、PL2303、FT232、MAX232等

2

传输速度快

传输速度慢

3

遵循USB协议(可通过逻辑分析仪抓取和解析)

遵循串口协议(可通过逻辑分析仪抓取和解析)

4

USB有枚举过程(可通过BusHound抓取通讯过程)

串口不存在枚举过程

Arduino开发教程汇总: