你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【stm32f407】ADC实验

[复制链接]
aimejia 发布时间:2018-6-1 15:44
1.      ADC简介

STM32F4xx系列一般都有3个ADC,这些ADC可以独立使用,也可以使用双重/三重模式(提高采样率)。STM32F4的ADC是12位逐次逼近型的模拟数字转换器。它有19个通道,可测量16个外部源、2个内部源和Vbat通道的信号。这些通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。 模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。

STM32F407VGT6包含有3个ADC。STM32F4的ADC最大的转换速率为2.4Mhz,也 就 是转换时间为0.41us(在ADCCLK=36M,采样周期为3个ADC时钟下得到),不要让ADC的时钟超过36M,否则将导致结果准确度下降

STM32F4将ADC的转换分为2个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。

通过一个形象的例子可以说明:假如你在家里的院子内放了5个温度探头,室内放了3个温度探头;你需要时刻监视室外温度即可,但偶尔你想看看室内的温度;因此你可以使用规则通道组循环扫描室外的5个探头并显示AD转换结果,当你想看室内温度时,通过一个按钮启动注入转换组(3个室内探头)并暂时显示室内温度,当你放开这个按钮后,系统又会回到规则通道组继续检测室外温度。从系统设计上,测量并显示室内温度的过程中断了测量并显示室外温度的过程,但程序设计上可以在初始化阶段分别设置好不同的转换组,系统运行中不必再变更循环转换的配置,从而达到两个任务互不干扰和快速切换的结果。可以设想一下,如果没有规则组和注入组的划分,当你按下按钮后,需要从新配置AD循环扫描的通道,然后在释放按钮后需再次配置AD循环扫描的通道。上面的例子因为速度较慢,不能完全体现这样区分(规则通道组和注入通道组)的好处,但在工业应用领域中有很多检测和监视探头需要较快地处理,这样对AD转换的分组将简化事件处理的程序并提高事件处理的速度。

STM32F4其ADC的规则通道组最多包含16个转换,而注入通道组最多包含4个通道。

STM32F4的ADC在单次转换模式下,只执行一次转换,该模式可以通过ADC_CR2寄存器的ADON位(只适用于规则通道)启动,也可以通过外部触发启动(适用于规则通道和注入通道),这时CONT位为0。

以规则通道为例,一旦所选择的通道转换完成,转换结果将被存在ADC_DR寄存器中,EOC(转换结束)标志将被置位,如果设置了EOCIE,则会产生中断。然后ADC将停止,直到下次启动

接下来,我们介绍一下我们执行规则通道的单次转换,需要用到的ADC寄存器。第一个要介绍的是ADC控制寄存器(ADC_CR1和ADC_CR2)。ADC_CR1的各位描述如图所示:

1.png

ADC_CR1的SCAN位,该位用于设置扫描模式,由软件设置和清除,如果设置为1,则使用扫描模式,如果为0,则关闭扫描模式。在扫描模式下,由ADC_SQRx或ADC_JSQRx寄存器选中的通道被转换。如果设置了EOCIE或JEOCIE,只在最后一个通道转换完毕后才会产生EOC或JEOC中断。

ADC_CR1[25:24]用于设置ADC的分辨率,详细的对应关系如图所示

2.png

本章我们使用12位分辨率,所以设置这两个位为0就可以了。接着我们介绍ADC_CR2,该寄存器的各位描述如图所示:

3.png

该寄存器我们也只针对性的介绍一些位:ADON位用于开关AD转换器。而CONT位用于设置是否进行连续转换,我们使用单次转换,所以CONT位必须为0。ALIGN用于设置数据对齐,我们使用右对齐,该位设置为0。

EXTEN[1:0]用于规则通道的外部触发使能设置,详细的设置关系如图所示

4.png

我们这里使用的是软件触发,即不使用外部触发,所以设置这2个位为0即可。ADC_CR2的SWSTART位用于开始规则通道的转换,我们每次转换(单次转换模式下)都需要向该位写1。

