串口的空闲中断+DMA接收(附F4代码)

传统串口接受与发送:
串口接受一个很长的帧,接受帧时依靠串口中断每次只能传输8位,传一个帧要进入好多次中断,每次进入中断都要判断是否接收完毕。

DMA串口接收与发送:
1,电脑通过串口1给STM32F407芯片发送数据(不定长,按照645格式来),芯片根据串口接收中断接收到数据后,通过DMA将数据存储在内存。当检测到数据接受完毕,产生接收完成标志位置位。当407检测到这个中断标志位后从TX端向电脑发送这段数据。

任务拆分:
1.检测到key0按下,由TX发送已经存在存储器的数据到电脑(用DMA存储器->外设):
2.实现RX通过串口中断能正常接受电脑发送的帧,检测到key0按下,由TX发送到电脑用(用DMA存储器->外设):
3.实现RX通过串口(事件)能正常接受电脑发送的帧(DMA:外设->内存),检测到key0按下,由TX发送到电脑用DMA(存储器->外设):

设置关注博文即可访问,程序发布到GitHUB

一、DMA基础(STM32F4芯片)

DMA(Direct Memory Access)直接存储器访问,当外设到存储器、存储器到外设、存储器到存储器有传输任务时,可以在无需任何 CPU 操作的情况下通过 DMA 快速移动数据。这样节省的 CPU 资源可供其它操作使用。

  • 存储器—>存储器,内存间拷贝
  • 外设—>存储器,如uart、spi、i2c等总线接收数据过程
  • 存储器—>外设,如uart、spi、i2c等总线发送数据过程

1.STM32407的DMA主要特性

  • 每个 DMA 控制器有 8 个数据流,每个数据流有多达 8 个通道(或称请求)
  • DMA 数据流请求之间的优先级可用软件编程(4 个级别:非常高、高、中、低)
  • 独立的源和目标传输宽度(字节、半字、字):源和目标的数据宽度不相等时,DMA 自动封装/解封必要的传输数据来优化带宽。这个特性仅在 FIFO 模式下可用
  • 对源和目标的增量或非增量寻址
  • 支持 4 个、8 个和 16 个节拍的增量突发传输。
  • 5 个事件标志(DMA 半传输、DMA 传输完成、DMA 传输错误、DMA FIFO 错误、直接模式错误)
  • 这两个DMA控制器支持循环缓冲区管理,还具有双缓冲功能,无需任何特殊代码即可自动使用和切换两个内存缓冲区。

2.DMA 控制系统框图

在这里插入图片描述
注:DMA1 控制器 AHB 外设端口与 DMA2 控制器的情况不同,不连接到总线矩阵,因此,仅 DMA2 数据流能够执行存储器到存储器的传输。关于总线矩阵的相关内容见文末链接。

3.DMA请求映射表

在这里插入图片描述
在这里插入图片描述

4.可能的 DMA 配置

在这里插入图片描述

二、DMA实现串口接收(STM32F4芯片)

1.DMA替代中断的原因

因为如果使用串口中断接收一帧很长很长的数据,每传8位都要打断一次CPU,CPU需要不停的进入中断做处理,严重影响CPU的工作效率。如果能通过DMA实现在接收完一帧数据之后再通知CPU处理这些数据,那岂不好处大大滴!
DMA+IDLE中断是一个常规的串口DMA接收方案,但是也有其局限性,如果数据帧的字节超时时间大于1个字节,那就会导致接收不完整的情况,所以建议在实现DMA接收的时候加入多字节超时的机制.

