片上ADC介绍
, P! ^" J3 P+ _1 zSTM32F103系列与STM32F407系列ADC的功能类似。它们总共有 3 个 ADC,精度为 12 位,每个ADC 有 16 个外部通道。另外还有两个内部 ADC 源和 VBAT通道挂在 ADC1 上。ADC 具有独立模式、双重模式和三重模式,对于不同 AD 转换要求几乎都有合适的模式可选。3 {6 S8 w4 D1 H: _- |/ t
X1 y- d8 \1 z) o0 F! _, y电压输入范围
0 u: Y) E; @* A" P4 u) N" [" ?ADC 输入范围为:VREF- ≤ VIN ≤ VREF+。由 VREF-、VREF+ 、VDDA 、VSSA、这四个外部引脚决定。在设计原理图的时候一般把 VSSA和 VREF-接地,把 VREF+和 VDDA 接 3V3,得到ADC 的输入电压范围为:0~3.3V。: e- \ ^ T) ^' y1 w
输入通道( N4 P* l* q% |& o3 n6 _3 x
STM32 的 ADC 多达 19 个通道,其中外部的 16 个通道就是框图中的 ADCx_IN0、ADCx_IN1…ADCx_IN5。这 16 个通道对应着不同的 IO 口。" C) }" ~& m$ s" e- {
9 z% ], b( I0 T* T; T4 p1 [& q对STM32F103系列单片机,ADC1的通道16连接到芯片内部的温度传感器,通道17连接到了内部参考电压 Vrefint。ADC2 的模拟通道 16 和 17 连接到了内部的 VSS。ADC3 的模拟通道 9、14、15、16 和 17 连接到了内部的 VSS。
! b. \: H" p; A$ Z( h9 O
8 S9 A3 {: V) w6 z+ J对STM32F407系列单片机,ADC1的通道16连接到芯片内部的温度传感器,通道17连接到了内部参考电压 Vrefint,通道18连接到了备用电源 Vbat。ADC2 和 ADC3 的通道 16、17、18 全部连接到了内部的 VSS。
2 a9 G$ @% G4 S; i/ \
+ x' }" v- K; R* Y
* H2 y( i1 r9 K3 W5 y% s2 ~3 F! F2 v
+ Q3 M) n. J" t! o8 g- B
) U* O* n5 Z0 m0 u+ l/ C0 n- _9 ]
8 Q' y, e5 d5 `4 C7 N扩展库特性6 d! ?7 ?$ ~ W5 i+ ~, {4 G9 v
本扩展库针对ADC最常见的操作方式,编写了两种ADC配置模板,其一是不使用DMA传输的单次单通道采样(void ADC_UserConfig1()),其二是使用DMA传输的连续多通道采样(void ADC_UserConfig2())。可根据需要在程序初始化的时候调用对应的配置函数实现ADC的快速配置。同时提供了在两种配置模式下的获取ADC数据与平均值数据的函数,也可在适合的位置直接调用。+ e2 U+ Q! {4 ]+ f6 B
9 p. {8 w1 z h使用了本扩展库,ADC的配置与读取数据将会变得十分简洁高效。2 B% T, s5 I1 Q
$ U) @; W# a0 M
扩展库源码
/ f7 X, Y E8 b1 YSTM32F103与STM32F407的ADC在使用的时候还是有一些区别的:/ ^5 G; a: f, g. j7 n0 S" p5 B" E
1.STM32F103的ADC需要校准,而STM32F407的ADC无需校准。
3 C @$ a) a7 R2 n2.STM32F103与F407的ADC时钟配置、外设配置、DMA配置等不同。+ J& i2 _5 W0 Q# z3 m& a& R
% S ^: {% z) Q) i1 F' Y9 P
基于STM32F103- {6 q, w- p) a! _. f, `
头文件* T9 T" i8 X9 \2 T/ Z6 O
- #ifndef __ADC_EXT_H__ ?" O* g7 b( T7 {
- #define __ADC_EXT_H__
g! F3 a' Z! n$ a9 o' c' g1 ? - : n$ d/ |9 V2 J x0 E9 F
- #include "stm32f10x_conf.h"7 K* x8 T" u) F' ^& y
- #include "stm32f10x.h", z& Y0 `& k0 e7 ?3 @+ S
- & F7 M. b; I6 Y6 V; O
- #define ADC_CHANNEL_NUM 48 J, G: }/ `: a: Q
- " f n8 n+ }3 q
- #define ADC_DMA_CHANNEL_DEEPTH 10
3 b+ {/ I$ h: C/ d0 I6 e - #define ADC_DMA_CHANNEL_NUM ADC_CHANNEL_NUM
6 T7 G0 j. I: O; T9 I0 T$ @ - #define ADC_DMA_BUFFER_SIZE (ADC_DMA_CHANNEL_NUM * ADC_DMA_CHANNEL_DEEPTH)
# s; L4 z4 F, @ @ R( i! T
' j2 h; _( \ A3 D& ^: a- extern volatile uint16_t ADC_Data[ADC_CHANNEL_NUM];! L* K, U6 i! I# g4 u. H" a
- extern volatile uint16_t ADC_DMA_Value[ADC_DMA_CHANNEL_DEEPTH][ADC_DMA_CHANNEL_NUM];
: ^ Y. u+ s D3 { e7 R6 J
' s5 ~, J" A5 [- void ADC_UserConfig1();
1 Q2 }( D; Y9 j& y0 B5 c! P& ~+ x8 Z/ j - void ADC_UserConfig2();+ ]- s6 p( E$ v9 }) O3 F9 @
$ Q# V$ i+ o) w6 Z( \" r- void ADC_Config_NotUseDMA();) n+ u6 \5 b/ U% [. D4 I2 H
- void ADC_Config_UseDMA();' F3 W3 b: `6 S
- void ADC_Config_SetDMA();5 R7 V$ x. W& l1 k
- ; {5 B) z. s: T6 P/ c) l+ B
- uint16_t ADC_GetData_NotUseDMA(uint8_t channel);3 d3 l% c9 k5 y# }$ j
- uint16_t ADC_GetAverageData_NotUseDMA(uint8_t channel, uint16_t count);
4 ]. K* q2 ^8 e - uint16_t ADC_GetData_UseDMA(uint8_t channel);5 R- [4 B4 y" b1 R( S* E
- uint16_t ADC_GetAverageData_UseDMA(uint8_t channel);
! C4 w* Y) L+ \/ S& w# b
$ p4 x! w, a4 w- #endif _, E! x( j6 H4 |
复制代码
! b2 k% W" D& |' }- H9 P. G源文件! F$ T" T7 Q6 i! z
- #include "adc_ext.h") y) V1 R2 K3 z V
- : P9 q3 U6 L( @ l% N- Z9 r
- volatile uint16_t ADC_Data[ADC_CHANNEL_NUM];
) F; h7 `7 ?7 Z* T3 C% E, ~3 z% t - volatile uint16_t ADC_DMA_Value[ADC_DMA_CHANNEL_DEEPTH][ADC_DMA_CHANNEL_NUM];
/ n6 K) y& K0 o2 o: M
. B6 Z5 I! [6 f8 f- /**
( z0 g% X! a$ R9 ^ - * @brief 预置的ADC配置1,不使用DMA传输
! h$ P3 y8 U9 n1 } - */
" F$ e! B$ C6 i; u. | - void ADC_UserConfig1(). p1 y; W( I# n; m
- {
4 }6 A S+ k6 q( y, ~8 | - GPIO_ConfigPort('A', 0, GPIO_Mode_AIN, 0);8 I$ d$ h K* U9 P1 P4 S1 q! _+ I% a' Z1 c
! \1 S0 }" P; h$ O- ADC_Config_NotUseDMA();, [$ U, ?* c* M% ~, w$ @2 h, b
- }3 Q) I: ?/ }5 o/ Z" v9 B9 e
- 1 H, P+ [5 ?* u/ S: s" U9 }7 Z, a
8 H9 \" p: \# b: e" f8 m- /**; K, f. F2 p( z' B- w
- * @brief 预置的ADC配置2,使用DMA传输7 q& q: w* ?- ?" ^
- */
+ ^1 n- t* i* j) H - void ADC_UserConfig2()% _/ {4 g$ s. J9 a5 J @
- {
& D- N# j. |1 x4 q5 c# \ - GPIO_ConfigPort('A', 0, GPIO_Mode_AIN, 0);
, i- |3 c) h/ c7 ?% m( c7 O: E6 P% e - GPIO_ConfigPort('A', 1, GPIO_Mode_AIN, 0);7 d7 l$ r/ Q1 P- j4 R( f
- GPIO_ConfigPort('A', 2, GPIO_Mode_AIN, 0);
' X, M3 a1 V3 z) B - GPIO_ConfigPort('A', 3, GPIO_Mode_AIN, 0);7 t& M& u! h2 F2 `( i7 B
-
- A+ A: x/ M y+ ~$ D; z5 X - ADC_Config_SetDMA();5 Q s( _4 P6 |
- ADC_Config_UseDMA();8 K* O) Z' G* ?- o9 ~# V* d d5 l. L
- }% L3 j; ~' l0 x
% A% _& c! W u4 t- ( v* Y; m" Q7 E1 ^) e6 ?
- /**% X5 I Q: {" F5 k2 f
- * @brief 不使用DMA进行数据传输时的ADC配置
: s% F$ j: m, J4 k+ j4 N8 U% M - * 由于ADC 规则组数据寄存器 ADC_DR 只有一个,是一个 32 位的寄存器,
0 Y" r9 ~$ R% n - * 只有低 16 位有效并且只是用于独立模式存放转换完成数据。& C* t4 P4 k" f
- * 如果使用多通道转换,那转换的数据就全部都挤在了 DR 里面,前一个时间点转换的通道数据,
# h: N# W& Y0 e - * 就会被下一个时间点的另外一个通道转换的数据覆盖掉,所以当通道转换完成后就应该把数据取走。
' x) t) F- }! q, i0 d - *8 e, I+ O# u: K5 w7 D5 B$ \& _
- * 因此不使用DMA模式的时候,每次只能读取一个通道的数据+ L. B- b' t! f j/ w
- */
, q' f9 W6 G* F8 o* b - void ADC_Config_NotUseDMA()
4 V" V! o/ f7 E' s3 g - {7 `. _* c+ G' b
- /**6 I9 e# M8 m' o$ z; u/ r
- * Configuration of ADC1
7 X5 k9 |9 L0 S: j9 M - */
5 X+ I4 W8 Q/ g0 O X4 ^) J) `5 ~5 ] - ADC_InitTypeDef ADC_InitStructure;
% \- \% z; z' X6 g+ b/ I9 F
+ N* s3 U8 R3 J8 L, T6 ^( I- RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
/ A9 J! U3 b2 i9 G$ ]. C - ADC_DeInit(ADC1); //复位ADC1# u: N% j. o7 V! J
- 1 T' {" ?# r1 o' R2 p1 ^
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式, X$ t- X/ `5 F3 \! e3 m& f
- ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式3 J, C2 o; g* {
- ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
. {: W" \8 ]. Q3 B( A W( S' d - ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
: {- ?" {: |1 S$ U4 [" a - ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐- {1 F, M5 C* A B: h; ?
- ADC_InitStructure.ADC_NbrOfChannel = ADC_CHANNEL_NUM; //顺序进行规则转换的ADC通道的数目 k: H2 C- u8 T- N9 g
- ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
. K: w7 U( H G0 c
* g+ S- B! w7 W8 z- // ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_71Cycles5); //ADC1,ADC通道,采样时间为239.5周期; l0 u' j: ?+ f% d$ W( u6 E; N# ?
, j% N# R; l. R- o/ B# }, |7 _( m- ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1- ?9 m. U! u9 K2 \1 J* A
* X8 q9 g" z/ o" ~. i- ADC_ResetCalibration(ADC1); //使能复位校准- s$ _5 J; Y7 o' N1 h
- $ f0 n( I: b$ S0 T$ @
- while (ADC_GetResetCalibrationStatus(ADC1))
. O- r% P) y I) A/ f2 B: B9 P - ; //等待复位校准结束
& ]. G7 d& m: Y
1 ?0 O U) l0 V& H- ADC_StartCalibration(ADC1); //开启AD校准& S1 ~3 N. N+ x1 e( {- H
- 0 V2 y' b. \. G2 i) i
- while (ADC_GetCalibrationStatus(ADC1))
# m- m$ v1 ?+ @ - ; //等待校准结束
5 H8 j; F% h: c
' M- N+ S- ]! o5 u# T1 D, z- // ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能- I7 A! s0 Q8 f! E7 z+ {$ ^
- }
; ~7 \2 c# L1 }8 ]
- U4 `* S- ~% _- & `8 O$ |7 V4 M
- /**9 F6 B3 Y- \6 D6 T( o: _' a1 R
- * @brief 使用DMA进行数据传输时的ADC配置/ C& W! W! V. i3 o J9 F* m
- * 注意规则通道的数量需要根据实际项目进行调整
8 E- L: |. k, p - */: b3 y- V! y3 c
- void ADC_Config_UseDMA()
) W" z0 S) w9 L" @" k. W - {
: {+ C. O9 P# Y2 b& E/ ~$ J: \ - /**7 A8 j2 q! t7 b0 z. g
- * Configuration of ADC
2 f$ b) K/ p% m3 Q8 o1 C - */2 h- Y- c E i0 v
- ADC_InitTypeDef ADC_InitStructure;2 m% f- j1 s1 V$ C. S. I( @* W5 {
- 3 ^& x: p, D. p' f. T
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1通道时钟
. z# b n1 q4 B2 P) d6 u8 k - RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M8 J( |6 B% A T7 R
- ADC_DeInit(ADC1); //复位ADC1: e! g& E4 k+ M. P( e' E
- . t7 `5 G) U% {2 R
- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
+ `/ [6 }6 i" r - ADC_InitStructure.ADC_ScanConvMode = ENABLE; //通道扫描0 @0 Q g9 \4 A
- ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换' E9 Q; A8 G; \+ W1 z- F6 B
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
- I4 Q. g6 G3 ]8 B* u* H- C - ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;) _1 m% h; Z6 ^* H' y) b. c. J
- ADC_InitStructure.ADC_NbrOfChannel = ADC_CHANNEL_NUM;
7 z+ ^, X& y' @9 N- c& D# b - ADC_Init(ADC1, &ADC_InitStructure);; q; R5 ^5 [$ U4 z$ `. ]
$ c. @8 o" j0 p S( r- ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5); //通道1转换结果保存到ADCConvertedValue[0~10][0]+ y+ o. y2 G) ?' m- L' W
- ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5); //通道2转换结果保存到ADCConvertedValue[0~10][1]. h, J4 w% A0 Q0 H+ g# ~9 k z
- ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5); //通道3转换结果保存到ADCConvertedValue[0~10][2]2 M! J# U/ a, x8 u) F& ^/ }8 @. f. |9 m
- ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_239Cycles5); //通道4转换结果保存到ADCConvertedValue[0~10][3]) ~- Y* K6 L9 g |3 Q4 Z" G
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5, ADC_SampleTime_239Cycles5); //通道5转换结果保存到ADCConvertedValue[0~10][4]# b$ c6 L9 F" E
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6, ADC_SampleTime_239Cycles5); //通道6转换结果保存到ADCConvertedValue[0~10][5]
, j( d- I" W) G; H: A - // ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 7, ADC_SampleTime_239Cycles5); //通道7转换结果保存到ADCConvertedValue[0~10][6]
2 [! H% Q* ~0 g4 x) F8 h - // ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 8, ADC_SampleTime_239Cycles5); //通道8转换结果保存到ADCConvertedValue[0~10][7]
# s% ]1 O; T, \5 U) @ - // ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 9, ADC_SampleTime_239Cycles5); //通道9转换结果保存到ADCConvertedValue[0~10][8]# l3 Z; ]9 P/ }0 k
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 10, ADC_SampleTime_239Cycles5); //通道10转换结果保存到ADCConvertedValue[0~10][9]' O& L4 U' C% J
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 11, ADC_SampleTime_239Cycles5); //通道11转换结果保存到ADCConvertedValue[0~10][10]
; w8 m" Z5 R& ^6 C* _ - // ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 12, ADC_SampleTime_239Cycles5); //通道12转换结果保存到ADCConvertedValue[0~10][11]5 a+ B% H: U; ~* Q0 ?: \% q
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 13, ADC_SampleTime_239Cycles5); //通道13转换结果保存到ADCConvertedValue[0~10][12]
. I7 N( k: l" b* z& s0 g# j) g - // ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 14, ADC_SampleTime_239Cycles5); //通道14转换结果保存到ADCConvertedValue[0~10][13]
* G {# V3 [% \7 D9 K! q8 D - // ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 15, ADC_SampleTime_239Cycles5); //通道15转换结果保存到ADCConvertedValue[0~10][14]
* K" D) ?; A/ c% P0 V9 Q Q - // ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 16, ADC_SampleTime_239Cycles5); //通道16转换结果保存到ADCConvertedValue[0~10][15]
) G4 v$ ?. Z% r) ?! c" @$ H( O- X
( P3 B1 d9 b/ Q- ? x: V$ E- ADC_DMACmd(ADC1, ENABLE); //开启ADC的DMA支持
: L% @+ H% H/ U A - ADC_Cmd(ADC1, ENABLE);# A3 [6 v Z, l6 a& K; N
- ADC_ResetCalibration(ADC1);9 T& H' r1 g6 V3 f; i
- while (ADC_GetResetCalibrationStatus(ADC1))
$ u7 \7 o0 i2 r3 p. o9 S1 p8 C7 ` - ;
1 b& A ?+ F8 v/ c% M - ADC_StartCalibration(ADC1);# @* h1 o% `( n; f1 X1 Y
- while (ADC_GetCalibrationStatus(ADC1))/ x4 V8 ?: \+ K* E/ g8 E
- ;
L$ f6 ?6 g4 S0 ~% T- W - ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
7 E* M" ?% c6 N9 H - }( o) x& K, j6 j3 Z; G
9 Y; \ X, [. j- z, @' K" L
3 n6 {& `' m' e1 U- /**
' l( u$ A! ~5 T4 d - * @brief 使用DMA进行数据传输时的DMA配置$ L5 |0 g7 J0 A! P
- */
/ K# {; _' ]8 o H7 b4 u7 T' B - void ADC_Config_SetDMA()9 W0 I" o# x8 o0 \* w
- {" l( S, U# Z6 c
- /**
V- d+ x N( h/ {1 M0 v) T - * Configuration of DMA
* c, g+ I# b4 B _$ Z# s - */$ ~) V l) M& q3 w, E: g v1 A
- DMA_InitTypeDef DMA_InitStructure;
* N6 H. D7 n+ P) ?4 A7 S* o - RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能时钟6 s4 J/ X! v/ V& i/ P" j3 Y
- DMA_DeInit(DMA1_Channel1); //将通道一寄存器设为默认值
) Z0 t/ v( a) r% S
8 g9 W; `% c+ m/ D- DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &(ADC1->DR); //该参数用以定义DMA外设基地址& {. P* {. r: p4 f
- DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) &ADC_DMA_Value; //该参数用以定义DMA内存基地址(转换结果保存的地址)6 K3 |; m& f+ _7 a- O% B) j
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //该参数规定了外设是作为数据传输的目的地还是来源,此处是作为来源
! `2 ]4 _, i8 @% ^ - DMA_InitStructure.DMA_BufferSize = ADC_DMA_BUFFER_SIZE; //定义指定DMA通道的DMA缓存的大小,单位为数据单位。这里也就是ADC_DMA_Value的大小
+ ]: E8 K- _$ \, {% x x - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //设定外设地址寄存器递增与否,此处设为不变 Disable! T2 O' Y( w3 Z6 z o/ S: x; F6 y
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //用来设定内存地址寄存器递增与否,此处设为递增,Enable
6 D9 K" `, s7 Z, m# D - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位3 e0 n4 y% T4 \
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位
S! C8 O; r: M/ s1 s: m9 q; u - DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式* S% i9 a F/ Y, E) P1 Q! K% s
- DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道拥有高优先级 分别4个等级 低、中、高、非常高7 h3 {0 l( Z6 [
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //使能DMA通道的内存到内存传输
8 t& r/ U+ i" W- s; S2 \ N/ ] - . P: w$ e8 A# G6 S% T1 y
- DMA_Init(DMA1_Channel1, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道
2 Y1 W$ T1 Z( L. `/ m - // DMA_ITConfig(DMA1_Channel1,DMA_IT_TC, ENABLE);% v, W8 `+ D* W1 [- a* A! e
- DMA_Cmd(DMA1_Channel1, ENABLE); //启动DMA通道一
, y6 c7 s. z3 P4 f. r - }
9 G) E+ s3 Y! m$ _' b) q - % W/ L/ n6 d+ f V ]" M2 x+ h) g
- 5 z t% \) V! V) n- R6 J
- /**
; c9 B+ h8 G! z+ f/ S( M6 ^ - * @brief 不使用DMA传输时,获取ADC的采样数据
9 d7 X, @: c: f: _& O - * @param channel: ADC的采样通道
; F% K; Z, S+ l T) V6 ? - * @return ADC的单次采样结果, z+ C" Q( D& }5 A
- */" W' H( }& V8 x9 W* d
- uint16_t ADC_GetData_NotUseDMA(uint8_t channel)
1 o4 [8 O3 e. @3 p/ g - {
8 S6 d4 B* f1 q+ g9 P - //设置指定ADC的规则组通道,一个序列,采样时间
/ j4 C4 j0 X( B3 c( b - ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_71Cycles5); //ADC1,ADC通道,采样时间为239.5周期
' ]2 B7 M, t: ?% e* Z - : y3 r: j$ n C9 s
- ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能9 x4 \! ]" K/ d9 r
* m! ?9 ?3 G+ |8 B0 k: `- /**
0 l6 e( U( V# J) H - * 通过查询的方式获取了ADC的转化结果,
* w. n: i. P4 Z- x1 x B - * 也可以通过中断的方式获取ADC的转换结果
) {( c( d3 M# B5 V - */
) |8 {6 D M5 C+ S - while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))# p" T; g4 @# q9 l
- ; //等待转换结束: U" M: c3 d5 a" ?
- return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
a6 i5 Q9 x; H6 { m+ w$ e - }
; U' `% c3 `: Z1 v: a - ) F! D- F2 N5 v; S- d% g) ^; y2 ^
, _) L& n& g) h7 U' f; q, c- /**
0 d' ]7 b( i/ w2 v( f8 b0 W - * @brief 不使用DMA传输时,获取ADC采样数据的平均值
9 i6 f1 g& s. S; v4 |; c- o v; s1 i - * @param channel: ADC的采样通道0 j- w/ `' k a; K
- * @param count: 计算平均值的采样数量1 Y! N: Z" ?" R2 Q _- |: a7 X
- * @return ADC的单次采样结果的平均值/ l# M# o' b5 t
- */2 a1 p- B) T6 x+ ?: _% c, p' Z
- uint16_t ADC_GetAverageData_NotUseDMA(uint8_t channel, uint16_t count)
# |" O# l3 x. V, `/ I& i0 P9 W - {) [$ O1 U& e3 m; `$ D0 I( R
- uint32_t temp_val = 0;
6 h2 A. J% m& u3 P. q) | - + ]1 K8 M# C: l% N( k" z. _
- for (uint8_t t = 0; t < count; t++)
7 L1 m4 e, a% ]4 T$ ] g( J5 k - {
9 c2 l7 ~4 ?' [7 H - temp_val += ADC_GetData_NotUseDMA(channel);
' M' G5 b) j+ U' O- S7 Q1 F8 U8 ], v - }1 F: {% S) m: ]8 }* x# `0 h
) g6 V* j9 i4 I3 B; a8 U- ADC_Data[channel] = temp_val / count;+ F. d/ |+ v4 j: ?. Z
- * v* |! |9 C$ G/ A# }6 k) C
- return ADC_Data[channel];
$ ^9 L# m" s% H' |) R - }& K- e7 D2 K9 P) x$ p# e
: |) V2 H# B9 F# Q- 0 `& e2 K$ H; ]0 j$ e, r- T
- /**6 o# m) e, ~) ?
- * @brief 获取通过DMA传输的采样数据6 t. K2 z: I' T8 b5 U" Q5 w: X8 {& [
- * @param channel: ADC的采样通道
7 l& w5 r9 p3 R: n - * @return ADC的采样数据5 {+ m* q7 M' C# [5 m
- */
9 j* T7 S2 J% @7 C - uint16_t ADC_GetData_UseDMA(uint8_t channel)
( F# C- h6 q1 m; i8 {6 W, } - {" c- N5 E, j: J% B6 F2 ?: K
- return ADC_DMA_Value[0][channel];
1 B$ B' ^1 }6 H! j [3 V4 P3 D ? - }0 P$ H/ k, e7 i% G3 y l
6 u, K v9 l; N- . |7 j @+ L' H6 H8 |
- /**
; Z5 m' J$ ^/ p N2 Z5 `# V% m - * @brief 获取通过DMA传输的采样数据(求平均值,过采样)# ^) M( |3 e/ Y E' ]6 K
- * @param channel: ADC的采样通道
. ^1 q c0 U9 z1 K) D' x4 Z/ P6 V6 @ r - * @return ADC的采样数据
" {3 q/ _- W- x/ c+ M5 K+ V8 [' v - */7 l/ G' B' L: \! b/ ~& l- W& r' }
- uint16_t ADC_GetAverageData_UseDMA(uint8_t channel)
2 l0 u3 Y; J, a7 x& Z - {
, d. \ q% y9 s8 \/ R" _9 G - uint32_t sum = 0;
* v: {- g, {5 i- c
. \1 K3 P* `( i' z- for (uint8_t j = 0; j < ADC_DMA_CHANNEL_DEEPTH; j++)
# s) A X2 B& |/ b! c3 H - {
7 S% X3 H( R4 w+ h' E3 i. h - sum += ADC_DMA_Value[j][channel];
$ d" }' X u6 @( [/ i - }( {- {+ p4 @, W6 N9 q0 c
- % N3 `# j* h- D/ L1 r
- return sum / ADC_DMA_CHANNEL_DEEPTH; //求平均值并转换成电压值( P' `7 I R$ \9 T. K
- }
复制代码 7 ~, m5 q9 D. {/ n! d8 Q' u4 d
基于STM32F407
$ _9 z- {* x/ s, P* a9 G头文件
% }( x$ q% \1 F2 M9 y( i: n5 M4 }- #ifndef __ADC_EXT_H__8 {( K- ~% g0 }
- #define __ADC_EXT_H__ . I( W3 ^* p7 D1 j& T6 {
& I' y4 u+ ~ g/ y. C' m+ ^- #include "stm32f4xx_conf.h"# q, D9 B6 o9 V* Q1 s% L% w
- #include "stm32f4xx.h"* J4 A3 h5 k: V
- 3 _7 V7 O- i' M+ p7 e3 S) X8 K
- #define ADC_CHANNEL_NUM 4
1 L: ]7 E4 y2 q" M+ ?
+ T5 B" k( ~% w" a- #define ADC_DMA_CHANNEL_DEEPTH 10" g* Y7 Q5 l5 S& F+ \+ j3 B: w
- #define ADC_DMA_CHANNEL_NUM ADC_CHANNEL_NUM
5 L9 G7 O! W! n+ V - #define ADC_DMA_BUFFER_SIZE (ADC_DMA_CHANNEL_NUM * ADC_DMA_CHANNEL_DEEPTH)
; O- t# w/ ^# ^ - ; Z# U9 `& [. m
- extern volatile uint16_t ADC_Data[ADC_CHANNEL_NUM];( K! C2 P" W7 `4 N- ?$ @" }
- extern volatile uint16_t ADC_DMA_Value[ADC_DMA_CHANNEL_DEEPTH][ADC_DMA_CHANNEL_NUM];9 j' X5 U8 j' b& A
1 U. p8 }* A9 y6 ~0 n- void ADC_UserConfig1();
7 U S+ ~/ [ T8 j. l - void ADC_UserConfig2();* g# h1 {: @& W1 ^! P/ k% t
- * c$ O, _% L$ k/ J {( G' r+ `' @6 @0 G
- void ADC_Config_NotUseDMA();( W; G; Z$ g4 M! X/ A8 s
- void ADC_Config_UseDMA();
$ K. x* K- T' j1 g; M' J - void ADC_Config_SetDMA();3 M9 Z! x* Y9 o8 _
! E8 `4 y! i/ k* J1 x; u- uint16_t ADC_GetData_NotUseDMA(uint8_t channel);6 r2 ]) v: U6 D0 k% a( n M8 z2 K
- uint16_t ADC_GetAverageData_NotUseDMA(uint8_t channel, uint16_t count);
0 C) ^6 N2 O/ r( w+ m2 k - uint16_t ADC_GetData_UseDMA(uint8_t channel); P# w1 J" Y4 j; r' }5 G
- uint16_t ADC_GetAverageData_UseDMA(uint8_t channel);. P* S. P; v a" n: q! @7 H
- & R' [) r" c4 U: v/ N
- #endif
复制代码 + f- c: D& a0 A" f' N
源文件
$ P c- m9 s8 t" Z/ q- #include "adc_ext.h"5 Q8 J0 ~* h0 h7 B9 a# _* }9 f" z
- / U: p7 d. Q$ W- s- z" `
- /**
( ]+ W! {( e& F: M2 g1 Z - * ADC时钟
/ F7 c7 A# D, R \- u1 [ - *
6 Z8 b3 R9 O0 q) ?7 s# x ?7 Z - * ADC输入时钟 ADC_CLK 由 PCLK2 经过分频产生,最大值是 36MHz,典型值为30MHz8 L% x* v: \- D9 _
- * 分频因子由 ADC 通用控制寄存器 ADC_CCR 的 ADCPRE[1:0]设置,可设置的分频系数有 2、4、6 和 80 m8 k+ n0 }" e' Y- b4 A1 e& g+ k
- * 对于STM32F407的PCLK2=HCLK/2=84MHz,所以程序一般使用 4 分频或者 6 分频
* R3 D- M! j9 V - */: x, M% W. H2 m2 ?( k. z# U0 D2 o9 F3 Z! C
- 2 `7 j4 u2 p: _- R) b& y& Q8 U
- /**
9 x- b r7 S! X; d/ o" v- L3 C7 ` - * 采样时间( B) D% r$ v9 ` a1 y- R! E
- *
- S7 J/ g4 y5 R - * ADC 需要若干个 ADC_CLK 周期完成对输入的电压进行采样, N: n4 a; D, h) h
- * 采样的周期数可通过ADC 采样时间寄存器 ADC_SMPR1 和 ADC_SMPR2 中的 SMP[2:0]位设置。% G8 V7 ?! C. ^% p5 F X7 r
- * 每个通道可以分别用不同的时间采样。9 L2 x4 {+ t5 X$ `. }7 F' C
- * 其中采样周期最小是 3 个,即如果我们要达到最快的采样,那么应该设置采样周期为 3 个周期。: [: Z- u( U" F4 r6 y
- * 这里说的周期就是 1/ADC_CLK。 Q9 T- q: X& ^# Q
- */
1 U9 } e: Z) Z
5 V: S/ J; [* B a4 T' b- /**
/ g% K9 K" W1 x2 F4 l: o- E - * 注意本库函数只实现了ADC1的配置与采样,ADC2与ADC3的配置与使用方法与此类似# l; n t0 |; C, R% s
- */
! y( |% n0 I2 T9 H, \1 F% P; O: x- v6 |
$ ]: M4 \& ] `! s- volatile uint16_t ADC_Data[ADC_CHANNEL_NUM];7 j. [! o" F4 j3 J8 V
- volatile uint16_t ADC_DMA_Value[ADC_DMA_CHANNEL_DEEPTH][ADC_DMA_CHANNEL_NUM];
& I1 H2 p5 F; G7 Z+ l& @1 K" w
6 Q5 R1 {" `! _+ @+ z; g' t- /**) V: x) Y6 D8 V/ \
- * @brief 预置的ADC配置1,不使用DMA传输
+ Q& ?0 ]' b1 r1 V - */+ w& k- w' y# z% ?$ r0 }
- void ADC_UserConfig1()' D; r7 ?3 ?0 s* h
- {; c8 T9 {4 b: L( N, {/ e- {
- GPIO_ConfigPort('A', 0, GPIO_Mode_AN, GPIO_OType_OD, GPIO_PuPd_NOPULL, 0);
( z7 M8 k& [2 u" X" Z# Y - r2 C1 a8 _5 d$ Z$ Y" Y2 c
- ADC_Config_NotUseDMA();
/ s# p# ?& u2 M' c/ T, J* v - }; \# G( f8 `1 F* u
' S( `, n* F& O
2 ?$ `' |" S- B8 {$ ~- /**
0 }1 t: {' ^3 R8 { Y" V4 D - * @brief 预置的ADC配置2,使用DMA传输
+ i( P! ^$ d) S% K# i% V$ n5 S3 I: k - */
# J. g: k- X- O - void ADC_UserConfig2()$ x' m# I' V( `( a
- {* S/ y) p7 ]8 j* q8 ~+ i) m
- GPIO_ConfigPort('A', 0, GPIO_Mode_AN, GPIO_OType_OD, GPIO_PuPd_NOPULL, 0);
4 s! j1 D1 c" {& w, X - GPIO_ConfigPort('A', 1, GPIO_Mode_AN, GPIO_OType_OD, GPIO_PuPd_NOPULL, 0);! b. _3 F9 B+ x/ N" K
- GPIO_ConfigPort('A', 2, GPIO_Mode_AN, GPIO_OType_OD, GPIO_PuPd_NOPULL, 0); j0 E6 p; Y' `6 [/ F
- GPIO_ConfigPort('A', 3, GPIO_Mode_AN, GPIO_OType_OD, GPIO_PuPd_NOPULL, 0);
4 f; Q5 q* N0 f+ h+ B; z9 U - 9 K! J2 @/ Z& E: \- }
- ADC_Config_SetDMA();" s/ Q, O/ O; q$ I$ [+ ^
- ADC_Config_UseDMA();: |8 y% h* `, |
- }! u. b) }3 S7 c- s
. |4 R3 T. `5 } L4 w5 R$ S
1 m8 U% [* Q: p, E, {- /**# n/ y5 W7 D4 b8 J4 x5 t
- * @brief 不使用DMA进行数据传输时的ADC配置% I" Q: h3 |/ Q \
- * 由于ADC 规则组数据寄存器 ADC_DR 只有一个,是一个 32 位的寄存器,
4 j: p! [- `; M2 i6 x+ F& A1 V - * 只有低 16 位有效并且只是用于独立模式存放转换完成数据。& ?) ?2 k' D2 P8 @
- * 如果使用多通道转换,那转换的数据就全部都挤在了 DR 里面,前一个时间点转换的通道数据,
5 _; c. X3 J2 O/ q - * 就会被下一个时间点的另外一个通道转换的数据覆盖掉,所以当通道转换完成后就应该把数据取走。1 q/ ]' Y- a5 S8 E% Q- ~8 }
- *2 J+ }, s$ Z3 e/ ?
- * 因此不使用DMA模式的时候,每次只能读取一个通道的数据
/ d& v9 ]+ ~/ [3 e - */
/ t; [1 \ o2 S: Y2 u - void ADC_Config_NotUseDMA()/ b* ]; a1 B- {) V% K
- {
% o: ^( ~0 P" B - /**
9 H" Q( ?, ^. B7 [8 v! q6 F( S - * Configuration of ADC# w2 } [& d- w8 ^/ j1 o0 {& h
- */8 ^) O- F% ]5 ~% q! S) f, d1 \4 O2 z
# M& e& g1 P) A4 [. x( w9 i( ?- ADC_CommonInitTypeDef ADC_CommonInitStructure;
7 d' v$ A8 b* u
& }9 p: r* D; P1 {$ E) u- RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟: }3 Y: N3 ]- i
- ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; //独立ADC模式
& F5 N( {: n" I, V! W+ s - ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; //ADC时钟为fpclock/分频8 x5 e' l5 S" e" D7 O1 G2 D- e
- ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //禁止DMA直接访问模式
1 ^- P$ z7 l* W/ ], _6 a - ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; //采样时间间隔, g" k! `/ J' t) U9 x
- - ^( B R6 u1 `& V
- ADC_CommonInit(&ADC_CommonInitStructure);
2 H2 O8 J% I& }; X! W* w - . Z: O7 d+ l0 r9 P: [+ W
- ADC_InitTypeDef ADC_InitStructure;+ |# s& V) z9 v" ?/ [
" m! Q/ l0 O E, A+ G/ p- ADC_StructInit(&ADC_InitStructure);, @% R* P3 U$ G; b/ q
- ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //设置ADC的分辨率12位8 i- r" N. M; F: R
- ADC_InitStructure.ADC_ScanConvMode = ENABLE; //通道扫描模式
+ Q* f3 Z4 k1 f+ k' r - ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换模式
( D8 K5 E8 e0 r) ]3 }; U( q/ C( S* E
' X- D( s o6 @7 E( z, O* h1 z. j- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None; //禁止外部边沿触发6 U7 O4 }6 Z* w% m0 h/ B! M
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //外部触发通道
: S+ q' z; X9 V1 R - ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //设置数据右对齐2 ?1 \9 R9 @3 v+ o
- ADC_InitStructure.ADC_NbrOfConversion = 1; //设置转换通道,固定为1
& k/ c) n) B x' w - ADC_Init(ADC1, &ADC_InitStructure);
7 e1 x" X: N- l+ f7 O
7 I) h! Z! {+ r) L6 v! V- ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_56Cycles); //配置 ADC 通道转换顺序和采样周期
/ ] z* a( g) m9 Y' z% Y
! d1 t) I8 @. F8 N9 ^- // ADC_ITConfig(RHEOSTAT_ADC, ADC_IT_EOC, ENABLE); //ADC 转换结束产生中断,可以在中断服务程序中读取转换值
& M7 I1 N+ U' A" }! Z# W
- f0 x7 Y; W/ r: v5 x3 C- ADC_Cmd(ADC1, ENABLE); //使能ADC
7 p. C5 G4 l" A3 j f2 e# u - }
! L& F$ L+ H- k - ; q, i( y4 W2 T
# [2 C5 ^$ z" g5 q% g- /**
6 o- c, O2 x8 F2 u$ ^' S# O - * @brief 使用DMA进行数据传输时的ADC配置
2 r2 P5 }: v! s - * 注意规则通道的数量需要根据实际项目进行调整
0 {3 X* ?. U6 F - */
& \# o4 E2 {" `/ m$ I - void ADC_Config_UseDMA()) S8 S6 J! \/ ]+ n8 v& c& C
- {
2 P, z p* L- ] - /**
7 K6 `4 U. f: V) e8 _ - * Configuration of ADC# v& B$ M7 v% W5 c7 g
- */
8 E) v$ x# E) V f - ADC_CommonInitTypeDef ADC_CommonInitStructure;/ ^. G* i/ [. [* ~/ b" {
- ( [$ [0 w r0 C2 S1 }7 h. s: y6 c
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟5 P3 E1 Z. L% O" `, ^
- ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; //独立ADC模式 H0 F' B6 _3 {) f' [7 k. _" `, O
- ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; //ADC时钟为Fpclock/分频8 c1 b: I' O1 q- }
- ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //禁止DMA直接访问模式
" y; S k0 }( F4 G( N - ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; //采样时间间隔# h+ F6 S; X. r9 ]
- ) K# f6 W1 Y2 ~# g
- ADC_CommonInit(&ADC_CommonInitStructure);) [' o6 {9 p4 \: \. ?+ O
5 Y/ | u' y% o, Z: D3 _; j/ b- ADC_InitTypeDef ADC_InitStructure;0 M1 [. _1 q" @
- ; Q5 s* H: b9 |' T( ^- T0 i* Z5 Z) v2 Q
- ADC_StructInit(&ADC_InitStructure);2 t* \" M3 o# T/ W2 L7 h0 | `
- ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //设置ADC的分辨率12位
' I% s. v2 V; _ - ADC_InitStructure.ADC_ScanConvMode = ENABLE; //通道扫描模式
, V! ?) }9 e( {! C - ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换模式# D* l2 ?5 H+ n
- $ `; x$ P o: k2 o! s) M% b
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None; //禁止外部边沿触发
$ x/ m$ n. A) u: P6 X0 P R - ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //外部触发通道; C* {( j5 T9 P( W8 X+ C2 x
- ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //设置数据右对齐
( z5 R/ U- K ]$ s. w' k2 L6 W - ADC_InitStructure.ADC_NbrOfConversion = ADC_CHANNEL_NUM; //设置转换通道, H R+ C6 ]! C' d8 O) v
- ADC_Init(ADC1, &ADC_InitStructure);* E7 s7 y+ j& X0 w* c% H* U' A
* s- A6 A# n7 H' m: `3 @- ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_3Cycles); //通道1转换结果保存到ADCConvertedValue[0~10][0]5 s7 G# W# G6 Q" i6 G9 v
- ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_3Cycles); //通道2转换结果保存到ADCConvertedValue[0~10][1]
" X; I# V, C# Z. c- v# n - ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_3Cycles); //通道3转换结果保存到ADCConvertedValue[0~10][2]7 H3 {- s4 p5 ~) H1 B% F3 `4 T
- ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_3Cycles); //通道4转换结果保存到ADCConvertedValue[0~10][3]! n( M" I8 D% X; D9 n
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5, ADC_SampleTime_3Cycles); //通道5转换结果保存到ADCConvertedValue[0~10][4]
0 B! l3 F3 y# _! p - // ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6, ADC_SampleTime_3Cycles); //通道6转换结果保存到ADCConvertedValue[0~10][5]
# V6 K8 N% X/ d1 T - // ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 7, ADC_SampleTime_3Cycles); //通道7转换结果保存到ADCConvertedValue[0~10][6]
) @: c3 R$ k* e4 \$ M( G% O - // ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 8, ADC_SampleTime_3Cycles); //通道8转换结果保存到ADCConvertedValue[0~10][7]+ W! C; K _! L& Q$ o: C2 S2 `! u
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 9, ADC_SampleTime_3Cycles); //通道9转换结果保存到ADCConvertedValue[0~10][8]
9 Y+ [" G* r6 o+ @1 J+ S `" Y - // ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 10, ADC_SampleTime_3Cycles); //通道10转换结果保存到ADCConvertedValue[0~10][9]
# ^8 M# f% ^/ {, }2 T - // ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 11, ADC_SampleTime_3Cycles); //通道11转换结果保存到ADCConvertedValue[0~10][10]& H7 j" E) D1 [$ \( R
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 12, ADC_SampleTime_3Cycles); //通道12转换结果保存到ADCConvertedValue[0~10][11]
# l* Y% y v7 O: M8 [ - // ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 13, ADC_SampleTime_3Cycles); //通道13转换结果保存到ADCConvertedValue[0~10][12]
$ A: y/ I1 f* q2 @# f- `1 N! F4 x$ h - // ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 14, ADC_SampleTime_3Cycles); //通道14转换结果保存到ADCConvertedValue[0~10][13]% i- W9 M, {7 r$ H$ {2 {0 C2 @6 p
- // ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 15, ADC_SampleTime_3Cycles); //通道15转换结果保存到ADCConvertedValue[0~10][14]
( c2 X: B9 O* k% _% q& H6 O% _' N. z - // ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 16, ADC_SampleTime_3Cycles); //通道16转换结果保存到ADCConvertedValue[0~10][15]
: P, {1 P# l; Z2 G) f
+ b& a% x* g) A7 ~8 m# V- ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); // 使能 DMA 请求 after last transfer (Single-ADC mode); V1 {7 S$ G1 h5 t
- ADC_DMACmd(ADC1, ENABLE); //开启ADC的DMA支持
. i s. N& b" k/ W% p, d - ADC_Cmd(ADC1, ENABLE); //使能ADC/ @3 g D+ Y- \4 T
- ADC_SoftwareStartConv(ADC1); //开始ADC转换,软件触发
% ^. P1 R& u" r - }
4 Y3 J0 A+ G: Q' x- ]9 K - 4 E7 O1 m5 b5 a I5 n% k
- ; T! K0 N7 {( X2 v
- /**
' t4 k$ ], r% X$ D* G1 @ - * @brief 使用DMA进行数据传输时的DMA配置# ~. v$ l. A. p) n5 k! |
- */
$ [: d# e" E5 h2 P( W! S& h% o - void ADC_Config_SetDMA()
# l* n" t! Y2 [ - {/ J0 w! }) U$ D6 L
- /** ]8 Y1 }- J1 q( M2 L w+ q9 [' Z3 L
- * Configuration of DMA- {. k( i1 C! {7 @ { Z
- */
% p2 r. n2 Y! [$ F/ p- N1 G - DMA_InitTypeDef DMA_InitStructure;0 H; V9 I+ d; V5 y! }1 T. b
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); //使能时钟
' }" x+ z* c6 @: ~( C& ~/ i) N - DMA_DeInit(DMA2_Stream0); //将通道一寄存器设为默认值
6 S7 C' a" z5 X1 x0 z/ v - 9 y" R0 k4 x6 Y! W/ A
- DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &(ADC1->DR); //该参数用以定义DMA外设基地址
' H, `6 A- J" j: v - DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) &ADC_DMA_Value; //该参数用以定义DMA内存基地址(转换结果保存的地址)6 G' P' E+ t: |; ]/ q
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //该参数规定了外设是作为数据传输的目的地还是来源,此处是作为来源
5 U, l% J$ ^+ U - DMA_InitStructure.DMA_BufferSize = ADC_DMA_BUFFER_SIZE; //定义指定DMA通道的DMA缓存的大小,单位为数据单位。这里也就是ADC_DMA_Value的大小
) y1 ?/ P1 v* t4 i - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //设定外设地址寄存器递增与否,此处设为不变 Disable x4 ?1 l1 J, x# Y% S! ^( \
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //用来设定内存地址寄存器递增与否,此处设为递增,Enable( B( f; F! f' |% z
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位
) T8 ` C. J& j8 u. Q+ N$ _* f/ \% T - DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位& P" H0 U5 N+ E# b" Z3 B
- DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式. H4 m' @0 ]! G+ r2 V/ F/ c
- DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道拥有高优先级 分别4个等级 低、中、高、非常高
6 X/ B5 ]9 c9 f" H4 H) O - DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; //禁用DMA的FIFO模式,使用直连模式
; p* Q" y" h. g; u6 a8 i6 p* | - DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
# j3 X: P# G3 J- V/ b g ` J - DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;! A* Z- l$ q0 u" I. T/ N
- DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
- O5 p1 m+ C3 G; L- U7 P6 E2 @5 o4 _ - DMA_InitStructure.DMA_Channel = DMA_Channel_0; //选择DMA通道
; W, f* p) d! z4 z: B; C - & v; ?3 s3 `& j+ G3 {9 n8 A# Y- r
- DMA_Init(DMA2_Stream0, &DMA_InitStructure); //初始化DMA流& U3 p; {7 E b- N. h4 ^* p; { @
- DMA_Cmd(DMA2_Stream0, ENABLE); //使能DMA流
! R6 n, u# k* e* g9 Y; F1 Z! @+ q - }6 k( T1 l( n* b% Y$ f
; G2 p4 g. Y( t
' S5 H C; y1 Z$ \, z- /**
, ?1 D( z) H8 O/ w, G* q - * @brief 不使用DMA传输时,获取ADC的采样数据# m7 M7 h0 A7 N5 w
- * @param channel: ADC的采样通道+ @; j8 e( ^- w& C; N1 B7 y W
- * @return ADC的单次采样结果% |+ a5 Z0 o) J: l% g9 c
- */1 p F: o. I! X0 L6 C
- uint16_t ADC_GetData_NotUseDMA(uint8_t channel)
. N" O' C+ G% b' R* I - {3 S4 N {2 K- q& a! k
- ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_56Cycles); //配置 ADC 通道转换顺序和采样周期" ]6 ?* s+ E' f H7 t2 a
- ADC_SoftwareStartConv(ADC1); //使能指定的ADC1的软件转换启动功能$ T1 A. d& J' a( i- e# T9 Q
* ^# H2 M8 H9 V3 u% k- /**2 p. |2 g" P. w& J4 B% n
- * 通过查询的方式获取了ADC的转化结果,0 s3 ~' \5 G o: G/ b/ O1 P! P7 r
- * 也可以通过中断的方式获取ADC的转换结果
$ @$ w! l; F1 H - */
# _2 r w3 \+ l+ N1 c3 d - while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); //等待转换结束* T! G0 a3 R$ x8 O
: m, c( B$ Y$ Y5 {- return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果. V; v( n6 `3 A- d0 s
- }9 T7 K& q$ @$ y; W5 B
- ) l9 h9 E3 m- @1 j
- ( D: U6 v X4 l* \3 \+ m5 t1 }2 g
- /**
" _: N+ c; }' Y3 [ - * @brief 不使用DMA传输时,获取ADC采样数据的平均值
$ _6 r: d2 K B& b( A3 W - * @param channel: ADC的采样通道
4 Q) V5 l. P, O$ K% L, _) D# T - * @param count: 计算平均值的采样数量
* Z+ L& Y9 u! j3 ? i. u) R - * @return ADC的单次采样结果的平均值
7 m' D: X+ ?, E. i0 R/ y5 c/ c0 R* G% d - */
1 H b# n% o4 T - uint16_t ADC_GetAverageData_NotUseDMA(uint8_t channel, uint16_t count)
. a$ }( d% m; T, l; G- K( c - {3 _# F( W8 U, ^) B0 K% `0 ]. T
- uint32_t temp_val = 0;
9 ~6 F6 X( x/ F7 [( C
' q$ @* e' W2 k1 b; y0 b- for (uint16_t t = 0; t < count; t++)
9 Z# Z# F/ I8 a! T( b - {
- Y; q( h( j7 F3 ~& [ - temp_val += ADC_GetData_NotUseDMA(channel);
( x8 p" N* S: n - }: q" t/ ~: S, ?, R: c& ]
9 N/ U2 `7 {5 Y# z- ADC_Data[channel] = temp_val / count;
( ?! G# }. K- e& f
8 C2 o, R$ r$ H; d. q- return ADC_Data[channel];
: B; d8 Y2 r A. Q2 d1 P0 g+ [ - }+ U" M+ q6 {) B5 D( \6 s
- ' d# O+ T+ K! Q4 L g" c9 k
- /**: f, i8 C; n6 g7 u$ n4 I9 l
- * @brief 获取通过DMA传输的采样数据
$ s' r2 o- R2 F- d - * @param channel: ADC的采样通道
5 r( c- L$ c! a3 @ - * @return ADC的采样数据: B' ~" `9 ^: {% k6 X4 Z# c
- */8 Z- l, r. M$ v8 b/ p9 V
- uint16_t ADC_GetData_UseDMA(uint8_t channel)6 X3 u; f& H! o1 J* P% u# f2 F
- {$ c5 X. h+ u" H k* ]
- return ADC_DMA_Value[0][channel];( I2 X( }7 [5 ]7 H
- }& n* J& ^6 N. t j/ X) g0 s9 \3 \
- 2 Q7 ?0 v. [6 S5 K
- 3 _0 N0 L; K/ ^1 z3 k6 Q- R1 w
- /**& A% V. B1 b- h, s1 `& V# I& A6 f' @
- * @brief 获取通过DMA传输的采样数据(求平均值,过采样)9 Y: g, L; S" S# L0 J/ {
- * @param channel: ADC的采样通道
) f& T0 \- K: k6 u" q - * @return ADC的采样数据9 ^3 I6 ]) Q ?2 k2 Y$ O
- */7 ?9 ?' U" M0 b' M2 @4 z5 I
- uint16_t ADC_GetAverageData_UseDMA(uint8_t channel)
) ]& ?- q- ]0 m; Q3 z X3 A' @ - {) }% N8 Q% T" u
- uint32_t sum = 0;
# i6 v, B8 {* p/ U# |
% |1 \' M+ m& \. @/ ]- for (uint16_t j = 0; j < ADC_DMA_CHANNEL_DEEPTH; j++)
/ B) a6 b0 |8 {3 N. l2 q6 A - {2 m5 _5 x/ h6 d1 N# Q& F w
- sum += ADC_DMA_Value[j][channel];8 j- J0 U, B* Y# c5 L
- }
6 e, t) s9 f3 o k5 h - : k, x5 N% Z4 \9 B
- return sum / ADC_DMA_CHANNEL_DEEPTH; //求平均值并转换成电压值
% E- ~5 @' d0 W4 b5 _ - }
复制代码
2 {1 [7 e1 W E5 B使用指南9 J; [6 r; n& j5 _6 s+ a6 F. ^9 x
由于使用了相同的功能逻辑与函数名(即头文件相同),STM32F103与STM32F407的ADC扩展函数库在使用方法上是相同的。由于在ADC扩展库中提供的模板配置函数中已经完成了GPIO外设与ADC外设的配置,因此可以直接调用一步完成ADC使用前的初始化工作。
1 S& U9 O; |1 A3 P! E
1 ^2 e) E b$ D/ f+ w q单次单通道采样(不使用DMA)( N6 f8 O L( J# g# t
配置ADC
3 C w$ F+ Y- _# d, \/ G- void Periphl_ConfigAll()
* h- u' B. I, `- \+ k- E; z* X - {
. Y6 ?% V) Y- _. M - /*省略无关代码*/: I3 w* l |! g0 X; o
- ADC_UserConfig1();
% T* b$ z+ Y5 _ - /*省略无关代码*/
+ W* {. P% q' o; C( J' c - }
复制代码 . T$ s( u5 ~/ v7 | _' p
启动采样并获取数据
/ E3 c7 l- R0 h: \5 k* O采集的数据保存在全局数组ADC_Data[ADC_CHANNEL_NUM]中,同时也可以调用相应的函数返回对应的结果。
/ y, R4 J, g9 T调用uint16_t ADC_GetData_NotUseDMA(uint8_t channel)完成一次数据采集。. B0 H2 v0 T" e& ~6 @# E* Z/ m
调用uint16_t ADC_GetAverageData_NotUseDMA(uint8_t channel, uint16_t count)连续采集count个数据后,返回数据采集的平均值。
% L' B* X% r h3 T4 N8 w9 H o; p% T0 {) e U; H4 o* @2 R$ M
5 a7 l) q* |- w* p( O1 K5 a3 S
连续多通道采样(使用DMA)$ V; s9 b* q! _1 f" ~: g
配置ADC5 ~6 V: c: ~* Q
- void Periphl_ConfigAll()" E1 F3 v/ B2 ?
- {
/ n1 K1 F7 N* \/ Q* g7 F" O - /*省略无关代码*/
( V0 O. K% K- ]& {: Q& M - ADC_UserConfig2();& W9 {& U8 |1 a P# g9 ]! C" @3 G; @
- /*省略无关代码*/
9 q c7 B. v8 a1 [ - }
复制代码 3 J2 g, @/ T) V+ L
获取数据2 ?9 ` q- N! w. @
在配置完ADC的DMA传输后就已经自动打开了ADC采样,采样的数据保存在全局二维数组ADC_DMA_Value[ADC_DMA_CHANNEL_DEEPTH][ADC_DMA_CHANNEL_NUM]中,同时也可以调用相应的函数返回对应的结果。2 q& v' d( v' G- g; \
调用uint16_t ADC_GetData_UseDMA(uint8_t channel)返回一次数据采集结果。
8 a: |0 n& Z$ A+ H3 ]* n 调用uint16_t ADC_GetAverageData_UseDMA(uint8_t channel)返回数组中保存的最近的ADC_DMA_CHANNEL_DEEPTH个数据的平均值。
, |( K7 V0 }- x* w4 E" l
6 i6 f( G, f) U; ~* R) _2 Q一般情况下,由于DMA的采样速度很快,调用uint16_t ADC_GetAverageData_UseDMA(uint8_t channel)返回一个平均值作为采样的结果是比较推荐的。这也是利用了过采样技术提高了数据的稳定性与分辨率。; a: S; a& ~; h* Z
* z+ J, d( J4 b7 ~
在FreeRTOS任务中
$ j# Y4 d! }2 x: `- Y" b可以创建一个任务完成ADC初始化与数据轮询的工作,但需要注意的是片上ADC的快速配置函数(ADC_UserConfig2())只能调用一次,否则可能会出错。也就是说要么在程序启动的地方配置ADC,要么在任务开始的地方配置ADC,但不能两处同时配置ADC。
7 q$ C* n# k8 c. q3 j# S! Z# D( x; k0 S& }
- void McuAdcDaq_task(void *pvParameters)9 A& O& o- l7 |2 |+ W
- {! j) Z( {! r& ?1 C, r6 G* M/ ~
- ADC_UserConfig2();- y" J- o7 r" M7 `/ b8 }
- ! B; D' h" R3 o4 |! A
- TickType_t ticks = xTaskGetTickCount();; n4 N2 x% @( @3 l. ^
- while (1)
; ^3 P/ j8 ^- E1 T# y8 v - {
! g' g, C' ?9 I4 O: N, @8 l( U. C - User_RefreshSensor();: f: A5 H. M z0 P2 m% p
- User_MB_RefreshInputRegister();
! Q+ ^) z _8 W2 p1 }9 M - _" M% d1 K; c9 |
- //100ms 一个处理周期
/ }% b; {0 U4 ^ - vTaskDelayUntil( &ticks, 100);
& I9 M! ]$ G7 N7 z5 ` W3 V4 j2 M - }
, d. y- R+ U8 |8 u6 [7 D1 M* A: Y - }
复制代码 6 o; D- f* m5 C- i) c
2 L# o& D/ q: R, F( |7 P; w
" B. G9 R q$ @2 ?! B7 \7 b- o% a
|