S2-03 ESP-IDF开发: UART

UART(串口通讯)

RS232 串口

串口不是RS232,RS232通讯使用了串口规范,RS232是电器层的通讯标准
任何通信都要有信息传输载体,或者是有线的或者是无线的。串口通信是通过串口线进行有线通信。串口通信在早期是计算机与外界通信的主要手段,那时候的计算机基本上都标配有串口以实现和外部通信。
串口通信早期就定义了一套标准的串口规约,DB9 (9个引脚)接口就是标准接口,此外还有不常见的DB25 (25个引脚)。
DB9接口中有9根通信线,其中3根线(GND、TXD、RXD)很重要必不可少,剩余6根都是和流控有关的,现代我们使用串口都是用来做调试,所以这6根很少使用。但是面试的时候还是可能会问到的,所以还是可以了解一下的。
S2-03 ESP-IDF开发: UART_第1张图片

针脚表示如下:

  1. DCD( Data Carrier Detect)载波检测:主要用于Modem通知计算机其处于在线状态,即Modem检测到拨号音,处于在线状态。
  2. RXD(Receive(rx) Data)串口数据输入:此引脚用于接收外部设备送来的数据。
  3. TXD(Transmit(tx) Data)串口数据输出:此引脚将计算机的数据发送给外部设备。
  4. DTR( Data Terminal Ready )数据终端就绪:当此引脚高电平时,通知Modem可以进行数据传输,计算机已经准备好。
  5. GND(Ground)信号地线
  6. DSR(data set ready)数据发送就绪:此引脚高电平时,通知计算机Modem已经准备好,可以进行数据通讯了。
  7. RTS(Request To Send)请求发送:此脚由计算机来控制,用以通知Modem马上传送数据至计算机;否则,Modem将收到的数据暂时放入缓冲区中。
  8. CTS(Clear to send)发送清除:此脚由Modem控制,用以通知计算机将欲传的数据送至Modem。
  9. RI ( Ring Indicator)铃声指示:Modem通知计算机有呼叫进来,是否接听呼叫由计算机决定。

串口通讯是一种基于异步传输方式的数据通讯方式,可以通过串行接口(通常为 RS232、RS485、TTL 等)在两个设备之间进行数据传输。在单片机、嵌入式系统、物联网等领域,串口通讯经常被用来完成设备之间的信息交换和控制指令传输。

想要了解串口通讯,我们需要先了解以下概念:

  1. 帧:串口通讯中的数据传输单位,一般由起始位、数据位、校验位和停止位组成。其中起始位和停止位用于标志帧的开始和结束,数据位用于传输实际数据,校验位用于检测数据是否正确。
  2. 波特率:串口通讯中传输速率的单位,表示每秒钟传输的比特数。通常使用的波特率为9600、115200等。发生器和接收器的波特率必须匹配,才能保证数据传输的正确性。
  3. 数据位:串口通讯中传输数据的位数,通常为8位或9位。数据位的位数应该与通讯双方相同。
  4. 校验位:用于检测数据是否正确的冗余位。常见的校验方式有奇偶校验、偶偶校验、和校验等。
  5. 奇偶校验:通过在每一帧数据的最后一位(校验位)加入奇偶标记,以检查传输的数据是否有错。如果所传输的数据中含有偶数个"1",则将校验位设置为“1”,反之为“0”。
  6. 流控制:一种保证数据传输完整性和正确性的技术。当接收器处理数据时,发送器将停止发送数据,直到接收器准备好接收新的数据。常见的流控制方式有硬件流控制和软件流控制。
  7. UART:通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),用于实现串口通讯的芯片。

串口通讯的基本原理

1. 串口通信连线

任何通信都要有信息传输载体,或者是有线的或者是无线的。串口通信是通过串口线进行有线通信,在通信时最少需要两根线(GND和信号线)既可以实现单工通信,GPS模块就是典型的串口单工通信实例。此外大部分的串口通信都是使用3根线(TXD、RXD、GND)来实现全双工通信。
GND:保证两设备共地,有统一的参考平面。你说你是高电平或者你说你是低电平肯定有一个参考,GND就是参考平面。
S2-03 ESP-IDF开发: UART_第2张图片

2. 串口通信时序