3.DMA流配置过程

  1. 确认没有正在进行的数据流(参考手册P218)
  2. 在 DMA_SxPAR 寄存器中设置外设端口寄存器地址
  3. 在 DMA_SxMA0R 寄存器(在双缓冲区模式的情况下还有 DMA_SxMA1R 寄存器)中设置存储器地址
  4. 在 DMA_SxNDTR 寄存器中配置要传输的数据项的总数
  5. 使用 DMA_SxCR 寄存器中的 CHSEL[2:0] 选择 DMA 通道(请求)
  6. 如果外设用作流控制器而且支持此功能,请将 DMA_SxCR 寄存器中的 PFCTRL 位置 1
  7. 使用 DMA_SxCR 寄存器中的 PL[1:0] 位配置数据流优先级
  8. 配置 FIFO 的使用情况(使能或禁止,发送和接收阈值)
  9. 配置数据传输方向、外设和存储器增量 / 固定模式、单独或突发事务、外设和存储器数据宽度、循环模式、双缓冲区模式和传输完成一半和/或全部完成,和/或 DMA_SxCR 寄存器中错误的中断
  10. 通过将 DMA_SxCR 寄存器中的 EN 位置 1 激活数据流

警告:要关闭连接到 DMA 数据流请求的外设,必须首先关闭外设连接的数据流请求的外设,必须首先关闭外设连接的 DMA 数据流,然后等待 EN 位 = 0 。只有这样才能安全地禁止外设。

2.DMA传输需要注意的细节

  • 当DMA的数据流被禁止传输后(DMA_S_CR寄存器EN位置零),最好不要直接打开(EN位置1),先清除DMA所有与数据流对应的事件标志标志位,然后检查EN是否已经清零,最后再将EN置1。
  • 循环模式是当&&8*****,突发模式*****
  • 为了使能 FIFO 阈值级别,必须通过将 DMA_SxFCR 寄存器中的 DMDIS 位置 1 来禁止直接模式。啥时候开启呢?
  • 双缓冲模式需要使能两个缓冲区,使能了双缓冲模式会自动使能循环模式,如何编程呢?。

3.DMA做串口接收数据的方法

接收定长数据:

使用DMA传输完成中断,比较简单

接收不定长数据:

方法1:用DMA + 串口空闲中断 :利用串口空闲标志位产生中断,在中断中处理接收的数据

