UART半双工通讯原理与设计实现 - 以航顺HK32F0301MxxxxC系列MCU为例
UART通讯协议简要说明
UART,即通用异步串行收发传输器(Universal Asynchronous Receiver/Transmitter),一般按照单片机的支持,可以配置为全双工或半双工通讯:
全双工会使用两条信号线,RX和TX,对于指定的一个全双工设备,TX为该设备的数据发送线,RX为数据接收线,一般需要交叉连接另一设备的TX和RX(也就是自己的发送线连接到对方的接收线,自己的接收线连接到对方的发送线)。
半双工只需要一条数据线,进行通讯的双方按照指定的一定标准,交替发送数据等,可以实现一条线上完成数据发送与接收的任务。
UART协议数据帧结构
一般UART的一帧数据包含以下几部分
- 启动位(Start Bit):一个低电平的比特,用于标识数据传输的开始;
- 数据位(Data Bits):包含要传输的数据的比特数,通常是5、6、7或8个比特;
- 奇偶校验位(Parity Bit):可选,用于验证数据的正确性。奇偶校验位的值可以是奇校验或偶校验,它们根据数据位中1的个数确定;
- 停止位(Stop Bit):一个时钟高电平的比特,用于标识数据传输的结束;
如下图展示了航顺MCU数据手册中的UART协议数据帧结构配图,注意这里面把奇偶校验位视为数据位的一部分。
UART半双工通讯的实现
这里简要展示一下UART通讯的几种情况,如下图所示:
(a)为单工通讯,规定好一方发送,另一方接收;
(b)为半双工通讯,双方都可以进行发送和接收,不过为了避免冲突,不能同步收发;
(c)为全双工通讯,双方通过两条信号线连接,可以同时收发。
如果希望使用UART进行半双工通讯,一般双方都使用TX引脚进行连接,此时一方需要使用上拉电阻置高电平(UART通讯默认情况下是高电平,拉低电平表示数据传输的开始)。
这时在软件中规定好双方的收发顺序等,正确配置通讯双方的发送、接收状态的切换,即可实现在一条数据线上完成双方的数据发送与接收。
半双工通讯设计
这里以航顺HK32F0301MxxxxC系列MCU为例进行半双工通讯的代码设计。
MCU硬件条件确认
首先我们需要确定MCU支持半双工通信,通过翻阅数据手册我们可以了解到航顺的该类型MCU支持UART的单线半双工通信。
另外,数据手册中有对UART单线半双工通信的进一步说明,需要仔细阅读。我们可以看到数据手册中明确说明TX需要配置为复用功能、开漏输出,并在外部接上拉电阻以拉高电平,这在最开始的硬件原理图设计中就要注意。
通讯设计思路
由于我们这里使用单线半双工通讯,双方的硬件条件都支持数据的接收与发送,那么如果软件上未做好设计,将可能出现双方同时准备发送数据的情况,也就是发生了抢占冲突的问题。
此时将会导致信号线电平不正确,通讯双方将无法接收到正确数据,我们有必要设计好双方的一个收发流程,保证数据传输的正确性。
这里简单提两种通讯设计思路:
- 主从设备:令一台设备作为主设备,另一台为从设备;从设备只有接收到主设备发送的数据后,才会开始传递数据给主设备,主设备在规定时间内收到后,可以按照需求继续发送数据或暂停等。此外主设备设计超时重发机制,在发送完数据后,若规定时间内未收到数据,判定为超时,重新进行发送以尝试建立通讯。
- 交替发送:通讯双方各自设定一个定时器,例如规定5ms进行一次定时器中断,在定时器中断触发时,进行数据发送,并在发送完毕之后切换回接收状态,这里要注意收发状态的修改,避免自发自收的情况出现。
此外,为了进一步保证收发数据的正确性,可以指定通讯协议帧头、帧尾,以及配合校验字节等确保完整接收。
代码实现
这里我们以主从设备模式为例,简要展示一下代码的实现。
首先是航顺UART的配置,我们需要使能UART外设的时钟,并设置好波特率、校验方式、停止位、数据位等,并且要注意我们这里需要打开UART的接收和发送,并使能半双工(UART_HalfDuplexCmd(UART1,ENABLE);
),最后使能接收中断。
这里还需要配置要复用为UART数据发送的引脚,并配置为开漏输出,这里不做展示。
void UART_Configurature(void){
UART_InitTypeDef m_usart;
RCC_APBPeriph2ClockCmd(RCC_APBPeriph2_UART1, ENABLE); // 使能UART1时钟
m_usart.UART_BaudRate = 9600; // 波特率9600
m_usart.UART_Mode = UART_Mode_Rx| UART_Mode_Tx; // 使能接收和发送
m_usart.UART_Parity = UART_Parity_Even; // 偶校验
m_usart.UART_StopBits = UART_StopBits_1; // 1位停止位
m_usart.UART_WordLength = UART_WordLength_9b; // 9位数据位
UART_Init(UART1, &m_usart);
UART_HalfDuplexCmd(UART1,ENABLE); // 半双工
UART_Cmd(UART1, ENABLE); // 使能UART1
UART_ITConfig(UART1, UART_IT_RXNE, ENABLE); // 配置接收中断
NVIC_SetPriority(UART1_IRQn,0); // 设置优先级
NVIC_EnableIRQ(UART1_IRQn); // 使能中断
}
这里我们简单来看一下UART_HalfDuplexCmd(UART1,ENABLE);
函数的含义,找到航顺官方提供的库函数源文件中该函数的实现,可以看到在使能半双工时,将会把UART对应寄存器CR3
的HDSEL
位置1,与前面数据手册中所提到的UART单线半双工通信一致。
/**
* @brief Enables or disables the UART's Half Duplex communication.
* @param UARTx: Select the UART or the UART peripheral.
* This