|
DMA简介: DMA(Direct Memory Access,直接存储器存取),是一种可以减轻CPU工作量的数据存取方式,如今被广泛的使用。它在传输数据的同时,CPU可以做其他事,比如数据运算或者响应中断等,DMA就给CPU分担了不少的工作量! DMA工作分析:
5 c; A2 K! Q8 i : W; p# n' V" a% V+ {, X( R. i- I
如图,我们可以看到STM32内核,存储器,外设及DMA的连接,这些硬件最终通过各种各样的线连接到总线矩阵中,硬件结构之间的数据转移都经过总线矩阵的协调,使各个外设和谐的使用总线来传输数据。 下面看有与没有DMA的情况下,ADC采集的数据是怎样存放到SRAM中的? 1.如果没有DMA,CPU传输数据还要以内核作为中转站,比如要将ADC采集的数据转移到到SRAM中,这个过程是这样的:内核通过DCode经过总线矩阵协调,使用AHB把外设ADC采集的数据,然后内核,DCode再通过总线矩阵协调把数据存放到内存SRAM中。 2.有DMA的话,DMA控制器的DMA总线与总线矩阵协调,使用AHB把外设ADC采集的数据经由DMA通道存放到SRAM中,这个数据的传输过程中,完全不需要内核的参与,也就是CPU的参与,不过DMA传输时要对DMA外设发出请求,才会触发其工作。 下面我是通过串口通信的例子来学习DMA的! 主函数main.c: - #include <stdio.h>
) c$ p3 F) v9 C9 T6 N0 v) h9 \( X8 E - uint8_t sendbuff[500];
: a2 n& x: j- W* a - uint16_t i;% q5 [0 e4 _" m: w
- int main()
3 i- ]' x% q( ~- u, { m% y - {
8 Y- x" b" l# q- h: p) h+ G - printf_init();" |$ A9 x- l) E5 S* X3 b& R1 ?' d
- dma_init();
+ R9 z8 x" K! W* V - for(i=0;i<500;i++)
# ~3 b. D- J0 h+ l7 m. N% K - {0 f+ d$ |. q$ ^. ]9 |* a" t
- sendbuff[i] =0xaf;
/ f% Q7 _& I3 ?$ v3 H - }
( Q: j2 L( m& c0 A' B ^0 V - USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);9 N4 l9 h& W Q# q. N8 u& K
- LED3_ON;
# L# |. g8 h7 M' x -
, A7 I5 ^- p( n2 J1 u) o3 I - while(1);
复制代码 . m6 d; F8 o2 C1 U& M; v
这个主函数实现的功能是利用DMA把数据(数组,数组里面的存放了500个AF字符)从内存转移到外设(串口),最后通过串口传输到我们的PC上显示。为了证明DMA在搬运数据的同时,CPU还可以做其他事,于是将LED3点亮,来测试一下。主函数至于为啥加while(1),才会产生中断?还不明白。。。 DMA配置dma.c: - #include "stm32f10x.h"
; b& F) J7 h- U - #include "stm32f10x_rcc.h"
! I* o6 @/ M# q* S# }5 Z' o, f a- m7 s - #include "stm32f10x_usart.h"
$ e- z! X5 S/ U* _& w6 ` - #include "stm32f10x_dma.h"+ ~$ R8 i2 } `% _! t; i
- #include "misc.h"
+ \+ t. `6 `3 l% s0 F# k - #include "dma.h"
; I3 L& Z- z7 A$ k+ t+ b - void dma_init() p( S% e o3 z+ K- _
- {
" u% x( r R6 l4 J -
" q# d$ q! f- o - DMA_InitTypeDef DMA_InitStructure;0 |) C& J! a2 b1 U" w& t& K. d' m1 K
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
; x7 Z: r9 b* f4 I8 ~' M - NVIC_Config();) ?( j; s- b7 g1 m( N
- /*DMA配置*/7 [' i8 U8 O8 f% q ^, f7 {
- DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;//串口数据寄存器地址
. M7 u7 X' h; z5 \2 M8 U - DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)sendbuff; //内存地址(要传输的变量的指针)7 j; G& N( L2 s
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //方向(从内存到外设)& t. `0 @& c& |% Q) y
- DMA_InitStructure.DMA_BufferSize = 500; //传输内容的大小
7 I- g( G7 Z; [- u. Z - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不增2 q& @1 d# L) b% k# e
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址自增
2 E. N" O; c* A" N. H7 z - DMA_InitStructure.DMA_PeripheralDataSize =
* W/ V# J. K, @0 m0 b - DMA_PeripheralDataSize_Byte ; //外设数据单位7 S* S/ U h+ s- i
- DMA_InitStructure.DMA_MemoryDataSize =$ N# K0 a: k, i6 Y: i" H
- DMA_MemoryDataSize_Byte ; //内存数据单位
. S Y% B4 J F4 s4 N4 I9 B. u9 U - DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ; //DMA模式:一次传输,循环" p( ] p- R+ [) [* b
- DMA_InitStructure.DMA_Priority = DMA_Priority_Medium ; //优先级:高
, H; |5 {$ P: e4 \. Z; W - DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止内存到内存的传输7 D& y0 W/ j6 X% o/ G# A4 R4 a( }
-
3 R3 A, s" n* u8 k; X - DMA_Init(DMA1_Channel4, &DMA_InitStructure); //配置DMA1的4通道
+ x1 w' S+ X/ I. ] l - DMA_Cmd(DMA1_Channel4,ENABLE);
- _+ a- m; ]$ }6 J \6 E, _: | - DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);//配置DMA发送完成后产生中断) K& q1 }, w0 i6 M. S4 d
- ( K% e4 W3 g& J+ u/ X# A, j
- }( h5 ?9 }4 n5 ?( A7 z5 G
-
9 F; e- [) ^* a3 Y" y - static void NVIC_Config(void)
4 {: U) ]3 h7 |) _- z8 O - {1 M+ |% s; M% ~7 ?+ t/ [
- NVIC_InitTypeDef NVIC_InitStructure;+ X4 m. Q$ H3 _* N! L# A
- /*中断配置*/' x# t& ~$ z, {4 } i. Z' h9 F
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);. Y- p- e' w6 G4 a3 V3 e
-
& @+ }5 P3 ~/ c1 ^1 I% v- ? - NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
# z3 w4 u X/ d" Y8 `6 t, X - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
5 z v( E# L' q1 |9 h - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;5 f: q; `2 J Z( [* v% c
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
$ }& F0 p9 `, W0 R - NVIC_Init(&NVIC_InitStructure);
- K* ]- M8 [/ ~9 L. w. h3 |! Q - }
复制代码
& T0 ]5 i1 [ V' L% E% c这里的串口数据寄存器地址配置是参考STM32的datasheet来设置的,USART1_DR_Base是一个宏,#define USART1_DR_Base 0x40013804,至于地址为啥是这个,请看下图: % s$ r1 z) l. s7 |& S4 P) m/ Y- V
6 P+ @7 o3 k/ W1 T8 \4 @0 F
* u% V( c1 R1 A5 D7 A7 ^) L$ o , S9 s q- B' A$ b
存储器映射图,找到USART1的基址,再看USART1数据寄存器偏移地址就可以知道串口1的地址了。 & Z' G2 f0 V; `4 t* U3 }" B c
; t" f# F6 `. q2 L3 A9 E
" I7 {8 h) Z, R, U至于在配置DMA1通道时,那里为啥是通道4,而不是其它通道,请看下图:
( y' X& m: t, u0 \8 g1 L% g, a6 Z! H$ R k$ V! Q
! x: i- E8 l& E' Q' a
dma.h: - #ifndef _dma_H
1 Q2 x8 Y( W6 X, R- j0 ? - #define _dma_H
/ A0 D, | ^- w4 J# k9 S - #include "stm32f10x.h" W0 h/ x4 |" c8 t
- #define USART1_DR_Base 0x40013804;* x- E/ X/ Q& e
- extern uint8_t sendbuff[500];1 h* j% D$ Z: Q
- static void NVIC_Config(void);
) X/ P1 S3 S6 l3 o - void dma_init(void);
3 j! k N4 T" P2 ` - ( _5 n8 `. l @$ ]2 q! m( y
- #endif
复制代码
, M g+ a! G1 G' S' |串口配置:printf.c - #include "printf.h"
8 l" q( L6 d, d& a' v* E7 s/ E( E - #include "stm32f10x.h"
9 _9 |$ b6 P, \. [ - #include "stm32f10x_rcc.h"
& `' p( @$ g% t0 L0 L - #include "stm32f10x_gpio.h"
$ Q1 T# l: Y, q- g+ _/ | - #include "stm32f10x_usart.h"2 H& G: P: ]# P7 F A2 C
- #include "misc.h"
6 j6 X0 k' f1 O8 E8 I - 0 R" \( j' p' U& j$ z
- int fputc(int ch,FILE *f)
z# ?8 H6 l" L; r( I - {
0 {9 W& v2 N, L$ W - while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);' p5 e' J6 v- k0 t% z. m; R& Z
- USART_SendData(USART1,(unsigned char)ch);
! K/ v3 `/ \2 _, ^, R8 ^7 T - while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
) f$ W" y1 Q& I! R! A - return (ch);
2 E) }* M7 I8 G - }
! d% Q7 u9 j3 }! }* I* [* n0 X -
' b3 \8 R5 v0 h6 ?$ U1 c! L% K - void printf_init(void) y+ ~7 q& l7 r; v2 T$ E. \9 }
- {9 \6 X# ^9 i4 Y, Z- g+ Q
- GPIO_InitTypeDef GPIO_InitStructure;# @* c9 z% ]- i
- USART_InitTypeDef USART_InitStructure;' x: Q! R0 }7 c) o$ U
- 9 s2 L6 b* A% B6 O6 A
- /*config USART clock*/- M2 B/ k- v$ k% B$ w$ ]) q2 e
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);+ e5 A+ X/ Q" Y7 o; E3 k
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
% X8 n* i8 i x4 ~! w- N+ a) d9 _ - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
0 @8 n7 J u4 r - /*USART1 GPIO config*/
0 l" T) k& I- r7 ~1 n/ D - GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9;1 \2 a# j! T' i& G! U# A( {' ]& ~$ E9 U
- GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP; //复用推挽输出4 y1 V& S" u6 [, F
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;! R$ S M9 Y, Y( e3 U% e
- GPIO_Init(GPIOA,&GPIO_InitStructure);
5 m4 r* u8 E6 K( t+ T - 1 Y6 r+ }9 R \+ Q: l
- GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10;
# m" ]4 Q! K |) h$ _ - GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING; //复用开漏输入. j0 r9 ~- i$ G! L1 f1 B* Y. d
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;, K, F4 q+ ~) c6 o) ]
- GPIO_Init(GPIOA,&GPIO_InitStructure);
- |$ D u/ c! r' G2 @4 _& n% i -
9 `1 R, a. U# ?2 |/ e - GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;) w4 n- g6 F1 z* M
- GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;$ j% w, ^& ] t, H
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;* O- A; B" I( X! {, D, P- x
- GPIO_Init(GPIOD,&GPIO_InitStructure);
E% g3 s( ~: `% t& o - /*USART1 mode Config*/
/ d0 F& |9 C2 t7 [3 D8 H - USART_InitStructure.USART_BaudRate = 9600;, j+ K& X0 m' L6 _/ z% o: B2 X
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;
8 P% o9 V2 c) S5 ~ - USART_InitStructure.USART_StopBits = USART_StopBits_1;5 n$ s* C0 M. N
- USART_InitStructure.USART_Parity = USART_Parity_No;0 N/ [0 K% d% v. a6 Q& `0 X$ P8 m9 ?
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
, M; T' Z1 s3 x8 i - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
& o! x" h, @ M$ R# | - USART_Init(USART1,&USART_InitStructure);
/ `3 `$ M) H9 C4 ^5 Y9 h4 Q$ d - USART_Cmd(USART1,ENABLE);
复制代码 ) f; {- Z- _; { Q1 g+ ~* `, ~7 m
printf.h: - #ifndef __printf_H
. _8 _$ |/ S% [2 q7 Z3 E - #define __printf_H
5 h+ r1 @4 V9 e/ B3 i9 E0 R - 5 k6 F' v' a) t' ]& d4 n
- #include "stm32f10x.h"
# a! ~( `) z3 m" t; ^ - #include <stdio.h>
! Q4 e8 ?4 z: H/ R3 q7 G - #define LED3_ON GPIO_SetBits(GPIOD,GPIO_Pin_3)6 P8 e' c( Q) D5 f& H5 V. P! \% C
- #define LED3_OFF GPIO_ResetBits(GPIOD,GPIO_Pin_3), |: E- E3 y5 A0 r9 @- J9 A
- void printf_init(void);3 n$ G) f1 J, ^7 k' N# q# s. q; Z0 R
- int fputc(int ch,FILE *f);
: L1 z& b) s) _, ]8 g5 i: L -
& Y1 {% o1 u7 x* e% i - #endif
复制代码
5 y# _) M% Q; }9 q5 D" ?中断代码: - #include "stm32f10x_it.h" u6 Y+ K6 U! u/ ~' r- x% e1 ]
- #include "stm32f10x.h"$ g% M+ Z8 ^8 b
- #include "printf.h", k, r; t7 y, O- W
- void DMA1_Channel4_IRQHandler(void)
, }: ^8 Y4 S* [+ n- J - {( t9 X+ y2 L- T. e; p1 q) F; c
- if(DMA_GetFlagStatus(DMA1_FLAG_TC4)==SET)
* N! I* G2 D8 g( z - {
3 E$ Q# v& @) }4 l - LED3_OFF;% C% Z8 d1 H! B: M
- DMA_ClearFlag(DMA1_FLAG_TC4);
7 w2 R$ L9 W8 U- {: u+ U7 o4 p - }
; S2 b4 B; H. k `' X( K# g - }
复制代码 _+ H6 t9 f. V' P4 x, S! N* E
效果图: 
/ R3 Z, W+ j6 [0 {: x+ v+ W
, @+ C0 U( o" d% ` v/ `7 b |