方法2:用DMA+ 定时器:当产生&&&&&*******(((((((

4.DMA + 串口空闲中断实现串口接收

串口空闲中断的功能先简单了解一下:注意,空闲中断是接受数据后出现一个Byte的高电平(空闲)状态,就会触发空闲中断,而且清除中断的方式很奇怪,需要通过先读SR寄存器再读DR寄存器来清除该标志,库函数的USART_ClearITPendingBit里头都没有IDLE参数的呦。
在这里插入图片描述在这里插入图片描述
程序的大致流程:在dma.h文件中编写DMA初始化配置过程和DMA中断函数,在usart.c文件中编写串口1初始化函数和串口中断函数,在usart1初始化过程中调用DMA接收中断配置函数,串口1的RX接收到串口调试助手发的一帧数据后,进入串口1中断函数进行数据处理,完事儿重新开启DMA传输后退出中断。(下面的程序实现了串口1的DMA收发,仅展示关键步骤)

dma.h文件中DMA初始化及DMA中断:

/**********************************************************
* @brief    串口接收端DMA配置函数(从外设->存储器模式/8位数据宽度/存储器增量模式 )
* @param	DMA_Streamx:DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7
*			chx:DMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7
*			par:外设地址
*			mar:存储器地址
*			ndtr:数据传输量 
***********************************************************/
void u2DMA_RX_Config(DMA_Stream_TypeDef *DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr)
{
	DMA_InitTypeDef  DMA_InitStructure;
	if((u32)DMA_Streamx>(u32)DMA2)//得到当前stream是属于DMA2还是DMA1
	{
	  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);	//DMA2时钟使能 
	}else 
	{
	  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);	//DMA1时钟使能 
	}
	DMA_DeInit(DMA_Streamx);
	while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}		//等待DMA可配置 
		
	/*配置 DMA Stream*/
	DMA_InitStructure.DMA_Channel = chx;  							//通道选择
	DMA_InitStructure.DMA_PeripheralBaseAddr = par;					//DMA外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = mar;					//DMA存储器0地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;			//外设到存储器
	DMA_InitStructure.DMA_BufferSize = ndtr;						//数据传输量 
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;			//存储器增量模式
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据长度:8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;	//存储器数据长度:8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; 					//正常模式,即满了就不在接收了,而不是循环存储
	DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;			//高优先级
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;		//存储器突发单次传输???
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发单次传输???
	DMA_Init(DMA_Streamx, &DMA_InitStructure);						//初始化DMA Stream
		
	DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_TCIF5);						//清除传输完成中断
	//开启DMA错误和接收完成中断 +(使能接收完成中断是为了防止一次性接收过长数据)
	DMA_ITConfig(DMA2_Stream5,DMA_IT_TE|DMA_IT_TC, ENABLE);
	NVIC_Configuration(2,2);										//NVIC 配置
	
	DMA_Cmd (DMA2_Stream5,ENABLE);									//使能DMA
}
/**********************************************************
* @brief    串口发送端DMA配置函数(从存储器->外设模式/8位数据宽度/存储器增量模式 )
* @param	DMA_Streamx:DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7
*			chx:DMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7
*			par:外设地址
*			mar:存储器地址
*			ndtr:数据传输量 
***********************************************************/ 
void u2DMA_TX_Config(DMA_Stream_TypeDef *DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr)
{
	DMA_InitTypeDef  DMA_InitStructure;
	if((u32)DMA_Streamx>(u32)DMA2)		//得到当前stream是属于DMA2还是DMA1
	{
	  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);	//DMA2时钟使能 
	}else 
	{
	  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);	//DMA1时钟使能 
	}
	DMA_DeInit(DMA_Streamx);
	while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}		//等待DMA可配置 
		
	/*配置 DMA Stream*/
	DMA_InitStructure.DMA_Channel = chx;  							//通道选择
	DMA_InitStructure.DMA_PeripheralBaseAddr = par;					//DMA外设地址
	DMA_InitStructure.DMA_Memory0BaseAddr = mar;					//DMA存储器0地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;			//存储器到外设模式
	DMA_InitStructure.DMA_BufferSize = ndtr;						//数据传输量 
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;			//存储器增量模式
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据长度:8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;	//存储器数据长度:8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;					//正常模式,即满了就不在接收了,而不是循环存储
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;			//中等优先级
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;		//存储器突发单次传输
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发单次传输
	DMA_Init(DMA_Streamx, &DMA_InitStructure);						//初始化DMA Stream
}
/************************************************************
* @brief    DMA2_Stream5的所有中断,防止接收到的帧过长无法触发串口空闲中断
* @param	
***********************************************************/
void DMA2_Stream5_IRQHandler(void)
{
if(DMA_GetFlagStatus(DMA2_Stream5,DMA_FLAG_TCIF5) != RESET)
	{
		DMA_Cmd(DMA2_Stream5, DISABLE); 					//关闭DMA,防止处理其间有数据
		Rx_DMA_receive_size = RECEIVE_BUF - DMA_GetCurrDataCounter(DMA2_Stream5);
		if(Rx_DMA_receive_size !=0)
		{
//			Receive_DataPack();/*处理接收数据函数*/;
		}
		DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_TCIF5 | DMA_FLAG_FEIF5 | DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5);//清除DMA2_Steam7传输完成标志
		while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE){}	//确保DMA可以被设置
		DMA_SetCurrDataCounter(DMA2_Stream5, RECEIVE_BUF);
        DMA_Cmd(DMA2_Stream5, ENABLE);    			 		//打开DMA
	}
}

usart.c文件中串口1初始化函数和串口中断函数