第二个要介绍的是ADC通用控制寄存器(ADC_CCR),该寄存器各位描述如图所示:

5.png

该寄存器我们也只针对性的介绍一些位:TSVREFE位是内部温度传感器和Vrefint通道使能位,内部温度传感器我们将在下一章介绍,这里我们直接设置为0。ADCPRE[1:0]用于设置ADC输入时钟分频,00~11分别对应2/4/6/8分频,STM32F4的ADC最大工作频率是36Mhz,而ADC时 钟(ADCCLK)来 自APB2,APB2频率一般是84Mhz,所以我们一般设置ADCPRE=01,即4分频,这样得到ADCCLK频率为21Mhz。MULTI[4:0]用于多重ADC模式选择,详细的设置关系如图所示:

6.png

本章我们仅用了ADC1(独立模式),并没用到多重ADC模式,所以设置这5个位为0即可

第三个要介绍的是ADC采样时间寄存器(ADC_SMPR1和ADC_SMPR2),这两个寄存器用于设置通道0~18的采样时间,每个通道占用3个位。ADC_SMPR1的各位描述如图所示:

7.png

对于每个要转换的通道,采样时间建议尽量长一点,以获得较高的准确度,但是这样会降低ADC的转换速率。ADC的转换时间可以由以下公式计算:

Tcovn=采样时间+12个周期

其中:Tcovn为总转换时间,采样时间是根据每个通道的SMP位的设置来决定的。例如,当ADCCLK=21Mhz的时候,并设置3个周期的采样时间,则得到:Tcovn=3+12=15个周期=0.71us。

第四个要介绍的是ADC规则序列寄存器(ADC_SQR1~3),该寄存器总共有3个,这几个寄存器的功能都差不多,这里我们仅介绍一下ADC_SQR1,该寄存器的各位描述如图所示:

8.png

L[3:0]用于存储规则序列的长度,我们这里只用了1个,所以设置这几个位的值为0。其他的SQ13~16则存储了规则序列中第13~16个通道的编号(0~18)。另外两个规则序列寄存器同ADC_SQR1大同小异,我们这里就不再介绍了,要说明一点的是:我们选择的是单次转换,所以只有一个通道在规则序列里面,这个序列就是SQ1,至于SQ1里面哪个通道,完全由用户自己设置,通过ADC_SQR3的最低5位(也就是SQ1)设置。

第五个要介绍的是ADC规则数据寄存器(ADC_DR)。规则序列中的AD转化结果都将被存在这个寄存器里面,而注入通道的转换结果被保存在ADC_JDRx里面。ADC_DR的各位描述如图:

9.png

这里要提醒一点的是,该寄存器的数据可以通过ADC_CR2的ALIGN位设置左对齐还是右对齐。在读取数据的时候要注意。

最后一个要介绍的ADC寄存器为ADC状态寄存器(ADC_SR),该寄存器保存了ADC转换时的各种状态。该寄存器的各位描述如图所示:

10.png

这里我们仅介绍将要用到的是EOC位,我们通过判断该位来决定是否此次规则通道的AD转换已经完成,如果该位位1,则表示转换完成了,就可以从ADC_DR中读取转换结果,否则等待转换完成。

2.      ADC库函数应用步骤

使用到的库函数分布在stm32f4xx_adc.c文件和stm32f4xx_adc.h文件中。下面讲解其详细设置步骤

STM32F407ZGT6的ADC1通道5在PA5上,所以,我们先要使能GPIOA的时钟,然后设置PA5为模拟输入。同时我们要把PA5复用为ADC,所以我们要使能ADC1时钟。这里特别要提醒,对于IO口复用为ADC我们要设置模式为模拟输入,而不是复用功能,也不需要调用GPIO_PinAFConfig函数来设置引脚映射关系。

使能GPIOA时钟和ADC1时钟都很简单,具体方法为:

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//使能GPIOA时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); //使能ADC1时钟

