ESP32-S2应用开发——USB通信(CDC类)

2023-11-16

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

前言

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的定义改掉
在这里插入图片描述

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

在这里插入图片描述
运行结果如下:
设备管理器能看到两个com口(一个是串口转换芯片,一个是ESP32-S2的USB)。
在这里插入图片描述
用串口助手先打开UART对应的端口,波特率115200。再打开一个串口助手,连接USB虚拟串口对应的com口,此时能看到UART会出现一些log。
在这里插入图片描述
USB连接上之后,两个串口助手之间可以互发数据,说明USB通讯是没问题的。
在这里插入图片描述
提示:如果烧录程序之后出现一直重启的现象,可能是因为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-S2的串口0打印实际的传输的时间和速度。
在这里插入图片描述
经过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开发教程汇总:
https://blog.csdn.net/ShenZhen_zixian/article/details/121659482

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ESP32-S2应用开发——USB通信(CDC类) 的相关文章

  • 如何返回变量?

    我正在编写一个程序 将字母翻译成摩尔斯电码 然后将它们传输到 LED 并闪烁 我无法返回值 include
  • 递增数组值 - Arduino

    我正在尝试增加一些数组值 int counter 0 0 0 0 0 0 0 0 如果位置 0 的数字值达到 25 则位置 1 的值加 1 位置 0 重置为 0 依此类推 当索引位置 2 达到 25 时 位置 3 增加 1 并重置它的值 自
  • VSCode 上的 PlatformIO 未编译:collect2.exe:错误:ld 返回 1 退出状态

    我最近不得不擦拭我的计算机 在一切准备就绪并运行之后 是时候打开我之前正在处理的一些 ESP32 程序了 发现 VSCode 上的 Platform IO 将不再编译 运行编译器后 我收到以下错误 Compiling pio build e
  • 使用 Arduino 将serial.read()转换为可用的字符串

    我正在使用两个 Arduino 来相互发送纯文本字符串新软系列 https docs arduino cc learn built in libraries software serial and an RF http en wikiped
  • Google iot MQTT - ESP32 首次连接,仅在 30m 后重新连接

    我正在使用 ESP32 与谷歌物联网云合作 我发送假值只是为了使用 MQTT 数据 PUB SUB 进行测试 显然我成功发布了值 有时 我无法重新连接到谷歌物联网 我不知道为什么它不断检查 wifi publishing 并且不检查 JWT
  • 如何在观察者中处理具有不同状态值类型的 Observables

    首先是上下文和问题 框架代码在帖子底部 我们正在创建并实现一个 C 框架 以便在 Arduino 等环境中使用 为此 我想使用观察者模式 其中任何对传感器状态变化感兴趣的组件 Observables 可以注册自己 并且它将通过 Observ
  • 如何在Arduino上将int转换为字符串?

    如何转换 int n 到一个字符串 以便当我通过串行发送它时 它作为字符串发送 这是我到目前为止所拥有的 int ledPin 13 int testerPin 8 int n 1 char buf 10 void setup pinMod
  • 批处理文件:从串口接收数据并将其写入txt文件

    我正在尝试通过 USB 串行端口从 Arduino 提取一些数据并将其写入 txt 文件 因此 我使用批处理文件 Windows 7 普通cmd 它发送例如 d 从 Arduino 请求所需的数据 收到 d 后 Arduino 开始向 PC
  • ARDUINO 常数有什么用?以前是22,现在是100

    我正在使用热电偶并下载了 MAX6675 库 我想知道以下几行中 ARDUINO 常量的值是做什么用的 if ARDUINO gt 100 lcd write byte 0 else lcd print 0 BYTE endif lcd p
  • DCF77 解码器与噪声信号

    我几乎完成了我的开源 DCF77 解码器项目 当我注意到标准 Arduino DCF77 库在噪声信号上表现非常差时 这一切就开始了 特别是当天线靠近计算机或洗衣机正在运行时 我永远无法从解码器中获取时间 我的第一个方法是向输入信号添加 数
  • Java RXTX 库不加载本机库

    基本上我想使用 Arduino 编写一个简单的通信工具java 的 RXTX 库 http rxtx qbang org wiki index php Main Page并且在加载动态库时严重失败 我的系统规格 操作系统 OS X 优胜美地
  • Android BLE 扫描与 UUID 过滤器列表混淆

    我正在 Android 移动设备 A 上执行 UUID 过滤扫描 并且有两个测试用例 一个人在另一台 Android 设备 移动设备 b 上使用名为 BLE Scanner 的应用程序来创建具有两个测试服务 每个服务都有自己唯一的 UUID
  • C-如何使用PROGMEM存储和读取char数组

    我有三个字符数组 我不希望 Arduino 将它们存储在SRAM http en wikipedia org wiki Static random access memory 所以我想使用PROGMEM来存储和读入flash http en
  • pySerial 与 python 2.7 和 3.4 的差异

    我正在开发一个项目 需要通过串口将一些数字从 Windows 10 中的 python 发送到 arduino uno 作为一个简单的测试 我只想通过发送 2 来打开 LED 并通过从命令提示符发送 4 来关闭 LED 尽管我希望最终能够将
  • 如何提高 PySerial 读取速度

    我目前正在构建一台使用 Arduino Mega2560 作为主控制器的机器 Arduino 通过串口连接 获取命令 执行它并每 1ms 吐出一堆测量数据 我有一个运行 Python 的 Raspberry Pi 为用户提供一个漂亮的 GU
  • 通过iPhone音频插孔读取数据

    我正在寻找一种方法来读取通过 iPhone 的音频插孔从 Arduino 发送的原始数据 我搜索了很多并发现this https github com sanjibahmad Is Headphone Plugged In tree mas
  • 编译器:如果条件始终为真/假怎么办

    我想到了条件和编译器 我正在为 Arduino 编写一个应用程序 因此我需要该应用程序尽可能快 在我的代码中我有这个 define DEBUG false if DEBUG String pinName pinName Pin pinNam
  • Arduino串口数据解析

    我正在编写一个应用程序 通过蓝牙用我的 Android 手机控制我的机器人 一切都很顺利 数据得到回显和验证 但我在协议方面遇到了一些问题 特别是我希望我的机器人的轮子在我发送时转动一个命令 例如s 10 100 or s 30 10 数值
  • 在链接时合并全局数组/从多个编译单元填充全局数组

    我想定义一系列的东西 比如事件处理程序 的内容 该数组在编译时是完全已知的 但定义在 多个编译单元 分布在多个库中 至少在最终 静态 链接之前是相当解耦的 我想要 也保持这种方式 因此添加或删除编译单元将 还可以自动管理事件处理程序 而无需
  • 如何在 Arduino 中将 char 变量作为数字打印到串行端口?

    我正在使用 Arduino Uno 我正在从 EEPROM 读取一个字节值并将该值存储在类型变量中char 1 字节 我想将变量的值作为数字 而不是相应的 ASCII 代码 打印到串行监视器 例如考虑char val 5 我想查看串行监视器

