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