STM32 使用DMA处理ADC 学习笔记!!

ADC:
1.STM32内部的ADC模块有三个ADC1,ADC2,ADC3,他们彼此独立,所以可以进行同步采样。
2ADC的输入时钟不得超过14MHz,它是由PCLK2经分频产生,要在RCC_CFGR配置,再ADC自己的寄存器中在没有时钟分频的配置位。
3.ADC转换时间:  STM32F103xx增强型产,时钟为56MHz时为1μ s( 时钟为72MHz为1.17 μ s)
4.ADC的转换精度默认设置为12位,输入范围: ADC输入范围:V REF-≤ VIN ≤ VREF+
5.共有18个通道,其中外部16个通道,内部两个通道,内部温度传感器连接在ADC1_IN16,内部参考电压V REFINT连接在ADC1_IN17
6.转换的启动方式:有外部触发,内部外设触发如TIMx,以及软件触发,一次触发转换一个组,软件使能方式通过设置ADC_CR2 寄存器的ADON。
7.两个组的概念,a.规则组:一般情况下使用的ADC转换序列;b.注入组,它的优先级高于规则组中的转换序列,当规则组正在转换的情况下,入股触发了注入组,他将会打断规则组正在进行的转换,知道注入组转换完成,再次会带规则则组转换。
8总的来说,规则转换的方式有两种,即连独立单次转换方式、间断一次启动转换n个通道,n可配置、连续不断地转换,知道知道设置了停止。连续加是扫描模式下,一次启动通道会在序列中逐个来回的转换,然而规则通道组只有一个数据寄存器ADCx_DR,因此下一次转换完成之前必须将上次转换的数据值读出,否则将会 被覆盖,这是一般会使能DMA请求,让每次数据刺激转换完成后产生EOC的同时,也产生DMA请求,DMA将DR中的数据传至存储器单元。
而单次转换模式下必须要每次完成之后查询EOC或利用中断将数据读出,然后再软件启动下一次转换,在这期间若要改变转换的序列也可以写入下次采集的通道。和以前使用AD的模式一样,但这样耗费时间。
间断模式,是在整个SQR寄存器组中通过设置n,一次制转换其中的几个,知道将这个序列转换完。暂时未用。
8.ADC的采样时间时刻配置的,在采样时间寄存器配置,一次转换所需要的时间TCONV= 采样时间+ 12.5 = 14 周期,12.5是转换周期。
9.有序数据只有12,不足16位,因此要设置第七方式,方便数据的提取,有注入组中能设置转换的品偏移量,即转换结果等于采样值减去偏移量的值,可能是负值,因此右对齐式,高位时符号位的扩展,规则组下高4位全部为0。
10.可以配置ADC转换的阀值,类似看门狗功能,ADC_HTR 和ADC_LTR寄存器分别配置上下限,不在这个范围内饰可以产生中断标志,用户可以选择进入中断。
11.外部触发模式,主要是外设的触发信号,TIM的中断时间来触发ADC的转换开始,达到控制采样时间的的目的,就不用如原来一样单独的写配置定时器中断。
12.双ADC使用,双ADC有很多中模式,最可能用到的就是双ADC规则同步转换方式,可查阅Datasheet来配置。
规则单次转换配置方式:
关闭CONT,关闭SCAN模式,设置n值等于1,ADC独立模式,初始化结构体基本上是这样。
然后向SQR组中的某个位置写入通道号,配置该通道的转换采样时间。
使能ADCx,比较重要的是在最后要进行ADC校准,否则可能不准,校准包括复位校准、AD校准,等待校准完成后才能开始转换。
每次转换完成查询EOC标志位,然后读取DR数据并且使能下一次转换,也可写入新的转换通道。
void Init_ADC()
{
	ADC_InitTypeDef ADC_InitStructure;  		
	GPIO_InitTypeDef GPIO_InitStructure; 			
		
		
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);				
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);																			
	GPIO_InitStructure.GPIO_Pin =GPIO_Pin_0|GPIO_Pin_1; 				
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;				
	GPIO_Init(GPIOA, &GPIO_InitStructure);        										

	GPIO_InitStructure.GPIO_Pin =GPIO_Pin_0|GPIO_Pin_1; 		
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;	
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);      
			
	ADC_DeInit(ADC1); 																																	
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;				
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;		//这是使用连续扫描模式时		
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;					
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;			
	ADC_InitStructure.ADC_NbrOfChannel = 2;																			    
