STM32G707HAL库配置串口DMA和IDEL空闲中断接收不定字长数据

STM32G070串口DMA&IDEL配置

0、概述

在配置STM32G070RBT6进行串口通信功能时,如果尝试开启空闲中断进行不定长数据接收时,串口一旦接收到数据,就会一直卡在串口中断,无法进行其他操作。配置空闲中断的方法是按照F系列的HAL库配置方法,本次使用的HAL版本是STM32Cube_FW_G0_V1.5.0。

1、解决方法

在查阅资料后,发现F4系列高版本的HAL库串口中断处理方法中,如果开启了串口空闲中断,中断回调函数里没有专门针对空闲中断处理的函数。按照HAL库给的方法,通过DMA接收数据并判断是否有空闲中断事件发生。其中,HAL库的中断函数代码参考如下:
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
  uint32_t isrflags   = READ_REG(huart->Instance->ISR);
  uint32_t cr1its     = READ_REG(huart->Instance->CR1);
  uint32_t cr3its     = READ_REG(huart->Instance->CR3);


  uint32_t errorflags;
  uint32_t errorcode;

···············省略一部分···············
/* Check current reception Mode :
     If Reception till IDLE event has been selected : */
//检查是否产生空闲中断事件
  if ((huart->ReceptionType == HAL_UART_RECEPTION_TOIDLE)
      && ((isrflags & USART_ISR_IDLE) != 0U)
      && ((cr1its & USART_ISR_IDLE) != 0U))
  {
        //清除空闲中断标志位
    __HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_IDLEF);


    /* Check if DMA mode is enabled in UART */
    //检查是否使用了串口DMA接收中断
    if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
    {
      /* DMA mode enabled */
      /* Check received length : If all expected data are received, do nothing,
         (DMA cplt callback will be called).
         Otherwise, if at least one data has already been received, IDLE event is to be notified to user */
      uint16_t nb_remaining_rx_data = (uint16_t) __HAL_DMA_GET_COUNTER(huart->hdmarx);
      if ((nb_remaining_rx_data > 0U)
          && (nb_remaining_rx_data < huart->RxXferSize))
      {
        /* Reception is not complete */
        huart->RxXferCount = nb_remaining_rx_data;


        /* In Normal mode, end DMA xfer and HAL UART Rx process*/
        if (HAL_IS_BIT_CLR(huart->hdmarx->Instance->CCR, DMA_CCR_CIRC))
        {
          /* Disable PE and ERR (Frame error, noise error, overrun error) interrupts */
          ATOMIC_CLEAR_BIT(huart->Instance->CR1, USART_CR1_PEIE);
          ATOMIC_CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);


          /* Disable the DMA transfer for the receiver request by resetting the DMAR bit
             in the UART CR3 register */
          ATOMIC_CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);


          /* At end of Rx process, restore huart->RxState to Ready */
          huart->RxState = HAL_UART_STATE_READY;
          huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;


          ATOMIC_CLEAR_BIT(huart->Instance->CR1, USART_CR1_IDLEIE);


          /* Last bytes received, so no need as the abort is immediate */
          (void)HAL_DMA_Abort(huart->hdmarx);
        }
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
        /*Call registered Rx Event callback*/
        huart->RxEventCallback(huart, (huart->RxXferSize - huart->RxXferCount));
#else
        /*Call legacy weak Rx Event callback*/
        //即使串口DMA没有接收完,产生空闲中断时,也会触发HAL_UARTEx_RxEventCallback回调函数
        HAL_UARTEx_RxEventCallback(huart, (huart->RxXferSize - huart->RxXferCount));
#endif /* (USE_HAL_UART_REGISTER_CALLBACKS) */
      }
      return;
    }
···············省略一部分···············

以上代码可以看出,新版本的HAL库将DMA接收中断和IDEL空闲中断结合起来用于接收不定长数据,当产生空闲中断时,HAL_UART_IRQHandler函数会检查是否使能了DMA接收中断:

Check received length : If all expected data are received, do nothing,
(DMA cplt callback will be called).
Otherwise, if at least one data has already been received, IDLE event is to be notified to user

从上面的解释可以看出,如果产生了空闲中断,检查DMA设定的数据长度是否接收满了,如果数据长度满了则会产生DMA中断,如果还没有接收满,但是已经有至少一字节数据被接收了,则会通过RxEventCallback回调函数通知用户来处理数据。

2、实验验证

2.1 STM32CubeMX配置
–>时钟配置:特别注意一定要对串口的时钟单独配置
在这里插入图片描述

–>串口DMA配置:Normal模式,其他默认即可
在这里插入图片描述

