串口通讯
串口通讯
概念
什么是串口通讯与并口通讯的区别?
数据传输方式不同 串口通讯:串行传输,即一位一位地传输数据,数据传输速率较慢。 并口通讯:并行传输,即同时传输多个数据位,数据传输速率较快。
数据线数量不同 串口通讯:只有两根数据线(TXD和RXD),因此连接简单,但速率较慢。 并口通讯:需要多根数据线(8根或16根),因此连接较为复杂,但速率较快。
传输距离和数据速率限制不同 串口通讯:适用于短距离通讯,传输速率一般在115200bps以下。 并口通讯:适用于长距离通讯,传输速率可达数百Mbps。
常用设备不同 串口通讯:串口通讯常用于单片机、调试工具、传感器等设备之间的通讯。 并口通讯:并口通讯常用于打印机、扫描仪、显示器等设备之间的通讯。 综上所述,串口通讯和并口通讯各有优缺点,应根据实际需要选择合适的通讯方式。串口通讯适用于短距离、低速率的通讯,而并口通讯适用于长距离、高速率的通讯。
UART就是串口通讯吗
UART全称是通用异步收发器,是串口的一种,但不能说串口通讯就是UART,只是UART可以说是最常用的串口通讯方式。
UART与USART的区别
通讯方式不同 UART:UART是异步通讯协议,使用一个引脚用于传输数据,即TX(发送)和RX(接收)引脚。 USART:USART可以同时支持异步和同步通讯,可以通过配置来选择使用异步或同步通讯。同步通讯使用时钟引脚,异步通讯使用TX和RX引脚。
数据传输速率不同 UART:UART通常支持的速率较低,一般在1Mbps以下。 USART:USART支持的速率较高,可以达到数十Mbps。
数据格式不同 UART:UART只支持固定的数据格式,如8个数据位、无校验位、1个停止位。 USART:USART可以通过配置支持不同的数据格式,如5-9个数据位、奇偶校验位、1-2个停止位等。
功能不同 UART:UART只能支持基本的数据传输功能,不支持高级功能,如硬件流控制、多主机通讯等。 USART:USART支持多种高级功能,如硬件流控制、多主机通讯、自动波特率检测等。 综上所述,UART和USART虽然都是串口通讯协议,但是它们的通讯方式、数据传输速率、数据格式和功能都有所不同。在选择使用时需要根据实际需求选择合适的协议。
UART与RS485 RS232的关系
UART,是通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),既然是“器”,显然,它就是个设备而已,要完成一个特定的功能的硬件。
RS232/RS485,是两种不同的电气协议,也就是说,是对电气特性以及物理特性的规定,作用于数据的传输通路上,它并不内含对数据的处理方式。比如,最显著的特征是:RS232使用3-15v有效电平,而UART,因为对电气特性并没有规定,所以直接使用CPU使用的电平,就是所谓的TTL电平(可能在0~3.3V之间)。更具体的,电气的特性也决定了线路的连接方式,比如RS232,规定用电平表示数据,因此线路就是单线路的,用两根线才能达到全双工的目的;而RS485, 使用差分电平表示数据,因此,必须用两根线才能达到传输数据的基本要求,要实现全双工,必需用4根线。但是,无论使用RS232还是RS485,它们与UART是相对独立的,但是由于电气特性的差别,必须要有专用的器件和UART接驳
从某种意义上,可以说,线路上存在的仅仅是电流,RS232/RS485规定了这些电流在什么样的线路上流动和流动的样式;在UART那里,电流才被解释和组装成数据,并变成CPU可直接读写的形式。
编程时候,无论是232还是485就是单纯的用UART进行通讯实验。232和485只是在硬件电路图上存在区别。
也可以从以下几个方面总结:
信号电平不同 UART使用TTL电平,RS232使用正负12V的电平,而RS485使用差分信号,即两条信号线分别传输正负电平。
通信距离不同 UART的通信距离较短,一般在几米范围内;RS232的通信距离可达15米左右;而RS485的通信距离可达1200米。
传输速率不同 UART的传输速率一般较低,最高只能达到115200bps;RS232的传输速率可达115200bps;而RS485的传输速率可达10Mbps。
支持设备数量不同 UART只支持点对点通信,即只能连接两个设备;RS232支持一对一或一对多的连接方式,可以连接多个设备;而RS485支持多对多的连接方式,可以连接多个设备。
网络拓扑结构不同 UART没有特定的网络拓扑结构;RS232是点对点或星型拓扑结构;而RS485是总线型拓扑结构。
ModuBus
规定数据帧结构,是软件层面的协议,是一个应用层的协议,modbus可用于232和485,也就是说使用ModuBus的前提是完成UART的驱动
常用的串行通讯协议
单工通信:数据只能沿一个方向传输
半双工通信:数据可以沿两个方向传输,但需要分时进行
全双工通信:数据可以同时进行双向传输
同步通信:共用同一时钟信号
异步通信:没有时钟信号,通过在数据信号中加入起始位和停止位等一些同步信号
UART (通用异步收发器)
TXD:发送端 RXD:接收端 GND:公共地
异步通信
全双工
1-wire
DQ:发送/接收端
异步通信
半双工
IIC
SCL:同步时钟 SDA:数据输入/输出端
同步通信
半双工
SPI
SCK:同步时钟 MISO:主机输入,从机输出 MOSI:主机输出,从机输入 CS:片选信号
同步通信
全双工
奇偶校验
要发送的字节是0x1a,二进制表示为0001 1010。
采用奇校验,则在数据后补上个0,数据变为0001 1010 0,数据中1的个数为奇数个(3个)
采用偶校验,则在数据后补上个1,数据变为0001 1010 1,数据中1的个数为偶数个(4个)
接收方通过计算数据中1个数是否满足奇偶性来确定数据是否有错。
需要将WordLength选择为UART_WORDLENGTH_9B。因为使用奇偶校验时,数据帧的总长度会增加一位,因此需要将WordLength设置为9位,以保证正确的接收和发送
停止位校验
停止位校验必须有,可以有0.5、1、1.5、2.0个位,保持逻辑1电平。但是在STM32的HAL库中似乎只能选择1个或者2个位
USART_DR 寄存器
USART_DR 串口数据(Data)寄存器;这是一个双寄存器,包含了TDR和RDR,对它读操作,读取的是RDR寄存器的值,对它的写操作,实际上是写到TDR寄存器的;当向该寄存器写数据的时候,串口就会自动发送,当收到收据的时候,也是存在该寄存器内。
接收与传送过程数据框图
如下图,在这个过程我们其实操作的只有USART_DR
寄存器剩下的有硬件来完成
USART_DR寄存器只有8位,因此1次只能接收8位,1个字节的数据,且接收导数据需要立即将其从DR寄存器读出,否则后续的数据会自动传至DR寄存器,覆盖原有数据,造成数据丢失,所以通常使用中断方式进行数据接收
UART的驱动
确定使用的串口号及对应的引脚
STM32F103ZET6共有3个USART,2个UART,这些数据可在选型手册获取,也可在对应芯片的数据手册的第一页获取,不过在USART的描述上似乎不太准确,这边直接说有5个USART显然是不准确的
查看参考手册8.3.3小节,可得到USART的复用功能重映射,下面以USART1为例
不过此处可能只有USART1-3的引脚,UART4,UART5没有,需要查看数据手册,手动搜索获得,值得注意的是在F103ZET6的数据手册的翻译版中由于翻译错误,将UART4,UART5写作USART4,USART5,因此需要搜索USART4,USART5,英文原版没有这个错误
USART的中断类型
USART1==只有一个中断向量==,但是可以通过配置USART的寄存器来选择不同的中断,也就是说可以将多个事件都链接到这个中断向量上。可以查看参考手册25.4章节
在HAL库stm32f1xx_hal_uart.h进行了宏定义
USART的中断触发机制
具体的可以查看USART_CR1寄存器,正如手册所描述的设置对应位为1,就会触发USART中断向量,调用服务函数
以接收中断为例,当USART_SR中的RXNE为’1’时,产生USART中断
而USART_SR是状态寄存器,描述如下
当RDR移位寄存器中的数据被转移到USART_DR寄存器中,该位被硬件置位。如果 USART_CR1寄存器中的RXNEIE为1,则产生中断。
USART1的驱动与回调机制
初始化与回调
HAL_UART_Init()函数会调用HAL_UART_MspInit()
只得注意的是,这里有两个缓存区
一个是给HAL库接收函数用的
g_rx_buffer
,缓存大小为1字节,只是为了在接收到数据后直接从DR寄存器将数据取出来存到这个缓存区另一个是我们真正处理数据用的数据缓存
g_usart_rx_buf
,缓存大小一般大于1字节,会在g_rx_buffer
中的数据合法的情况下将数据存入g_usart_rx_buf
之所以这样是因为,数据接收需要协议,HAL库的
UART_Receive_IT
函数只是单纯的将数据进行读取,不涉及到到数据协议,我们需要区分一次数据读取是否完成,就需要有数据帧的概念,即如何去判断一帧数据的完结,这就涉及到数据协议的概念,在回调函数HAL_UART_RxCpltCallback
中就是根据数据协议的要求将数据存到g_usart_rx_buf
中
HAL_UART_Init()
:该函数是针对应用程序层的,用于对UART外设进行配置和初始化。它会根据用户传入的参数对UART的各个寄存器进行配置,包括波特率、数据位、停止位、校验等参数。HAL_UART_MspInit()
:该函数是针对底层的,用于初始化UART外设的底层硬件资源,如GPIO、DMA、NVIC等。它会对UART所需要的所有底层硬件资源进行初始化、配置和启用,以确保UART能够正常工作。
因此,HAL_UART_Init()
和HAL_UART_MspInit()
两个函数本质上是不同的。HAL_UART_MspInit()
函数在HAL_UART_Init()
函数中被调用,确保底层硬件资源的正常初始化。这种分离的设计可以使得应用程序层与底层硬件资源之间进行解耦,从而提高代码的可维护性和可移植性。
值得注意的是:
1、开启接收中断
1.首先判断是否已经在进行接收操作,如果是,则返回
HAL_BUSY
,否则继续执行。 2.检查pData
和Size
参数是否为空或为0,如果是,则返回HAL_ERROR
。 3.对UART进行加锁(因为是多任务环境下,可能会有多个任务同时在使用UART,需要进行加锁)。 4.将用户提供的接收缓冲区指针pData
和缓冲区大小Size
存到UART句柄结构体变量的成员变量中。这个缓存区大小需要根据需求自行设置 5.清除错误码,并将接收状态设置为
HAL_UART_STATE_BUSY_RX
。 6.解锁UART。 7.使能UART的奇偶校验错误中断、帧错误中断、噪声错误中断、接收数据寄存器非空中断。 8.返回HAL_OK
。 该函数的主要作用是设置UART的接收参数,并使能相应的中断,等待数据到来。当数据到来时,会触发UART接收中断,中断服务程序会把接收到的数据存储到用户提供的接收缓冲区中,在中断服务程序执行完毕后,用户就可以在应用程序中读取接收缓冲区中的数据了。2、与之类似的,开启发送
中断与回调
HAL库的串口接收嵌套了较多层,关系比较复杂,
void USART1_IRQHandler(void)
是串口中断服务函数(这里宏定义重命名为USART_UX_IRQHandler
),是当发生串口中断后首先调用的函数
在中断调用
HAL_UART_IRQHandler(&UART1_Handler)
;这是一个HAL库函数,该函数是一个HAL库函数,在该函数中调用UART_Receive_IT(huart);
,在函数UART_Receive_IT
中会把数据保存在串口句柄的成员变量pRxBuffPtr
缓存中,同时RxXferCount
计数器减 1(其实我没找到减1的语句
)。如果我们设置RxXferSize
=10,那么当接收到 10 个字符之后,RxXferCount
会由 10 减到 0(RxXferCount
初始值等于RxXferSize
),这个时候再调用接收完成回调函数 HAL_UART_RxCpltCallback 进行处理。也就是说HAL_UART_RxCpltCallback是在中断接收完成之后处触发的。
总的来说用HAL库的回调去实现串口收发还是有点复杂,嵌套太多层,其实可以不使用这些回调函数,直接在中断向量服务函数实现自己的任务逻辑即可
不使用回调的串口中断服务
以下代码编写了一个接收协议,接收到的数据必须是0x0d(回车/r的ASCII码13,对应的控制字符CR) 0x0a(换行/n的ASCII码)结尾
主要使用到了__HAL_UART_GET_FLAG(HANDLE, FLAG)函数
这段代码是一个宏定义,用于检查串口(UART)外设的指定标志位是否被设置。
具体代码如下:
该宏有两个参数:
HANDLE 指定了串口句柄:
其实就是前面初始化USART1的时候实例化的UART_HandleTypeDef型的结构体
FLAG 参数可以取以下值之一:
UART_FLAG_CTS:CTS 变化标志(UART4 和 UART5 不支持)
UART_FLAG_LBD:LIN 中断检测标志
UART_FLAG_TXE:发送寄存器为空标志
UART_FLAG_TC:传输完成标志
UART_FLAG_RXNE:接收寄存器非空标志
UART_FLAG_IDLE:空闲线检测标志
UART_FLAG_ORE:溢出错误标志
UART_FLAG_NE:噪声错误标志
UART_FLAG_FE:帧错误标志
串口数据的发送
Last updated