STM32f4 串口DMA配置与使用(含代码)

这一篇可和AD那篇一起stm32外部AD——ADC08060 驱动_居安士的博客-CSDN博客

搜索stm32代码大部分的DMA配置都和内部AD相连,没有单独实现串口DMA的,对于外部AD或者其他应用需要单独启用串口DMA的应用,本篇或许可以给大家以参考

当我们计算的串口波特率 无法满足数据传输速度的时候,我们需要将串口设置为DMA发送(不占用CPU资源),但是这样也不够,这个时候我们需要采用抽帧的方法进行发送,接下来会依次进行详解。

目录

串口DMA配置方法

DMA串口发送


串口DMA配置方法

在配置串口DMA之前,我们要先看一下自己的串口是usart1还是usart2

不同的usart对应着不同的DMA,以及不同的数据流

STM32f4 串口DMA配置与使用(含代码)_第1张图片

usart2对应着DMA1通道4的数据流5和数据流6

usart1对应着DMA2通道4的数据流5和数据流7

STM32f4 串口DMA配置与使用(含代码)_第2张图片

为了方便大家,我把需要修改的地方备注出来(备注乱码忽略),大家直接对应修改即可!!赞赞

下面是usart2的

//-----------------DMA1配置-------------------------------------
void uart2_init(int bps)      
{
		GPIO_InitTypeDef 					GPIO_InitStructure;
		USART_InitTypeDef 				USART_InitStructure;
		NVIC_InitTypeDef 					NVIC_InitStructure;
		
		RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //修改
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//修改
	 
		GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2); //GPIOA2¸修改
		GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_USART2); //GPIOA3¸修改
		
		//USART2¶Ë¿ÚÅäÖÃ
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; //修改
		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); //修改

		//USART2³õʼ»¯ÉèÖÃ
		USART_InitStructure.USART_BaudRate =bps; //²¨ÌØÂÊÉèÖÃ
		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_CTS; //ÎÞÓ²¼þÊý¾ÝÁ÷¿ØÖÆ
		USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//ÊÕ·¢Ä£Ê½
		USART_Init(USART2, &USART_InitStructure); //³õʼ»¯´®¿Ú2
		
		USART_Cmd(USART2, ENABLE); //修改
		USART_DMACmd(USART2,USART_DMAReq_Tx,ENABLE);
		USART_ClearFlag(USART2, USART_FLAG_TC);
		USART_ITConfig(USART2, USART_IT_RXNE, DISABLE); //修改
		
		//Usart2 NVIC ÅäÖÃ
		NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//ÉèÖÃϵͳÖжÏÓÅÏȼ¶·Ö×é0
		NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;//´®¿Ú1ÖжÏͨµÀ
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//ÇÀÕ¼ÓÅÏȼ¶3
		NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;	//×ÓÓÅÏȼ¶3
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;	//IRQͨµÀʹÄÜ
		NVIC_Init(&NVIC_InitStructure);	//¸ù¾ÝÖ¸¶¨µÄ²ÎÊý³õʼ»¯VIC¼Ä´æÆ÷¡¢
}
void usart2_dma_Config(uint32_t * usart2_dma_buffer, int byteLen)
{ 
		DMA_InitTypeDef  		DMA_InitStructure;
	
		RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE); //修改 
		DMA_DeInit(DMA1_Stream6);
		while (DMA_GetCmdStatus(DMA1_Stream6) != DISABLE){} //µÈ´ýDMA¿ÉÅäÖà 
		
		/* ÅäÖÃ DMA Stream */
		DMA_InitStructure.DMA_Channel = DMA_Channel_4; //ͨµÀÑ¡Ôñ
		DMA_InitStructure.DMA_PeripheralBaseAddr =((u32)&USART2->DR); //DMAÍâÉèµØÖ·
		DMA_InitStructure.DMA_Memory0BaseAddr = (u32)usart2_dma_buffer; //DMA ´æ´¢Æ÷0µØÖ·
		DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //´æ´¢Æ÷µ½ÍâÉèģʽ
		DMA_InitStructure.DMA_BufferSize =byteLen; //Êý¾Ý´«ÊäÁ¿ 
		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(DMA1_Stream6, &DMA_InitStructure); //修改
		DMA_Cmd(DMA1_Stream6, ENABLE);
} 
void usart2_dma_ConfigHalfWord(uint16_t * usart2_dma_buffer, int byteLen)
{ 
		DMA_InitTypeDef  		  DMA_InitStructure;
	
		RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE); //修改
		DMA_DeInit(DMA1_Stream6);
		while (DMA_GetCmdStatus(DMA1_Stream6) != DISABLE){} //修改
		
		/* ÅäÖÃ DMA Stream */
		DMA_InitStructure.DMA_Channel = DMA_Channel_4; //ͨµÀÑ¡Ôñ
		DMA_InitStructure.DMA_PeripheralBaseAddr =((u32)&USART2->DR); //DMAÍâÉèµØÖ·
		DMA_InitStructure.DMA_Memory0BaseAddr = (u32	)usart2_dma_buffer; //DMA ´æ´¢Æ÷0µØÖ·
		DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //´æ´¢Æ÷µ½ÍâÉèģʽ
		DMA_InitStructure.DMA_BufferSize =byteLen; //Êý¾Ý´«ÊäÁ¿ 
		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(DMA1_Stream6, &DMA_InitStructure); //修改
		DMA_Cmd(DMA1_Stream6, ENABLE);
} 
void usart2_dma_ConfigByte(uint8_t * usart2_dma_buffer, int byteLen)
{ 
		DMA_InitTypeDef  DMA_InitStructure;
	
		RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE); //修改
		DMA_DeInit(DMA1_Stream6);
		while (DMA_GetCmdStatus(DMA1_Stream6) != DISABLE){} //修改
		
		/* ÅäÖÃ DMA Stream */
		DMA_InitStructure.DMA_Channel = DMA_Channel_4; //ͨµÀÑ¡Ôñ
		DMA_InitStructure.DMA_PeripheralBaseAddr =((u32)&USART2->DR); //修改
		DMA_InitStructure.DMA_Memory0BaseAddr = (u32	)usart2_dma_buffer; //修改
		DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //´æ´¢Æ÷µ½ÍâÉèģʽ
		DMA_InitStructure.DMA_BufferSize =byteLen; //Êý¾Ý´«ÊäÁ¿ 
		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(DMA1_Stream6, &DMA_InitStructure); //修改
		//DMA_SetCurrDataCounter(DMA1_Stream6, byteLen);
		DMA_Cmd(DMA1_Stream6, ENABLE);