串口通信时,收发是一个周期一个周期进行的,每个周期传输n个二进制位。这一个周期就叫做一个通信单元,一个通信单元由:起始位(1bit)+数据位(8bit)+奇偶校验位(1bit)+停止位(1bit) 组成的。

串口通信是异步通信,所以通信双方必须事先约定好通信参数,这些通信参数包括:波特率、数据位、校验位、停止位,这些参数中的任何一个设置错误,都会导致通信失败。譬如波特率调错了,发送方发送没问题,接收方也能接收,但是接收到全是乱码。
S2-03 ESP-IDF开发: UART_第3张图片

波特率
简而言之,串口传输的波特率即为每秒钟传输二进制的位数,是衡量资料传送速率的指标。

波特率-115200:

1s传输二进制的位数115200bit
传输1bit需要 1/115200s=8.68us
串口通信是一种异步通信方式,收发双方并没有同步时钟信号来规约一个bit的数据发送电平维持多长时间,这样只能靠收发双方的速率来同步收发数据,这个速率叫做波特率(BaudRate),其单位为bps(bit per second)。

串口通信常用速率为115200(3G/4G/调试串口等)、9600(NB-loT/GPS等)、4800等。收发双方的速率必须保持一致,否则会出现乱码或完全接收不到的现象。

起始位

先发出一个逻辑”0”的信号,表示传输字符的开始。它表示发送方要开始发送一个通信单元,起始位的定义是串口通信标准事先指定的,是由通信线上的电平变化来反映的。对于串口通信而言总线没有数据传输空闲时维持高电平,一旦产生一个下降沿变成低电平则表示起始信号。

数据位

它一个通信单元中发送的有效信息位,是本次通信真正要发送的有效数据,串口通信一次发送多少位有效数据是可以设定的(可选的有6、7、8、9,一般都是选择8位数据位,因为一般通过串口发送的数据都是以字节为单位的ASCII码编码,而ASCII码中一个字符刚好编码为8位)。

奇偶校验位
奇偶校验是一种校验代码传输正确性的方法。根据被传输的一组二进制代码的数位中的"1"的个数是奇数或者偶数来进行校验。但是一般都不怎么准确,有50%d误差(等于没说)

奇数校验:8个bit数据位中有偶数个1,那么奇偶校验位为1才能满足1的个数为奇数(奇校验)。如果为0就是偶校验了。
1111 0000 1

偶数校验:8个bit数据位中有奇数个1,那么奇偶校验位为1才能满足1的个数为偶数(偶校验)。如果为0就是奇校验了。
1111 1000 1

此位还可以去除,即不需要奇偶校验位。

停止位
它是发送方用来表示本通信单元结束标志的,停止位的定义是串口通信标准事先指定的,是由通信线上的电平变化来反映的。

TTL、RS232、RS485 的介绍

1. TTL

TTL(Transistor-Transistor Logic)具体指的是 UART (通用异步收发传输器)通过 TTL 电平标准进行通信的方式。UART 是一种常用的串口通信协议,它可以广泛应用于各种数字电路及嵌入式系统中,例如传感器数据采集、工业控制、机器人控制等领域。

TTL 电平标准规定,当输入电压低于 0.8V 时为逻辑 0,当输入电压高于 2.0V 时为逻辑 1,电压为 0.8V 至 2.0V 之间的电平保证不确定性输出状态。TTL 技术具有传输速度快、功耗低、噪声干扰抗性高等优点,因此在数字电路设计中得到了广泛的应用。

TTL是芯片上的串口直接出的电平,它适合距离近且干扰小的情况,如开发板上芯片与芯片之间、开发板与串口模块之间的短距离串口通信。

2. RS232

RS232 是一种串口通信协议,是一种标准化的串行通信接口。它使用正负电平来表示二进制代码,其中逻辑 1 被表示为 -3V 至 -15V 的负电平,逻辑 0 被表示为 +3V 至 +15V 的正电平,也就是说RS232是负逻辑 ,逻辑0为高电平。RS232 协议中定义的信号线包括 TXD、RXD、RTS、CTS、DTR、DSR、DCD 和 GND 等。RS232 协议通常用于计算机和外围设备之间的串口通信,例如调制解调器、打印机、条码扫描仪等等。

