|
DMA简介: DMA(Direct Memory Access,直接存储器存取),是一种可以减轻CPU工作量的数据存取方式,如今被广泛的使用。它在传输数据的同时,CPU可以做其他事,比如数据运算或者响应中断等,DMA就给CPU分担了不少的工作量! DMA工作分析: 9 z7 a$ c+ x7 @* a. t% x
$ A# n/ }' f* F8 _% V+ ~5 L0 |; V
如图,我们可以看到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>: Q- V/ m, }; k+ r& B1 ~
- uint8_t sendbuff[500];
. ^: e- p3 r) q3 ^. ?9 b$ t. M - uint16_t i;9 Q" x! y1 _& I+ a
- int main()5 e9 }) g! ?% C
- {+ }; e# w Y8 i
- printf_init();
$ I' W( ]) h( ^2 i- P3 B& n - dma_init();
a9 J3 N) i+ s3 s7 `" A4 v; l - for(i=0;i<500;i++)' G( x( s7 q! \& k: g3 o
- {8 ?4 C* L9 ?3 b( C( U" d
- sendbuff[i] =0xaf;
4 _3 J' F3 U2 J& @# w - }& {* d. j# n$ A7 L8 ?" c
- USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);8 \8 V. p# D/ {0 e0 b9 V$ i8 s
- LED3_ON;. n( c1 r6 [7 n/ [
-
. [3 u/ [- c1 e0 | - while(1);
复制代码
% \& }/ o; W7 W/ L- X' c0 b4 L这个主函数实现的功能是利用DMA把数据(数组,数组里面的存放了500个AF字符)从内存转移到外设(串口),最后通过串口传输到我们的PC上显示。为了证明DMA在搬运数据的同时,CPU还可以做其他事,于是将LED3点亮,来测试一下。主函数至于为啥加while(1),才会产生中断?还不明白。。。 DMA配置dma.c: - #include "stm32f10x.h", Z7 Q5 w- H6 |
- #include "stm32f10x_rcc.h"
- S6 }. N8 w! o. { - #include "stm32f10x_usart.h". l4 G$ O- f4 n9 G$ b# C: I
- #include "stm32f10x_dma.h"
1 s8 `' S# q2 |2 ^ - #include "misc.h"" c; w; _. h+ D6 v" g+ l! W% c
- #include "dma.h"
4 q% A% ]9 x8 {" P1 Q+ Z - void dma_init() H6 [3 F5 B! H: z5 ?6 ] d; D
- {* O: e/ L* E* }( |# t; U# T
- % b" n. i0 Z s: M X1 u+ q" p
- DMA_InitTypeDef DMA_InitStructure;1 J; M/ |& M0 s1 D7 s3 j& l. i
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
6 @* R( G$ B# H - NVIC_Config();( n# m* y* W4 Y3 O+ d! g: G( k
- /*DMA配置*/. p# w# O' C/ W- b. H- y, ^
- DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;//串口数据寄存器地址2 Y. Z& S! u6 x$ y8 v
- DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)sendbuff; //内存地址(要传输的变量的指针)
$ U0 l3 D0 q1 u2 }3 u1 O9 K - DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //方向(从内存到外设)
; ]( ^( S& U& b9 X( @* G, @" z2 k- k - DMA_InitStructure.DMA_BufferSize = 500; //传输内容的大小
8 f4 @) O, a" L2 E- h* p - DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不增
$ K1 I& x9 v0 B- O% D9 y - DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址自增- i/ X( J( z( g/ ]9 F) G- B! Q
- DMA_InitStructure.DMA_PeripheralDataSize =1 w; X% }9 D) k$ ]+ m2 x$ B+ U
- DMA_PeripheralDataSize_Byte ; //外设数据单位
" ~+ u. S% `6 c1 T - DMA_InitStructure.DMA_MemoryDataSize =2 |) z4 A( r& V ]
- DMA_MemoryDataSize_Byte ; //内存数据单位. k# @& c6 U$ D0 F
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ; //DMA模式:一次传输,循环0 ?! E3 o9 S8 @9 S' |
- DMA_InitStructure.DMA_Priority = DMA_Priority_Medium ; //优先级:高
# v' L- E4 b# f( A' }, ~: ~: L - DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止内存到内存的传输2 m- b6 G2 L' J2 E! ?
- 0 ~+ Z' b0 r. ]* E* L+ N7 z
- DMA_Init(DMA1_Channel4, &DMA_InitStructure); //配置DMA1的4通道
# s7 E- w c5 q; x n) Q4 Z5 G - DMA_Cmd(DMA1_Channel4,ENABLE);
D* E* A W9 o8 |2 s9 k( {! _ - DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);//配置DMA发送完成后产生中断; x8 O+ U, Q; ?3 X
-
* P# w0 I3 T% w& I - }
: ^1 I$ @7 e- G, m ~ - : L3 G N# a! _
- static void NVIC_Config(void)
) [' s8 q8 |3 t' X5 ^ - {/ \8 Z) V- f/ L3 d
- NVIC_InitTypeDef NVIC_InitStructure;9 `! @$ m7 a! |0 c' D+ {
- /*中断配置*/* Y4 p$ D3 Q# ^+ F
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
9 F; k' [0 s3 e I -
, Z! W% v4 j1 }( [5 {+ R - NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;* J# z, c; P" u- h
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;9 I$ X- d. Z6 R9 |. \! y* m
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
3 j( a6 F, M, F* h$ s( k' R" h3 h - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
% `, R% E/ p/ a" f - NVIC_Init(&NVIC_InitStructure); [: Z; U4 B8 c% U& c
- }
复制代码 ( s* k3 s0 P# }2 L8 V+ A/ h, R
这里的串口数据寄存器地址配置是参考STM32的datasheet来设置的,USART1_DR_Base是一个宏,#define USART1_DR_Base 0x40013804,至于地址为啥是这个,请看下图: / [+ G! Z8 O4 m; P$ q2 |% ~
) T8 i) n; M& C6 _0 p' C
! z: Y1 C& }9 ^* E5 E
/ i+ o; f" N4 R. U5 g% e
存储器映射图,找到USART1的基址,再看USART1数据寄存器偏移地址就可以知道串口1的地址了。
4 [; x% z; }# | f
8 l) S! ~' v& {% J, @- X Y5 f
2 ~( }; _. g2 E/ W9 p% C, _至于在配置DMA1通道时,那里为啥是通道4,而不是其它通道,请看下图:
, _. e! r# a1 a( k5 P5 v8 Z" ~' [+ f; F; h! R) ~
) B7 ^: Y @3 S9 Ydma.h: - #ifndef _dma_H
" ~7 Q) ~+ N) p; \& S1 y - #define _dma_H
; [4 i0 ~: G8 x - #include "stm32f10x.h"
( Q5 K# t/ s6 k - #define USART1_DR_Base 0x40013804;$ M( W! [+ E8 D* s( E) z
- extern uint8_t sendbuff[500];. H; M. T1 s+ [/ k3 ^. S/ J+ g
- static void NVIC_Config(void);
5 c1 N) A) e- J" M7 j% v - void dma_init(void);
% I z$ A, N* X/ T) o -
7 |- R& [, c& a- w, D5 @( |' x; p - #endif
复制代码 + U7 @& @2 y! O& P0 `1 r0 @
串口配置:printf.c - #include "printf.h"! g7 g( V# V) v' j3 k ]8 S
- #include "stm32f10x.h"
4 j0 |, S5 r) i( R! Q& R - #include "stm32f10x_rcc.h"2 e( v& |3 J2 ]0 `, @: Q3 e
- #include "stm32f10x_gpio.h"0 M0 I4 y. U0 e& f9 h9 ^/ N
- #include "stm32f10x_usart.h"
+ g. z, M. }/ E2 p* M* e - #include "misc.h"
: A5 z6 k0 {. j8 U -
/ x8 X: Q3 f) ^9 y& Y* a - int fputc(int ch,FILE *f)
% Q, [) P" v9 f5 ?3 A; N* [$ C - {
* Z6 s. r3 I& N - while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
% S8 [% ]$ G% Q6 s9 S( D( T - USART_SendData(USART1,(unsigned char)ch);7 ^' D8 n& ^/ W) z2 _
- while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);& ^8 b: k# r6 g6 Y b" u9 K
- return (ch);/ E: H2 I+ M2 |
- }
9 l4 b, q6 L4 o. g. y- J* E1 r: J -
2 k+ C3 l v- b - void printf_init(void)
$ V5 ?5 W3 }& |: L: P4 J' v" W$ X8 n - {
7 ]2 z6 ]9 J# c! r: b - GPIO_InitTypeDef GPIO_InitStructure;" ^$ i5 A" [4 |4 x r
- USART_InitTypeDef USART_InitStructure;
& y1 t& m' v4 X1 U -
3 x1 V; @6 E4 f3 }* L: O, j7 C5 M$ } - /*config USART clock*/+ F1 p% I8 H8 x4 O$ T, ?( ?. a: \
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);/ X4 ^/ L- c) j1 Q3 N: N' s
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);8 d* v5 i& {; p& T9 e: ?
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
6 {; j a# Z$ Z# a- L! f - /*USART1 GPIO config*/
6 u( M4 t! V1 t$ G$ T - GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9;
) j$ r/ {7 H* B+ \: ^9 p - GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP; //复用推挽输出
' _ C: p' E* t2 i) V! h9 ~ - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;' P4 o4 ~/ R, y) i( L, Y: R
- GPIO_Init(GPIOA,&GPIO_InitStructure);9 z3 l& M v0 m! B1 y6 @; z$ F$ O
-
6 Z4 q: a) p, M* X H4 J; b - GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10;
/ s4 X; E3 j; h2 j! N - GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING; //复用开漏输入
' x( C; F5 \9 F' Y, f - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
, g8 G+ e {9 i+ x - GPIO_Init(GPIOA,&GPIO_InitStructure);
' c; {- @. J C; s1 n7 i E$ F# S* N - / r; g Y5 b8 Q, }( R
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;, g* M$ v) g6 W8 B9 x. b% b' m
- GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;" y( H' w7 v! I
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
6 E! R( A- ? q7 s - GPIO_Init(GPIOD,&GPIO_InitStructure);8 E9 f9 R8 L7 S1 h* w
- /*USART1 mode Config*/3 q6 g. x1 G$ ^* }* T
- USART_InitStructure.USART_BaudRate = 9600;
/ G ]7 C& Y I# e1 T - USART_InitStructure.USART_WordLength = USART_WordLength_8b;
- F! U( ^3 [/ Z - USART_InitStructure.USART_StopBits = USART_StopBits_1;
* }: a) M. A2 `7 W% }) y - USART_InitStructure.USART_Parity = USART_Parity_No;) b+ G; r- K. Z
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
' a6 M _9 [) U) k* D2 b - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
8 L: K8 J9 r+ f- C: U1 a1 R - USART_Init(USART1,&USART_InitStructure);
3 |9 Q* g6 e" Y. W+ p6 ] - USART_Cmd(USART1,ENABLE);
复制代码
/ ` ]" j( Y- ^/ \' ^printf.h: - #ifndef __printf_H
8 e2 R7 F% O% }; n U/ `9 \ _ - #define __printf_H! Y7 g2 y: |5 _( f" ^5 {2 y
- 4 A7 ^8 D2 K) V3 c
- #include "stm32f10x.h"
4 F) M* R$ ^0 K# u1 H+ |" u/ b - #include <stdio.h>
/ R4 Z2 m, k9 r3 @: `( S+ W - #define LED3_ON GPIO_SetBits(GPIOD,GPIO_Pin_3)
# Y9 n$ }4 q, k; J# [# h. e - #define LED3_OFF GPIO_ResetBits(GPIOD,GPIO_Pin_3)6 T7 I" O2 N$ H% r$ f9 U0 l1 h; k
- void printf_init(void);
) j) Z/ k3 e! f, x9 c% V - int fputc(int ch,FILE *f);
3 @+ x" w" a) L - * f* ~) x7 C! Q6 C! p- r2 L) B
- #endif
复制代码
2 N' w8 E k% z* e; g" q9 b2 X中断代码: - #include "stm32f10x_it.h"
/ `1 E! I0 \; A - #include "stm32f10x.h"
% `8 S1 r( L/ |! j6 f4 P% ^) U* h H - #include "printf.h"; O0 ]# a/ `3 e
- void DMA1_Channel4_IRQHandler(void)# X- ]- ]+ t8 I; T: Y1 I3 w
- {, c" R, p0 O d/ q# ^. C! L- i
- if(DMA_GetFlagStatus(DMA1_FLAG_TC4)==SET)1 T7 [! A0 W: D/ l5 }9 y& _& }
- {
9 r* U# f0 L! W/ I4 ~( P1 l: W" `: A - LED3_OFF;! |' w- @& ^3 Q$ a/ c) [8 [
- DMA_ClearFlag(DMA1_FLAG_TC4);
& Z L8 h) y o - }$ O- J8 m% D6 j* s% g
- }
复制代码
; c2 l0 b: p& p U3 I) N效果图: 
# S C1 u- X/ y) _
+ m q6 |7 \, X1 O( J |