8、STM32之DMA

本文介绍了STM32中的DMA(直接存储器存取)功能,用于在外设和存储器之间高效传输数据,减少CPU的负担。在ADC(模拟数字转换器)连续模式下,DMA能自动将转换结果存入内存。文中详细阐述了DMA的基本结构、工作原理和参数设置,并提供了一个使用STM32 DMA与ADC结合的初始化代码示例,实现了ADC转换结果的自动存储和循环传输。
摘要由CSDN通过智能技术生成

一、DMA简介

在这里插入图片描述
DMA主要作用于外设和存储器存储器和存储器之间的数据转运。不需要CPU干预,节省CPU资源。经常性跟ADC一起作用,因为ADC使用扫描模式的时候,需要不停将数据进行转运,不然就会被下一个数据覆盖。

DMA可以直接访问STM32内部的存储器,包括运行内存SRAM、程序存储器Falsh和寄存器等。不然它不能访问也无法完成数据转运的工作。
存储器到存储器之间的触发一般会选择软件触发,外设到存储器的触发会选择硬件触发模式。

DMA特性

在这里插入图片描述
DMA (Direct Memory Access)直接存储器存取
其实外设寄存器也是一种存储器,所以这个名字十分科学

STM32内存映像

在这里插入图片描述
ROM:掉电不丢失
RAM:掉电丢失

DMA框图

所有的工作就是从某个地方读取内容,放到别的寄存器里
在这里插入图片描述
如上图所示,虽然有两个DMA,但是DMA总线只有一条。所以哪一个DMA过就是这个仲裁器根据通道的优先级进行一个确定。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
对Flash的工作(比如擦除和写)需要经过Flash接口控制器,一般情况下Flash是只读模式(就是你不能动他,DMA的数据转运到Falsh的地址就会出错)

DMA基本结构图

数据的移动方向,之前就说了可以是外设到存储器,以及存储器到存储器。
要进行数据转运,就要知道转运双方的参数。即双方的起始地址,数据宽度(一次转运按多大的宽度进行,有8位,16位,32位)和地址是否自增(就指针那种感觉,一般来说你的转运的终点肯定得自增,不然你转一次覆盖一次转的意义在哪里)。
M2M给1是软件触发,M2M给0是硬件触发
软件触发的目的是,最快把传输计数器清0,我理解为一种连续触发。
软件触发和循环模式不能同时使用,循环模式是清零后自动重装(那这不就死在这了)
硬件触发源可以选择ADC,串口,定时器等。比如ADC转换完成,串口收到数据,定时时间到,触发DMA进行转运。
DMA要开启有三个条件,首先开关控制必须打开,第二传输计数器不能是0,第三必须有触发源(硬件触发软件触发)。
触发一次,转运一次,传输器自减一次
当传输器为0且无自动重装,无论是否触发DMA都不会再进行转运
如果要再次开启DMA,就要先关闭开关控制,然后写入传输计时器,然后再打开开关控制。

在这里插入图片描述这里比较重要的DMA进行转换(我自己认为很像复制)的参数设定是
外设的起始地址,数据宽度,地址是否自增
存储器的起始地址,数据宽度,地址是否自增
我的理解是,外设和存储器就是一个叫法,就是你存的那个数,和要复制到的位置。
双方数据宽度有可能不太相同,不相同是遵循少的前面补0,多的舍弃高位数据。

地址自增就要看你是把多个地址的数给多个地址,还是把一个地址里的数给多个,还是把多个地址里的给1个,当然最后一种情况就是给的最后一个数成功复制了。
在这里插入图片描述

在这里插入图片描述
DMA传输一个完成后会产生中断,然后就是表明我传完了,可以干接下来的别的事比如传新的。
在这里插入图片描述

外设的DMA请求映像

你要使用某个外设触发源的时候,就必须选择这个触发源对应的通道
如果你要软件触发就无所谓通道了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、代码

#include "stm32f10x.h"   
#include "ADC.h"

u16 AD_value[2];
void Adc_Init()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	ADC_InitTypeDef ADC_InitStructure; 
	DMA_InitTypeDef DMA_InitStructure;
	//1、开启时钟ADC1的时钟和GPIOA的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE );
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE );
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	//2、配置分频,因为最大不能超过14MHz 72/6=12
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
	
	//3、配置GPIOC和ADC结构体 PC0 PC1 作为模拟通道输入引脚                         
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;		//模拟输入引脚
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);	
	
	ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_239Cycles5 );
	ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_239Cycles5 );
	
	//ADC_DeInit(ADC1); //复位ADC1
	
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;	//ADC工作模式:ADC1和ADC2工作在独立模式
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;	//模数转换工作在单通道模式
	//ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;	//模数转换工作在单次转换模式
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;	//模数转换工作在连续转换模式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//转换由软件而不是外部触发启动
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	//ADC数据右对齐
	ADC_InitStructure.ADC_NbrOfChannel = 2;	//顺序进行规则转换的ADC通道的数目
	ADC_Init(ADC1, &ADC_InitStructure);	//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器  

  //可以在这里进行模拟看门狗
	
	
	//DMA
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //外设地址
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //16位
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不自增,一直转运同一个位置的数据
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_value;//转运到数组
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//16位
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器地址自增
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA_InitStructure.DMA_BufferSize = 2; //两个ADC通道。传2次
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //DMA循环模式
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //1,硬件触发
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);
	
	DMA_Cmd(DMA1_Channel1,ENABLE);
	ADC_DMACmd(ADC1, ENABLE); //开启DMA触发信号
	//4、使能ADC1,(上电)
	ADC_Cmd(ADC1, ENABLE);
	//5、校准并等待校准结束
	ADC_ResetCalibration(ADC1);	//使能复位校准  
	 
	while(ADC_GetResetCalibrationStatus(ADC1));	//等待复位校准结束
	
	ADC_StartCalibration(ADC1);	 //开启AD校准
 
	while(ADC_GetCalibrationStatus(ADC1));	 //等待校准结束
	
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
	
}

//防止头文件重复包含
#ifndef __ADC_H
#define __ADC_H

extern u16 AD_value[2];
void Adc_Init();

#endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值