Skip to content

一、如何启用UART并建立通信连接

1.1 UART建立通信连接流程

https://documentation.espressif.com/projects/esp-idf/zh_CN/latest/esp32s3/api-reference/peripherals/uart.html

如何使用 UART 驱动程序的函数和数据类型在 ESP32-S3 和其他 UART 设备之间建立通信。基本编程流程分为以下几个步骤:

  1. 安装驱动程序 - 为 UART 驱动程序分配 ESP32-S3 资源
  2. 设置通信参数 - 设置波特率、数据位、停止位等
  3. 设置通信管脚 - 分配连接设备的管脚
  4. 运行 UART 通信 - 发送/接收数据
  5. 使用中断 - 触发特定通信事件的中断
  6. 删除驱动程序 - 如无需 UART 通信,则释放已分配的资源

步骤 1 到 3 为配置阶段,步骤 4 为 UART 运行阶段,步骤 5 和 6 为可选步骤。

UART 驱动程序函数通过 uart_port_t 识别不同的 UART 控制器。调用以下所有函数均需此标识。

1.2 配置则按以下步骤安装驱动、配置uart和绑定引脚

步骤函数作用类比
1️⃣ 安装驱动uart_driver_install()分配内存、初始化 FIFO、注册中断、创建缓冲区“给 UART 装操作系统、分配硬件资源”
2️⃣ 配置参数uart_param_config()设置波特率、数据位、时钟源等通信规则“结构体传参,设置通信语言(如普通话/英语)”
3️⃣ 绑定引脚uart_set_pin()将 UART 的 TX/RX 映射到具体 GPIO“插上网线/接上麦克风”

CAUTION

顺序不能乱:先装驱动 → 再设规则 → 最后接线

ESP32-S3 电源域

ESP32系统时钟

ESP32 UART基本结构图

1.2.1 第一步:uart_driver_install() —— 安装驱动(激活硬件 + 分配资源)

c
esp_err_t uart_driver_install(
    uart_port_t uart_num,           // UART 端口号(0, 1, 2)
    int rx_buffer_size,             // RX 环形缓冲区大小(字节)
    int tx_buffer_size,             // TX 环形缓冲区大小(字节)
    int queue_size,                 // 事件队列深度
    QueueHandle_t* uart_queue,      // 事件队列句柄(输出参数)
    int intr_alloc_flags            // 中断分配标志
);
参数详解:
参数你的值含义为什么这么设?
uart_numUART_NUM_0使用哪个 UART 控制器ESP32-S3 有 3 个 UART(0/1/2)
rx_buffer_sizeRX_BUF_SIZE * 2 = 2048分配多大的内存缓存接收数据防止突发数据丢失,越大越安全(但占 RAM)
tx_buffer_size0TX 是否用环形缓冲区0= 同步发送(uart_write_bytes会阻塞直到发完), 适合小数据
queue_size0是否使用事件队列0= 不用,你用轮询方式读取(uart_read_bytes
uart_queueNULL不需要事件队列句柄因为queue_size=0
intr_alloc_flags0中断分配选项默认即可,除非你需要高优先级中断
⚙️ 内部做了什么?
  • 使能 UART 模块的时钟(APB_CLK)
  • 复位 UART 硬件 FIFO
  • 分配 rx_buffer_size 字节的 Ring Buffer(在内部 RAM)
  • 注册 UART 中断服务程序(ISR)
  • 初始化 FreeRTOS 队列(如果 queue_size > 0

💡 关键点
这个函数必须最先调用!
因为后续的 uart_param_config()uart_set_pin() 都要操作硬件寄存器,而硬件必须先被“激活”。

1.2.2 第二步:uart_param_config() —— 配置通信规则(UART通信参数配置)

1.2.2.1 启用UART前,也就是uart_param_config()传参前必须先配置结构体uart_config_t

esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config)

c
 uart_config_t my_uart_config = {0};
    my_uart_config.baud_rate = 115200;
    my_uart_config.data_bits = UART_DATA_8_BITS;
    my_uart_config.stop_bits = UART_STOP_BITS_1;
    my_uart_config.parity = UART_PARITY_DISABLE;
    my_uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
    my_uart_config.source_clk = UART_SCLK_DEFAULT;  
    // 或者UART_SCLK_APB都可以,因为ESP-IDF中UART的时钟源默认就是APB_CLK时钟源
    // 因为有且只有UART_SCLK_APB\UART_SCLK_RTC\UART_SCLK_XTAL这三个选项可以选,选default就是选APB_CLK

    // .flags 通常不需要配置,绝大多数应用(包括你的 UART 回显、调试通信等)都可以完全忽略它。
    // 示例项目不配置它,是因为它的功能属于 高级电源管理场景,普通用户用不到。
c
esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t* config);
作用:

