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

STM32-ADC模块讲解

[复制链接]
STMCU-管管 发布时间:2020-9-21 13:00
一.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

1_meitu_5.jpg

+ O6 S$ E9 _. q  R! y& C
3 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

2.png

- I# a. o8 `8 [8 t
8 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

3_meitu_7.jpg

( 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

4_meitu_8.jpg
$ 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

5_meitu_9.jpg
, _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

6_meitu_10.jpg
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

  1. / L7 b% ?( M4 |5 ~. [2 a5 z
  2. /**; x/ C* ^8 l/ y( \3 a! }9 R$ [
  3.   * @brief  Configures for the selected ADC regular channel its corresponding
    : B2 M. G$ o8 y0 n+ Q* y
  4.   *         rank in the sequencer and its sample time./ O6 \+ ^% w' e  \$ H
  5.   * @param  ADCx: where x can be 1, 2 or 3 to select the ADC peripheral.
    6 l5 s0 {" _0 l  P% N" x6 \
  6.   * @param  ADC_Channel: the ADC channel to configure.
    : U- s& d# o9 c8 E7 X
  7.   *   This parameter can be one of the following values:
    1 c  _; _) Y/ U& |5 D- Q# k- I. Q# U& r
  8.   *     @arg ADC_Channel_0: ADC Channel0 selected
    2 Z' [( ?* K& u9 L, A
  9.   *     @arg ADC_Channel_1: ADC Channel1 selected# J- O/ s$ o6 O  a4 q
  10.   *     @arg ADC_Channel_2: ADC Channel2 selected6 b, r( a; X  c4 N; K. ?& `
  11.   *     @arg ADC_Channel_3: ADC Channel3 selected
    * \" T* c+ q  x4 v
  12.   *     @arg ADC_Channel_4: ADC Channel4 selected- i. g1 v- J9 g, B  b' L& j( Y
  13.   *     @arg ADC_Channel_5: ADC Channel5 selected+ E! ]0 J! @* z0 q; x
  14.   *     @arg ADC_Channel_6: ADC Channel6 selected) ]! n/ F- Q. C
  15.   *     @arg ADC_Channel_7: ADC Channel7 selected
    , N- ^$ h. B: v
  16.   *     @arg ADC_Channel_8: ADC Channel8 selected6 w" S# ]- T) [% U3 @5 K
  17.   *     @arg ADC_Channel_9: ADC Channel9 selected  I# e2 e4 w5 u/ t$ [
  18.   *     @arg ADC_Channel_10: ADC Channel10 selected
    $ T3 \1 K3 _+ G$ ^
  19.   *     @arg ADC_Channel_11: ADC Channel11 selected
    - [2 j6 c- x- j
  20.   *     @arg ADC_Channel_12: ADC Channel12 selected
    2 S" t1 ~! r( {/ T' |% C/ y- L8 {
  21.   *     @arg ADC_Channel_13: ADC Channel13 selected' k; _, e, c/ C: o  o% J
  22.   *     @arg ADC_Channel_14: ADC Channel14 selected% G! W3 \/ i" ]$ ~6 K/ O( _
  23.   *     @arg ADC_Channel_15: ADC Channel15 selected
    ! ~: F, z; K( `5 |- H% \7 O
  24.   *     @arg ADC_Channel_16: ADC Channel16 selected
    5 I7 ?$ I1 ~8 U+ }9 a8 K
  25.   *     @arg ADC_Channel_17: ADC Channel17 selected
    0 d0 Y' J- m# G% ~2 t  r: ?
  26.   * @param  Rank: The rank in the regular group sequencer. This parameter must be between 1 to 16.
    % `. Y- }# M% s/ I% W
  27.   * @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
  28.   *   This parameter can be one of the following values:
    9 L$ ~% Y! r$ `. n3 `
  29.   *     @arg ADC_SampleTime_1Cycles5: Sample time equal to 1.5 cycles% x  J- d1 p; z- ]' x, F; u" C% }
  30.   *     @arg ADC_SampleTime_7Cycles5: Sample time equal to 7.5 cycles2 R  c$ x  Y. e
  31.   *     @arg ADC_SampleTime_13Cycles5: Sample time equal to 13.5 cycles
    % Z' g; k1 X# D* O
  32.   *     @arg ADC_SampleTime_28Cycles5: Sample time equal to 28.5 cycles       
    & a& X* o1 K* ], @
  33.   *     @arg ADC_SampleTime_41Cycles5: Sample time equal to 41.5 cycles        ) ^0 o$ |  Z9 S& }
  34.   *     @arg ADC_SampleTime_55Cycles5: Sample time equal to 55.5 cycles       
    ) M$ m: K: x. A$ M. v6 }1 c' G
  35.   *     @arg ADC_SampleTime_71Cycles5: Sample time equal to 71.5 cycles        ; s/ ]9 P+ q' T& r& e
  36.   *     @arg ADC_SampleTime_239Cycles5: Sample time equal to 239.5 cycles       
    & b) g7 Z& |- P! b9 g
  37.   * @retval None
    4 h- x, j, h( f8 \
  38.   */
    . M7 O- T3 n8 {. L" V& |  R
  39. 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
  40. {) p1 d: D$ {2 ^1 {+ S, y, Y6 V: O
  41.   函数内容略;
    ( s9 M2 F1 G6 {3 m$ }
  42. }
    * 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

7_meitu_11.jpg
& 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
8_meitu_12.jpg
) \. V# V' T4 ?* u& B

+ u% U# h8 S7 M! p' e) i' E; o
9_meitu_13.jpg
7 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

10_meitu_14.jpg

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

11_meitu_15.jpg
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 |
12_meitu_16.jpg

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

13_meitu_17.jpg

% 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


  1. / I. ]7 K9 O" y1 D  Y
  2. typedef struct
    & S6 _5 K' k. n9 k) }1 N, q
  3. {. ], r& |4 C8 y: P
  4. uint32_t ADC_Mode; // ADC 工作模式选择$ R- f8 Z& W7 _4 Z: Y
  5. FunctionalState ADC_ScanConvMode; // ADC 扫描(多通道)或者单次(单通道)模式选择
    % a  l9 f2 T9 H" h8 h0 N; Z; ^; h
  6. FunctionalState ADC_ContinuousConvMode; // ADC 单次转换或者连续转换选择: \( e% @( z: C8 n4 b% P5 W
  7. uint32_t ADC_ExternalTrigConv; // ADC 转换触发信号选择
    3 }8 b# h; s; @8 q$ w
  8. uint32_t ADC_DataAlign; // ADC 数据寄存器对齐格式1 a" s! e! Y/ m
  9. uint8_t ADC_NbrOfChannel; // ADC 采集通道数# J7 y. n; i8 ^( U! x4 _- e
  10. } 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


  1. 3 R  N) `! J' n/ B4 [  j. K# V1 F
  2. #ifndef __ADC_H' {* _1 R  u) b7 s9 L: B
  3. #define __ADC_H6 N. C& I: Q2 e. t& y) z7 d

  4. ' E% e  |7 R& h( a( [! f6 k8 \
  5. #include "stm32f10x.h") {# [# B* V3 g, x1 {
  6. # D/ H, Q% f% ?# e$ A# z- D& C
  7. /* 采用ADC1的通道11  引脚为PC^1 模式必须是模拟输入*/
    0 Z) i4 a6 `7 H/ I: U8 p& d3 z
  8. #define ADC_GPIO_RCC     RCC_APB2Periph_GPIOC
    & Z6 \' H! [3 D) Y
  9. #define ADC_GPIO_PORT    GPIOC( T, q- b: s: S1 h
  10. #define ADC_GPIO_PIN     GPIO_Pin_18 I# B& {$ U8 x, U9 t
  11. #define ADC_GPIO_MODE    GPIO_Mode_AIN  - x* ^5 s! B, `% S9 J, D: a* ~. |
  12. 1 ^& N- s. g" j7 l8 \
  13. /* 配置与中断有关的信息 */
    - @% |0 z- Y6 k. a2 m* K
  14. #define ADC_IRQn         ADC1_2_IRQn
    8 u. K, m$ Y2 g, k) k
  15. #define ADC_RCC          RCC_APB2Periph_ADC1
    ! ]5 E- e; E; d- s; q2 I
  16. ' T9 V6 k% V& I9 v; d
  17. # j. }5 p/ [/ O" T
  18. /* 配置ADC初始化结构体的宏定义 */
    # V. K0 i8 {/ H3 C0 B7 \
  19. #define ADCx                          ADC1
    1 j/ E& R0 e/ h: }% b
  20. #define ADCx_ContinuousConvMode       ENABLE                                  //连续转换模式' O$ N/ G, \/ L! N
  21. #define ADCx_DataAlign                ADC_DataAlign_Right                     //转换结果右对齐- m3 u9 V: G3 k8 U& A2 `
  22. #define ADCx_ExternalTrigConv         ADC_ExternalTrigConv_None      //不使用外部触发转换,采用软件触发+ {* @1 D2 K8 g6 f( [, b8 J
  23. #define ADCx_Mode                     ADC_Mode_Independent                    //只使用一个ADC,独立模式
    - W3 b/ h% i4 \6 A" {) g6 t
  24. #define ADCx_NbrOfChannel             1                                      //一个转换通道1 Q7 j) O1 i- `4 T/ z7 y. k* g
  25. #define ADCx_ScanConvMode             DISABLE                                 //禁止扫描模式,多通道时使用
    ( t4 {, P- u8 h
  26. " u$ ?; t0 L& ?) v/ H* M+ V
  27. /* 通道信息和采样周期 */5 [, N  k* O* y, E" |& u" R* z6 Z& I
  28. #define ADC_Channel                   ADC_Channel_115 p1 O% @5 B9 m& w- R% x6 B
  29. #define ADC_SampleTime                ADC_SampleTime_55Cycles5* m2 O; o: {7 E4 G/ t
  30. ' G3 m( L3 ?$ b* s* B8 P

  31. . k5 z4 m" u9 ?- {
  32. /* 函数声明 */
    4 o# a$ h% @# `" y+ k9 f" g2 X
  33. void ADC_COnfig(void);
    5 T6 b& z# F0 X" S! E% ?1 ?5 ]
  34. void ADC_NVIC_Config(void);/ R& h, y/ ^: H9 W: C; z6 r4 T
  35. void ADC_GPIO_Config(void);
    ! Z$ E3 W$ ^+ E: u
  36. void ADCx_Init(void);
    0 Y/ C% X/ t8 j

  37. : K- X- _7 L6 }( u! t9 ~* s: Z5 `
  38. #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


  1. 0 w0 w. w' O0 Z2 l7 |
  2. void ADC_GPIO_Config(void)
    ' ~/ S4 o- b* Z& e
  3. {
    * t9 k- v4 j( J4 }! @
  4.         GPIO_InitTypeDef   GPIO_InitStruct;/ z+ x4 b9 q  C9 _# d
  5.         RCC_APB2PeriphClockCmd(ADC_GPIO_RCC,  ENABLE);
    ) [+ e. c, M6 x8 p/ H; q1 |
  6.         8 S  n6 g, `$ L
  7.         GPIO_InitStruct.GPIO_Pin = ADC_GPIO_PIN ;! P& {, {7 c% @
  8.         GPIO_InitStruct.GPIO_Mode = ADC_GPIO_MODE ;$ q  K3 s* Y# k5 T: q' O6 y
  9.         # l# d# d, Z( v# Z
  10.         GPIO_Init(ADC_GPIO_PORT , &GPIO_InitStruct);
    & n/ D9 B9 i/ U8 ~/ l2 J& _
  11. }
    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


  1. ) C5 T/ e& K; ]7 O6 c
  2. void ADC_NVIC_Config(void)
    " w/ q' R; O4 Q7 h. I& z4 |
  3. {
    . X% \8 _" g1 H) p" S! O
  4. 1 g' v2 a# s% `) V  A. @8 W' q& U4 f
  5.         NVIC_InitTypeDef NVIC_InitStruct ;' C& y% x) @9 d
  6.         " u; `2 T! c  }* B+ O
  7.         /* 配置中断优先级分组(设置抢占优先级和子优先级的分配),在函数在misc.c */
    # `1 ?: y3 n( M9 ?5 K; w  g
  8.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1) ;
    2 O+ ~5 L/ u9 u  C% _
  9.         1 M( c5 B, F5 D. G" ^' o# I1 V% ^
  10.         /* 配置初始化结构体 在misc.h中 */+ g9 a+ a2 h' ^. A
  11.         /* 配置中断源 在stm32f10x.h中 */. L! ~8 w* a; p
  12.         NVIC_InitStruct.NVIC_IRQChannel = ADC_IRQn ;* i0 ^$ W3 J9 [
  13.         /* 配置抢占优先级 */9 t  |* c" I2 A
  14.         NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1 ;
    1 n7 ~: g3 O! p. t& w. ]
  15.         /* 配置子优先级 */  t2 `! p  G- B! p/ W. x/ v+ S& R! u
  16.         NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1 ;6 a3 h- i) S' X, @  ^. d, V
  17.         /* 使能中断通道 */
    4 r5 \' u3 `6 U+ J$ u8 {. }! b
  18.         NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;) d. v1 W& M0 T
  19.         /* 调用初始化函数 */
    " ^& a7 b, g3 ~) u" D
  20.         NVIC_Init(&NVIC_InitStruct) ;
    : A5 k( a; y: p
  21. }
    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
函数中都有详细的注释:


  1. & l' w3 S- v3 |- ?
  2. void ADC_COnfig(void)# A4 M: ?; \. Z* R1 z6 d& R
  3. {4 `; B* [+ Q# s8 K8 }
  4.         ADC_InitTypeDef  ADC_InitStruct;# t9 \. f- Z2 w7 z* \) l- n3 x1 _; G
  5.         RCC_APB2PeriphClockCmd(ADC_RCC,  ENABLE);7 ?6 ]2 Y8 a6 ?, }
  6.         3 B3 s' B! S: u
  7.         /* 配置初始化结构体,详情见头文件 */+ p- a+ ]7 o' z  V* m8 C
  8.         ADC_InitStruct.ADC_ContinuousConvMode = ADCx_ContinuousConvMode  ;
    ' [' r/ y8 S5 V% V
  9.         ADC_InitStruct.ADC_DataAlign = ADCx_DataAlign ;
    & h( p  h6 q8 |5 i9 N2 I
  10.         ADC_InitStruct.ADC_ExternalTrigConv = ADCx_ExternalTrigConv ;9 ?7 _* x' U3 Z* K" u- q
  11.         ADC_InitStruct.ADC_Mode = ADCx_Mode ;% x; g' }9 D2 J% \, ~/ [0 e
  12.         ADC_InitStruct.ADC_NbrOfChannel = ADCx_NbrOfChannel ;
    ' h+ ~$ H5 H) }  x
  13.         ADC_InitStruct.ADC_ScanConvMode = ADCx_ScanConvMode ;
    & m* e9 Q, i! i9 J' ~
  14.        
    " J# r1 I) ~% R4 g9 a- e- V
  15.         ADC_Init(ADCx, &ADC_InitStruct);8 @1 i. l% O/ Z! }" x& k
  16.         & d) V8 D( u. H# n: q. G7 {& H/ E8 i
  17.         /* 配置ADC时钟为8分频,即9M */
    . V' c. u3 y' O" @8 o
  18.         RCC_ADCCLKConfig(RCC_PCLK2_Div8);
    0 |6 ~- G, E* ^/ T
  19.         /* 配置ADC通道转换顺序和时间 */
    7 h0 Z4 M5 N/ T# S* C/ L
  20.         ADC_RegularChannelConfig(ADCx, ADC_Channel, 1, ADC_SampleTime );
    - f0 e; }) E9 _- y
  21.         /* 配置为转换结束后产生中断 在中断中读取信息 */0 R+ y2 _2 d0 q7 o4 J
  22.         ADC_ITConfig(ADCx, ADC_IT_EOC,ENABLE);
    . \& i/ L1 s6 @8 y/ E$ w2 C. x
  23.         /* 开启ADC,进行转换 */$ K8 P- W0 e; z. T7 _9 `. L) I
  24.         ADC_Cmd(ADCx, ENABLE );
    / J( ]& L4 H' q. N4 z; h6 Y
  25.         /* 重置ADC校准 */
    ! }1 W- Q  ?2 P
  26.         ADC_ResetCalibration(ADCx);
    4 {: U3 x: V, H
  27.         /* 等待初始化完成 */. K* `6 \) P% W. ?% ]2 k7 S
  28.         while(ADC_GetResetCalibrationStatus( ADCx))
    0 }, G2 ~5 S, o0 l& S/ d0 A
  29.                 /* 开始校准 */
    ' l6 a4 r1 K) P
  30.                 ADC_StartCalibration(ADCx);* u" P- x- B1 d1 {  Z
  31.         /* 等待校准完成 */0 a' E, n& `: g9 e4 n# X. _
  32.         while (ADC_GetCalibrationStatus(ADCx));
    7 ], P4 W7 b1 K+ c! E. G  [( }9 M4 m
  33.         /* 软件触发ADC转换 */
    : \) d- p0 o/ `6 k2 J' j5 A
  34.         ADC_SoftwareStartConvCmd(ADCx, ENABLE);* Z7 Z& R: e; k( T8 o
  35.         + d  M/ }9 M# {6 ]
  36. }
    ; 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

  1. - D" [* g' P+ A+ I4 f" v6 ~0 ~
  2. extern uint16_t resurt;
    : [% y- T" b. c/ h& u+ \

  3. " p' l) O6 X6 L1 y- l
  4. void ADC1_2_IRQHandler(void)" _, E5 R  O1 f, A
  5. {
    * _+ @3 D3 I1 O% H" V
  6.     /* 判断产生中断请求 */
    2 e/ ]6 ~/ b0 |4 }- V
  7.         while(ADC_GetITStatus(ADCx, ADC_IT_EOC) == SET)1 j; P. }7 E. K& k( _. x
  8.                 resurt=ADC_GetConversionValue(ADCx);
    . d/ [: Y# h# ?( {6 |
  9.         /* 清除中断标志 */
    6 W4 A* v- g, L5 e6 n- a1 E
  10.         ADC_ClearITPendingBit(ADCx, ADC_IT_EOC);2 h% D; ^1 n& l5 L
  11. }
    ' 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
赞 收藏 3 评论2 发布时间:2020-9-21 13:00

举报

2个回答
goyhuan 回答时间:2020-9-21 16:39:22
非常详细,注入通道和规则通道可以设定成同一路吗?就是平时是规则转换,一唤酣就进入注入通道?
goyhuan 回答时间:2020-9-21 16:43:23
同一通道为什么分布成ADC1\ADC2\ADC3三个位置?
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版