#define RECEIVE_BUF_SIZE  100		//定义最大接收字
extern  u8 Rx_DMA_receive[];  		//100bytes buffer
/************************************************************
* @brief    串口 1 初始化函数
* @param	bound:波特率
***********************************************************/
void uart_init(u32 bound)
	{
	//GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	//开启GPIO与串口时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); 		//使能GPIOA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);		//使能USART1时钟
 
	//串口1对应引脚复用映射
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); 	//GPIOA9复用为USART1
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);	//GPIOA10复用为USART1
	
	//串口1的GPIO配置
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; 	//GPIOA9,GPIOA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;				//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			//速度50MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 				//推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; 				//上拉
	GPIO_Init(GPIOA,&GPIO_InitStructure); 						//初始化PA9,PA10

    //USART1初始化设置
	USART_InitStructure.USART_BaudRate = bound;					//波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;	//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;		//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;			//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;//收发模式
	USART_Init(USART1, &USART_InitStructure); 					//初始化串口1
	
#if USE_USART_DMA_RX	
	//Usart1 开启中断 + NVIC 配置
	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);				//开启串口空闲中断
	NVIC_Configuration(1,1);									//中断优先级配置
	
	USART_DMACmd(USART1,USART_DMAReq_Rx, ENABLE); 				//使能的DMA收
	//DMA2,STEAM5,CH4,外设为串口1,存储器为SendBuff,方向为外设到存储器,长度为:RECEIVE_BUF_SIZE.
	u2DMA_RX_Config(DMA2_Stream5,DMA_Channel_4,(u32)&USART1->DR,(u32)Rx_DMA_receive,100);
#else
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);				//开启串口接收中断
	NVIC_Configuration(3,3);
#endif
	
	
#if USE_USART_DMA_TX
	USART_DMACmd(USART1,USART_DMAReq_Tx, ENABLE); 				//使能的DMA发
	//DMA2,STEAM7,CH4,外设为串口1,存储器为Rxbuf,方向为存储器到外设,长度为:RECEIVE_BUF_SIZE.
 	u2DMA_TX_Config(DMA2_Stream7,DMA_Channel_4,(u32)&USART1->DR,(u32)Rxbuf,0);	
#endif
	
	USART_Cmd(USART1, ENABLE); 					  				//使能串口1,要放最后
}
u8 dum;
u8 Rx_DMA_receive[RECEIVE_BUF_SIZE];  //100bytes
u8 Rx_DMA_receive_size = 0;
/************************************************************
* @brief    串口 1 中断函数
* @param	
***********************************************************/
void USART1_IRQHandler(void)
{
#if SYSTEM_SUPPORT_OS 		//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
	OSIntEnter();    
#endif
	
#if USE_USART_DMA_RX
	/*空闲中断 */
	if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)
	{		
		dum = USART1->SR;
		dum = USART1->DR;	//读取SR DR数据可清除“空闲中断”标志
		Rx_DMA_receive_size = RECEIVE_BUF_SIZE - DMA_GetCurrDataCounter(DMA2_Stream5); //本帧数据长度=DMA准备的接收数据长度-DMA已接收数据长度
		
		if( 0 != Rx_DMA_receive_size)
		{
			Receive_DataPack();/*DMA接收模式时处理接收数据函数*/
		}
		
		DMA_Cmd(DMA2_Stream5,DISABLE);							//暂停DMA传输 
		// 清除DMA中断标志位
		DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_TCIF5 | DMA_FLAG_FEIF5 | DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5);//清除DMA2_Steam7传输完成标志
		while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE){}		//确保DMA可以被设置  
		DMA_SetCurrDataCounter(DMA2_Stream5,RECEIVE_BUF_SIZE); 	//设置DMA数据传输量 
		DMA_Cmd(DMA2_Stream5, ENABLE);                     		//开启DMA传输 
	}

#endif
	
#if SYSTEM_SUPPORT_OS 	//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
	OSIntExit();  											 
#endif
} 

其中USE_USART_DMA_RXUSE_USART_DMA_TX两个宏定义放在.h文件作为决定是否开启串口通道DMA功能的开关。这种串口接收方式存在很大的隐患,如果发送端设备发送一帧数据并不是一次性发完的而是分多次发送(期间间隔几毫秒),就会导致收到的数据不完整,如何解决呢?看下面的方法。

好文链接:

STM32之串口DMA接收不定长数据

一个严谨的STM32串口DMA发送&接收(1.5Mbps波特率)机制