设备与设备之间的长距离通信,因为压降和信号干扰等原因通常会使用RS232来进行通信。

RS232 的特点:

  • 接口使用一根Tx信号线和一根Rx信号线而构成共地的传输形式,这种方式抗噪声抗干扰性弱;
  • 传输距离有限,最大传输距离标准值为50英尺,实际上也只能用在50米左右。
  • 传输速率较低,在异步传输时,波特率为20Kbps(一般是115200bps);
  • 通信的时候只能两点之间进行通信,不能够实现多机联网通信;
  • RS232 与TTL电平不兼容,另外接口的信号电平值较高,易损坏接口电路的芯片。
3. TTL 和 RS232 之间的转换

CPU或芯片引出的串口默认都是TTL电平,如果要转成RS232电平的话一般会接一个MAX232的芯片。简单介绍一下MAX232,至于内部是怎么转换或者设计的,想知道的小伙伴可以自行去了解一下:
S2-03 ESP-IDF开发: UART_第4张图片

当用单片机和PC机通过串口进行通信,尽管单片机有串行通信的功能,但单片机提供的信号电平和RS232的标准不一样,因此要通过MAX232这种类似的芯片进行电平转换。

MAX232芯片的作用:是将单片机输出的TTL电平(0V,5V)转换成PC机能接收的232电平(-10V,10V)或将PC机输出的232电平(-10V,10V)转换成单片机能接收的TTL电平(0V,5V)。

4. RS485

RS485 和RS232一样都是基于串口的通讯接口,数据收发的操作是一致的,但是它们在实际应用中通讯模式却有着很大的区别,RS232接口为全双工数据通讯模式,而RS485接口一般为半双工数据通讯模式,数据的收发不能同时进行,为了保证数据收发的不冲突,硬件上是通过方向切换来实现的,相应也要求软件上必须将收发的过程严格地分开。

上述针对RS232接口的不足,就不断出现了一些新的接口标准,RS485 就是其中之一,它具备以下的特点:

差分传输增加噪声抗扰度,减少噪声辐射;

  • 长距离链路,最长可达4000英尺(约1219米);
  • 数据速率高达10Mbps (40英寸内,约12.2米) ;
  • 同一总线可以连接多个驱动器和接收器宽共模范围允许驱动器和接收器之间存在地电位差异,允许最大共模电压-7-12V。
  • RS-485能够进行远距离传输主要得益于使用差分信号进行传输,当有噪声干扰时仍可以使用线路上两者差值进行判断,使传输数据不受噪声干扰。

ESP32 串口通讯

硬件方面,ESP32-S3 有三个 UART(通用异步收发器)控制器,即 UART0、UART1、UART2,支持异步通信(RS232 和RS485)和 IrDA,通信速率可达到 5 Mbps。UART 控制器具有如下特性:

  • 支持三个可预分频的时钟源
  • 可编程收发波特率
  • 三个 UART 的发送 FIFO 以及接收 FIFO 共享 1024 x 8-bit RAM
  • 全双工异步通信
  • 支持输入信号波特率自检功能
  • 支持 5/6/7/8 位数据长度
  • 支持 1/1.5/2/3 个停止位
  • 支持奇偶校验位
  • 支持 AT_CMD 特殊字符检测
  • 支持 RS485 协议
  • 支持 IrDA 协议
  • 支持 GDMA 高速数据通信
  • 支持 UART 唤醒模式
  • 支持软件流控和硬件流控

软件方面,ESP32-S3 支持多个 UART(通用异步收发传输器)接口,每个接口都可以独立配置。以下是 ESP32-S3 串口通讯的详细介绍:

  • 支持多个串口接口:ESP32-S3 支持多个串口接口,每个接口都有单独的引脚控制和配置。在 ESP-IDF 开发环境中,我们可以使用 “uart_num” 参数来指定要使用的串口编号。
  • 可以自由选择波特率:UART 接口支持多种传输速率,包括标准、非标准波特率等。我们可以通过 “uart_config_t” 结构体来设置波特率、数据位、停止位、校验位等参数。
  • 支持硬件流控制:ESP32-S3 的串口支持硬件流控制,可以通过 RTS/CTS 引脚实现,以确保数据传输的正确性和完整性。
  • 支持 DMA 传输:ESP32-S3 的串口支持 DMA 传输,可以将数据传输负载转移到 DMA 子系统中,以提高数据传输效率和性能。
  • 支持中断和回调函数:为了实现数据传输的实时性和可靠性,ESP32-S3 的串口支持中断和回调函数。在接收到数据或发送完成后,可以触发相应的中断或回调函数,对接收或发送数据进行处理。

