ESP32-S3学习笔记<3>:UART的应用
- 1. 头文件包含
- 2. UART的配置
- 2.1 uart_num的选择
- 2.2 uart_config的设定
- 2.2.1 baud_rate/波特率设置
- 2.2.2 data_bits/数据位数设置
- 2.2.3 parity/奇偶校验位设置
- 2.2.4 stop_bits/停止位设置
- 2.2.5 flow_ctrl/流控位设置
- 2.2.6 rx_flow_ctrl_thresh/接收流控门限设置
- 2.2.6 lp_source_clk/指定时钟源
- 3. UART使用的GPIO的配置
- 4. UART驱动的加载
- 5. 获取缓冲中的数据量
- 6. 读取和写入数据
- 7. 示例
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 > 2UART_NUM_2, /*!< UART port 2 */
#endif
#if SOC_UART_HP_NUM > 3UART_NUM_3, /*!< UART port 3 */
#endif
#if SOC_UART_HP_NUM > 4UART_NUM_4, /*!< UART port 4 */
#endif
#if (SOC_UART_LP_NUM >= 1)LP_UART_NUM_0, /*!< LP UART port 0 */
#endifUART_NUM_MAX, /*!< UART port max */
} uart_port_t;
对于ESP32-S3,一共有3个UART。但是 UART_NUM_0 一般给系统下载和调试用,所以一般用 UART_NUM_1 或 UART_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);
- uart_num:同上,指定哪一个UART。
- tx_io_num:指定UART-TX GPIO。
- rx_io_num:指定UART-RX GPIO。
- rts_io_num:指定UART-RTS GPIO。
- 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);
- uart_num:指定使用哪一个UART外设。
- rx_buffer_size:指定接收缓存大小。
- tx_buffer_size:指定发送缓存大小。这两个缓存大小,不是指UART外设中FIFO的大小,而是由驱动开辟的位于内存中的缓存大小。接收缓冲大小 rx_buffer_size 必需大于分配给UART外设的接收FIFO的大小(例如,当接收FIFO是默认值128时,则设置为大于128的值)。发送缓冲大小 tx_buffer_size 必需大于分配给UART外设的发送FIFO的大小,或者设置为0。如果设置为0,则发送是阻塞的,发送函数会在数据完全发送后返回(个人理解应该是发送数据全部填入到发送FIFO后就返回)。
- queue_size:事件队列大小。所谓事件队列,是UART操作(如发送或接收)触发了某个事件,如发送完成或接收到数据、超时之类的,则产生一个事件,给用户处理。不需要,则设置为0。
- uart_queue:这是一个出参,不使用消息队列时,设置为NULL即可。
- 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) ;}
}