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中断请求

具有三个中断类型,都连接在一个中断通道上。 可以配置这个中断在数据传输完成后进入中断服务函数,进行数据处理,如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使能对应通道,设置源地址目标地址,即可进行传输。        

点这里可以跳转到人工智能网站

发表评论