初始化GPIOA5为模拟输入,方法也多次讲解,关键代码为:

GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AN;//模拟输入

这里需要说明一下,ADC的通道与引脚的对应关系在STM32F4的数据手册可以查到,我们这里使用ADC1的通道5,在数据手册中的表格为:

11.png

这里我们把ADC1~ADC3的引脚与通道对应关系列出来, 16个外部源的对应关系如下表:

12.png

2)设置ADC的通用控制寄存器CCR,配置ADC输入时钟分频,模式为独立模式等。

在库函数中,初始化CCR寄存器是通过调用ADC_CommonInit来实现的:

void ADC_CommonInit(ADC_CommonInitTypeDef*ADC_CommonInitStruct)

这里我们不再李处初始化结构体成员变量,而是直接看实例。初始化实例为:

ADC_CommonInitStructure.ADC_Mode =ADC_Mode_Independent;//独立模式

ADC_CommonInitStructure.ADC_TwoSamplingDelay= ADC_TwoSamplingDelay_5Cycles;

ADC_CommonInitStructure.ADC_DMAAccessMode =ADC_DMAAccessMode_Disabled;

ADC_CommonInitStructure.ADC_Prescaler =ADC_Prescaler_Div4;

ADC_CommonInit(&ADC_CommonInitStructure);//初始化

第一个参数ADC_Mode用来设置是独立模式还是多重模式,这里我们选择独立模式。

第二个参数ADC_TwoSamplingDelay用来设置两个采样阶段之间的延迟周期数。这个比较好理解。取值范围为:ADC_TwoSamplingDelay_5Cycles~ADC_TwoSamplingDelay_20Cycles。

第三个参数ADC_DMAAccessMode是DMA模式禁止或者使能相应DMA模式。

第四个参数ADC_Prescaler用来设置ADC预分频器。这个参数非常重要,这里我们设置分频系数为4分频ADC_Prescaler_Div4,保证ADC1的时钟频率不超过36MHz。

3)初始化ADC1参数,设置ADC1的转换分辨率,转换方式,对齐方式,以及规则序列等相关信息。

在设置完分通用控制参数之后,我们就可以开始ADC1的相关参数配置了,设置单次转换模式、触发方式选择、数据对齐方式等都在这一步实现。具体的使用函数为:

void ADC_Init(ADC_TypeDef* ADCx,ADC_InitTypeDef* ADC_InitStruct)

初始化实例为:

ADC_InitStructure.ADC_Resolution =ADC_Resolution_12b;//12位模式

ADC_InitStructure.ADC_ScanConvMode =DISABLE;//非扫描模式

ADC_InitStructure.ADC_ContinuousConvMode =DISABLE;//关闭连续转换

ADC_InitStructure.ADC_ExternalTrigConvEdge= ADC_ExternalTrigConvEdge_None;

//禁止触发检测,使用软件触发

ADC_InitStructure.ADC_DataAlign =ADC_DataAlign_Right;//右对齐

ADC_InitStructure.ADC_NbrOfConversion =1;//1个转换在规则序列中

ADC_Init(ADC1,&ADC_InitStructure);//ADC初始化

第一个参数ADC_Resolution用来设置ADC转换分辨率。取值范围为:ADC_Resolution_6b,

ADC_Resolution_8b,ADC_Resolution_10b和ADC_Resolution_12b。

第二个参数ADC_ScanConvMode用来设置是否打开扫描模式。这里我们设置单次转换所以不打开扫描模式,值为DISABLE。

第三个参数ADC_ContinuousConvMode用来设置是单次转换模式还是连续转换模式,这里我们是单次,所以关闭连续转换模式,值为DISABLE。

第三个参数ADC_ExternalTrigConvEdge用来设置外部通道的触发使能和检测方式。这里我们直接禁止触发检测,使用软件触发。还可以设置为上升沿触发检测,下降沿触发检测以及上升沿和下降沿都触发检测。

