STM32H7系列单片机串口DMA双区空闲中断接收方式(HAL库)

一、串口DMA接收配置

UART_HandleTypeDef huart1;
DMA_HandleTypeDef hdma_usart1_rx;
DMA_HandleTypeDef hdma_usart1_tx;
/* USART1 init function */

void MX_USART1_UART_Init(void)
{

  huart1.Instance = USART1;
  huart1.Init.BaudRate = 57600;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&huart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&huart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_DisableFifoMode(&huart1) != HAL_OK)
  {
    Error_Handler();
  }

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART1 DMA Init */
    /* USART1_RX Init */
    hdma_usart1_rx.Instance = DMA1_Stream0;
    hdma_usart1_rx.Init.Request = DMA_REQUEST_USART1_RX;
    hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_rx.Init.Mode = DMA_NORMAL;
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
    hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart1_rx);

    /* USART1_TX Init */
    hdma_usart1_tx.Instance = DMA1_Stream1;
    hdma_usart1_tx.Init.Request = DMA_REQUEST_USART1_TX;
    hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_tx.Init.Mode = DMA_NORMAL;
    hdma_usart1_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
    hdma_usart1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx);

    /* USART1 interrupt Init */
    HAL_NVIC_SetPriority(USART1_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */
  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspDeInit 0 */

  /* USER CODE END USART1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART1_CLK_DISABLE();

    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);

    /* USART1 DMA DeInit */
    HAL_DMA_DeInit(uartHandle->hdmarx);
    HAL_DMA_DeInit(uartHandle->hdmatx);

    /* USART1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(USART1_IRQn);
  /* USER CODE BEGIN USART1_MspDeInit 1 */

  /* USER CODE END USART1_MspDeInit 1 */
  }
}

DMA配置

