STM32F207 USART+DMA代码+个人理解

环境:STM32F207

目的USART通过DMA通信+DMA初步理解


1.之前发送数据的方式

①数据放到串口数据寄存器里面

②等待一个字节发送完成

③重复第一二步。 

看到我们平时的方式我们就会有个想法,如果我们发送五百个字节,我们就需要让CPU在这里等待五百次。也就是在等待过程中我们是不能够去做其他事情的,只能够通过一个while循环去查看串口状态寄存器里面对应的发送完成标志是否为1。


2.现在我们大致描述一下DMA传输模式:

①数据写好放在内存  

②告诉DMA,我们发送多少数据

③干其他事情

④有想要发送数据了,先看看发送完了没有,没有发送完需要等待

⑤重复①②③④步骤

我们看到其实DMA也需要等,很多同学就说,这不是一样吗,我们都需要等待。其实这个情况不一样,这就像一岁小孩吃饭和十岁小孩吃饭一个道理,一岁小孩吃饭需要我们大人一口一口的喂他,在这期间,我们不能够去干其他事情,只能够看到他,第一口吃完了才能够喂第二口。  但是十岁小孩子吃饭就不一样了,你把饭给弄好,然后给他,让他自己吃,这个期间我们就可以去洗衣服了,如果你还想让小孩子吃完这一碗饭再吃一碗,那么你等一会儿去看看就可以了。

言归正传,如果我们用普通方式去做的话,相当于软件去循环查询,但是DMA的话,相当于硬件来做,到底谁快谁慢,我们就不用说了。

其实说了那么多,就是想给大家普及一下DMA大致的工作方式,到底好不好,还是要看具体的项目,对时间的要求等等。下面我对我的代码进行一个讲解。


总体如下:

void usart1_Init(void)
{
	usart1_PortInit();	//相关管脚初始化
	usart1_IT_Init();	//中断初始化
	usart1_DmaInit();	//DMA初始化
	
}
其中PortInit和IT_Init就不需要详细说明了,和一般模式一样,只不过需要在PortInit里面加一个DMA使能,如下图所示:


这里调用了一个USART_DMACmd去使能对应的DMA功能,现在我们跟踪进去看看,这个函数到底做了什么,对应哪个寄存器哪一位,有什么用:

跟踪进去发现两个东西:


①USART_DMAReq 参数的值是什么,我们进到assert_param看到如下:


②再看看寄存器的说明:


其实关于DMAT,我个人的理解就是,使能了这个,我们就可以让USART控制器明白发送的数据可以从DMA通道过来,不一定要我们人为一个一个给DR寄存器里面放。其实我觉得这样理解也符合情理。


好了,上面将串口使能DMA说了,下面将要说说DMA配置了,这个也是我们这篇文章的重中之重,我个人理解,如果这个理解到了的话,比如AD+DMA什么的都应该能够理解了。


DMA学前普及:

在我们具体把一大堆代码搬上来之前,我们先说说DMA相关知识

1.流和通道的关系,直接上图


初学者看到这里一定会问,为哈先来这个图,因为F1系列和F2系列不同,F1系列只有通道(channel)一说,但是F2多了一个流(Stream),为啥?很显然,因为F2支持的DMA通道更加多了。我们要用USART1_TX,我们就马上能够看出来我们需要定位到Stream7-Channel4. 然后通过一大堆的设置,我们就完成了设置。


2.配置DMA需要想到哪些东西?所以我们应该有以下问题:

①我们使用的外设对应DMA 的StreamX channelX

②我们的外设地址是多少

③我们的内存地址是多少

④我们数据传输方向是什么,是内存->外设还是外设->内存

⑤内存的大小是多少

⑥外设地址是否需要增加,比如ADDR ADDR+1 ADDR+2

⑦内存地址是否需要增加,比如ADDR ADDR+1 ADDR+2

⑧内存和外设分别用的是byte,short,还是Int,也就是位宽是多少

⑨我们的DMA是一直运转还是只需要运转一次就停止

⑩当前DMA请求的等级高度

11.是否使用FIFO

