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

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

[复制链接]
STMCU小助手 发布时间:2022-5-16 11:19
1、什么是DMA2 {! L. D+ G% G& E/ |4 Z$ F
DMA全称是Direct Memory Access,即是直接存储器访问。 DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路,能使CPU的效率大为提高。
: ?4 n  w1 e' Q8 g6 G: C5 @1 Z2 @* }& f
2、DMA特性$ O4 T% A& _+ n
● STM32F1有12个独立的可配置的通道(请求):DMA1有7个通道,DMA2有5个通道
. J6 y; e2 M2 E% S1 @, d
! o; n% W0 \! Y● 每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。可通过软件来配置。
4 b: V( r4 M& d2 _, P% ?5 S) d% F  D% l% Q, R  Y+ o1 V
● 在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、, k3 K1 D4 V+ Q+ I1 S2 h, I* e

5 P$ O+ f$ s! d) [9 }中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推) 。
4 L# p; Z2 m$ e$ {3 j/ |7 K
/ g5 t) a, v3 Y) h0 o, x7 v● 独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。! {2 N/ e2 F3 ^9 {0 |' b- t
5 n$ }2 @3 M5 y
● 支持循环的缓冲器管理
# I9 `4 d! K6 C6 w5 M, `" Q
# W+ ]8 \! P% F& u● 每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。6 O, q; Z$ H7 U3 ?; [

0 y% y7 N1 W& ^● 存储器和存储器间的传输
: v) w8 T0 ?# [/ M' f, V& {
" x, e( D! ?* v0 H( q9 ?& u0 F● 外设和存储器、存储器和外设之间的传输6 t4 ~  W* q- V+ ?+ _) k6 V% |

8 y. V$ |$ K/ W6 m● 闪存、SRAM、外设的SRAM、APB1、APB2和AHB外设均可作为访问的源和目标。% _" q) B% ^2 D& @( N0 \
/ j9 ]! c8 E) M; y3 R' O7 i) Y
● 可编程的数据传输数目:最大为65535; \9 |9 X3 f9 f9 x2 o
2 d4 D5 P- s" `9 H
3、请求映射表
- r9 {$ f! C" f" S8 \: k% A' V两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。' M% s5 H5 i" B
/ c* ~, o) c; B3 u
3.1、DMA1请求映射表
9 h) h2 W* Z- ?  E/ Y0 A* O$ X  r3 m0 W5 h
Y8GPL}G%LHW~FCL$XUG8K3I.png ; n% n# O' _+ V' \9 q
) i; ~4 k/ S- I: ^1 B' Q% ^! e) @
3.2、DMA2请求映射表

