一、UART通信协议
UART通用异步收发器(Universal Asynchronous Receiver and Transmitter)是STM32 上常用的串行通信外设,可以灵活地与外部设备进行全双工数据交换,需要注意区别:
【1】USART-通用同步异步收发器(Universal Synchronous Asynchronous Receiver and Transmitter),UART是在USART上裁减了同步通信(时钟同步)的串行通信。
【2】串行通信和并行通信区别,串行通信是指设备之间通过一根数据信号线,地线以及控制信号线,按数据位形式一位一位地传输数据的通讯方式,同一时刻只能传输一位(bit)数据;并行通讯:是指使用 8 根或更多()的数据线进行传输的通讯方式,可以同一时刻传输多个数据位的数据(有多少根线就可以一次传输多少bit数据)。
串行通信一般是以帧格式传输数据,即是一帧一帧的传输,每帧包含有起始信号、数据信息、校验信息(由我们自己设置)、停止信号。而每帧数据由二进制(0、1组成)转换为TTL电平对外发送,TTL电平中逻辑1采用2.4~5V电平标识,逻辑0采用0~0.5V电平标识。
串口通讯协议由启始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致(一样的起始位 数据 校验位 停止位)才能正常收发数据。
以传输一个字节数(8bit)为例,数据传输格式如下:
【1】数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一个逻辑 0 的数据位表示,而数据包的停止信号可由 0.5、1、1.5 或 2 个逻辑 1 的数据位表示:
1个停止位:停止位位数的默认值。
2个停止位:可用于常规USART模式、单线模式以及调制解调器模式。
0.5个停止位:在智能卡模式下接收数据时使用。
1.5个停止位:在智能卡模式下发送和接收数据时使用。
【2】起始位和校验位中间的数据内容为真正需要传输的有效内容。
【3】奇偶校验位,指的是停止位前一bit的采用0或1值来标识偶校验还是奇校验。
偶校验,校验位值为0时,要求起始位和校验位中间的数据内容包含'1'的个数为偶数个。
奇校验,校验位值为1时,要求起始位和校验位中间的数据内容包含'1'的个数为奇数个。
注:奇偶校验位也是不严禁的,如果起始位和校验位中间的数据内容出现多次错误,例如先发送一个奇校验的01001100的数据,但是接收方得到01111100的奇校验数据,认为是正确的,但其实是出错了,这种情况是存在的,但概率会很少,毕竟单字节数发送,同时出现两个以上数据位错误的概率是极少的。通常我们在实际使用UART通信是,都不设置奇偶检验。
二、UART数据收发原理
从数据转换层面上来说,UART数据收发主要涉及的几个寄存器,数据寄存器(Data Register)、控制寄存器(Control Register)、状态寄存器(Status Register)、波特率寄存器(BaudRate Register ),在HLA底层实现数据收发过程如下图。
2.1 数据寄存器UART_DR
数据寄存器UART_DR,包含了发送和接收数据,通常由两个寄存器组成,一个用于发送的TDR,一个接收数据用的RDR,两个寄存器均具有读写功能,UART_DR寄存器与控制器寄存器(UART_CR)通信。在STM32-HAL库中关于UART通信实现驱动在stm32*_hal_uart.c文件中,UART数据从缓存区转换到DR寄存器是有UART的注册函数UART_TxISR_8BIT或UART_RxISR_8BIT实现的。
而注册函数UART_TxISR_8BIT调用时由更上传的HAL发送函数HAL_UART_Transmit*来传递的,接收类似。
数据发送寄存器UART_TDR和数据接收寄存器UART_RDR在HLA库中是16bit宽度的存储区域。
2.2 控制寄存器UART_CR
控制寄存器UART_CR,由三个32bit宽度的数据组成(CR1、CR2、CR3),共96bit数据,每个BIT都具有明确定义。UART_CR会一次从UART_TDR读取8数据位宽度的数据,即一个字节数据,然后按位转换成TTL电平发送。
控制寄存器CR1、CR2、CR3的定义在stm32-HAL驱动实现中,是根据芯片型号不同,各个字段定义有所区别,因此CR1、CR2、CR3的每个bit定义存放在CMSIS(微控制器软件接口标准)和芯片序列相关的头文件内,例如stm32l496xx.h。
关于CR1、CR2、CR3的每个bit定义及相关数值含义可以在上述描述的头文件中查看,例如stm32l496xx.h的定义如下。
/****************** Bit definition for USART_CR1 register *******************/
#define USART_CR1_UE_Pos (0U)
#define USART_CR1_UE_Msk (0x1UL << USART_CR1_UE_Pos) /*!< 0x00000001 */
#define USART_CR1_UE USART_CR1_UE_Msk /*!< USART Enable */
#define USART_CR1_UESM_Pos (1U)
#define USART_CR1_UESM_Msk (0x1UL << USART_CR1_UESM_Pos) /*!< 0x00000002 */
#define USART_CR1_UESM USART_CR1_UESM_Msk /*!< USART Enable in STOP Mode */
#define USART_CR1_RE_Pos (2U)
#define USART_CR1_RE_Msk (0x1UL << USART_CR1_RE_Pos) /*!< 0x00000004 */
#define USART_CR1_RE USART_CR1_RE_Msk /*!< Receiver Enable */
#define USART_CR1_TE_Pos (3U)
#define USART_CR1_TE_Msk (0x1UL << USART_CR1_TE_Pos) /*!< 0x00000008 */
#define USART_CR1_TE USART_CR1_TE_Msk /*!< Transmitter Enable */
#define USART_CR1_IDLEIE_Pos (4U)
#define USART_CR1_IDLEIE_Msk (0x1UL << USART_CR1_IDLEIE_Pos) /*!< 0x00000010 */
#define USART_CR1_IDLEIE USART_CR1_IDLEIE_Msk /*!< IDLE Interrupt Enable */
#define USART_CR1_RXNEIE_Pos (5U)
#define USART_CR1_RXNEIE_Msk (0x1UL << USART_CR1_RXNEIE_Pos) /*!< 0x00000020 */
#define USART_CR1_RXNEIE USART_CR1_RXNEIE_Msk /*!< RXNE Interrupt Enable */
#define USART_CR1_TCIE_Pos (6U)
#define USART_CR1_TCIE_Msk (0x1UL << USART_CR1_TCIE_Pos) /*!< 0x00000040 */
#define USART_CR1_TCIE USART_CR1_TCIE_Msk /*!< Transmission Complete Interrupt Enable */
#define USART_CR1_TXEIE_Pos (7U)
#define USART_CR1_TXEIE_Msk (0x1UL << USART_CR1_TXEIE_Pos) /*!< 0x00000080 */
#define USART_CR1_TXEIE USART_CR1_TXEIE_Msk /*!< TXE Interrupt Enable */
#define USART_CR1_PEIE_Pos (8U)
#define USART_CR1_PEIE_Msk (0x1UL << USART_CR1_PEIE_Pos) /*!< 0x00000100 */
#define USART_CR1_PEIE USART_CR1_PEIE_Msk /*!< PE Interrupt Enable */
#define USART_CR1_PS_Pos (9U)
#define USART_CR1_PS_Msk (0x1UL << USART_CR1_PS_Pos) /*!< 0x00000200 */
#define USART_CR1_PS USART_CR1_PS_Msk /*!< Parity Selection */
#define USART_CR1_PCE_Pos (10U)
#define USART_CR1_PCE_Msk (0x1UL << USART_CR1_PCE_Pos) /*!< 0x00000400 */
#define USART_CR1_PCE USART_CR1_PCE_Msk /*!< Parity Control Enable */
#define USART_CR1_WAKE_Pos (11U)
#define USART_CR1_WAKE_Msk (0x1UL << USART_CR1_WAKE_Pos) /*!< 0x00000800 */
#define USART_CR1_WAKE USART_CR1_WAKE_Msk /*!< Receiver Wakeup method */
#define USART_CR1_M_Pos (12U)
#define USART_CR1_M_Msk (0x10001UL << USART_CR1_M_Pos) /*!< 0x10001000 */
#define USART_CR1_M USART_CR1_M_Msk /*!< Word length */
#define USART_CR1_M0_Pos (12U)
#define USART_CR1_M0_Msk (0x1UL << USART_CR1_M0_Pos) /*!< 0x00001000 */
#define USART_CR1_M0 USART_CR1_M0_Msk /*!< Word length - Bit 0 */
#define USART_CR1_MME_Pos (13U)
#define USART_CR1_MME_Msk (0x1UL << USART_CR1_MME_Pos) /*!< 0x00002000 */
#define USART_CR1_MME USART_CR1_MME_Msk /*!< Mute Mode Enable */
#define USART_CR1_CMIE_Pos (14U)
#define USART_CR1_CMIE_Msk (0x1UL << USART_CR1_CMIE_Pos) /*!< 0x00004000 */
#define USART_CR1_CMIE USART_CR1_CMIE_Msk /*!< Character match interrupt enable */
#define USART_CR1_OVER8_Pos (15U)
#define USART_CR1_OVER8_Msk (0x1UL << USART_CR1_OVER8_Pos) /*!< 0x00008000 */
#define USART_CR1_OVER8 USART_CR1_OVER8_Msk /*!< Oversampling by 8-bit or 16-bit mode */
#define USART_CR1_DEDT_Pos (16U)
#define USART_CR1_DEDT_Msk (0x1FUL << USART_CR1_DEDT_Pos) /*!< 0x001F0000 */
#define USART_CR1_DEDT USART_CR1_DEDT_Msk /*!< DEDT[4:0] bits (Driver Enable Deassertion Time) */
#define USART_CR1_DEDT_0 (0x01UL << USART_CR1_DEDT_Pos) /*!< 0x00010000 */
#define USART_CR1_DEDT_1 (0x02UL << USART_CR1_DEDT_Pos) /*!< 0x00020000 */
#define USART_CR1_DEDT_2 (0x04UL << USART_CR1_DEDT_Pos) /*!< 0x00040000 */
#define USART_CR1_DEDT_3 (0x08UL << USART_CR1_DEDT_Pos) /*!< 0x00080000 */
#define USART_CR1_DEDT_4 (0x10UL << USART_CR1_DEDT_Pos) /*!< 0x00100000 */
#define USART_CR1_DEAT_Pos (21U)
#define USART_CR1_DEAT_Msk (0x1FUL << USART_CR1_DEAT_Pos) /*!< 0x03E00000 */
#define USART_CR1_DEAT USART_CR1_DEAT_Msk /*!< DEAT[4:0] bits (Driver Enable Assertion Time) */
#define USART_CR1_DEAT_0 (0x01UL << USART_CR1_DEAT_Pos) /*!< 0x00200000 */
#define USART_CR1_DEAT_1 (0x02UL << USART_CR1_DEAT_Pos) /*!< 0x00400000 */
#define USART_CR1_DEAT_2 (0x04UL << USART_CR1_DEAT_Pos) /*!< 0x00800000 */
#define USART_CR1_DEAT_3 (0x08UL << USART_CR1_DEAT_Pos) /*!< 0x01000000 */
#define USART_CR1_DEAT_4 (0x10UL << USART_CR1_DEAT_Pos) /*!< 0x02000000 */
#define USART_CR1_RTOIE_Pos (26U)
#define USART_CR1_RTOIE_Msk (0x1UL << USART_CR1_RTOIE_Pos) /*!< 0x04000000 */
#define USART_CR1_RTOIE USART_CR1_RTOIE_Msk /*!< Receive Time Out interrupt enable */
#define USART_CR1_EOBIE_Pos (27U)
#define USART_CR1_EOBIE_Msk (0x1UL << USART_CR1_EOBIE_Pos) /*!< 0x08000000 */
#define USART_CR1_EOBIE USART_CR1_EOBIE_Msk /*!< End of Block interrupt enable */
#define USART_CR1_M1_Pos (28U)
#define USART_CR1_M1_Msk (0x1UL << USART_CR1_M1_Pos) /*!< 0x10000000 */
#define USART_CR1_M1 USART_CR1_M1_Msk /*!< Word length - Bit 1 */
/****************** Bit definition for USART_CR2 register *******************/
#define USART_CR2_ADDM7_Pos (4U)
#define USART_CR2_ADDM7_Msk (0x1UL << USART_CR2_ADDM7_Pos) /*!< 0x00000010 */
#define USART_CR2_ADDM7 USART_CR2_ADDM7_Msk /*!< 7-bit or 4-bit Address Detection */
#define USART_CR2_LBDL_Pos (5U)
#define USART_CR2_LBDL_Msk (0x1UL << USART_CR2_LBDL_Pos) /*!< 0x00000020 */
#define USART_CR2_LBDL USART_CR2_LBDL_Msk /*!< LIN Break Detection Length */
#define USART_CR2_LBDIE_Pos (6U)
#define USART_CR2_LBDIE_Msk (0x1UL << USART_CR2_LBDIE_Pos) /*!< 0x00000040 */
#define USART_CR2_LBDIE USART_CR2_LBDIE_Msk /*!< LIN Break Detection Interrupt Enable */
#define USART_CR2_LBCL_Pos (8U)
#define USART_CR2_LBCL_Msk (0x1UL << USART_CR2_LBCL_Pos) /*!< 0x00000100 */
#define USART_CR2_LBCL USART_CR2_LBCL_Msk /*!< Last Bit Clock pulse */
#define USART_CR2_CPHA_Pos (9U)
#define USART_CR2_CPHA_Msk (0x1UL << USART_CR2_CPHA_Pos) /*!< 0x00000200 */
#define USART_CR2_CPHA USART_CR2_CPHA_Msk /*!< Clock Phase */
#define USART_CR2_CPOL_Pos (10U)
#define USART_CR2_CPOL_Msk (0x1UL << USART_CR2_CPOL_Pos) /*!< 0x00000400 */
#define USART_CR2_CPOL USART_CR2_CPOL_Msk /*!< Clock Polarity */
#define USART_CR2_CLKEN_Pos (11U)
#define USART_CR2_CLKEN_Msk (0x1UL << USART_CR2_CLKEN_Pos) /*!< 0x00000800 */
#define USART_CR2_CLKEN USART_CR2_CLKEN_Msk /*!< Clock Enable */
#define USART_CR2_STOP_Pos (12U)
#define USART_CR2_STOP_Msk (0x3UL << USART_CR2_STOP_Pos) /*!< 0x00003000 */
#define USART_CR2_STOP USART_CR2_STOP_Msk /*!< STOP[1:0] bits (STOP bits) */
#define USART_CR2_STOP_0 (0x1UL << USART_CR2_STOP_Pos) /*!< 0x00001000 */
#define USART_CR2_STOP_1 (0x2UL << USART_CR2_STOP_Pos) /*!< 0x00002000 */
#define USART_CR2_LINEN_Pos (14U)
#define USART_CR2_LINEN_Msk (0x1UL << USART_CR2_LINEN_Pos) /*!< 0x00004000 */
#define USART_CR2_LINEN USART_CR2_LINEN_Msk /*!< LIN mode enable */
#define USART_CR2_SWAP_Pos (15U)
#define USART_CR2_SWAP_Msk (0x1UL << USART_CR2_SWAP_Pos) /*!< 0x00008000 */
#define USART_CR2_SWAP USART_CR2_SWAP_Msk /*!< SWAP TX/RX pins */
#define USART_CR2_RXINV_Pos (16U)
#define USART_CR2_RXINV_Msk (0x1UL << USART_CR2_RXINV_Pos) /*!< 0x00010000 */
#define USART_CR2_RXINV USART_CR2_RXINV_Msk /*!< RX pin active level inversion */
#define USART_CR2_TXINV_Pos (17U)
#define USART_CR2_TXINV_Msk (0x1UL << USART_CR2_TXINV_Pos) /*!< 0x00020000 */
#define USART_CR2_TXINV USART_CR2_TXINV_Msk /*!< TX pin active level inversion */
#define USART_CR2_DATAINV_Pos (18U)
#define USART_CR2_DATAINV_Msk (0x1UL << USART_CR2_DATAINV_Pos) /*!< 0x00040000 */
#define USART_CR2_DATAINV USART_CR2_DATAINV_Msk /*!< Binary data inversion */
#define USART_CR2_MSBFIRST_Pos (19U)
#define USART_CR2_MSBFIRST_Msk (0x1UL << USART_CR2_MSBFIRST_Pos) /*!< 0x00080000 */
#define USART_CR2_MSBFIRST USART_CR2_MSBFIRST_Msk /*!< Most Significant Bit First */
#define USART_CR2_ABREN_Pos (20U)
#define USART_CR2_ABREN_Msk (0x1UL << USART_CR2_ABREN_Pos) /*!< 0x00100000 */
#define USART_CR2_ABREN USART_CR2_ABREN_Msk /*!< Auto Baud-Rate Enable*/
#define USART_CR2_ABRMODE_Pos (21U)
#define USART_CR2_ABRMODE_Msk (0x3UL << USART_CR2_ABRMODE_Pos) /*!< 0x00600000 */
#define USART_CR2_ABRMODE USART_CR2_ABRMODE_Msk /*!< ABRMOD[1:0] bits (Auto Baud-Rate Mode) */
#define USART_CR2_ABRMODE_0 (0x1UL << USART_CR2_ABRMODE_Pos) /*!< 0x00200000 */
#define USART_CR2_ABRMODE_1 (0x2UL << USART_CR2_ABRMODE_Pos) /*!< 0x00400000 */
#define USART_CR2_RTOEN_Pos (23U)
#define USART_CR2_RTOEN_Msk (0x1UL << USART_CR2_RTOEN_Pos) /*!< 0x00800000 */
#define USART_CR2_RTOEN USART_CR2_RTOEN_Msk /*!< Receiver Time-Out enable */
#define USART_CR2_ADD_Pos (24U)
#define USART_CR2_ADD_Msk (0xFFUL << USART_CR2_ADD_Pos) /*!< 0xFF000000 */
#define USART_CR2_ADD USART_CR2_ADD_Msk /*!< Address of the USART node */
/****************** Bit definition for USART_CR3 register *******************/
#define USART_CR3_EIE_Pos (0U)
#define USART_CR3_EIE_Msk (0x1UL << USART_CR3_EIE_Pos) /*!< 0x00000001 */
#define USART_CR3_EIE USART_CR3_EIE_Msk /*!< Error Interrupt Enable */
#define USART_CR3_IREN_Pos (1U)
#define USART_CR3_IREN_Msk (0x1UL << USART_CR3_IREN_Pos) /*!< 0x00000002 */
#define USART_CR3_IREN USART_CR3_IREN_Msk /*!< IrDA mode Enable */
#define USART_CR3_IRLP_Pos (2U)
#define USART_CR3_IRLP_Msk (0x1UL << USART_CR3_IRLP_Pos) /*!< 0x00000004 */
#define USART_CR3_IRLP USART_CR3_IRLP_Msk /*!< IrDA Low-Power */
#define USART_CR3_HDSEL_Pos (3U)
#define USART_CR3_HDSEL_Msk (0x1UL << USART_CR3_HDSEL_Pos) /*!< 0x00000008 */
#define USART_CR3_HDSEL USART_CR3_HDSEL_Msk /*!< Half-Duplex Selection */
#define USART_CR3_NACK_Pos (4U)
#define USART_CR3_NACK_Msk (0x1UL << USART_CR3_NACK_Pos) /*!< 0x00000010 */
#define USART_CR3_NACK USART_CR3_NACK_Msk /*!< SmartCard NACK enable */
#define USART_CR3_SCEN_Pos (5U)
#define USART_CR3_SCEN_Msk (0x1UL << USART_CR3_SCEN_Pos) /*!< 0x00000020 */
#define USART_CR3_SCEN USART_CR3_SCEN_Msk /*!< SmartCard mode enable */
#define USART_CR3_DMAR_Pos (6U)
#define USART_CR3_DMAR_Msk (0x1UL << USART_CR3_DMAR_Pos) /*!< 0x00000040 */
#define USART_CR3_DMAR USART_CR3_DMAR_Msk /*!< DMA Enable Receiver */
#define USART_CR3_DMAT_Pos (7U)
#define USART_CR3_DMAT_Msk (0x1UL << USART_CR3_DMAT_Pos) /*!< 0x00000080 */
#define USART_CR3_DMAT USART_CR3_DMAT_Msk /*!< DMA Enable Transmitter */
#define USART_CR3_RTSE_Pos (8U)
#define USART_CR3_RTSE_Msk (0x1UL << USART_CR3_RTSE_Pos) /*!< 0x00000100 */
#define USART_CR3_RTSE USART_CR3_RTSE_Msk /*!< RTS Enable */
#define USART_CR3_CTSE_Pos (9U)
#define USART_CR3_CTSE_Msk (0x1UL << USART_CR3_CTSE_Pos) /*!< 0x00000200 */
#define USART_CR3_CTSE USART_CR3_CTSE_Msk /*!< CTS Enable */
#define USART_CR3_CTSIE_Pos (10U)
#define USART_CR3_CTSIE_Msk (0x1UL << USART_CR3_CTSIE_Pos) /*!< 0x00000400 */
#define USART_CR3_CTSIE USART_CR3_CTSIE_Msk /*!< CTS Interrupt Enable */
#define USART_CR3_ONEBIT_Pos (11U)
#define USART_CR3_ONEBIT_Msk (0x1UL << USART_CR3_ONEBIT_Pos) /*!< 0x00000800 */
#define USART_CR3_ONEBIT USART_CR3_ONEBIT_Msk /*!< One sample bit method enable */
#define USART_CR3_OVRDIS_Pos (12U)
#define USART_CR3_OVRDIS_Msk (0x1UL << USART_CR3_OVRDIS_Pos) /*!< 0x00001000 */
#define USART_CR3_OVRDIS USART_CR3_OVRDIS_Msk /*!< Overrun Disable */
#define USART_CR3_DDRE_Pos (13U)
#define USART_CR3_DDRE_Msk (0x1UL << USART_CR3_DDRE_Pos) /*!< 0x00002000 */
#define USART_CR3_DDRE USART_CR3_DDRE_Msk /*!< DMA Disable on Reception Error */
#define USART_CR3_DEM_Pos (14U)
#define USART_CR3_DEM_Msk (0x1UL << USART_CR3_DEM_Pos) /*!< 0x00004000 */
#define USART_CR3_DEM USART_CR3_DEM_Msk /*!< Driver Enable Mode */
#define USART_CR3_DEP_Pos (15U)
#define USART_CR3_DEP_Msk (0x1UL << USART_CR3_DEP_Pos) /*!< 0x00008000 */
#define USART_CR3_DEP USART_CR3_DEP_Msk /*!< Driver Enable Polarity Selection */
#define USART_CR3_SCARCNT_Pos (17U)
#define USART_CR3_SCARCNT_Msk (0x7UL << USART_CR3_SCARCNT_Pos) /*!< 0x000E0000 */
#define USART_CR3_SCARCNT USART_CR3_SCARCNT_Msk /*!< SCARCNT[2:0] bits (SmartCard Auto-Retry Count) */
#define USART_CR3_SCARCNT_0 (0x1UL << USART_CR3_SCARCNT_Pos) /*!< 0x00020000 */
#define USART_CR3_SCARCNT_1 (0x2UL << USART_CR3_SCARCNT_Pos) /*!< 0x00040000 */
#define USART_CR3_SCARCNT_2 (0x4UL << USART_CR3_SCARCNT_Pos) /*!< 0x00080000 */
#define USART_CR3_WUS_Pos (20U)
#define USART_CR3_WUS_Msk (0x3UL << USART_CR3_WUS_Pos) /*!< 0x00300000 */
#define USART_CR3_WUS USART_CR3_WUS_Msk /*!< WUS[1:0] bits (Wake UP Interrupt Flag Selection) */
#define USART_CR3_WUS_0 (0x1UL << USART_CR3_WUS_Pos) /*!< 0x00100000 */
#define USART_CR3_WUS_1 (0x2UL << USART_CR3_WUS_Pos) /*!< 0x00200000 */
#define USART_CR3_WUFIE_Pos (22U)
#define USART_CR3_WUFIE_Msk (0x1UL << USART_CR3_WUFIE_Pos) /*!< 0x00400000 */
#define USART_CR3_WUFIE USART_CR3_WUFIE_Msk /*!< Wake Up Interrupt Enable */
#define USART_CR3_UCESM_Pos (23U)
#define USART_CR3_UCESM_Msk (0x1UL << USART_CR3_UCESM_Pos) /*!< 0x02000000 */
#define USART_CR3_UCESM USART_CR3_UCESM_Msk /*!< USART Clock enable in Stop mode */
#define USART_CR3_TCBGTIE_Pos (24U)
#define USART_CR3_TCBGTIE_Msk (0x1UL << USART_CR3_TCBGTIE_Pos) /*!< 0x01000000 */
#define USART_CR3_TCBGTIE USART_CR3_TCBGTIE_Msk /*!< Transmission Complete Before Guard Time Interrupt Enable */
这里只说CR1、CR2、CR3主要的标识位。
【1】在CR1中:
1)UE位(0位)定义UART使能;
2)UESM位(1位)定义使能是否开启STOP模式;
3)RE位(2位)定义数据接收使能;
4)TE位(3位)定义数据发送使能;
5)M位(10位)定义数据字长度,0是一个起始位、8个数据位、n个停止位,1则表示一个起始位、9个数据位、n个停止位;
6)PCE位(12位)定义是否开启硬件检验控制使能奇偶校验,0是禁止校验控制,1是开启校验控制。
【2】在CR2中,STOP位(12、13位)标注停止位,就是前面所述的:
00-1个停止位:停止位位数的默认值。
01-0.5个停止位:在智能卡模式下接收数据时使用。
10-2个停止位:可用于常规USART模式、单线模式以及调制解调器模式。
11-1.5个停止位:在智能卡模式下发送和接收数据时使用。
【3】在CR3中,DMAR位(6bit)使能DMA接收,而DMAT位(7位)使能DMA发送。
2.3 波特率寄存器BRR
波特率寄存器BRR若是一个16位的存储空间用于存放分频除法因子UARTDIV,低4位存储小数部分,高12位存储整数部分。假设UART的时钟经过多次分频,总分频数是div,最终输出频率是fpclk,则波特率br=fpclk/(div*UARTDIV)。这个UARTDIV我们在cubeMX是不用做配置的,我们是配置了波特率、时钟分频及时钟频率后,生成输出代码时,在UART初始化时,自动根据波特率和时钟参数计算出UARTDIV写入到BRR寄存器。
如果BRR寄存器是32bit的存储空间,一般就是低8位存储小数部分,高24位存储整数部分。HLA库里面,目前采用的就是32bit的存储空间,即uint32_t UART_BRR。
现例如fpclk=80MHz,波特率br=115200,分频div=1(PCLK1到最终频率输出涉过程涉及的分频器),如下图所示
则UARTDIV = 80000000÷(115200*1)=694.44。整数部分直接写入高位部分,694=0X2B6,而小数部分是8个数据位存储的,相当于从整数借1(8bit,最大值是二进制11111111=0XFF)划分为256份,那么小数部分乘256再转16进制就是要填写的小数部分。0.444*256=113.64≈114=0X72。最终合并整数部分和小数部分,向BRR写入数据是0X2B672。
然后在调试中,查看串口BRR寄存器数值,也是0X2B672。
2.4 中断寄存器IR
如果开启了中断功能,还会涉及到中断寄存器IR使用,在控制寄存器CR、状态寄存器SR都会与其产生交互。中断寄存器IR又包含中断状态寄存器ISR、中断清除寄存器ICR。收发控制器会依据控制寄存器(CR1、CR2、CR3)配置的诸如发送完成中断TCIE、发送清零中断TXEIE等与中断寄存器打交道。
下面列出CR寄存器设计中断的配置:TXEIE(发送数据寄存器为空)、TCIE(发送完成)、IDLEIE(空闲线路)、RXNEIE(读取数据已准备或过载)、PEIE(奇偶校验错误)、CMIE(字符匹配)、RTOIE(超时)、EOBIE(块接收结束)、LBDIE(断路)、CTSIE(CTS标记)、WUFIE(唤醒)、EIE(各异常)等。
2.5 其他寄存器
UART串口通信还包含预分频寄存器、接收超时寄存器、请求寄存器等,在HLA库中体现为约定数据位宽度大小的存储区域。
/**
* @brief Universal Synchronous Asynchronous Receiver Transmitter
*/
typedef struct
{
__IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x00 */
__IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x04 */
__IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x08 */
__IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x0C */
__IO uint16_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x10 */
uint16_t RESERVED2; /*!< Reserved, 0x12 */
__IO uint32_t RTOR; /*!< USART Receiver Time Out register, Address offset: 0x14 */
__IO uint16_t RQR; /*!< USART Request register, Address offset: 0x18 */
uint16_t RESERVED3; /*!< Reserved, 0x1A */
__IO uint32_t ISR; /*!< USART Interrupt and status register, Address offset: 0x1C */
__IO uint32_t ICR; /*!< USART Interrupt flag Clear register, Address offset: 0x20 */
__IO uint16_t RDR; /*!< USART Receive Data register, Address offset: 0x24 */
uint16_t RESERVED4; /*!< Reserved, 0x26 */
__IO uint16_t TDR; /*!< USART Transmit Data register, Address offset: 0x28 */
uint16_t RESERVED5; /*!< Reserved, 0x2A */
} USART_TypeDef;
三、UART配置及HAL实现
3.1 UART配置梳理
在cubeMX中配置UART主要就是在模式下拉框中开启相关模式,进行串行通信一般就是选择异步模式,其他模式针对特定场景使用。
可以根据需要开启RS232流控制支出或RS485流控制支持,主要就是针对STM32板与RS232或RS485做连接并配合外设设备开启硬件流控时使用。
针对UART的参数设置,主要就是设置串行通信的波特率、数据长度、校验位、停止位。如前面叙述的波特率和时钟频率计算出除法因子存储在BRR寄存器内。校验位、停止位的配置最终体现在控制寄存器CR中,数据长度同样也最终体现到控制寄存器CR中USART_CR1_M/M0。
3.2 UART初始化梳理
cubeMX生成输出代码时,如果配置了给外设生成独立的.h/.c源文件是,串口外设的源码头文件及源文件分别在Core的Inc和Src目录下,输出代码usart.c会为每个独立的串口外设生成初始化函数,将在cubeMX配置的串口参数写入到串口参数变量中,并调用HLA串口初始化函数HAL_UART_Init。
注:HLA有4个UART相关初始化函数,HAL_UART_Init(), HAL_HalfDuplex_Init(), HAL_LIN_Init()and HAL_MultiProcessor_Init()。分别对应UART asynchronous、UART Half duplex、UART LIN mode、 UART multiprocessor 模式,就是前面模式下拉框配置的模式。
在HAL_UART_Init函数中主要实现三个主要功能,底层硬件初始化,配置一般参数、配置高级参数。底层硬件初始化就是调用HAL_UART_MspInit实现引脚配置、时钟设置、中断等相关功能,HAL_UART_MspInit在HLA库中默认是弱函数,CubeMX会根据界面配置生成真正HAL_UART_MspInit函数覆盖原来的函数,同样放置在usart.c源码文件中。
配置一般参数UART_SetConfig、配置高级参数UART_AdvFeatureConfig则会将前面已经设置到串口参数里的变量进一步写入到各个寄存器中,并进行合理校验。其实HLA串口通信最终还是通过操作寄存器实现的,只是HLA库帮开发这屏蔽调对串口相关寄存器的操作,只要调用上层HLA-API就可以。UART_SetConfig实现了对波特率、校验位、停止位以及其他参数等设置,该函数很大,更多细节请查看HLA源码阅读。
UART_AdvFeatureConfig实现了对串口高级特征设置,在CubeMX上配置的高级特征都能在该函数得到体现。
3.3 UART收发数据源码梳理
UART的HLA库其发送数据和接收数据的API分别是HAL_UART_Transmit*和HAL_UART_Receive*,以HAL_UART_Transmit_IT为例,其先将需要发送的数据写入到串口的缓存中,就是相当于前面所述的从MCU或系统总线到发送数据存BUFF区域。
然后通过判定数据长度参数来调用不同注册函数,将上面存储在串口缓存区域的数据转移到发送数据寄存器TDR中。
数据从串口缓存区域到发送寄存器TDR,并会在数据转移完成后关闭TXEIE使能(发送数据寄存器为空数据位)和开启TCIE使能(发送完成中断数据位):
硬件层面上,串口的收发控制器会根据寄存器各个数据位数值变更作出数据发送、数据接收以及控制寄存器CR上各个数据位设值动作,实现从软数据到硬电平的转换及发送。
而在数据接收方面,类似发送机制,首选调用HAL_UART_Receive*函数开启,类似注册及等待回调。程序主要是判定到CR寄存器上接收数据相关的数据位变更,从而调用接收数据的回调函数开启接收,实现由接收数据寄存器RDR到串口缓冲区域,再进而触发用户实现串口回调函数调用及存储数据到用户定义缓存的过程。