串口通信概述

通用同步异步收发器 (USART Universal Synchronous Asynchronous Receiver/Transmitter) 能够灵活地与外部设备进行全双工数据交换,满足外部设备对 工业标准 NRZ 异步串行数据格式的要求。USART 通过小数波特率发生器提供了多种波特率。它支持同步单向通信和半双工单线通信;还支持 LIN(局域互连网络)、智能卡协议与 IrDA (红外线数据协会)SIR ENDEC 规范,以及调制解调器操作 (CTS/RTS)。而且,它还支持 多处理器通信。通过配置多个缓冲区使用 DMA 可实现高速数据通信。

串口通信接口概述

USART接口最多五个信号:

TX:串行输出信号

RX:并行输出信号

nCTS:允许发送信号(Clear to Send)低电平有效,是对方设备发来的信号。假设nCTS信号为低电平,则表示对方已经准备好接受数据,本机可以发送数据了。

nRTS:请求发送(Request to Send)低电平有效,是发送给对方设备的信号。假设,本机做好接受数据准备,nRTS置为低电平,通知对方可以发送数据。

SCLK:发送器输出的时钟信号,仅用于同步模式。

TX和RX是必须的,nCTS和nRTS为硬件流控制信号,在异步通信时,可以选择是否启用,在同步通信是,没有硬件流控制信号。

UART只有RX和TX。

串口通信软件协议

接口通过三个引脚从外部连接到其它设备。任何 USART 双向通信均需要至少两个引脚:接收数据输入引脚 (RX) 和发送数据引脚输出 (TX):

RX:接收数据输入引脚就是串行数据输入引脚。过采样技术可区分有效输入数据和噪声,从而用于恢复数据。

TX:发送数据输出引脚。如果关闭发送器,该输出引脚模式由其 I/O 端口配置决定。如果使 能了发送器但没有待发送的数据,则 TX 引脚处于高电平。在单线和智能卡模式下,该 I/O 用于发送和接收数据(USART 电平下,随后在 SW_RX 上接收数据)。

注:使用ttl或者RS232进行通讯是,一般需要共地,否则会出现传输乱码。485通信由于采用差分信号表示不同的逻辑信号,仅需要两根线即可。

TX 引脚在起始位工作期间处于低电平状态。在停止位工作期间处于高电平状态。

串口通信的基本参数如下:

数据位:8位或者9位

奇偶校验位:

停止位:1个或者两个停止位

波特率:串行数据传输的速率,单位bit/s

STM32F4中有一个过采样(over sampling)参数,可以设置位8次采样和16次采样。8次采样速度快,容错性差,16次采样速度慢,但容错性好。

常用的串口电路

TLL和CMOS电平通信

串口转RS232

串口转RS485

串口转USB

串口的HAL的驱动程序

常用功能的函数

串口通信的驱动文件stm32f4xx_hal_uart.h

 

HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)

设置串口通信参数

 

__weak void HAL_UART_MspInit(UART_HandleTypeDef *huart)

设置串口通信引脚配置以及中断位置

 

HAL_UART_StateTypeDef HAL_UART_GetState(UART_HandleTypeDef *huart)

uint32_t HAL_UART_GetError(UART_HandleTypeDef *huart)

获取串口状态

 

uint32_t HAL_UART_GetError(UART_HandleTypeDef *huart)

返回串口错误代码

 

HAL_UART_StateTypeDef HAL_UART_GetState(UART_HandleTypeDef *huart)

获取串口当前状态

阻塞方式传输

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout)

HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)

 

中断方式传输

HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

 

DMA方式传输

HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)

 

HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

 

HAL_StatusTypeDef HAL_UART_DMAPause(UART_HandleTypeDef *huart)

 

HAL_StatusTypeDef HAL_UART_DMAResume(UART_HandleTypeDef *huart)

 

HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart)

 

取消数据传输

HAL_StatusTypeDef HAL_UART_Abort(UART_HandleTypeDef *huart)

终止以DMA方式传输数据,函数自身以阻塞方式运行

HAL_StatusTypeDef HAL_UART_AbortTransmit(UART_HandleTypeDef *huart)

HAL_StatusTypeDef HAL_UART_AbortReceive(UART_HandleTypeDef *huart)

HAL_StatusTypeDef HAL_UART_Abort_IT(UART_HandleTypeDef *huart)

终止以DMA方式传输数据,函数自身以非阻塞方式运行

HAL_StatusTypeDef HAL_UART_AbortTransmit_IT(UART_HandleTypeDef *huart)

