【ESP32 IDF】UART串口
@TOC
前言
UART(通用异步收发器)是一种常见的串口通信协议,用于在微控制器和外部设备之间进行数据传输。在ESP32 IDF(Espressif IoT Development Framework)中,提供了强大的UART功能,使得ESP32能够轻松地与其他设备进行串口通信。本文将介绍ESP32 IDF中关于UART串口的使用方法和一些重要的功能。
一、数据传输的基本概念
1.1 串行与并行通信
串行通信:
串行通信是指通过单个数据线依次传输数据位,一个接一个地发送或接收数据。在串行通信中,每个数据位依次按照顺序传输,每个时钟周期只传输一个数据位。由于只需要一根数据线,串行通信在物理线缆使用上相对简单,但数据传输速率较慢。典型的串行通信接口包括 UART、SPI、I2C 等。
并行通信:
并行通信是指同时使用多个数据线传输数据,每个数据线传输一个数据位,从而实现多个数据位同时传输。在并行通信中,多个数据位同时传输,因此可以在同一个时钟周期内传输多个数据位,从而实现较高的数据传输速率。然而,并行通信需要使用多个物理线缆,布线复杂,并且受到信号传输时延等问题的影响。典型的并行通信接口包括并行总线(如 PCI、ISA 等)。
比较:
速率: 串行通信速率较慢,每次传输一个数据位;而并行通信速率较快,每次可以同时传输多个数据位,因此可以实现更高的数据传输速率。
线缆: 串行通信只需要一根数据线,线缆布线相对简单;而并行通信需要多个数据线,布线复杂。
时延: 串行通信中每个数据位依次传输,因此受到时延影响较小;而并行通信中多个数据线同时传输,受到时延影响较大。
总体来说,串行通信适用于远距离通信和简单布线的应用场景,而并行通信适用于高速数据传输和较短距离的应用场景。
1.2 单工/半双工/全双工通信
单工通信(Simplex):
单工通信是指数据只能单向传输的通信方式,通信的一方只能发送数据,另一方只能接收数据。在单工通信中,数据流动的方向是固定的,一般用于只需单向传输数据的场合,如广播电台、电视等。
半双工通信(Half-Duplex):
半双工通信是指通信的双方都可以发送和接收数据,但不能同时进行。在同一时刻,一方只能发送数据,另一方只能接收数据。因此,在半双工通信中,数据的流动方向是可以改变的,但不能同时发送和接收数据。
全双工通信(Full-Duplex):
全双工通信是指通信的双方可以同时进行发送和接收数据。在全双工通信中,通信的双方能够独立地发送和接收数据,不会发生冲突。全双工通信常用于需要双向同时传输数据的场合,如电话通话、网络通信等。
1.3 同步/异步通信
同步通信:
在同步通信中,数据的传输是在发送方和接收方之间按照事先约定好的时钟信号进行的。发送方和接收方需要共享一个时钟信号,以确保数据传输的同步性。通常,同步通信需要发送方和接收方事先协商好时钟频率和时序控制方式,以便正确地进行数据传输。常见的同步通信接口包括 SPI(串行外设接口)、I2C(Inter-Integrated Circuit)等。
异步通信:
在异步通信中,数据的传输不需要发送方和接收方共享一个时钟信号。发送方和接收方在通信开始之前不需要进行时钟同步,而是通过发送数据中的起始位、终止位和校验位等信息来实现数据的同步和解析。异步通信不需要事先协商时钟频率和时序控制方式,因此更加灵活。常见的异步通信接口包括 UART(通用异步收发器)、RS-232 等。
比较:
时序控制: 同步通信需要发送方和接收方共享一个时钟信号进行数据传输,而异步通信不需要时钟同步。
灵活性: 异步通信更加灵活,不需要事先协商时钟频率和时序控制方式,适用于简单的通信应用。而同步通信需要事先约定时钟频率和时序控制方式,相对不够灵活。
应用场景: 同步通信适用于对时序要求较高的应用场景,如高速数据传输;而异步通信适用于不需要时序同步的简单通信场景,如串口通信。
总的来说,同步通信和异步通信各有其适用的场景,开发者可以根据具体的需求和应用场景选择合适的通信
1.4 波特率
波特率是指串行通信中每秒钟传输的比特数,通常用波特率(bps)来表示。它决定了数据传输的速度和稳定性,即每秒钟能够传输的位数。在串行通信中,发送方和接收方需要使用相同的波特率才能正确地进行数据传输。常见的波特率包括 9600 bps、115200 bps 等。
1.5 UART 四要素
UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器)是一种常见的串行通信接口,用于实现串行数据的发送和接收。UART 通信的四要素包括:
波特率(Baud Rate): 波特率是串行通信中每秒钟传输的比特数,决定了数据传输的速度。发送方和接收方需要使用相同的波特率才能正常通信。
数据位(Data Bits): 数据位指的是每个数据字节中所包含的位数,常见的数据位数包括 5 位、6 位、7 位、8 位等。
停止位(Stop Bits): 停止位是在每个数据字节之后发送的用于指示数据传输结束的位。通常情况下,停止位的个数为 1 位或 2 位。
校验位(Parity Bit): 校验位用于检测数据传输过程中是否发生了错误,常见的校验方式包括奇校验、偶校验、无校验等。
二、串口的使用
2.1 配置UART串口
我们可以使用下面这个函数初始化一个UART
串口:
esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config)
参数1为串口编号,他有两个取值:
#define UART_NUM_0 (0) /*!< UART port 0 */
#define UART_NUM_1 (1) /*!< UART port 1 */
需要注意的是:我们的printf
是使用的UART_NUM_0
,如果你需要使用新串口,需要使用UART_NUM_1
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 */
bool use_ref_tick __attribute__((deprecated)); /*!< Deprecated method to select ref tick clock source, set source_clk field instead */
};
} uart_config_t;
baud_rate:
类型:int
作用:指定UART的波特率(波特率是指每秒传输的位数,通常用bps表示)。这个参数决定了通信双方之间传输数据的速度。
data_bits:
类型:uart_word_length_t
作用:指定UART每个数据帧的位数,即数据位的长度。通常可以选择8位、9位等,表示一个数据帧中有多少位用于携带实际的数据。
parity:
类型:uart_parity_t
作用:指定奇偶校验模式,可以选择禁用奇偶校验、奇校验或偶校验。奇偶校验用于检测传输过程中的错误。
stop_bits:
类型:uart_stop_bits_t
作用:指定停止位的数量。通常可以选择1位或2位停止位。
flow_ctrl:
类型:uart_hw_flowcontrol_t
作用:指定硬件流控制的模式,可以选择禁用流控制、启用CTS(Clear To Send)和RTS(Request To Send)
rx_flow_ctrl_thresh:
类型:uint8_t
作用:当启用硬件流控制时,该参数指定了UART接收缓冲区中的数据量,当数据量低于这个阈值时,RTS信号将被拉高,允许继续接收数据。
source_clk:
类型:uart_sclk_t
作用:指定UART的时钟源。可以选择使用内部时钟源(PLL输出)或外部时钟源。
他有这些取值:
typedef enum {
UART_SCLK_APB = 0x0, /*!< UART source clock from APB*/
#if SOC_UART_SUPPORT_RTC_CLK
UART_SCLK_RTC = 0x1, /*!< UART source clock from RTC*/
#endif
#if SOC_UART_SUPPORT_XTAL_CLK
UART_SCLK_XTAL = 0x2, /*!< UART source clock from XTAL*/
#endif
#if SOC_UART_SUPPORT_REF_TICK
UART_SCLK_REF_TICK = 0x3, /*!< UART source clock from REF_TICK*/
#endif
} uart_sclk_t;
UART_SCLK_APB (0x0):
作用:指定UART的时钟源来自APB总线。
APB是Advanced Peripheral Bus(高级外设总线)的缩写,是用于连接外设的总线之一。
UART_SCLK_RTC (0x1):
作用:指定UART的时钟源来自RTC(Real-Time Clock,实时时钟)。
RTC通常是微控制器中一个独立的时钟模块,用于提供精确的时间信息。
UART_SCLK_XTAL (0x2):
作用:指定UART的时钟源来自外部晶体振荡器(XTAL)。
外部晶体振荡器提供了高精度的时钟信号,常用于需要精准时序要求的应用。
UART_SCLK_REF_TICK (0x3):
作用:指定UART的时钟源来自参考时钟(REF_TICK)。
参考时钟通常是一个周期性的时钟信号,用于提供基准时钟。
use_ref_tick:
类型:bool
作用:(已弃用)以前用于选择参考时钟源,现在被source_clk字段替代。
2.2 配置UART引脚
我们可以使用下面这个函数
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端口号,例如UART0、UART1等。
tx_io_num:
类型:int
含义:指定UART的发送引脚(TX)的引脚号。这是数据从ESP32发送到外部设备的引脚。
rx_io_num:
类型:int
含义:指定UART的接收引脚(RX)的引脚号。这是数据从外部设备接收到ESP32的引脚。
rts_io_num:
类型:int
含义:指定UART的RTS(Request To Send)引脚的引脚号。RTS用于硬件流控制,在一些情况下可以让接收方暂停发送数据。
如果没有,使用UART_PIN_NO_CHANGE
cts_io_num:
类型:int
含义:指定UART的CTS(Clear To Send)引脚的引脚号。CTS也是用于硬件流控制,接收方通过CTS信号告知发送方是否可以发送数据。
如果没有,使用UART_PIN_NO_CHANGE
2.3 安装串口驱动
我们可以使用下面这个函数安装串口驱动:
esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int event_queue_size, QueueHandle_t *uart_queue, int intr_alloc_flags)
uart_num:
类型:uart_port_t
含义:指定要安装的UART端口号,例如UART0、UART1等。
rx_buffer_size:
类型:int
含义:指定UART接收缓冲区的大小,即用于存储接收到的数据的缓冲区大小。
tx_buffer_size:
类型:int
含义:指定UART发送缓冲区的大小,即用于存储待发送数据的缓冲区大小。
event_queue_size:
类型:int
含义:指定用于存储UART事件的队列的大小。UART事件包括接收到数据、发送数据完成等事件。
uart_queue:
类型:QueueHandle_t *
含义:指定一个指针,用于存储创建的UART事件队列的句柄。这样,用户可以在其他部分的代码中使用这个队列句柄来接收和处理UART事件。
他里面的取值如下:
typedef enum {
UART_DATA, /*!< UART data event*/
UART_BREAK, /*!< UART break event*/
UART_BUFFER_FULL, /*!< UART RX buffer full event*/
UART_FIFO_OVF, /*!< UART FIFO overflow event*/
UART_FRAME_ERR, /*!< UART RX frame error event*/
UART_PARITY_ERR, /*!< UART RX parity event*/
UART_DATA_BREAK, /*!< UART TX data and break event*/
UART_PATTERN_DET, /*!< UART pattern detected */
UART_EVENT_MAX, /*!< UART event max index*/
} uart_event_type_t;
UART_DATA:
表示接收到了新的数据。
UART_FIFO_OVF:
表示接收 FIFO 溢出。
UART_BUFFER_FULL:
表示发送缓冲区已满。
UART_BREAK:
表示接收到 Break 信号。
UART_PARITY_ERR:
表示接收到的数据帧中有奇偶校验错误。
UART_FRAME_ERR:
表示接收到的数据帧中存在帧错误。
UART_PATTERN_DET:
表示接收到了特定的数据模式。
intr_alloc_flags:
类型:int
含义:指定中断分配的标志位。这个参数用于确定中断服务程序的运行上下文。可以使用常量 ESP_INTR_FLAG_* 来指定中断分配的特性。
作用:
控制 UART 接收和发送中断的行为,包括是否允许中断,以及中断的优先级等。
可能的取值:
ESP_INTR_FLAG_LEVEL1
: 设置中断优先级为 1 级,表示具有最高的优先级,适用于需要立即响应的紧急情况。
ESP_INTR_FLAG_LEVEL2
: 设置中断优先级为 2 级,次于级别 1,适用于需要较高优先级但不是最高的情况。
ESP_INTR_FLAG_LEVEL3
: 设置中断优先级为 3 级,优先级次于级别 2,适用于一般性的中断处理。
ESP_INTR_FLAG_IRAM
: 将中断服务程序(ISR)分配到内部 RAM 中运行,适用于对中断响应速度要求较高的情况。
ESP_INTR_FLAG_EDGE
: 使用边沿触发模式,当引脚状态发生变化时触发中断。
ESP_INTR_FLAG_LEVEL
: 使用电平触发模式,当引脚保持特定电平时触发中断。
ESP_INTR_FLAG_LOWMED
: 用于指示低或中等优先级的中断请求。
ESP_INTR_FLAG_HIGH
: 用于指示高优先级的中断请求。
如果 intr_alloc_flags
参数设置为 0,表示不使用任何中断配置标志。这意味着 UART 的中断将按照系统的默认设置进行分配和处理。在某些情况下,可能会选择不自定义中断配置,而是依赖于系统的默认设置,这通常是因为应用场景对中断处理的要求不是很严格或者默认设置已经足够满足需求。
因此,如果将 intr_alloc_flags
参数设置为 0,则系统将根据默认配置来处理 UART 的中断,这可能与系统的硬件和软件环境有关,具体的行为取决于 ESP-IDF 的实现细节。
2.4 获取环形缓冲区的数据长度
我们可以使用下面这个函数来获取环形缓冲区的数据长度:
esp_err_t uart_get_buffered_data_len(uart_port_t uart_num, size_t *size)
参数1为UART的编号,参数2为size_t *
,用于存储长度
2.5 读取数据
我们可以使用下面这个函数来读取UART数据;
int uart_read_bytes(uart_port_t uart_num, void *buf, uint32_t length, TickType_t ticks_to_wait)
参数1为UART的编号,参数2为你数据存储的地方,参数3为读取的长度,参数4为等待事件
2.6 发送数据
我们可以使用下面这个函数来发送数据;
int uart_write_bytes(uart_port_t uart_num, const void *src, size_t size)
参数1为UART的编号,参数2为发送源,参数3为源的size
总结
在ESP32 IDF中,UART串口模块提供了丰富的功能和灵活的配置选项,使得开发者可以方便地实现与外部设备的通信。通过使用UART串口,ESP32可以与各种设备(如传感器、显示器、无线模块等)进行可靠的数据交换。本文总结了ESP32 IDF中UART串口的基本用法,并介绍了一些常用的配置选项和注意事项,希望能够帮助开发者更好地利用ESP32的UART功能进行开发。
- 点赞
- 收藏
- 关注作者
评论(0)