7-3 STM32F405--USART通过DMA接收数据

功能描述:通过STM32F405RGT6的USART1与DMA相结合,方便高效地接收数据。

特别注意:DMA传输时,会根据设置的存储器接收数据的单位(DMA_MemoryDataSize)将FIFO中的值传入接收缓存区(RxBuff)中。当存储器接收数据的单位不是字节时,最后几个字节可能不是这次传输的数据,而是上一次最后传输时存储在FIFO中的数据。

  • main.c文件内容如下
    #include "stm32f4xx.h"                  // Device header
    #include "MyDMA.h"
    #include "Serial.h"
    
    uint8_t data[100] = {0};		//数据接收指针
    
    int main(void)
    {
    	Serial_Init();	//初始化串口
    	
    	Serial_SendChinese(USART1, "现在正在进行串口功能测试。");	//发送中文字符串
    	
    	Serial_SendString(USART1, "\r\n");				//回车换行
    
    	while(1)
    	{
    		if(Serial_GetRxFlag() == 1)
    		{
    			memset(data, 0, sizeof(data));					//清空data,避免上一次数据的干扰
    			Serial_GetRxData(data);							//将接收缓冲区的数据复制到data中来
    			Serial_ClearRxFlag();							//清除接收标志位
    			//回传接收到的数据和数据大小
    			Serial_Printf(USART1, "%s\n%d\n", data, Serial_GetRxSize());
    		}	
    	}
    }
    
  • Serial.c文件的内容如下
    #include "Serial.h"             
    
    uint8_t RxBuff1[Serial_Buffer_Size]={0};
    uint16_t RxSize = 0;
    uint8_t RxFlag1 = 0;
    
    /**
      * @摘要  		初始化USART
      * @参数  		无
      * @返回值  	无
      * @说明  		无
      */
    void Serial_Init(void)
    {
    	//开启GPIOA时钟
    	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    	
    	//将PA9配置为复用模式,复用为USART1的TX引脚
    	GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
    	//将PA10配置为复用模式,复用为USART1的RX引脚
    	GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
    	
    	//定义GPIO初始化结构体
    	GPIO_InitTypeDef GPIO_InitStructure;
    	//初始化上述结构体
    	GPIO_StructInit(&GPIO_InitStructure);
    	//配置GPIO模式为复用模式
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    	//配置GPIO输出类型为推挽输出
    	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    	//配置GPIO输入类型为上拉输入
    	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    	//配置GPIO的速度为快速50MHZ
    	GPIO_InitStructure.GPIO_Speed = GPIO_Fast_Speed;
    	//指定GPIO引脚为Pin9和Pin10
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;
    	//初始化对应的GPIO
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    	
    	//初始化DMA
    	MyDMA_Init((uint32_t)&(USART1->DR), (uint32_t)RxBuff1, Serial_Buffer_Size);
    	
    	//开启USART1时钟
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    	
    	//定义USART初始化结构体
    	USART_InitTypeDef USART_InitStructure;
    	//初始化上述结构体
    	USART_StructInit(&USART_InitStructure);
    	//配置波特率为115200
    	USART_InitStructure.USART_BaudRate = 115200;
    	//配置数据长度为8位
    	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    	//配置校验位为无
    	USART_InitStructure.USART_Parity = USART_Parity_No;
    	//配置停止位为1位
    	USART_InitStructure.USART_StopBits = USART_StopBits_1;
    	//配置串口模式为接收和发送模式
    	USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
    	//配置硬件流控制为无
    	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    	//初始化对应的USART
    	USART_Init(USART1, &USART_InitStructure);
    	
    	//使能USART1的线路空闲中断到NVIC
    	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
    
    	//NVIC优先级分组
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    	//定义NVIC初始化结构体
    	NVIC_InitTypeDef NVIC_InitStructure;
    	//选择NVIC通道为USART1_IRQn
    	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    	//使能该通道
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    	//配置抢占优先级为1
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    	//配置响应优先级为1
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    	//初始化NVIC
    	NVIC_Init(&NVIC_InitStructure);
    	
    	//开启USART1的接收DMA请求
    	USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
    	
    	//开启USART1
    	USART_Cmd(USART1, ENABLE);
    }
    
    /**
      * @摘要  		串口发送一个字节数据
      * @参数  		USARTx:串口号,x可以为1、2、3...(根据具体芯片型号选择)
      * @参数  		Byte:	要发送的字节数据
      * @返回值  	0或1:0代表发送失败,1代表发送成功
      * @说明  		无
      */
    uint8_t Serial_SendByte(USART_TypeDef* USARTx, uint8_t Byte)
    {
    	uint16_t timeout = 10000;
    	USART_SendData(USARTx, Byte);
    	while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET)
    	{
    		timeout--;
    		if(timeout == 0)
    			return 0;
    	}
    	return 1;
    }
    
    uint8_t Serial_SendArray(USART_TypeDef* USARTx, uint8_t *array, uint16_t length)
    {
    	uint8_t state = 0;
    	for(uint16_t i=0; i<length; i++)
    	{
    		state = Serial_SendByte(USARTx, array[i]);
    		if(state == 0)
    			return 0;
    	}
    	return 1;
    }
    
    /**
      * @摘要  		串口发送一个字符串
      * @参数  		USARTx:串口号,x可以为1、2、3...(根据具体芯片型号选择)
      * @参数  		str:	要发送的字符串
      * @返回值  	0或1:0代表发送失败,1代表发送成功
      * @说明  		无
      */
    uint8_t Serial_SendString(USART_TypeDef* USARTx, const char *str)
    {
    	uint8_t state = 0;
    	for(uint16_t i=0; str[i]!='\0'; i++)
    	{
    		state = Serial_SendByte(USARTx, str[i]);
    		if(state == 0)
    			return 0;
    	}
    	return 1;
    }
    
    /**
      * @摘要  		串口发送一个无符号整数
      * @参数  		USARTx:串口号,x可以为1、2、3...(根据具体芯片型号选择)
      * @参数		Number:指定要发送的数字,范围:0~4294967295
      * @参数  		type:	要以什么进制发送上述整数,比如2,8,10,16
      * @参数  		length:要发送的整数的长度
      * @返回值  	0或1:0代表发送失败,1代表发送成功
      * @说明  		无
      */
    uint8_t Serial_SendNum(USART_TypeDef* USARTx, uint32_t Number, uint8_t type, uint8_t length)
    {
    	uint8_t state = 0;
    	uint8_t temp;
    	for(uint8_t i=0; i<length; i++)
    	{
    		temp = (Number/Pow(type,length-i-1))%type;
    		if(temp > 9)
    			state = Serial_SendByte(USARTx, temp-10+'A');
    		else
    			state = Serial_SendByte(USARTx, temp+'0');
    		if(state == 0)
    			return 0;
    	}
    	return 1;
    }
    
    /**
      * @摘要  		串口发送一个有符号整数
      * @参数  		USARTx:串口号,x可以为1、2、3...(根据具体芯片型号选择)
      * @参数  		Number:要发送的整数
      * @参数  		length:要发送的整数的长度
      * @参数		ShowSign:是否显示'+'号,ENABLE(显示)或DISABLE(不显示)
      * @返回值  	0或1:0代表发送失败,1代表发送成功
      * @说明  		Number会以十进制格式发送出去
      */
    uint8_t Serial_SendSignedNum(USART_TypeDef* USARTx, int32_t Number, uint8_t length, FunctionalState ShowSign)
    {
    	uint8_t state = 0;
    	if(Number < 0)
    	{
    		state = Serial_SendByte(USARTx, '-');
    		if(state == 0)
    			return 0;
    		state = Serial_SendNum(USARTx, -Number,10,length);
    		if(state == 0)
    			return 0;
    		
    	}
    	else
    	{
    		if(ShowSign == ENABLE)
    		{
    			state = Serial_SendByte(USARTx, '+');
    			if(state == 0)
    				return 0;
    			state = Serial_SendNum(USARTx, Number,10,length);
    			if(state == 0)
    				return 0;
    		}
    		else
    		{
    			state = Serial_SendNum(USARTx, Number,10,length);
    			if(state == 0)
    				return 0;
    		}
    	}
    	return 1;
    }
    
    /**
      * @摘要  		串口发送一个小数
      * @参数  		USARTx:串口号,x可以为1、2、3...(根据具体芯片型号选择)
      * @参数  		Number:要发送的小数,范围:-4294967295.0~4294967295.0
      * @参数  		type:要以什么进制发送上述小数,比如2,8,10,16
      * @参数		IntLength:指定数字的整数部分长度
      * @参数		FraLength:指定数字的小数部分长度,小数进行去尾补0显示
      * @返回值  	0或1:0代表发送失败,1代表发送成功
      * @说明  		无
      */
    uint8_t Serial_SendFloatNum(USART_TypeDef* USARTx, double Number,uint8_t type, uint8_t IntLength, uint8_t FraLength)
    {
    	uint8_t state = 0;
    	uint32_t tempNum = Number;
    	uint8_t temp;
    	for(uint8_t i=0; i<IntLength; i++)
    	{
    		temp = (tempNum/Pow(type,IntLength-i-1))%type;
    		if(temp > 9)
    			state = Serial_SendByte(USARTx, temp-10+'A');
    		else
    			state = Serial_SendByte(USARTx, temp+'0');
    		if(state == 0)
    			return 0;
    	}
    	
    	state = Serial_SendByte(USARTx, '.');
    	if(state == 0)
    		return 0;
    	
    	tempNum = (Number-tempNum)*Pow(10,FraLength);
    	for(uint8_t i=0; i<FraLength; i++)
    	{
    		tempNum = tempNum*type;
    		temp = tempNum/Pow(10,FraLength);
    		tempNum = tempNum-tempNum/Pow(10,FraLength)*Pow(10,FraLength);
    		if(temp > 9)
    			state = Serial_SendByte(USARTx, temp-10+'A');
    		else
    			state = Serial_SendByte(USARTx, temp+'0');
    		if(state == 0)
    			return 0;
    	}
    	return 1;
    }
    
    /**
      * @摘要  		这是一个可变参数函数,用于格式化发送字符串
      * @参数  		USARTx:串口号,x可以为1、2、3...(根据具体芯片型号选择)
      * @参数		format:字符串
      * @参数		...:可变参数
      * @返回值  	0或1:0代表发送失败,1代表发送成功
      * @说明  		无
      */
    uint8_t Serial_Printf(USART_TypeDef* USARTx, char* format,...)
    {
    	uint8_t state = 0;
    	char str[100];
    	va_list arg;
    	va_start(arg,format);
    	vsprintf(str,format,arg);
    	va_end(arg);
    	state = Serial_SendString(USART1,str);
    	if(state == 0)
    		return 0;
    	return 1;
    }
    
    /**
      * @摘要  		串口发送中文字符串(或一个字符)
      * @参数  		USARTx:串口号,x可以为1、2、3...(根据具体芯片型号选择)
      * @参数  		Chinese:要发送的中文字符串
      * @返回值  	0或1:0代表发送失败,1代表发送成功
      * @说明  		无
      */
    uint8_t Serial_SendChinese(USART_TypeDef* USARTx, char *Chinese)
    {
    	uint8_t state = 0;
    	uint8_t cnt = 0, pChinese = 0, SingleChinese[4] = {0};
    	uint8_t index;
    	
    	for(uint8_t i=0; Chinese[i]!='\0'; i++)
    	{
    		SingleChinese[pChinese] = Chinese[i];
    		pChinese++;
    		if(pChinese>=Serial_CHN_CHAR_WIDTH)
    		{
    			pChinese = 0;
    			cnt++;
    			for(index=0; strcmp(Serial_Chinese[index].Index, "") != 0; index++)
    			{
    				if(strcmp(Serial_Chinese[index].Index, (const char*)SingleChinese) == 0)
    					break;
    			}
    			state = Serial_SendArray(USARTx, (uint8_t *)Serial_Chinese[index].Data, 3);
    			if(state == 0)
    				return 0;
    		}
    	}
    	return 1;
    }
    
    /**
      * @摘要  		串口发送数据包
      * @参数  		USARTx:串口号,x可以为1、2、3...(根据具体芯片型号选择)
      * @参数  		package:要发送的数据包
      * @返回值  	0或1:0代表发送失败,1代表发送成功
      * @说明  		无
      */
    uint8_t Serial_SendPackage(USART_TypeDef* USARTx, uint8_t* package)
    {
    	uint8_t state = 0;
    	state = Serial_SendByte(USART1,'@');			//包头
    	if(state == 0)
    		return 0;
    	state = Serial_SendArray(USART1, package, 4);	//数据
    	if(state == 0)
    		return 0;
    	state = Serial_SendString(USART1, "\r\n");		//包尾
    	if(state == 0)
    		return 0;
    	return 1;
    }
    
    /**
      * @摘要  		获取接收数据大小
      * @参数  		无
      * @返回值  	接收数据大小(单位:Byte)
      * @说明  		无
      */
    uint16_t Serial_GetRxSize(void)
    {
    	return RxSize;
    }
    
    /**
      * @摘要  		获取接收标志位
      * @参数  		无
      * @返回值  	接收标志:1(完成接收),0(未完成接收或未接收)
      * @说明  		无
      */
    uint8_t Serial_GetRxFlag(void)
    {
    	return RxFlag1;
    }
    
    /**
      * @摘要  		清除接收标志位
      * @参数  		无
      * @返回值  	无
      * @说明  		无
      */
    void Serial_ClearRxFlag(void)
    {
    	RxFlag1 = 0;
    }
    
    /**
      * @摘要  		获取接收数据
      * @参数  		Rxdata:指向存储接收数据地址的指针
      * @返回值  	无
      * @说明  		调用此函数会自动清除接收缓冲区
      */
    void Serial_GetRxData(uint8_t* Rxdata)
    {
    	//等待相应的DMA数据流传输完成
    	while(DMA_GetFlagStatus(DMA2_Stream2, DMA_FLAG_TCIF2) == RESET);
    	//清除相应的DMA数据流传输完成标志位
    	DMA_ClearFlag(DMA2_Stream2, DMA_FLAG_TCIF2);
    	
    	for(uint16_t i=0; i<Serial_Buffer_Size; i++)
    	{
    		if(RxBuff1[i] == 0)
    			break;
    		Rxdata[i] = RxBuff1[i];
    		RxBuff1[i] = 0;
    	}
    	//重新开启DMA,此时DMA等待硬件触发
    	DMA_Cmd(DMA2_Stream2, ENABLE);
    }
    
    /**
      * @摘要  		USART1中断服务函数
      * @参数  		无
      * @返回值  	无
      * @说明  		无
      */
    void USART1_IRQHandler(void)
    {
    	if(USART_GetITStatus(USART1, USART_IT_IDLE) == SET)
    	{
    		//下一句和上一句if判断里的语句共同作用清除了USART_IT_IDLE这个标志位
    		USART_ReceiveData(USART1);
    		//计算收到的字节数
    		RxSize = Serial_Buffer_Size - DMA_GetCurrDataCounter(DMA2_Stream2);
    		//失能DMA,此处作用是结束传输过程、标志传输完成
    		DMA_Cmd(DMA2_Stream2, DISABLE);
    		//接收标志置1
    		RxFlag1 = 1;
    	}
    }
    
  • Serial.h文件内容如下
    #ifndef __SERIAL_H
    #define __SERIAL_H
    
    #include <String.h>
    #include <stdarg.h>
    #include <stdio.h>
    #include "stm32f4xx.h"                  // Device header
    #include "Serial_Data.h"
    #include "MyMath.h"
    #include "MyDMA.h"
    
    #define Serial_Buffer_Size		1024
    
    void Serial_Init(void);
    uint8_t Serial_SendByte(USART_TypeDef* USARTx, uint8_t Byte);
    uint8_t Serial_SendArray(USART_TypeDef* USARTx, uint8_t *array, uint16_t length);
    uint8_t Serial_SendString(USART_TypeDef* USARTx, const char *str);
    uint8_t Serial_SendNum(USART_TypeDef* USARTx, uint32_t Number, uint8_t type, uint8_t length);
    uint8_t Serial_SendSignedNum(USART_TypeDef* USARTx, int32_t Number, uint8_t length,FunctionalState ShowSign);
    uint8_t Serial_SendFloatNum(USART_TypeDef* USARTx, double Number,uint8_t type, uint8_t IntLength, uint8_t FraLength);
    uint8_t Serial_SendChinese(USART_TypeDef* USARTx, char *Chinese);
    uint8_t Serial_Printf(USART_TypeDef* USARTx, char* format,...);
    void Serial_GetRxData(uint8_t* Rxdata);
    uint16_t Serial_GetRxSize(void);
    uint8_t Serial_GetRxFlag(void);
    void Serial_ClearRxFlag(void);
    uint8_t* Serial_GetRxPackage(void);
    
    #endif
    
  •  MyDMA.c文件内容如下
    #include "MyDMA.h"
    
    /**
      * @摘要  		初始化DMA
      * @参数  		PAddress: 外设地址
      * @参数  		MAddress: 存储器地址
      * @参数  		Size: 要传输的数据个数
      * @返回值  	无
      * @说明  		无
      */
    void MyDMA_Init(uint32_t PAddress, uint32_t MAddress,uint16_t Size)
    {
    	//开启DMA2的时钟
    	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
    	
    	//定义DMA初始化结构体
    	DMA_InitTypeDef DMA_InitStructure;
    	//初始化上述结构体
    	DMA_StructInit(&DMA_InitStructure);
    	//配置DMA通道为通道4
    	DMA_InitStructure.DMA_Channel = DMA_Channel_4;
    	//配置DMA传输的存储器地址
    	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)MAddress;
    	//配置存储器接收数据的单位为字节
    	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    	//配置存储器地址指针为自增模式
    	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    	//配置DMA传输的外设地址
    	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)PAddress;
    	//配置外设发送数据的单位为字节
    	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    	//配置外设地址指针为非自增模式
    	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    	//配置存储器突发传输配置为单次传输
    	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    	//配置外设突发传输配置为单次传输
    	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    	//配置DMA传输方向为外设到存储器
    	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
    	//配置DMA要传输的数据个数
    	DMA_InitStructure.DMA_BufferSize = Size;
    	//配置DMA为非循环模式
    	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    	//配置DMA为非直接模式
    	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
    	//配置FIFO阈值为全满
    	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    	//配置该通道的DMA优先级为中
    	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
    	
    	//等待DMA失能,以确保初始化可以成功
    	while(DMA_GetCmdStatus(DMA2_Stream2) == ENABLE);
    	//初始化DMA
    	DMA_Init(DMA2_Stream2, &DMA_InitStructure);
    	
    	//开启DMA,此时DMA等待硬件触发
    	DMA_Cmd(DMA2_Stream2, ENABLE);
    }
    
  •   MyDMA.h文件内容如下
    #ifndef __MYDMA_H
    #define __MYDMA_H
    
    #include "stm32f4xx.h"                  // Device header
    
    void MyDMA_Init(uint32_t SAddress, uint32_t DAddress, uint16_t Size);
    
    #endif
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值