HAL_StatusTypeDef HAL_UART_AbortReceive_IT(UART_HandleTypeDef *huart)

     

HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

不同函数数据发送和接收方式说明

串口传输模式有两种:

  • 阻塞模式(blocking mode) 即轮询模式,在函数执行阶段,函数会一直执行,知道函数数据传输完成或者超时。
  • 非阻塞模式(non-blocking mode)即使用中断或者DMA方式进行传输。

阻塞模式函数说明 略

中断模式函数说明

  • 串口发送中断

HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)

其中pData是存放数据的指针,参数size是数据的长度。数据发送结束以后,会触发中断并且调用回调函数:

__weak void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)

若需要在完成数据以后进行操作可以重新定义回调函数

  • 串口接收中断

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

其中pData是存放数据的指针,参数size是数据的长度。数据发送结束以后,会触发中断并且调用回调函数:

__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

若需要在完成数据以后进行操作可以重新定义回调函数

注:这个函数执行一次只能接收固定长度的数据。在完成数据接收后悔自动关闭中断,不会再继续接受数据。这个函数是“一次性”的,若需要再次使用程接收,需要再次执行这个函数。但是,不能在回调函数中再次调用这个函数启动重复调用。原因?

例:串口通信

使用USART2进行串口通信,接口与RS232芯片进行连接,转化成233电平,通过RS232引脚引出。

  • Cubemx配置
    • 时钟配置 略
    • USART2 配置

模式配置,配置对应的波特率,字长度,停止位,校验位等参数。在NVIC Setting中打开串口中断。生成代码。

  • 代码配置

在完成初始化串口函数以后,即可使用HAL库的串口函数进行数据传送

数据发送代码

uint8_t text[]="test";

HAL_UART_Transmit(&huart2, text, sizeof(text), 100);

数据接收:在数据接收时,最好外部设备向接收端发送数据时,串口启动的方式较好。因此一般不采用轮训的方式进行串口接受的操作,而使用中断或者DMA的方式进行串口接收数据。DMA数据方式,在DMA中讲,本次只解释中断方式接收串口数据。

根据函数定义,HAL_UART_Receive_IT()需要指定串口号,接收数据存储位和接受数据长度。

uint8_t rxBuffer[50];需要指定接受数据存放的数组

在接受完成数据以后 HAL_UART_Receive_IT(&huart2, rxBuffer, 5)跳转到中断函数中,执行中断回调函数HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)。

注:看到很多人在HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)中重新调用HAL_UART_Receive_IT(&huart2, rxBuffer, 5) 。但不知道会出现什么问题。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

{

for(int i=0;i<5;i++)

{

proBuffer[i]=rxBuffer[i];

}

HAL_UART_Transmit(&huart2, proBuffer, 5, 100);

HAL_UART_Receive_IT(&huart2, rxBuffer, 5);

}

该中断回调函数 实现的是将接受到的数据完成传输后发送回串口。在接受数据满数据长度 5 时触发中断。代码存疑

方法2 使用串口空闲中断发送数据——指定数据长度

使用串口空闲中断进行函数重启的操作

在开启空闲中断以后,串口中断完成时,立即清除空闲中断标志位。因为串口经常处于空闲状态,会非常占用处理器时间。

 1 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
 2 {
 3 
 4         rxCompleted = SET;
 5         for(uint16_t i=0;i<RX_CMD_LEN;i++)
 6         {
 7             proBuffer[i]=rxBuffer[i];
 8 
 9         }
10         __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);
11 }
12 void on_UART_IDLE(UART_HandleTypeDef *huart)
13 {
14     if(__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) ==RESET)
15     {
16         return;
17     }
18     __HAL_UART_CLEAR_IDLEFLAG(huart);
19     __HAL_UART_DISABLE_IT(huart,UART_IT_IDLE);
20     if(rxCompleted)
21     {
22         HAL_UART_Transmit(&huart2,proBuffer,5,100);
23         rxCompleted = RESET;
24         HAL_UART_Receive_IT(huart, rxBuffer, RX_CMD_LEN);
25     }
26 }

 

在中断函数中添加空闲中断的处理

 1 void USART2_IRQHandler(void)
 2 
 3 {
 4 
 5 /* USER CODE BEGIN USART2_IRQn 0 */
 6 
 7 /* USER CODE END USART2_IRQn 0 */
 8 
 9 HAL_UART_IRQHandler(&huart2);
10 
11 /* USER CODE BEGIN USART2_IRQn 1 */
12 
13 on_UART_IDLE(&huart2);
14 
15 /* USER CODE END USART2_IRQn 1 */
16 
17 }