1. ADC简介 stm32f103最少有2个AD模数转换器,每个ADC都有18个通道,可以测量16个外部和2个内部模拟量。最大转换频率为1Mhz,也就是转换时间为1us(在 ADCCLK = 14Mhz,采样周期为1.5个时钟周期时)。最大时钟超过14Mhz,将导致ADC转换准确度降低。stm32的ADC是12位精度的。 stm32的ADC转换有两种通道,规则通道和注入通道,注入通道可以抢占式地打断规则通道的采样,执行注入通道采样后,再执行之前的规则通道采样,和中断类似。本例只使用规则通道实现独立模式的中断采样,这里不再赘述两种通道区别。 stm32的ADC可以由外部事件触发(例如定时器捕获,EXTI线)和软件触发(即在配置相关寄存器时,直接开启采样)。 STM32的ADC在单次转换模式下,只执行一次转换,该模式可以通过ADC_CR2 寄存器的ADON 位(只适用于规则通道)启动,也可以通过外部触发启动(适用于规则通道和注入通道),这是CONT 位为0 。 以规则通道为例,一旦所选择的通道转换完成,转换结果将被存在ADC_DR 寄存器,EOC (转换结束)标志将被置位,如果设置了EOCIE ,则会产生中断。然后ADC将停止,直到下次启动。 + }4 m& G" _) t. ]+ r3 N$ z
2. 寄存器简介2.1. ADC控制寄存器(ADC_CR1和ADC_CR2)
ADC_CR1的SCAN 位,该位用于设置扫描模式,由软件设置和清除,如果设置为1 ,则使用扫描模式,如果为 0,则关闭扫描模式。在扫描模式下,由 ADC_SQRx或ADC_JSQRx寄存器选中的通道被转换。如果设置了 EOCIE 或JEOCIE,只在最后一个通道转换完毕后才会产生EOC 或JEOC 中断。 ; ]0 i+ I/ p, k6 n" U4 k9 ?3 N, Q

