飞书文档https://x509p6c8to.feishu.cn/wiki/NqrMw6X8Si6sSqkyPbxcFRxGnid
UART全称是通用异步接收器/发送器,ESP32-S3 芯片有 3 个 UART 控制器。每个 UART 控制器可以独立配置波特率、数据位长度、位顺序、停止位位数、奇偶校验位等参数。
串口文档参考:
https://docs.espressif.com/projects/esp-idf/zh_CN/v5.4/esp32s3/api-reference/peripherals/uart.html
串口的使用分三步:
- 配置串口参数
- 设置串口管脚
- 安装驱动程序
配置串口参数
串口参数配置一般有两种方式,使用结构体一次性配置所有参数,或者使用API函数,逐个设置,可以根据具体情况灵活使用。
一次性配置有参数
调用函数 uart_param_config并向其传递 uart_config_t结构体,
uart_param_config用于配置 UART 的参数,包括波特率、数据位、校验位、停止位、流控和时钟源。
esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config);
参数说明
uart_num (uart_port_t):指定要配置的 UART 端口号。ESP32 支持多个 UART 端口,通常使用 UART_NUM_0、UART_NUM_1 或 UART_NUM_2。
uart_config (const uart_config_t *):指向 uart_config_t 结构体的指针,包含要配置的 UART 参数。
返回值
ESP_OK: 成功配置 UART 参数。
uart_config_t 结构体应包含所有必要的参数。请参考以下示例。
//配置 UART 参数,包括波特率、数据位、校验位、停止位、流控和时钟源。
const uart_config_t uart_config = {
.baud_rate = 115200,
.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 参数配置。
uart_param_config(UART_NUM_0, &uart_config);
分步依次配置每个参数
调用下表中的专用函数,能够单独配置特定参数。如需重新配置某个参数,也可使用这些函数。
配置参数 | 函数 |
波特率 | uart_set_baudrate() |
传输位 | 调用 uart_set_word_length() 设置 uart_word_length_t |
奇偶控制 | 调用 uart_parity_t 设置 uart_set_parity() |
停止位 | 调用 uart_set_stop_bits() 设置 uart_stop_bits_t |
硬件流控模式 | 调用 uart_set_hw_flow_ctrl() 设置 uart_hw_flowcontrol_t |
通信模式 | 调用 uart_set_mode() 设置 uart_mode_t |
设置串口管脚
通信参数设置完成后,可以配置其他 UART 设备连接的 GPIO 管脚。调用函数uart_set_pin,指定配置 Tx、Rx、RTS 和 CTS 信号的 GPIO 管脚编号。在ESP32S3中,串口IO可以通过软件配置到指定IO上,这使得我们在设计硬件电路时,非常灵活。
我们的板卡中,会使用GPIO43 GPIO44作为串口的TX和RX
uart_set_pin用于配置 UART 的引脚,该函数允许你指定 UART 的 TX(发送)和 RX(接收)引脚,
以及其他可选的 RTS(请求发送)和 CTS(清除发送)硬件流控引脚。
函数原型
esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num);
参数说明
uart_num (uart_port_t):
指定要配置的 UART 端口号。ESP32 支持多个 UART 端口,
通常使用 UART_NUM_0、UART_NUM_1 或 UART_NUM_2。
tx_io_num (int):
指定 TX(发送)引脚的 GPIO 编号。
rx_io_num (int):
指定 RX(接收)引脚的 GPIO 编号。
rts_io_num (int):
指定 RTS(请求发送)引脚的 GPIO 编号。如果不需要使用 RTS 引脚,可以设置为 UART_PIN_NO_CHANGE
cts_io_num (int):
指定 CTS(清除发送)引脚的 GPIO 编号。如果不需要使用 CTS 引脚,可以设置为 UART_PIN_NO_CHANGE
返回值
ESP_OK: 成功配置 UART 引脚。
使用参考
例如:设置串口0,对应的IO为TX: IO4, RX: IO5
uart_set_pin(UART_NUM_0, 4, 5, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
安装驱动程序
通信管脚设置完成后,请调用 uart_driver_install 安装驱动程序并指定以下参数:
- UART 控制器编号
- Tx 环形缓冲区的大小
- Rx 环形缓冲区的大小
- 指向事件队列句柄的指针
- 事件队列大小
- 分配中断的标志
该函数将为 UART 驱动程序分配所需的内部资源。
uart_driver_install 用于安装和初始化 UART 驱动程序。
esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, QueueHandle_t *uart_queue, int intr_alloc_flags);
参数说明
uart_num (uart_port_t):指定要初始化的 UART 端口号。ESP32 支持多个 UART 端口,通常使用 UART_NUM_0、UART_NUM_1 或 UART_NUM_2。
rx_buffer_size (int):接收缓冲区的大小(以字节为单位)。如果设置为 0,则不使用接收缓冲区。
tx_buffer_size (int):发送缓冲区的大小(以字节为单位)。如果设置为 0,则不使用发送缓冲区。
queue_size (int):事件队列的大小。如果设置为 0,则不使用事件队列。
uart_queue (QueueHandle_t *):指向 QueueHandle_t 类型的指针,用于存储事件队列的句柄。如果 queue_size 为 0,则此参数可以为 NULL。
intr_alloc_flags (int):
中断分配标志,用于配置中断的属性。常见的取值包括:
ESP_INTR_FLAG_LEVEL1: 设置中断优先级为 1。
ESP_INTR_FLAG_LEVEL2: 设置中断优先级为 2。
ESP_INTR_FLAG_LEVEL3: 设置中断优先级为 3。
ESP_INTR_FLAG_LEVEL4: 设置中断优先级为 4。
ESP_INTR_FLAG_LEVEL5: 设置中断优先级为 5。
ESP_INTR_FLAG_LEVEL6: 设置中断优先级为 6。
ESP_INTR_FLAG_IRAM: 将中断处理程序和相关数据放在 IRAM 中,确保在 flash 休眠模式下仍能正常工作。
ESP_INTR_FLAG_SHARED: 允许多个中断源共享同一个中断处理程序。
返回值
ESP_OK: 成功安装 UART 驱动程序。
使用参考
例如:配置接收缓冲区大小为 1024,不使用发送缓冲区,不使用消息队列。
uart_driver_install(UART_NUM_0, 1024, 0, 0, NULL, 0);
例如:配置接收缓冲区大小为 1024,发送缓冲区大小为512,使用消息队列,消息队列大小为10。
QueueHandle_t uart_queue;
uart_driver_install(UART_NUM_0, 1024, 512, 10, &uart_queue, 0);
使用队列参考:
esp-idf/examples/peripherals/uart/uart_events
最终程序
我们创建了两个任务,一个用于发送,一个用于接收
- 发送任务每间隔2S往UART0发送字符串Hello world
- 接收任务轮询读取接收缓冲区的数据,一旦读取到数据马上进行打印
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "string.h"
#include "driver/gpio.h"
static const char *TAG = "UART"; // 定义日志标签
static const int RX_BUF_SIZE = 1024;
#define TXD_PIN (GPIO_NUM_43)
#define RXD_PIN (GPIO_NUM_44)
#define UART_NUM (UART_NUM_1)
void init(void)
{
//配置 UART 参数,包括波特率、数据位、校验位、停止位、流控和时钟源。
const uart_config_t uart_config = {
.baud_rate = 115200,
.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 参数配置。
uart_param_config(UART_NUM, &uart_config);
//设置 UART 引脚,指定 TX 和 RX 引脚,其他引脚保持不变。
uart_set_pin(UART_NUM, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
//安装 UART 驱动程序,配置接收缓冲区大小为 RX_BUF_SIZE * 2,不使用发送缓冲区。
uart_driver_install(UART_NUM, RX_BUF_SIZE * 2, 0, 0, NULL, 0);
}
static void tx_task(void *arg)
{
while (1) {
//发送字符串 "Hello PC"。
char data[] = {"Hello PC"};
const int len = strlen(data);
//发送指定长度的数据到 UART。
uart_write_bytes(UART_NUM, data, len);
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
static void rx_task(void *arg)
{
uint8_t* data = (uint8_t*) malloc(RX_BUF_SIZE + 1);
while (1) {
// 从 UART 读取数据,最多读取 RX_BUF_SIZE 字节,等待时间最多 1000 毫秒(1 秒)。
const int rxBytes = uart_read_bytes(UART_NUM, data, RX_BUF_SIZE, 1000 / portTICK_PERIOD_MS);
if (rxBytes > 0) {
//在接收到的数据末尾添加空字符,使其成为有效的 C 字符串。
data[rxBytes] = 0;
ESP_LOGI(TAG, "Read %d bytes: %s", rxBytes, data);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
free(data);
}
void app_main(void)
{
init();
//rx_task: 任务名称为 "uart_rx_task",堆栈大小为 2048 字节。
xTaskCreate(rx_task, "uart_rx_task", 1024 * 2, NULL, 1, NULL);
//tx_task: 任务名称为 "uart_tx_task",堆栈大小为 2048 字节。
xTaskCreate(tx_task, "uart_tx_task", 1024 * 2, NULL, 1, NULL);
while (1)
{
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
然后把板卡的串口线接上,下图蓝色的线:
运行后,打开串口调试软件就可以接收到来自板卡的数据。
串口助手点击发送后,调试端会打印出接收到的hello ESP32数据,当然了,因为ESP32S3默认使用UART0作为日志口,所以UART0也会打印启动日志和hello ESP32
如果你不希望这个串口打印日志信息,那你可以绑定GPIO_NUM_43和GPIO_NUM_44到其它串口,例如UART_NUM_1上。
#define TXD_PIN (GPIO_NUM_43)
#define RXD_PIN (GPIO_NUM_44)
#define UART_NUM (UART_NUM_1)