文章目录
一、UART简介
1.1 串口通信简介
一般情况下,设备之间的通信方式可以分成并行通信和串行通信两种,它们的区别是:
通信方式 | 并行通信 | 串行通信 |
---|---|---|
传输原理 | 数据各个位同时传输 | 数据按位顺序传输 |
优点 | 传输速度快 | 占用引脚资源少 |
缺点 | 占用引脚资源多 | 传输速度相对较慢 |
在集成电路芯片的内部、同一硬件板上各部件之间等(比如指令总线、数据总线、系统总线等)的数据传送通常采用并行通信方式;硬件板与外接设备间为了减少引脚占用常采用串行通信方式,这里我们重点关注开发板与外界设备的通信,故继续看串行通信的分类:
串行通信 | 分类 | 特点 |
---|---|---|
工作模式 | 单工模式 半双工模式 全双工模式 |
通讯双方一方为发送端,另一方则固定为接收端,数据只能单向传输; 通讯双方既可以发射也可以接收,但是接收和发射不能同时进行; 双方既可以发射也可以接收,且能在同一时刻进行发送和接收操作; |
同步方式 | 同步通信 异步通信 |
带时钟同步信号传输,双方靠时钟同步,比如:SPI,IIC通信接口; 不带时钟同步信号,双方需规约好传输速率,比如:UART; |
在同步通讯中,数据信号所传输的内容绝大部分是有效数据,而异步通讯中会则会包含数据帧的各种标识符(用于数据同步的信号位,比如起始位、停止位、奇偶校验位等),所以同步通讯效率高,但是同步通讯双方的时钟允许误差小,稍稍时钟出错就可能导致数据错乱,异步通讯双方的时钟允许误差较大。
常见的串行通信接口汇总如下:
通信协议 | 引脚说明 | 工作模式 | 同步方式 |
---|---|---|---|
UART | TXD:发送端 RXT:接收端 GND:共地 |
全双工 | 异步通信 |
SPI | SCK:同步时钟 MISO:主机输入,从机输出 MOSI:主机输出,从机输入 |
全双工 | 同步通信 |
I2C / IIC | SCK:同步时钟 SDA:数据输入/输出端 |
半双工 | 同步通信 |
1.2 USART简介
USART全称为Universal Synchronous Asynchronous Receiver and Transmitter,通用同步-异步接收发射器,怎么既同步又异步呢?实际上USART既支持同步通信也支持异步通信,如果作为异步通信就是UART(Universal Asynchronous Receiver and Transmitter),作为同步通信相比UART多了一根时钟同步信号线,USART相当于UART的增强版,二者的关系如下:
由上图可以看出USART以同步方式通信需要时钟同步信号,但不需要额外的起始、停止位,可以实现更快的传输速度。但USART的支持范围更有限,后面我们主要介绍其作为异步通信也即UART通信。
串口通讯的数据包由发送设备通过自身的TXD接口传输到接收设备的RXD接口,通讯双方的数据包格式要规约一致才能正常收发数据。UART串口通信的数据包以帧为单位,常用的帧结构为:1位起始位+8位数据位+1位奇偶校验位(可选)+1位停止位,如下图所示:
奇偶校验位分为奇校验和偶校验两种,是一种简单的数据误码校验方法。奇校验是指每帧数据中,包括数据位和奇偶校验位的全部9个位中1的个数必须为奇数;偶校验是指每帧数据中,包括数据位和奇偶校验位的全部9个位中1的个数必须为偶数。
STM32L475中USART结构框图如下图所示:
这个框图分成上、中、下三个部分。本文大概讲述一下各个部分的内容,具体的可以看《SMT32L475VE Reference manual》中的描述。
框图的上部分,数据从RX进入到接收移位寄存器,后进入到接收数据寄存器,最终供CPU或者DMA来进行读取;数据从CPU或者DMA传递过来,进入发送数据寄存器,后进入发送移位寄存器,最终通过TX发送出去(感觉上面框图箭头有误)。
框图的中间部分,UART的发送移位寄存器和接收移位寄存器分别由发送控制器和接收控制器来进行控制,而发送控制器和接收控制器主要受硬件流控信号和时钟信号控制。硬件流控nRTS / nCTS (Request To Send / Clear To Send,n表示低电平有效)可以控制数据发送、接收的速率,防止因数据缓冲区溢出而导致部分数据丢失的情况出现,在很多情况下为了减少引脚数量,都省去了硬件流控引脚而采用软件流控方式以达到同样的目的。针对USART的同步通信方式还有一个发送器时钟输出引脚CK,仅适用于同步模式。
框图的下部分,接收控制器、发送控制器都有一个进入的箭头,分别连接到接收器时钟、发送器时钟。也就是说,异步通信尽管没有时钟同步信号,但是在串口内部,是提供了时钟信号来进行控制的。接收器时钟和发送器时钟又被连接到同一个控制单元,也就是说它们共用一个波特率发生器。
上面提到了波特率,讲波特率之前首先了解一下通讯速率,通讯速率通常是以比特率来表示,即每秒钟传输的二进制位数,单位为比特每秒(bit/s)。容易和比特率混淆的概念是“波特率”,它表示每秒传输了多少码元。码元是通讯信号调制的概念,时间间隔相同的符号来表示一个二进制数字,这样的信号就称为码元。如常见的通讯传输中,用0V表示数字0,5V表示数字1,那么一个码元可以表示两种状态0和1,所以一个码元等于一个二进制比特位,此时波特率的大小与比特率一致;若传输中,有0V、2V、4V和6V分别表示00、01、10、11,那么每个码元可以表示四种状态,两个二进制比特位,所以码元数是二进制比特位数的一半,这个时候的波特率为比特率的一半。因为很多常见的通讯中一个码元都是表示两种状态,人们常常直接以波特率来表示比特率,其实二者是有区别的。 异步通讯由于没有时钟信号,所以两个通讯设备需要规约好波特率,即每个码元的长度,以便对信号进行解码,常见的波特率为4800,9600,115200。
我们的STM32L475开发板支持的串口模式及特征如下:
1.3 USART配置
前面介绍了UART数据帧的格式和波特率的概念,UART的初始化主要是确定数据帧格式和双方通信的波特率,下面先介绍UART初始化结构体的定义:
// Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_uart.h
/**
* @brief UART Init Structure definition
*/
typedef struct
{
uint32_t BaudRate; /*!< This member configures the UART communication baud rate.
The baud rate register is computed using the following formula:
LPUART:
=======
Baud Rate Register = ((256 * lpuart_ker_ckpres) / ((huart->Init.BaudRate)))
where lpuart_ker_ck_pres is the UART input clock (divided by a prescaler if applicable)
UART:
=====
- If oversampling is 16 or in LIN mode,
Baud Rate Register = ((uart_ker_ckpres) / ((huart->Init.BaudRate)))
- If oversampling is 8,
Baud Rate Register[15:4] = ((2 * uart_ker_ckpres) / ((huart->Init.BaudRate)))[15:4]
Baud Rate Register[3] = 0
Baud Rate Register[2:0] = (((2 * uart_ker_ckpres) / ((huart->Init.BaudRate)))[3:0]) >> 1
where uart_ker_ck_pres is the UART input clock (divided by a prescaler if applicable) */
uint32_t WordLength; /*!< Specifies the number of data bits transmitted or received in a frame.
This parameter can be a value of @ref UARTEx_Word_Length. */
uint32_t StopBits; /*!< Specifies the number of stop bits transmitted.
This parameter can be a value of @ref UART_Stop_Bits. */
uint32_t Parity; /*!< Specifies the parity mode.
This parameter can be a value of @ref UART_Parity
@note When parity is enabled, the computed parity is inserted
at the MSB position of the transmitted data (9th bit when
the word length is set to 9 data bits; 8th bit when the
word length is set to 8 data bits). */
uint32_t Mode; /*!< Specifies whether the Receive or Transmit mode is enabled or disabled.
This parameter can be a value of @ref UART_Mode. */
uint32_t HwFlowCtl; /*!< Specifies whether the hardware flow control mode is enabled
or disabled.
This parameter can be a value of @ref UART_Hardware_Flow_Control. */
uint32_t OverSampling; /*!< Specifies whether the Over sampling 8 is enabled or disabled, to achieve higher speed (up to f_PCLK/8).
This parameter can be a value of @ref UART_Over_Sampling. */
uint32_t OneBitSampling; /*!< Specifies whether a single sample or three samples' majority vote is selected.
Selecting the single sample method increases the receiver tolerance to clock
deviations. This parameter can be a value of @ref UART_OneBit_Sampling. */
#if defined(USART_PRESC_PRESCALER)
uint32_t ClockPrescaler; /*!< Specifies the prescaler value used to divide the UART clock source.
This parameter can be a value of @ref UART_ClockPrescaler. */
#endif /* USART_PRESC_PRESCALER */
} UART_InitTypeDef;
/** @defgroup UART_Stop_Bits UART Number of Stop Bits
*/
#define UART_STOPBITS_0_5 USART_CR2_STOP_0 /*!< UART frame with 0.5 stop bit */
#define UART_STOPBITS_1 0x00000000U /*!< UART frame with 1 stop bit */
#define UART_STOPBITS_1_5 (USART_CR2_STOP_0 | USART_CR2_STOP_1) /*!< UART frame with 1.5 stop bits */
#define UART_STOPBITS_2 USART_CR2_STOP_1 /*!< UART frame with 2 stop bits */
/** @defgroup UART_Parity UART Parity
*/
#define UART_PARITY_NONE 0x00000000U /*!< No parity */
#define UART_PARITY_EVEN USART_CR1_PCE /*!< Even parity */
#define UART_PARITY_ODD (USART_CR1_PCE | USART_CR1_PS) /*!< Odd parity
/** @defgroup UART_Mode UART Transfer Mode
*/
#define UART_MODE_RX USART_CR1_RE /*!< RX mode */
#define UART_MODE_TX USART_CR1_TE /*!< TX mode */
#define UART_MODE_TX_RX (USART_CR1_TE |USART_CR1_RE) /*!< RX and TX mode */
/** @defgroup UART_Hardware_Flow_Control UART Hardware Flow Control
*/
#define UART_HWCONTROL_NONE 0x00000000U /*!< No hardware control */
#define UART_HWCONTROL_RTS USART_CR3_RTSE /*!< Request To Send */
#define UART_HWCONTROL_CTS USART_CR3_CTSE /*!< Clear To Send */
#define UART_HWCONTROL_RTS_CTS (USART_CR3_RTSE | USART_CR3_CTSE) /*!< Request and Clear To Send */
/** @defgroup UART_Over_Sampling UART Over Sampling
*/
#define UART_OVERSAMPLING_16 0x00000000U /*!< Oversampling by 16 */
#define UART_OVERSAMPLING_8 USART_CR1_OVER8 /*!< Oversampling by 8 */
/** @defgroup UART_OneBit_Sampling UART One Bit Sampling Method
*/
#define UART_ONE_BIT_SAMPLE_DISABLE 0x00000000U /*!< One-bit sampling disable */
#define UART_ONE_BIT_SAMPLE_ENABLE USART_CR3_ONEBIT /*!< One-bit sampling enable */
UART初始化结构体UART_InitTypeDef中的成员配置在CubeMX配置中会涉及,但多数可以使用默认配置,如果默认配置满足不了需求,则需要了解结构体成员有哪些配置选项,各配置选项有何意义。
前面介绍HAL库提供了更高的抽象,将每类外设抽象为一个句柄,对实例化句柄的操作相当于对该外设的操作,这就类似于C++中面向对象编程的思想。前面介绍GPIO更偏底层,只提供了初始化结构体,不需要句柄结构体,所以并没有介绍句柄结构体是怎样实现的?下面以UART为例介绍其句柄结构体定义如下:
// Drivers\STM32L4xx_HAL_Driver\Inc\stm32l4xx_hal_uart.h
/**
* @brief UART handle Structure definition
*/
typedef struct __UART_HandleTypeDef
{
USART_TypeDef *Instance; /*!< UART registers base address */
UART_InitTypeDef Init; /*!< UART communication parameters */
UART_AdvFeatureInitTypeDef AdvancedInit; /*!< UART Advanced Features initialization parameters */
uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */
uint16_t TxXferSize; /*!< UART Tx Transfer size */
__IO uint16_t TxXferCount; /*!< UART Tx Transfer Counter */
uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */
uint16_t RxXferSize; /*!< UART Rx Transfer size */
__IO uint16_t RxXferCount; /*!< UART Rx Transfer Counter */
uint16_t Mask; /*!< UART Rx RDR register mask */
#if defined(USART_CR1_FIFOEN)
uint32_t FifoMode; /*!< Specifies if the FIFO mode is being used.
This parameter can be a value of @ref UARTEx_FIFO_mode. */
uint16_t NbRxDataToProcess; /*!< Number of data to process during RX ISR execution */
uint16_t NbTxDataToProcess; /*!< Number of data to process during TX ISR execution */
#endif /*USART_CR1_FIFOEN */
void (*RxISR)(struct __UART_HandleTypeDef *huart); /*!< Function pointer on Rx IRQ handler */
void (*TxISR)(struct __UART_HandleTypeDef *huart); /*!< Function pointer on Tx IRQ handler */
DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters */
DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */
HAL_LockTypeDef Lock; /*!< Locking object */
__IO HAL_UART_StateTypeDef gState; /*!< UART state information related to global Handle management
and also related to Tx operations.
This parameter can be a value of @ref HAL_UART_StateTypeDef */
__IO HAL_UART_StateTypeDef RxState; /*!< UART state information related to Rx operations.
This parameter can be a value of @ref HAL_UART_StateTypeDef */
__IO uint32_t ErrorCode; /*!< UART Error code */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
void (* TxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Tx Half Complete Callback */
void (* TxCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Tx Complete Callback */
void (* RxHalfCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Rx Half Complete Callback */
void (* RxCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Rx Complete Callback */
void (* ErrorCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Error Callback */
void (* AbortCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Complete Callback */
void (* AbortTransmitCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Transmit Complete Callback */
void (* AbortReceiveCpltCallback)(struct __UART_HandleTypeDef *huart)