摘要
本文详细介绍ESP32-S3芯片的UART外设开发方法,涵盖UART0(默认调试串口)、UART1和UART2的配置与使用技巧,并提供完整示例代码,帮助开发者快速实现多设备串口通信。
一、ESP32-S3串口硬件资源
ESP32-S3芯片提供3个UART控制器:
控制器 | 默认引脚 | 特性 |
---|---|---|
UART0 | GPIO43(TX)、44(RX) | 通常用于USB CDC调试输出 |
UART1 | GPIO17(TX)、18(RX) | 可自由映射引脚 |
UART2 | 无默认引脚 | 完全灵活配置 |
二、开发环境准备
2.1 硬件要求
-
ESP32-S3开发板(如ESP32-S3-DevKitC-1)
-
USB数据线
-
逻辑分析仪(可选,用于调试)
2.2 软件要求
-
vsCode + PlatformIO
-
驱动:CP210x/CH340 USB转串口驱动
-
库:
HardwareSerial.h
(内置)
三、三串口配置实战
3.1 UART0 - 调试串口配置
UART0默认连接USB-OTG,用于Serial Monitor调试输出:
void setup() {
// 初始化UART0,波特率115200
Serial.begin(115200);
// 等待串口连接
while(!Serial);
Serial.println("UART0初始化完成");
}
void loop() {
Serial.println(millis()); // 打印系统运行时间
delay(1000);
}
3.2 UART1 - 可重映射串口
UART1默认使用GPIO17/18,但可以重映射:
HardwareSerial uart1(1); // 创建UART1实例
void setup() {
// 初始化UART1,自定义引脚
uart1.begin(115200, SERIAL_8N1, 6, 5); // TX=5, RX=6,注意TX和RX的顺序
// 发送测试数据
uart1.println("UART1初始化成功");
}
void loop() {
// 双向通信示例
if(uart1.available()) {
String data = uart1.readString();
Serial.print("收到UART1数据: ");
Serial.println(data);
}
}
3.3 UART2 - 全功能串口
UART2需要完全手动配置:
HardwareSerial uart2(2); // 创建UART2实例
void setup() {
// 初始化UART2,波特率9600
uart2.begin(9600, SERIAL_8N1, 8, 7); // TX=7, RX=8,注意Rx和Tx的顺序
// 设置中断回调
uart2.onReceive({
Serial.println("UART2收到数据!");
});
}
void loop() {
// 定时发送数据
static uint32_t lastSend = 0;
if(millis() - lastSend > 2000) {
uart2.printf("时间戳: %lu\n", millis());
lastSend = millis();
}
}
四、多串口协同工作示例
4.1 温湿度数据采集转发系统
#include <Arduino.h>
#include <HardwareSerial.h>
// 串口定义
HardwareSerial uart1(1);
HardwareSerial uart2(2);
// 模拟传感器读数
float readTemperature() { return random(200, 350)/10.0; }
float readHumidity() { return random(300, 800)/10.0; }
void setup() {
Serial.begin(115200);
uart1.begin(115200, SERIAL_8N1, 17, 18); // 默认引脚
uart2.begin(9600, SERIAL_8N1, 8, 9); // 自定义引脚
Serial.println("多串口系统启动");
}
void loop() {
// 每5秒采集并转发数据
static uint32_t lastTime = 0;
if(millis() - lastTime > 5000) {
float temp = readTemperature();
float hum = readHumidity();
// UART1发送JSON格式
uart1.printf("{\"temp\":%.1f,\"hum\":%.1f}\n", temp, hum);
// UART2发送CSV格式
uart2.printf("%.1f,%.1f\n", temp, hum);
// 串口监视器显示
Serial.printf("数据已发送: %.1fC, %.1f%%\n", temp, hum);
lastTime = millis();
}
}
4.2 使用freeROST实时操作系统来实现
#include <Arduino.h>
#include <HardwareSerial.h>
// UART1 引脚配置 (ESP32-S3默认: TX=17, RX=18)
#define UART1_TX_PIN 17
#define UART1_RX_PIN 18
// UART2 引脚配置 (自定义)
#define UART2_TX_PIN 8
#define UART2_RX_PIN 9
// 创建UART实例
HardwareSerial uart1(1);
HardwareSerial uart2(2);
// 数据结构
typedef struct {
float temperature;
float humidity;
uint32_t timestamp;
} SensorData;
// FreeRTOS任务句柄
TaskHandle_t uart1TaskHandle = NULL;
TaskHandle_t uart2TaskHandle = NULL;
// 模拟读取传感器数据
SensorData readSensorData() {
SensorData data;
data.temperature = random(200, 350) / 10.0f; // 20.0°C ~ 35.0°C
data.humidity = random(300, 800) / 10.0f; // 30.0% ~ 80.0%
data.timestamp = millis();
return data;
}
// 通过UART1发送数据的任务
void uart1SendTask(void *pvParameters) {
const TickType_t xDelay = pdMS_TO_TICKS(3000); // 3秒间隔
while(1) {
// 读取传感器数据
SensorData data = readSensorData();
// 准备JSON格式数据
char jsonBuffer[128];
snprintf(jsonBuffer, sizeof(jsonBuffer),
"{\"uart\":1,\"temp\":%.1f,\"hum\":%.1f,\"time\":%lu}",
data.temperature, data.humidity, data.timestamp);
// 通过UART1发送数据
uart1.println(jsonBuffer);
// 调试信息
Serial.printf("[UART1发送] %s\n", jsonBuffer);
vTaskDelay(xDelay);
}
}
// 通过UART2发送数据的任务
void uart2SendTask(void *pvParameters) {
const TickType_t xDelay = pdMS_TO_TICKS(5000); // 5秒间隔
while(1) {
// 读取传感器数据
SensorData data = readSensorData();
// 准备CSV格式数据
char csvBuffer[64];
snprintf(csvBuffer, sizeof(csvBuffer),
"%.1f,%.1f,%lu",
data.temperature, data.humidity, data.timestamp);
// 通过UART2发送数据
uart2.println(csvBuffer);
// 调试信息
Serial.printf("[UART2发送] %s\n", csvBuffer);
vTaskDelay(xDelay);
}
}
void setup() {
// 初始化USB串口(用于调试)
Serial.begin(115200);
// 初始化UART1 (JSON格式)
uart1.begin(115200, SERIAL_8N1, UART1_RX_PIN, UART1_TX_PIN);
// 初始化UART2 (CSV格式)
uart2.begin(9600, SERIAL_8N1, UART2_RX_PIN, UART2_TX_PIN);
// 等待串口初始化
while(!Serial);
Serial.println("\nESP32-S3 双UART温湿度数据发送示例");
Serial.printf("UART1配置: TX=%d, RX=%d (JSON格式, 3秒间隔)\n", UART1_TX_PIN, UART1_RX_PIN);
Serial.printf("UART2配置: TX=%d, RX=%d (CSV格式, 5秒间隔)\n", UART2_TX_PIN, UART2_RX_PIN);
// 创建发送任务
xTaskCreate(
uart1SendTask, // 任务函数
"UART1 Send Task", // 任务名称
2048, // 堆栈大小
NULL, // 参数
1, // 优先级
&uart1TaskHandle // 任务句柄
);
xTaskCreate(
uart2SendTask, // 任务函数
"UART2 Send Task", // 任务名称
2048, // 堆栈大小
NULL, // 参数
1, // 优先级
&uart2TaskHandle // 任务句柄
);
Serial.println("系统初始化完成,开始发送数据...");
}
void loop() {
// 主循环保持空闲,所有工作由任务完成
static uint32_t lastHeapPrint = 0;
if(millis() - lastHeapPrint > 10000) { // 每10秒打印一次内存状态
Serial.printf("[系统] 空闲内存: %d bytes\n", ESP.getFreeHeap());
lastHeapPrint = millis();
}
delay(1000);
}
4.3 性能优化技巧
-
使用DMA缓冲:
#include <driver/uart.h>
void setup() {
// 配置UART1使用DMA缓冲区
uart_param_config(UART_NUM_1, &uart_config);
uart_driver_install(UART_NUM_1, 2048, 2048, 0, NULL, 0);
}
-
中断优先级设置:
void setup() {
// 设置UART2中断优先级
uart_isr_free(UART_NUM_2);
uart_isr_register(UART_NUM_2, uart2_isr, NULL,
ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL3, NULL);
}
五、常见问题解答
Q1: 如何解决串口数据丢失问题?
A:
-
检查波特率是否匹配
-
增加硬件流控(RTS/CTS)
-
使用环形缓冲区和DMA
Q2: 多串口同时工作时如何避免冲突?
A:
-
为每个UART分配独立任务
-
使用互斥锁保护共享资源
-
设置不同的中断优先级
Q3: 如何自定义UART引脚?
A: 在begin()
函数中指定TX/RX引脚:
uart2.begin(115200, SERIAL_8N1, custom_tx_pin, custom_rx_pin);
六、总结
本文全面介绍了ESP32-S3的三UART开发方法,关键要点:
-
UART0默认用于调试,不建议复用
-
UART1提供灵活的引脚重映射能力
-
UART2适合连接外部设备
-
多串口协同需注意资源分配和冲突避免 进阶学习:
-
点赞 👍 收藏 ⭐ 关注 ➕ 获取更多嵌入式开发干货!