第四个参数ADC_DataAlign 用来设置数据对齐方式。取值范围为右对齐

ADC_DataAlign_Right和左对齐ADC_DataAlign_Left。

第五个参数ADC_NbrOfConversion用来设置规则序列的长度,这里我们是单次转换,所以值为1即可。

实际上还有个参数ADC_ExternalTrigConv是用来为规则组选择外部事件。因为我们前面配置的是软件触发,所以这里我们可以不用配置。如果选择其他触发方式方式,这里需要配置。

4)开启AD转换器。

在设置完了以上信息后,我们就开启AD转换器了(通过ADC_CR2寄存器控制)。

ADC_Cmd(ADC1, ENABLE);//开启AD转换器

5)读取ADC值。

在上面的步骤完成后,ADC就算准备好了。接下来我们要做的就是设置规则序列1里面的通道,然后启动ADC转换。在转换结束后,读取转换结果值值就是了。

这里设置规则序列通道以及采样周期的函数是:

void ADC_RegularChannelConfig(ADC_TypeDef*ADCx, uint8_t ADC_Channel,

uint8_t Rank, uint8_t ADC_SampleTime);

我们这里是规则序列中的第1个转换,同时采样周期为480,所以设置为:

ADC_RegularChannelConfig(ADC1,ADC_Channel_5, 1, ADC_SampleTime_480Cycles );

软件开启ADC转换的方法是:

ADC_SoftwareStartConvCmd(ADC1);//使能指定的ADC1的软件转换启动功能

开启转换之后,就可以获取转换ADC转换结果数据,方法是:

ADC_GetConversionValue(ADC1);

同时在AD转换中,我们还要根据状态寄存器的标志位来获取AD转换的各个状态信息。库函数获取AD转换的状态信息的函数是:

FlagStatus ADC_GetFlagStatus(ADC_TypeDef*ADCx, uint8_t ADC_FLAG)

比如我们要判断ADC1的转换是否结束,方法是:

while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));//等待转换结束

这里还需要说明一下ADC的参考电压,探索者STM32F4开发板使用的是STM32F407ZGT6,该芯片只有Vref+参考电压引脚,Vref+的输入范围为:1.8~VDDA。探索者STM32F4开发板通过P7端口,来设置Vref+的参考电压,默认的我们是通过跳线帽将ref+接到VDDA,参考电压就是3.3V。如果大家想自己设置其他参考电压,将你的参考电压接在Vref+上就OK了(注意要共地)。另外,对于还有Vref-引脚的STM32F4芯片,直接就近将Vref-接VSSA就可以了。

本章我们的参考电压设置的是3.3V。

通过以上几个步骤的设置,我们就能正常的使用STM32F4的ADC1来执行AD转换操作了。

3.      ADC实例

实现采集3.3V电压和GND电压,用串口打印出来

Adc.h

  1. [cpp] view plain copy
  2. #ifndef _ADC_H_H_H  
  3. #define _ADC_H_H_H  
  4. #include "stm32f4xx_gpio.h"  
  5. #include "stm32f4xx_rcc.h"  
  6. #include "stm32f4xx_adc.h"  
  7.    
  8. void Adc_Init(void);                                                        //ADC通道初始化  
  9. u16 Get_Adc(u8 ch);                                                     //获得某个通道值  
  10. u16 Get_Adc_Average(u8 ch,u8 times);//得到某个通道给定次数采样的平均值   
  11. #endif  
