STM32----摸石头过河系列(五)

今天总结一下DMA(Direct Memory Access,直接存储器存储),在以往我们从串口读取数据到内存的流程是,cpu通过串口读取导数据,然后CPU再将数据写入固定的内存。这种读取方式占用了大量的cpu资源,如果数据量非常大,CPU将耗费大量的时间来进行数据的读写操作。因此DMA应运而生。DMA的作用是啥呢?从名字能看出来一二,它可以直接从串口读取数据存储在内存中,几乎不占用CPU的资源。打个比方来说,一个砌墙的工人,他的主要工作是砌墙,但他首先需要耗费大量时间去搬砖到自己身边,然后才开始砌墙,这样他砌墙的进度肯定很慢。现在砌墙的工人不自己去搬砖了,他雇佣了另外一个人来帮助他去把砖搬过来,在搬之前砌墙的工人告诉搬砖的工人,他要去哪里搬砖、搬什么样的砖、搬砖的速度、搬过来后放在那里等问题。这样虽然在开始耗费了点时间去告诉搬砖工人一些要求,看起来比较繁琐,其实这些所占用的时间比起自己去搬砖耗费的时间少多了。在这个例子中,砌墙的工人就是我们所说的CPU,砖就是需要读写数据,搬砖工人就是DMA,DMA的引入为CPU读写数据节省了大量的时间,CPU只需要在开始耗费极少的时间对DMA进行相关的配置,就可以去做其他的事情。

我在做实验的过程中,遇到了很多狗血的bug,不过最终都得以解决。今天做的实验是利用DMA通过USART1向发送数据,同时LED持续点亮,当发送数据完成时,进入中断将LED熄灭,从而来验证DMA工作时CPU可以去做别的事情,而没有影响。下面来总结一下DMA的开发流程:

  1. NVIC中断优先级的配置
  2. DMA_InitTypeDef结构体的配置(记得调用NVIC配置的函数,我多次忘记调用)
  3. 初始化DMA,DMA_Init(DMA1_Channel4,&结构体)(DMA1_Channel4为串口1的发送,具体的可以看手册)
  4. 使能DMA,DMA_Cmd(DMA1_Channel4,ENABLE)
  5. 配置并使能中断DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE)
  6. 开始利用DMA进行串口发送数据USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
  7. 在中断中DMA_GetFlagStatus(DMA1_FLAG_TC4) == SET来确定发送数据完成

根据以上步骤进行先关的配置,其他的比如串口和LED的配置在前面已经配置过了直接把USART和LED的配置文件添加在该项目中即可。下面来看代码,代码中只有DMA的配置和主函数。

下面这是dma.c文件中的内容:

#include"dma.h"

uint16_t SendBuff[SENDSBUFF_SIZE];

static void NVIC_Config(void)
{
	NVIC_InitTypeDef nvic_struct;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	
	nvic_struct.NVIC_IRQChannel = DMA1_Channel4_IRQn;
	nvic_struct.NVIC_IRQChannelPreemptionPriority = 1;
	nvic_struct.NVIC_IRQChannelSubPriority = 1;
	nvic_struct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&nvic_struct);
}

void DMA1_Config(void)
{
	DMA_InitTypeDef dma_struct;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	NVIC_Config();
	
	dma_struct.DMA_PeripheralBaseAddr = USART1_BASE + 0x04;//数据要到达的地址
	dma_struct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//每次发送数据的大小
	dma_struct.DMA_MemoryBaseAddr = (u32)SendBuff;//数据所在的地址
	dma_struct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//每次读取数据的大小
	dma_struct.DMA_BufferSize = SENDSBUFF_SIZE;//总共传输的数据的大小
	dma_struct.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储数据的地址是否自动增加
	dma_struct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//数据到达的地址是否自动增加
	dma_struct.DMA_DIR = DMA_DIR_PeripheralDST;//传输的方向内存到外设
	dma_struct.DMA_M2M = DMA_M2M_Disable;//内存到内存是否允许
	dma_struct.DMA_Mode = DMA_Mode_Normal;//传输方式,发送一次还是循环发送
	dma_struct.DMA_Priority = DMA_Priority_Medium;//优先级
	DMA_Init(DMA1_Channel4,&dma_struct);//初始化
	
	DMA_Cmd(DMA1_Channel4,ENABLE);//使能
	 
	DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);//中断配置并使能
	
}

SendBuff数组存储的是要发送的数据,两个函数为中断配置函数,在前面的外部中断中已经配置过了。第二个函数为DMA的配置函数,与其他外设的配置类似,先定义一个结构体,然后开启时钟。这个DMA1的时钟和之前的都不一样,在AHB上,配置时需要注意。

在main.c文件中:

#include"led.h"
#include"usart.h"
#include"dma.h"


extern uint16_t SendBuff[SENDSBUFF_SIZE];//引用外部定义的变量
int i;


int main(void)//主函数
{
	GPIO_LED_Config();
	USART1_Config();
	DMA1_Config();
	
	for(i = 0;i < SENDSBUFF_SIZE;i++)//为发送数据赋值
	{
		SendBuff[i] = 0xff;
	}
	printf("Send start!");
	USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
	LED(ON);
	printf("wait interrupt!");
	while(1);
}

void DMA1_Channel4_IRQHandler(void)//中断函数
{
	printf("into interrupt!");
	if(DMA_GetFlagStatus(DMA1_FLAG_TC4) == SET)//判断是否发送完成
	{
		printf("send success");
		LED(OFF);
		DMA_ClearFlag(DMA1_FLAG_TC4);//清除标志位
	}
}

在实验的过程中,编译出现symbol multiply defined错误的原因,因为在xxx.h中定义了许多变量,xxx.c中调用xxx.h中的变量,在主文件中也调用了xxx.h中的变量,导致变量被重复定义。

解决办法:不应该在xxx.h中定义xxx.c中使用的变量,应该在xxx.c中定义所需要的变量,然后再在主程序中将调用xxx.c中定义的变量使用extern 例:extern u32 test.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值