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

【stm32f407】ADC实验

[复制链接]
aimejia 发布时间:2018-6-1 15:44
1.      ADC简介
  z' o( v+ v( u  t
4 J+ {8 @4 n' A7 I' J% oSTM32F4xx系列一般都有3个ADC,这些ADC可以独立使用,也可以使用双重/三重模式(提高采样率)。STM32F4的ADC是12位逐次逼近型的模拟数字转换器。它有19个通道,可测量16个外部源、2个内部源和Vbat通道的信号。这些通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。 模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。* s0 v4 N: z( H; E* ^

+ Y  t! _) Z' nSTM32F407VGT6包含有3个ADC。STM32F4的ADC最大的转换速率为2.4Mhz,也 就 是转换时间为0.41us(在ADCCLK=36M,采样周期为3个ADC时钟下得到),不要让ADC的时钟超过36M,否则将导致结果准确度下降
/ Q. W% r7 j- }, C. g4 K
+ @/ I* J, y3 j! {  i. ~" i: H. }- v, TSTM32F4将ADC的转换分为2个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。
! n* W4 s. W1 R' [" P$ |4 f# f) h- [  i/ f4 |
通过一个形象的例子可以说明:假如你在家里的院子内放了5个温度探头,室内放了3个温度探头;你需要时刻监视室外温度即可,但偶尔你想看看室内的温度;因此你可以使用规则通道组循环扫描室外的5个探头并显示AD转换结果,当你想看室内温度时,通过一个按钮启动注入转换组(3个室内探头)并暂时显示室内温度,当你放开这个按钮后,系统又会回到规则通道组继续检测室外温度。从系统设计上,测量并显示室内温度的过程中断了测量并显示室外温度的过程,但程序设计上可以在初始化阶段分别设置好不同的转换组,系统运行中不必再变更循环转换的配置,从而达到两个任务互不干扰和快速切换的结果。可以设想一下,如果没有规则组和注入组的划分,当你按下按钮后,需要从新配置AD循环扫描的通道,然后在释放按钮后需再次配置AD循环扫描的通道。上面的例子因为速度较慢,不能完全体现这样区分(规则通道组和注入通道组)的好处,但在工业应用领域中有很多检测和监视探头需要较快地处理,这样对AD转换的分组将简化事件处理的程序并提高事件处理的速度。" L* `# ^* }2 j' G

1 L; S2 o: Z0 ~0 ?STM32F4其ADC的规则通道组最多包含16个转换,而注入通道组最多包含4个通道。
* N  M1 `' U! S' p# [" h
  J3 ^$ x2 @& }$ ?STM32F4的ADC在单次转换模式下,只执行一次转换,该模式可以通过ADC_CR2寄存器的ADON位(只适用于规则通道)启动,也可以通过外部触发启动(适用于规则通道和注入通道),这时CONT位为0。
0 J3 N, h: u5 \/ u; ^# ~
& B& r  y# C% b以规则通道为例,一旦所选择的通道转换完成,转换结果将被存在ADC_DR寄存器中,EOC(转换结束)标志将被置位,如果设置了EOCIE,则会产生中断。然后ADC将停止,直到下次启动
' j0 c  F2 P( Q+ |+ h$ f5 b/ `. U$ Y# T" h1 {% N- `. K
接下来,我们介绍一下我们执行规则通道的单次转换,需要用到的ADC寄存器。第一个要介绍的是ADC控制寄存器(ADC_CR1和ADC_CR2)。ADC_CR1的各位描述如图所示:
, y9 X/ ?$ i( V3 @1 \+ u5 c
5 _2 q9 M6 L5 }0 P- W! k1 _" i
1.png

6 m9 r0 Z/ s* `ADC_CR1的SCAN位,该位用于设置扫描模式,由软件设置和清除,如果设置为1,则使用扫描模式,如果为0,则关闭扫描模式。在扫描模式下,由ADC_SQRx或ADC_JSQRx寄存器选中的通道被转换。如果设置了EOCIE或JEOCIE,只在最后一个通道转换完毕后才会产生EOC或JEOC中断。$ Z; b0 s5 e+ F) C* P
1 L8 S: a, r" n7 `2 N
ADC_CR1[25:24]用于设置ADC的分辨率,详细的对应关系如图所示
5 x# e) t8 j# u2 U2 k3 J0 f
& o, N8 [9 F! b+ b- r" N7 Z- x4 v
2.png

' O6 p' [+ m/ _' B' s  P* ?本章我们使用12位分辨率,所以设置这两个位为0就可以了。接着我们介绍ADC_CR2,该寄存器的各位描述如图所示:4 n3 y  |; u# g& K0 ^# I
$ N& F' B$ W# h  R
3.png
: a$ j0 \/ I( f6 R0 H4 `
该寄存器我们也只针对性的介绍一些位:ADON位用于开关AD转换器。而CONT位用于设置是否进行连续转换,我们使用单次转换,所以CONT位必须为0。ALIGN用于设置数据对齐,我们使用右对齐,该位设置为0。* `- I8 X) X+ S: ]. N0 B' O

2 i4 w1 p" G8 WEXTEN[1:0]用于规则通道的外部触发使能设置,详细的设置关系如图所示
* l. h6 S. p" J/ p7 F$ S, V- ?
! ^, _% d6 n' u$ t1 D1 Q! L
4.png

; |% s5 ~8 P: H2 b8 X. ?我们这里使用的是软件触发,即不使用外部触发,所以设置这2个位为0即可。ADC_CR2的SWSTART位用于开始规则通道的转换,我们每次转换(单次转换模式下)都需要向该位写1。! }; D! R9 s4 p: r( L+ ~
0 G8 R' }5 `2 {- I1 y5 I
第二个要介绍的是ADC通用控制寄存器(ADC_CCR),该寄存器各位描述如图所示:
! n7 U0 U. P( e4 s$ P4 [* r
: `( H8 X7 ^' B7 F( M# ?
5.png

4 d+ [, |0 S1 `" B: [/ w! _! l该寄存器我们也只针对性的介绍一些位: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模式选择,详细的设置关系如图所示:; q+ w  |0 P( u. A3 a& G& Y' j4 H
! M. a0 ]' p  G+ |+ }
6.png
2 [$ L+ E9 K) [: ^
本章我们仅用了ADC1(独立模式),并没用到多重ADC模式,所以设置这5个位为0即可
6 a5 t7 ?, o; R, t) m+ a: `9 a5 X$ U
第三个要介绍的是ADC采样时间寄存器(ADC_SMPR1和ADC_SMPR2),这两个寄存器用于设置通道0~18的采样时间,每个通道占用3个位。ADC_SMPR1的各位描述如图所示:
9 Z, _" l- ]7 y$ W
$ \$ s- V8 i( [& O2 Y9 D1 Y2 m
7.png
: s  v! I3 C- B; \, Y6 q
对于每个要转换的通道,采样时间建议尽量长一点,以获得较高的准确度,但是这样会降低ADC的转换速率。ADC的转换时间可以由以下公式计算:
) P0 g, H; @5 C3 h; z
% u7 k7 Q" x$ K! m/ ?4 rTcovn=采样时间+12个周期) o; B& r& Z) J9 C& b6 Z  [+ O

/ B& [2 k' l# m9 f2 @% J其中:Tcovn为总转换时间,采样时间是根据每个通道的SMP位的设置来决定的。例如,当ADCCLK=21Mhz的时候,并设置3个周期的采样时间,则得到:Tcovn=3+12=15个周期=0.71us。8 p! w1 ^5 G1 }% v" e

. p1 {! B* z9 i2 d7 M8 R第四个要介绍的是ADC规则序列寄存器(ADC_SQR1~3),该寄存器总共有3个,这几个寄存器的功能都差不多,这里我们仅介绍一下ADC_SQR1,该寄存器的各位描述如图所示:
2 ~( R8 a% e5 S' V
4 g$ I. ?2 D: L) E/ B2 k
8.png

% ]* Q2 V% j( ?L[3:0]用于存储规则序列的长度,我们这里只用了1个,所以设置这几个位的值为0。其他的SQ13~16则存储了规则序列中第13~16个通道的编号(0~18)。另外两个规则序列寄存器同ADC_SQR1大同小异,我们这里就不再介绍了,要说明一点的是:我们选择的是单次转换,所以只有一个通道在规则序列里面,这个序列就是SQ1,至于SQ1里面哪个通道,完全由用户自己设置,通过ADC_SQR3的最低5位(也就是SQ1)设置。
1 E& ^8 A, j  `+ M
  ?! D4 ~9 y$ Q+ S1 v第五个要介绍的是ADC规则数据寄存器(ADC_DR)。规则序列中的AD转化结果都将被存在这个寄存器里面,而注入通道的转换结果被保存在ADC_JDRx里面。ADC_DR的各位描述如图:
2 x0 V; Y2 q, ]/ I
9 n6 \; l2 r3 N% a, _, b( ~5 n6 }
9.png
/ n) C4 f# ~' D3 _6 F
这里要提醒一点的是,该寄存器的数据可以通过ADC_CR2的ALIGN位设置左对齐还是右对齐。在读取数据的时候要注意。  ?" I5 X$ B: I0 e/ x/ f9 ^
! L, W% p: d" w. ?* l
最后一个要介绍的ADC寄存器为ADC状态寄存器(ADC_SR),该寄存器保存了ADC转换时的各种状态。该寄存器的各位描述如图所示:! F4 [1 I2 j6 p+ q- @( G
9 H) S/ X  L% @
10.png
1 C) o) B7 |; m4 S: Y
这里我们仅介绍将要用到的是EOC位,我们通过判断该位来决定是否此次规则通道的AD转换已经完成,如果该位位1,则表示转换完成了,就可以从ADC_DR中读取转换结果,否则等待转换完成。
2 |: ]/ c" B6 r) p+ _2 h* j& t1 q/ r7 U  Z; e. n; g3 {
2.      ADC库函数应用步骤2 g5 K! ~5 `& z& e

! h  `/ Z3 b' J, }( o( l使用到的库函数分布在stm32f4xx_adc.c文件和stm32f4xx_adc.h文件中。下面讲解其详细设置步骤
6 |( w/ w8 A) J0 B5 j0 Q( [; q* s+ G& }: O& z2 _3 h
STM32F407ZGT6的ADC1通道5在PA5上,所以,我们先要使能GPIOA的时钟,然后设置PA5为模拟输入。同时我们要把PA5复用为ADC,所以我们要使能ADC1时钟。这里特别要提醒,对于IO口复用为ADC我们要设置模式为模拟输入,而不是复用功能,也不需要调用GPIO_PinAFConfig函数来设置引脚映射关系。
+ c  A; g/ k7 k' A$ [# e. v1 {
3 n& i. c& @: o3 z. D/ m使能GPIOA时钟和ADC1时钟都很简单,具体方法为:$ k; G1 R+ J; @
, M7 N, Y( _, `* v
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//使能GPIOA时钟
5 I1 p. d& `1 T# [* h+ K( f5 ]  i1 V& R* j' ^
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); //使能ADC1时钟4 ]. c0 g. \2 @2 j
0 V2 Q! c- w0 B  z# [+ w
初始化GPIOA5为模拟输入,方法也多次讲解,关键代码为:
# O0 a4 u& i: t, N  e) l5 F
8 P0 ?1 v. |6 h& ~: O. @* YGPIO_InitStructure.GPIO_Mode =GPIO_Mode_AN;//模拟输入
5 Z4 u3 M$ l' p7 @+ }2 o4 |' p! F8 ^, D' Q0 Z0 V/ |0 s. F) C
这里需要说明一下,ADC的通道与引脚的对应关系在STM32F4的数据手册可以查到,我们这里使用ADC1的通道5,在数据手册中的表格为:! S, ?/ y2 L, }# V# @) S8 v3 f- k
% D! x5 U* \, ]+ q3 a& N
11.png