复制代码
Adc.c
  1. [cpp] view plain copy
  2. #include "adc.h"  
  3. #include "delay.h"  
  4. //初始化ADC                                                                                                                                                                                                                                       
  5. void Adc_Init(void)  
  6. {  
  7. GPIO_InitTypeDef GPIO_InitStructure;  
  8. ADC_CommonInitTypeDef ADC_CommonInitStructure;  
  9. ADC_InitTypeDef      ADC_InitStructure;  
  10.                  
  11. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟  
  12. RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟  
  13.    
  14.   //先初始化ADC1通道5 IO口  
  15. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PA5 通道5  
  16. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模拟输入  
  17. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;//不带上下拉  
  18. GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化   
  19.    
  20. RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE);                 //ADC1复位  
  21. RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE);               //复位结束  
  22.    
  23. ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;//独立模式  
  24. ADC_CommonInitStructure.ADC_TwoSamplingDelay =ADC_TwoSamplingDelay_5Cycles;//两个采样阶段之间的延迟5个时钟  
  25. ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;//DMA失能  
  26. ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;//预分频4分频。ADCCLK=PCLK2/4=84/4=21Mhz,ADC时钟最好不要超过36Mhz  
  27. ADC_CommonInit(&ADC_CommonInitStructure);//初始化  
  28.    
  29. ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//12位模式  
  30. ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非扫描模式         
  31. ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//关闭连续转换  
  32. ADC_InitStructure.ADC_ExternalTrigConvEdge =ADC_ExternalTrigConvEdge_None;//禁止触发检测,使用软件触发  
  33.   ADC_InitStructure.ADC_DataAlign= ADC_DataAlign_Right;//右对齐     
  34. ADC_InitStructure.ADC_NbrOfConversion = 1;//1个转换在规则序列中 也就是只转换规则序列1  
  35. ADC_Init(ADC1, &ADC_InitStructure);//ADC初始化  
  36.    
  37. ADC_Cmd(ADC1, ENABLE);//开启AD转换器   
  38. }  
  39.    
  40. //获得ADC值  
  41. //ch: @ref ADC_channels  
  42. //通道值 0~16取值范围为:ADC_Channel_0~ADC_Channel_16  
  43. //返回值:转换结果  
  44. u16 Get_Adc(u8 ch)   
  45. {  
  46. ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_480Cycles );            //ADC1,ADC通道,480个周期,提高采样时间可以提高精确度                                             
  47.    
  48. ADC_SoftwareStartConv(ADC1);                            //使能指定的ADC1的软件转换启动功能         
  49.                   
  50. while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束  
  51.    
  52. return ADC_GetConversionValue(ADC1);           //返回最近一次ADC1规则组的转换结果  
  53. }  
  54.    
  55. u16 Get_Adc_Average(u8 ch,u8 times)  
  56. {  
  57.   u32temp_val=0;  
  58.   u8t;  
  59. for(t=0;t<times;t++)  
  60.   {  
  61.    temp_val+=Get_Adc(ch);  
  62.    delay_ms(5);  
  63.   }  
  64.   returntemp_val/times;  
  65. }  
复制代码
Main.c
  1. [cpp] view plain copy
  2. #include "led.h"  
  3. #include "key.h"  
  4. #include "delay.h"  
  5. #include "uart.h"  
  6. #include "exit.h"  
  7. #include "iwdog.h"  
  8. #include "pwm.h"  
  9. void User_Delay(__IO uint32_t nCount)  
  10. {  
  11. while(nCount--)  
  12.   {  
  13.   }  
  14. }  
  15. static int count = 0;  
  16. int main(void)  
  17. {  
  18.    
  19.   u16adcx;  
  20. float temp;  
  21. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  
  22.   My_USART2_Init();  
  23.   delay_init(168);  
  24.   printf("main test start\n");  
  25.   Adc_Init();  
  26.    
  27.   while(1)  
  28.    {  
  29.     adcx=Get_Adc_Average(ADC_Channel_5,20);  
  30.     temp=(float)adcx*(3.3/4096);  
  31.     printf("adc value:(%d) \n",adcx);  
  32.     delay_ms(250);  
  33.       
  34.    }  
  35. }  
复制代码





转载自于忠军

收藏 1 评论1 发布时间:2018-6-1 15:44

举报

1个回答
epochal 回答时间:2018-6-2 08:08:26
谢谢分享!

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版