ESP-C3入门6. 使用UART串口
- 一、简介
 - 二、UART使用的一般步骤
 - 三、使用的API
 - 1. `uart_config_t`结构体和设置参数函数`uart_param_config()`
 - 2. 专用函数设置参数
 - 3. 设置通信管脚`uart_set_pin()`
 - 4. 安装驱动程序`uart_driver_install()`
 - 5. 运行UART通信 `uart_write_bytes()`和`uart_read_bytes()`
 - (1)发送数据
 - 3.5.1.1 `uart_write_bytes()函数`
 - 3.5.1.2 `uart_write_bytes_with_break()函数`
 - 3.5.1.3 `uart_tx_chars()`
 - 3.5.1.4 `uart_wait_tx_done()`
 
- (2) 接收数据`uart_read_bytes()`
 
- 6. 软件流控
 - 7. 使用中断
 - (1)中断列表
 - (2)启用和禁用中断函数
 - (3)安装中断
 - (4)专用函数包装中断
 - 3.7.4.1 事件检测
 - 3.7.4.2 达到FIFO空间阈值或传输超时
 - 3.7.4.3 模式检测
 
- 8. 删除驱动程序
 
- 四、示例程序
 

一、简介
ESP32有三个UART控制器:
- UART0
 - UART1
 - UART2
 
其中UART0用作下载、调试串口,引脚不可改变,
 UART1和UART2的引脚是可以设置的。
本文使用的ESP32-C3芯片,只有一组UART0资源,开发板型号选用: ESP32-C3-DevKitM-1 v1.0,管脚资源如下图所示:

二、UART使用的一般步骤
- 初始化串口,设置通讯参数
 - 设置通信管脚
 - 安装驱动程序
 - 运行UART通信
 - 使用中断
 - 任务中阻塞等待串口队列
 - 如果不再使用串口,删除驱动程序
 
三、使用的API
1. uart_config_t结构体和设置参数函数uart_param_config()
 
用来初始化串口使用。
/**
 * @brief UART configuration parameters for uart_param_config function
 */
typedef struct {
	// 波特率
    int baud_rate;                      /*!< UART baud rate*/
    // 字节长度
    uart_word_length_t data_bits;       /*!< UART byte size*/
    // 校验
    uart_parity_t parity;               /*!< UART parity mode*/
    // 停止位
    uart_stop_bits_t stop_bits;         /*!< UART stop bits*/
    // 硬件流控模式
    uart_hw_flowcontrol_t flow_ctrl;    /*!< UART HW flow control mode (cts/rts)*/
    uint8_t rx_flow_ctrl_thresh;        /*!< UART HW RTS threshold*/
    union {
    	// 时钟源
        uart_sclk_t source_clk;         /*!< UART source clock selection */
        bool use_ref_tick  __attribute__((deprecated)); /*!< Deprecated method to select ref tick clock source, set source_clk field instead */
    };
} uart_config_t;
 
使用示例:
const int uart_num = UART_NUM_2;
uart_config_t uart_config = {
    .baud_rate = 115200,
    .date_bits = UART_DATA_8_BITS,
    .parity = UART_PARITY_DISABLE,
    .stop_bits = UART_STOP_BITS_1,
    .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS,
    .rx_flow_ctrl_thress = 122,
};
// Configure UART parameters
ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config));
 
2. 专用函数设置参数
- 波特率 
uart_set_baudrate() - 传输位 
uart_set_wod_length() - 奇偶控制 
uart_set_parity() - 停止位 :
uart_set_stop_bits() - 硬件流控模式: 
uart_set_hw_flow_ctrl() - 通信模式: 
uart_set_mode() 
如果要查询参数,可以把上面的_set_改成_get_。
3. 设置通信管脚uart_set_pin()
 
参数顺序: Tx,Rx,RTS,CTS。
 保持不变的参数,使用宏: UART_PIN_NO_CHANGE
 使用示例:
// 设置TX=IO4, RX=IO5, RTS=IO18, CTS=IO19
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_2, 4, 5, 18, 19));
 