将你定义的 uart_config_t 结构体写入 UART 硬件寄存器。

内部关键操作:
  1. 选择时钟源(通过 UART_SCLK_SEL 寄存器)
    • 你选了 UART_SCLK_DEFAULT → 实际是 APB_CLK = 80MHz
  2. 计算波特率分频系数
    • 公式:div = APB_CLK / baud_rate
    • 例如:80,000,000 / 115200 ≈ 694.44 → 写入 UART_CLKDIV_REG
  3. 配置帧格式
    • 数据位(8 bits)→ UART_BIT_NUM
    • 停止位(1 bit)→ UART_STOP_BIT_NUM
    • 奇偶校验(无)→ UART_PARITY_EN = 0

为什么在这一步做?
因为硬件必须先被 uart_driver_install() 激活,才能写寄存器!

1.2.3 第三步:uart_set_pin() —— 绑定物理引脚(连接物理线路)

c
esp_err_t uart_set_pin(
    uart_port_t uart_num,
    int tx_io_num,      // TX 引脚(如 GPIO43)
    int rx_io_num,      // RX 引脚(如 GPIO44)
    int rts_io_num,     // RTS 引脚(硬件流控,不用则 UART_PIN_NO_CHANGE)
    int cts_io_num      // CTS 引脚(硬件流控,不用则 UART_PIN_NO_CHANGE)
);
作用:
  • 调用 GPIO 矩阵(GPIO Matrix) 功能
  • 将 UART 内部信号(如 U0TXD)路由到指定 GPIO
  • 配置 GPIO 为 复用功能模式(Alternate Function)
为什么需要这一步?
  • ESP32-S3 的 UART 信号 不固定在某个 GPIO
  • 你可以把 UART0 TX 放在 任意 GPIO(只要支持)
  • 这是通过 I/O MUX 和 GPIO Matrix 实现的

示例
uart_set_pin(UART_NUM_0, 43, 44, ...)
→ 把 UART0 的 TX 信号连到 GPIO43,RX 连到 GPIO44

1.2.4 三个函数的核心作用

函数核心任务必须?类比
uart_driver_install()激活硬件 + 分配内存 + 注册中断✅ 必须第一个调用“启动引擎”
uart_param_config()设置波特率、数据格式、时钟源✅ 必须“设定档位和油门”
uart_set_pin()将 UART 信号连接到物理 GPIO✅ 必须“挂上变速箱”

二、借鉴ESP-IDF乐鑫开发示例程序

三、ESP32 UART如何接收数据(uart_read_bytes)?

int uart_read_bytes(uart_port_t uart_num, void *buf, uint32_t length, uint32_t ticks_to_wait)

UART read bytes from UART buffer.

参数:

  • uart_num -- UART port number, the max port number is (UART_NUM_MAX -1).
  • buf-- pointer to the buffer.
  • length -- data length
  • ticks_to_wait -- sTimeout, count in RTOS ticks

返回:

  • (-1) Error
  • OTHERS (>=0) The number of bytes read from UART buffer

3.1 官方示例代码

plain
static void rx_task(void *arg)
{
    static const char *RX_TASK_TAG = "RX_TASK";
    esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
    uint8_t* data = (uint8_t*) malloc(RX_BUF_SIZE + 1);
    while (1) {
        const int rxBytes = uart_read_bytes(UART_NUM_1, data, RX_BUF_SIZE, 1000 / portTICK_PERIOD_MS);
        if (rxBytes > 0) {
            data[rxBytes] = 0;
            ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, data);
            ESP_LOG_BUFFER_HEXDUMP(RX_TASK_TAG, data, rxBytes, ESP_LOG_INFO);
        }
    }
    free(data);
}

3.2 官方技术开发文档参考代码

plain
// Read data from UART.
const uart_port_t uart_num = UART_NUM_2;
uint8_t data[128];
int length = 0;
ESP_ERROR_CHECK(uart_get_buffered_data_len(uart_num, (size_t*)&length));
length = uart_read_bytes(uart_num, data, length, 100);

觉醒,然后燎原。 © 2026 门主引擎