我们除了需要修改串口号,IO口号,还要注意修改一个是数据流(RX和TX要注意区分,我这里是TX) 另一个是DMA内存号

.h文件别忘了加:

#include "sys.h"
#include 

void uart2_init(int bps);
void usart2_dma_Config(uint32_t * usart2_dma_buffer, int byteLen);
void usart2_dma_ConfigHalfWord(uint16_t * usart2_dma_buffer, int byteLen);
void usart2_dma_ConfigByte(uint8_t * usart2_dma_buffer, int byteLen);

DMA串口发送

我们在配置里面,已经将串口DMA发送的代码写过了,就是函数:usart2_dma_ConfigByte(uint8_t * usart2_dma_buffer, int byteLen)

我们是不可以把AD输出的input直接用串口输出的,这样会掉帧,所以所有的数据我们都先放进一个缓冲区,再从缓冲区里面读取。

之后我们只需要调用函数 usart2_dma_ConfigByte(数据缓冲区名称,一次传输的数据个数);

当我们这样写之后,会发现依然漏掉数据,这是因为仅仅串口DMA发送,只是不占用CPU资源,但是速度上不会有很大提高,我们需要使用“抽帧”方法 :

即计数存多幅图的图像,之后只输出一幅图图像,保留足够的时间给串口进行传输

在这里我们需要设计两个计数器:一个是像素计数器(计数输出一幅图的所有像素个数);另一个是帧计数器(计数输出图的个数) 

假如我一幅图是552个像素,那么像素计数器=552的时候,帧计数器应该+1

再利用AD的上升沿作为像素计数器加一的标志

总结如下:每来一个AD上升沿,我们进行一次中断,将AD输出数据input存一次缓冲区,像素计数器+1;当像素计数器=552的时候,帧计数器+1,当帧计数器=n时(n为多少帧发一次,n越大越慢,可以进行几次尝试,选择最小的n值),启动串口DMA发送

void TIM4_IRQHandler(void)
{
	int i=0;
	uint8_t frameBufCopy[1024];//缓冲区
	if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET) //中断
	{

		  frameBuf[pixleInd]=input;
		
			pixleInd++;     //像素计数器  
		  if(pixleInd>=552)
			{
				 pixleInd=0;
				 frameCount++;//帧计数器
			}
			if(frameCount>=100)
			{
				frameCount=0;
				
			   memcpy(frameBufCopy,frameBuf,552);
               usart2_dma_ConfigByte(frameBufCopy,552);//串口发送
			}

		
	}
	TIM_ClearITPendingBit(TIM4,TIM_IT_Update);  //
//}

}

我们要注意以下几点

(1)DMA发送的缓冲区和计数器需要定义成全局变量

extern uint8_t frameBuf[1024];  
extern u16 pixleInd;           
extern u16 frameCount;

(2)串口DMA发送前拷贝数据

在串口发送的时候,AD还在不断覆盖缓冲区,需要把将要发送的数据拷贝出来,放进一个单独的缓冲区,在这里,使用了memcpy函数

void *memcpy(void*dest, const void *src, size_t n); 

它用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度;

它由src指向地址为起始地址的连续n个字节的数据复制到以destin指向地址为起始地址的空间内。


 我这里的数据是用眼睛去看,由于视觉残留,少图没关系,如果大家对应数据完整性要求特别高, 而数据本身又是M级别的速率,就不要使用串口,应该使用网口

你可能感兴趣的:(stm32,arm,嵌入式硬件)