4. 安装驱动程序uart_driver_install()
 
参数:
- Tx 环形缓冲区的大小
 - Rx 环形缓冲区的大小
 - 事件队列句柄和大小
 - 分配中断的标志
示例: 
// Setup UART buffered IO with event queue
const int uart_buffer_size = (1024 * 2);
QueueHandle_t uart_queue;
// Install UART driver using an event queue here
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_2, uart_buffer_size, \
                                        uart_buffer_size, 10, &uart_queue, 0));
 
5. 运行UART通信 uart_write_bytes()和uart_read_bytes()
 
串行通信由每个 UART 控制器的有限状态机 (FSM) 控制。发送数据的过程分为以下步骤:
- 将数据写入 Tx FIFO 缓冲区
 - FSM 序列化数据
 - FSM 发送数据
 
接收数据的过程类似,只是步骤相反:
- FSM 处理且并行化传入的串行流
 - FSM 将数据写入 Rx FIFO 缓冲区
 - 从 Rx FIFO 缓冲区读取数据
 
应用程序参考读写缓冲区即可进行UART通信。
(1)发送数据
3.5.1.1 uart_write_bytes()函数
 
写入缓冲区,空间不足时会阻塞,示例代码:
// Write data to UART.
char* test_str = "This is a test string.\n";
uart_write_bytes(uart_num, (const char*)test_str, strlen(test_str));
 
3.5.1.2 uart_write_bytes_with_break()函数
 
传输结束时添加串行中断信号,示例代码:
// Write data to UART, end with a break signal.
uart_write_bytes_with_break(uart_num, "test break\n",strlen("test break\n"), 100);
 
3.5.1.3 uart_tx_chars()
 
空间不足时不会阻塞,运行后立刻返回写入的字节数。
3.5.1.4 uart_wait_tx_done()
 
监听Tx FIFO缓冲区的状态,在缓冲区为空时返回。
(2) 接收数据uart_read_bytes()
 
uart_get_buffered_data_len() 用于查看Rx FIFO 缓冲区中可用的字节数,示例代码:
// 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);
 
6. 软件流控
如果硬件流控被禁用,您可使用函数 uart_set_rts() 和 uart_set_dtr() 分别手动设置 RTS 和 DTR 信号电平。
7. 使用中断
(1)中断列表
- UART_AT_CMD_CHAR_DET_INT: 接收到at_cmd字符时触发;
 - UART_RS485_CLASH_INT: RS-485 模式下检测到发送、接收有冲突时触发;
 - UART_RS485_FRM_ERR_INT: RS-485检测到数据帧错误;
 - UART_RS485_PARITY_ERR_INT: RS-485 模式下检测到奇偶校验错误;
 - UART_TX_DONE_INT: 发送完FIFO数据时触发;
 - UART_TX_BRK_IDLE_DONE_INT: 发送的空闲状态在发送完最后一个数据后保持在最低限值时触发;
 - UART_TX_BRK_DONE_INT: FIFO发送完后,完成发送NULL时触发;
 - UART_GLITCH_DET_INT: 当接收检测到 START 位时触发;
 - UART_SW_XOFF_INT: UART_SW_FLOW_CON_EN设置为1时收到Xon字符时触发;
 - UART_SW_XON_INT:UART_SW_FLOW_CON_EN设置为1时收到Xoff字符时触发;
 - UART_RXFIFO_TOUT_INT: 接收字节超出RX_TOUT_THRHD 时间触发;
 - UART_BRK_DET_INT: STOP位后检测到低电平时触发;
 - UART_CTS_CHG_INT: 当接收检测到 CTSn 信号的边沿变化时触发;
 - UART_DSR_CHG_INT: 当接收检测到 DSRn 信号的边沿变化时触发;
 - UART_RXFIFO_OVF_INT: 当接收获取的数据多于 FIFO 可存储的数据时触发;
 - UART_FRM_ERR_INT: 当接收检测到数据帧错误时触发 ;
 - UART_PARITY_ERR_INT: 当接收检测到数据中的奇偶校验错误时触发;
 - UART_TXFIFO_EMPTY_INT: 当传输 FIFO 中的数据量小于 tx_mem_cnttxfifo_cnt 指定的值时触发;
 - UART_RXFIFO_FULL_INT: 接收获得的数据多于 (rx_flow_thrhd_h3, rx_flow_thrhd) 指定的数据时触发。
 
