硬件串口的使用
硬件资源

我使用的具体的模组型号为 ESP32-S3-WROOM-1(U), 根据官方手册其有3个串口。
- UART0:通常用于下载和输出调试信息串口,信号管脚默认与 GPIO43(TX) ~ GPIO44(RX) 复用,可以通过 GPIO 交换矩阵连接到任意 GPIO.
- UART1:信号管脚默认与 GPIO17(TX) ~ GPIO18(RX) 复用,可以通过 GPIO 交换矩阵连接到任意 GPIO.
- UART2:可以为任意 GPIO,通过 GPIO 交换矩阵配置。
UART0 的使用
UART0 是默认串口,使用方法是与 Arduino 官方 Board 兼容的。
1、参考文章:Serial | Arduino Documentation
2、简单示例。作用是当串口接收到数据后,通过串口发送回接收到的数据。
void setup()
{Serial.begin(115200);
}void loop()
{if (Serial.available() > 0){String receivedData = Serial.readStringUntil('\n');Serial.printf("Received: %s \r\n", receivedData);}
}
UART1 和 UART2 的使用
UART1 和 UART2 同样是 ESP32 的硬件串口,可以参考 UART0 的方法使用。但存在的问题是这两个串口默认的引脚配置在 Arduino 和 ESP32 的数据手册中描述的不同,因此在使用这两个串口时通常需要手动指定引脚。
1、参考文章:
- Arduino Nano ESP32 User Manual | Arduino Documentation
- Serial.begin() | Arduino Documentation
- arduino-esp32/cores/esp32/HardwareSerial.cpp
2、串口引脚的设置可以在串口初始化时指定。
Serial1.begin(9600, SERIAL_8N1, RX1PIN, TX1PIN);
Serial2.begin(9600, SERIAL_8N1, RX2PIN, TX2PIN);
- Parm 1:波特率;
- Parm 2:串口配置,默认为 SERIAL_8N1;
- Parm 3:串口 RX 引脚;
- Parm 4:串口 TX 引脚;
2、简单示例。作用是当串口接收到数据后,通过串口发送回接收到的数据。
// 示例代码使用 UART1,UART2 使用方法与 UART1 完全一致所以不再赘述。#define RX1PIN 17
#define TX1PIN 18void setup()
{Serial1.begin(115200, SERIAL_8N1, RX1PIN, TX1PIN);
}void loop()
{if (Serial1.available() > 0){String receivedData = Serial1.readStringUntil('\n');Serial1.printf("Received: %s \r\n", receivedData);}
}
软件模拟串口的使用
如果硬件串口不够的话也可以使用软件模拟串口,有一个现成的第三方库可用 —— EspSoftwareSerial, 关于改库的使用方法一定程度上也可以参考 Arduino 官方的 SoftwareSerial Library
1、参考文章:
- plerup/espsoftwareserial: Implementation of the Arduino software serial for ESP8266
- SoftwareSerial Library | Arduino Documentation
- Software Serial for ESP8266 | Circuits4you.com
2、安装并导入库。
- 在 Arduino IDE 的库管理中搜索
EspSoftwareSerial
并安装; - 接在项目代码中
#include <SoftwareSerial.h>
,或者依次点击 Arduino IDE 菜单栏中的项目
>导入库
>点击 EspSoftwareSerial
导入库。
3、简单示例,实现软串口与硬件串口 (Serial) 之间的双向数据转发。
#include <SoftwareSerial.h>#define MYPORT_RX 15
#define MYPORT_TX 16EspSoftwareSerial::UART myPort;void setup()
{pinMode(MYPORT_RX, INPUT);pinMode(MYPORT_TX, OUTPUT);Serial.begin(115200);// 初始化软件串口,baud,config,rxPin,txPin,invertmyPort.begin(19200, SWSERIAL_8N1, MYPORT_RX, MYPORT_TX, false);
}void loop()
{// 如果软件串口有数据可读,则转发到硬件串口if (myPort.available() > 0){Serial.write(myPort.read());}// 如果硬件串口有数据可读,则转发到软件串口if (Serial.available() > 0){myPort.write(Serial.read());}
}
4、🟡 使用软串口的注意事项:
- 软串口的波特率不宜过高,否则错误率会很高,官方例程中常用的
19200
和9600
就是不错的选择。 - 软串口是半双工的,所以无法实现前文中硬串口同时自发自收的效果。
- 软串口支持的 Functions 有限,比如硬串口中的
readBytesUntil()
在这里就不被支持。
USB虚拟串口的使用
是的,一般情况下开发板上都会有一个 USB 转串口芯片来连接 ESP32 的硬件串口,但如果引脚资源特别吃紧的话,也是可以直接用 ESP32 的 USB 外设来虚拟串口的,同样也支持程序烧录,Arduino 官方推出的开发板 Arduino Nano ESP32 就是这么干的。
ESP32S3 的 USB 支持两种模式,分别是 USB-Serial-JTAG
和 USB-OTG
, 这两种模式都支持作为虚拟串口使用,具体情况可参考官方手册。