ESP32 串口使用

在 ESP32-S3-DevKitC-1 开发板中,UART0 可以通过 USB 转 UART 接口连接到 PC 或其他设备,用于调试和数据传输,而 UART1 和 UART2 则可通过扩展接口连接外部设备,例如 RS-485 模块、蓝牙模块等。
在实际开发过程中,UART0~2 都可通过扩展接口连接外部设备,例如 RS-485 模块、蓝牙模块等,如果需要使用UART0,则需要关闭对外的日志输出功能。
如果需要更换系统日志输出串口,则可以在 Menuconfig 中找到 Component config → ESP System Settings → Channel for console output 进行修改。

ESP32-S3 的三路 UART 可以通过 IO MUX 指定任意端口,也可使用其默认端口:

通道 UART0 UART1 UART2
发送(TX) IO43 IO17/任意 任意IO
发送(RX) IO44 IO18/任意 任意IO

1. 串口通讯参数设置

在使用串口通讯之前,首先需要对串口进行设置,需要调用 uart_param_config() 并向其传递 uart_config_t 结构体对串口进行初始化操作,以下是示例代码:

const uart_port_t uart_num = UART_NUM_2;	// 使用UART2
uart_config_t uart_config = {
    .baud_rate = 115200,			// 设置串口波特率位115200
    .data_bits = UART_DATA_8_BITS,		// 设置数据位位8位
    .parity = UART_PARITY_DISABLE,		// 不使用校验
    .stop_bits = UART_STOP_BITS_1,		// 设置1位停止位
    .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS,	//  RTS/CTS流控
    .rx_flow_ctrl_thresh = 122,			// RX缓冲区长度为122字节
};
// 配置串口阐述
ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config));

以上示例代码中使用 uart_config_t 结构体进行配置,该结构体原型如下:

typedef struct {
    int baud_rate;                      /* 串口波特率 */
    uart_word_length_t data_bits;       /* 数据位长度 */
    uart_parity_t parity;               /* 校验模式 */
    uart_stop_bits_t stop_bits;         /* 停止位长度 */
    uart_hw_flowcontrol_t flow_ctrl;    /* 是否使用流控 */
    uint8_t rx_flow_ctrl_thresh;        /* RX缓冲区长度 */
    union {
        uart_sclk_t source_clk;         /* 时钟源选择 */
        bool use_ref_tick  __attribute__((deprecated)); 
    };
} uart_config_t;
成员 描述
baud_rate 用于设置串口通信的波特率,表示每秒钟传输的比特数。常见的波特率有 9600、115200 等,需要根据具体应用场景设置。
data_bits 用于设置数据位长度,即每个数据字节中包含的位数。可选值有 5 位、6 位、7 位和 8 位,需要根据实际应用场景选择合适数值。
parity 用于设置校验方式。可选值有 UART_PARITY_DISABLE(无校验)、UART_PARITY_ODD(奇校验)和 UART_PARITY_EVEN(偶校验)。
stop_bits 用于设置停止位长度,即在数据位后面添加多少个停止位来表示一次数据传输结束。常用的停止位长度有 1 位和 2 位。
flow_ctrl 用于设置硬件流控制模式,即 CTS/RTS 流控制或者禁用。可选值有 UART_HW_FLOWCTRL_DISABLE(禁用)、UART_HW_FLOWCTRL_CTS(CTS 流控制)和 UART_HW_FLOWCTRL_RTS_CTS(CTS/RTS 流控制)。
rx_flow_ctrl_thresh 用于设置 UART 接收缓冲区的阈值,当接收缓冲区中的字节数达到该值时,会自动触发 RTS 信号,并向外部设备发送暂停数据传输的请求。需要根据具体应用场景设置。
source_clk 用于设置UART源时钟的选择。source_clk是属性值,可以设置为 UART_SCLK_APB 或者 UART_SCLK_REF_TICK(如果硬件支持)。同时提供了一个 use_ref_tick 的旧方法,已经被废弃,不再推荐使用。

