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

【stm32f407】ADC实验

[复制链接]
aimejia 发布时间:2018-6-1 15:44
1.      ADC简介8 ^7 x) |, k& \) z3 n
8 {: W6 O8 T: U. r5 a. Q
STM32F4xx系列一般都有3个ADC,这些ADC可以独立使用,也可以使用双重/三重模式(提高采样率)。STM32F4的ADC是12位逐次逼近型的模拟数字转换器。它有19个通道,可测量16个外部源、2个内部源和Vbat通道的信号。这些通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。 模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。5 Q6 c+ [# V! J6 {) M! y1 s

, B  I+ m; C1 y/ E/ I' ?) P! ISTM32F407VGT6包含有3个ADC。STM32F4的ADC最大的转换速率为2.4Mhz,也 就 是转换时间为0.41us(在ADCCLK=36M,采样周期为3个ADC时钟下得到),不要让ADC的时钟超过36M,否则将导致结果准确度下降6 \/ A" k$ O" ~. t

# x! p$ ]; m+ T" F  r) `STM32F4将ADC的转换分为2个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。
6 G( k& }# Q, g+ _: b
) a* R0 T/ }. h" w' Z通过一个形象的例子可以说明:假如你在家里的院子内放了5个温度探头,室内放了3个温度探头;你需要时刻监视室外温度即可,但偶尔你想看看室内的温度;因此你可以使用规则通道组循环扫描室外的5个探头并显示AD转换结果,当你想看室内温度时,通过一个按钮启动注入转换组(3个室内探头)并暂时显示室内温度,当你放开这个按钮后,系统又会回到规则通道组继续检测室外温度。从系统设计上,测量并显示室内温度的过程中断了测量并显示室外温度的过程,但程序设计上可以在初始化阶段分别设置好不同的转换组,系统运行中不必再变更循环转换的配置,从而达到两个任务互不干扰和快速切换的结果。可以设想一下,如果没有规则组和注入组的划分,当你按下按钮后,需要从新配置AD循环扫描的通道,然后在释放按钮后需再次配置AD循环扫描的通道。上面的例子因为速度较慢,不能完全体现这样区分(规则通道组和注入通道组)的好处,但在工业应用领域中有很多检测和监视探头需要较快地处理,这样对AD转换的分组将简化事件处理的程序并提高事件处理的速度。
5 i: p6 q/ {$ G3 k( J
4 l3 E! F- N5 W9 E/ J( qSTM32F4其ADC的规则通道组最多包含16个转换,而注入通道组最多包含4个通道。
- Y; k& t' C+ s6 l) V6 f% I! f
* B0 X. {, t' h+ U# SSTM32F4的ADC在单次转换模式下,只执行一次转换,该模式可以通过ADC_CR2寄存器的ADON位(只适用于规则通道)启动,也可以通过外部触发启动(适用于规则通道和注入通道),这时CONT位为0。
( o( Q+ j1 N% O# X0 R9 P, H% z
& D2 o( I0 a: t以规则通道为例,一旦所选择的通道转换完成,转换结果将被存在ADC_DR寄存器中,EOC(转换结束)标志将被置位,如果设置了EOCIE,则会产生中断。然后ADC将停止,直到下次启动
6 J+ l! W* p: N* i' l2 A, Y' T& V9 J* ^
接下来,我们介绍一下我们执行规则通道的单次转换,需要用到的ADC寄存器。第一个要介绍的是ADC控制寄存器(ADC_CR1和ADC_CR2)。ADC_CR1的各位描述如图所示:. m% N3 s/ C2 K
3 o. }* I5 \2 s; z# y( S( p5 n# {
1.png

6 G) S- o( C9 kADC_CR1的SCAN位,该位用于设置扫描模式,由软件设置和清除,如果设置为1,则使用扫描模式,如果为0,则关闭扫描模式。在扫描模式下,由ADC_SQRx或ADC_JSQRx寄存器选中的通道被转换。如果设置了EOCIE或JEOCIE,只在最后一个通道转换完毕后才会产生EOC或JEOC中断。* q+ q, z+ w5 v5 E
2 y' |/ a+ l- [2 X
ADC_CR1[25:24]用于设置ADC的分辨率,详细的对应关系如图所示
; M. [8 c- D7 G, ?5 s2 p6 n3 c( V( |& j- K6 R; h( p8 Q
2.png

# C* t& P: \! _! u本章我们使用12位分辨率,所以设置这两个位为0就可以了。接着我们介绍ADC_CR2,该寄存器的各位描述如图所示:
& x* A1 y7 i9 ^$ [9 Q5 N2 ~
0 {5 S' ?9 [. D- S) C/ r: \- C5 ~0 e* ~$ Z
3.png

% P9 ~0 F# _2 C& x7 Q# j5 i) I该寄存器我们也只针对性的介绍一些位:ADON位用于开关AD转换器。而CONT位用于设置是否进行连续转换,我们使用单次转换,所以CONT位必须为0。ALIGN用于设置数据对齐,我们使用右对齐,该位设置为0。! T, N% i, e9 H  g+ |$ S

8 F& Y$ a/ J9 h# ?8 |EXTEN[1:0]用于规则通道的外部触发使能设置,详细的设置关系如图所示
) |+ l0 f( F8 M0 i0 q- B( [1 j" p8 P
: m$ u' u0 f. _& q" y& u+ |
4.png

% ^# b  c! j1 y3 U我们这里使用的是软件触发,即不使用外部触发,所以设置这2个位为0即可。ADC_CR2的SWSTART位用于开始规则通道的转换,我们每次转换(单次转换模式下)都需要向该位写1。7 y( V) z0 p& t, K$ c

9 ^1 ~: K) a9 `, X第二个要介绍的是ADC通用控制寄存器(ADC_CCR),该寄存器各位描述如图所示:5 [6 k- g' \, s+ }5 M; U

* O4 h/ M- A+ D/ w; m
5.png
+ t; d' k- u3 E
该寄存器我们也只针对性的介绍一些位: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模式选择,详细的设置关系如图所示:
0 R, q1 {6 Z5 K1 D4 X1 h) F) [7 r) T' N2 g: |2 t+ v" z& e7 O
6.png

4 v" Z/ z: n8 q: y' L8 ~; Q( w本章我们仅用了ADC1(独立模式),并没用到多重ADC模式,所以设置这5个位为0即可
. F5 T+ o: W/ L
) F+ S% D% G" D1 V; z) m' H第三个要介绍的是ADC采样时间寄存器(ADC_SMPR1和ADC_SMPR2),这两个寄存器用于设置通道0~18的采样时间,每个通道占用3个位。ADC_SMPR1的各位描述如图所示:7 c  |" D3 K. [# A, B4 U
  \9 m5 f2 D( g0 r2 ^7 \
7.png

$ [3 J$ `  f( q9 C( L对于每个要转换的通道,采样时间建议尽量长一点,以获得较高的准确度,但是这样会降低ADC的转换速率。ADC的转换时间可以由以下公式计算:
. J7 F' q7 K) A8 R0 |7 Y' k; f% {. M- H& c! F
Tcovn=采样时间+12个周期
& k; t9 l3 y. J% k5 d) r4 o) I6 s+ G; p; g, V9 m2 O( e
其中:Tcovn为总转换时间,采样时间是根据每个通道的SMP位的设置来决定的。例如,当ADCCLK=21Mhz的时候,并设置3个周期的采样时间,则得到:Tcovn=3+12=15个周期=0.71us。3 u+ T& C& X" A. u- V+ C# B3 q

. `/ ]$ Z/ Q6 F$ i第四个要介绍的是ADC规则序列寄存器(ADC_SQR1~3),该寄存器总共有3个,这几个寄存器的功能都差不多,这里我们仅介绍一下ADC_SQR1,该寄存器的各位描述如图所示:4 `5 K( N3 }8 L0 e7 Q/ }1 {: g

8 b4 ]0 L) m( k6 V% Y( k' Z! Z" ~
8.png

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

$ W8 n4 i6 |+ K& O8 c* V  e. H第五个要介绍的是ADC规则数据寄存器(ADC_DR)。规则序列中的AD转化结果都将被存在这个寄存器里面,而注入通道的转换结果被保存在ADC_JDRx里面。ADC_DR的各位描述如图:
" d& r+ D) Y+ u9 x, ]2 G7 }! C3 W" F: |, \& V1 `/ b( ]7 s
9.png

& |8 T1 [) k6 [这里要提醒一点的是,该寄存器的数据可以通过ADC_CR2的ALIGN位设置左对齐还是右对齐。在读取数据的时候要注意。; a( b/ K6 ?6 `

/ G2 g) B: v! @- p6 p/ V最后一个要介绍的ADC寄存器为ADC状态寄存器(ADC_SR),该寄存器保存了ADC转换时的各种状态。该寄存器的各位描述如图所示:
0 P* d8 ]3 @* ~3 o6 K0 m9 I/ S8 X0 R5 @! U- ?4 @3 U, d: t7 c+ B
10.png
  {- y# p7 I6 t* H. S( v3 v
这里我们仅介绍将要用到的是EOC位,我们通过判断该位来决定是否此次规则通道的AD转换已经完成,如果该位位1,则表示转换完成了,就可以从ADC_DR中读取转换结果,否则等待转换完成。. H' w& V% [; t: v( c6 s

. m" f+ C  X; ^. D3 S% b% H! Y2.      ADC库函数应用步骤
3 H- ~. j" S3 _  c( O3 z" b# f+ I, t% V
使用到的库函数分布在stm32f4xx_adc.c文件和stm32f4xx_adc.h文件中。下面讲解其详细设置步骤
5 |( A! T9 r/ f4 P
% E: ~' w8 F( qSTM32F407ZGT6的ADC1通道5在PA5上,所以,我们先要使能GPIOA的时钟,然后设置PA5为模拟输入。同时我们要把PA5复用为ADC,所以我们要使能ADC1时钟。这里特别要提醒,对于IO口复用为ADC我们要设置模式为模拟输入,而不是复用功能,也不需要调用GPIO_PinAFConfig函数来设置引脚映射关系。
3 \* r% K; I) M& m% a9 \# Z
/ N" }. J. w* |: ?( L, ]使能GPIOA时钟和ADC1时钟都很简单,具体方法为:
+ P, u1 k' F8 Q# c& L% r; ]8 o
, M/ q0 Q5 V$ x$ [3 z" e4 ]! ~RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//使能GPIOA时钟% N% ^. ~: b2 z2 i, @$ t1 Y5 u
" E9 u2 o* V# h: [
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); //使能ADC1时钟! J  s$ N7 c8 D6 G' _
" A8 p3 B) T7 ?, G/ [* |
初始化GPIOA5为模拟输入,方法也多次讲解,关键代码为:
) ?2 F& k5 A; u9 k" Y, u5 r0 _3 l# |( [7 r; i* W
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AN;//模拟输入/ C# c1 j4 h4 i$ ?+ X; `
- ^+ `$ V2 O9 j+ ~: I
这里需要说明一下,ADC的通道与引脚的对应关系在STM32F4的数据手册可以查到,我们这里使用ADC1的通道5,在数据手册中的表格为:
% ~; O# P* V5 Z* j0 M. y; k1 ~, ~% e; O: u- V/ d/ ~
11.png
5 M0 f0 }, G+ d! e! ~( u& j4 r
这里我们把ADC1~ADC3的引脚与通道对应关系列出来, 16个外部源的对应关系如下表:- K9 X$ r3 G/ C
  ^+ J$ w: o3 P2 K+ [) V; p' S# n
12.png

# {: `: @: O6 t2)设置ADC的通用控制寄存器CCR,配置ADC输入时钟分频,模式为独立模式等。
/ w& D. X4 m5 J4 M$ K5 H5 G
% |2 Q9 O# R3 V" k+ h7 r' k在库函数中,初始化CCR寄存器是通过调用ADC_CommonInit来实现的:
7 j" N  t. n+ u0 @, V9 j( u
: c9 f" q3 x7 B1 Z6 Pvoid ADC_CommonInit(ADC_CommonInitTypeDef*ADC_CommonInitStruct)0 n. c+ \* z, |* k& o4 \& e  |

1 w" ~2 k! H( H# E1 I& I/ z, q这里我们不再李处初始化结构体成员变量,而是直接看实例。初始化实例为:# X9 _/ \) U$ L$ G6 k/ S2 U
- B0 x* p) i) |$ {( z! K" _$ k
ADC_CommonInitStructure.ADC_Mode =ADC_Mode_Independent;//独立模式
: l" e* W/ ?0 [7 l, h$ g0 L) O( O& O- ?6 V5 W' a& K4 ?) T
ADC_CommonInitStructure.ADC_TwoSamplingDelay= ADC_TwoSamplingDelay_5Cycles;# v. N2 o. }  i6 j8 s

" v7 d6 ^4 u6 z1 |2 o* Y) f9 LADC_CommonInitStructure.ADC_DMAAccessMode =ADC_DMAAccessMode_Disabled;# d) K8 V6 I$ Y, g" y% U

5 c4 @3 Z+ x! A3 Q- Y/ xADC_CommonInitStructure.ADC_Prescaler =ADC_Prescaler_Div4;
. \% F: c* e" y- E3 l9 L
# ^% J$ C. Y4 O% B% XADC_CommonInit(&ADC_CommonInitStructure);//初始化7 E7 p  R8 X0 v) U/ y/ f, T
/ ?) T3 G: g9 V$ J" ^
第一个参数ADC_Mode用来设置是独立模式还是多重模式,这里我们选择独立模式。' P+ i3 `/ z& _: Q
/ ?. C, _# H0 i7 \0 J
第二个参数ADC_TwoSamplingDelay用来设置两个采样阶段之间的延迟周期数。这个比较好理解。取值范围为:ADC_TwoSamplingDelay_5Cycles~ADC_TwoSamplingDelay_20Cycles。6 `* j4 u* z3 [: |2 Q
, `2 W. H! O$ H3 R) V/ l% K9 o
第三个参数ADC_DMAAccessMode是DMA模式禁止或者使能相应DMA模式。7 y5 m' x/ s0 C, Z* r( a' x

: p, ^& F$ t4 o) B3 [' Q; N第四个参数ADC_Prescaler用来设置ADC预分频器。这个参数非常重要,这里我们设置分频系数为4分频ADC_Prescaler_Div4,保证ADC1的时钟频率不超过36MHz。
3 |2 Z: [+ W4 a5 |8 t' ]
7 s4 ^/ z+ R0 E0 f7 T) j3)初始化ADC1参数,设置ADC1的转换分辨率,转换方式,对齐方式,以及规则序列等相关信息。
: @& O1 N3 S( K2 }5 p
' Y/ U1 g# h5 M2 i4 G- V/ L: i+ _在设置完分通用控制参数之后,我们就可以开始ADC1的相关参数配置了,设置单次转换模式、触发方式选择、数据对齐方式等都在这一步实现。具体的使用函数为:
4 T+ H+ i& S0 d; Q2 O( m/ H: v) a- B4 @- L& z- |6 Z9 R' H3 T
void ADC_Init(ADC_TypeDef* ADCx,ADC_InitTypeDef* ADC_InitStruct)
& ^2 y3 q6 P4 u4 @3 i* v8 ^8 `: J# A' ?& v( G5 |  c. Z
初始化实例为:6 P& M* D8 J( L* ~! b7 d

' m8 U. m9 y4 V! }% K  y9 BADC_InitStructure.ADC_Resolution =ADC_Resolution_12b;//12位模式
* C3 A: }! k8 B* P+ Y  }7 G4 L$ w) r/ h  E  m( v1 B+ f, B/ M
ADC_InitStructure.ADC_ScanConvMode =DISABLE;//非扫描模式
3 C+ O1 z/ I0 F) _/ d4 g: K* d6 P3 B$ G( J7 R6 \
ADC_InitStructure.ADC_ContinuousConvMode =DISABLE;//关闭连续转换! d6 j3 [; w* v$ O4 y3 V
. X8 p9 P4 B1 K! J# H
ADC_InitStructure.ADC_ExternalTrigConvEdge= ADC_ExternalTrigConvEdge_None;
! J# s( I, M1 I+ w: h- ~# v6 Q( r. ?* g+ T6 A
//禁止触发检测,使用软件触发
  k! \. B# e3 p- Q# ~
  E' n, |- y7 W! }4 ]2 sADC_InitStructure.ADC_DataAlign =ADC_DataAlign_Right;//右对齐
' a! ^& ~; M, R6 V* P8 b. g& U3 L. j. i7 n; N5 _8 n) F
ADC_InitStructure.ADC_NbrOfConversion =1;//1个转换在规则序列中9 o# J. F/ V4 Y* s

' E& c* j3 ]3 U& ^0 I. u4 {& lADC_Init(ADC1,&ADC_InitStructure);//ADC初始化" T$ _7 z6 L9 |" k2 I
5 w0 ]/ p1 [" S: \
第一个参数ADC_Resolution用来设置ADC转换分辨率。取值范围为:ADC_Resolution_6b,
* {( G( t2 T0 n; j7 T
, [6 a/ G: u( t+ U* cADC_Resolution_8b,ADC_Resolution_10b和ADC_Resolution_12b。
- c6 g4 s! z2 w3 g- b: b' U- ~/ Z6 ^0 }4 \# |) r
第二个参数ADC_ScanConvMode用来设置是否打开扫描模式。这里我们设置单次转换所以不打开扫描模式,值为DISABLE。. M; N  R& j# t: v2 E% m+ ?

1 k0 X6 G/ w4 R5 a9 w第三个参数ADC_ContinuousConvMode用来设置是单次转换模式还是连续转换模式,这里我们是单次,所以关闭连续转换模式,值为DISABLE。# p1 \6 j# Y3 i9 ]
: U  s2 M/ ^8 m8 I! I
第三个参数ADC_ExternalTrigConvEdge用来设置外部通道的触发使能和检测方式。这里我们直接禁止触发检测,使用软件触发。还可以设置为上升沿触发检测,下降沿触发检测以及上升沿和下降沿都触发检测。
, W% k0 s# \: k# J+ K
3 K& Q5 l; B4 d第四个参数ADC_DataAlign 用来设置数据对齐方式。取值范围为右对齐: L) M, h: k( ?) a3 |
  N  W; `- U, o- C
ADC_DataAlign_Right和左对齐ADC_DataAlign_Left。
, j6 ?# `) l& H4 O9 o  X4 a. d0 n% w1 R! X' Z' L
第五个参数ADC_NbrOfConversion用来设置规则序列的长度,这里我们是单次转换,所以值为1即可。
) f+ `; w+ f" }6 P1 d; S
: P5 V* u' ^: g% ]% M实际上还有个参数ADC_ExternalTrigConv是用来为规则组选择外部事件。因为我们前面配置的是软件触发,所以这里我们可以不用配置。如果选择其他触发方式方式,这里需要配置。
" c1 @# \4 Y$ C" U2 m# Y- v% X+ H8 C2 T4 Q$ I
4)开启AD转换器。# D" f  a' a7 z, I% d/ y: L. K. C

  K; `4 u" n7 s' E1 c* _在设置完了以上信息后,我们就开启AD转换器了(通过ADC_CR2寄存器控制)。
5 B1 c% V6 Q$ l! ?4 C
9 {# w$ @/ _& r5 SADC_Cmd(ADC1, ENABLE);//开启AD转换器
: G+ K+ e0 R2 q! }  }& f; ]6 G5 g+ n' ~
5)读取ADC值。
2 w3 r! t' R8 j3 Q# t* |$ ]4 |, D+ ]2 m2 I; `
在上面的步骤完成后,ADC就算准备好了。接下来我们要做的就是设置规则序列1里面的通道,然后启动ADC转换。在转换结束后,读取转换结果值值就是了。! A# }- B1 z1 ]. a; c9 i$ J; h
0 P) o- A# i8 R9 H- w- z' S
这里设置规则序列通道以及采样周期的函数是:
5 ]5 `! M- z* I7 ?- J9 U
. ?4 O5 m) ?, n& u- S; K4 `void ADC_RegularChannelConfig(ADC_TypeDef*ADCx, uint8_t ADC_Channel,. F1 v9 ~7 H* f0 M& [, u
1 d+ Z* K2 o, c
uint8_t Rank, uint8_t ADC_SampleTime);0 P( z: k7 j3 F4 f0 M* ~

" u1 r% U" i" s2 T* _* G0 R8 ~% }我们这里是规则序列中的第1个转换,同时采样周期为480,所以设置为:6 f% `1 s: ~+ I/ r9 f# ~
3 m0 @1 ]: E" T1 k
ADC_RegularChannelConfig(ADC1,ADC_Channel_5, 1, ADC_SampleTime_480Cycles );
6 g9 S2 a8 R3 H) Z* H7 t: C& c7 z5 u( S* F# ^
软件开启ADC转换的方法是:$ D! i1 j9 @7 |6 F  T5 O% P

% Z8 O# E/ W2 V( V6 V1 j. CADC_SoftwareStartConvCmd(ADC1);//使能指定的ADC1的软件转换启动功能
9 F, e. ^$ H$ q5 Y+ R
0 \0 b  U- T5 B- f& R开启转换之后,就可以获取转换ADC转换结果数据,方法是:, X3 [1 ~0 s. ]+ i/ J
" _/ P5 R5 s" w, _1 w6 V; A9 g
ADC_GetConversionValue(ADC1);3 P( D4 v/ Y/ n- P

2 i* I  l  A0 x9 W同时在AD转换中,我们还要根据状态寄存器的标志位来获取AD转换的各个状态信息。库函数获取AD转换的状态信息的函数是:
& L* e" W/ U3 i* J  l) B( T+ b( u) @, q5 @+ W
FlagStatus ADC_GetFlagStatus(ADC_TypeDef*ADCx, uint8_t ADC_FLAG)0 g/ I6 B- @( l: U2 I! y1 E. D. y2 M

, x0 j; `% K& D! c比如我们要判断ADC1的转换是否结束,方法是:
8 _7 {5 ^4 ^. I+ \& Z, Y% N
' G8 g+ M+ I' |0 Lwhile(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));//等待转换结束' j  m! }/ `( B4 p  x

; F" o/ Z& @# t% t5 A9 `% c3 ~, D这里还需要说明一下ADC的参考电压,探索者STM32F4开发板使用的是STM32F407ZGT6,该芯片只有Vref+参考电压引脚,Vref+的输入范围为:1.8~VDDA。探索者STM32F4开发板通过P7端口,来设置Vref+的参考电压,默认的我们是通过跳线帽将ref+接到VDDA,参考电压就是3.3V。如果大家想自己设置其他参考电压,将你的参考电压接在Vref+上就OK了(注意要共地)。另外,对于还有Vref-引脚的STM32F4芯片,直接就近将Vref-接VSSA就可以了。
" E- u. c0 S" S
2 p6 G. A( S. f本章我们的参考电压设置的是3.3V。; J9 a) P' a2 F! f6 k: z

6 a2 d1 w& s) C5 e+ T: d3 C通过以上几个步骤的设置,我们就能正常的使用STM32F4的ADC1来执行AD转换操作了。
4 u, O" h0 q: G+ S4 o3 S! z
! {* n  |. q: c" K0 H0 z$ \4 ]0 t3.      ADC实例0 H. g6 a/ _" e: W  w( I: j3 @

$ Y* a1 r7 J, v0 v. c实现采集3.3V电压和GND电压,用串口打印出来$ H8 c, W- z( z; G- Z, O5 J  H4 f
8 ^' F, T$ f) J* w, _$ z# |, p
Adc.h
. x: B/ b( ^' X) H3 i, j+ Z7 \1 `9 {6 C
  1. [cpp] view plain copy" n  x+ I9 H( y  L$ a' w  \* N
  2. #ifndef _ADC_H_H_H  
    4 G. f7 f. H, p6 \0 m5 x2 k3 T7 ?
  3. #define _ADC_H_H_H  
    7 u0 Z! q0 v$ Z: a0 D6 y$ ~
  4. #include "stm32f4xx_gpio.h"  # C5 `$ Z2 g4 }2 ?$ ~. N* p
  5. #include "stm32f4xx_rcc.h"  
    % }0 r1 V5 f/ V! p& R
  6. #include "stm32f4xx_adc.h"  1 [# R% ?; y: B
  7.    $ C: x* h, g/ A
  8. void Adc_Init(void);                                                        //ADC通道初始化  
    ) W) Z0 H- t/ \& K$ h; Y: U4 {
  9. u16 Get_Adc(u8 ch);                                                     //获得某个通道值  5 k: a3 ]# O# [; R
  10. u16 Get_Adc_Average(u8 ch,u8 times);//得到某个通道给定次数采样的平均值   ) g# R  ^2 Y4 Z1 N  p
  11. #endif  
复制代码
Adc.c2 I! k  o' `! \; Y: n0 i
  1. [cpp] view plain copy
    " u3 C% _: `, D8 Q
  2. #include "adc.h"  
    . _" Q3 P5 ~$ s6 [# M
  3. #include "delay.h"  
    6 P# r9 S3 N  n
  4. //初始化ADC                                                                                                                                                                                                                                       
    + v% s8 S- h% g* ?# F
  5. void Adc_Init(void)  
    , S% c+ U( Q# U' {2 X, ?5 j: f
  6. {  
      X9 U  I% x( G2 |* k- g9 Z( w2 |
  7. GPIO_InitTypeDef GPIO_InitStructure;  
    8 d) i. L: @4 D( o! B
  8. ADC_CommonInitTypeDef ADC_CommonInitStructure;  $ c! l/ `7 m6 w- m" t( O9 ?
  9. ADC_InitTypeDef      ADC_InitStructure;  ) i1 L+ W7 n" S! J, G/ r6 C# H
  10.                  
    $ }% M) T, H8 X4 q( ~4 `: x6 d
  11. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟  $ h0 k/ l  a  m( b( z) b- T' f
  12. RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟  % a! L" h- |: g; d3 |% q+ f4 A
  13.    
    ; n+ G3 F; s6 ?1 Z
  14.   //先初始化ADC1通道5 IO口  - b0 I2 G: m  \1 ~; l
  15. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PA5 通道5  2 ]3 f" R/ I& K  G, {3 x
  16. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模拟输入  ! F* B$ n1 D' V! x
  17. GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;//不带上下拉  0 K) z+ }/ |% }
  18. GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化   1 F$ C' n' [# m+ ^# H: s9 z
  19.    
    * H; f: D( e9 x+ z3 |5 k* F
  20. RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE);                 //ADC1复位  
    ; S: {, d2 s5 A& R  M* m8 a1 ]% V
  21. RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE);               //复位结束  
    0 h# ~( N5 F8 `! }
  22.    % Z8 A' n& Z; a; V
  23. ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;//独立模式  
    7 L! c3 y. Z; P  i9 }) T
  24. ADC_CommonInitStructure.ADC_TwoSamplingDelay =ADC_TwoSamplingDelay_5Cycles;//两个采样阶段之间的延迟5个时钟  ( d$ m/ V! g. B
  25. ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;//DMA失能  3 _9 J: y9 U3 ]. A
  26. ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;//预分频4分频。ADCCLK=PCLK2/4=84/4=21Mhz,ADC时钟最好不要超过36Mhz  $ b; v- b, P/ `  [8 n+ u
  27. ADC_CommonInit(&ADC_CommonInitStructure);//初始化  + c  _+ B; Y9 k  J" ]* I2 U
  28.    - p/ J5 J1 a3 w, E* E8 U! ~& M
  29. ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//12位模式  
      p$ N+ W" @% @- w  K1 ^/ |+ r
  30. ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非扫描模式          % t; [# K* ^; J
  31. ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//关闭连续转换  
    / I; ^, f0 ~9 ^& |' S+ C
  32. ADC_InitStructure.ADC_ExternalTrigConvEdge =ADC_ExternalTrigConvEdge_None;//禁止触发检测,使用软件触发  & ~% `$ Y; x; y; P
  33.   ADC_InitStructure.ADC_DataAlign= ADC_DataAlign_Right;//右对齐     
    1 R. H( Y! `2 [; ^5 l+ @
  34. ADC_InitStructure.ADC_NbrOfConversion = 1;//1个转换在规则序列中 也就是只转换规则序列1  ! ~& ~6 R  Q8 P# l8 ?3 T
  35. ADC_Init(ADC1, &ADC_InitStructure);//ADC初始化  
    % R5 m0 v( ]7 `/ b
  36.    
    : \1 T" ^& ~4 S) \! {$ K
  37. ADC_Cmd(ADC1, ENABLE);//开启AD转换器   
    " z# r* e2 t4 o" v
  38. }  
    9 O. m* M1 S1 A9 q3 p: I5 i
  39.    . i: D* T# G& W- B* r3 D
  40. //获得ADC值  
    ; V8 Y+ {. l- y5 ^
  41. //ch: @ref ADC_channels  
    2 K( x2 `% F  w
  42. //通道值 0~16取值范围为:ADC_Channel_0~ADC_Channel_16  
    5 X9 _) W9 x7 z3 d( Y* \( j, d
  43. //返回值:转换结果  
    % m: \! B' u4 I5 a6 \
  44. u16 Get_Adc(u8 ch)   
    * z' j/ y% @' [" ~4 Y4 T
  45. {  
    3 Y3 W; \; S7 d% |
  46. ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_480Cycles );            //ADC1,ADC通道,480个周期,提高采样时间可以提高精确度                                             
    : C( U! k/ r; H9 F2 W* y; r* z
  47.    
    4 J/ X% z- s+ Y+ H) @1 U
  48. ADC_SoftwareStartConv(ADC1);                            //使能指定的ADC1的软件转换启动功能          8 A& p" [) D# w
  49.                   
    ! X+ f' E0 a$ N& C3 l
  50. while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束  # E& }$ s  |3 C" E. z$ L, n
  51.    
    ' f8 B2 m2 Z% Y( P
  52. return ADC_GetConversionValue(ADC1);           //返回最近一次ADC1规则组的转换结果  5 \, ^+ d1 W  _+ @' n' z
  53. }  
    : L$ Z8 @7 i0 I6 p# s. Z2 [
  54.    - W( A( r  T( K) h' V
  55. u16 Get_Adc_Average(u8 ch,u8 times)  % X5 M# Z: j6 D# P) n% r
  56. {  
    1 _) M5 J8 M$ W6 y
  57.   u32temp_val=0;  
    ) t6 r: U0 n6 e  s* n$ @: j& a: m
  58.   u8t;  : F+ a* B! l! x! s
  59. for(t=0;t<times;t++)  
    7 W2 g) F; T; o8 U, S! C7 z
  60.   {  
    + w0 v" g8 |; v3 B/ x" y8 y: g; n
  61.    temp_val+=Get_Adc(ch);    _' `9 ^* l  h6 C. d
  62.    delay_ms(5);  ' |. l8 n  z" z% H, u% x1 U
  63.   }  
    ! A- K$ z  o3 a9 X5 h+ \: f
  64.   returntemp_val/times;  
    4 l% ?) I6 \3 ?, w& m' }+ _. }3 ?  v
  65. }  
复制代码
Main.c4 j1 h$ i" j8 h5 o' V' P
  1. [cpp] view plain copy: X' r# S' y1 c1 F% e" e
  2. #include "led.h"  
    # ?$ W" T1 O4 J. t3 `+ q9 M
  3. #include "key.h"  7 t2 Z; z+ C  \5 d" r' |3 E
  4. #include "delay.h"  0 a$ p7 p( g- z6 B2 g- |2 ?
  5. #include "uart.h"  
    8 @+ O( A$ H# a9 {
  6. #include "exit.h"  
    6 o4 k6 w" Y! Z' ]" M- O
  7. #include "iwdog.h"  
    9 Y: Z8 ]9 \7 ~
  8. #include "pwm.h"  
    8 ?$ V7 R! d. q. `3 Z" n
  9. void User_Delay(__IO uint32_t nCount)  
    4 T# e1 A* W2 u/ E1 r" C5 F/ [0 S
  10. {  
    . [/ ]) @( u; p/ ]+ D, G
  11. while(nCount--)  $ q9 f4 J6 P9 C3 @0 e% y, Y% o
  12.   {  6 M9 K0 _% _, H6 D8 y  h* l. l8 k8 P
  13.   }  4 T7 l; x  q2 M: D6 E
  14. }  
    4 f2 J5 H' ^/ O3 j
  15. static int count = 0;  * Z; d3 g2 E4 K
  16. int main(void)  
    : d, M  z3 X$ u7 C" Y. p
  17. {  ( r. |' ?2 j1 Q- w  `
  18.    
    % t0 J$ S6 N0 _" `+ v* Q7 j$ }
  19.   u16adcx;  " Z; Y9 u' z, N- n# x' W% N
  20. float temp;  , s- I# }9 S" ~6 o* p+ t
  21. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  * i! W$ u' S2 k$ X1 X
  22.   My_USART2_Init();  
    ; X2 o) e& k, W! d
  23.   delay_init(168);  
    & H, O. {. t: c& w
  24.   printf("main test start\n");  - c# u9 _5 E8 D, \) ?0 U1 z3 h, B5 U
  25.   Adc_Init();  
    ; Y9 J/ B# W8 w( K9 {
  26.    
    : g4 G1 a0 e& _2 Z) [# T4 O
  27.   while(1)  
    9 A3 {% r3 R; P
  28.    {  
    : `+ e/ x( A' {$ `6 P
  29.     adcx=Get_Adc_Average(ADC_Channel_5,20);  
    + }: {8 p% R9 Q: L4 |8 A7 O
  30.     temp=(float)adcx*(3.3/4096);  
    + k# u/ B! L5 ^* i" D8 u( L$ b3 M
  31.     printf("adc value:(%d) \n",adcx);  
    4 l( Q7 G5 |- n0 E
  32.     delay_ms(250);  
    8 A# n% K  j5 f6 i3 q! t
  33.       
    & a' r  v. m9 y( ^# Y! H: A
  34.    }  ( |4 @0 `9 R: X" w$ g: {
  35. }  
复制代码
* c4 }2 i- Y9 ^. i9 r  N$ D& p- [5 a& \

' E4 l1 ]) q( @4 T( }
; W' U& q4 r$ Q8 U  d! o4 h' L9 @6 b6 ~8 v
% g2 {" G# i4 G' ], ]+ U- T; C9 u* K
转载自于忠军
& w1 T( X! ^+ W5 Z8 f& X, g, r
2 x0 s! d5 o3 J% M" d* D; N0 v, `3 w
赞 收藏 2 评论1 发布时间:2018-6-1 15:44

举报

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

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版