STM32——DMA笔记

本文介绍了STM32中DMA(直接存储器访问)与ADC(模拟数字转换器)的结合使用,详细阐述了DMA的原理和作用,以及在ADC数据采集中的配置过程。通过DMA,数据从ADC自动传输到内存,无需CPU介入,提高了系统的效率。示例代码展示了如何配置DMA通道和ADC,以实现ADC1通道1和通道3的数据采集,并将数据存储到二维数组中进行后续处理。
摘要由CSDN通过智能技术生成

一、DMA是什么

DMA 传输将数据从一个 地址空间复制到另外一个地址空间。当 CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器 来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的 内存区。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工 作。DMA 传输方式无需 CPU 直接 控制传输,通过硬件为 RAM 与 I/O 设备 开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。

二、DMA的作用

“搬运”,将外部数据搬运进MCU,这件事不需要CPU去做,CPU可以去处理别的事情。

 三、DMA1的通道对应的“搬用源”

四、DMA的相关配置

 其中主要配置前三个标红的框,第四个紫色框根据一次搬运的长度选择,有一个8位、16位、32位。

u16 ADC1_Value[C_Channel_Sample][C_ADC_Channel] = { 0 };    // DMA搬运数据存放处(数组)

DMA的配置:

void DMA_Init_JX(void)
{
	DMA_InitTypeDef DMA_InitStructure;
	//NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); 			// 使能DMA时钟

	
	// DMA_CH1(ADC1)参数设置
	//--------------------------------------------------------------------------------------------------------------------------
	DMA_DeInit(DMA1_Channel1); 													// 将DMA的通道1寄存器重设为缺省值
	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR; 					// DMA外设ADC基地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC1_Value; 					// DMA内存基地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; 							// 内存作为数据传输的目的地
	DMA_InitStructure.DMA_BufferSize = C_ADC_Channel * C_Channel_Sample; 		// DMA通道的DMA缓存的大小
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 			// 外设地址寄存器不变
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; 					// 内存地址寄存器递增
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 数据宽度为16位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; 		// 数据宽度为16位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; 							// 工作在循环缓存模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_High; 						// DMA通道 x拥有高优先级
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; 								// DMA通道x没有设置为内存到内存传输
	DMA_Init(DMA1_Channel1, &DMA_InitStructure); 								// 根据DMA_InitStruct中指定的参数初始化DMA的通道
	//--------------------------------------------------------------------------------------------------------------------------
	
	DMA_Cmd(DMA1_Channel1, ENABLE);		// 启动DMA通道
	
}

ADC通道的配置:

void ADC1_Rocker_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	ADC_InitTypeDef ADC_InitStructure; 
	// NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1, ENABLE);	// 使能PA端口和ADC1时钟
	

	// 模拟输入:PA1、PA2、PA3、PA6
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;			// 模拟输入引脚
	//GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;		// 输入模式无需设置端口速度
	GPIO_Init(GPIOA, &GPIO_InitStructure);	

	
	// 配置ADC1_CH1、ADC1_CH2、ADC1_CH3、ADC1_CH6
	// ADC工作条件:ADC的时钟频率<=14MHz && ADC采样频率<=1MHz
	// ADC的总转换周期(必须>=1us) = (采样周期(1.5~239.5) + 12.5(固定转换周期)) / ADC时钟频率
	// 当:ADC的时钟频率==12MHz、采样周期==1.5,ADC转换周期≈1.17us
	//---------------------------------------------------------------------------------------------------------------------
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);						// ADC时钟6分频:72M/6=12M

	ADC_DeInit(ADC1);  										// 复位ADC1 

	// 设置ADC1的工作模式
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;		// ADC工作模式:ADC1和ADC2工作在独立模式
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;			// "扫描转换模式"使能。即:ADC工作在多通道模式
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;		// "连续转换模式"失能。即:ADC工作在单次转换模式
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;		// 转换由软件启动(也可以设置为外部触发)
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	// ADC数据对齐方式:右对齐
	ADC_InitStructure.ADC_NbrOfChannel = C_ADC_Channel;		// 设置顺序进行规则转换的ADC通道的数目
	ADC_Init(ADC1, &ADC_InitStructure);						// 初始化ADC1  
	
	
	// 为了能够正确地配置每一个 ADC 通道,用户在调用 ADC_Init()之后,
	// 必须调用ADC_xxxChannelConfig()来配置每个所使用通道的转换次序和采样时间。
	//--------------------------------------------------------------------
	ADC_RegularChannelConfig(ADC1, N_ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);	// 参数:ADC1,要设置的ADC通道序号,规则组采样顺序,采样周期
	// 此函数:配置的是"ADC1"的通道"1",它在规则组中的采样顺序是"1",采样周期是"ADC_SampleTime_239Cycles5"
	
	ADC_RegularChannelConfig(ADC1, N_ADC_Channel_3, 2, ADC_SampleTime_239Cycles5);
	// 此函数:配置的是"ADC1"的通道"3",它在规则组中的采样顺序是"2",采样周期是"ADC_SampleTime_239Cycles5"
	//---------------------------------------------------------------------------------------------------------------------
	
	
	/*
	//ADC1的中断NVIC设置
	//---------------------------------------------------------------------------------
	NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;  			// ADC1中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;  	// 抢占优先级3级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  		// 子优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 			// 使能ADC1_2_IRQn中断
	NVIC_Init(&NVIC_InitStructure);  							// 初始化NVIC寄存器
	
	ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE ); 						// 允许ADC1的EOC中断
	//---------------------------------------------------------------------------------
	*/
	
	
	// 开启ADC的DMA支持
	ADC_DMACmd(ADC1, ENABLE);					// 要实现DMA功能,还需独立配置DMA通道等参数
	
	
	ADC_Cmd(ADC1, ENABLE);						// 使能ADC1
	
	
	ADC_ResetCalibration(ADC1);					// 使能ADC1复位校准  
	 
	while(ADC_GetResetCalibrationStatus(ADC1));	// 等待ADC1复位校准结束
	
	ADC_StartCalibration(ADC1);	 				// 开启ADC1校准
 
	while(ADC_GetCalibrationStatus(ADC1));	 	// 等待ADC1校准结束

}

ADC采集:

在配置时,配置为软件触发方式。在使用ADC时,使用程序使能ADC1转换,也就是第一句。

搬运时DMA一次搬运16位数据,前8位为ADC1通道1的值,放在二维数组的第一列

                                                   后8位为ADC1通道3的值,放在二维数组的第二列

void ADC1_Value_average(void)
{
	// 摇杆检测
	//------------------------------------------------------------------------
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					// 软件使能ADC1转换
	
	// 转换完成后,DMA自动将数据搬运到指定位置
	//while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));		// 等待转换结束
	
	AM_ADC_Channel1_Sample += ADC1_Value[C_ADC_Sample][0];	// 得到
		
	AM_ADC_Channel3_Sample += ADC1_Value[C_ADC_Sample][1];

	C_ADC_Sample++ ;	// ADC实际采样次数

	// 判断是否已经采样10次
	if(C_ADC_Sample >= C_Channel_Sample)
	{ 
		C_ADC_Sample = 0; 
		
		AV_ADC_Channel1_Sample = AM_ADC_Channel1_Sample / C_Channel_Sample;		// 求通道1的平均值
		
		AM_ADC_Channel1_Sample = 0 ;
		
		
		AV_ADC_Channel3_Sample = AM_ADC_Channel3_Sample / C_Channel_Sample;		// 求通道3的平均值
		
		AM_ADC_Channel3_Sample = 0 ;
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值