& J( M: a! @& b  ^" {3 Q
3 H% h" }& `6 E: I JP~~Y2J]{L01}}[T62BMC8P.png
' H; w- \7 ]7 X8 L7 {- {7 ^
  z  g6 p  Y4 i# {! }) x4、软件实现
  `- n1 K7 k+ D我这里使用USART3和DMA来实现。通过上表可以知道,USART3_TX和SART3_RX分别对应的是DMA1的通道2和通道3,首先就是对USART3和DMA进行初始化# c6 l* r/ B6 ?

2 e3 K  j# S) m4.1 串口的初始化! a1 b: S; E: o  Y; c  W6 y
串口有单独讲解,可自行搜索参考,这里不再讲解。
% V1 t7 ~# d4 t" b) v) ~) T- e+ s) ~, R- o6 }
  1. void uart3_init(u32 bound)" F; y; `" O' H( R/ G: ~
  2. {
    9 |# u2 T- ^) I7 k9 k, W. b: U
  3.         //GPIO端口设置8 g! I- r9 ]9 G7 P, ?/ P7 u( G
  4.         GPIO_InitTypeDef GPIO_InitStructure;
    , {5 Y1 w  a- H# y: K- x" f7 ]$ H
  5.         USART_InitTypeDef USART_InitStructure;
    , N! v3 E; Y* ~3 \7 u, v) j/ h% a* T
  6.         NVIC_InitTypeDef NVIC_InitStructure;. _0 i% m, Q3 d( d0 }
  7.         //使能USART3,GPIOB时钟
    4 L! o7 Y2 n8 h
  8.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    * P$ z- y# y+ u( [
  9.         % r5 S* P, N9 E4 c$ u
  10.           //USART3_TX   GPIOB10
    3 S" P2 F! V3 }/ Q
  11.           GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //P010
    ) g; J* w4 O' ]3 X% p9 f& t, e
  12.           GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    9 A) E. h5 J- ~- X" x
  13.           GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //复用推挽输出8 t+ O4 F$ ~6 X% a
  14.           GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB10
    / U: m! M" Y! i2 e5 r0 m: x# t
  15. 0 c9 R4 m  k/ _7 l, L# A5 \
  16.           //USART3_RX          GPIOB11初始化
    0 _1 c' J/ P# {% s0 u) d# d) Y( ?8 ?
  17.           GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;//PB11: B" m+ D6 @/ h, [. f9 C6 c
  18.           GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    ! l0 q3 H: ]3 v- `1 y: M( U
  19.           GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB11  $ K2 T! L1 }3 f

  20.   `3 {! o; }  M- P3 L
  21.           //Usart3 NVIC 配置2 c& P: H  {' @9 f
  22.           NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
    5 c$ q; e& `1 Z) T  T
  23.           NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级38 B: H" _; A5 J' E4 Y: `
  24.           NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                //子优先级3
    % B! `! w+ }7 z. I
  25.           NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能
    - v& N4 `. B4 e" f, J
  26.           NVIC_Init(&NVIC_InitStructure);        //根据指定的参数初始化VIC寄存器& g- n3 \/ F! f$ B7 A8 R
  27. 3 x8 o9 \' a/ B. X( I3 P; ~) f
  28.           //USART 初始化设置
    % c2 Z, d6 e. R; R7 U5 q0 ?
  29.           USART_InitStructure.USART_BaudRate = bound;//串口波特率% k) ?1 B5 r" X$ Q. }$ a* c
  30. //字长为8位数据格式
    . P; r; @* z: U. E1 o) [. x& A
  31.         USART_InitStructure.USART_WordLength = USART_WordLength_8b;                 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
    & A2 H0 v+ R3 L) `# Q/ v: E1 X
  32.           USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位* i; H& J7 W* u1 @9 k9 ?
  33.           USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制/ x  h4 y1 ]' G8 J( G9 A
  34.           USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    6 ]7 x. e* V# {2 Q
  35.           USART_Init(USART3, &USART_InitStructure); //初始化串口31 A. k1 S$ q3 _) m  J7 U
  36.           USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启串口接受中断3 u5 s; s! G9 Z
  37.           USART_Cmd(USART3, ENABLE);                    //使能串口3
    + L& l9 E; Z6 ^4 R& c/ t0 @1 V( U
  38. }
复制代码

4 }( a- t! R* W2 ]0 ?" \& |/ ^  f4.2 DMA初始化
- o2 ]# ^" k  Z$ Y) n# Z1)使能 DMA 时钟6 _6 p+ k% _2 S8 ^( o: S2 k  b- z
  1. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能 DMA 时钟
复制代码
2 A% G! n& d0 [1 j3 q
2)初始化 DMA 通道 4 参数/ y. W7 K9 S- z7 O3 a' M
  1. void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
复制代码

' x, v0 ^! n1 n; o+ P! m! Q+ ~7 K' X跟其他外设一样,同样是通过初始化结构体成员变量值来达到初始化的目的,DMA_InitTypeDef 结构体的定义如下:
" R; g, H) O( l  {" E/ z/ v$ F5 R6 a
5 K; H/ }" @, y1 g" J: ]
  1. typedef struct
    8 a7 n. d) N2 q- n, c" d+ x
  2. {
    * ^3 [$ A5 |8 H, n1 q1 x
  3.     //来设置 DMA 传输的外设基地址,比如串口3,表示方法为 &USART1->DR,也就是外设基地址为串口3的接受发送数据存储器DR的地址! E  m" \9 _  }- k$ T0 w( N
  4.     uint32_t DMA_PeripheralBaseAddr;
    3 C0 I/ z8 x" J) H
  5.     //内存基地址,也就是我们存放DMA传输数据的内存地址
    + Q* O8 S% v+ i% `2 [: ]) d6 l' z) N/ \" }
  6.     uint32_t DMA_MemoryBaseAddr;
    , o- ~! {1 m- R% P
  7.     //置数据传输方向,可以是外设读取数据到内存,也可以是从内存读取数据发送到外设
    4 r5 z/ d/ \2 W+ b# J/ n) D
  8.     uint32_t DMA_DIR;
    - D; B* L: H% K6 |9 m
  9.     uint32_t DMA_BufferSize;//设置一次传输数据量的大小,也就是缓存大小
    9 l+ @" O1 |5 t, C
  10.     uint32_t DMA_PeripheralInc;//设置传输数据的时候外设地址是不变还是递增
    2 b% f. T; T2 ~( Z' y4 m! S
  11.     uint32_t DMA_MemoryInc;//传输数据时候内存地址是否递增
    / m7 W" A% T- K+ K+ [
  12.     uint32_t DMA_PeripheralDataSize;//设置外设的数据宽度,8bit,16bit,还是36bit ) U' L4 f: T+ n4 i, z
  13.     uint32_t DMA_MemoryDataSize;//设置内存的数据长度) _# V' r$ Q) y
  14.     uint32_t DMA_Mode;//设置DMA模式是否循环采集 ' ^5 S( f$ h; H+ E) B
  15.     uint32_t DMA_Priority;//设置DMA通道的优先级   o% G6 o6 E9 v/ |8 [
  16.     uint32_t DMA_M2M;//设置是否是存储器到存储器模式传输 1 ]" A3 I  y7 o' C' t- p
  17. }DMA_InitTypeDef;
复制代码

9 f# r7 j! L" l+ L所以,使用DMA发送的初始化代码如下:
! p; B) Y# z& X& c8 \7 T
$ N8 W  e# y' \9 i" j
  1. void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr). u& F. I  ~  r0 `+ @; F
  2. {
    7 p1 h1 F6 f9 ?! L8 i
  3.          RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);        //使能DMA传输
    # @1 a0 ]  v) |2 X
  4.           DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值# V) v- q6 \* `' |
  5.         DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  //DMA外设基地址: a: A) c' }6 m' L
  6.         DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  //DMA内存基地址
    - \2 K$ [( w+ h5 s" t& y
  7.         DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  //数据传输方向,从内存读取发送到外设
    + }5 G. `" Q; h9 Z( `4 g' G3 G
  8.         DMA_InitStructure.DMA_BufferSize = cndtr;  //DMA通道的DMA缓存的大小- t& ~8 c0 {" O# b
  9.         DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变; r7 N9 P7 _4 S2 i
  10.         DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增8 V; \. ^5 ^0 ]  ~4 Z9 s  f
  11.         DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度为8位
    7 g( t8 z7 k4 Y, P3 |+ c
  12.         DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
    + ~! @$ O" o5 n6 d  A, q! y! E
  13.         DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常模式) h% Y: R+ \7 F2 P- F" r7 F2 ~
  14.         DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级
    0 F$ E6 L" r. p
  15.         DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
    : x7 ?& K4 t; f4 t4 p3 S
  16.         DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道所标识的寄存器
    8 i7 K9 V( ?! F$ Q
  17. }% S' K+ T, ?  o4 f+ w6 w) V5 J. K5 A" Z

  18. 8 J( H3 ^5 v5 B( X
  19. //因为使用的是正常模式,所以在每次使用DMS传输时都需要使能一下DMA+ H( V( w& A, M, m
  20. //开启一次DMA传输$ n7 _4 \$ H8 E; p7 C
  21. void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
    1 P% l6 I) b( s# \: t
  22. { 0 S# C0 ]" M8 G) o5 G
  23.         DMA_Cmd(DMA_CHx, DISABLE );  //关闭USART1 TX DMA1 所指示的通道      & e$ U, t: z8 s2 M( R2 O
  24.          DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小
    + b  G% {7 Z7 A9 A3 i( o
  25.          DMA_Cmd(DMA_CHx, ENABLE);  //使能USART1 TX DMA1 所指示的通道
    ) ^* l9 @% ]! S% v
  26. }
复制代码

8 i. ]* ~' S+ {$ a% l/ G* e7 \因为是使用的串口,所以还要开启串口的DMA发送功能* x' |' K2 X3 {) f

& w- s' H2 H0 R: YUSART_DMACmd(USART3,USART_DMAReq_Tx,ENABLE);//开启串口的DMA发送功能
- f- Z! Y9 A& a0 }8 _' p7 {总结:也就是MCU内部有一条总线,专门用来提供给DMA使用,如果不使用DMA,串口有数据来,CPU要从串口的数据寄存器把数据搬运到内存(也就是我们定义的buff),如果使用了DMA,数据就可以直接从串口的数据寄存器到内存,也就不关CPU的事了。9 C( x1 z! e" ]2 w1 z3 W