ADCON 位用于开关AD转换器。而CONT 位用于设置是否进行连续转换,我们使用单次转换,所以CONT 位必须为0。CAL 和RSTCAL 用于AD校准。ALIGN用于设置数据对齐,我们使用右对齐,该位设置为0 。 EXTSEL[2:0]用于选择启动规则转换组转换的外部事件,详细的设置关系如下: 
这里使用的是软件触发(SWSTART ),所以设置这3 个位为111 。ADC_CR2 的SWSTART 位用于开始规则通道的转换,我们每次转换(单次转换模式下)都需要向该位写 1 。AWDEN 为用于使能温度传感器和Vrefint 。 T/ z; Y2 \$ C0 Q
2.2. ADC采样事件寄存器(ADC_SMPR1 和ADC_SMPR2 )这两个寄存器用于设置通道0~17的采样时间,每个通道占用 3 个位。 
ADC_SMPR2 的各位描述如下 
对于每个要转换的通道,采样时间建议尽量长一点,以获得较高的准确度,但是这样会降低ADC的转换速率。ADC的转换时间可以由下式计算: Tcovn= 采样时间+12.5 个周期 其中:Tcovn 为总转换时间,采样时间是根据每个通道的SMP位的设置来决定的。例如,当ADCCLK=14Mhz 的时候,并设置 1.5个周期的采样时间,则得到:Tcovn=1.5+12.5=14 个周期=1us 。 C& {0 \7 k' i* B9 @$ n
2.3. ADC规则序列寄存器(ADC_SQR1~3)
L[3:0] 用于存储规则序列的长度,我们这里只用了 1 个,所以设置这几个位的值为 0 。其他的SQ13~16 则存储了规则序列中第13~16 个通道的编号(0~17)。另外两个规则序列寄存器同ADC_SQR1大同小异,我们这里就不再介绍了,要说明一点的是:我们选择的是单次转换,所以只有一个通道在规则序列里面,这个序列就是SQ0 ,通过ADC_SQR3的最低5 位设置。 - h: Y- m/ X+ V- x4 ?" ~4 @1 {) e: v1 y
2.4. ADC规则数据寄存器(ADC_DR)
这里要提醒一点的是,该寄存器的数据可以通过ADC_CR2 的ALIGN位设置左对齐还是右对齐。在读取数据的时候要注意。
2 A( k/ E4 d% n 2.5. ADC状态寄存器(ADC_SR )
这里我们要用到的是EOC 位,我们通过判断该位来决定是否此次规则通道的AD转换已经完成,如果完成我们就从ADC_DR 中读取转换结果,否则等待转换完成。 1 s, K1 a) F1 s- G; x
3. 寄存器操作步骤1 、开启PA口时钟,设置PA0 为模拟输入。 STM32F103RBT6的ADC通道0 在PA 0 上,所以,我们先要使能 PORTA的时钟,然后设置PA 0 为模拟输入。 2 、使能ADC1 时钟,并设置分频因子。 要使用ADC1,第一步就是要使能 ADC1 的时钟,在使能完时钟之后,进行一次 ADC1 的复位。接着我们就可以通过RCC_CFGR设置ADC1 的分频因子。分频因子要确保 ADC1 的时钟(ADCCLK)不要超过14Mhz 。 3 、设置ADC1 的工作模式。 在设置完分频因子之后,我们就可以开始 ADC1 的模式配置了,设置单次转换模式、触发方式选择、数据对齐方式等都在这一步实现。 4 、设置ADC1 规则序列的相关信息。 接下来我们要设置规则序列的相关信息,我们这里只有一个通道,并且是单次转换的,所以设置规则序列中通道数为1 ,然后设置通道 0 的采样周期。 5 、开启AD转换器,并校准。 在设置完了以上信息后,我们就开启AD转换器,执行复位校准和AD校准,注意这两步是必须的!不校准将导致结果很不准确。 6 )读取ADC值。 在上面的校准完成之后,ADC就算准备好了。接下来我们要做的就是设置规则序列 0 里面的通道,然后启动ADC转换。在转换结束后,读取ADC1_DR 里面的值就是了。 硬件设置:我们通过ADC1 的通道0 (PA 0 )来读取外部电压值。 注意:这里不能接到板上5V电源上去测试,这可能会烧坏 ADC! - #include <stm32f10x_lib.h>1 V# Y' P: }! `) P' W
- #include "adc.h"
# k# R& y* L3 C& t/ S! a - //ADC 驱动代码 2 z% M5 Q, W) i0 T1 a" H5 a! N5 w: r
- //初始化ADC
; y- M" p/ g8 N4 }4 Z* K - //这里我们仅以规则通道为例
8 n/ ~$ l$ a0 s3 [$ L" t: z - //我们默认将开启通道0~3 $ ^5 b+ h7 _' |
- void Adc_Init(void)6 H8 `: g# N! A
- { - R( P! t# I! j6 \4 L' n
- //先初始化IO口
) G1 S" n. t, {, f - RCC->APB2ENR|=1<<2; //使能PORTA口时钟 # W" q+ y, p7 ], p6 {! m
- GPIOA->CRL&=0XFFFF0000;//PA0 1 2 3 anolog输入
8 X3 D: G7 {8 B" `* I/ e) c - //通道10/11设置
% @8 m* m/ T/ h$ ~ - RCC->APB2ENR|=1<<9; //ADC1时钟使能
2 }8 @6 c. k( |2 ? F3 ? - RCC->APB2RSTR|=1<<9; //ADC1复位
) c0 E- @4 t# W2 s" S) R - RCC->APB2RSTR&=~(1<<9);//复位结束 - ~. o8 W; Y6 J; [* Y/ b
- RCC->CFGR&=~(3<<14); //分频因子清零 ) d0 r0 U6 q4 n9 |9 m9 A: G
- //SYSCLK/DIV2=12M ADC时钟设置为12M,ADC最大时钟不能超过14M!
# j9 A+ d3 |7 j2 _5 g! Y0 C& ] - //否则将导致ADC准确度下降!
+ e: G0 Z, Z1 W5 ` - RCC->CFGR|=2<<14;
! Q, V5 Y: R$ R! j9 i/ P! ^( y - " C4 n! O( }/ b5 `
- ADC1->CR1&=0XF0FFFF; //工作模式清零/ `' S8 B" Z7 @+ w' k F
- ADC1->CR1|=0<<16; //独立工作模式
" |. G j' ?* K8 A* ] - ADC1->CR1&=~(1<<8); //非扫描模式 + ] g) m% F6 c) C$ s% Z
- ADC1->CR2&=~(1<<1); //单次转换模式3 |1 r& P$ n1 J) j1 N
- ADC1->CR2&=~(7<<17); $ l3 O% F5 M B) h1 _5 a
- ADC1->CR2|=7<<17; //软件控制转换
3 p/ J; U4 B( M H0 n* r - ADC1->CR2|=1<<20; //使用用外部触发(SWSTART)!!! 必须使用一个事件来触发
4 ?. t, v1 n4 [) h* ^. } - ADC1->CR2&=~(1<<11); //右对齐
$ {- R( C" F5 m2 U+ K - ADC1->SQR1&=~(0XF<<20);
$ S8 L/ ~: E& l% l - ADC1->SQR1&=0<<20; //1个转换在规则序列中 也就是只转换规则序列1
. ]$ r n) j2 ^; o; u - //设置通道0~3的采样时间) W0 G2 \! D# N. [2 D j8 u
- ADC1->SMPR2&=0XFFFFF000;//通道0,1,2,3采样时间清空 % K L$ P7 f9 P
- ADC1->SMPR2|=7<<9; //通道3 239.5周期,提高采样时间可以提高精确度
4 w G2 n7 U) Q9 e$ [2 z! p; y$ m - ADC1->SMPR2|=7<<6; //通道2 239.5周期,提高采样时间可以提高精确度
4 J4 E* A |7 j( j+ A/ V - ADC1->SMPR2|=7<<3; //通道1 239.5周期,提高采样时间可以提高精确度
$ r. b" }6 j( |7 B - ADC1->SMPR2|=7<<0; //通道0 239.5周期,提高采样时间可以提高精确度
6 i3 N; B+ ]8 S) z - * L* b% P1 \( N( m
- ADC1->CR2|=1<<0; //开启AD转换器 3 N+ Q& G3 X [, L, _. d
- ADC1->CR2|=1<<3; //使能复位校准
" t' l! d$ U3 N7 B9 ^+ l - while(ADC1->CR2&1<<3); //等待校准结束 5 A: e3 k$ I( X& d6 O: R
- //该位由软件设置并由硬件清除。在校准寄存器被初始化后该位将被清除。
! V8 {+ j$ u& L1 Y' s3 u i - ADC1->CR2|=1<<2; //开启AD校准
( n' h W" a9 G - while(ADC1->CR2&1<<2); //等待校准结束
2 r( z$ x% R5 X - //该位由软件设置以开始校准,并在校准结束时由硬件清除
# p6 ?: P+ u6 N. O. f - } * |* A0 K( P1 K$ T7 r
- //获得ADC值, R$ Y$ g: }' E6 D- t
- //ch:通道值 0~3% b. I) e& k) {( u9 F( m# T
- u16 Get_Adc(u8 ch)
1 z6 j' w* ~' n; F - {1 C& g/ g# n- T' F4 J
- //设置转换序列
; b/ {& Q" K6 K - ADC1->SQR3&=0XFFFFFFE0;//规则序列1 通道ch; i$ ^8 @3 ?5 X
- ADC1->SQR3|=ch; % A# }9 h" b9 R' ~' Y3 A) H# A
- ADC1->CR2|=1<<22; //启动规则转换通道 ! E6 U. m. `1 r/ v' ~
- while(!(ADC1->SR&1<<1));//等待转换结束
/ V+ o& x& V) v8 I( @0 c! C: P - return ADC1->DR; //返回adc值 / k+ [' S3 `7 W
- }
复制代码 2 P6 L& [$ w: A7 K t1 u% k
- #include <stm32f10x_lib.h>
5 K# s! H* f/ _4 V4 Q - #include "adc.h"
2 ^! l' E( d+ d O# F3 U9 X _ - //ADC 驱动代码
5 y# r9 {+ I& M - //初始化ADC
' u" D. H% k8 l1 |# j - //这里我们仅以规则通道为例
) z5 B2 k& g6 m. h - //我们默认将开启通道0~3
7 I8 U/ Z9 w7 G2 @7 J0 i - void Adc_Init(void)
) |. m% ^* B0 f/ @1 ^ - {
( X& _$ |; z. F; O - //先初始化IO口6 R- r7 h2 j$ e4 H
- RCC->APB2ENR|=1<<2; //使能PORTA口时钟 , q7 K$ L# ~1 J0 V# e1 `
- GPIOA->CRL&=0XFFFF0000;//PA0 1 2 3 anolog输入( ~* Y# k+ V2 N* s
- //通道10/11设置 7 F" {: P& j6 |' x* i- U
- RCC->APB2ENR|=1<<9; //ADC1时钟使能 # z8 W- n3 R$ W" X
- RCC->APB2RSTR|=1<<9; //ADC1复位# _6 N5 f% |1 @( b: |
- RCC->APB2RSTR&=~(1<<9);//复位结束
- @4 P- r J$ f. ]2 U5 I - RCC->CFGR&=~(3<<14); //分频因子清零 ) ]5 D3 ~) G; Z( `
- //SYSCLK/DIV2=12M ADC时钟设置为12M,ADC最大时钟不能超过14M!
/ o9 |' Q; @- H9 E8 y - //否则将导致ADC准确度下降!
- W, A: y6 @) D# Y) s - RCC->CFGR|=2<<14; 9 w* Y% H3 g/ E& x: A& p
-
" g% P8 Q; M' Y: x9 g6 B# s# i* ~ - ADC1->CR1&=0XF0FFFF; //工作模式清零
: w& _. F! g, A6 c3 y2 O - ADC1->CR1|=0<<16; //独立工作模式 , k6 q$ P, _- x* M$ Q9 h
- ADC1->CR1&=~(1<<8); //非扫描模式 9 H: m0 l' z$ [& [
- ADC1->CR2&=~(1<<1); //单次转换模式
/ B. s3 \( E6 _ F2 C - ADC1->CR2&=~(7<<17); + a/ W3 ]# k) ~8 R3 S
- ADC1->CR2|=7<<17; //软件控制转换 0 d B7 i2 S, g) n4 Y4 G3 Z2 A
- ADC1->CR2|=1<<20; //使用用外部触发(SWSTART)!!! 必须使用一个事件来触发1 ?% }+ I9 W2 c4 o
- ADC1->CR2&=~(1<<11); //右对齐 $ l% T) G! Z: x) \0 Q3 P
- ADC1->SQR1&=~(0XF<<20);) i! O c' y- n: S4 z& }
- ADC1->SQR1&=0<<20; //1个转换在规则序列中 也就是只转换规则序列1 ; F" l& B+ t. @" E6 W
- //设置通道0~3的采样时间" K- x/ }, s3 _' X! R9 N
- ADC1->SMPR2&=0XFFFFF000;//通道0,1,2,3采样时间清空 ; n1 f7 @% M; C/ Q) Q4 r
- ADC1->SMPR2|=7<<9; //通道3 239.5周期,提高采样时间可以提高精确度
1 O+ C% U+ w: K. i0 Z4 z! { - ADC1->SMPR2|=7<<6; //通道2 239.5周期,提高采样时间可以提高精确度
, @. {& E6 v. B7 e. o% u0 X - ADC1->SMPR2|=7<<3; //通道1 239.5周期,提高采样时间可以提高精确度
! @- Q; m9 j ^& `( ] - ADC1->SMPR2|=7<<0; //通道0 239.5周期,提高采样时间可以提高精确度
0 t- D" L2 y! U0 v -
+ M4 s$ y) f/ V. q5 x5 W0 M7 ?8 Q - ADC1->CR2|=1<<0; //开启AD转换器
0 E# _5 d# `& O# G- O7 d4 i% W+ ^ - ADC1->CR2|=1<<3; //使能复位校准
# R7 L1 b3 l+ F) i1 [. H - while(ADC1->CR2&1<<3); //等待校准结束
1 k, ]* f5 B7 a' a - //该位由软件设置并由硬件清除。在校准寄存器被初始化后该位将被清除。 $ [4 b0 |$ @6 N; j% h: o
- ADC1->CR2|=1<<2; //开启AD校准 8 z5 k. M# K; |3 C, R5 v2 W
- while(ADC1->CR2&1<<2); //等待校准结束! L( H. e$ P3 S8 q
- //该位由软件设置以开始校准,并在校准结束时由硬件清除 2 e# n. O/ F0 j5 z
- }
* K M& X+ |* N' c - //获得ADC值
" o$ g$ i* W' j4 `4 I - //ch:通道值 0~3 ^6 s4 l. s( o% |4 Y) E3 l
- u16 Get_Adc(u8 ch) 8 Y+ B4 A+ V. {5 t3 v! n
- {
4 J S% T1 u+ J. R# ~" M9 x - //设置转换序列
1 j, J F* G% K" Y7 e' c - ADC1->SQR3&=0XFFFFFFE0;//规则序列1 通道ch( Z1 ?, K) H. F8 P* @; s% s) k# ^7 O
- ADC1->SQR3|=ch;
+ v/ Q' {# S0 Y3 {5 i - ADC1->CR2|=1<<22; //启动规则转换通道
8 Q, ]7 K1 ^5 Z1 N5 B# z8 R - while(!(ADC1->SR&1<<1));//等待转换结束 / L& h- r( v' u9 ]' X! y: K u# ~
- return ADC1->DR; //返回adc值 $ i+ S( y9 \* H6 W2 v
- }
复制代码 + X/ }" y( O+ y8 A( {. `; r" S
- #ifndef __ADC_H
- #define __ADC_H
-
- #define ADC_CH0 0 //通道0
- #define ADC_CH1 1 //通道1
- #define ADC_CH2 2 //通道2
- #define ADC_CH3 3 //通道3
- void Adc_Init(void);
- u16 Get_Adc(u8 ch);
- #endif
. B7 R2 [& n8 I% B0 n- A+ ]- u
5 h4 a+ R! N; }: w& C
6 G3 N" b8 N# G, p7 c; y( V6 E$ g) K0 u/ |
|