1. 头文件包含

#include "driver/gpio.h"
#include "hal/gpio_types.h"
#include "driver/uart.h"
#include "esp_err.h"

2. UART的配置

使用如下函数配置UART:

esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config);

各个参数的含义:

2.1 uart_num的选择

这个参数用来选择配置哪个UART。可用的选项有:

typedef enum {
    UART_NUM_0,                         /*!< UART port 0 */
    UART_NUM_1,                         /*!< UART port 1 */
#if SOC_UART_HP_NUM > 2
    UART_NUM_2,                         /*!< UART port 2 */
#endif
#if SOC_UART_HP_NUM > 3
    UART_NUM_3,                         /*!< UART port 3 */
#endif
#if SOC_UART_HP_NUM > 4
    UART_NUM_4,                         /*!< UART port 4 */
#endif
#if (SOC_UART_LP_NUM >= 1)
    LP_UART_NUM_0,                      /*!< LP UART port 0 */
#endif
    UART_NUM_MAX,                       /*!< UART port max */
} uart_port_t;

对于ESP32-S3,一共有3个UART。但是 UART_NUM_0 一般给系统下载和调试用,所以一般用 UART_NUM_1UART_NUM_2

2.2 uart_config的设定

第二个参数用来配置UART的工作参数。结构体 **uart_config_t ** 的定义如下:

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 */
#if (SOC_UART_LP_NUM >= 1)
        lp_uart_sclk_t lp_source_clk;       /*!< LP_UART source clock selection */
#endif
    };
    struct {
        uint32_t backup_before_sleep: 1;    /*!< If set, the driver will backup/restore the HP UART registers before entering/after exiting sleep mode.
                                                 By this approach, the system can power off HP UART's power domain.
                                                 This can save power, but at the expense of more RAM being consumed */
    } flags;                                /*!< Configuration flags */
} uart_config_t;

2.2.1 baud_rate/波特率设置

用于设定UART的波特率。直接填入数据即可。例如115200。但是最大不超过5000000。

2.2.2 data_bits/数据位数设置

设定数据位。可用的选项有:

typedef enum {
    UART_DATA_5_BITS   = 0x0,    /*!< word length: 5bits*/
    UART_DATA_6_BITS   = 0x1,    /*!< word length: 6bits*/
    UART_DATA_7_BITS   = 0x2,    /*!< word length: 7bits*/
    UART_DATA_8_BITS   = 0x3,    /*!< word length: 8bits*/
    UART_DATA_BITS_MAX = 0x4,
} uart_word_length_t;

最常见是8bit传输,即 UART_DATA_8_BITS

2.2.3 parity/奇偶校验位设置

设定奇偶校验位。可用的选项有:

typedef enum {
    UART_PARITY_DISABLE  = 0x0,  /*!< Disable UART parity*/
    UART_PARITY_EVEN     = 0x2,  /*!< Enable UART even parity*/
    UART_PARITY_ODD      = 0x3   /*!< Enable UART odd parity*/
} uart_parity_t;

大多数时候都设置为 UART_PARITY_DISABLE ,即不使用奇偶校验位。不过根据实际使用要求选择。

2.2.4 stop_bits/停止位设置

设定停止位长度。可用的选项有:

typedef enum {
    UART_STOP_BITS_1   = 0x1,  /*!< stop bit: 1bit*/
    UART_STOP_BITS_1_5 = 0x2,  /*!< stop bit: 1.5bits*/
    UART_STOP_BITS_2   = 0x3,  /*!< stop bit: 2bits*/
    UART_STOP_BITS_MAX = 0x4,
} uart_stop_bits_t;

停止位可以是1位、1.5位、2位。常见的是使用1位。

2.2.5 flow_ctrl/流控位设置

设定是否使用硬件流控。可用的选项有:

typedef enum {
    UART_HW_FLOWCTRL_DISABLE = 0x0,   /*!< disable hardware flow control*/
    UART_HW_FLOWCTRL_RTS     = 0x1,   /*!< enable RX hardware flow control (rts)*/
    UART_HW_FLOWCTRL_CTS     = 0x2,   /*!< enable TX hardware flow control (cts)*/
    UART_HW_FLOWCTRL_CTS_RTS = 0x3,   /*!< enable hardware flow control*/
    UART_HW_FLOWCTRL_MAX     = 0x4,
} uart_hw_flowcontrol_t;

根据需要选择。一般2线UART应用,设置为 UART_HW_FLOWCTRL_DISABLE

2.2.6 rx_flow_ctrl_thresh/接收流控门限设置

ESP32-S3有3个UART,同时内置1KB的FIFO。这个FIFO由所有的3个UART分享。由于3个UART有6个通道(3发3收),所以默认情况下,每个UART分得128B的FIFO空间(需要注意,这个FIFO空间是可调的,但是一般情况下无需调整)。
在这里插入图片描述
rx_flow_ctrl_thresh 用于设置接收流控门限。因为接收缓存默认128B,rx_flow_ctrl_thresh 设置为比128B略小的数据即可,比如120。也就是接收FIFO内容超过120字节,产生接收流控。
这个仅在使能了硬件流控(flow_ctrl = UART_HW_FLOWCTRL_RTS 或者flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS)时候有效。

2.2.6 lp_source_clk/指定时钟源

该参数指定UART时钟源。有如下选项:

typedef enum {
    UART_SCLK_APB = SOC_MOD_CLK_APB,     /*!< UART source clock is APB CLK */
    UART_SCLK_RTC = SOC_MOD_CLK_RC_FAST, /*!< UART source clock is RC_FAST */
    UART_SCLK_XTAL = SOC_MOD_CLK_XTAL,   /*!< UART source clock is XTAL */
    UART_SCLK_DEFAULT = SOC_MOD_CLK_APB, /*!< UART source clock default choice is APB */
} soc_periph_uart_clk_src_legacy_t;

