stm32f103系列单片机内部ADC为12位ADC。/ B; b3 Z' s. J2 Q
12位ADC是一种逐次逼近型模拟数字转换器。它有多达18个通道,可测量16个外部和2个内部
4 h0 @9 V" S; I% O% N' w1 o2 B信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右
( B6 c; k, d0 ~! I6 C1 ?& h& }) ], o+ U对齐方式存储在16位数据寄存器中。+ X2 @- n S! t5 w& O
模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。) |9 U. Q! k% `. h! }
ADC的输入时钟不得超过14MHz,它是由PCLK2经分频产生。
' r4 I' \- {) [: Q/ W* a# V3 X1 S4 I, @6 \6 l( O9 K
ADC 主要特征/ H9 t6 V) k3 Z. e
W2 q$ u9 M* l+ `# {: b● 12位分辨率2 C9 f8 l' Q0 [
' c( _2 q: q. M& _0 y
● 转换结束、注入转换结束和发生模拟看门狗事件时产生中断, E! w- {3 L1 `4 y7 i: ?
9 j" T- M: U( V● 单次和连续转换模式; D; Q6 C7 [3 E; w
: ^5 k$ C2 x6 _4 P5 m7 I' T9 o# O9 Y7 Q
● 从通道0到通道n的自动扫描模式
0 T; R" Z$ x1 V
+ F) h1 t! a$ S4 E& }$ z/ `● 自校准
0 D6 r k) O d2 l* q- X P- O- J) Q# d0 ]) p
● 带内嵌数据一致性的数据对齐5 A9 Q: f8 U) t+ U, f0 s' P
5 G) r5 _# _9 U" X$ p8 R/ e# u/ ~
● 采样间隔可以按通道分别编程
1 s! H2 h; r7 M, M2 m0 f! }( `! q8 R F3 Y% d/ v
● 规则转换和注入转换均有外部触发选项
$ b* Z7 H, }. r1 r- K8 o9 k! `4 K( ?+ x
● 间断模式
9 v0 A; D9 `+ l( p0 h! x# r
% X! B0 ?' ?$ n: G% P7 V6 ^5 i● 双重模式(带2个或以上ADC的器件)( O2 s9 Z/ C0 \0 _" x+ ]
2 Z& X" b' T2 K1 X$ ?% w( Z& H/ F下面就用代码演示如何设置ADC单次转换模式
' I _' b6 R t+ q1 ?& d/ M1 N& q8 m4 f3 ^; U& _
- void ADCx_Init(void)
+ Q; Y- F' E# |( {# C6 o - {9 J9 i# @/ u# x f4 P
- GPIO_InitTypeDef GPIO_InitStructure;
- z5 R4 r: v8 v% A" D3 O; E5 R) G - ADC_InitTypeDef ADC_InitStructure;
1 v* x7 |) e+ h+ y - , G- H) `3 B, D3 |0 ]" [* g# k$ p: S3 g
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); B- n/ `) m2 A) b
- RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
, n+ n6 r* x1 \ B T! f) d! i - & z" F- A* B- r2 A9 H! k8 P
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1| GPIO_Pin_2| GPIO_Pin_3| GPIO_Pin_4| GPIO_Pin_5; //PA1
+ h- C* o8 W) S) i - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入; l7 T. A; {) H, t6 P7 l
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;2 p& j) j e, Y& n. Y
- GPIO_Init(GPIOA, &GPIO_InitStructure);7 V) K$ @ V: p" U" K# W
4 e$ e, B k* _( |/ u- ADC_DeInit(ADC1);- H" v7 x* j% }, B( _
3 }/ S- @8 E4 G# M+ G* R- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //工作在独立模式* ]' y$ e2 x! K' `
- ADC_InitStructure.ADC_ScanConvMode = DISABLE; //单通道模式
1 j; g, `9 T0 \7 u7 j5 c - ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //单次转换模式5 Z- P9 a, M4 g/ {" J5 A' F# V9 u
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //软件触发5 L. N5 e( t7 ]% m) x
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //右对齐
. u+ h" K+ K/ ?1 z" _ - ADC_InitStructure.ADC_NbrOfChannel = 1; //规则转换ADC通道数
. ]$ g7 e" r. V% o6 L& h2 U - ADC_Init(ADC1, &ADC_InitStructure);
5 }+ n1 \: S7 @( S
- {* _+ m3 @1 m+ e; m" e+ I- ADC_Cmd(ADC1, ENABLE); //使能ADC15 A$ X8 V8 i" X. r
- . o7 b: e5 {) T1 ?; O6 ]" b
- ADC_ResetCalibration(ADC1); //复位校准% U; D. R m# \2 `% z2 Y1 w
- while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
3 X+ A3 B2 Z& m6 V- q0 T - ADC_StartCalibration(ADC1); //开启AD校准& W- r, M2 A' d8 ^: E
- while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束4 x7 \* @8 E2 `& y
) ?7 o5 ]2 h+ C5 _# R* M8 h6 Y- }
复制代码
+ |! e$ q+ R: w0 }2 H+ } 首先初始化IO口,这里的初始化的是ADC抓换通道的0–5,对应的IO口PA0到PA5,将这6个IO口这是为模拟输入模式。接下来在将这几个口设置为ADC口,在设置之前首先将ADC模式复位,然后将ADC设置为单通道单词转换模式,也就是每次只转换一个通道,转换一次后就停止转换,直到接收到下一次转换命令为止。开始转换的命令是由软件来设置的。$ Y, w9 u5 }+ e. y. B U
/ y) B& q/ W Q+ ^ ADC口初始化好之后,还需要一个读取转换结果的函数,用于读取指定通道的转换值。
% G1 n: u* Z+ a/ Z' @5 ~1 {9 x6 v6 E7 I. a7 F8 X( Y+ Q; x% v, R
- //获取ADC值* p6 v G4 k. ^8 J
- //ch:通道号
/ l2 M0 v/ b/ V( ^0 P3 m - u16 Get_Adc( u8 ch )7 K. b) t( K% t* P9 z
- {
2 _9 f& K8 Q6 I7 `' I& a - ADC_RegularChannelConfig( ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期
; P. G }3 _/ i! q$ u3 x. ^ - ADC_SoftwareStartConvCmd( ADC1, ENABLE ); //使能ADC1软件启动转换6 n+ C0 n# a5 S# F; P% k. H! x
- while( !ADC_GetFlagStatus( ADC1, ADC_FLAG_EOC ) ); //等待转换结束
+ s0 ~1 p- O/ p. }5 N, d" B - return ADC_GetConversionValue( ADC1 ); //返回最近一次ADC1规则组的转换结果
" c3 {# M- D( l6 ?6 r - }
% t3 B, K, Q7 e! w0 i - % D6 G1 b J6 F5 ^/ F. c
- u16 Get_Adc_Average( u8 ch, u8 times )$ G0 H( `6 B# N1 h; U5 I0 e W
- { m0 K! b% E5 W4 n! d7 w
- u32 temp_val = 0;* z9 E- d# k! q+ _/ g! n- b3 K
- u8 t;( J9 u$ Q8 m6 d. O5 _, R9 R* }
- for( t = 0; t < times; t++ ); {, R1 g5 k: }6 f7 C" \
- {$ h# D: g x" b, `9 N# L
- temp_val += Get_Adc( ch );
) M) N9 k& u) |- g - delay_ms( 5 );
! R+ _# q- s4 D/ t# Q5 f2 @/ t. o - }
. _$ i& A% j1 m - return temp_val / times;
, [& D4 }' e% m& A - }
复制代码
/ T3 e* o$ f ^4 Y# s, U) xGet_Adc()函数用于读取指定通道的ADC转换值,这里的通道必须是初始化函数中初始化过的通道,这个函数每次只读取一次通道值,为了确保转换结果的正确性,需要多次读取通道值取平均值,所以这里Get_Adc_Average()函数就是用来设置多次读取指定通道值,然后取平均值后返回。比如可以设置读取通道1,100次然后取平均值。ADC相关的设置函数就初始化好了,接下来在主函数中调用Get_Adc_Average()函数就可以读取通道值了。
+ X M& m( v- j
0 B/ p' U' s# y# ~/ g+ u- int main(void)
' v# U3 I! L* C1 h5 m% P - {
4 U4 N8 i9 m' R: ~4 j7 E, E7 |7 M - u16 adcx = 0;
2 p/ |( }* y9 z" _0 v - float temp;2 G- A9 A/ Z1 _& ~' }
- delay_init(); //延时函数初始化% d+ p3 ~0 G# s( M
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);( N) e8 |5 [' q" d/ i, J* `
- uart_init(115200);
6 |6 o* n: f9 O, l4 s - LED_Init();7 w+ L: z6 ^4 t9 v I' T6 }6 K: _: x
- 3 |( c8 s4 A. h/ e1 x
- 0 B* O* c' I: c
- LED0 = 1;
" _; e5 F7 S5 i; B - LED1 = 1;
7 w9 g, ~/ ?! Y% {1 ~" j5 U( m - delay_ms(500);
( Y# r+ X& ^& ~) i% @ - LED0 = 0;
?" V' X B1 O& M0 Q5 `+ q! X5 } - LED1 = 0;
% l' ^5 b: l4 z: H. W& H/ u - " N1 v& t4 J; r q" j
- ADCx_Init();
5 E3 ]" j8 D; B! N
) a8 C# f1 m* A: k2 F$ N, a- printf("ADC test!!!\r\n");+ v( U: O/ C# w! B) Z& ~
- while(1)8 c% H1 U/ R% n, @7 V
- {
0 d: @. |# A* ?% z' @* F - adcx = Get_Adc_Average(ADC_Channel_0, 10);
$ f- b' q- ^2 e) Q - printf("ch0 num: %d\r\n", adcx);
2 E, Y) J2 ~7 i& p+ |+ | - temp = (float)adcx * (3.3 / 4096);
# a- n$ y$ w! M - adcx = temp * 1000;: r7 t. c0 s2 K, J5 r
- printf("ch0 adc value: %d\r\n", adcx);
4 x" k. `1 Q0 Y* m$ M6 E; y - 4 b! a9 i ?, v8 e7 u& b
- adcx = Get_Adc_Average(ADC_Channel_1, 10);
% Q6 J7 n5 j% m. _% ~ d! ] - printf("ch1 num: %d\r\n", adcx);
: e9 k0 A6 y6 K0 E& K, @3 d' |/ V - temp = (float)adcx * (3.3 / 4096);9 r: q: t& s. {1 v- Y
- adcx = temp * 1000;
# i4 H5 g" t9 j6 A9 y1 ` - printf("ch1 adc value: %d\r\n", adcx);# P7 v7 Q/ }) x" Z
# _ O) K9 d. j! z1 o5 a- adcx = Get_Adc_Average(ADC_Channel_2, 10);1 j$ ~9 F9 t8 a7 y I. k+ E
- printf("ch2 num: %d\r\n", adcx);
& X# g; K' A8 h7 S1 H/ ] - temp = (float)adcx * (3.3 / 4096);
4 o4 b Q1 m' m' j% _ - adcx = temp * 1000;1 G c6 L3 y' s# r6 k
- printf("ch2 adc value: %d\r\n", adcx);7 Q: [) X9 K/ {( ^
, v y& \$ X5 u, S2 @6 J. i- adcx = Get_Adc_Average(ADC_Channel_3, 10);1 p' A- l0 b# p1 N
- printf("ch3 num: %d\r\n", adcx);' ^- q; i+ ~% V5 v8 ?" N8 R) z
- temp = (float)adcx * (3.3 / 4096);3 _0 k+ f; N* r& s( x2 a
- adcx = temp * 1000;6 Y# A( Y. O+ L
- printf("ch3 adc value: %d\r\n", adcx);* o9 `, C+ H- A4 l/ `
. S( b- `5 ?6 l: E- adcx = Get_Adc_Average(ADC_Channel_4, 10);
2 Q) b" y' z3 a% F ]! M - printf("ch4 num: %d\r\n", adcx);1 r' s: j9 k/ C2 R) ]. Z( B
- temp = (float)adcx * (3.3 / 4096);( N) G0 n) N& X3 ]
- adcx = temp * 1000;& U; R- S2 y, {" i3 z
- printf("ch4 adc value: %d\r\n", adcx);
% t- ~" Y2 `/ T+ Z. R; G - " I j: V, y1 m8 _
- adcx = Get_Adc_Average(ADC_Channel_5, 10);
. W9 \8 @% y( Z3 u `3 W; L - printf("ch5 num: %d\r\n", adcx);5 J7 j% u& y- K" {/ e3 l ?
- temp = (float)adcx * (3.3 / 4096);
# a/ O _* n' p* Z1 [& c - adcx = temp * 1000;8 b* A Q6 P. q* H
- printf("ch5 adc value: %d\r\n", adcx);, D# r3 Y2 d8 `8 K! S2 U
- ) K( E4 F6 M1 Q+ p! D9 g
- LED0 = !LED0;
0 Z. s1 W$ ]& y( p; x. ]! P - delay_ms(500);8 {* `: ?7 ?- `7 V# y
- }
# Z3 {* L# O4 \& q- m5 L - }
复制代码 + O9 N% ?, W% n* t
在主函数中依次读取通道0到通道5的值,读取10次取平均值,然后将转换后的值打印出来。由于ADC为12位,所以转换后的最大值为4096,对应的最大电压值为3.3V,为了方便观察,将转换后的值换换位电压值。# _$ y8 |$ @; y* S
9 I. Z5 x" W) q8 y) {4 u
" x7 E: w" X( `. M7 x |