stm32的串口DMA空闲中断接收不等长数据,stm32F1的usart1-DMA-IDLE收发

stm32的DMA收发原理,和stm32F4 + USART2 +DMA +IDLE使用,见另一篇:https://blog.csdn.net/Mark_md/article/details/107243054

stm32F1的串口DMA和stm32F4的大同小异,使用时要区分通道和数据流区别。

 

stm32F1的usart1-DMA-IDLE收发

直接上代码

usart.c

#include "usart.h"

uint8_t Uart1_Rx_Buff[DMA_UART1_RX_SIZE];
uint8_t Uart1_Tx_Buff[DMA_UART1_TX_SIZE];
uint16_t Uart1_RxLength;	//Uart1 DMA 一次接收到的数据长度

void uart1_init(u32 bound){
	/*定义串口初始化结构体*/
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	/*使能内部外设时钟*/ 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
	
	/*GPIO端口设置*/
	/*USART1_TX   GPIOA.9*/
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 				//PA.9
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		//输出最大速率
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;			//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//初始化GPIOA.9
   
	/*USART1_RX	  GPIOA.10*/
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;				//PA.10
	//GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;	//浮空输入
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;			//上拉输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//初始化GPIOA.10  
  
	/*USART1 参数设置*/
	USART_InitStructure.USART_BaudRate = bound;					//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;	//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;		//停止位 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
	
	/*Usart1 NVIC 配置*/
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;			//串口1中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=6 ;	//抢占优先级6
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;			//子优先级0
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
	
	/*设置UART1 中断触发模式*/
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//RX NO Empty,RX非空,RX有数据中断。开启串口接受中断,使用DMA接收时要失能这个,使能空闲中断
	//USART_ITConfig(USART1, USART_IT_TC, ENABLE);// Transmit Complete,发送完成中断。开启串口发送完成中断,发送一个字节就会进入中断,只需要清除中断标志位,不需要关闭中断
	//USART_IT_TXE TX Empty,TX为空,发送寄存器DR清零。发送寄存器空闲中断,发送完一个字节后,必须关闭这个中断,否则只要寄存器为空值,就会反复进入中断
	//USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//开启串口空闲中断??发送会不会触发空闲中断
	
	/*开启UART1 */
	USART_Cmd(USART1, ENABLE);                    		//使能串口1
}

void uart1_dma_rx_configuration(void)
{
	/*定义DMA初始化结构体*/
	DMA_InitTypeDef  DMA_InitStructure;
	
	/*使能内部外设时钟*/ 
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	//使能DMA时钟
	
	/*DMA 通道5 接收配置*/
	DMA_DeInit(DMA1_Channel5);  
	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;		//DMA外设基地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)Uart1_Rx_Buff;			//DMA内存基地址,把接收到的数据放到哪儿
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;					//数据传输方向,从内存从外设读取,外设作为数据来源
	DMA_InitStructure.DMA_BufferSize = DMA_UART1_RX_SIZE;				//DMA通道的缓存的大小,一次接收的最大字节数
	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通道 中优先级
//	DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; 			//DMA通道 优先级很高
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  						//DMA通道为内存与内存通信,而非内存到内存
	DMA_Init(DMA1_Channel5, &DMA_InitStructure);						//启动DMA
	
	/*设置UART1 中断触发模式*/
	USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);//RX NO Empty,RX非空,RX有数据中断。开启串口接受中断,使用DMA接收时要失能这个,使能空闲中断
	//USART_ITConfig(USART1, USART_IT_TC, ENABLE);// Transmit Complete,发送完成中断。开启串口发送完成中断,发送一个字节就会进入中断,只需要清除中断标志位,不需要关闭中断
	//USART_IT_TXE TX Empty,TX为空,发送寄存器DR清零。发送寄存器空闲中断,发送完一个字节后,必须关闭这个中断,否则只要寄存器为空值,就会反复进入中断
	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//开启串口空闲中断??发送会不会触发空闲中断
	
	/*开启DMA接收*/
	DMA_Cmd(DMA1_Channel5, ENABLE);     								//开启DMA 通道5传输,即接收传输
	USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);						//使能UART1的DMA接收
}