USB-Serial-JTAG 模式
这种模式使用简单,功能固定。此模式的功能完全由硬件实现,也只能执行串口和 JTAG 调试功能。
1、参考文章:
- Arduino Nano ESP32 User Manual | Arduino Documentation
- ESP32S3的三种下载方式UART0、Hardware CDC、USB-OTG CDC(TinyUSB)的设置和区别_esp32s3下载模式-CSDN博客
- 请问USB虚拟串口传输速率最高能到多少呢? - 恩智浦技术社区
2、开发板配置中将:
USB CDC On Boot
配置为Enable
.USB Mode
配置为Hardware CDC and JTAG
.
3、🟡 USB 虚拟串口的注意事项。
- USB-Serial-JTAG 模式下的虚拟串口的名字顶替掉了原硬件串口 UART0 的
Serial
, 在这种模式下使用 UART0 应使用Serial0
. - USB 虚拟串口没有的概念,在串口调试助手中波特率任意设置都是可以通信的。
4、简单示例,实现 USB-Serial 与硬件串口 UART0 之间的双向数据转发。
void setup()
{Serial.begin(115200);Serial0.begin(115200);
}void loop()
{// 如果USB虚拟串口有数据,则转发到硬件串口UART0if (Serial.available() > 0){Serial0.write(Serial.read());}// 如果硬件串口UART0有数据,则转发到USB虚拟串口if (Serial0.available() > 0){Serial.write(Serial0.read());}
}
5、🟡 关于使用 USB 虚拟串口进行程序烧录,我遇到的问题是可以成功烧录并运行,但是到最后总会报一个错误出来,判断原因是烧录成功重启后与烧录工具的通信有问题,不过无伤大雅我就没细究了。

USB-OTG 模式
配置一
USB Mode | USB CDC On Boot |
---|---|
USB-OTG (TinyUSB) | Enable |
此时虚拟串口的使用方式与 USB-Serial-JTAG 模式时完全一致,但其他地方略有差异:
- 无法使用该虚拟串口下载程序,可能是因为 USB-OTG 模式下 esptool 无法通过 USB 控制协议自动将设备切换到下载模式。
- 无法使用内置 JTAG 进行调试(这是自然)。
配置二
USB Mode | USB CDC On Boot |
---|---|
USB-OTG (TinyUSB) | Disable |
诚然,关闭 USB CDC On Boot
后也可以自己用代码实现一个虚拟串口。
1、参考文章:
- Arduino ESP32 USB CDC功能使用介绍_usb cdc on boot-CSDN博客
2、简单示例,实现 USB-Serial 与硬件串口 UART0 之间的双向数据转发。
#include "USB.h"USBCDC USBSerial;void setup()
{Serial.begin(115200);USBSerial.begin(115200);USB.begin();
}void loop()
{// 如果USB虚拟串口有数据,则转发到硬件串口UART0if (USBSerial.available() > 0){Serial.write(USBSerial.read());}// 如果硬件串口UART0有数据,则转发到USB虚拟串口if (Serial.available() > 0){USBSerial.write(Serial.read());}
}
注意/备注
1、USB 虚拟串口能否正常使用与 USB Mode
和 USB CDC On Boot
两项配置息息相关,配置不正确的话即使编译通过实际后果也不堪设想。实际代码中建议判断 ARDUINO_USB_MODE
和 ARDUINO_USB_CDC_ON_BOOT
这两个宏的状态来决定程序的设计。
总结
使用 Arduino IDE 开发 ESP32 的感受
这是第一次用 Arduino 开发 ESP32, 虽说 Arduino 也是乐鑫官方推荐的开发工具,但实际用起来还是有很多坑的。
比如硬件串口引脚分配的问题,UART1 在 ESP32S3 模组的规格书和其他资料上都说了默认是 GPIO17(TX) 和 GPIO18(RX), 但不知道为什么到了 Arduino 上就变成了 GPIO16(TX) 和 GPIO15(RX), 明明这 Arduino 的支持也是乐鑫自己写的。


还有就是串口引脚的重定向问题,Arduino 官方关于 Serial 的 begin()
这个函数的参数只有两个 (speed, config), 是没有 .begin(115200, SERIAL_8N1, RX1PIN, TX1PIN)
这种用法的。出现这种用法的比较权威的地方就是乐鑫的 Arduino 支持包代码和 Arduino Nano ESP32 的用户手册中。
所以使用 Arduino IDE 开发 ESP32 时除了要看 Arduino 官方的资料一定还要看乐鑫的支持包的资料。
因为当在 Arduino IDE 选择的开发板是使用对应支持包下面的文件的,举个例子即使是 Arduino.h
这个文件其实际指向的也是对应开发板支持包中的如 ...packages/esp32/.../Arduino.h
, 既然支持包是第三方提供的,那资料自然也不能只看 Arduino 官方的,虽说第三方一定会最大程度上兼容官方,但细微之处难免会有差别。