3 s7 Q+ u9 z# x& z6 l这里我们把ADC1~ADC3的引脚与通道对应关系列出来, 16个外部源的对应关系如下表:4 ^6 j* k9 j/ K1 u) F8 U9 U1 l

  _) `7 I: z1 X- U. S4 v
12.png
2 k+ }$ c4 r8 w8 a, e/ c
2)设置ADC的通用控制寄存器CCR,配置ADC输入时钟分频,模式为独立模式等。. ]3 l' \/ G4 _4 [6 o8 l) E- K

% |7 K* K, x- Z. k$ j1 n; V4 {) n9 |在库函数中,初始化CCR寄存器是通过调用ADC_CommonInit来实现的:5 ^2 L' X3 t2 V" u9 ]1 `$ O

' }. Q/ H2 A( f% x) M# G( |1 E) X2 \void ADC_CommonInit(ADC_CommonInitTypeDef*ADC_CommonInitStruct)2 M+ u% l$ X- H# e  f
' F& ?( t) d  Y: e& h6 u
这里我们不再李处初始化结构体成员变量,而是直接看实例。初始化实例为:
! B  w# m1 c' h" ^6 E- p8 n; Q7 V, X! |% N# Z# d) d
ADC_CommonInitStructure.ADC_Mode =ADC_Mode_Independent;//独立模式; \5 n; `6 B8 \" h8 m" L1 x7 D
4 ~3 }9 B! X, K& A/ N* h
ADC_CommonInitStructure.ADC_TwoSamplingDelay= ADC_TwoSamplingDelay_5Cycles;, a7 w+ P3 ~, B! r) M) a5 r
3 L; w3 N2 D* D" s) S+ V
ADC_CommonInitStructure.ADC_DMAAccessMode =ADC_DMAAccessMode_Disabled;2 P: E- @8 M* a8 ?8 {# z
% t1 E* l! L9 P8 B% }1 [
ADC_CommonInitStructure.ADC_Prescaler =ADC_Prescaler_Div4;' n" T% B' T- R- G: K
( f+ p+ E" r5 T' ?, L) d& z+ l% r2 `
ADC_CommonInit(&ADC_CommonInitStructure);//初始化& c# [1 S* J6 m

  W4 |$ A& x8 k6 f& @# ^第一个参数ADC_Mode用来设置是独立模式还是多重模式,这里我们选择独立模式。
! r, P4 x) B+ ?& f. k% r- t' L5 }! O3 Z
第二个参数ADC_TwoSamplingDelay用来设置两个采样阶段之间的延迟周期数。这个比较好理解。取值范围为:ADC_TwoSamplingDelay_5Cycles~ADC_TwoSamplingDelay_20Cycles。8 }! U" E) q! g7 Y6 L2 E5 A
/ r' b0 j7 ^, t& n# C5 Y, G
第三个参数ADC_DMAAccessMode是DMA模式禁止或者使能相应DMA模式。
8 o  e( K! |  _8 w7 i
7 G+ s- v5 l3 d# S- _* D第四个参数ADC_Prescaler用来设置ADC预分频器。这个参数非常重要,这里我们设置分频系数为4分频ADC_Prescaler_Div4,保证ADC1的时钟频率不超过36MHz。+ b2 S8 e2 Y  W0 M7 i7 Y" d+ r
+ i( A$ i* W: Y. `3 |
3)初始化ADC1参数,设置ADC1的转换分辨率,转换方式,对齐方式,以及规则序列等相关信息。
& R! p  [; K9 a, e( l1 a& C& S. ~' V2 C8 h) Y: E7 p8 W$ ?
在设置完分通用控制参数之后,我们就可以开始ADC1的相关参数配置了,设置单次转换模式、触发方式选择、数据对齐方式等都在这一步实现。具体的使用函数为:
9 a/ O2 E( W* ^, n* D2 O# w8 |
% R. a+ q' K4 tvoid ADC_Init(ADC_TypeDef* ADCx,ADC_InitTypeDef* ADC_InitStruct); O, |& H- Y2 |
* ]; s+ l: |: w
初始化实例为:
$ x8 r9 P+ E" A, O. M$ \5 q
8 v* c: j4 E" Z& u; S' P0 V( ?& JADC_InitStructure.ADC_Resolution =ADC_Resolution_12b;//12位模式. o; J+ u: P3 S( c/ j2 }" x$ U
. s6 h8 {3 F; B) k# U' T1 `
ADC_InitStructure.ADC_ScanConvMode =DISABLE;//非扫描模式! J. a+ y6 y: F, B4 L

; ?! g9 B4 A' k  B; \ADC_InitStructure.ADC_ContinuousConvMode =DISABLE;//关闭连续转换
' i- B8 A8 q5 y  r  q- k2 A7 o7 E& A. U* M* u( Z4 j
ADC_InitStructure.ADC_ExternalTrigConvEdge= ADC_ExternalTrigConvEdge_None;2 _4 y" N$ h; m
& X& c4 R* f$ ?; [4 `5 g, N
//禁止触发检测,使用软件触发4 r0 r( D5 R& {* Q6 t5 S

- ?5 ?* Y% F6 q" OADC_InitStructure.ADC_DataAlign =ADC_DataAlign_Right;//右对齐) h; l8 ^9 ]& c. x, y8 |

4 J9 ]# j8 n2 e1 z7 i6 Y9 Z  yADC_InitStructure.ADC_NbrOfConversion =1;//1个转换在规则序列中8 C# b; j6 c, E. p
: p  U3 K* n! z+ i  ]6 ]  t
ADC_Init(ADC1,&ADC_InitStructure);//ADC初始化
- }2 F/ O* f2 Z5 B# Z5 C
& l- B6 O4 m* I8 r8 B" t4 Z第一个参数ADC_Resolution用来设置ADC转换分辨率。取值范围为:ADC_Resolution_6b,) C0 U( @7 x7 s$ z- \- H5 F# c
2 I$ g7 L6 i" v# L! E. g7 P
ADC_Resolution_8b,ADC_Resolution_10b和ADC_Resolution_12b。
5 V8 {5 X! d5 g* Y$ d' u- \
3 }& _  k' O/ {+ j$ I* a第二个参数ADC_ScanConvMode用来设置是否打开扫描模式。这里我们设置单次转换所以不打开扫描模式,值为DISABLE。$ R; _& F! r' q' k: @

0 m) p0 {8 u# B0 ~; k& i2 G2 {第三个参数ADC_ContinuousConvMode用来设置是单次转换模式还是连续转换模式,这里我们是单次,所以关闭连续转换模式,值为DISABLE。$ |8 D/ E' ^0 W/ y

* R' l. E# f' [* W# g第三个参数ADC_ExternalTrigConvEdge用来设置外部通道的触发使能和检测方式。这里我们直接禁止触发检测,使用软件触发。还可以设置为上升沿触发检测,下降沿触发检测以及上升沿和下降沿都触发检测。
+ @% ^* p1 T& M3 ?$ }  \# p! ?3 S& O$ ]" Y; K# `1 W9 P
第四个参数ADC_DataAlign 用来设置数据对齐方式。取值范围为右对齐7 x9 Z1 a7 j) @& s% }/ R$ e

) N+ u$ A/ c" x  Y" V: o3 S+ }' NADC_DataAlign_Right和左对齐ADC_DataAlign_Left。. m: e# N% H& [/ T$ `8 R
$ i6 I; u' ?9 \3 I3 ~5 N
第五个参数ADC_NbrOfConversion用来设置规则序列的长度,这里我们是单次转换,所以值为1即可。, ]) [9 e. Y2 q9 R# Y; X5 d: _

2 d0 }0 Q0 r' I8 K4 W实际上还有个参数ADC_ExternalTrigConv是用来为规则组选择外部事件。因为我们前面配置的是软件触发,所以这里我们可以不用配置。如果选择其他触发方式方式,这里需要配置。
* ~5 s& p$ z7 D- R0 S1 N* W( j( A
# y/ i% y, M8 A& |! r9 v4)开启AD转换器。5 [; S+ H5 b# D  M. R
1 Y) b, j3 ~* {4 F- d, @0 v- s
在设置完了以上信息后,我们就开启AD转换器了(通过ADC_CR2寄存器控制)。7 Q5 F& \7 y3 m# r6 D& w, f8 @

2 v6 q6 D. e  g6 T! o4 DADC_Cmd(ADC1, ENABLE);//开启AD转换器3 l4 N, y2 U* j5 g, S$ n
, ^" z/ }8 p1 D# ], ^
5)读取ADC值。8 s4 t; t5 W) ?