串口中的流控(Flow Control)是一种数据传输控制方式,通常用于协调发送方和接收方之间的数据传输速度,以防止数据丢失或重复。

串口数据传输时,发送方和接收方之间需要通过一定的机制互相协调数据传输速度。如果发送方的数据发送速度过快,而接收方来不及处理,缓冲区会溢出;反之,如果接收方的数据读取速度过慢,发送方可能会由于等待回复而出现阻塞或超时的情况。流控机制就是为了解决这些问题而存在的。

串口中的流控方式通常有硬件流控和软件流控两种。其中,硬件流控是通过 RTS/CTS 信号实现的,当接收方的接收缓冲区已满时,会自动触发 RTS 信号,向发送方发送暂停数据传输的请求,直到接收方空出足够的缓冲区;软件流控则是通过发送特殊的控制字符来实现,当接收方的接收缓冲区已满时,会向发送方发送一个特殊的控制字符 XOFF,通知其停止数据传输,直到接收方准备好后再发送特殊的控制字符 XON,通知发送方恢复数据传输。

uart_param_config() 用于配置串口参数,函数原型如下:

esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config)
参数 描述
uart_num UART 端口编号,范围为 UART_NUM_0 至 UART_NUM_2
uart_config 指向 uart_config_t 结构体类型的指针,表示 UART 的参数配置结构体
返回值* 如果函数执行成功,则返回的值为 ESP_OK,表示设置参数成功;否则返回其他枚举值,表示设置参数失败,并根据具体的错误类型进行相应的处理,如果返回值为 ESP_ERR_INVALID_ARG,则表示传入的参数无效;如果返回值为 ESP_FAIL,则表示初始化 UART 硬件模块失败;如果返回值为 ESP_ERR_INVALID_STATE,则表示 UART 驱动程序未注册或已被占用。

或者可以通过多个步骤对串口进行初始化,可以调用以下函数:

  1. uart_set_baudrate() 设置串口波特率
  2. uart_set_word_length() 设置串口数据位长度
  3. uart_set_parity() 设置串口校验方式
  4. uart_set_stop_bits() 设置串口停止位长度
  5. uart_set_hw_flow_ctrl() 设置串口流控
  6. uart_set_mode 设置串口工作模式,可选的两种工作模式为普通模式( UART_MODE_UART )和红外模式( UART_MODE_IRDA )。

以上所有函数都有对应的 get 方法

2. 设置通讯引脚

因为 ESP32 采用的 IO MUX GPIO 交换矩阵,所以所有外设理论上是可以使用任意引脚的,在使用 UART 之前也需要先设置引脚,
设置通信参数后,配置其他 UART 设备将连接到的物理 GPIO 引脚。为此,调用函数 uart_set_pin() 并指定驱动程序应将 Tx、Rx、RTS 和 CTS 信号路由到的 GPIO 引脚号。如果您想为特定信号保留当前分配的引脚号,请传递宏 UART_PIN_NO_CHANGE
该函数原型如下:

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 端口编号,范围为 UART_NUM_0 至 UART_NUM_2
tx_io_num UART 的 TX 输出引脚号,即将要使用的串行端口的发送引脚(Transmit Pin)。需要注意的是,ESP32 的 UART 端口可用的 TX 引脚并不与硬件 UART 端口的引脚一一对应,而是由硬件 UART 端口的数量和可用引脚数量决定,具体需要查看 ESP32 开发板的引脚分配图以确定可用的引脚号。
rx_io_num UART 的 RX 输入引脚号,即将要使用的串行端口的接收引脚(Receive Pin)。
rts_io_num UART 的 RTS 输出引脚号,即请求发送引脚(Request to Send)。该参数是可选的,可以设置为 -1 表示不使用 RTS 引脚。
cts_io_num UART 的 CTS 输入引脚号,即清除发送引脚(Clear to Send)。该参数是可选的,可以设置为 -1 表示不使用 CTS 引脚。

