|
一.ADC简介 STM32f103系列有3个ADC,精度为12位,每个ADC最多有16个外部通道。其中ADC1和ADC2都有16个外部通道,ADC3一般有8个外部通道,各通道的A/D转换可以单次、连续、扫描或间断执行,ADC转换的结果可以左对齐或右对齐储存在16位数据寄存器中。ADC的输入时钟不得超过14MHz,其时钟频率由PCLK2分频产生。 二.ADC功能框图讲解学习STM32开发板上的外设时首先要了解其外设的功能框图,如下:
2 ]. \0 P: d2 K! Y
+ O6 S$ E9 _. q R! y& C3 e: b3 s& v% \ @+ w
( d D% ^6 `0 Q. l+ ~1 v
功能框图可以大体分为7部分,下面一一讲解:
! j0 n0 s8 `7 I! c2 @' F; L0 t( S- @$ G
1.电压输入范围ADC所能测量的电压范围就是VREF- ≤ VIN ≤ VREF+,把 VSSA 和 VREF-接地,把 VREF+和 VDDA 接 3V3,得到ADC 的输入电压范围为: 0~3.3V。
8 ^2 Q# {1 [/ B 2.输入通道
( H! v! Y- U% @ADC的信号输入就是通过通道来实现的,信号通过通道输入到单片机中,单片机经过转换后,将模拟信号输出为数字信号。STM32中的ADC有着18个通道,其中外部的16个通道已经在框图中标出,如下:
6 n( ]% A% r7 n+ l( g2 d2 j5 Z
- I# a. o8 `8 [8 t8 O% S+ v% Y$ Q* T# y
, o/ c- G* k' y6 r3 X7 j2 d I
这16个通道对应着不同的IO口,此外ADC1/2/3 还有内部通道: ADC1 的通道 16 连接到了芯片内部的温度传感器, Vrefint 连接到了通道 17。 ADC2 的模拟通道 16 和 17 连接到了内部的 VSS。+ i; O$ S' }# m8 ^, l5 N- x
8 u7 l4 L7 k! ^" M6 S' F) FADC的全部通道如示:
3 X6 c5 ?' L; A3 w3 @. C
( F; K/ Y8 }( N+ r# n5 F1 Z( z- b6 C; U
. w7 q' D: L4 T( b' x) ~, _
外部的16个通道在转换时又分为规则通道和注入通道,其中规则通道最多有16路,注入通道最多有4路(注入通道貌似使用不多),下面简单介绍一下俩种通道:: g! S' @1 V4 _$ E0 J. F( `7 t. S7 |$ o3 i
4 W; ?- v% t6 Z2 O$ E7 x2 |规则通道0 [6 b* g5 v5 J+ U7 |7 b
规则通道顾名思义就是,最平常的通道、也是最常用的通道,平时的ADC转换都是用规则通道实现的。
/ J& E( n1 B. d/ G3 V注入通道8 B8 ~' `9 j# y7 Q6 @
注入通道是相对于规则通道的,注入通道可以在规则通道转换时,强行插入转换,相当于一个“中断通道”吧。当有注入通道需要转换时,规则通道的转换会停止,优先执行注入通道的转换,当注入通道的转换执行完毕后,再回到之前规则通道进行转换。
! J7 ?3 |% A# W) q
3.转换顺序& Y0 V0 i8 Q1 @! U* v q
知道了ADC的转换通道后,如果ADC只使用一个通道来转换,那就很简单,但如果是使用多个通道进行转换就涉及到一个先后顺序了,毕竟规则转换通道只有一个数据寄存器。多个通道的使用顺序分为俩种情况:规则通道的转换顺序和注入通道的转换顺序。 规则通道转换顺序
! ?0 {% k+ P, |7 w0 C. n* w# _规则通道中的转换顺序由三个寄存器控制:SQR1、SQR2、SQR3,它们都是32位寄存器。SQR寄存器控制着转换通道的数目和转换顺序,只要在对应的寄存器位SQx中写入相应的通道,这个通道就是第x个转换。具体的对应关系如下:
2 r2 L9 V) D7 D. Q5 O0 w8 O2 J2 J# v $ q# W5 s* ]% i
6 N/ m" i% q& N
5 u+ i$ L+ H9 |5 `6 {通过SQR1寄存器就能了解其转换顺序在寄存器上的实现了:
. B1 ~0 Q, b0 P: w. M Y- h9 x4 s5 ?2 _( V' o' O
, _2 B0 O% a1 l3 p0 T
% C/ X/ [; E; w 2 S l6 o+ r8 m
, W, j" L/ k* w2 D3 Z6 \. n; R( I
注入通道转换顺序
# |+ L& {7 n7 _和规则通道转换顺序的控制一样,注入通道的转换也是通过注入寄存器来控制,只不过只有一个JSQR寄存器来控制,控制关系如下:
! W& B, c Z: d( D. V" P 7 L4 E: r3 S' K- c6 n
/ [! m3 x8 p3 z8 @* b - g' q/ G9 ^5 {5 H& _6 q7 G
需要注意的是,只有当JL=4的时候,注入通道的转换顺序才会按照JSQ1、JSQ2、JSQ3、JSQ4的顺序执行。当JL<4时,注入通道的转换顺序恰恰相反,也就是执行顺序为:JSQ4、JSQ3、JSQ2、JSQ1。
* P" |% V* M! D0 h* ?0 b
/ [$ v. d; [' V$ B% O2 D配置转换顺序的函数如示:
3 o8 r% t+ A4 C5 F, p
- / L7 b% ?( M4 |5 ~. [2 a5 z
- /**; x/ C* ^8 l/ y( \3 a! }9 R$ [
- * @brief Configures for the selected ADC regular channel its corresponding
: B2 M. G$ o8 y0 n+ Q* y - * rank in the sequencer and its sample time./ O6 \+ ^% w' e \$ H
- * @param ADCx: where x can be 1, 2 or 3 to select the ADC peripheral.
6 l5 s0 {" _0 l P% N" x6 \ - * @param ADC_Channel: the ADC channel to configure.
: U- s& d# o9 c8 E7 X - * This parameter can be one of the following values:
1 c _; _) Y/ U& |5 D- Q# k- I. Q# U& r - * @arg ADC_Channel_0: ADC Channel0 selected
2 Z' [( ?* K& u9 L, A - * @arg ADC_Channel_1: ADC Channel1 selected# J- O/ s$ o6 O a4 q
- * @arg ADC_Channel_2: ADC Channel2 selected6 b, r( a; X c4 N; K. ?& `
- * @arg ADC_Channel_3: ADC Channel3 selected
* \" T* c+ q x4 v - * @arg ADC_Channel_4: ADC Channel4 selected- i. g1 v- J9 g, B b' L& j( Y
- * @arg ADC_Channel_5: ADC Channel5 selected+ E! ]0 J! @* z0 q; x
- * @arg ADC_Channel_6: ADC Channel6 selected) ]! n/ F- Q. C
- * @arg ADC_Channel_7: ADC Channel7 selected
, N- ^$ h. B: v - * @arg ADC_Channel_8: ADC Channel8 selected6 w" S# ]- T) [% U3 @5 K
- * @arg ADC_Channel_9: ADC Channel9 selected I# e2 e4 w5 u/ t$ [
- * @arg ADC_Channel_10: ADC Channel10 selected
$ T3 \1 K3 _+ G$ ^ - * @arg ADC_Channel_11: ADC Channel11 selected
- [2 j6 c- x- j - * @arg ADC_Channel_12: ADC Channel12 selected
2 S" t1 ~! r( {/ T' |% C/ y- L8 { - * @arg ADC_Channel_13: ADC Channel13 selected' k; _, e, c/ C: o o% J
- * @arg ADC_Channel_14: ADC Channel14 selected% G! W3 \/ i" ]$ ~6 K/ O( _
- * @arg ADC_Channel_15: ADC Channel15 selected
! ~: F, z; K( `5 |- H% \7 O - * @arg ADC_Channel_16: ADC Channel16 selected
5 I7 ?$ I1 ~8 U+ }9 a8 K - * @arg ADC_Channel_17: ADC Channel17 selected
0 d0 Y' J- m# G% ~2 t r: ? - * @param Rank: The rank in the regular group sequencer. This parameter must be between 1 to 16.
% `. Y- }# M% s/ I% W - * @param ADC_SampleTime: The sample time value to be set for the selected channel.
2 q. ]4 U8 R+ c8 F( o( o, ?: q$ f/ l - * This parameter can be one of the following values:
9 L$ ~% Y! r$ `. n3 ` - * @arg ADC_SampleTime_1Cycles5: Sample time equal to 1.5 cycles% x J- d1 p; z- ]' x, F; u" C% }
- * @arg ADC_SampleTime_7Cycles5: Sample time equal to 7.5 cycles2 R c$ x Y. e
- * @arg ADC_SampleTime_13Cycles5: Sample time equal to 13.5 cycles
% Z' g; k1 X# D* O - * @arg ADC_SampleTime_28Cycles5: Sample time equal to 28.5 cycles
& a& X* o1 K* ], @ - * @arg ADC_SampleTime_41Cycles5: Sample time equal to 41.5 cycles ) ^0 o$ | Z9 S& }
- * @arg ADC_SampleTime_55Cycles5: Sample time equal to 55.5 cycles
) M$ m: K: x. A$ M. v6 }1 c' G - * @arg ADC_SampleTime_71Cycles5: Sample time equal to 71.5 cycles ; s/ ]9 P+ q' T& r& e
- * @arg ADC_SampleTime_239Cycles5: Sample time equal to 239.5 cycles
& b) g7 Z& |- P! b9 g - * @retval None
4 h- x, j, h( f8 \ - */
. M7 O- T3 n8 {. L" V& | R - void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime)' p: y1 S" I% h0 {6 a* W
- {) p1 d: D$ {2 ^1 {+ S, y, Y6 V: O
- 函数内容略;
( s9 M2 F1 G6 {3 m$ } - }
* f. p1 Z( y( l' V2 Q* K0 Z0 K
复制代码 4 o! _* g. M& c6 _# k. z' w
4.触发源. Y) z2 P' \% E0 r
ADC转换的输入、通道、转换顺序都已经说明了,但ADC转换是怎么触发的呢?就像通信协议一样,都要规定一个起始信号才能传输信息,ADC也需要一个触发信号来实行模/数转换。 K5 Z+ w+ m+ j$ _8 m0 a! ?
其一就是通过直接配置寄存器触发,通过配置控制寄存器CR2的ADON位,写1时开始转换,写0时停止转换。在程序运行过程中只要调用库函数,将CR2寄存器的ADON位置1就可以进行转换,比较好理解。
: l/ K. [3 `, {另外,还可以通过内部定时器或者外部IO触发转换,也就是说可以利用内部时钟让ADC进行周期性的转换,也可以利用外部IO使ADC在需要时转换,具体的触发由控制寄存器CR2决定。& L4 c: x5 q8 V# U( v( k
ADC_CR2寄存器的详情如下:
+ n" Z5 E2 _$ w& y% n! J & v& q2 c6 U8 T/ y9 x; J# E
9 W+ z$ F0 M+ X& a5 W 4 d/ k3 @( O2 C9 b9 n z' q0 ]0 m
) \. V# V' T4 ?* u& B
+ u% U# h8 S7 M! p' e) i' E; o7 F- R1 l% a, Q( C3 p* _1 G
z( k/ r" n( Q% c3 a
0 t/ X) W9 d' d6 _7 T9 e
5.转换时间; h0 I) n6 `! Y1 p( b# n$ l$ I, p
还有一点,就是转换时间的问题,ADC的每一次信号转换都要时间,这个时间就是转换时间,转换时间由输入时钟和采样周期来决定。
' Y. d: y* _7 k9 N. Z输入时钟6 m6 y1 h Y# ~* \: {
由于ADC在STM32中是挂载在APB2总线上的,所以ADC得时钟是由PCLK2(72MHz)经过分频得到的,分频因子由 RCC 时钟配置寄存器RCC_CFGR 的位 15:14 ADCPRE[1:0]设置,可以是 2/4/6/8 分频,一般配置分频因子为8,即8分频得到ADC的输入时钟频率为9MHz。
3 f7 m. ]/ z b W6 Q0 M采样周期' i1 a* `) ]+ ~+ \1 @* u0 `
采样周期是确立在输入时钟上的,配置采样周期可以确定使用多少个ADC时钟周期来对电压进行采样,采样的周期数可通过 ADC采样时间寄存器 ADC_SMPR1 和 ADC_SMPR2 中的 SMP[2:0]位设置,ADC_SMPR2 控制的是通道 0~9, ADC_SMPR1 控制的是通道 10~17。每个通道可以配置不同的采样周期,但最小的采样周期是1.5个周期,也就是说如果想最快时间采样就设置采样周期为1.5.
* v' ~* [3 y3 A- h% F, i* I转换时间" n, d. g8 Y2 c
转换时间=采样时间+12.5个周期2 E6 E9 y7 P% n, ]' ]* K. j
12.5个周期是固定的,一般我们设置 PCLK2=72M,经过 ADC 预分频器能分频到最大的时钟只能是 12M,采样周期设置为 1.5 个周期,算出最短的转换时间为 1.17us。
- J) x) t2 o$ ^/ V; @$ W- g
6 [8 K4 t8 r% J1 x8 h 6.数据寄存器2 {. r8 L6 E% D$ H, A7 ~( W
转换完成后的数据就存放在数据寄存器中,但数据的存放也分为规则通道转换数据和注入通道转换数据的。 ) r, y7 r4 x! D
规则数据寄存器
' z6 J9 v+ k2 h规则数据寄存器负责存放规则通道转换的数据,通过32位寄存器ADC_DR来存放。
9 b. G1 L! u9 B% s; Y& N/ `7 M' U
9 x1 ^ b! m& r% p& p/ v8 U当使用ADC独立模式(也就是只使用一个ADC,可以使用多个通道)时,数据存放在低16位中,当使用ADC多模式时高16位存放ADC2的数据。需要注意的是ADC转换的精度是12位,而寄存器中有16个位来存放数据,所以要规定数据存放是左对齐还是右对齐。. z$ v4 S# H. h
当使用多个通道转换数据时,会产生多个转换数据,然鹅数据寄存器只有一个,多个数据存放在一个寄存器中会覆盖数据导致ADC转换错误,所以我们经常在一个通道转换完成之后就立刻将数据取出来,方便下一个数据存放。一般开启DMA模式将转换的数据,传输在一个数组中,程序对数组读操作就可以得到转换的结果。
! ^: ?; ~' G6 F/ M, R% NDMA的使用之前在一篇博客中介绍过:DMA介绍
注入数据寄存器 z: c/ I- s( n6 T; P2 }
注入通道转换的数据寄存器有4个,由于注入通道最多有4个,所以注入通道转换的数据都有固定的存放位置,不会跟规则寄存器那样产生数据覆盖的问题。 ADC_JDRx 是 32 位的,低 16 位有效,高 16 位保留,数据同样分为左对齐和右对齐,具体是以哪一种方式存放,由ADC_CR2 的 11 位 ALIGN 设置。 6 p% P5 P8 O& F
2 i+ m- r* Q8 ]: B
& d/ w z1 \ Q' |
* v/ O* J0 w- s. m8 @ m$ b0 |: I7 N; [+ P; r, t( T9 z
7.中断3 U2 v% I, t6 |
9 p" ~, P! K9 ?$ Y2 N' d1 s
! T% u( W c, v$ O从框图中可以知道数据转换完成之后可以产生中断,有三种情况:, {0 ^6 @# O9 W( e' u
规则通道转换完成中断9 j7 ?8 e1 z' ^
规则通道数据转换完成之后,可以产生一个中断,可以在中断函数中读取规则数据寄存器的值。这也是单通道时读取数据的一种方法。2 U6 e, @; N' o' j; l
注入通道转换完成中断/ h+ x4 U* F0 P9 f$ ]" ^
注入通道数据转换完成之后,可以产生一个中断,并且也可以在中断中读取注入数据寄存器的值,达到读取数据的作用。3 z6 ?2 [6 m, w7 L. v9 z* k
模拟看门狗事件
) m: _3 \* Y* C0 n: {& ^0 b) Q当输入的模拟量(电压)不再阈值范围内就会产生看门狗事件,就是用来监视输入的模拟量是否正常。
; ^ m+ {' Z! D$ s# ~, L5 k以上中断的配置都由ADC_SR寄存器决定:# \" \# A& J( a' S& ^+ X. Q. m
* \3 Y" z0 H* O5 \* R& A1 M3 M
% n5 b Q* T; ~* l% G& x' V, M9 ?
当然,在转换完成之后也可以产生DMA请求,从而将转换好的数据从数据寄存器中读取到内存中。
1 g6 R- d, ?1 o3 _) y% T
8.电压转换. N2 q! H8 C; Y$ w4 i2 j- a* |
要知道,转换后的数据是一个12位的二进制数,我们需要把这个二进制数代表的模拟量(电压)用数字表示出来。比如测量的电压范围是0~3.3V,转换后的二进制数是x,因为12位ADC在转换时将电压的范围大小(也就是3.3)分为4096(2^12)份,所以转换后的二进制数x代表的真实电压的计算方法就是:
1 d/ ?* \$ {7 y; ?& ^- ey=3.3* x / 4096
$ [) y& r9 W5 P: V$ P. Z- Y) Q 三.初始化结构体0 N7 e, c8 o$ O) \& e( d' V. C
每个外设的核心就是其对应的初始化结构体了,ADC的初始化结构体如下: # `& h; t$ N: s6 f- ~ P
/ I. ]7 K9 O" y1 D Y- typedef struct
& S6 _5 K' k. n9 k) }1 N, q - {. ], r& |4 C8 y: P
- uint32_t ADC_Mode; // ADC 工作模式选择$ R- f8 Z& W7 _4 Z: Y
- FunctionalState ADC_ScanConvMode; // ADC 扫描(多通道)或者单次(单通道)模式选择
% a l9 f2 T9 H" h8 h0 N; Z; ^; h - FunctionalState ADC_ContinuousConvMode; // ADC 单次转换或者连续转换选择: \( e% @( z: C8 n4 b% P5 W
- uint32_t ADC_ExternalTrigConv; // ADC 转换触发信号选择
3 }8 b# h; s; @8 q$ w - uint32_t ADC_DataAlign; // ADC 数据寄存器对齐格式1 a" s! e! Y/ m
- uint8_t ADC_NbrOfChannel; // ADC 采集通道数# J7 y. n; i8 ^( U! x4 _- e
- } ADC_InitTypeDef;
复制代码
9 |+ g/ Y! |# }, g
通过配置初始化结构体来设置ADC的相关信息。
, E8 Q; m2 p g4 n6 v: k0 B" \9 J
% R; G) f! u0 U( h: u R; w& M
四.单通道电压采集
; c7 ^& H' L9 t2 _9 F* X& Q1 h
# v O1 F( _4 F; W9 {用这个程序来简单熟练一下ADC的单通道电压采集吧,程序使用了ADC1的通道11,对应的IO口是PC^1,因为博主的开发板上PC ^1引脚没有任何复用,使用中断,在中断中读取转换的电压。 / _% f& K6 ^9 Z, |6 `( G; R; b
8 C; L* c8 j; [, R! @ 1.头文件为了提高文件的可移植性,头文件中定义了一些与ADC和中断相关的量,在移植程序的时候只需要修改头文件中的定义即可。 , h1 |" U5 B G- G4 J
3 R N) `! J' n/ B4 [ j. K# V1 F- #ifndef __ADC_H' {* _1 R u) b7 s9 L: B
- #define __ADC_H6 N. C& I: Q2 e. t& y) z7 d
-
' E% e |7 R& h( a( [! f6 k8 \ - #include "stm32f10x.h") {# [# B* V3 g, x1 {
- # D/ H, Q% f% ?# e$ A# z- D& C
- /* 采用ADC1的通道11 引脚为PC^1 模式必须是模拟输入*/
0 Z) i4 a6 `7 H/ I: U8 p& d3 z - #define ADC_GPIO_RCC RCC_APB2Periph_GPIOC
& Z6 \' H! [3 D) Y - #define ADC_GPIO_PORT GPIOC( T, q- b: s: S1 h
- #define ADC_GPIO_PIN GPIO_Pin_18 I# B& {$ U8 x, U9 t
- #define ADC_GPIO_MODE GPIO_Mode_AIN - x* ^5 s! B, `% S9 J, D: a* ~. |
- 1 ^& N- s. g" j7 l8 \
- /* 配置与中断有关的信息 */
- @% |0 z- Y6 k. a2 m* K - #define ADC_IRQn ADC1_2_IRQn
8 u. K, m$ Y2 g, k) k - #define ADC_RCC RCC_APB2Periph_ADC1
! ]5 E- e; E; d- s; q2 I - ' T9 V6 k% V& I9 v; d
- # j. }5 p/ [/ O" T
- /* 配置ADC初始化结构体的宏定义 */
# V. K0 i8 {/ H3 C0 B7 \ - #define ADCx ADC1
1 j/ E& R0 e/ h: }% b - #define ADCx_ContinuousConvMode ENABLE //连续转换模式' O$ N/ G, \/ L! N
- #define ADCx_DataAlign ADC_DataAlign_Right //转换结果右对齐- m3 u9 V: G3 k8 U& A2 `
- #define ADCx_ExternalTrigConv ADC_ExternalTrigConv_None //不使用外部触发转换,采用软件触发+ {* @1 D2 K8 g6 f( [, b8 J
- #define ADCx_Mode ADC_Mode_Independent //只使用一个ADC,独立模式
- W3 b/ h% i4 \6 A" {) g6 t - #define ADCx_NbrOfChannel 1 //一个转换通道1 Q7 j) O1 i- `4 T/ z7 y. k* g
- #define ADCx_ScanConvMode DISABLE //禁止扫描模式,多通道时使用
( t4 {, P- u8 h - " u$ ?; t0 L& ?) v/ H* M+ V
- /* 通道信息和采样周期 */5 [, N k* O* y, E" |& u" R* z6 Z& I
- #define ADC_Channel ADC_Channel_115 p1 O% @5 B9 m& w- R% x6 B
- #define ADC_SampleTime ADC_SampleTime_55Cycles5* m2 O; o: {7 E4 G/ t
- ' G3 m( L3 ?$ b* s* B8 P
-
. k5 z4 m" u9 ?- { - /* 函数声明 */
4 o# a$ h% @# `" y+ k9 f" g2 X - void ADC_COnfig(void);
5 T6 b& z# F0 X" S! E% ?1 ?5 ] - void ADC_NVIC_Config(void);/ R& h, y/ ^: H9 W: C; z6 r4 T
- void ADC_GPIO_Config(void);
! Z$ E3 W$ ^+ E: u - void ADCx_Init(void);
0 Y/ C% X/ t8 j -
: K- X- _7 L6 }( u! t9 ~* s: Z5 ` - #endif /* __ADC_H */. q6 z u# }- p, y. h
复制代码
( V0 v- V. X) _3 Z. B* f6 j. ], i+ V( b8 F: Z
, P/ B7 W$ m& ?2.引脚配置函数首先配置相应的GPIO引脚,毕竟模拟信号是通过GPIO引脚传输到开发板的,注意的是,引脚的模式一定要是模拟输入!
& v& R8 x- T/ y6 I
0 w0 w. w' O0 Z2 l7 |- void ADC_GPIO_Config(void)
' ~/ S4 o- b* Z& e - {
* t9 k- v4 j( J4 }! @ - GPIO_InitTypeDef GPIO_InitStruct;/ z+ x4 b9 q C9 _# d
- RCC_APB2PeriphClockCmd(ADC_GPIO_RCC, ENABLE);
) [+ e. c, M6 x8 p/ H; q1 | - 8 S n6 g, `$ L
- GPIO_InitStruct.GPIO_Pin = ADC_GPIO_PIN ;! P& {, {7 c% @
- GPIO_InitStruct.GPIO_Mode = ADC_GPIO_MODE ;$ q K3 s* Y# k5 T: q' O6 y
- # l# d# d, Z( v# Z
- GPIO_Init(ADC_GPIO_PORT , &GPIO_InitStruct);
& n/ D9 B9 i/ U8 ~/ l2 J& _ - }
4 F- N1 q3 {/ ^! L3 U
复制代码
4 p9 D) w8 Q' b9 `5 S配置引脚就是老套路:声明结构体变量、开启时钟、写入结构体、初始化GPIO 0 X% Q3 q0 i- Q t4 v5 W8 w+ n
& z) j c. C8 V A9 y 3.NVIC配置函数
4 P0 `% V, Y! A$ l: v因为我们是在转换完成后利用中断,在中断函数中读取数据,所以要首先配置中断函数的优先级,因为程序中只有这一个中断,所以优先级的配置就比较随意。 # c) c& X* A# Z3 F: B% Y
) C5 T/ e& K; ]7 O6 c- void ADC_NVIC_Config(void)
" w/ q' R; O4 Q7 h. I& z4 | - {
. X% \8 _" g1 H) p" S! O - 1 g' v2 a# s% `) V A. @8 W' q& U4 f
- NVIC_InitTypeDef NVIC_InitStruct ;' C& y% x) @9 d
- " u; `2 T! c }* B+ O
- /* 配置中断优先级分组(设置抢占优先级和子优先级的分配),在函数在misc.c */
# `1 ?: y3 n( M9 ?5 K; w g - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1) ;
2 O+ ~5 L/ u9 u C% _ - 1 M( c5 B, F5 D. G" ^' o# I1 V% ^
- /* 配置初始化结构体 在misc.h中 */+ g9 a+ a2 h' ^. A
- /* 配置中断源 在stm32f10x.h中 */. L! ~8 w* a; p
- NVIC_InitStruct.NVIC_IRQChannel = ADC_IRQn ;* i0 ^$ W3 J9 [
- /* 配置抢占优先级 */9 t |* c" I2 A
- NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1 ;
1 n7 ~: g3 O! p. t& w. ] - /* 配置子优先级 */ t2 `! p G- B! p/ W. x/ v+ S& R! u
- NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1 ;6 a3 h- i) S' X, @ ^. d, V
- /* 使能中断通道 */
4 r5 \' u3 `6 U+ J$ u8 {. }! b - NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;) d. v1 W& M0 T
- /* 调用初始化函数 */
" ^& a7 b, g3 ~) u" D - NVIC_Init(&NVIC_InitStruct) ;
: A5 k( a; y: p - }
7 D E. \1 }4 f5 R. h
复制代码
4 t2 w+ E, a, V9 |4.ADC配置函数ADC的配置函数是ADC的精髓,在这个函数中包含的内容有:ADC的初始化结构体配置、配置了时钟分频、配置了通道转换顺序、打开转换中断、进行校准、软件触发ADC采集等。: J; ^1 X. Z& a
函数中都有详细的注释:
& l' w3 S- v3 |- ?- void ADC_COnfig(void)# A4 M: ?; \. Z* R1 z6 d& R
- {4 `; B* [+ Q# s8 K8 }
- ADC_InitTypeDef ADC_InitStruct;# t9 \. f- Z2 w7 z* \) l- n3 x1 _; G
- RCC_APB2PeriphClockCmd(ADC_RCC, ENABLE);7 ?6 ]2 Y8 a6 ?, }
- 3 B3 s' B! S: u
- /* 配置初始化结构体,详情见头文件 */+ p- a+ ]7 o' z V* m8 C
- ADC_InitStruct.ADC_ContinuousConvMode = ADCx_ContinuousConvMode ;
' [' r/ y8 S5 V% V - ADC_InitStruct.ADC_DataAlign = ADCx_DataAlign ;
& h( p h6 q8 |5 i9 N2 I - ADC_InitStruct.ADC_ExternalTrigConv = ADCx_ExternalTrigConv ;9 ?7 _* x' U3 Z* K" u- q
- ADC_InitStruct.ADC_Mode = ADCx_Mode ;% x; g' }9 D2 J% \, ~/ [0 e
- ADC_InitStruct.ADC_NbrOfChannel = ADCx_NbrOfChannel ;
' h+ ~$ H5 H) } x - ADC_InitStruct.ADC_ScanConvMode = ADCx_ScanConvMode ;
& m* e9 Q, i! i9 J' ~ -
" J# r1 I) ~% R4 g9 a- e- V - ADC_Init(ADCx, &ADC_InitStruct);8 @1 i. l% O/ Z! }" x& k
- & d) V8 D( u. H# n: q. G7 {& H/ E8 i
- /* 配置ADC时钟为8分频,即9M */
. V' c. u3 y' O" @8 o - RCC_ADCCLKConfig(RCC_PCLK2_Div8);
0 |6 ~- G, E* ^/ T - /* 配置ADC通道转换顺序和时间 */
7 h0 Z4 M5 N/ T# S* C/ L - ADC_RegularChannelConfig(ADCx, ADC_Channel, 1, ADC_SampleTime );
- f0 e; }) E9 _- y - /* 配置为转换结束后产生中断 在中断中读取信息 */0 R+ y2 _2 d0 q7 o4 J
- ADC_ITConfig(ADCx, ADC_IT_EOC,ENABLE);
. \& i/ L1 s6 @8 y/ E$ w2 C. x - /* 开启ADC,进行转换 */$ K8 P- W0 e; z. T7 _9 `. L) I
- ADC_Cmd(ADCx, ENABLE );
/ J( ]& L4 H' q. N4 z; h6 Y - /* 重置ADC校准 */
! }1 W- Q ?2 P - ADC_ResetCalibration(ADCx);
4 {: U3 x: V, H - /* 等待初始化完成 */. K* `6 \) P% W. ?% ]2 k7 S
- while(ADC_GetResetCalibrationStatus( ADCx))
0 }, G2 ~5 S, o0 l& S/ d0 A - /* 开始校准 */
' l6 a4 r1 K) P - ADC_StartCalibration(ADCx);* u" P- x- B1 d1 { Z
- /* 等待校准完成 */0 a' E, n& `: g9 e4 n# X. _
- while (ADC_GetCalibrationStatus(ADCx));
7 ], P4 W7 b1 K+ c! E. G [( }9 M4 m - /* 软件触发ADC转换 */
: \) d- p0 o/ `6 k2 J' j5 A - ADC_SoftwareStartConvCmd(ADCx, ENABLE);* Z7 Z& R: e; k( T8 o
- + d M/ }9 M# {6 ]
- }
; c2 y- S1 s; U: d+ [8 b9 D {
复制代码 : u% _ O* \' R3 O: X9 W
( s, P2 ?! t0 n6 {& a# N4 E. d 5.中断函数& A& Z! P' ]4 S0 B7 f# {. ~
在中断函数中进行读取数据,将数据存放在变量result中,此处使用关键字extern声明,代表变量result已经在其他文件中定义.
" w8 h' ]) M$ L; D
- - D" [* g' P+ A+ I4 f" v6 ~0 ~
- extern uint16_t resurt;
: [% y- T" b. c/ h& u+ \ -
" p' l) O6 X6 L1 y- l - void ADC1_2_IRQHandler(void)" _, E5 R O1 f, A
- {
* _+ @3 D3 I1 O% H" V - /* 判断产生中断请求 */
2 e/ ]6 ~/ b0 |4 }- V - while(ADC_GetITStatus(ADCx, ADC_IT_EOC) == SET)1 j; P. }7 E. K& k( _. x
- resurt=ADC_GetConversionValue(ADCx);
. d/ [: Y# h# ?( {6 | - /* 清除中断标志 */
6 W4 A* v- g, L5 e6 n- a1 E - ADC_ClearITPendingBit(ADCx, ADC_IT_EOC);2 h% D; ^1 n& l5 L
- }
' e$ G0 b% W3 v# F# p* s3 f
复制代码
, s# i# }- [1 x( x6.主函数8 X; R( o8 B2 | H) X
主函数负责接收转换的值,并将其转换为电压值,然后通过串口打印在计算机上,便于调试。3 q4 {. L* [! F" W
变量result是主函数中的全局变量,注意最后的结果应该转换为浮点型。 / P. T( |* l$ Q8 E6 l- e# U
. p) Q' w* m" x2 @
0 k( G' g7 {) D. b T7 R: D( R |