void uart1_dma_rxtx_configuration(void)
{
	/*定义DMA初始化结构体*/
	DMA_InitTypeDef  DMA_InitStructure;
	
	/*使能内部外设时钟*/ 
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	//使能DMA时钟
	
	/*DMA 通道5 接收配置*/
	DMA_DeInit(DMA1_Channel5);  
	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;		//DMA外设基地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)Uart1_Rx_Buff;			//DMA内存基地址,把接收到的数据放到哪儿
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;					//数据传输方向,从内存读取发送到外设,外设作为数据来源
	DMA_InitStructure.DMA_BufferSize = DMA_UART1_RX_SIZE;				//DMA通道的缓存的大小,一次接收的最大字节数
	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通道 中优先级
//	DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; 			//DMA通道 优先级很高
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  						//DMA通道为内存与外设通信,而非内存到内存
	DMA_Init(DMA1_Channel5, &DMA_InitStructure);						//配置DMA 通道5
	
	/*DMA 通道4 发送配置*/
	DMA_DeInit(DMA1_Channel4);  
	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;		//DMA外设基地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)Uart1_Tx_Buff;			//DMA发送的内存地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;					//数据传输方向,从内存到外设发送,外设作为数据传输目的地
	//因为刚开始初始化时候不需要发送数据,所以发送长度为0
	DMA_InitStructure.DMA_BufferSize = 0;				//发送长度为0
	DMA_Init(DMA1_Channel4, &DMA_InitStructure);						//配置DMA 通道4
	
	/*设置UART1 中断触发模式*/
	USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);//RX NO Empty,RX非空,RX有数据中断。开启串口接受中断,使用DMA接收时要失能这个,使能空闲中断
	//USART_ITConfig(USART1, USART_IT_TC, ENABLE);// Transmit Complete,发送完成中断。开启串口发送完成中断,发送一个字节就会进入中断,只需要清除中断标志位,不需要关闭中断
	//USART_IT_TXE TX Empty,TX为空,发送寄存器DR清零。发送寄存器空闲中断,发送完一个字节后,必须关闭这个中断,否则只要寄存器为空值,就会反复进入中断
	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//开启串口空闲中断??发送会不会触发空闲中断
	
	/*开启DMA接收	因为刚开始初始化时候不需要发送数据,所以 DMA发送通道4 不开启*/
	DMA_Cmd(DMA1_Channel5, ENABLE);     								//开启DMA 通道5传输,即接收传输
	USART_DMACmd(USART1,USART_DMAReq_Tx|USART_DMAReq_Rx,ENABLE);		//使能UART1的DMA发送和接收
}

uint16_t Uart1_DMA_Send_Data(void * buffer, u16 size)
{
	if(!size) return 0;// 判断长度是否有效
	while (DMA_GetCurrDataCounter(DMA1_Channel4));// 检查DMA发送通道内是否还有数据
	if(buffer) memcpy(Uart1_Tx_Buff, buffer,(size > DMA_UART1_TX_SIZE?DMA_UART1_TX_SIZE:size));//判断发送长度是否大于DMA可传输长度
	//DMA发送数据-要先关DMA,再设置发送长度,最后开启DMA
	DMA_Cmd(DMA1_Channel4, DISABLE);//开启DMA 通道4传输,即发送传输
	DMA1_Channel4->CNDTR = size;// 设置发送长度
	DMA_Cmd(DMA1_Channel4, ENABLE);// 启动DMA发送
	return size;
}

