stm32f103系列单片机内部ADC为12位ADC。
9 k1 S4 y" l4 p1 s$ Q9 G* Q12位ADC是一种逐次逼近型模拟数字转换器。它有多达18个通道,可测量16个外部和2个内部
2 s3 c! N3 a1 o信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右
& ^" B }: L/ t& x! Q- y* e对齐方式存储在16位数据寄存器中。
" v4 G6 ]; k9 R7 l: M模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。
6 [2 C% q6 ^7 k2 eADC的输入时钟不得超过14MHz,它是由PCLK2经分频产生。! o& y3 F+ [+ T# I$ a6 t
9 ?- _& R4 s- i: k. H# P1 L9 kADC 主要特征8 l0 O, [" J5 u$ t
/ C8 d& n' k5 ^1 D
● 12位分辨率4 |7 Q1 N+ `5 I; r @7 |
8 X! p6 e& m' I9 ]● 转换结束、注入转换结束和发生模拟看门狗事件时产生中断
5 g) q2 I# F2 h* \- G# ~: H( I8 h1 X. _! s9 s: X2 b
● 单次和连续转换模式9 \( z: K& Q* e4 {
" l8 j" j t# Q
● 从通道0到通道n的自动扫描模式
5 K! P( y- e: G3 t* }" U h8 G% @1 I2 v( `
● 自校准
( ~4 S9 S' r+ u; G% ]4 g5 d
6 H* n. M! ^5 Y( i% t● 带内嵌数据一致性的数据对齐) X" h1 ]$ \: ~9 Z! _8 S
- {! d2 s2 F' ] D- E/ C# i● 采样间隔可以按通道分别编程
6 M1 q6 T' c6 y9 }+ O; s& }: D$ Z$ |4 `
● 规则转换和注入转换均有外部触发选项; @% u8 w W D7 W
& \: y' Z) X6 b8 o● 间断模式
" ^7 o) m3 ^( s: t6 \* y7 e: v' J) F" r8 e
● 双重模式(带2个或以上ADC的器件)/ R) X3 |2 _' k
2 |( D# R% J0 W# i5 \
下面就用代码演示如何设置ADC单次转换模式
) U8 o" A; O' M2 ^5 Q' I# T; N, F* H2 ` A, P( k5 K/ H
- void ADCx_Init(void)7 i1 n# R6 i/ R! M" V) P
- {4 E+ W0 \3 K8 g% A/ l
- GPIO_InitTypeDef GPIO_InitStructure;4 `1 |2 O4 Q0 P6 c2 K: w3 z
- ADC_InitTypeDef ADC_InitStructure;
2 l7 `8 M* U/ ~/ G+ i7 R! G% N
6 X( a$ Q1 O1 i3 q- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);& [0 \: `8 V1 v# D
- RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M1 p& y/ T5 L* ]
- ) t, \& c. p* c" L1 P! U- O
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1| GPIO_Pin_2| GPIO_Pin_3| GPIO_Pin_4| GPIO_Pin_5; //PA17 D0 }9 B8 i' R3 O! L
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入* m, {" x/ G, {: R7 F
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
5 d; a5 A0 P, F6 ]# E - GPIO_Init(GPIOA, &GPIO_InitStructure);
' R% u: E6 l; P
) m$ D$ h9 C) b1 M9 H# i* y5 H- ADC_DeInit(ADC1);0 N- B: ~0 A4 V+ d
- 4 B% o# R% b) n, T$ N% c
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //工作在独立模式
. d6 H1 {( x2 `1 z% K& ` - ADC_InitStructure.ADC_ScanConvMode = DISABLE; //单通道模式+ Q- ?* y; J- Q: m+ Y/ e* g) p
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //单次转换模式3 F3 N& g" |2 I" U- b
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //软件触发. s0 T' d! W! k
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //右对齐
{; D6 Q* M0 P( ?3 L% C& W - ADC_InitStructure.ADC_NbrOfChannel = 1; //规则转换ADC通道数
4 w1 z |/ I4 G - ADC_Init(ADC1, &ADC_InitStructure);
$ s% t0 _1 h' p* O5 [ - : C' c0 o d# D' [
- ADC_Cmd(ADC1, ENABLE); //使能ADC1" K1 o) }& X: ^! L9 Q' W
- " x3 \* _1 Z' Z1 ~) h
- ADC_ResetCalibration(ADC1); //复位校准$ a: ?$ t, ^0 X# n* n# H6 `
- while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
S% |6 `, F: b - ADC_StartCalibration(ADC1); //开启AD校准
6 W9 y! H8 U6 Y( L! H, B: U3 K - while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束4 [6 C7 _3 \& H6 G, K! V
* s+ {: [" I4 l7 ~9 W6 g- }
复制代码
/ l/ x0 [+ D8 u5 [4 ]5 B1 s$ b+ t 首先初始化IO口,这里的初始化的是ADC抓换通道的0–5,对应的IO口PA0到PA5,将这6个IO口这是为模拟输入模式。接下来在将这几个口设置为ADC口,在设置之前首先将ADC模式复位,然后将ADC设置为单通道单词转换模式,也就是每次只转换一个通道,转换一次后就停止转换,直到接收到下一次转换命令为止。开始转换的命令是由软件来设置的。1 D; [7 S8 X# x
8 n$ K! j3 t+ x$ Y8 ]
ADC口初始化好之后,还需要一个读取转换结果的函数,用于读取指定通道的转换值。# E. F7 v& ~: J( m6 p5 p# u8 d
8 T* @3 w1 g6 r& x
- //获取ADC值
7 l- p. b9 k7 {5 ` - //ch:通道号1 e. M* R' n/ S
- u16 Get_Adc( u8 ch )+ I: U# Q$ o2 Z6 L/ p* J
- {
7 Z+ d( e, W$ [0 ^, i - ADC_RegularChannelConfig( ADC1, ch, 1, ADC_SampleTime_239Cycles5 ); //ADC1,ADC通道,采样时间为239.5周期
' P9 D& {& e& A4 H+ y( K4 B7 T& B - ADC_SoftwareStartConvCmd( ADC1, ENABLE ); //使能ADC1软件启动转换6 U3 T) p: V% x: ` A4 o
- while( !ADC_GetFlagStatus( ADC1, ADC_FLAG_EOC ) ); //等待转换结束 A/ G: F9 e8 D
- return ADC_GetConversionValue( ADC1 ); //返回最近一次ADC1规则组的转换结果
2 x9 E( Z# r$ R( W& Z - }
& R4 P) s9 R( |4 R
# y" H/ ?/ g- Z- u16 Get_Adc_Average( u8 ch, u8 times )
. g9 t6 W: O7 z5 S - {- @+ J/ W P+ O, @
- u32 temp_val = 0;
5 x# @% j% @$ ^5 I* O5 L - u8 t;
- j7 R9 G2 m: X/ e - for( t = 0; t < times; t++ )
; Y l1 E7 k% D+ z ] - {) r. g1 [ a5 x# r* e* Y, h; @- z
- temp_val += Get_Adc( ch );
8 b! r2 g* n; y& ?7 h9 z& s - delay_ms( 5 );
- O6 Z$ k$ @; W S' r- ` - }
z' ~( a6 R6 I$ S- ^ - return temp_val / times;
) t& D8 o: X4 f9 a# N f5 ~ - }
复制代码 7 _% z. v% R7 N
Get_Adc()函数用于读取指定通道的ADC转换值,这里的通道必须是初始化函数中初始化过的通道,这个函数每次只读取一次通道值,为了确保转换结果的正确性,需要多次读取通道值取平均值,所以这里Get_Adc_Average()函数就是用来设置多次读取指定通道值,然后取平均值后返回。比如可以设置读取通道1,100次然后取平均值。ADC相关的设置函数就初始化好了,接下来在主函数中调用Get_Adc_Average()函数就可以读取通道值了。
) R' [$ |; N# @+ @ [0 I; W$ Y7 w: j$ N
- int main(void)
. F& ]2 ]% v, R7 v$ E- q - {
: y' Y1 w8 i, T0 q% I - u16 adcx = 0;
* r. e: a, m0 w8 q2 x - float temp;2 ]9 h4 B4 E+ k2 L4 x* E
- delay_init(); //延时函数初始化. n( O# ~1 w4 D" y$ E, Z" x; ^
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
6 d' x/ z) S- i) ?' h+ w - uart_init(115200);, S% C- u8 Y4 S" e( K; Z
- LED_Init();! {3 e$ r; B) u: g: N, M; t
- \% }( D W# J
) r# X4 \2 i* h1 T( B- LED0 = 1;+ T3 z j3 X% U4 |5 k
- LED1 = 1;. N, u* l M9 l. X7 i# @
- delay_ms(500);0 ]( w2 P5 v# f( @
- LED0 = 0;
) U1 k% K6 I+ v) k - LED1 = 0;
+ ?7 ]7 _0 X9 }' b) o' Y
: V$ @' j, p l; o/ ]" G- ADCx_Init();
]& G4 u7 U; p8 Q: X - ! |& @' b( W* m' i
- printf("ADC test!!!\r\n");( {2 d$ _/ u# ?, P$ |' R. U
- while(1)
$ }7 W! Y% N8 M/ _* p# O2 s% Y - {
9 t( {+ `- M8 w5 r% s% r - adcx = Get_Adc_Average(ADC_Channel_0, 10);
% @& X W. Y( o* j9 Q - printf("ch0 num: %d\r\n", adcx);% J1 A& X0 ^7 J0 x
- temp = (float)adcx * (3.3 / 4096);
' r9 c9 r! Q3 E" | - adcx = temp * 1000;/ u! x; x6 e/ p
- printf("ch0 adc value: %d\r\n", adcx);# X2 M; ]! ]8 M8 y6 I2 H- Q9 l X
- " t: P1 h: S: I8 r6 e; A
- adcx = Get_Adc_Average(ADC_Channel_1, 10);
5 Z" i9 P0 {: F% `- O' r - printf("ch1 num: %d\r\n", adcx);
7 z/ t+ `( x7 t1 ~3 h# ?- b - temp = (float)adcx * (3.3 / 4096);. l. I) k% G0 I+ m- J
- adcx = temp * 1000;
) n0 [) L1 c. y4 i- Z9 x( d - printf("ch1 adc value: %d\r\n", adcx);9 f, K$ w' ]8 B' Z
: S7 d; y8 l2 x3 @. D6 g- adcx = Get_Adc_Average(ADC_Channel_2, 10);1 z3 |/ y6 ? e3 @4 w
- printf("ch2 num: %d\r\n", adcx);
' \4 x' z( E3 {# w5 W* A" o - temp = (float)adcx * (3.3 / 4096);3 Z! @* k- Z% @9 @. c& s! o* {
- adcx = temp * 1000;0 G% b& y) ~, o: Q9 T
- printf("ch2 adc value: %d\r\n", adcx);; J7 L$ F4 { l6 F& |) |
4 w$ n) u) G: }# D6 v9 a' I, a- adcx = Get_Adc_Average(ADC_Channel_3, 10);
( {! p1 u; ^" S5 S& O- x# z m* f$ s - printf("ch3 num: %d\r\n", adcx);* ~6 Y: s2 X* E, l
- temp = (float)adcx * (3.3 / 4096);4 O+ X3 `2 _, H v% j! s6 Q
- adcx = temp * 1000;1 ~* v2 d, ^, q
- printf("ch3 adc value: %d\r\n", adcx);
7 \* O" p5 y( J! Y& X+ w' ]
: ^: ^8 V: E, B# \' N3 A- adcx = Get_Adc_Average(ADC_Channel_4, 10);- v3 `" L& }, k) q' g( `: p
- printf("ch4 num: %d\r\n", adcx); G! e$ F5 Y1 r# _5 K: \
- temp = (float)adcx * (3.3 / 4096);1 i% P+ y6 i! C- h" D7 {
- adcx = temp * 1000;
' }' t2 x' ]* Z) k" D' w) Q - printf("ch4 adc value: %d\r\n", adcx);
; ?. O! Y, ]9 l5 N: C - - I! C/ V' e1 j( ]
- adcx = Get_Adc_Average(ADC_Channel_5, 10);
/ W' L5 }9 H4 E' [8 N+ N - printf("ch5 num: %d\r\n", adcx);
1 s: [" ?1 f5 t( U - temp = (float)adcx * (3.3 / 4096);9 z" Z6 M S* k8 L+ [1 Q
- adcx = temp * 1000;. C. c+ F3 i* q0 N" F/ I5 @
- printf("ch5 adc value: %d\r\n", adcx);9 _4 W; E4 P/ I6 g" h
6 w. n7 g* L/ f( @$ L- LED0 = !LED0;- [" Y2 f$ O3 E' `5 F* Z; q
- delay_ms(500);
% C$ P( O1 M/ j4 a# F) m; ?/ ~" \ - }. l8 ?! L: T: X$ O ]' g% U
- }
复制代码
- Q9 F2 O" m# \# {( m在主函数中依次读取通道0到通道5的值,读取10次取平均值,然后将转换后的值打印出来。由于ADC为12位,所以转换后的最大值为4096,对应的最大电压值为3.3V,为了方便观察,将转换后的值换换位电压值。
8 T. f( t7 l8 t! e7 D! X
* g- I% L V! x% [2 v' o9 d5 N
! ]8 F3 U* z8 a7 H; k! t3 n |