12.如果使用FIFO,当FIFO数据存量为多少的时候将数据发送出去

13.还有就是对内存和外设burst模式的配置,这个地方我也不是很懂,具体的话,可以看看链接点击打开链接


如果能够理解上面我说的词汇的话,DMA设置就应该没多大问题了,下面可以上代码了,我都进行了备注,如下:

void usart1_DmaInit(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	DMA_InitTypeDef DMA_InitStructure;

	//使能DMA总线时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);
	//去初始化STREAM 7 ,USART1_TX流 将使用CHANNEL 4
	DMA_DeInit(DMA2_Stream7);

	//wait the DMA reset OK
	while(DMA_GetCmdStatus(DMA2_Stream7) != DISABLE){}

	//Configure DMA Stream
	DMA_InitStructure.DMA_Channel = DMA_Channel_4;	//通道4			
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;	//数据寄存器
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)usartBuf;	//内存地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;	//外设地址
	DMA_InitStructure.DMA_BufferSize = 200;	//传输数据个数
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;	//传输过程中外设地址增加失能
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;	//传输过程中内存地址增加失能
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;	//数据位宽
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;	//数据位宽
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;	//单次传输模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;	//DMA等级高度
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;	//不使用FIFO
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;	//FIFO溢出个数
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;	//单次突发模式
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;	//单次突发模式
	DMA_Init(DMA2_Stream7,&DMA_InitStructure);

	//使能DMA Stream7的传输中断
	DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE);
	

	//中断等级NVIC配置
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
	NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);

	DMA_Cmd(DMA2_Stream7,DISABLE);	
}

上面既然使用了NVIC配置,肯定少不了中断处理函数,其实这个中断函数就是等DMA把数据发送完了就会产生。如下:

extern unsigned  char Usart1DmaSendOverFlag;
void DMA2_Stream7_IRQHandler(void)
{

	#if 1
	if(DMA_GetITStatus(DMA2_Stream7,DMA_IT_TCIF7) == SET){
		DMA_ClearITPendingBit(DMA2_Stream7,DMA_IT_TCIF7);
		Usart1DmaSendOverFlag = 1;
	}	
	#endif
}


中断函数就不需要怎么说了吧,也很简单,如果有不懂得,请留言。

下面是我们怎么调用DMA来发送:


void msgOut(unsigned short len)
{
	
	volatile int count = 0;
	if(len > 200)
		return;
	
	DMA_SetCurrDataCounter(DMA2_Stream7,len);
	DMA_Cmd(DMA2_Stream7 , ENABLE);
}

//输出
void strout(char *p)
{
	int len = 0;
	len = strlen(p);
	msgOut(len);
}

void printk(char *msg,...)
{
	va_list argp;

	while(!Usart1DmaSendOverFlag);//等待上次数据发送完
	Usart1DmaSendOverFlag = 0;
	va_start(argp,msg);
	vsnprintf((char*)usartBuf , sizeof(usartBuf) , msg , argp);
	va_end(argp);
	strout((char*)usartBuf);
}

其实,我们调用DMA也就是直接发送请求,然后就OK了


  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
stm32f103的串口2可以通过DMA来实现数据传输,以下是串口2 DMA的源代码示例: ```c #include "stm32f10x.h" void USART2_Init(void) { // 使能串口2时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); // 初始化串口2的GPIO GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化串口2 USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; 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(USART2, &USART_InitStructure); // 使能串口2 USART_Cmd(USART2, ENABLE); } void DMA_Init(void) { // 使能DMA1时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 初始化DMA配置 DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(USART2->USART_DR); DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = 256; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 初始化DMA通道 DMA_Init(DMA1_Channel7, &DMA_InitStructure); // 使能DMA通道 DMA_Cmd(DMA1_Channel7, ENABLE); // 使能串口2的DMA发送 USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE); } ``` 以上是一个简单的串口2 DMA使用的初始化函数,首先通过USART2_Init函数初始化了串口2,然后通过DMA_Init函数初始化了DMA配置并使能了 DMA 通道和串口2的DMA发送功能。在实际使用时,可以根据具体需求进行相应的配置和调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值