01. ADC简介
2 B' ]6 B' _; o% l J6 X! ?STM32F4xx 系列一般都有 3 个 ADC,这些 ADC 可以独立使用,也可以使用双重/三重模式(提高采样率)。STM32F4 的 ADC 是 12 位逐次逼近型的模拟数字转换器。它有 19 个通道,可测量 16 个外部源、2 个内部源和 Vbat 通道的信号。这些通道的 A/D 转换可以单次、连续、扫描或间断模式执行。ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。 模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。
3 j2 K. p" W( `& L- B/ H5 X' [" c0 k8 ]/ `% V
STM32F407ZGT6 包含有 3 个 ADC。STM32F4 的 ADC 最大的转换速率为 2.4Mhz,也就是转换时间为 0.41us(在 ADCCLK=36M,采样周期为 3 个 ADC 时钟下得到),不要让 ADC 的时钟超过 36M,否则将导致结果准确度下降。# r' D: ^/ S1 H- s0 z
0 Z; E* h/ ]% ^$ eSTM32F4 将 ADC 的转换分为 2 个通道组:规则通道组和注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。" Q, y0 X3 ^7 {8 J4 }
) u' Y4 l+ e+ c# a5 |02. 硬件资源
) Z/ o# T$ W) }7 o7 ?! I/ \用到的硬件资源有:
9 g( r3 i8 k3 S: h; Y1) 指示灯 DS0
. B' {- M; h. A# M" ~4 G2) TFTLCD 模块+ U3 V5 d- t( M/ W, S3 y
3) ADC, y0 m4 j- w: I s
4) 杜邦线1 i* u! g* B) W" G p
! ?8 }1 g' c# \1 O03. 编程思路# M( i+ I/ w/ N" }1 [$ {6 t- S
①开启PA口时钟和ADC1时钟,设置PA1为模拟输入。* P" {4 Q6 r* {( z* {/ v3 O
: ]4 b4 l! Z, U! i
RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_GPIOA, ENABLE);
9 A' X( i1 S, s, Y
+ J$ o5 R; h+ M. ^RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);' ~& [/ z* z$ n
9 Q: R. ~7 L& i& Y3 I( d$ j; n' gGPIO_Init();: ~" x2 q5 C4 S9 ~ B
8 C; R1 x4 @6 b" l: ^0 V- I/ N
② 复位ADC1,同时设置ADC1分频因子。6 a. l p! R' C
4 K, C: r N. z N8 aADC_DeInit(ADC1);
/ l0 {& N1 w. i
* J; W# U7 N6 N9 P③ 初始化ADC_CCR寄存器。
+ Y/ `9 n; I( _! c* Z+ U ^; p. M s" ` R7 T! z
ADC_CommonInit();/ ^+ c0 K" U$ S+ P
! M- p- M4 ]- d$ A L8 _% M. e
④初始化ADC1参数,设置ADC1的工作模式以及规则序列的相关信息。/ Z% B2 K* @: B R
j% @6 y! O/ `; J5 [( @) p& Xvoid ADC_Init(ADC_TypeDefADCx,ADC_InitTypeDef* ADC_InitStruct);*
& h. b, m1 D7 ?# \/ B8 \. |: ]. L8 L4 P5 M" V9 U$ {7 V- a
⑤ 使能ADC。
6 c2 ^' O5 \* e/ `; Q6 s$ Y# Z0 Z- m6 j( N. F
ADC_Cmd(ADC1, ENABLE);7 ~7 h3 R6 E$ ^
R% e9 ~ E. p+ Z. [ Q⑥配置规则通道参数:
0 ]9 K4 ?& j1 L( L: [6 J& z* A) D, B( v
ADC_RegularChannelConfig();
' l; W) j: C0 t$ F5 R. l' f s/ J7 `3 q8 u
⑦开启软件转换:ADC_SoftwareStartConvCmd(ADC1);7 g% V# l" [2 H' V2 ]# h7 W
/ P: s2 o# Z* k2 r) V, m
⑧ 等待转换完成,读取ADC值。; c- |0 I( m, {( t \* }. h
f% E4 U+ {5 R; ?' q! VADC_GetConversionValue(ADC1);; I* v& ]; z4 H
# J" V6 y' R: x; p* @! k- q
04. 程序示例
4 l; H9 Z' R( I: G5 r dadc.h
/ ]; f) q3 {4 @; x( d/ p/ M
2 ?6 ?+ z5 K: o- #ifndef __ADC_H__& g, H' w# y, F9 L6 |" |
- #define __ADC_H__
0 X- O1 r* z) d - 1 K! ^ e: L& k1 N
- #include "sys.h"
9 j" l( n7 B! V6 Z# H
7 t' i+ d% \+ x7 L0 J- //ADC通道初始化3 G" ` j* i3 N# e) X! J( P
- void ADC1_Init(void);
+ J& a' l) j; {" W" \+ V% l
% R5 a# k' y0 z7 M1 Z+ P. l1 b7 Y- //获取某个通道的值
9 b8 Q! `# Q _! l, _8 \ - u16 Get_Adc(u8 ch);5 x/ ]: o( h. ~! U3 p
- 2 r- d5 `' s" C9 Y
- //获取某个通道给定次数采样平均值! N6 G! E- P. W
- u16 Get_Adc_Average(u8 ch, u8 times);
5 Q& [$ o& I! I0 e" N J4 R* t* a
) Q! L! r2 P6 F- i- #endif/*__ADC_H__*/
复制代码
; d) i4 Q2 e- B& a8 Zadc.c& O# G5 W+ m5 D
- - x: Y+ d/ ~9 N9 X( g# H; A
- #include "adc.h"
* x& t4 p8 S- x - #include "delay.h"6 X9 q% g. @5 j0 [
) |4 L( l5 w2 ~1 z, b4 o- - i! j; ^; j2 ]7 f' J) L
- //ADC通道初始化+ \9 ^, c9 k! g! Z% X
- void ADC1_Init(void)
$ y4 t% m w- r - {
: u# d* T" S+ X( h& W - GPIO_InitTypeDef gpio_InitTypeDef;) e& N2 I# \, h
- 7 Q' n% x, r& x: N) `' h
- ADC_InitTypeDef ADC_InitStruct;& }- s- j5 y9 o' @
-
6 d% |2 p- E1 d, }" y - ADC_CommonInitTypeDef ADC_CommonInitStruct;7 u/ }, s; o0 j! A% n- F: A
- ! `# O- W4 t% E" |4 v0 _
- //开启ADC1时钟
0 l9 Z3 X+ k3 K: S - RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
! D" }, w8 u% t/ ]+ ^9 M: Z -
6 Z, Y7 ]+ x4 S N - //开启GPIO时钟 PA52 m9 J' W1 K D: o% [- d& G U
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
P! y) [) [& S7 W3 v8 r -
2 w! Z% B* W9 O3 H2 S3 W -
& a k# }3 ~+ M2 N1 P% I - //GPIO初始化 初始化为模拟功能
& H# ^: Q$ q, | - gpio_InitTypeDef.GPIO_Pin = GPIO_Pin_5;2 S3 y0 S: k7 ^* L8 V5 o
- gpio_InitTypeDef.GPIO_Mode = GPIO_Mode_AN;/ Q$ m4 `: h0 g/ J
- gpio_InitTypeDef.GPIO_PuPd = GPIO_PuPd_NOPULL;3 S: {, M& W" d0 c
- GPIO_Init(GPIOA, &gpio_InitTypeDef);' S' ~+ g2 `) v% A0 W0 w
- # \3 L, [* h8 | ^0 U
- RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE); //ADC1复位4 ?" u- I2 d% g
- RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE); //复位结束 1 Z9 |7 o& _! p; O
- 9 h# }8 C' a4 `6 ] ]: |# L
-
7 c, i; }! Y; s0 d' g0 s! k5 h - //初始化ADC_CCR寄存器
" v0 w7 G r1 M$ c; L - ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;0 Z# E: n2 k0 P2 q, g
- ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent;, ?. x, @* [% t$ R* t3 g
- ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div4;
3 s p; S. r. R3 a8 b C5 a - ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
5 l! h, w: o: B! A$ o - ADC_CommonInit(&ADC_CommonInitStruct);
. d" r c5 z. C9 U - + E- y) y0 v T. N/ z4 s6 o
- //初始化ADC1
6 U8 R( u/ [$ E8 l. z0 r% |0 Q+ u+ w - ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;
$ y- I8 ^0 o' a; E3 r$ x3 ` - ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
6 Q3 b& U6 V$ J6 ]* R. s% p! F1 B - ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
* [! J0 {1 E5 p' |; b1 T - ADC_InitStruct.ADC_NbrOfConversion = 1;
9 I) _1 p7 G8 o5 R; T - ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;
! ^, i# b+ f' o8 J; h1 r - ADC_InitStruct.ADC_ScanConvMode = DISABLE;% I* j6 H T u! ^9 p- Y' r5 m% U! K
- ADC_Init(ADC1, &ADC_InitStruct);
# S9 l4 [! n0 h. X$ Y4 p - 7 F; v* U3 y ^& o w7 r C s6 r
- //使能ADC( E9 w$ T2 d5 V6 M2 V0 N
- ADC_Cmd(ADC1, ENABLE);
$ D7 A0 T0 ^0 F2 G5 f! t6 L( @ - 2 O/ N: ]# f& Q$ j. [2 D1 o
- }& O, z/ Z. n r x% p
. E( R* |! C/ S' Q1 t0 i+ \* E- //获取某个通道的值
/ `6 z I; Q8 s3 M p0 i8 J - u16 Get_Adc(u8 ch)+ O$ I# s. v% h6 W9 v7 d
- {( r5 C* ? [2 d% F! M! m
- 8 s% |# J6 V' b$ f9 b# Y+ X
- //设置ADC规则组通道 一个序列 采样时间$ e; r7 L! v4 y8 ~5 w# i7 l5 q
- ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_480Cycles); H% }' z( Y9 x& }, k& N% P
-
/ S. {2 v( j2 e# W- v* T - //开启软件转换
% n4 V& g2 O) I; D) y Q3 j - ADC_SoftwareStartConv(ADC1);
7 s' [8 }) o) k6 c -
E" E7 i! K/ b* u) L; v! l. I - //等待转换结束
- g- H7 t" f% E7 Z - while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)
) ?5 K; t, V+ R& @6 v+ f- E4 k- [ - ;
6 f0 k6 M$ J7 F6 A8 M' X - % m. \1 y: r; l4 v- |+ \
- //读取ADC的值7 o' g2 x: { P- }1 f! A1 X+ P
- return ADC_GetConversionValue(ADC1);( P/ a, n7 |- w
- }
! Q4 D- C# |4 s) X/ Z
# o. z* D* ?2 S5 }2 E2 }9 C3 Y- //获取某个通道给定次数采样平均值
' d# J1 c7 Y% S6 C - u16 Get_Adc_Average(u8 ch, u8 times)
- i. X- J) u: f! }+ ~3 p9 _ - {
' n: G' m# }/ ^6 h - u32 tmp_val = 0;
2 j& X' r3 c4 f/ ` W$ e1 C - , z1 K& F7 I9 z% M5 c6 t- j
- u8 i = 0;
* D# v6 Y0 ~: j) X6 H% k, w -
- q' l0 @. |9 ~' c - for (i = 0; i < times; i++)
) I( t1 K3 s y( p; o( `) x - {
7 u1 m! V z; @+ K% _( s5 x) E' X; X; Z - tmp_val += Get_Adc(ch);
$ _ } s; F+ j) }; v - delay_ms(5);8 e7 Y4 H4 X) c' U9 R+ @/ ?- n
- }
; t( U5 a1 i3 x7 m4 Z# p+ B3 Y - ) P$ l( J! A0 _; I2 p# R
- return tmp_val / times;; g& f1 A9 E* D, p9 T
- }
复制代码 8 v) y7 \7 l }" Y
main.c' x. U( m3 Z) E1 T, a( f" V
/ R" g5 l& ~8 Q' r% M
- #include "sys.h"6 q$ F( b; m( I# K
- #include "delay.h"
! b* _& r! K/ I& J - #include "usart.h"6 P) \& _ c8 F# _2 o% D' J
- #include "led.h"4 f: w5 y2 P- @& U( ~; P) {
- #include "beep.h"
: R2 Y+ v# l: r, [2 s- T* n - #include "key.h"/ C% p) W( x# J1 L
- #include "usmart.h"4 U- A1 K) R9 C/ @* z3 o* U/ D9 [
- #include "lcd.h"
, ]% ?2 o/ W. f - #include "rtc.h"% }9 D& K D0 [7 s* ~
- #include "rng.h", ^' K( d4 o) i, Z4 e
- #include "key.h"9 Z4 L! r6 m8 p- D9 a, }
- #include "wkup.h"
- i; L) i3 V8 p w7 H/ x# B; Q - #include "adc.h"
! f6 P7 j9 o( }# ^- O% s+ W+ _# _
9 t) f- X7 ]; z- int main(void)
+ H6 [6 G- _% z# E2 F* ^ - {
) x" b8 h {7 Y2 p& r - u16 value = 0;
' D. e* E% c: _0 J - ! {# c# I$ n1 i/ I1 p1 D! U
- float temp = 0;
) L2 o" L& Q) l9 g5 D2 }7 U - 6 Y( I7 ]- n$ W" J" j
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组25 I) p9 ~2 w& [9 |% N: ^, K
- * |7 Y# w! H( u) Y; B+ a# V) p
- delay_init(168);
: P" J/ m9 b- |' R- { - ) c/ q+ f0 H: x
- uart_init(115200);
- H% t1 b. _# {; | - - W+ c5 b# A; l2 }( y4 {6 _- O" V
- usmart_dev.init(84);2 r1 F* r- q4 ]% w9 G6 H
- " ^( U" N4 V) c
- # j/ u: x ^8 H3 E! G
- LED_Init();, K3 t! o$ |# B: d% o% k* S$ E
- 7 k# w/ d( d: w0 t
-
8 b. O/ Y- ~' h) U; j$ H - LCD_Init();: V7 e( e/ G* J% e+ Q& C E
- 9 M, b" h" [) L6 `7 n, E
- ADC1_Init();& X' r k' g, K) R
-
# s3 X+ Q! r. y1 ?0 ~! C) @1 P - 9 N# @$ x0 H9 D9 r
- POINT_COLOR = RED;( g) N& l# O' Z# H) T
- 4 ]* C3 A: {0 i; G! |& q0 R
- LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");
0 O. O( Z0 E2 }3 Y' A3 p/ K - LCD_ShowString(30,70,200,16,16,"ADC TEST"); ( |6 Z+ x; E5 e" E/ I& D0 t
- LCD_ShowString(30,90,200,16,16,"ATOM@tom");
" }1 T. d3 J; `2 ^+ `' u - LCD_ShowString(30,110,200,16,16,"2020/09/10"); " I+ a! \$ q/ f2 x# f! K5 L) K3 ]
- ; M" ?" N# t d: j7 z# _& e
- //设置字体为蓝色6 u4 x4 d, B+ S1 u/ E. @$ U( w; I
- POINT_COLOR = BLUE;
+ |& y* Y5 Q$ h; l/ z - LCD_ShowString(30,130,200,16,16,"ADC1_CH5_VAL:");
! o+ J. f; \; C/ U - LCD_ShowString(30,150,200,16,16,"ADC1_CH5_VOL:0.000V"); / |6 Y2 C! z4 p& N1 h# ^
-
4 W+ i. g2 t& f6 W: B1 }7 b - while(1)4 ?' L" f. R9 B3 h* x9 S
- {* ]5 ^9 U+ j1 c
- value = Get_Adc_Average(ADC_Channel_5, 20);
9 _' f) }8 z; l7 `" k) }! d l -
1 r/ {' J1 G0 @$ c& n; ]4 t; _ - //显示采样之后的原始值0 I3 S3 G0 b0 v# i' r
- LCD_ShowxNum(134, 130, value, 4, 16, 0);- p$ N+ K8 g4 g; l$ k1 [) e
-
" e! @) j: i/ L5 x6 K( H- x- E8 a - temp = (float)value * (3.3 / 4096);
+ h" I: N% e9 B- @) S! \% r1 W$ ~ - value = temp;
) Z2 `# l; p4 B - LCD_ShowxNum(134, 150, value, 1, 16, 0);
# E" Q* }( n! f( W - 5 M- }$ X' ^7 n% n
- //小数部分
3 a( g3 C( M" l: }. s% }2 [$ F - temp = temp - value;+ }- D4 A) L5 i! `( r8 p3 Z
- temp *= 1000;. Y/ j z" ^0 |( r+ K1 y8 V# J8 _
- LCD_ShowxNum(150, 150, temp, 3, 16, 0x80); 9 y6 V+ l5 }+ X
-
. @) K- z; ?8 M2 l0 n - LED1 = !LED1;
7 e) Z+ r( J( D: q$ v - : y; C6 p' V# ~1 q) \, o
- delay_ms(250);9 k# U/ C7 Y; G3 J* O9 r
- }
" J# H; U( {$ ~3 } - }% u* ]3 l+ C# x! T L( ~
复制代码 , u5 k1 s1 _6 h5 K
2 u$ O: _4 a! a' h
: y* ?' r T! ?, G& D; C
) D1 I3 r8 f- Z6 t/ I
|