随机推荐

  • 【随笔】年轻人的存款多少取决于个人或家庭的消费观

    近日 有调查称 大概五分之一的年轻人存款在一万元以内 10万元存款是一个 坎 存款超过10万就会超过53 7 的人 年轻人 存款 两个词碰撞在一起 引来了广泛的关注和讨论 你认为年轻人存款难吗 可以从以下几个角度发表你的看法 目录 一 灵魂
  • 脑机接口BCI技术概述

    脑机接口BCI技术概述 前言 一 脑机接口BCI是什么 二 BCI的框架 1 信号采集 2 信号处理 2 1 预处理 2 2 特征提取 2 3 模式分类 3 BCI应用 三 脑控系统中常用的BCI范式 1 基于感觉运动节律的BCI 2 基于
  • v-text的用法

    v text指令 相当于原生js中的innerText 用于将数据填充到标签中 作用于插值表达式类似 但是没有闪动问题 如果数据中有HTML标签会将html标签一并输出 注意 此处为单向绑定 数据对象上的值改变 插值会发生变化 但是当插值发
  • 安卓智能手机开发,打地鼠实例(登录页面+游戏界面+课程设计)

    主要为大家详细介绍了Android实现打地鼠小游戏 文中示例代码介绍的非常详细 具有一定的参考价值 感兴趣的小伙伴们可以参考一下 文件 url80 ctfile com f 25127180 743379579 b297fb p 55168
  • JavaSe学习日记

    前言 How to study 需求 工作需要 跳槽 对方需要 技术控 看看传统技术能否解决 能解决 不完美 解决不了 问清楚新技术到底有什么优异 引出我们学习的新技术和知识点 学习新技术或者知识点的基本原理和基本语法 先不要考虑细节 快速
  • Qt 5.9.6 配置MSVC 2017编译器

    一 安装Visual Studio 使用MSVC 2017的最低版本为Visual Studio 2017 高版本适用 我使用的是Visual Studio Community 2019 VS官网下载 选择安装通用Windows平台开发和使
  • GCC - structure/union前端解析说明

    以GCC8 2 0版本为例 介绍gcc语法解析器 parser 对声明即函数定义的解析过程以及structure union的简单解析说明 1 GCC中声明和定义的解析过程 1 1 解析入口 c parse file GCC中gcc c c
  • 双指针算法模板

    什么是同向双指针 什么是相向双指针 双指针的鼻祖题 两数之和 Two Sum 链表上的快慢指针算法 快速排序 归并排序 几乎所有 Two Sum 变种 Partition Quick Select 分成两个部分 分成三个部分 一些你没听过的
  • 关于keil中内存分配的问题,局部变量、全局变量,堆栈的分配

    看 map文件 从 map文件中可以看出 ram中的数据是如何摆放的 1 首先从0x20000000开始 放全局变量的数据 如上图2所示 0x20000000以及0x200000004 0x20000008的地方的数据 存放的是全局变量 分
  • 基于STM32F407的摄像头(不带FIFO的OV7670)图像采集及LCD显示实验-笔记整理

    硬件说明 STM32F4开发板 stm32f4系列的芯片处理的能力可以说非常强大了的 而且内部还自带有数字摄像头接口 DCMI 可以说stm32f407的核心板和不带FIFO的摄像头模块OV7670简直就是绝配 我手上的这块张这个样子 图片
  • 安卓studio 配置Flutter

    1 下载Flutter SDK Flutter SDK 我的安卓studio版本是4 2 1 下载的Flutter SDK 是Dev channel Windows 2 6 0版本 2 将压缩包解压到指定目录 例如C src flutter
  • 最新如何在kali linux中安装搜狗输入法

    在安装之前我们先来更新我们的源 更新方法 leafpad etc apt sources list 在打开的文件中输入下面的源 保存退出 阿里云源 deb http mirrors aliyun com kali sana main non
  • javaFX用IDEA打包导出exe后图片不显示问题

    今天在用idea打包完成了JavaFX项目时 查到了两种方法 一种是用eclipse中的ant直接打包形成可安装的exe文件 见https code makery ch zh cn library javafx tutorial part7
  • docker-compose deploy 高可用 elasticsearch TLS

    文章目录 1 sysctl 2 swap 3 hosts 4 配置 instances yaml 5 创建证书 6 部署 7 修改 kibanna 密码 8 清理 1 sysctl root github es tls cat etc sy
  • 用canvas绘制的饼状图,颜色随机,动态生成

    想想自己平时太懒了 像jQuery一样 write less do more 但是 我决定改变了 write more do more 这里贴上我用canvas绘制的饼状图 均分6等分 颜色随机
  • 点击table表格单元格修改数据同时更新数据库

    XML HTML Code
  • matlab读取excel数据并画图

    Matlab可以使用函数 xlsread 来读取Excel数据 语法 num txt raw xlsread filename 其中 filename 是Excel文件的名称 num 是读取的数字数据 txt 是读取的文本数据 raw 是读
  • word2016页码设置(包括目录去除页码)

    word2016页码设置 包括目录去除页码 把你鼠标的光标 移到正文 最开始 点击布局 选择 分隔符 里面的 下一页 开始插入页码 然后设置页码格式 点击链接到前一节 手动删除你目录上的页码 操作流程 1 把你鼠标的光标 移到正文 最开始
  • stm32单片机控制28BYJ48步进电机原理及代码

    命名方式 以28BYJ48步进电机为例 28BYJ48步进电机的编号 28 步进电机的有效最大外径是28毫米 B 表示步进电机 Y 表示永磁式 J 表示减速型 48 表示四相八拍 工作原理 1 五线四相步进电机 不同相位得电会让步进电机的转
  • ESP32-S2应用开发——USB通信(CDC类)

    ESP32S2应用开发 USB通信 CDC类 目录 ESP32S2应用开发 USB通信 CDC类 前言 1 硬件介绍 1 1 硬件连接 2 软件开发 2 1 安装开发板 2 2 安装库 2 3 运行示例代码 2 4 USB传输速度测试 结束