(2)启用和禁用中断函数
调用 uart_enable_intr_mask() 或 uart_disable_intr_mask() 能够分别启用或禁用特定中断。
(3)安装中断
uart_driver_install() 函数可以安装驱动程序的内部中断处理程序,用以管理 Tx 和 Rx 环形缓冲区,并提供事件等高级 API 函数。
(4)专用函数包装中断
3.7.4.1 事件检测
uart_event_type_t定义多个事件,FreeRTOS队列功能上报事件。
3.7.4.2 达到FIFO空间阈值或传输超时
Tx和Rx FIFO缓冲区在填充特定数量的字符和在发送或接收数据超时时触发中断。使用此类中断的操作是:
- 配置缓冲区长度和超时阈值:在结构体
uart_intr_config_t中输入阈值并调用uart_intr_config() - 启用中断: 
uart_enable_tx_intr()和uart_enable_rx_intr() - 禁用中断: 
uart_disable_tx_intr()或uart_disable_rx_intr() 
3.7.4.3 模式检测
在检测到重复接收/发送同一字符的模式时触发中断。使用中断的步骤:
- 配置并启用此中断: 
uart_enable_pattern_det_intr() - 禁用中断: 
uart_disable_pattern_det_intr() 
8. 删除驱动程序
uart_driver_delete()
四、示例程序
基本的发送接收示例程序,不使用中断
#include "freertos/FreeRTOS.h"
#include "sdkconfig.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/uart.h"
#include "string.h"
#include "driver/gpio.h"
const int RX_BUF_SIZE = 1024;
#define TXD_PIN (GPIO_NUM_0)
#define RXD_PIN (GPIO_NUM_1)
/**
 * 初始化串口
 */
void uart_init(void) {
    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_APB,
    };
    // 安装驱动,发送缓冲区设置为空
    uart_driver_install(UART_NUM_1, RX_BUF_SIZE * 2, 0, 0, NULL, 0);
    // 设置参数
    uart_param_config(UART_NUM_1, &uart_config);
    // 设置引脚
    uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
}
/**
 * 发送数据
 * @param logName 
 * @param data 
 * @return 
 */
int sendData(const char* logName, const char* data)
{
    const int len = strlen(data);
    const int txBytes = uart_write_bytes(UART_NUM_1, data, len);
    ESP_LOGI(logName, "Wrote %d bytes: %s", txBytes, data);
    return txBytes;
}
/**
 * 发送数据任务
 * @param arg 
 */
void tx_task(void *arg)
{
    static const char *TX_TASK_TAG = "TX_TASK";
    esp_log_level_set(TX_TASK_TAG, ESP_LOG_INFO);
    while (1) {
        sendData(TX_TASK_TAG, "Hello world");
        vTaskDelay(2000 / portTICK_PERIOD_MS);
    }
}
/**
 * 接收数据任务
 * @param arg 
 */
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_RATE_MS);
        if (rxBytes > 0) {
            data[rxBytes] = 0;
            ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, data);
        }
        vTaskDelay(1);
    }
    free(data);
}
void app_main(void)
{
    uart_init();
    xTaskCreate(rx_task, "uart_rx_task", 1024*2, NULL, configMAX_PRIORITIES, NULL);
    xTaskCreate(tx_task, "uart_tx_task", 1024*2, NULL, configMAX_PRIORITIES-1, NULL);
    while(1){
        vTaskDelay(1);
    }
}
                
![[GXYCTF2019]luck_guy1题解](https://img-blog.csdnimg.cn/img_convert/9802c8db9a7ada5cbeea23f2a6b413cb.jpeg)