//验证uart1的DMA,在不清除接收队列的情况下,后来的数据会覆盖原来的数据,未覆盖的数据会保留。所以接收一次数据并使用完成后,想要接收下一次数据,必须将接收缓存区清零
//验证清除uart1 IDLE中断,正确的方法是先读SR,再度DR
void USART1_IRQHandler(void)                	//串口1中断服务程序
{
	uint8_t temp;
	if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
	{
		DMA_Cmd(DMA1_Channel5,DISABLE);//关闭DMA通道
		
		//没用,不过还是加上吧
		DMA_ClearFlag(DMA1_FLAG_TC5);//DMA 通道5 清中断标志,否则会一直中断
		USART_ClearITPendingBit(USART1, USART_IT_IDLE);//清除空闲中断标志
		
		//IDLE标志位清零的过程是:先读SR,再读DR寄存器
		//注意:这句必须要,否则不能够清除中断标志位。
		temp = USART1->SR;//先读SR,然后读DR才能清除
		temp = USART1->DR;//清除DR
		temp = temp;
		
		/*在这里处理数据长度 及数据处理事件*/
		Uart1_RxLength = DMA_UART1_RX_SIZE - DMA_GetCurrDataCounter(DMA1_Channel5);
		//DMA接收完数据后,不会将Uart1_Rx_Buff任何数据清零,下一次数据会重新覆盖,没有覆盖的数据会保留,所以获取数据长度和清零接收缓冲区是必要的
		
		/*清零本次DMA接收缓冲区*/
		//清零接收缓存区,可以不全部清空,提高运行效率;全部清空,确保长时间稳定性
		//memset(Uart1_Rx_Buff,0,Uart1_RxLength);
		memset(Uart1_Rx_Buff,0,DMA_UART1_RX_SIZE);//待uart接收缓存区项目处理完后,清除数据接收缓冲区USART_RX_BUF,用于下一次数据接
		
		/*重新开启下一次DMA接收*/
		DMA_SetCurrDataCounter(DMA1_Channel5,DMA_UART1_RX_SIZE);//DMA通道的DMA缓存的大小。重置传输数目,当再次达到这个数目就会进中断
		DMA_Cmd(DMA1_Channel5,ENABLE);//开启DMA通道	
	}
}

usart.h

#ifndef __USART_H
#define __USART_H
#include "stdio.h"	
#include "sys.h" 
/************************************************

************************************************/

#define	DMA_UART1_RX_SIZE	20
#define	DMA_UART1_TX_SIZE	20

extern uint8_t Uart1_Rx_Buff[DMA_UART1_RX_SIZE];
extern uint8_t Uart1_Tx_Buff[DMA_UART1_TX_SIZE];
extern uint16_t Uart1_RxLength;	//Uart1 DMA 一次接收到的数据长度

void uart1_init(u32 bound);
void uart1_dma_rx_configuration(void);
void uart1_dma_rxtx_configuration(void);
uint16_t Uart1_DMA_Send_Data(void * buffer, u16 size);

#endif

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用STM32串口空闲中断DMA可以实现接收不定长度数据的功能,具体步骤如下: 1. 配置串口DMA 首先需要配置串口DMA,使其能够正常工作。具体配置方法可以参考STM32的官方文档或者其他相关资料。 2. 配置接收数组和接收计数器 在代码中定义一个接收数组和一个接收计数器,用于存储接收到的数据和记录接收到的数据长度。 3. 配置空闲中断空闲中断中判断接收数据是否完成,如果完成则将接收到的数据发送出去。具体实现方法如下: ```c void HAL_UART_IDLECallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART1) { uint32_t tmp_flag = 0; uint32_t temp; tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE); //获取空闲中断标志 if((tmp_flag != RESET)) //判断是否是空闲中断 { __HAL_UART_CLEAR_IDLEFLAG(&huart1); //清除空闲中断标志 HAL_UART_DMAStop(&huart1); //停止DMA传输 temp = huart1.hdmarx->Instance->CNDTR; //获取DMA缓存区剩余数据量 uart1_rx_len = UART_RCV_BUF_SIZE - temp; //计算接收到的数据长度 HAL_UART_Transmit(&huart1, uart1_rx_buf, uart1_rx_len, 0xffff); //将接收到的数据发送出去 HAL_UART_Receive_DMA(&huart1, uart1_rx_buf, UART_RCV_BUF_SIZE); //重新开启DMA传输 } } } ``` 4. 启动DMA传输 在代码中启动DMA传输,将串口接收到的数据存储到接收数组中。具体实现方法如下: ```c HAL_UART_Receive_DMA(&huart1, uart1_rx_buf, UART_RCV_BUF_SIZE); ``` 以上就是使用STM32串口空闲中断DMA实现接收不定长度数据的方法。如果您有任何问题,请随时提出。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值