请选择 进入手机版 | 继续访问电脑版

你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32的DMA基本原理及实现过程

[复制链接]
STMCU小助手 发布时间:2022-5-16 11:19
1、什么是DMA+ o% t+ A: q2 w+ b0 w& ^$ Z
DMA全称是Direct Memory Access,即是直接存储器访问。 DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路,能使CPU的效率大为提高。
# \% w/ w" m, S7 t' h# a; }; J! g* o$ H+ N! y3 b% M" Y' F1 M
2、DMA特性
: A# w5 v' i  `● STM32F1有12个独立的可配置的通道(请求):DMA1有7个通道,DMA2有5个通道
2 v. E+ p8 g+ ?" o: Z" [; a
& Z+ w% [) }8 j4 b8 E● 每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。可通过软件来配置。/ Z# \: z( M: t9 P

, J2 h- n/ U! I, C% V2 ?$ }$ N● 在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、
5 }: m, M$ l; A2 U( y3 N9 A* B1 S' i, k4 r9 _6 ~, v+ J0 q
中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推) 。# M" G; F& D6 G( Q1 J
/ M4 e8 c. m  u; u6 S: _. a
● 独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。
4 X3 }, k; J, [, t; m4 F# M. n% O% Q. {& }/ ^& U
● 支持循环的缓冲器管理% Q( d7 ~/ A; K7 G
; w  S( {! w' M" a6 J
● 每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。
$ s, U' G" L# v- U8 Q3 A' ~- k
3 o, P  Q! I* {2 ^1 [+ M6 H5 J● 存储器和存储器间的传输
7 s3 {. L4 l& \7 s) U) u! M6 z1 x1 q' A3 }1 m" O( J* L4 D
● 外设和存储器、存储器和外设之间的传输
+ i, Z6 ~: q: a! E, o2 e
! V( C- t( e5 e  F3 S● 闪存、SRAM、外设的SRAM、APB1、APB2和AHB外设均可作为访问的源和目标。
- Y& V4 m. @4 w- L" m2 U0 w
' w1 G) H8 r* d) y● 可编程的数据传输数目:最大为655351 b1 c% j- w4 z, _* D+ N& R
/ a/ G5 {+ M2 Q
3、请求映射表
0 N1 v$ {  c" E$ e+ k两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。$ b# f" e# U) t& e* z

% L3 S9 p5 @( t) e( T0 a% _3.1、DMA1请求映射表
5 I8 j! O* K6 q' t
  x4 v$ O' J  ?' w0 Q5 ?
Y8GPL}G%LHW~FCL$XUG8K3I.png
" @6 J, ]0 c1 |8 Y) i. T, a, H
3.2、DMA2请求映射表
9 g# S! X  B+ y; y

% F% S* k3 }' Y& K' s JP~~Y2J]{L01}}[T62BMC8P.png ; A; k. @; s6 b6 X4 v

% U* w' {: W3 l2 F# Z# z9 `, S/ v4 b/ ^4、软件实现
" t7 {$ }6 Y8 z# O. h我这里使用USART3和DMA来实现。通过上表可以知道,USART3_TX和SART3_RX分别对应的是DMA1的通道2和通道3,首先就是对USART3和DMA进行初始化+ n! V3 v' w! V4 y3 f: R2 {

6 ?- ?2 L  `0 k' B9 Z4.1 串口的初始化( _+ f' K7 J- @
串口有单独讲解,可自行搜索参考,这里不再讲解。; W; W" B0 i0 T' v0 h4 f3 D

' y& S2 s* c1 R
  1. void uart3_init(u32 bound)1 U) |( s& R" n6 y
  2. {
    $ A- z' f4 }# Z) s8 I
  3.         //GPIO端口设置4 G* Q% j# [4 j: y; K
  4.         GPIO_InitTypeDef GPIO_InitStructure;
    9 F9 y3 v& R  `  s# z
  5.         USART_InitTypeDef USART_InitStructure;
    " t) |* F- Z" Z5 v# q( S) i4 B, I
  6.         NVIC_InitTypeDef NVIC_InitStructure;
    $ [" I5 U8 M: H4 b  y# K
  7.         //使能USART3,GPIOB时钟
    - _5 c& `7 F# U! T; A6 w6 O- d
  8.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);% `5 J4 }; [' B4 r
  9.         
    8 ]: J1 o% T9 _/ R: N
  10.           //USART3_TX   GPIOB10  L% a% R: [" M0 }8 _1 a) x# q
  11.           GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //P010
    0 {) p6 W1 \5 ?9 Q
  12.           GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;% J8 r2 P) M7 E
  13.           GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //复用推挽输出  F2 i$ G' p) Q
  14.           GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB10! F0 u: ~% q" ]9 ?) J. n2 e7 V
  15. $ R( y+ z" A1 q5 Q
  16.           //USART3_RX          GPIOB11初始化
    + W1 ]. t  S- `- t6 o
  17.           GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;//PB11
    % A7 [9 b* j2 r% H+ X
  18.           GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    ; V& U" p7 J- ?. E
  19.           GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB11  : y, `1 S  ~. o4 M/ @' B4 w9 B

  20. , l, u9 A1 e5 H
  21.           //Usart3 NVIC 配置
    ( j3 W3 A5 i  ]2 z( z! S3 A; X! t
  22.           NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
    + Y9 T" x1 ^3 q* i' Q( }5 G
  23.           NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
    ) f- ]( C; D5 N# k4 {4 y% L- c- M2 r
  24.           NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                //子优先级3
    : m3 y7 T6 `; w) E
  25.           NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能5 @7 ?0 b9 _" Q  V: J: J, n
  26.           NVIC_Init(&NVIC_InitStructure);        //根据指定的参数初始化VIC寄存器, V; b$ O# a9 g* q
  27. , C( `6 a/ ^1 y' v% A& Y
  28.           //USART 初始化设置
    ; _* E: Z7 w6 u. x/ K  r
  29.           USART_InitStructure.USART_BaudRate = bound;//串口波特率9 l1 g6 a7 j, N, u" @, x
  30. //字长为8位数据格式
    6 e: T7 [  y) B+ x, M- p' f: }4 r
  31.         USART_InitStructure.USART_WordLength = USART_WordLength_8b;                 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位3 U& V) S! R8 D; z
  32.           USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
    ; @- E7 S. r3 O- j/ o
  33.           USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
    , y: r  v! R$ |% W0 Y: a- Y- v* e$ ~
  34.           USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;; \  x+ `0 t; ~; ]) ^+ z# H
  35.           USART_Init(USART3, &USART_InitStructure); //初始化串口3
    * z) Q9 b9 N: T2 U
  36.           USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启串口接受中断
    4 g! a  I# O1 ]) z7 H4 o
  37.           USART_Cmd(USART3, ENABLE);                    //使能串口3 / a% c4 Y0 Y' b) C* O! ]0 l1 C
  38. }
复制代码

! }! I( k5 o# W, Q+ X4.2 DMA初始化
% [5 C1 F" ]2 J7 Y, H9 p: H1)使能 DMA 时钟  [8 `# U" [9 n( y: p) b; O3 N
  1. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能 DMA 时钟
复制代码

( V6 A) {8 Y1 S3 h9 q2)初始化 DMA 通道 4 参数* P: S5 }# |5 T; y1 P3 h# z. m
  1. void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
复制代码
2 h( s; ~. G9 y: |+ l' A2 _
跟其他外设一样,同样是通过初始化结构体成员变量值来达到初始化的目的,DMA_InitTypeDef 结构体的定义如下:! ~# z$ u5 W3 F$ o/ a6 S0 z
* {6 v; o' L( [" j2 N" W
  1. typedef struct
    $ g. X  Y6 d0 m8 K" |5 h
  2. {
    ' q& v1 o+ k" h: r+ d
  3.     //来设置 DMA 传输的外设基地址,比如串口3,表示方法为 &USART1->DR,也就是外设基地址为串口3的接受发送数据存储器DR的地址# V0 Y. X: F/ |" _6 e0 f
  4.     uint32_t DMA_PeripheralBaseAddr;
    6 C# C( T$ \' `" y# d) H
  5.     //内存基地址,也就是我们存放DMA传输数据的内存地址
    ! z" q: ^; f3 i6 e' s, n0 x) H# x
  6.     uint32_t DMA_MemoryBaseAddr;
    3 ^/ m7 s' R7 k4 X0 T
  7.     //置数据传输方向,可以是外设读取数据到内存,也可以是从内存读取数据发送到外设2 }  ^- k9 N9 @6 i$ c! ^5 x
  8.     uint32_t DMA_DIR;
    & R, @: V+ |  T5 Z+ X
  9.     uint32_t DMA_BufferSize;//设置一次传输数据量的大小,也就是缓存大小
    * m9 V3 M- D0 t7 P; }' d% E
  10.     uint32_t DMA_PeripheralInc;//设置传输数据的时候外设地址是不变还是递增
    0 r4 e$ u8 L: y  l% f9 N1 S
  11.     uint32_t DMA_MemoryInc;//传输数据时候内存地址是否递增
    - p! E+ l/ @6 `! u2 \8 B
  12.     uint32_t DMA_PeripheralDataSize;//设置外设的数据宽度,8bit,16bit,还是36bit , q1 M/ S- H, V  A
  13.     uint32_t DMA_MemoryDataSize;//设置内存的数据长度
    . @1 Z  b* C5 m$ V9 ?1 t/ u& Y
  14.     uint32_t DMA_Mode;//设置DMA模式是否循环采集 2 |. i! V4 X2 ^0 k) P+ M" {
  15.     uint32_t DMA_Priority;//设置DMA通道的优先级 9 I+ V" e3 A, o
  16.     uint32_t DMA_M2M;//设置是否是存储器到存储器模式传输
    ! e5 Q5 _  K/ n
  17. }DMA_InitTypeDef;
复制代码
# X& d1 ^- U! z! K# W  D
所以,使用DMA发送的初始化代码如下:
7 R9 c7 p+ C/ _; R, v& n6 A- W' p: {6 ?0 b, l: K- J
  1. void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
    1 D" X8 o2 I% S) F- j, I
  2. {
    7 ^3 W# o$ t+ x/ ], ~* s
  3.          RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);        //使能DMA传输
    * S2 |4 u, @# [0 e- ]; I
  4.           DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值* F, e! J. t# S
  5.         DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  //DMA外设基地址* N: u# b9 r0 K. I
  6.         DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  //DMA内存基地址/ N. @" I! V9 s4 b% {
  7.         DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  //数据传输方向,从内存读取发送到外设
    & j- U7 {0 ~" m9 |1 \( K
  8.         DMA_InitStructure.DMA_BufferSize = cndtr;  //DMA通道的DMA缓存的大小
    4 D# }8 m9 A" ?. b1 b6 w4 f1 c% t) f
  9.         DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变/ h, H6 F( ]: H# @9 g. B4 v
  10.         DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增
      T) k2 a) s2 k' r* B
  11.         DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度为8位
    * G2 g7 b4 M0 F4 l4 N
  12.         DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
    8 Y: M) W" Z3 E3 U
  13.         DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常模式
    + ~5 i) b  f: J3 ~
  14.         DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级 ! W3 @% k, q" [8 b/ {0 z% w/ z
  15.         DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
    # ?3 U8 Z# Q! p% O
  16.         DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道所标识的寄存器 1 E2 a: O( a: ^: I( j( `1 f8 j
  17. }
    9 |8 \; c9 }% q6 H
  18. 3 z% p0 O; Z4 Q% |$ [
  19. //因为使用的是正常模式,所以在每次使用DMS传输时都需要使能一下DMA
    + b& K+ ~/ r" g6 Z. {
  20. //开启一次DMA传输0 i" j, ?; M# }
  21. void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)8 ?, W" e. y) `! r  z# s
  22. { 9 g" F, U3 ?! K/ g
  23.         DMA_Cmd(DMA_CHx, DISABLE );  //关闭USART1 TX DMA1 所指示的通道      
    9 g; l) f* L8 u! ~! g) h( B7 X: o! o
  24.          DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小
    ; V2 B6 ^$ F2 i3 n. |: K
  25.          DMA_Cmd(DMA_CHx, ENABLE);  //使能USART1 TX DMA1 所指示的通道 : X; r9 E  p5 z  F' Y3 z: X7 Y
  26. }
复制代码
1 g9 C! [: A% E
因为是使用的串口,所以还要开启串口的DMA发送功能# e1 Y! V9 }4 m, J) z

3 N1 N" ~8 \: v3 C' x  o8 sUSART_DMACmd(USART3,USART_DMAReq_Tx,ENABLE);//开启串口的DMA发送功能! q  r, F2 e/ H2 K+ l% j
总结:也就是MCU内部有一条总线,专门用来提供给DMA使用,如果不使用DMA,串口有数据来,CPU要从串口的数据寄存器把数据搬运到内存(也就是我们定义的buff),如果使用了DMA,数据就可以直接从串口的数据寄存器到内存,也就不关CPU的事了。
7 l1 _5 W1 G9 Y' `* c4 {% z/ _/ n2 N! n# _$ [3 ]
4.3 主函数
) R: [/ D0 g$ x& I- Q主函数如下
  n2 w; e7 Z0 [* W4 o9 H. H
  1. //发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍.
    " \# N% Y  ?  h6 d# ?
  2. #define SEND_BUF_SIZE 8200        
    0 ]+ {1 Y- b! \% W) g# D+ d
  3. u8 SendBuff[SEND_BUF_SIZE];        //发送数据缓冲区
    " U! C/ r6 {  o  M6 g$ x. ~
  4. const u8 TEXT_TO_SEND[]={"STM32F1 DMA 串口实验"};
    ) c( ]& o: I$ R% M. \

  5. # E3 c: V% f( C# T
  6. int main(void)! N' r% I6 j9 K. ?. c+ z
  7. {         
    & y; c) F) E3 ?$ E* g
  8.         float pro=0;//进度, r9 B" b- L3 [2 p
  9.         delay_init();                     //延时函数初始化          & ?/ X/ k" N& \* v) N
  10. //设置中断优先级分组为组2:2位抢占优先级,2位响应优先级' O3 t/ D- R6 l% C$ I0 w
  11. / z* [8 t9 n5 D5 Y
  12.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);        
    & m( U+ p% A4 \- l
  13.         uart_init(115200);//串口初始化为1152006 m2 z7 _4 R# M* E' N. L# ?
  14.         MYDMA_Config(DMA1_Channel2,(u32)&USART3->DR,(u32)SendBuff,SEND_BUF_SIZE);//DMA1通道2,外设为串口3,存储器为SendBuff,长度SEND_BUF_SIZE.5 E  C, V' S/ I+ O2 ~2 v0 T" R
  15.         / R  z! }) \* H: j% U: O
  16.         while(1)  k3 j9 ]! l- B7 x& q* g6 h
  17.         {        
    + p( g3 q; ^/ @# y" H3 n$ t2 y' ?+ t
  18.                 printf("\r\nDMA DATA:\r\n");            
    4 Y+ k5 m+ n# Z5 G! u/ ~
  19.                 USART_DMACmd(USART3,USART_DMAReq_Tx,ENABLE); //使能串口1的DMA发送      
    0 C; {/ R6 m* L1 l$ l8 ?* r
  20.                 MYDMA_Enable(DMA1_Channel4);//开始一次DMA传输!         
    * G* f- p- @6 ^% t& O! e# i
  21.                 //等待DMA传输完成,此时我们来做另外一些事,点灯
    + l+ G0 C5 l6 u: m5 K! }
  22.                 //实际应用中,传输数据期间,可以执行另外的任务) ?: t1 m+ i: i: G
  23.                 while(1)2 f7 U0 J2 N. L" x$ m. V# e
  24.                 {
    5 j, J1 A$ N0 D# \
  25.                         if(DMA_GetFlagStatus(DMA1_FLAG_TC4)!=RESET)        //判断通道4传输完成& y" U( ^" X9 n% _, j4 L
  26.                         {$ K% I, u/ C2 b8 x
  27.                                 DMA_ClearFlag(DMA1_FLAG_TC4);//清除通道4传输完成标志  c/ }: D9 ^5 M) l
  28.                                 break;
    1 f% |8 b: H5 w3 ?8 F% t7 q
  29.                         }
    , S# v  |7 K5 a" e, _/ J8 P
  30.                 pro=DMA_GetCurrDataCounter(DMA1_Channel4);//得到当前还剩余多少个数据6 v$ O6 O. ?' ^6 X1 ~
  31.                         pro=1-pro/SEND_BUF_SIZE;//得到百分比          ( Q, |3 Z/ w5 u7 ^  t# e
  32.                         pro*=100;      //扩大100倍) M! X7 w3 k; c. Q) R# m( e
  33.                 }                           , z: d# d( B6 \  u; T
  34.                 dealy_ms(500);$ c/ s4 t% U* N
  35.         }
    4 Y4 G0 D& [- @9 s
  36. }
复制代码

5 b, d1 d2 ]/ S7 w# P5 j! i, Q& X- A6 q0 o$ Q; b9 P8 Z
% C& {% t% _5 v* L9 M
收藏 评论0 发布时间:2022-5-16 11:19

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版