缺省使用 SOC_MOD_CLK_APB 即可。

3. UART使用的GPIO的配置

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);
  1. uart_num:同上,指定哪一个UART。
  2. tx_io_num:指定UART-TX GPIO。
  3. rx_io_num:指定UART-RX GPIO。
  4. rts_io_num:指定UART-RTS GPIO。
  5. cts_io_num:指定UART-CTS GPIO。
    一般情况下,使用2线UART,则不需要使用CTS引脚和RTS引脚。则后两个参数设置为 UART_PIN_NO_CHANGE 。还有一些场合,只需要使用接收或发送,则另一个不用的发送或接收引脚也设置为 UART_PIN_NO_CHANGE

4. 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);
  1. uart_num:指定使用哪一个UART外设。
  2. rx_buffer_size:指定接收缓存大小。
  3. tx_buffer_size:指定发送缓存大小。这两个缓存大小,不是指UART外设中FIFO的大小,而是由驱动开辟的位于内存中的缓存大小。接收缓冲大小 rx_buffer_size 必需大于分配给UART外设的接收FIFO的大小(例如,当接收FIFO是默认值128时,则设置为大于128的值)。发送缓冲大小 tx_buffer_size 必需大于分配给UART外设的发送FIFO的大小,或者设置为0。如果设置为0,则发送是阻塞的,发送函数会在数据完全发送后返回(个人理解应该是发送数据全部填入到发送FIFO后就返回)。
  4. queue_size:事件队列大小。所谓事件队列,是UART操作(如发送或接收)触发了某个事件,如发送完成或接收到数据、超时之类的,则产生一个事件,给用户处理。不需要,则设置为0。
  5. uart_queue:这是一个出参,不使用消息队列时,设置为NULL即可。
  6. intr_alloc_flags:指定一些标记,如中断优先级等。定义在 esp_intr_alloc.h 文件中。注意不可使用 ESP_INTR_FLAG_IRAM ,因为驱动的ISR程序没有定义在内部指令RAM中。

5. 获取缓冲中的数据量

在从驱动读取数据之前,一般要先获取缓冲中有多少数据可读;在向驱动写入数据之前,一般要先获取缓冲中有多少空闲空间可写。如下两个函数可实现这两项功能。

esp_err_t uart_get_buffered_data_len(uart_port_t uart_num, size_t *size) ;

esp_err_t uart_get_tx_buffer_free_size(uart_port_t uart_num, size_t *size) ;

6. 读取和写入数据

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

int uart_write_bytes(uart_port_t uart_num, const void *src, size_t size) ;

对于读取数据,可以通过参数 ticks_to_wait 指定等待超时时间,以FreeRTOS中的systick计数为单位。

7. 示例

以下程序示例中,配置一个UART接口,然后安装驱动,循环从驱动中读取数据并发送回去,从而实现上位机发送数据的loopback。
test_uart.h文件:

#define TEST_UART_UART_GPIO_TX        (GPIO_NUM_4)
#define TEST_UART_UART_GPIO_RX        (GPIO_NUM_5)
#define TEST_UART_UART_NUM            (UART_NUM_1)


void TEST_UART_UARTConfig(void) ;
void TEST_UART_UARTRecv2Send(void) ;

test_uart.c文件:

#include "driver/gpio.h"
#include "hal/gpio_types.h"
#include "driver/uart.h"
#include "esp_err.h"

#include "test_uart.h"

void TEST_UART_UARTConfig(void)
{
    esp_err_t iRetVal ;

    const uart_config_t stUARTConfig =
        {
            .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 ,
            .rx_flow_ctrl_thresh = 64 ,
            .source_clk          = UART_SCLK_APB 
        } ;

    /* config UART */
    iRetVal = uart_param_config(TEST_UART_UART_NUM, &stUARTConfig) ;

    ESP_ERROR_CHECK(iRetVal) ;

    /* set UART TX & RX GPIO */
    iRetVal = uart_set_pin(TEST_UART_UART_NUM, TEST_UART_UART_GPIO_TX, TEST_UART_UART_GPIO_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE) ;

    ESP_ERROR_CHECK(iRetVal) ;

    /* install driver */
    iRetVal = uart_driver_install(TEST_UART_UART_NUM, 128 * 4, 128 * 4, 0, NULL, 0);

    ESP_ERROR_CHECK(iRetVal) ;

    return ;
}

void TEST_UART_UARTRecv2Send(void)
{
	size_t        szSize = 0 ;
	unsigned char ucBuffer[128] ;
    esp_err_t     iRetVal ;
    int           iReadSize ;

    /* get rx data size */
    iRetVal = uart_get_buffered_data_len(TEST_UART_UART_NUM, &szSize) ;

    ESP_ERROR_CHECK(iRetVal) ;

    if(0 != szSize)
    {
        iReadSize = uart_read_bytes(TEST_UART_UART_NUM, (void *)ucBuffer, (szSize > sizeof(ucBuffer)) ? sizeof(ucBuffer) : szSize, 10) ;

        if(0 < iReadSize)
        {
            uart_write_bytes(TEST_UART_UART_NUM, (void *)ucBuffer, iReadSize) ;
        }
    }
}

main.c文件:

#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"

#include "test_uart.h"

void app_main(void)
{
    TEST_UART_UARTConfig() ;
    
    while (true)
    {
        TEST_UART_UARTRecv2Send() ;

        vTaskDelay(50) ;
    }
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