文档类型:ESP-IDF 通用 API 教程,不绑定单一开发板。
板级例程见 热门例程详解。
目录
- 简介
- UART 基础概念
- ESP32-S3 的 UART 资源
- ESP-IDF 中的 UART 配置
- 基于读取 Buffer 长度的 UART 接收
- 基于事件驱动的 UART 接收
- 实用示例
- 常见问题与解决方案
- 高级主题
- 总结
- 参考资料
简介
UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)是一种常用的串行通信协议,广泛应用于微控制器与各种外设、传感器或其他微控制器之间的通信。ESP32-S3 提供了多个 UART 控制器,可以同时与多个设备进行串口通信。
本教程将详细介绍如何在 ESP-IDF 框架下使用 UART 功能,包括初始化配置、数据收发以及两种不同的数据接收方式:基于读取 Buffer 长度和基于事件驱动。
UART 基础概念
1. UART 通信原理
UART 是一种异步串行通信协议,通常包含以下两条信号线:
- TX(Transmit):发送数据的信号线
- RX(Receive):接收数据的信号线
此外,有些 UART 还支持硬件流控制,增加以下信号线:
- RTS(Request to Send):请求发送信号
- CTS(Clear to Send):清除发送信号
2. UART 通信参数
UART 通信需要设置以下参数:
- 波特率(Baud Rate):数据传输速率,常用值有 9600、115200 等
- 数据位(Data Bits):每个字符的数据位数,通常为 8 位
- 停止位(Stop Bits):表示一个字符结束的位数,通常为 1 位
- 奇偶校验(Parity):用于错误检测,可以是无校验、奇校验或偶校验
- 流控制(Flow Control):控制数据流,可以是无流控制、硬件流控制或软件流控制
3. UART 通信特点
- 异步通信:发送方和接收方不共享时钟信号
- 全双工通信:可以同时发送和接收数据
- 简单易用:配置简单,硬件要求低
- 适中的传输速率:通常低于 SPI 和 I2C,但足够大多数应用场景
ESP32-S3 的 UART 资源
ESP32-S3 提供了 3 个 UART 控制器:
- UART0:通常用于调试和日志输出,连接到 USB-UART 桥接器
- UART1:可供用户使用的通用 UART 控制器
- UART2:可供用户使用的通用 UART 控制器
每个 UART 控制器都支持以下功能:
- 标准异步通信
- RS485 半双工通信
- 硬件流控制(RTS/CTS)
- 自动波特率检测
- 多种中断触发条件
ESP-IDF 中的 UART 配置
1. 包含必要的头文件
#include "driver/uart.h"
#include "driver/gpio.h"
#include "esp_log.h"
2. UART 配置步骤
在 ESP-IDF 中配置 UART 通常遵循以下步骤:
- 配置 UART 参数
- 安装 UART 驱动
- 设置 UART 引脚
- 配置 UART 接收缓冲区
- 发送/接收数据
3. UART 基本配置示例
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "sdkconfig.h"
static const char *TAG = "uart_example";
// UART 端口号
#define UART_NUM UART_NUM_1
// UART 引脚
#define UART_TXD_PIN 17
#define UART_RXD_PIN 18
#define UART_RTS_PIN UART_PIN_NO_CHANGE
#define UART_CTS_PIN UART_PIN_NO_CHANGE
// UART 通信参数
#define UART_BAUD_RATE 115200
// UART 缓冲区大小
#define BUF_SIZE (1024)
void app_main(void)
{
// 配置 UART 参数
uart_config_t uart_config = {
.baud_rate = UART_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
// 安装 UART 驱动
ESP_ERROR_CHECK(uart_driver_install(UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 0, NULL, 0));
// 配置 UART 参数
ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config));
// 设置 UART 引脚
ESP_ERROR_CHECK(uart_set_pin(UART_NUM, UART_TXD_PIN, UART_RXD_PIN, UART_RTS_PIN, UART_CTS_PIN));
ESP_LOGI(TAG, "UART 初始化完成");
// 发送欢迎消息
const char *welcome_msg = "欢迎使用 ESP32-S3 UART 示例!\r\n";
uart_write_bytes(UART_NUM, welcome_msg, strlen(welcome_msg));
// 主循环
while (1) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
基于读取 Buffer 长度的 UART 接收
这种方式通过轮询或定时检查 UART 接收缓冲区中的数据长度,然后读取数据。这种方法简单直观,适合对实时性要求不高的应用。
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "sdkconfig.h"
static const char *TAG = "uart_buffer_example";
// UART 端口号
#define UART_NUM UART_NUM_1
// UART 引脚
#define UART_TXD_PIN 17
#define UART_RXD_PIN 18
// UART 通信参数
#define UART_BAUD_RATE 115200
// UART 缓冲区大小
#define BUF_SIZE (1024)
// UART 接收任务
void uart_rx_task(void *arg)
{
uint8_t *data = (uint8_t *) malloc(BUF_SIZE);
while (1) {
// 获取 UART 接收缓冲区中的数据长度
int len = 0;
ESP_ERROR_CHECK(uart_get_buffered_data_len(UART_NUM, (size_t*)&len));
if (len > 0) {
// 读取数据
int read_len = uart_read_bytes(UART_NUM, data, len, 100 / portTICK_PERIOD_MS);
if (read_len > 0) {
// 确保数据以 null 结尾
data[read_len] = '\0';
ESP_LOGI(TAG, "接收到 %d 字节数据: %s", read_len, data);
// 回显数据
char reply[64];
sprintf(reply, "回显: %s\r\n", data);
uart_write_bytes(UART_NUM, reply, strlen(reply));
}
}
// 短暂延时,避免过于频繁地检查
vTaskDelay(pdMS_TO_TICKS(50));
}
free(data);
}
void app_main(void)
{
// 配置 UART 参数
uart_config_t uart_config = {
.baud_rate = UART_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
// 安装 UART 驱动
ESP_ERROR_CHECK(uart_driver_install(UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 0, NULL, 0));
// 配置 UART 参数
ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config));
// 设置 UART 引脚
ESP_ERROR_CHECK(uart_set_pin(UART_NUM, UART_TXD_PIN, UART_RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
ESP_LOGI(TAG, "UART 初始化完成");
// 发送欢迎消息
const char *welcome_msg = "欢迎使用 ESP32-S3 UART Buffer 读取示例!\r\n";
uart_write_bytes(UART_NUM, welcome_msg, strlen(welcome_msg));
// 创建 UART 接收任务
xTaskCreate(uart_rx_task, "uart_rx_task", 4096, NULL, 5, NULL);
}
基于 Buffer 长度接收的特点
-
优点:
- 实现简单,容易理解
- 不需要额外的中断处理
- 适合处理不定长数据
-
缺点:
- 需要定期轮询,占用 CPU 资源
- 实时性可能不如事件驱动方式
- 可能会错过短时间内的多次数据到达
基于事件驱动的 UART 接收
这种方式通过配置 UART 事件队列,当特定事件(如数据到达、接收超时等)发生时,触发回调或向队列发送事件。这种方法响应更及时,CPU 利用率更高。
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "sdkconfig.h"
static const char *TAG = "uart_event_example";
// UART 端口号
#define UART_NUM UART_NUM_1
// UART 引脚
#define UART_TXD_PIN 17
#define UART_RXD_PIN 18
// UART 通信参数
#define UART_BAUD_RATE 115200
// UART 缓冲区大小
#define BUF_SIZE (1024)
// UART 事件处理任务
static void uart_event_task(void *pvParameters)
{
uart_event_t event;
uint8_t *dtmp = (uint8_t *) malloc(BUF_SIZE);
QueueHandle_t uart_queue = (QueueHandle_t) pvParameters;
while (1) {
// 等待 UART 事件
if (xQueueReceive(uart_queue, (void *)&event, portMAX_DELAY)) {
bzero(dtmp, BUF_SIZE);
switch (event.type) {
// 数据可用事件
case UART_DATA:
ESP_LOGI(TAG, "UART 数据事件,长度: %d", event.size);
// 读取数据
uart_read_bytes(UART_NUM, dtmp, event.size, portMAX_DELAY);
// 确保数据以 null 结尾
dtmp[event.size] = '\0';
ESP_LOGI(TAG, "接收到数据: %s", dtmp);
// 回显数据
char reply[64];
sprintf(reply, "回显: %s\r\n", dtmp);
uart_write_bytes(UART_NUM, reply, strlen(reply));
break;
// 接收缓冲区满事件
case UART_FIFO_OVF:
ESP_LOGW(TAG, "UART 接收缓冲区溢出");
uart_flush_input(UART_NUM);
xQueueReset(uart_queue);
break;
// 接收缓冲区超时事件
case UART_BUFFER_FULL:
ESP_LOGW(TAG, "UART 接收缓冲区已满");
uart_flush_input(UART_NUM);
xQueueReset(uart_queue);
break;
// 接收超时事件
case UART_BREAK:
ESP_LOGI(TAG, "UART 接收中断");
break;
// 奇偶校验错误事件
case UART_PARITY_ERR:
ESP_LOGW(TAG, "UART 奇偶校验错误");
break;
// 帧错误事件
case UART_FRAME_ERR:
ESP_LOGW(TAG, "UART 帧错误");
break;
// 其他事件
default:
ESP_LOGI(TAG, "UART 事件类型: %d", event.type);
break;
}
}
}
free(dtmp);
dtmp = NULL;
vTaskDelete(NULL);
}
void app_main(void)
{
// 配置 UART 参数
uart_config_t uart_config = {
.baud_rate = UART_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
.rx_flow_ctrl_thresh = 122, // 接收流控阈值
};
// 创建事件队列
QueueHandle_t uart_queue;
// 安装 UART 驱动,并创建事件队列
ESP_ERROR_CHECK(uart_driver_install(UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart_queue, 0));
// 配置 UART 参数
ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config));
// 设置 UART 引脚
ESP_ERROR_CHECK(uart_set_pin(UART_NUM, UART_TXD_PIN, UART_RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
// 设置 UART 模式为正常模式
ESP_ERROR_CHECK(uart_set_mode(UART_NUM, UART_MODE_UART));
// 设置 UART 接收超时
ESP_ERROR_CHECK(uart_set_rx_timeout(UART_NUM, 10)); // 10 符号时间
ESP_LOGI(TAG, "UART 初始化完成");
// 发送欢迎消息
const char *welcome_msg = "欢迎使用 ESP32-S3 UART 事件驱动示例!\r\n";
uart_write_bytes(UART_NUM, welcome_msg, strlen(welcome_msg));
// 创建 UART 事件处理任务
xTaskCreate(uart_event_task, "uart_event_task", 4096, (void*)uart_queue, 12, NULL);
}
事件驱动接收的特点
-
优点:
- 实时性好,可以立即响应数据到达
- CPU 利用率高,不需要轮询
- 可以处理多种 UART 事件(数据到达、缓冲区满、奇偶校验错误等)
-
缺点:
- 实现相对复杂
- 需要理解事件驱动编程模型
- 可能需要更多的内存资源
实用示例
示例1:串口命令解析器
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "sdkconfig.h"
static const char *TAG = "uart_cmd_example";
// UART 端口号
#define UART_NUM UART_NUM_1
// UART 引脚
#define UART_TXD_PIN 17
#define UART_RXD_PIN 18
// UART 通信参数
#define UART_BAUD_RATE 115200
// UART 缓冲区大小
#define BUF_SIZE (1024)
// 命令结束符
#define CMD_TERMINATOR '\n'
// 命令处理函数
static void process_command(const char *cmd)
{
char response[128];
// 去除命令末尾的回车和换行符
char clean_cmd[64];
strcpy(clean_cmd, cmd);
char *p = clean_cmd;
while (*p) {
if (*p == '\r' || *p == '\n') {
*p = '\0';
break;
}
p++;
}
// 命令为空
if (strlen(clean_cmd) == 0) {
return;
}
ESP_LOGI(TAG, "处理命令: %s", clean_cmd);
// 解析命令
if (strcmp(clean_cmd, "help") == 0) {
sprintf(response, "可用命令:\r\n"
" help - 显示帮助信息\r\n"
" info - 显示系统信息\r\n"
" led on - 打开 LED\r\n"
" led off - 关闭 LED\r\n");
} else if (strcmp(clean_cmd, "info") == 0) {
sprintf(response, "系统信息:\r\n"
" 芯片: ESP32-S3\r\n"
" 固件版本: 1.0.0\r\n"
" 编译时间: %s %s\r\n", __DATE__, __TIME__);
} else if (strcmp(clean_cmd, "led on") == 0) {
// 这里可以添加控制 LED 的代码
sprintf(response, "LED 已打开\r\n");
} else if (strcmp(clean_cmd, "led off") == 0) {
// 这里可以添加控制 LED 的代码
sprintf(response, "LED 已关闭\r\n");
} else {
sprintf(response, "未知命令: %s\r\n"
"输入 'help' 获取帮助\r\n", clean_cmd);
}
// 发送响应
uart_write_bytes(UART_NUM, response, strlen(response));
}
// UART 事件处理任务
static void uart_event_task(void *pvParameters)
{
uart_event_t event;
uint8_t *dtmp = (uint8_t *) malloc(BUF_SIZE);
static char cmd_buffer[128];
static int cmd_pos = 0;
QueueHandle_t uart_queue = (QueueHandle_t) pvParameters;
while (1) {
// 等待 UART 事件
if (xQueueReceive(uart_queue, (void *)&event, portMAX_DELAY)) {
bzero(dtmp, BUF_SIZE);
switch (event.type) {
// 数据可用事件
case UART_DATA:
// 读取数据
uart_read_bytes(UART_NUM, dtmp, event.size, portMAX_DELAY);
// 处理接收到的数据
for (int i = 0; i < event.size; i++) {
char c = dtmp[i];
// 回显字符
uart_write_bytes(UART_NUM, &c, 1);
if (c == CMD_TERMINATOR) {
// 命令结束,处理命令
cmd_buffer[cmd_pos] = '\0';
process_command(cmd_buffer);
cmd_pos = 0;
// 输出新的命令提示符
uart_write_bytes(UART_NUM, "> ", 2);
} else if (c == '\b' || c == 127) {
// 退格键,删除上一个字符
if (cmd_pos > 0) {
cmd_pos--;
// 发送退格序列(退格、空格、再退格)
uart_write_bytes(UART_NUM, "\b \b", 3);
}
} else if (cmd_pos < sizeof(cmd_buffer) - 1) {
// 存储字符到命令缓冲区
cmd_buffer[cmd_pos++] = c;
}
}
break;
// 其他事件处理(省略,与前面示例相同)
case UART_FIFO_OVF:
case UART_BUFFER_FULL:
ESP_LOGW(TAG, "UART 缓冲区溢出");
uart_flush_input(UART_NUM);
xQueueReset(uart_queue);
break;
default:
break;
}
}
}
free(dtmp);
dtmp = NULL;
vTaskDelete(NULL);
}
void app_main(void)
{
// 配置 UART 参数
uart_config_t uart_config = {
.baud_rate = UART_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
// 创建事件队列
QueueHandle_t uart_queue;
// 安装 UART 驱动,并创建事件队列
ESP_ERROR_CHECK(uart_driver_install(UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart_queue, 0));
// 配置 UART 参数
ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config));
// 设置 UART 引脚
ESP_ERROR_CHECK(uart_set_pin(UART_NUM, UART_TXD_PIN, UART_RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
ESP_LOGI(TAG, "UART 命令解析器初始化完成");
// 发送欢迎消息
const char *welcome_msg = "欢迎使用 ESP32-S3 UART 命令解析器!\r\n"
"输入 'help' 获取可用命令列表\r\n"
"> ";
uart_write_bytes(UART_NUM, welcome_msg, strlen(welcome_msg));
// 创建 UART 事件处理任务
xTaskCreate(uart_event_task, "uart_event_task", 4096, (void*)uart_queue, 12, NULL);
}
ESP-IDF UART 串口通信教程(续)
示例2:UART 与蓝牙透传(续)
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/uart.h"
#include "esp_log.h"
#include "sdkconfig.h"
static const char *TAG = "uart_bt_example";
// UART 端口号
#define UART_NUM UART_NUM_1
// UART 引脚
#define UART_TXD_PIN 17
#define UART_RXD_PIN 18
// UART 通信参数
#define UART_BAUD_RATE 115200
// UART 缓冲区大小
#define BUF_SIZE (1024)
// 模拟蓝牙发送函数
void bt_send_data(const uint8_t *data, size_t len)
{
ESP_LOGI(TAG, "蓝牙发送数据: %.*s", len, data);
// 实际应用中,这里应该调用蓝牙发送 API
}
// 模拟蓝牙接收任务
void bt_receive_task(void *arg)
{
while (1) {
// 模拟接收到蓝牙数据
const char *bt_data = "蓝牙数据包\r\n";
// 将蓝牙数据转发到 UART
uart_write_bytes(UART_NUM, bt_data, strlen(bt_data));
// 每 5 秒模拟接收一次蓝牙数据
vTaskDelay(pdMS_TO_TICKS(5000));
}
}
// UART 接收任务
void uart_rx_task(void *arg)
{
uint8_t *data = (uint8_t *) malloc(BUF_SIZE);
while (1) {
// 获取 UART 接收缓冲区中的数据长度
int len = 0;
ESP_ERROR_CHECK(uart_get_buffered_data_len(UART_NUM, (size_t*)&len));
if (len > 0) {
// 读取数据
int read_len = uart_read_bytes(UART_NUM, data, len, 100 / portTICK_PERIOD_MS);
if (read_len > 0) {
ESP_LOGI(TAG, "UART 接收到 %d 字节数据", read_len);
// 将 UART 数据转发到蓝牙
bt_send_data(data, read_len);
}
}
// 短暂延时
vTaskDelay(pdMS_TO_TICKS(50));
}
free(data);
}
void app_main(void)
{
// 配置 UART 参数
uart_config_t uart_config = {
.baud_rate = UART_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
// 安装 UART 驱动
ESP_ERROR_CHECK(uart_driver_install(UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 0, NULL, 0));
// 配置 UART 参数
ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config));
// 设置 UART 引脚
ESP_ERROR_CHECK(uart_set_pin(UART_NUM, UART_TXD_PIN, UART_RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
ESP_LOGI(TAG, "UART 蓝牙透传初始化完成");
// 发送欢迎消息
const char *welcome_msg = "欢迎使用 ESP32-S3 UART 蓝牙透传示例!\r\n";
uart_write_bytes(UART_NUM, welcome_msg, strlen(welcome_msg));
// 创建 UART 接收任务
xTaskCreate(uart_rx_task, "uart_rx_task", 4096, NULL, 5, NULL);
// 创建蓝牙接收任务
xTaskCreate(bt_receive_task, "bt_receive_task", 4096, NULL, 5, NULL);
}
常见问题与解决方案
1. UART 通信失败
如果 UART 通信失败,可能的原因和解决方法:
-
硬件连接问题:
- 检查 TX 和 RX 线是否正确连接(注意:一般需要交叉连接,即一端的 TX 连接另一端的 RX)
- 确保设备共地(GND 连接)
- 检查电平转换是否正确(ESP32-S3 使用 3.3V 逻辑电平)
-
波特率不匹配:
- 确保通信双方使用相同的波特率
- 尝试使用常见波特率(如 9600、115200)
-
参数配置错误:
- 检查数据位、停止位、奇偶校验等参数是否匹配
- 确认流控制设置是否一致
2. 数据丢失或乱码
如果接收到的数据不完整或出现乱码,可能的原因和解决方法:
-
缓冲区溢出:
- 增加接收缓冲区大小
- 更频繁地读取接收缓冲区
- 使用流控制机制
-
波特率误差过大:
- 使用更精确的时钟源
- 降低波特率
- 确保两端的时钟精度足够高
-
电气干扰:
- 使用屏蔽线缆
- 缩短通信距离
- 添加滤波电容
3. 高 CPU 占用率
如果 UART 处理导致 CPU 占用率过高,可能的解决方法:
- 使用事件驱动方式而非轮询方式
- 增加任务延时时间
- 降低 UART 任务优先级
- 使用 DMA 传输(ESP32-S3 支持)
4. 多 UART 端口管理
当需要同时使用多个 UART 端口时,可以采取以下策略:
- 为每个 UART 端口创建单独的任务
- 使用事件驱动方式,在一个任务中处理多个 UART 事件
- 使用资源池管理 UART 资源
高级主题
1. RS485 通信
ESP32-S3 支持 RS485 半双工通信模式,配置方法如下:
// 配置 RS485 半双工模式
uart_set_mode(UART_NUM, UART_MODE_RS485_HALF_DUPLEX);
// 设置 RTS 引脚(用于控制发送/接收切换)
uart_set_pin(UART_NUM, UART_TXD_PIN, UART_RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
2. 使用 DMA 传输
ESP32-S3 支持使用 DMA 进行 UART 数据传输,可以减少 CPU 负担:
// 安装 UART 驱动时启用 DMA
// 参数:UART 端口号、RX 缓冲区大小、TX 缓冲区大小、事件队列大小、队列句柄、DMA 通道
ESP_ERROR_CHECK(uart_driver_install(UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart_queue, ESP_INTR_FLAG_IRAM));
3. 自动波特率检测
ESP32-S3 支持自动波特率检测功能,可以自动适应不同的通信速率:
// 启用自动波特率检测
uart_set_baudrate(UART_NUM, 0);
uart_set_auto_baudrate(UART_NUM, true);
// 等待自动波特率检测完成
while (uart_get_baudrate(UART_NUM, NULL) == 0) {
vTaskDelay(pdMS_TO_TICKS(100));
}
// 获取检测到的波特率
uint32_t detected_baudrate;
uart_get_baudrate(UART_NUM, &detected_baudrate);
ESP_LOGI(TAG, "检测到的波特率: %d", detected_baudrate);
4. 使用 UART 进行固件升级
ESP32-S3 支持通过 UART 进行固件升级(串口下载),这是一种常用的开发和维护方式:
- 将 GPIO0 拉低(进入下载模式)
- 复位 ESP32-S3
- 使用 esptool.py 或其他工具通过 UART 下载固件
- 释放 GPIO0 并复位,进入正常运行模式
总结
本教程详细介绍了 ESP-IDF 中 UART 的使用方法,包括配置、基本操作以及两种不同的数据接收方式:基于读取 Buffer 长度和基于事件驱动。通过掌握这些知识,您可以轻松地在 ESP32-S3 项目中实现串口通信功能。
UART 是一种简单而可靠的通信协议,适用于连接各种外设、传感器或其他微控制器。ESP32-S3 的 UART 控制器提供了丰富的功能和灵活的配置选项,可以满足大多数应用场景的需求。
在实际应用中,应根据具体需求选择合适的 UART 接收方式:
- 对于简单应用或对实时性要求不高的场景,可以使用基于读取 Buffer 长度的方式
- 对于复杂应用或需要及时响应的场景,建议使用基于事件驱动的方式
此外,还应注意合理设置缓冲区大小、波特率等参数,以及正确处理各种异常情况,确保 UART 通信的稳定性和可靠性。