使用DMA功能操作外设时,可以极大的简化代码,提高程序的执行效率。特别是在需要频繁操作的外设上。比如现在要采集单片机16个ADC通道的电压值,就可以使用DMA功能,直接将ADC通道转换好的值,传输到数组中。需要操纵ADC的值时,直接去数组中拿数据就行。不需要再去判断ADC数据转换是否结束。下面直接通过代码来实现。
+ B- q, c( {- |+ Q1 Y- u9 x" Y! X7 ~% x3 c6 }
首先初始化ADC,这里将ADC的16个采样通道全部开启。
8 ^" n& q* H$ |' ^/ [* n
! U" Z; ^6 S' X) Y( b8 W- 8 q T, `1 E t7 o A1 b, F
- void ADC1_Init ( void )
" r5 r4 Z0 ]6 P H( g% @ - {4 z2 ^' i+ E! j- l* z
- GPIO_InitTypeDef GPIO_InitStructure;* `7 ]& ?. S0 y% c% ^' z9 v
- ADC_InitTypeDef ADC_InitStructure; : r; X) x2 |2 C R3 k0 Q
- ; y# Q2 C( W# h1 @( k
- RCC_APB2PeriphClockCmd ( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO, ENABLE );& r: k I" k9 C6 D: y9 Y& z
- RCC_ADCCLKConfig ( RCC_PCLK2_Div6 ); // 72M / 6 = 12M ADC最大不能超过14M
; T' w" w4 [1 Z$ h" g1 v
3 H& M+ X# m0 f! n/ g* s4 l: |- // ADC1 --- ADC8
) N9 t/ H& |% D7 E1 A, D! ~ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;' }- O- g$ u( C: Q6 @9 v3 Y% V3 S
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
B) a! Y3 Q0 V$ P - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;2 J9 t3 s9 Y9 r r& i1 B2 G) V
- GPIO_Init ( GPIOA, &GPIO_InitStructure );3 L7 |4 p6 [% U* l D5 R5 b) @
- // ADC9 ADC10- j V& O; ?! ^& t6 w5 K
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
8 }/ I6 m6 h/ u - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
" ]5 \( s2 K0 d$ g3 u( v - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
3 W. F$ L) f8 _% y6 B - GPIO_Init ( GPIOB, &GPIO_InitStructure );
/ n2 @1 ?& i8 k" M6 |5 w - // ADC11 --- ADC 16( ^" O; H1 N2 T9 N- \
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
0 O9 V1 |& g+ q1 r0 q) W - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
! \* m" {. f) C6 L - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
. U/ q& _/ C6 ]) b$ a L - GPIO_Init ( GPIOC, &GPIO_InitStructure );9 G5 r+ P4 C3 x, p
- / L" Q. A- [. a& R3 ~1 t! |! ~
- ADC_DeInit ( ADC1 );
( c) M; p- I/ E' i; I
8 `/ Z" [5 y) L* T: A- ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立模式
% p* s5 {. S \4 o - ADC_InitStructure.ADC_ScanConvMode = ENABLE; //扫描模式
" D/ A+ R: @' r/ {7 c: ^ - ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换0 R( t* Z. [! y+ b3 {7 H3 C
- ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //不使用外部触发
7 W s9 }* O- f: r0 H4 ~2 [1 e9 S# m - ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据右对齐. }: d( y/ L5 t2 [8 e
- ADC_InitStructure.ADC_NbrOfChannel = 16; //ADC通道数
; ~4 d+ l7 ^/ M3 X9 y - ADC_Init ( ADC1, &ADC_InitStructure );) x% X+ |! R2 I% |
- 3 y7 n, v3 r$ ?& t7 E" h1 ^
- //规则通道配置7 ~/ Z- d* ]: w6 q) D i8 ?& i
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5 );
- N. p% a( V- R9 D) h - ADC_RegularChannelConfig ( ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5 );
* f% I9 q+ A; k: S" p9 h5 ^ - ADC_RegularChannelConfig ( ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5 );2 c) t: Y* A( U
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_3, 4, ADC_SampleTime_239Cycles5 );9 s5 N+ S* D! ^, v
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_4, 5, ADC_SampleTime_239Cycles5 );
1 |8 ` _& Q5 `1 g" O _1 q$ H - ADC_RegularChannelConfig ( ADC1, ADC_Channel_5, 6, ADC_SampleTime_239Cycles5 );0 s7 P+ O0 A' H0 ?" @
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_6, 7, ADC_SampleTime_239Cycles5 );- z* h3 s5 F g& h8 e3 ~
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_7, 8, ADC_SampleTime_239Cycles5 );
# T* S- A7 g. b9 P, X0 y# i - ADC_RegularChannelConfig ( ADC1, ADC_Channel_8, 9, ADC_SampleTime_239Cycles5 );0 C! s7 e. ]% L' v4 f& z
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_9, 10, ADC_SampleTime_239Cycles5 );
9 q7 C e- W7 n! M/ W" \3 q- l1 { - ADC_RegularChannelConfig ( ADC1, ADC_Channel_10, 11, ADC_SampleTime_239Cycles5 );
% W. J; U0 u: S* e - ADC_RegularChannelConfig ( ADC1, ADC_Channel_11, 12, ADC_SampleTime_239Cycles5 ); D' Z6 `+ l6 F
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_12, 13, ADC_SampleTime_239Cycles5 );
. t- C- s) [0 j$ S% s! L1 L - ADC_RegularChannelConfig ( ADC1, ADC_Channel_13, 14, ADC_SampleTime_239Cycles5 ); Z0 a7 _+ ]* Q- J$ ?* K; w
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_14, 15, ADC_SampleTime_239Cycles5 );% A: O- t5 u, |
- ADC_RegularChannelConfig ( ADC1, ADC_Channel_15, 16, ADC_SampleTime_239Cycles5 );& W1 y( b3 ]: M' `0 O
- 0 v* W7 l( [$ v5 @
- ADC_DMACmd ( ADC1, ENABLE ); //使能ADC DMA传输8 _; y5 k& E. r% A7 b
- 9 G( n7 b/ B# w4 x1 Q9 x7 G, p
- ADC_Cmd ( ADC1, ENABLE ); //使能ADC1
4 {! X3 \6 d/ [) d. V, ` - ( p6 l0 ^) v; J2 C- v( j; \( N
- ADC_ResetCalibration ( ADC1 ); //使能复位校准
6 s, v; M8 K( o - while ( ADC_GetResetCalibrationStatus ( ADC1 ) ); //等待复位校准结束1 s8 B& x$ n3 @
- . Q& E1 b- ~. L' q" Y! L# N
- ADC_StartCalibration ( ADC1 ); //开启AD校准0 b( D# l ^& P3 p* M6 ]. k
- while ( ADC_GetCalibrationStatus ( ADC1 ) ); //等待校准结束
9 N. V9 X @, g8 m8 X4 J& R - // ADC_SoftwareStartConvCmd ( ADC1, ENABLE ); //启动转换8 s+ |* Z9 Q' u: ~1 p r9 q
- }
复制代码
$ s- s% v1 g; H/ P6 C9 t% |ADC设置为扫描模式,连续转换,16个通道依次扫描采样,一轮采样结束后,接着开启下一轮采样。8 p. Q& y, Y# B3 {) M2 b b0 s
6 X% c; \; ^, E
下面初始化DMA功能。7 a7 o4 u& `# F8 N
/ k; O! _' K, n) M' _7 J- void MYDMA_Config ( DMA_Channel_TypeDef *DMA_CHx, u32 cpar, u32 cmar, u16 cndtr )
! S+ e: | l$ t2 x - {
" Y/ y9 s- u/ } r9 R. a. B# J4 Q2 E - DMA_InitTypeDef DMA_InitStructure;
: S! v3 P8 @% I: E+ K - RCC_AHBPeriphClockCmd ( RCC_AHBPeriph_DMA1, ENABLE );
! i; |* z3 X7 l, G: X - $ _$ A/ U/ u0 c
- DMA_DeInit ( DMA_CHx );
n7 R( p* D8 q7 P& P -
1 X, C5 ^/ {; b- b3 \8 a - DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //外设基地址
o5 E3 A* M: s1 ^: y' V$ [ - DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //内存基地址# d1 [1 e- E: m$ K# Z9 V$ Q
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输从外设到内存4 k5 x" t! n% ~* |) C. s
- DMA_InitStructure.DMA_BufferSize = cndtr; //DMA缓存; |# q' p+ J; C/ l
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不变! N& e5 E9 T6 i- p% h9 h
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增
' Y2 m4 w4 V! r - DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据16位
9 L' x# f7 f8 B - DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //内存地址1位# e, Z) o6 d8 }( T' U" ~) D
- DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //循环工作模式, V; J8 g! E" i. _
- DMA_InitStructure.DMA_Priority = DMA_Priority_High; //高优先级9 l3 }+ K" d; @. v1 c
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //没有设置内存到内存传输( i+ B d" \8 _( d
- 1 \7 r% n- q1 q9 j1 E: L. d" y
- DMA_Init ( DMA_CHx, &DMA_InitStructure );
4 J% p# l, F2 `/ v" @# I - }
复制代码 * I9 P; A% v+ U& j; O/ q( X
这里DMA的数据方向要设置为从外设到内存,也就是将外设ADC采样的数据结果保存到内部存储空间中。6 q) E/ t+ i1 }0 a$ M. p, ?
& j O4 I; K% K9 \. F初始化完成之后,在主函数中直接启动DMA转换功能。% ]' Y0 Q& m( l1 X% z8 |* m
- @# U6 E* L* p( M6 e& m( q# }
- #define ADC1_DR_Address ((uint32_t)0x4001244C)
# s ^6 U8 A) n - u16 AD_Value[6];
: \. G3 D9 W6 H2 x: j% ~/ ^' J ~ - float volta[16];+ H& A# E# ]9 r4 p( k: q
- u8 i, j;! E6 J+ q+ d4 u
- uint16_t ADC1ConvertedValue[10][16];
9 S) {) w4 |( W$ k. \8 \" F - uint32_t ADC1ConvertedVoltage[16];
& c6 B6 g' _, G' S& k% S+ r - int main ( void )& |" y; ?5 r1 G% o$ O" h4 B. Y3 s, I
- {- [2 u+ u# W7 a/ a3 H
- delay_init(); //延时函数初始化
! V$ G/ b0 a, Y8 g$ S0 W: W& E1 R - NVIC_PriorityGroupConfig ( NVIC_PriorityGroup_2 );9 c" [* d: ^0 o+ g9 v
- uart_init ( 115200 );
* ?! @/ B. v7 i# M* b0 u/ { - LED_Init(); //初始化与LED连接的硬件接口
X5 {/ k4 X6 j- o - MYDMA_Config ( DMA1_Channel1, ( u32 ) &ADC1->DR, ( u32 ) &ADC1ConvertedValue, 160 );6 t n5 D5 p# V3 [
- ADC1_Init();
1 `9 v# |; H) b& ? - DMA_Cmd ( DMA1_Channel1, ENABLE ); //启动DMA通道
. E0 F" X7 C; @& }/ v - ADC_SoftwareStartConvCmd ( ADC1, ENABLE ); //启动ADC1软件转换. J, w+ A) b6 Y5 F% I
- while ( 1 )" A! B: t% P+ F4 s$ m* w
- {
1 S( b, k9 S9 L: ?) D4 Z0 ~2 `0 v8 Z - ADC1ConvertedVoltage[0] = 0;
& M& \6 \0 a) }( e - for ( i = 0; i < 16; i++ )
Q& [. m5 o4 ~4 B0 s# w; Z - {
9 D z0 }6 `* D' W, m) _0 b - ADC1ConvertedVoltage= 0;
3 u% x% ^' k) K0 G" Q- m - for ( j = 0; j < 10; j++ ) //采样10次取平均值! a! i$ U/ N1 ^, U, \2 k
- {
0 q, D2 U+ j! C+ A& x1 p7 D - ADC1ConvertedVoltage += ADC1ConvertedValue[j];
% v3 z4 ?/ ]" [7 ` - }
C# O i7 Q. d0 ? - ADC1ConvertedVoltage = ADC1ConvertedVoltage / 10;
9 B% @' @# t& ~( x3 J - }
l" _' E1 d9 {# M, [5 A$ v$ S# p - for(i=0;i<16;i++)# `9 B6 F( z' {% g
- { F h' Y: w: W% G9 D
- volta = ADC1ConvertedVoltage * 3.3 / 4096;
* @8 \, [/ k1 R$ s, [ - printf("AD%2d: value: %4d vol: %f\r\n",i,ADC1ConvertedVoltage,volta);
) _7 f( F% ]/ ?/ | - ( z% ~( M+ P7 @1 m$ f( R8 s/ @+ f- n
- }- _" K* H1 d; Q, n% K& L! V/ @
- printf ( "\r\n\r\n" );
* ]/ c4 L6 C( M5 _. i% h0 K - ' y6 n% G5 F. E! _8 P* |! H
- LED0 = 0;& U6 o, ?1 N$ X) ]& ?
- LED1 = 1;
$ E% r7 {9 h# i5 R! O - delay_ms ( 500 ); //延时300ms3 T1 g# `$ _/ i9 n: e
- LED0 = 1;
# Z4 m; F$ C; h) c% u - LED1 = 0;+ D% i7 T" U' q; L0 y; p/ }
- delay_ms ( 500 ); //延时300ms
+ K3 ?0 K% [3 B5 j3 g - }! }+ N2 A) k) Z3 i/ S5 D
- }8 R' L0 I& b6 W8 V% T- X# F
复制代码
& w! P( N" f- Z4 m' N4 R m5 A, ? n通过一个二维数据存储16个通道的转换值,每个通道存储10个数据然后取平均值。最后将采样的16个通道值依次打印出来。
$ M- \; s6 | a0 b& A6 ~. B$ p- e# e( r. a. x, u( h
: F% ^4 b: ]/ l+ {: N$ i( ~0 f |