/ a2 j$ Y+ o7 m/ c. E4.3 主函数
! x! G0 o- f  M0 F, ~主函数如下
0 t; E: X5 ~. y
  1. //发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍.. ?% [; a6 |4 `2 W* g
  2. #define SEND_BUF_SIZE 8200        
    7 q6 ?+ _' ]! j! R6 J3 Z+ q
  3. u8 SendBuff[SEND_BUF_SIZE];        //发送数据缓冲区/ m1 z8 x" L% o/ E. J5 `1 _
  4. const u8 TEXT_TO_SEND[]={"STM32F1 DMA 串口实验"};
    : X6 @& h) }7 b4 a# J: b
  5. 1 K$ \1 Y8 Z( F! G2 _( T7 D
  6. int main(void)
      O" q7 C9 f# H8 I! f( n7 P6 V
  7. {         
    ; f. c1 T8 n& J+ h, L- b7 {- w  ^* |
  8.         float pro=0;//进度
    8 d/ G& m! p9 G1 W
  9.         delay_init();                     //延时函数初始化         
    ( y+ O* N0 P6 T8 {% F
  10. //设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
    " k$ N2 T2 k: ^7 k) c

  11. , [' _' t' ]6 Y& Q5 v+ G
  12.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);        # x% Q: }& @$ y) a' Y
  13.         uart_init(115200);//串口初始化为115200; c2 {' x* P- ?
  14.         MYDMA_Config(DMA1_Channel2,(u32)&USART3->DR,(u32)SendBuff,SEND_BUF_SIZE);//DMA1通道2,外设为串口3,存储器为SendBuff,长度SEND_BUF_SIZE.! {3 B7 D0 w. E: N
  15.         
    2 a- g) G% i' |* k
  16.         while(1). b0 L( `  v& R! W
  17.         {        1 Y+ d" J8 q' w6 [3 ^* ^
  18.                 printf("\r\nDMA DATA:\r\n");             ; F( p/ a" P0 G, h. Z: M, q, H" {; @
  19.                 USART_DMACmd(USART3,USART_DMAReq_Tx,ENABLE); //使能串口1的DMA发送      # j( n6 t* S1 X8 z+ Y4 Z( }- n* F
  20.                 MYDMA_Enable(DMA1_Channel4);//开始一次DMA传输!          7 Y! q+ N* y: Y6 ]0 T: W" I8 V
  21.                 //等待DMA传输完成,此时我们来做另外一些事,点灯
    7 K* [0 Q2 d0 x8 ^
  22.                 //实际应用中,传输数据期间,可以执行另外的任务3 Y2 z2 z+ l) q( b# l
  23.                 while(1)
    : z' `" n6 N1 w# R
  24.                 {
    ) O: P6 O2 ]( `) |8 [* H
  25.                         if(DMA_GetFlagStatus(DMA1_FLAG_TC4)!=RESET)        //判断通道4传输完成3 I2 K, Y( l( l7 o4 {
  26.                         {& j1 F' s6 }- A4 S7 Q
  27.                                 DMA_ClearFlag(DMA1_FLAG_TC4);//清除通道4传输完成标志
    . Z" `) v9 z+ I( z
  28.                                 break;
    9 Z9 C- s9 k2 l: ]" d6 i; i% O# [
  29.                         }9 _3 ^. _  q: l# Z
  30.                 pro=DMA_GetCurrDataCounter(DMA1_Channel4);//得到当前还剩余多少个数据
    5 e' [# |' a# ^
  31.                         pro=1-pro/SEND_BUF_SIZE;//得到百分比         
      t6 u0 M& R8 R# h
  32.                         pro*=100;      //扩大100倍" _$ c/ N& ?* u/ F& E! y
  33.                 }                           
    ) R' H6 R9 c7 a8 J* _
  34.                 dealy_ms(500);  E  p+ h: v2 {7 f# W5 F
  35.         }
    : t3 Q3 R, V9 i& k5 M" q- ~( f7 M" R
  36. }
复制代码

) B8 u- G; A# l  W3 K8 N
) O# \0 C& U( y! T  B& N
. |* Y; H% y9 x# P0 |1 M
收藏 评论0 发布时间:2022-5-16 11:19

举报

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