深入解析stm32f407参考手册——总线架构

STM32的DMA串口直通(外设到外设)

STM32—无需中断来实现使用DMA接收串口数据

DMA和UART的深刻认识–串口接收的3种工作方式

STM32 串口采用DMA方式收发

STM32使用串口1配合DMA接收不定长数据,大大减轻CPU载荷

  • 35
    点赞
  • 172
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
串口空闲中断DMA中断接收是两种不同的接收方式。 串口空闲中断接收是指当串口接收到数据后,数据传输完成并且串口空闲时,触发中断来进行数据处理。这种方式比较简单,适用于数据量较小的情况。 DMA中断接收是指使用DMA控制器来进行数据传输,当DMA传输完成后触发中断进行数据处理。这种方式适用于数据量较大的情况,可以提高数据传输的效率。 下面是一个基于STM32串口空闲中断+DMA中断接收的例子: ```c #include "stm32f4xx.h" #include "stm32f4xx_conf.h" #define USARTx USART2 #define DMAx DMA1 #define DMAx_Streamx DMA1_Stream5 #define DMAx_Streamx_IRQn DMA1_Stream5_IRQn #define DMAx_Streamx_IRQHandler DMA1_Stream5_IRQHandler #define BUFFER_SIZE 1024 uint8_t buffer[BUFFER_SIZE]; volatile uint16_t bufferIndex = 0; void initUSART() { USART_InitTypeDef USART_InitStruct; GPIO_InitTypeDef GPIO_InitStruct; NVIC_InitTypeDef NVIC_InitStruct; DMA_InitTypeDef DMA_InitStruct; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); USART_InitStruct.USART_BaudRate = 115200; USART_InitStruct.USART_WordLength = USART_WordLength_8b; USART_InitStruct.USART_StopBits = USART_StopBits_1; USART_InitStruct.USART_Parity = USART_Parity_No; USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USARTx, &USART_InitStruct); USART_ITConfig(USARTx, USART_IT_IDLE, ENABLE); DMA_InitStruct.DMA_Channel = DMA_Channel_4; DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)buffer; DMA_InitStruct.DMA_BufferSize = BUFFER_SIZE; DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&USARTx->DR; DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; DMA_InitStruct.DMA_Priority = DMA_Priority_Medium; DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMAx_Streamx, &DMA_InitStruct); NVIC_InitStruct.NVIC_IRQChannel = DMAx_Streamx_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); USART_Cmd(USARTx, ENABLE); DMA_Cmd(DMAx_Streamx, ENABLE); } void DMAx_Streamx_IRQHandler() { if (DMA_GetITStatus(DMAx_Streamx, DMA_IT_TCIF5) != RESET) { DMA_ClearITPendingBit(DMAx_Streamx, DMA_IT_TCIF5); DMA_Cmd(DMAx_Streamx, DISABLE); bufferIndex = BUFFER_SIZE - DMA_GetCurrDataCounter(DMAx_Streamx); } } void USART2_IRQHandler() { if (USART_GetITStatus(USART2, USART_IT_IDLE) != RESET) { USART_ClearITPendingBit(USART2, USART_IT_IDLE); DMAx_Streamx->CR &= ~(1 << 0); // Disable DMA Stream DMA_ClearFlag(DMAx_Streamx, DMA_FLAG_TCIF5 | DMA_FLAG_HTIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_DMEIF5 | DMA_FLAG_FEIF5); DMA_Cmd(DMAx_Streamx, ENABLE); } } int main() { initUSART(); while (1) { if (bufferIndex > 0) { // 处理接收到的数据 bufferIndex = 0; } } } ``` 该例子中使用DMA控制器进行数据传输,并通过串口空闲中断触发DMA传输完成中断。在DMA传输完成中断中,通过DMA_GetCurrDataCounter函数获取当前未传输的数据长度,即接收到的数据长度。在串口空闲中断中,通过禁用并重新启用DMA Stream来触发DMA传输完成中断

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值