如果在使用 UART 硬件控制流时要求使用 RTS/CTS 引脚,则需要将 uart_config_t 结构体中的 flow_ctrl 参数设置为 UART_HW_FLOWCTRL_CTS_RTS。

// Set UART pins(TX: IO4, RX: IO5, RTS: IO18, CTS: IO19)
ESP_ERROR_CHECK(uart_set_pin(UART_NUM_2, 4, 5, 18, 19));

3. 安装驱动

在使用串口之前,必须要先通过调用 uart_driver_install() 并指定以下参数来安装驱动程序,以上三组函数的调用顺序可以调整,但必须都要在使用串口收发数据之前完成。

// 使用事件队列设置UART缓冲IO
const int uart_buffer_size = (1024 * 2);
QueueHandle_t uart_queue;
// 在此处使用事件队列安装UART驱动程序
ESP_ERROR_CHECK(uart_driver_install(UART_NUM_2, uart_buffer_size, \
                                        uart_buffer_size, 10, &uart_queue, 0));

该函数原型如下:

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 端口编号,范围为 UART_NUM_0 至 UART_NUM_2
rx_buffer_size UART 接收缓冲区大小,单位为字节。该参数指定了接收缓冲区的大小,如果该参数设置为 0,则表示使用默认值(128 字节)。
tx_buffer_size UART 发送缓冲区大小,单位为字节。该参数指定了发送缓冲区的大小,如果设置为0,驱动程序将不会使用 TX 缓冲区,TX 功能将阻塞任务,直到所有数据都已发送出去。
event_queue_size UART 事件队列大小,单位为条目。UART 驱动程序会将读取到的数据和相关事件发布给应用程序队列,该参数指定了事件队列的大小,如果该参数设置为 0,则表示使用默认值(10 条目)。
uart_queue UART 事件队列句柄指针。该参数用于传递一个由应用程序定义的队列句柄,用于接收 UART 驱动程序发布的事件消息,如果为 NULL 表示不使用事件队列
intr_alloc_flags 中断分配标志,用于指定中断服务程序使用的内存管理方式。该参数可以设置为 0 或者 ESP_INTR_FLAG_IRAM,表示使用默认内存分配方式或者使用内部 RAM 分配方式。
返回值 函数返回值 ESP_OK 表示操作成功,其他错误码则表示发生错误。

4. 开始通讯

通过以上三个函数对 UART进行初始化后,您可以连接外部 UART 设备并检查通信。

串行通信由每个 UART 控制器的有限状态机 (FSM) 控制。

发送数据的过程包括以下步骤:

  1. 将数据写入 Tx FIFO 缓冲区
  2. FSM 序列化数据
  3. FSM 将数据发送出去

接收数据的过程类似,只是步骤相反:

  1. FSM 处理传入的串行流并将其并行化
  2. FSM 将数据写入 Rx FIFO 缓冲区
  3. 从 Rx FIFO 缓冲区读取数据

因此,应用程序将仅限于使用 uart_write_bytes()uart_read_bytes() 分别从各自的缓冲区写入和读取数据,而 FSM 将完成其余的工作。

a. 发送数据

准备传输数据后,调用该函数uart_write_bytes()并将数据缓冲区的地址和数据长度传递给它。该函数会将数据复制到 Tx 环形缓冲区(立即或在有足够空间可用后),然后退出。当 Tx FIFO 缓冲区中有可用空间时,中断服务例程 (ISR) 会在后台将数据从 Tx 环形缓冲区移动到 Tx FIFO 缓冲区。下面的代码演示了这个函数的用法。

// 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));

uart_write_bytes_with_break() 和 uart_write_bytes() 都是 ESP32 SDK 中用于向 UART 硬件写入数据的函数。它们的区别在于,uart_write_bytes() 函数可以连续写入多个字节的数据,而 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);

两个函数原型如下:

esp_err_t uart_write_bytes(uart_port_t uart_num, const char *data, size_t size);
esp_err_t uart_write_bytes_with_break(uart_port_t uart_num, const char *data, size_t size, int break_duration);
参数 描述
uart_num UART 端口编号,范围为 UART_NUM_0 至 UART_NUM_2
data 指向要写入的数据缓冲区的指针。
size 要写入的字节数。
break_duration 发送停止信号的持续时间,单位为毫秒。