2 i5 g5 C6 v; F$ M在上面的步骤完成后,ADC就算准备好了。接下来我们要做的就是设置规则序列1里面的通道,然后启动ADC转换。在转换结束后,读取转换结果值值就是了。
1 Q! m* Z# `) `: n" O# o8 g( f* ~4 E5 d* _
这里设置规则序列通道以及采样周期的函数是:4 T1 b5 F8 n0 Q

1 B% K" W. C. y6 [8 evoid ADC_RegularChannelConfig(ADC_TypeDef*ADCx, uint8_t ADC_Channel,
" Z) ]* Y6 k, W: r) k5 ^* N, v  @  h4 R% U
uint8_t Rank, uint8_t ADC_SampleTime);+ y2 u, K$ `- n, D. s3 O& D* Z) ^# }
; t1 t& n5 t, w
我们这里是规则序列中的第1个转换,同时采样周期为480,所以设置为:
+ W4 N% R. w# T8 d/ t2 R& J2 Q* V9 S+ |- G( `( z& ]9 E' _
ADC_RegularChannelConfig(ADC1,ADC_Channel_5, 1, ADC_SampleTime_480Cycles );* z8 b1 c. ?2 i* J3 |/ z, ^

, N3 K9 |1 |  x/ D0 @软件开启ADC转换的方法是:$ E5 t' ~. M3 Y" l

