[HAL库] UART使用记录

标志位

  • USART_SR_TC:发送完成标志(Transmission Complete);

轮询收发

不带后缀的UART收发函数 HAL_UART_Transmit 、HAL_UART_Receive 是阻塞的,其内部调用了 UART_WaitOnFlagUntilTimeout ,来等待UART_FLAG_TXE(发送出数据)、UART_FLAG_RXNE(接收到数据)。而相对的 HAL_UART_Transmit_IT、HAL_UART_Transmit_DMA 等收发函数是非阻塞函数,因此实际使用时最好加上判断,比如在发送结束的中断回调函数 HAL_UART_TxCpltCallback 设置全局标志位。

以轮询串口接收函数为例:

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

该函数是阻塞接收定长数据,其中的 pData 是存储数据的地址(或者说缓存区的首地址),Size 表示需接收多少个字节的数据(定长),接收寄存器接收到的字节数目达到后才是完成接收。“接收”数据本身是由硬件自动完成(正确配置了UART之后),该函数只是将RDR寄存器的数据转存到 pData 。在程序,TDR和RDR占同一地址,即发送和接收同属一个数据寄存器(DR),硬件上则为不同的寄存器;

UART句柄(UART_HandleTypeDef)中有以下与发送数据、接收数据相关的成员:

  • RxXferSize:接收数据的大小(字节数,(接收函数内部赋值Size)
  • RxXferCount:(函数中赋值Size,随着接受字节而递减)

中断收发

以中断接收函数为例:

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

与轮询相比较,形参没有等待时间。pData 可以是缓存区数组首地址,依据 Size 存储串口接收到的数据。需要注意,只有接收到的字节数为 Size 时才算接收完毕,满足接收完成条件而进入 HAL_UART_RxCpltCallback 回调函数程序。接收结束后需要再次调用接收函数才能开启中断接收。

HAL库中断接收函数内部逻辑:

HAL_UART_Receive_IT(huart, pData, Size);
  UART_Start_Receive_IT(huart, pData, Size);
    __HAL_UART_ENABLE_IT(huart, UART_IT_ERR);
    __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);

HAL_UART_Receive_IT 函数内调用了 UART_Start_Receive_IT ,里面使能了RXNE、ERR等中断,相当于开启了接收中断。

需要注意的是,HAL库的串口中断处理函数内存在许多条件判断,想要正确通过 HAL_UART_IRQHandler 调用 HAL_UART_RxCpltCallback ,最好是要通过 HAL_UART_Receive_IT 来启用接收中断

DMA收发

从HAL库DMA接收相关函数节选一些来初步解释内部逻辑:

/*DMA接收函数*/
HAL_UART_Receive_DMA(huart, pData, Size);
  UART_Start_Receive_DMA(huart, pData, Size);    //开启DMA接收
    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt;    //设置结束传输回调函数
    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t *)tmp, Size);  //开启DMA中断

/*DMA接收完成回调函数*/
static void UART_DMAReceiveCplt(DMA_HandleTypeDef *hdma);
  UART_HandleTypeDef *huart = (UART_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;  //通过Parent找到与DMA数据流绑定的串口句柄
  HAL_UART_RxCpltCallback(huart);    //调用串口接收完成回调函数(由用户自定义功能)

/*DMA中断服务函数*/
void HAL_DMA_IRQHandler(DMA_HandleTypeDef *hdma);
  if(hdma->XferCpltCallback != NULL){
    hdma->XferCpltCallback(hdma);  //调用传输完成中断(除此以外还有传输半完成中断)
  }

串口的函数 HAL_UART_Receive_DMA 需要通过 huart->hdmarx 使用相关的DMA句柄,DMA的函数

另外,可以看到接收函数调用了 HAL_DMA_Start_IT ,相当于中断发出后HAL库内部进行DMA转运。因此使用DMA传输需要配置DMA的NVIC,(最好)调用 HAL_DMA_IRQHandler 以正确处理HAL库中断相关内容。

需要注意,调用传输函数 HAL_UART_Transmit_DMA 时很可能会出现 huart->gState != HAL_UART_STATE_READY 的情况,使得函数无法运行,具体原因不明确,可尝试直接调用之前手动置 HAL_UART_STATE_READY 。

IDLE中断

利用空闲中断,可实现不定长数据的接收。实现需要:

  • 配置、开启UART中断,调用串口中断服务函数;
  • 使用 HAL_UARTEx_ReceiveToIdle_DMA 来接收数据,其中形参 Size 表示可接收的最大字长,而非定长;
  • 接收结束回调函数为 HAL_UARTEx_RxEventCallback ;

posted on 2025-01-09 06:57  fhbui  阅读(30)  评论(0编辑  收藏  举报