STM32f103的数电采集电路的双ADC的设计与使用 STM32F103C8T6拥有3个ADC,其独立使用已经在本文的3.1.3里面有详细的介绍,这里主要是介绍双ADC的同时使用,即STM32的同步规则模式使用。在此模式在规则通道组上执行时,外部触发来自ADC1的规则组多路开关(由ADC1_CR2寄存器的EXTSEL[2:0]选择),它同时给ADC2提供同步触发。此功能必须使用DMA通道。同时两组数据是公用一个寄存器,ADC1数据在低16位,ADC2数据在高16位。由于保证数据稳定,在双ADC同步规则模式的情况下,还添加了多通道同时采样。& Q* W7 \3 [# D
! Z2 g+ M, T; `& S
9 ~5 J; a, d* B; X: N) rADC1和ADC2,工作方式采用了同步规则模式,使得两个ADC可以同时对不同的AD输入进行采集和数据存储和传输,而且相互不影响,也可以确保采样时间的减少,同时两个ADC都是使用4通路同时采集,确保了数据的稳定性。 H3 s E9 U4 _. E0 n( \1 c
" X1 v6 u( N5 w; t* h' a S1 U6 v. k
; r+ B' c1 [. L B
多功能采集显示平台使用的芯片是STM32F103ZET6,片上资源提供了ADC123共3路ADC模块,为了布线方便以及使用的习惯,多功能采集显示平台采用了ADC1的Chanel 4~7,占用IO口PA 4~7,以及ADC2的Chanel 10~13,占用IO口PC 0~3。
5 y' l7 ~3 @5 x2 U. w$ o
. u0 M+ h- Y+ s: G1 t+ P4 S# F+ _" y
使用时有几点需要注意的:
, H( S' i8 {6 g6 Y9 w. B2 N/ M; v
' }. A& }9 \& F1 A3 p
1.选择正确的模式:ADC_Mode_RegSimult,即DUALMOD[3:0] = 0110,ADC2在双模式中,这些位为保留位
: J j( Q% s) T% N0 |! y
/ M+ r+ F- j6 M* s+ k5 m* t
, o6 x0 O1 U9 w* d2.开启ADC的DMA,在双ADC模式里,为了在主数据寄存器上读取从转换数据,必须使能DMA位,即使不使用DMA传输规则通道数据。只有ADC1和ADC3能产生DMA请求。所以只需设置ADC1的DMA:ADC_DMACmd(ADC1, ENABLE);& K4 o# I) w: g, i
: P! }2 t& T* U+ L! f6 p
/ |; }" Z9 l- f! C& V3.ADC2的转换数据存在ADC1_DR的高半字;3 v F5 O0 {$ \) N0 ~1 o
. V5 B1 R- \0 `7 x3 E' H) X+ [7 W1 K' `+ @7 \4 k
4.不要在2个ADC上转换相同的通道(两个ADC在同一个通道上的采样时间不能重叠)。3 C- U1 i# B1 J( d. T! P
$ ~: P/ T" m! O; v: J" \$ s
3 R& \; O$ e3 O5.ADC2的CR2寄存器的第20位——EXTTRIG:规则通道的外部触发转换模式必须开启(软件启动的时候也要),这样才能利用到ADC1的触发信号。不然的话,需要手动再软启动一次ADC2,例如ADC_SoftwareStartConvCmd(ADC2, ENABLE);
5 [" z" |" G2 P6 V' [4 V3 u$ y1 e$ W; Y9 {5 S0 O! u& C4 F0 d; Q. z
; I4 M( A# J; z9 Y, M# ^( G7 I
但是,假如你设置了这个位之后,就不需要手动软启动ADC2了,所以考虑到同步,这样比较好。用ADC_SoftwareStartConvCmd(ADC2, ENABLE);为什么可以,一方面它 也设置了EXTTRIG位,另一方面也设置了SWSTART。但我觉得先用ADC_ExternalTrigConvCmd(ADC2, ENABLE);的话,一会只要ADC1一启动,两者就同 时启动了,这样更正确一点。
7 B4 u! L$ `* l9 e7 r& r9 Y' M& B$ P9 A5 Q/ e
5 H1 b$ x4 B& m+ M% h' N以下是ADC1配置代码和使能代码:
1 H# `$ t: r1 B0 I: |8 {6 \- //初始化ADC1
0 y5 f- N7 U" N! Z% U5 m- \# v" ^ - //这里采用多通道连续采样,并用DMA1的通道传送
* v3 @" ^+ T( \% ]% ?: m0 Q7 v; C - //我们默认将开启通道4~7 / [/ o& |% z9 \
- //相应管脚PA4~7
5 O3 B: l$ C* L9 R - void Adc1_Multi_Init(void)
# T# `& j! t! d1 B2 G - {
* q+ I+ c8 Q; ~6 t2 X - ADC_InitTypeDef ADC_InitStructure; * u. b7 B; u' i8 q6 C0 H3 s
- GPIO_InitTypeDef GPIO_InitStructure; & A( i6 b5 k+ c; |: Q6 w/ c
- & E9 R% L: e3 c* X
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE ); //使能ADC1通道时钟
) T7 ~. ^/ m8 x: ~) U. p - ; W. L( o+ R: b8 E: ~5 s
-
8 @' Z3 {; E* a1 j e& p - RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M 9 b, G2 x. _3 x% V
- 8 T7 @4 I8 p3 Y+ d( p L3 ?
- //PA1 作为模拟通道输入引脚 - B' F3 e! E9 k6 H# _. F
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; . \8 [/ c& B7 v7 D+ W! D
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
$ B9 E# P3 Q ]' Q - GPIO_Init(GPIOA, &GPIO_InitStructure);
5 y; g" Q9 J2 @; d; f7 J - 4 m7 S* R W6 p2 Y
- ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值 % U' e3 L# w4 C. v) p: Q/ C
- ; i" Z* l7 H$ p8 B
- ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult; //ADC工作模式:ADC1同步规则组模式
+ M6 q2 J9 Y+ K' Q+ F. x - ADC_InitStructure.ADC_ScanConvMode =ENABLE; //模数转换工作在扫描模式
, d7 d' y) e! z - ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //模数转换工作在连续转换模式 9 q# o/ Y% G" l" M9 h
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
! I* h; j) L. b - ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐 3 C9 p- K, M4 k/ _# u, k& U0 d; `2 p
- ADC_InitStructure.ADC_NbrOfChannel = 4; //顺序进行规则转换的ADC通道的数目 3 W: F! E* s- V
- ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器 / u, I0 R- w" X( N1 F' q
- 0 B% a# u% J5 u4 P
- ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_239Cycles5 ); 2 p+ j" N% s1 ~: K$ W. q) E) l4 j
- ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 2, ADC_SampleTime_239Cycles5 ); / J* B! K% B- {: c2 {! _) @' ~/ h9 K9 v
- ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 3, ADC_SampleTime_239Cycles5 );
( K* E: ]# S5 [ - ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 4, ADC_SampleTime_239Cycles5 );
: d' _: ?9 B ?) [) D) M! p -
0 P) g/ m& O+ O7 ~ - // 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数) " w& w. `; m! \/ r
- ADC_DMACmd(ADC1, ENABLE); 0 W4 i1 T8 b: S- Q; T' L( a
- }
9 q, X3 [4 v7 C - //计算多通道ADC值 0 R' n3 S+ ^* L6 K) J
- //AD_Value[]是DMA目标地址的数组空间 ' U) o& q) y+ u- K
- u16 Get_Multi_Adc1(void) , f( v) {$ T: T
- { & `2 G$ u) |, d( [
- u32 temp_val=0; 7 U/ y' O9 O# C6 Z+ e- K
- u8 t;
B3 d! k2 {8 Y9 B" y - for(t=0;t<4;t++) 7 b& X) g) V& x S- m
- { # k: U0 x! [8 L* t9 C
- temp_val+=(AD_Value[t] & 0xffff);
0 U; w( k, q) y9 g - } % s( L% s' j: H4 A" r0 q
- return temp_val/4; S/ ^; \6 H* |5 w/ L! f
- }
复制代码 主要需要修改的是这句话,使得ADC处于同步规则组模式。
3 f+ Q( }5 G3 V- <strong>ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult; //ADC工作模式:ADC1同步规则组模式</strong>
复制代码 AD_Value[]是32位的,用于DMA传送的目标的ADC数据数组。其中高16位是ADC2的数据,地16位的是ADC1的数据。
8 \7 _; b2 b7 `+ v$ f5 X2 x; Z& ^. P- e, K! o
9 h7 R5 ~7 q6 a9 \3 i
7 h- @ t ~. u; X1 u其次是ADC2配置代码和使能代码:
/ \ N4 j# M/ Q7 F! S ~- //初始化ADC2
. j2 s9 D9 \# l- Z: s - //这里采用多通道连续采样,并用DMA1的通道传送 0 b4 E f- Z% H2 l K3 k
- //我们默认将开启通道10~13 ! O9 n, k g# S$ l( z
- //相应管脚PC0~3 4 f$ r# k* e6 x9 @9 l& Q
- void Adc2_Multi_Init(void) & d% ~" H2 D; T3 O- M& p
- { 2 X9 S# ?, z1 i! C( N' [: e+ d
- ADC_InitTypeDef ADC_InitStructure;
# t, h: l9 E2 V, S, ~! a7 Y& H - GPIO_InitTypeDef GPIO_InitStructure;
w6 A$ s9 {. W6 N1 B/ ~ -
. c2 X9 w* a. Q2 y( |/ }1 v - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC |RCC_APB2Periph_ADC2 , ENABLE ); //使能ADC2通道时钟
! k, S/ \+ A$ B- n6 G) U) [ - 2 n' P) S% a; r8 ^" ?( T$ ]
- & a o5 m: d" d4 w9 y
- RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
2 V V" ^5 M% X% [$ G- o# r7 ^ -
) ~1 t! u9 i6 N! V# |0 i5 e - //PB0,1 作为模拟通道输入引脚
7 ~9 p0 n& ~! p: T - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; 5 K5 w( z- I4 u" M7 C( N( A
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
7 `+ j( U: Q2 ]9 m% i - GPIO_Init(GPIOC, &GPIO_InitStructure); % q9 {# w" B# j# e$ p5 H6 ^( O
- 7 Y* ^3 F5 w$ R: ~2 ]& }
- ADC_DeInit(ADC2); //复位ADC2,将外设 ADC2 的全部寄存器重设为缺省值 {$ U" Q t/ Y
-
$ k# F1 z, ?: p, d/ b - ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult; //ADC工作模式:ADC1同步规则组模式 ( O p& d. D7 ?( a5 Y! a; r" f& I
- ADC_InitStructure.ADC_ScanConvMode =ENABLE; //模数转换工作在扫描模式
+ H5 M' K3 \4 k5 p/ [2 |" @7 b - ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //模数转换工作在连续转换模式 $ `5 ?8 l, j8 S0 n+ U0 S
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
: Z& a+ P7 U: G! B, I4 W - ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
2 J8 v( t& E' A0 Q+ Q n. @( g. S7 a - ADC_InitStructure.ADC_NbrOfChannel = 4; //顺序进行规则转换的ADC通道的数目 8 T- s g9 S. `
- ADC_Init(ADC2, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
6 A8 U! `7 d |2 }6 Z - : o: w' ]7 {/ q' n$ I0 |$ m
- ADC_RegularChannelConfig(ADC2, ADC_Channel_10, 1, ADC_SampleTime_239Cycles5 );
. ]# n" t2 d7 x$ o1 i- B9 u - ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 2, ADC_SampleTime_239Cycles5 ); $ m m( w+ c) A! G" ~' w# q; \
- ADC_RegularChannelConfig(ADC2, ADC_Channel_12, 3, ADC_SampleTime_239Cycles5 ); , |* G" o S7 W% T
- ADC_RegularChannelConfig(ADC2, ADC_Channel_13, 4, ADC_SampleTime_239Cycles5 ); * J6 w4 V5 C& m+ [8 i6 y3 S
- : J7 Y- U) u* b6 y! _7 M4 C7 H7 x6 M
- ADC_ExternalTrigConvCmd(ADC2, ENABLE); //使能ADC2的外部触发模式
9 f! q/ f! X8 D -
! u2 ^% o7 n1 }) s, z a0 C - // 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)
( F% _- {: L9 A' J" R- f+ H8 k) q/ ~7 f7 ^ - //ADC_DMACmd(ADC2, ENABLE); # I9 Z3 w f0 n4 e' ]8 B/ ^) }
- }
复制代码- //计算多通道ADC值 ) W1 J( N$ |* K9 u
- //AD_Value2[]是DMA目标地址的数组空间
* Z# ~0 P8 v' j7 w - u16 Get_Multi_Adc2(void)
7 A% h6 M6 u Q& k5 l - {
9 d9 f0 l- N+ r" f+ q3 Q- H - u32 temp_val=0; % t: i; k' C# s( n1 o) s. U9 u# R% c
- u8 t;
. l; l+ I# S7 I9 o, [, q' L. X# } - for(t=0;t<4;t++) 9 N, p5 V! c( z- R
- {
! Q% V8 m' N' j. v - temp_val+=((AD_Value[t]>>16) & 0xffff); W! |7 t. _; Q' B, x8 T
- } 6 c/ J* C, d6 Q: H& c4 U
- return temp_val/4;
, I1 l, ^) B9 i9 v - }
复制代码 ADC2主要需要添加下面语句,用于ADC1触发ADC2的工作。
, `, C0 f. s% g; {' x- ADC_ExternalTrigConvCmd(ADC2, ENABLE);//使能ADC2的外部触发模式
复制代码 AD_Value[]是32位的,用于DMA传送的目标的ADC数据数组。其中高16位是ADC2的数据,地16位的是ADC1的数据。这里使用AD_Value[t]>>16进行移位操作,获取数据。$ U! q$ f7 f- Y* S
" Y! R4 L5 _& ?+ m6 a. |
' t! c, H, c( \: k
% A! Y) }6 ^3 @1 x1 E |