2.2 主要部分代码展示
main函数部分

  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */


  HAL_UARTEx_ReceiveToIdle_DMA(&huart2, RxBuf, RxBuf_SIZE);
  //_HAL_DMA_DISABLE_IT(&hdma_usart2_rx, DMA_IT_HT);
  //__HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);
 //__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);//不在主函数里使能空闲中断,程序也正常运行,建议不用添加
  /* USER CODE END 2 */

红色部分为使能串口接收中断和DMA&&空闲中断,HAL_UARTEx_ReceiveToIdle_DMA函数参数为串口句柄、数据缓存数组、DMA接收的最大长度
usart部分代码

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
    if (huart->Instance == USART2)
    {
        oldPos = newPos;  // Update the last position before copying new data

        /* If the data in large and it is about to exceed the buffer size, we have to route it to the start of the buffer
         * This is to maintain the circular buffer
         * The old data in the main buffer will be overlapped
         */
        if (oldPos+Size > MainBuf_SIZE)  // If the current position + new data size is greater than the main buffer
        {
            uint16_t datatocopy = MainBuf_SIZE-oldPos;  // find out how much space is left in the main buffer
            memcpy ((uint8_t *)MainBuf+oldPos, RxBuf, datatocopy);  // copy data in that remaining space


            oldPos = 0;  // point to the start of the buffer
            memcpy ((uint8_t *)MainBuf, (uint8_t *)RxBuf+datatocopy, (Size-datatocopy));  // copy the remaining data
            newPos = (Size-datatocopy);  // update the position
        }


        /* if the current position + new data size is less than the main buffer
         * we will simply copy the data into the buffer and update the position
         */
        else
        {
            memcpy ((uint8_t *)MainBuf+oldPos, RxBuf, Size);
            newPos = Size+oldPos;
        }
        /* start the DMA again */
        Uart2_SendStr((char*)RxBuf);
        HAL_UARTEx_ReceiveToIdle_DMA(&huart2, (uint8_t *) RxBuf, RxBuf_SIZE);
        __HAL_DMA_ENABLE_IT(&hdma_usart2_rx, DMA_IT_HT);


    }

    /****************** PROCESS (Little) THE DATA HERE *********************/

    /* Let's say we want to do if IDEL Event happend */
    
}

以上代码可以自定义内容,按照以上程序内容如果一次向DMA发送的数据超过了RxBuf_SIZE,新数据会把旧数据覆盖掉,但是!!!如果一次发送的数据量很大,memcpy拷贝函数操作时会越界,最好不要一次发送上百字节的较长数据流,或者将数据缓冲数组设置大一些,否则回调函数里数组越界将会有不可测的事情发生。

3、总结

还是没有在新版本HAL库中找到能够单独使用IDEL中断函数的用法,以上方法是使用了DMA和IDEL中断进行不定长数据的接收。

4、参考

  • 知乎: https://zhuanlan.zhihu.com/p/400728566
  • 网友博客教程: https://controllerstech.com/uart-dma-with-idle-line-detection/
  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
针对STM32G4HAL库串口空闲中断接收不定数据的问题,可以按照以下步骤进行解决: 1. 开启串口空闲中断,即在初始化串口时,将USART_CR1_REG中的USART_CR1_IDLEIE位设置为1。 2. 在串口空闲中断中,通过读取USART_ISR_REG寄存器中的位USART_ISR_RXNE和USART_ISR_IDLE来判断是接收数据还是空闲中断。 3. 如果是接收数据,则读取USART_RDR_REG寄存器中的数据,并将其存储到缓冲区中。 4. 如果是空闲中断,则通过计算接收到的数据长度来确定数据的长度,并将其存储到缓冲区中。 5. 在数据长度达到预定长度时,可以通过回调函数或者其他方式来通知数据接收已经完成。 下面是一个示例代码: ```c uint8_t rx_buffer[100]; uint8_t rx_counter = 0; uint8_t rx_length = 0; uint8_t rx_flag = 0; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USARTx) { if(rx_flag == 0) { //接收数据 if((__HAL_UART_GET_FLAG(huart, UART_FLAG_RXNE) != RESET) && (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) == RESET)) { rx_buffer[rx_counter++] = (uint8_t)(huart->Instance->RDR & 0x00FF); } //空闲中断 else if(__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET) { rx_length = rx_counter; rx_counter = 0; rx_flag = 1; } } } } ``` 在上面的代码中,当接收数据时,将数据存储到缓冲区中,并将计数器rx_counter加1。当空闲中断发生时,计算接收到的数据长度,并将其存储到rx_length中。在接收完成后,将rx_flag设置为1,表示数据接收已经完成。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值