STM32H7 DMA USART空闲中断接收不定长数据

本文详细介绍了STM32H7系列微控制器的USART模块配置,包括初始化、中断处理和DMA传输。通过实例展示了如何设置关键配置,编写了驱动程序及中断服务例程,实现了串口数据的接收和发送,以及与DMA的高效配合。

1.关键设置

 

2.代码

2.1 bsp_usart.h

/***********************************************************************************
bsp_usart.h
***********************************************************************************/
#ifndef __BSP_USART_H__
#define __BSP_USART_H__

#include "stm32h7xx_hal.h"
#include "main.h"



#define RX_LEN 1024  

typedef struct  
{  
    uint8_t  RX_flag:1;        //IDLE receive flag
    uint8_t  TX_flag:1;        //IDLE send flag
    uint16_t RX_Size;          //receive length
    uint8_t  RX_pData[RX_LEN]; //DMA receive buffer
}Usart_ReciveType;  

extern Usart_ReciveType UsartType; 

void EnableUsart_IT(void);
void Analysis_Serial_Data(void);
void UsartReceive_IDLE(UART_HandleTypeDef *huart);
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);

#endif

 2.2 bsp_usart.c

/***********************************************************************************
bsp_usart.c
***********************************************************************************/
#include "bsp_usart.h"
#include "stm32h7xx_hal.h"
#include "main.h"
#include  <stdarg.h>
#include  <stdio.h>
#include  <stdlib.h>

Usart_ReciveType UsartType;

/*****************************************************************************
 * 函 数 名  : EnableUsart_IT
 * 函数功能  : 使能串口中断
 * 输入参数  : 
 * 返 回 值  : 
 * 其    它  : 
*****************************************************************************/
void EnableUsart_IT(void)  
{  
   __HAL_UART_CLEAR_IDLEFLAG(&huart1);
	HAL_UART_Receive_DMA(&huart1,UsartType.RX_pData,RX_LEN);
	__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
}

/*****************************************************************************
 * 函 数 名  : UsartReceive_IDLE
 * 函数功能  : 串口接受空闲中断处理函数
 * 输入参数  : UART_HandleTypeDef *huart  串口句柄
 * 返 回 值  : 
 * 其    它  : 
*****************************************************************************/
void UsartReceive_IDLE(UART_HandleTypeDef *huart)  
{  
    uint32_t temp;  

    if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))  
    {   
        __HAL_UART_CLEAR_IDLEFLAG(huart);  
        HAL_UART_DMAStop(huart);  
        temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
        if(huart->Instance==USART1)
        {
            UsartType.RX_Size = RX_LEN - temp;   
            UsartType.RX_flag = 1;  
            HAL_UART_Receive_DMA(huart,UsartType.RX_pData,RX_LEN);  
        }
    } 
}

/*****************************************************************************
 * 函 数 名  : Analysis_Serial_Data
 * 函数功能  : 串口接收数据直接转发函数
 * 返 回 值  : void
 * 其    它  : 
*****************************************************************************/
 void Analysis_Serial_Data(void)
 {
     if(UsartType.RX_flag)         
     {    
         UsartType.RX_flag=0;      
         HAL_UART_Transmit_DMA(&huart1, UsartType.RX_pData, UsartType.RX_Size);
     }
 }

 /*****************************************************************************
 * 函 数 名  : HAL_UART_TxCpltCallback
 * 函数功能  : 串口发送完成回调函数
 * 输入参数  : UART_HandleTypeDef *huart  串口操作句柄
 * 返 回 值  : 
 * 其    它  :   使用DMA时需要将发送完成状态更新,否则DMA会出现bug
*****************************************************************************/
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    huart->gState = HAL_UART_STATE_READY;
}
 /*****************************************************************************
 * 函 数 名  : fputc
 * 函数功能  : 实现printf函数重定向到串口1,即支持printf信息到USART1
 * 输入参数  : 
 * 返 回 值  : 
 * 其    它  :
*****************************************************************************/
int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
  return ch;
}

 2.3 main.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ****************************************************************************