void MX_DMA_Init(void)
{

    /* DMA controller clock enable */
    __HAL_RCC_DMA1_CLK_ENABLE();

    /* DMA interrupt init */
    /* DMA1_Stream0_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
    /* DMA1_Stream1_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
}

开启IDLE空闲中断并设置DMA接收缓冲区:

//开启空闲中断
__HAL_UART_CLEAR_IDLEFLAG(huart);
__HAL_UART_ENABLE_IT(huart,UART_IT_IDLE);

//设置DMA接收缓冲区及长度,接收长度应大于最大报文长度
HAL_UART_Receive_DMA(huart,buff,MESSAGELENTH);

在串口的中断函数中判断空闲中断,然后切换DMA接收缓冲区并重新启动,

void USART1_IRQHandler(void)
{
    if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET)  // 空闲中断标记被置位
    {
        __HAL_UART_CLEAR_IDLEFLAG(&huart1);  // 清除中断标记
      
        .... 处理函数
    }
    else 
    {
        HAL_UART_IRQHandler(&huart1);
    }
}

接收长度可以通过(MESSAGELENTH - __HAL_DMA_GET_COUNTER(huart->hdmarx))得出,MESSAGELENTH是最大接收长度,__HAL_DMA_GET_COUNTER(huart->hdmarx)是此句柄剩余接收长度,相减即得出实际接收长度。

<think>嗯,用户想了解STM32H7系列中如何用HAL实现串口DMA空闲中断,或者解决相关问题。首先,我得回忆一下STM32的UART和DMA的相关知识。HAL虽然封装了很多函数,但有时候配置起来还是容易出问题。 首先,空闲中断(Idle Interrupt)的作用是在串口检测到总线空闲时触发中断,通常用于接收不定长度的数据。结合DMA的话,可以自动接收数据,当空闲中断触发时,就知道一帧数据接收完了。这在处理如Modbus等协议时很有用。 那HAL中如何配置呢?可能需要先启用UART的空闲中断,然后配置DMA传输。步骤大概包括初始化UART,启用空闲中断,配置DMA接收,然后在中断处理函数中处理空闲事件。不过HAL本身可能没有直接处理空闲中断的函数,可能需要自己写回调或者覆盖某些处理函数。 接着,用户可能遇到哪些问题呢?比如空闲中断无法触发,DMA传输不停止,数据不完整,或者中断冲突。这时候需要检查中断是否使能,DMA配置是否正确,是否有清除中断标志位,或者优先级设置的问题。 然后想到参考内容中的优先级继承,虽然主要在内核层面,但可能在中断处理中也有优先级的问题需要注意。比如确保空闲中断的优先级设置合理,避免被其他中断阻塞。不过这部分可能更多是硬件配置的问题。 具体到实现步骤,可能需要先初始化UART,设置波特率等参数,然后使能空闲中断。接着配置DMA,设置内存地址、传输长度等。在中断服务函数中,检测是否是空闲中断,然后处理数据,重置DMA传输。同时要注意,HAL的中断处理可能需要用户自己添加空闲中断的处理,因为默认可能不包含。 可能还需要使用__HAL_UART_ENABLE_IT(&huart, UART_IT_IDLE)来使能空闲中断,然后在HAL_UART_IRQHandler中处理。或者使用回调函数,比如HAL_UARTEx_RxEventCallback,这个可能需要查看HAL的文档。 调试时,如果发现空闲中断没有触发,可能需要检查是否使能了全局中断,或者是否正确配置了NVIC。另外,DMA的配置是否正确,比如循环模式还是单次模式,传输完成中断是否影响等。 可能还需要注意缓存一致性问题,尤其是STM32H7有DCache,DMA传输的数据可能需要手动失效缓存,确保CPU读取的是最新数据。这点有时候容易被忽略,导致数据错误。 总结一下,步骤大概是配置UART和DMA,使能空闲中断,处理中断,处理数据,然后重置DMA。常见问题包括中断未使能、缓存问题、DMA配置错误等。需要仔细检查各个步骤的配置,并利用调试工具查看寄存器状态。</think>在STM32H7系列中,基于HAL实现串口DMA空闲中断需要结合空闲中断检测(IDLE)与DMA传输的特性。以下是具体实现步骤和注意事项: --- ### 一、实现步骤 1. **初始化UART与DMA配置** ```c UART_HandleTypeDef huart2; DMA_HandleTypeDef hdma_usart2_rx; // UART参数配置 huart2.Instance = USART2; huart2.Init.BaudRate = 115200; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; HAL_UART_Init(&huart2); // 绑定DMA到UART接收 __HAL_LINKDMA(&huart2, hdmarx, hdma_usart2_rx); hdma_usart2_rx.Instance = DMA1_Stream0; hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_usart2_rx.Init.Mode = DMA_CIRCULAR; // 循环模式或单次模式 HAL_DMA_Init(&hdma_usart2_rx); ``` 2. **启用空闲中断** ```c // 使能UART的空闲中断 __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); // 启动DMA接收 HAL_UART_Receive_DMA(&huart2, rx_buffer, BUFFER_SIZE); ``` 3. **重写中断处理函数** ```c void USART2_IRQHandler(void) { HAL_UART_IRQHandler(&huart2); // 调用HAL默认处理 // 检测空闲中断标志 if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)) { __HAL_UART_CLEAR_IDLEFLAG(&huart2); // 必须手动清除标志 // 自定义处理逻辑 uint16_t data_length = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx); process_received_data(rx_buffer, data_length); // 处理接收到的数据 HAL_UART_Receive_DMA(&huart2, rx_buffer, BUFFER_SIZE); // 重新启动DMA } } ``` --- ### 二、常见问题及解决方法 1. **空闲中断未触发** - 检查UART和DMA的中断使能状态,确保`UART_IT_IDLE`已启用。 - 确认NVIC中UART全局中断已配置优先级并启用。 2. **DMA传输数据不完整** - 使用`DMA_CIRCULAR`模式时需注意缓冲溢出问题。 - 对于STM32H7的缓存一致性,需在访问DMA接收缓冲前调用`SCB_InvalidateDCache_by_Addr()`[^1]。 3. **中断冲突或优先级问题** - 参考优先级继承机制,合理配置UART和DMA中断的优先级,避免被高优先级任务阻塞[^1]。 --- ### 三、优化建议 1. 使用`HAL_UARTEx_RxEventCallback()`替代手动中断处理,简化代码结构。 2. 结合缓冲(Double Buffer)减少数据处理延迟。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值