//      ADC_InitStructure.ADC_ScanConvMode = DSIABLE;					
//	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;				
//	ADC_InitStructure.ADC_NbrOfChannel = 1;			
	ADC_Init(ADC1, &ADC_InitStructure);  
			
	ADC_RegularChannelConfig(ADC1,ADC_Channel_0, 1, ADC_SampleTime_239Cycles5 );  //
	ADC_RegularChannelConfig(ADC1,ADC_Channel_1, 2, ADC_SampleTime_239Cycles5 );  //
		
	/* Enable ADC1 DMA */
	ADC_DMACmd(ADC1, ENABLE);
	
	
	/* Enable ADC1 */
	ADC_Cmd(ADC1, ENABLE); 
	
	/* Enable ADC1 reset calibration register */   
	ADC_ResetCalibration(ADC1);  																											
	while(ADC_GetResetCalibrationStatus(ADC1)) 																
	{
		;
	}
		
	/* Start ADC1 calibration */
	ADC_StartCalibration(ADC1); 																													
	while(ADC_GetCalibrationStatus(ADC1)) 																					
	{
		;
	}
}

读取函数:
void GetADValue()																		
{
//	ADC_RegularChannelConfig(ADC1,ADC_Channel_0, 1, ADC_SampleTime_239Cycles5 );  //如要改变通道可重新写入
	ADC_SoftwareStartConvCmd(ADC1, ENABLE); 									
	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ))
	{
		;
	}
	AD_data[0] = ADC_GetConversionValue(ADC1);		//软件触发
}

DMA:
stm32F10x中配置了两个独立的DMA控制器,他们分别连接到了不同的外设。DMA1有7个通道,DMA2有5个通道,尽管通道之间独立,但同时DMA只能处理一个通道的请求。
DMA的特点是在脱离CPU的情况下直接利用数据总线,在外设和存储器自检进行数据传输。解放了CPU在数据传输过程中的消耗。
DMA的工作机制类似于中断响应,当外设的DMA时间产生后就产生DMA请求,DMA控制器根据优先级选择相应DMA通道的请求,执行数据传输。
1.优先级配置
分为软件和硬件两件配置两种:软件配置有4中方式,在DMA_CCRx中设置,00:低 01:中 10:高 11:最高,
可见软件设置的优先级数目比实际的DMA通道数目少,所以硬件优先级的判定就是在两个通道的软件优先级一样的情况下,比较通道号,序号低的具有更高的优先级。
2.数据传输设置
一旦DMA响应了某个外设的请求,就要开始在外设之间传输数据,这是要提供源地址和目标地址
在DMA_CPARx寄存器中设置外设寄存器的地址,在DMA_CMARx寄存器中设置数据存储器的地址。
然后在DMA_CMARx寄存器的DIR中设置传输的方向。
在DMA_CCRx寄存器中的PSIZE 和MSIZE位没别设置外设和存储器的位宽,一般要保持两个位宽一致。如果不一致在手册中也有提到,半字写入不会出错。
3.传输模式
一般一次请求会连续的传输一串数据,这是通过设置DMA_CNDTRx设置传输数据量,没传输一个数据将会递减1,。
设置设置DMA_CCRx寄存器中的PINC和MINC标志位设定是否开启增量模式,开启了增量的条件下降会根据位宽对地址进行增减。
循环:DMA_CCRx寄存器中的CIRC设置是否开启循环模式,如果开启将会在一次请求传输完毕之后,自动复位计数值和地址,重新开始传输。
4.存储器之间的互传
通过设置了DMA_CCRx寄存器中的MEM2MEM位来使能,同时软件设置了DMA_CCRx寄存器中的EN位启动DMA通道时,DMA传输将马上开始。DMA通道硬件请求外分配给了外外设,存储器之间互传用软件启动。
5.DMA的CPU中断请求
STM32 使用DMA处理ADC 学习笔记!!_第1张图片
具有三个中断类型,都连接在一个中断通道上。
可以配置这个中断在数据传输完成后进入中断服务函数,进行数据处理,如AD采样传输到存储器之后,进行软件滤波,均值等,当然也可以用查询的方式。
 
根据以上介绍,在使用DMA读取规则组连续转换时的AD值,基本步骤如下:
a.使能AD1的DMA请求允许,
b.设置DMA位连续循环(根据需要),并且使能AD1所在的DMA1通道1.
c.外设地址设置成AD1地址,存储地址根据程序设置为AD采样值得数组。
d.设置位宽为半字,设置优先级,设置CNDTR为1,一次传输一个数据,数据长度是半字。
e.使能DMA1
这样当ADC1每次转换完成一一个数据,就会产生DMA通道,数据会传输到指定的内存,注意的是,要合理安排采样和存储的方式,符合数组的序列要求。
具体的流程要按实际需要来配合使用。
 

void Init_DMA()
{
  DMA_InitTypeDef DMA_InitStructure;
  DMA_DeInit(DMA1_Channel1);
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADCConvertedValue;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                    //
  DMA_InitStructure.DMA_BufferSize = 1;                                 //
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;            //
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;               //
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;  //
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;    //
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                       
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;                       
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                       
  DMA_Init(DMA1_Channel1, &DMA_InitStructure);        
    /* Enable DMA1 channel1 */
  DMA_Cmd(DMA1_Channel1, ENABLE);
}
 
实际DMA的思想很简单,外设DMA事件请求使能,DMA使能对应通道,设置源地址目标地址,即可进行传输。
 
 
 
 

你可能感兴趣的:(stm32)