<think> 我们参考上述引用,特别是引用[3]和[4]中提到了使用STM32H743的串口DMA空闲中断来实现接收和发送不定长数据。下面将结合HAL库进行说明。 实现思路: 1. 发送:使用DMA发送,可以避免CPU等待,提高效率。 2. 接收:使用DMA接收不定长数据,结合空闲中断(IDLE)来判断一帧数据接收完成。 步骤: 一、CubeMX配置(以USART2为例,引用[3]): 1. 使能USART2,选择异步模式。 2. 使能USART2全局中断(NVIC Settings中使能USART2中断)。 3. 配置DMA: - 添加USART2_RX的DMA流(注意通道选择,根据数据手册选择对应的DMA和流),配置为循环模式(CIRCULAR),外设到内存,数据宽度为字节。 - 添加USART2_TX的DMA流,配置为正常模式(NORMAL),内存到外设,数据宽度为字节。 4. 使能串口空闲中断(在USART2的高级配置中,勾选空闲中断)。 二、代码实现: 1. 在main函数中启动接收(启动DMA接收,并开启空闲中断): ```c // 启动串口DMA接收,设置接收缓冲区和长度 HAL_UART_Receive_DMA(&huart2, rx_buffer, BUFFER_SIZE); // 使能空闲中断 __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); ``` 2. 在串口中断服务函数中处理空闲中断: ```c void USART2_IRQHandler(void) { // 处理空闲中断 if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) != RESET) { __HAL_UART_CLEAR_IDLEFLAG(&huart2); // 清除空闲中断标志 // 计算接收到的数据长度 // 公式:总长度 - DMA剩余未传输的数据量 uint16_t rx_len = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx); // 在这里处理接收到的数据,rx_buffer中前rx_len个字节是有效数据 // 重新启动DMA接收(因为DMA在循环模式下不需要重新启动,但如果是非循环模式,则需要重新配置) // 如果是循环模式,则不需要重新启动,但是需要处理缓存和长度 // 注意:由于是循环模式,所以当数据很长超过BUFFER_SIZE时,可能会被覆盖,因此需要及时处理 // 如果是非循环模式,则需要重新启动(但通常接收不定长数据使用循环模式,这样可以避免频繁重启) // HAL_UART_Receive_DMA(&huart2, rx_buffer, BUFFER_SIZE); } // 其他中断处理... HAL_UART_IRQHandler(&huart2); } ``` 3. 发送数据:使用DMA发送 ```c HAL_UART_Transmit_DMA(&huart2, tx_buffer, tx_len); ``` 三、注意事项(引用[5]中提到的问题): 1. 对于STM32H7系列,由于有缓存(Cache),在DMA传输中需要注意数据一致性。当DMA接收数据后,CPU读取之前,可能需要无效数据缓存(Invalidate DCache),以确保读取的是内存中最新的数据(而不是缓存中的旧数据)。 在空闲中断处理函数中,读取接收数据之前,可以调用: ```c SCB_InvalidateDCache_by_Addr((uint32_t *)rx_buffer, BUFFER_SIZE); ``` 或者,可以在配置DMA时,将接收缓冲区设置为非缓存(Non-Cacheable)区域。 2. 如果只接收一次数据,需要检查是否因为缓存问题导致。引用[5]中提到加入无效缓存指令后重新开启接收。 3. DMA接收模式:循环模式(Circular)可以持续接收,不需要每次重新启动。但要注意缓冲区大小,防止溢出。如果一帧数据超过缓冲区大小,则需要进行分包处理。 四、HAL库回调函数(可选): 可以使用HAL库提供的回调函数机制。但是,空闲中断并没有直接的回调函数,所以需要在中断服务函数中自行处理。 另外,HAL库提供了DMA传输完成中断回调(HAL_UART_RxCpltCallback),但这里我们主要用空闲中断。 总结: 1. 配置串口和DMA(循环模式接收,正常模式发送),使能空闲中断。 2. 启动DMA接收。 3. 在空闲中断中,计算接收长度,处理数据,并注意缓存一致性问题(STM32H7需要处理Cache)。 4. 发送数据使用DMA发送函数。 以上方法可以实现高效、稳定的串口通信,特别适合处理不定长数据。 </think>### STM32H743 串口DMA发送与空闲中断接收实现方法 在STM32H743上实现串口DMA发送和DMA空闲中断接收不定长数据的关键步骤如下: #### 1. CubeMX配置 ```c // 引用[3]示例配置 USART2_Init(); DMA_RX_Init(); // 接收DMA配置为CIRCULAR模式 DMA_TX_Init(); // 发送DMA配置为NORMAL模式 __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE); // 使能空闲中断 ``` #### 2. 关键代码实现 ```c // 启动接收(主函数初始化阶段) HAL_UART_Receive_DMA(&huart2, rx_buf, BUFFER_SIZE); // 空闲中断处理(在stm32h7xx_it.c中) void USART2_IRQHandler(void) { if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)) { __HAL_UART_CLEAR_IDLEFLAG(&huart2); // 关键缓存同步操作[^5] SCB_InvalidateDCache_by_Addr((uint32_t*)rx_buf, BUFFER_SIZE); // 计算接收长度 uint16_t recv_len = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(hdma_usart2_rx); // 处理数据 process_data(rx_buf, recv_len); // 重新启动接收[^3] HAL_UART_Receive_DMA(&huart2, rx_buf, BUFFER_SIZE); } HAL_UART_IRQHandler(&huart2); } // DMA发送函数 void uart_dma_send(uint8_t* data, uint16_t len) { HAL_UART_Transmit_DMA(&huart2, data, len); // 引用[4] } ``` #### 3. 关键注意事项 1. **缓存一致性处理** STM32H7需在空闲中断中添加缓存无效化指令: `SCB_InvalidateDCache_by_Addr()`[^5] 2. **DMA模式选择** - 接收DMA:必须配置为`CIRCULAR`模式 - 发送DMA:配置为`NORMAL`模式即可 3. **空闲中断使能** ```c __HAL_UART_ENABLE_IT(&huart, UART_IT_IDLE); // 引用[3] ``` 4. **接收长度计算** ```c recv_len = buffer_size - __HAL_DMA_GET_COUNTER(hdma_rx); ``` #### 4. 配置要点总结 | 功能 | 配置要求 | |-------------|--------------------------| | 接收DMA模式 | CIRCULAR(循环模式) | | 发送DMA模式 | NORMAL(普通模式) | | 中断使能 | 空闲中断+串口全局中断 | | 缓存管理 | 必须处理Cache一致性[^5] | > 实现原理:DMA在后台持续搬运串口数据到缓冲区,当总线出现空闲时触发中断,此时计算实际接收数据长度并处理数据。发送时DMA直接搬运数据到串口发送寄存器,释放CPU资源[^1][^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值