uart_write_bytes 用于向 UART 硬件连续写入一串二进制数据。需要注意的是,该函数不会自动添加任何停止位或起始位,因此需要确保写入的数据已经按照协议添加了正确的位数。
uart_write_bytes_with_break 可以在写入一串二进制数据之前插入一个持续一段时间的停止信号,以便在传输数据之间添加间隔。该函数也可用于 UART 通信协议中的 Break 功能实现,通过设置 break_duration 参数为较长时间实现发送短暂的停止信号。

注意:
这两个函数都是阻塞式调用,意味着在写入数据期间程序将无法执行其他任务,直到数据完全被写入完成。因此,在实际使用中需要根据具体需求选择合适的函数,并注意读取返回值以避免写入错误。

另一个将数据写入 Tx FIFO 缓冲区的函数是 uart_tx_chars() 。与 不同uart_write_bytes(),此函数在空间可用之前不会阻塞。相反,它将写入所有可以立即放入硬件 Tx FIFO 的数据,然后返回写入的字节数,该函数原型如下:

int uart_tx_chars(uart_port_t uart_num, const char* buffer, uint32_t len);
参数 描述
uart_num UART 端口编号,范围为 UART_NUM_0 至 UART_NUM_2
buffer 指向要写入的数据缓冲区的指针。
len 表示要发送数据的长度,单位为字节数。
返回值 返回值为实际发送的字节数,如果出现错误则返回负数。
该函数是阻塞式调用,调用该函数时,如果 UART 缓冲区未满,则会将缓冲区中的数据立即发送出去,并且该函数会一直阻塞,直到所有数据都被发送完毕。因此,该函数适用于发送数据量较小的场景,但如果需要发送大量数据请使用其他异步方式。

该函数有一个“配套”函数 uart_wait_tx_done() 可以监视 Tx FIFO 缓冲区的状态,并在缓冲区为空时返回。

// Wait for packet to be sent
const uart_port_t uart_num = UART_NUM_2;
ESP_ERROR_CHECK(uart_wait_tx_done(uart_num, 100)); // wait timeout is 100 RTOS ticks (TickType_t)
b. 接收数据

一旦数据被 UART 接收并保存在 Rx FIFO 缓冲区中,就需要使用函数来检索它 uart_read_bytes() 。在读取数据之前,您可以通过调用检查 Rx FIFO 缓冲区中可用的字节数 uart_get_buffered_data_len() 。下面给出了使用这些函数的示例:

// 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);

如果不再需要 Rx FIFO 缓冲区中的数据,您可以通过调用清除缓冲区 uart_flush()

uart_get_buffered_data_len 用于获取 UART 接收缓冲区中剩余数据的长度,函数原型如下:

esp_err_t uart_get_buffered_data_len(uart_port_t uart_num, size_t* size);
参数 描述
uart_num UART 端口编号,范围为 UART_NUM_0 至 UART_NUM_2
size 表示获取到的缓冲区数据长度的指针变量,数据类型为 size_t。
返回值 该函数是一个阻塞式调用,如果当前缓冲区中没有数据,则该函数会一直等待直到有新的数据到达,如果返返回值为 ESP_OK 则表示正确返回,否则失败。

uart_read_bytes 用于从 URAT 缓冲区中接收数据,该函数会一直阻塞,直到接收到制定字节数的数据或等待超时,该函数原型如下:

int uart_read_bytes(uart_port_t uart_num, void* buf, uint32_t length, TickType_t ticks_to_wait);
参数 描述
uart_num UART 端口编号,范围为 UART_NUM_0 至 UART_NUM_2
buf 表示接收数据的缓冲区,数据类型为 void*(即任意类型的指针)。
length 表示要读取的数据字节数。
ticks_to_wait 表示等待数据到来的最长时间,单位为时钟节拍数,可以使用 FreeRTOS 提供的 pdMS_TO_TICKS() 函数将毫秒转换为时钟节拍数。
返回值
该函数会一直阻塞直到接收到指定字节数的数据或等待超时,如果成功接收到指定字节数的数据,则返回实际接收到的字节数;否则返回一个负值。

uart_flush 用于清空 UART 发送缓冲区和接收缓冲区,该函数原型如下:

esp_err_t uart_flush(uart_port_t uart_num);

该函数会立即清空指定 UART 端口的发送缓冲区和接收缓冲区,并返回一个错误码,常见的错误码包括:

  • ESP_OK:调用成功。
  • ESP_ERR_INVALID_ARG:传入的参数无效。
  • ESP_ERR_INVALID_STATE:UART 驱动程序状态无效,可能未初始化或已关闭。

注意:
在调用 uart_flush() 函数之前,必须先停止 UART 的发送和接收操作,否则可能会出现数据丢失或者缓冲区未清空的情况。另外,在使用 UART 通信时,如果出现了数据传输错误或者噪声干扰等问题,可以尝试使用该函数清空缓冲区来解决问题。
uart_flush() 函数用于清空 UART 接收缓冲区和发送缓冲区。其清空缓冲区的过程是将缓冲区中的数据发送出去,而不是直接删除掉。也就是说,如果在调用 uart_flush() 函数之前,UART 发送缓冲区中还有尚未发送的数据,那么这些数据会被立即发送出去。

5. 软件流控

uart_set_rts() 、 uart_set_dtr() 如果禁用了硬件流控,您可以分别使用函数和函数手动设置RTS 和DTR 信号电平。

6. 通讯模式选择

UART 控制器支持多种通信模式。可以使用功能选择模式 uart_set_mode() 。选择特定模式后,UART 驱动程序将相应地处理连接的 UART 设备的行为。例如,它可以使用RTS线控制RS485驱动芯片,实现半双工RS485通信。

// Setup UART in rs485 half duplex mode
ESP_ERROR_CHECK(uart_set_mode(uart_num, UART_MODE_RS485_HALF_DUPLEX));

7. 中断控制

在特定的 UART 状态或检测到的错误之后可以生成许多中断。ESP32-S3 技术参考手册> UART 控制器 (UART) > UART 中断和UHCI 中断[ PDF ]中提供了可用中断的完整列表。您可以分别通过调用uart_enable_intr_mask()或来启用或禁用特定中断uart_disable_intr_mask()。所有中断的掩码可作为UART_INTR_MASK。

默认情况下,该uart_driver_install()函数安装驱动程序的内部中断处理程序来管理 Tx 和 Rx 环形缓冲区,并提供事件等高级 API 函数(见下文)。也可以注册一个较低级别的中断处理程序而不是使用uart_isr_register(),并使用 再次释放它uart_isr_free()。某些使用 Tx 和 Rx 环形缓冲区、事件等的 UART 驱动程序函数在这种情况下不会自动工作——需要直接在 ISR 中处理中断。在自定义处理程序实现中,使用清除中断状态位uart_clear_intr_status()。

API 提供了一种方便的方法来处理本文档中讨论的特定中断,方法是将它们包装到专用函数中:

  • **事件检测:**其中定义了多个事件,uart_event_type_t可以使用 FreeRTOS 队列功能将其报告给用户应用程序。uart_driver_install()您可以在驱动程序安装中描述的调用时启用此功能。可以在peripherals/uart/uart_events中找到使用事件检测的示例。

  • **达到 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()
  • **模式检测:**检测到多次重复接收/发送同一字符的“模式”时触发的中断。此功能在示例peripherals/uart/uart_events中进行了演示。例如,它可用于检测命令字符串,后跟添加在命令字符串末尾的特定数量的相同字符(“模式”)。以下功能可用:

    • 使用配置并启用此中断uart_enable_pattern_det_intr()
    • 使用禁用中断uart_disable_pattern_det_intr()

8. 宏指令

API 还定义了几个宏。例如,UART_FIFO_LEN 定义硬件 FIFO 缓冲区的长度;UART_BITRATE_MAX 给出 UART 控制器等支持的最大波特率。

9. 卸载串口驱动

uart_driver_install() 如果不再需要与建立的通信,则可以通过调用删除驱动程序以释放分配的资源 uart_driver_delete()

代码共享位置:http://192.168.172.17:3000/Mars.CN/ESP-IDF-S2-UART.git

你可能感兴趣的:(ESP-IDF,入门篇,ESP32,FreeRTOS,嵌入式硬件,ESP-IDF)