. v7 j2 N5 r9 V% {+ t" {+ u- KADC_SoftwareStartConvCmd(ADC1);//使能指定的ADC1的软件转换启动功能
5 k1 b7 d, u, i+ d7 Z! r. q9 A: V6 x- ?2 W
开启转换之后,就可以获取转换ADC转换结果数据,方法是:
4 d; K/ I- _9 \, J8 F: ~# d& J6 g& ?% q' D' y& Z. ~- t' e
ADC_GetConversionValue(ADC1);, [9 @# L, c; L5 U& l: j+ z& U

4 Y0 P3 j0 [3 k( Z) k0 }$ F同时在AD转换中,我们还要根据状态寄存器的标志位来获取AD转换的各个状态信息。库函数获取AD转换的状态信息的函数是:
2 r9 I7 D' g( A1 C$ H
7 ^& k2 o/ v( N; C8 mFlagStatus ADC_GetFlagStatus(ADC_TypeDef*ADCx, uint8_t ADC_FLAG)! `) @2 i. N! y* J" H6 y+ z0 h! c
- ^" W$ k$ Y+ e( r' i( E7 t
比如我们要判断ADC1的转换是否结束,方法是:. l; N$ P( f) F6 q
9 }7 P/ b  D2 T$ A: w: c
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));//等待转换结束
: S+ ~  e# p4 A) u* p" K$ U
1 x5 c2 @+ u7 x7 I2 x这里还需要说明一下ADC的参考电压,探索者STM32F4开发板使用的是STM32F407ZGT6,该芯片只有Vref+参考电压引脚,Vref+的输入范围为:1.8~VDDA。探索者STM32F4开发板通过P7端口,来设置Vref+的参考电压,默认的我们是通过跳线帽将ref+接到VDDA,参考电压就是3.3V。如果大家想自己设置其他参考电压,将你的参考电压接在Vref+上就OK了(注意要共地)。另外,对于还有Vref-引脚的STM32F4芯片,直接就近将Vref-接VSSA就可以了。
: d2 `( R: Z' a! e4 v9 a4 D$ h( @, l
本章我们的参考电压设置的是3.3V。+ B( V% e, b# w: A- e3 V' Z9 S
1 k5 Z. z# z4 i6 z7 v8 Q
通过以上几个步骤的设置,我们就能正常的使用STM32F4的ADC1来执行AD转换操作了。/ a3 H" [; Q( d; G  O
: b2 r" N! w' R. b
3.      ADC实例
3 z( q6 I% ]2 ]! Y, ?* B) K  E6 P
* s3 k; q5 \% y0 G4 T+ x; g实现采集3.3V电压和GND电压,用串口打印出来+ B. p4 Q, `- J7 j: I8 }- s
7 S* ^# P( A' `- H
Adc.h
4 `- c. u) W' Q+ N* [* a7 G
9 N3 @# G/ y7 q; k2 s3 d( c
  1. [cpp] view plain copy& H8 {3 o) d9 o" f- W! H6 E
  2. #ifndef _ADC_H_H_H  / T: W* `. K9 y2 o
  3. #define _ADC_H_H_H  
    , N- @- C' s- J1 e
  4. #include "stm32f4xx_gpio.h"  ( g8 {+ q. u  p# h6 Z" Q: P: I
  5. #include "stm32f4xx_rcc.h"  6 E2 s. S2 L2 x4 ~
  6. #include "stm32f4xx_adc.h"  
    . ?* B$ [3 L, d
  7.    8 S# M& p( ?6 S" ^; M; a
  8. void Adc_Init(void);                                                        //ADC通道初始化  
    . e0 w# {. H0 j* W
  9. u16 Get_Adc(u8 ch);                                                     //获得某个通道值  
    6 H, |0 D4 ?: d  I. v# A* N; v
  10. u16 Get_Adc_Average(u8 ch,u8 times);//得到某个通道给定次数采样的平均值   
    ' g' [. i5 |! \2 p
  11. #endif  
复制代码
Adc.c* v% Q! C$ i7 G' F
  1. [cpp] view plain copy
    $ `+ f. Y7 N$ V+ T* b+ q
  2. #include "adc.h"  7 D2 [6 u- t1 v9 ?9 i) l0 q
  3. #include "delay.h"  9 e6 B' S# F9 K; R. ]4 B
  4. //初始化ADC                                                                                                                                                                                                                                       8 ^' \& j) b" @$ ~/ N
  5. void Adc_Init(void)  ! V' _6 s: ^+ U) O1 f$ l  V- a$ i) j
  6. {  " H: m- R4 B5 H: {! O/ l+ x
  7. GPIO_InitTypeDef GPIO_InitStructure;  : e/ C' E' n  r
  8. ADC_CommonInitTypeDef ADC_CommonInitStructure;  
    ( p( Q1 V! H2 g+ ^  r+ A, {$ X
  9. ADC_InitTypeDef      ADC_InitStructure;  
    " J% p. a+ F6 U$ _
  10.                  
    # T$ L, Q7 E% o* K  b4 |
  11. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟    t  h6 I6 c" I) Y& i
  12. RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟  4 S! a# X0 w) E/ A4 r9 A( f) w
  13.    
    3 ]8 _. D( {0 J1 e! C  z
  14.   //先初始化ADC1通道5 IO口  
    ! T3 J9 ?1 n' ~1 |6 V" a- z4 {
  15. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PA5 通道5  % W  ?* K4 l* U9 N0 a
  16. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模拟输入  
    9 ^" i, F, B7 E+ b3 u
  17. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;//不带上下拉  0 T/ \2 y2 m' V7 I8 t; L% x
  18. GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化   
    $ M& r- _3 T2 E. O  i) C
  19.    
    7 W% J$ ^% |8 ?$ e  X. d
  20. RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE);                 //ADC1复位  , p& E: f- i) S$ G! I. d1 Z1 z
  21. RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE);               //复位结束  : F5 \, L$ I- y# u1 g5 Z
  22.    
    + Q9 A: Q2 X7 d! \# C
  23. ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;//独立模式  0 K$ t! t! w% W. L
  24. ADC_CommonInitStructure.ADC_TwoSamplingDelay =ADC_TwoSamplingDelay_5Cycles;//两个采样阶段之间的延迟5个时钟  # \1 B! A: a0 t' G
  25. ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;//DMA失能  
    $ T. y7 x+ |& b. g
  26. ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;//预分频4分频。ADCCLK=PCLK2/4=84/4=21Mhz,ADC时钟最好不要超过36Mhz  
    & v5 }! ~% z* c) Q
  27. ADC_CommonInit(&ADC_CommonInitStructure);//初始化  7 J6 p6 j- k3 I5 t7 v
  28.    
    9 Y. g6 [* d# T
  29. ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//12位模式  7 I! c6 S5 y1 r6 W+ i( F. v1 t
  30. ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非扫描模式         
    7 U5 H, Q' m7 ]- C2 ?. e
  31. ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//关闭连续转换  * S$ d' O' r- z; e* P
  32. ADC_InitStructure.ADC_ExternalTrigConvEdge =ADC_ExternalTrigConvEdge_None;//禁止触发检测,使用软件触发  ! }6 ?6 h6 X7 L3 ]' s7 ~: M
  33.   ADC_InitStructure.ADC_DataAlign= ADC_DataAlign_Right;//右对齐       y2 x% G0 t5 q4 A7 S
  34. ADC_InitStructure.ADC_NbrOfConversion = 1;//1个转换在规则序列中 也就是只转换规则序列1  
    / O+ @5 u! n+ r0 [% o- h
  35. ADC_Init(ADC1, &ADC_InitStructure);//ADC初始化  $ I: S: v, h7 S& U8 ~
  36.    2 o8 p) y7 k( q) j* ]0 Q
  37. ADC_Cmd(ADC1, ENABLE);//开启AD转换器   
    ( p2 u# N3 s5 ]- z$ L" {2 A6 c
  38. }  / U  P, n3 z9 Y: H9 k9 @8 J& S, x
  39.    5 A& f. Q" [" D" @9 ~
  40. //获得ADC值  ) w! }" A- R; O! V
  41. //ch: @ref ADC_channels  0 n/ A0 @6 m* ~+ g9 m& {, H
  42. //通道值 0~16取值范围为:ADC_Channel_0~ADC_Channel_16  . ^: Y( m  o# [0 p6 d( H
  43. //返回值:转换结果  * c7 o" T  m8 l* p1 G9 q* T5 L
  44. u16 Get_Adc(u8 ch)   
    # g3 p4 e; h' E+ b  j5 Y
  45. {  " I% C9 C  a  v  @- a
  46. ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_480Cycles );            //ADC1,ADC通道,480个周期,提高采样时间可以提高精确度                                             
    " G* \5 J+ Q# Q- A- Y
  47.    
    1 ~) e" s, J, I5 ]" g+ \
  48. ADC_SoftwareStartConv(ADC1);                            //使能指定的ADC1的软件转换启动功能         
    2 X+ I+ a4 x+ I: V' g  g  |
  49.                   
    ) g7 a& a# _& Z, }. ^' {: |5 A# W" Z
  50. while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束  . z. N: w" A$ f9 t1 H
  51.    0 b' b2 Q: H8 `, f) U. Q
  52. return ADC_GetConversionValue(ADC1);           //返回最近一次ADC1规则组的转换结果  
    $ c! k- j) c9 i  u5 G
  53. }  5 k+ R0 w6 G1 D( M( L$ |2 n( W4 j
  54.    
    8 `2 z2 Q0 B0 A8 o
  55. u16 Get_Adc_Average(u8 ch,u8 times)  # A3 R# I$ g# w$ `2 f1 E7 k! M
  56. {  8 D5 [: z( o& \- M) W
  57.   u32temp_val=0;  
    ( d5 j7 D  K- }( [
  58.   u8t;  
    # ?/ U4 `/ E* j' _4 e) p3 N
  59. for(t=0;t<times;t++)  
    3 I4 e$ {& [) I7 `! f, T, |  [
  60.   {  ! X& J2 q( U3 g% t& q
  61.    temp_val+=Get_Adc(ch);  
    0 Z/ w! {! ]+ s$ A6 v6 a5 G) X
  62.    delay_ms(5);  # u# L+ h6 ?; C8 X4 P3 L0 ^
  63.   }  
    $ G6 A6 B, g2 z' W. k1 W5 ?
  64.   returntemp_val/times;  
    : H+ E( Z1 f  N2 _
  65. }  
复制代码
Main.c1 X; b( K% V* c
  1. [cpp] view plain copy
    + l5 t1 f& j% U! F8 p0 D' e4 K5 S/ m. D
  2. #include "led.h"  - m" f4 n; c; j4 A( D4 M3 W8 a
  3. #include "key.h"  
    5 R) c1 j' i/ l, n8 w4 V7 u
  4. #include "delay.h"  
    9 y& u/ \. d) v2 a. q6 H) c) d
  5. #include "uart.h"  , C+ b$ O" t$ p5 h1 G
  6. #include "exit.h"  ) Y& y! S5 Y$ U, ]
  7. #include "iwdog.h"  6 g9 {: Q7 G) o. i- x/ N
  8. #include "pwm.h"  ) e$ k6 f" e! W/ I/ M
  9. void User_Delay(__IO uint32_t nCount)  
    : I2 I) F5 h" ]' i7 C) t
  10. {  
    8 |* ], I! y7 Z8 `4 R( v# J3 p4 y8 _
  11. while(nCount--)  
    & g/ ^/ h4 R; n' c3 Z/ [
  12.   {  
      o; n  {( V- e* |: `, K3 T5 s
  13.   }  ; A3 J* I; b+ a
  14. }  * c- {: p5 Y3 g
  15. static int count = 0;  0 b! f. j5 g% \6 c( m7 p: e9 q- ~6 ^' f
  16. int main(void)  - d, l' `  I9 _3 o, `
  17. {  1 h; n) ^4 W: @# t) A4 |' R
  18.    & q# X' o9 h% r1 [' j: p- u$ y
  19.   u16adcx;  4 i) v5 E6 H( g0 P# D. t
  20. float temp;  $ m" U1 s% B: O) T
  21. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  
    & n9 z! e! a  I( s, ?
  22.   My_USART2_Init();  
    ! Y$ l. C+ V6 |) _4 d8 u
  23.   delay_init(168);  
    4 ~; z. w% L1 w. d2 R" p: d; C
  24.   printf("main test start\n");  
    ; H3 B  X$ N5 a- H" H2 [/ k& \: i
  25.   Adc_Init();  
    % M" M# [# k; \% ^! B  M
  26.    
    " s3 T- F" l  z6 L  d* [- @# c& Z
  27.   while(1)  
    9 n; }6 B2 k' |
  28.    {  - `: v6 ]: L5 h
  29.     adcx=Get_Adc_Average(ADC_Channel_5,20);  7 v, S( N( H* v0 a9 \) G. \" ~
  30.     temp=(float)adcx*(3.3/4096);  
    % N& i# Y) W# L+ Q7 a, H
  31.     printf("adc value:(%d) \n",adcx);  
    ' h4 [5 J4 A4 f" \
  32.     delay_ms(250);  . W' k9 K6 g0 h/ D3 S
  33.       
    % M0 z7 L$ M( v5 d( m- S
  34.    }  
    * e- ?7 O5 {0 S- y
  35. }  
复制代码
0 Y' s( {& L+ d6 m. ~

( g, O0 s5 ~8 [. C& Q+ [3 P* L3 T8 ]
# |. f# b0 G, v* h8 J. i
: {6 m! O4 p; m- N* x
0 H' m% L, S) o. S0 d) ~1 h6 }/ W3 k0 k* ]转载自于忠军6 N$ h" H0 D: [$ W
& l3 m7 @3 _' [# w% ?3 U- ]
收藏 2 评论1 发布时间:2018-6-1 15:44

举报

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

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版