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

【经验分享】STM32之DMA实例

[复制链接]
STMCU小助手 发布时间:2022-2-1 18:00
DMA简介:
  DMA(Direct Memory Access,直接存储器存取),是一种可以减轻CPU工作量的数据存取方式,如今被广泛的使用。它在传输数据的同时,CPU可以做其他事,比如数据运算或者响应中断等,DMA就给CPU分担了不少的工作量!
DMA工作分析:

5 c; A2 K! Q8 i
20170713211811342.png
                                                            : 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:
  1. #include <stdio.h>
    ) c$ p3 F) v9 C9 T6 N0 v) h9 \( X8 E
  2. uint8_t sendbuff[500];
    : a2 n& x: j- W* a
  3. uint16_t i;% q5 [0 e4 _" m: w
  4. int main()
    3 i- ]' x% q( ~- u, {  m% y
  5. {
    8 Y- x" b" l# q- h: p) h+ G
  6. printf_init();" |$ A9 x- l) E5 S* X3 b& R1 ?' d
  7. dma_init();
    + R9 z8 x" K! W* V
  8. for(i=0;i<500;i++)
    # ~3 b. D- J0 h+ l7 m. N% K
  9. {0 f+ d$ |. q$ ^. ]9 |* a" t
  10. sendbuff[i] =0xaf;
    / f% Q7 _& I3 ?$ v3 H
  11. }
    ( Q: j2 L( m& c0 A' B  ^0 V
  12. USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);9 N4 l9 h& W  Q# q. N8 u& K
  13. LED3_ON;
    # L# |. g8 h7 M' x

  14. , A7 I5 ^- p( n2 J1 u) o3 I
  15. while(1);
复制代码
. m6 d; F8 o2 C1 U& M; v
这个主函数实现的功能是利用DMA把数据(数组,数组里面的存放了500个AF字符)从内存转移到外设(串口),最后通过串口传输到我们的PC上显示。为了证明DMA在搬运数据的同时,CPU还可以做其他事,于是将LED3点亮,来测试一下。主函数至于为啥加while(1),才会产生中断?还不明白。。。
DMA配置dma.c:
  1. #include "stm32f10x.h"
    ; b& F) J7 h- U
  2. #include "stm32f10x_rcc.h"
    ! I* o6 @/ M# q* S# }5 Z' o, f  a- m7 s
  3. #include "stm32f10x_usart.h"
    $ e- z! X5 S/ U* _& w6 `
  4. #include "stm32f10x_dma.h"+ ~$ R8 i2 }  `% _! t; i
  5. #include "misc.h"
    + \+ t. `6 `3 l% s0 F# k
  6. #include "dma.h"
    ; I3 L& Z- z7 A$ k+ t+ b
  7. void dma_init()  p( S% e  o3 z+ K- _
  8. {
    " u% x( r  R6 l4 J

  9. " q# d$ q! f- o
  10. DMA_InitTypeDef DMA_InitStructure;0 |) C& J! a2 b1 U" w& t& K. d' m1 K
  11. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
    ; x7 Z: r9 b* f4 I8 ~' M
  12. NVIC_Config();) ?( j; s- b7 g1 m( N
  13. /*DMA配置*/7 [' i8 U8 O8 f% q  ^, f7 {
  14. DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;//串口数据寄存器地址
    . M7 u7 X' h; z5 \2 M8 U
  15. DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)sendbuff; //内存地址(要传输的变量的指针)7 j; G& N( L2 s
  16. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //方向(从内存到外设)& t. `0 @& c& |% Q) y
  17. DMA_InitStructure.DMA_BufferSize = 500; //传输内容的大小
    7 I- g( G7 Z; [- u. Z
  18. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不增2 q& @1 d# L) b% k# e
  19. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址自增
    2 E. N" O; c* A" N. H7 z
  20. DMA_InitStructure.DMA_PeripheralDataSize =
    * W/ V# J. K, @0 m0 b
  21. DMA_PeripheralDataSize_Byte ; //外设数据单位7 S* S/ U  h+ s- i
  22. DMA_InitStructure.DMA_MemoryDataSize =$ N# K0 a: k, i6 Y: i" H
  23. DMA_MemoryDataSize_Byte ; //内存数据单位
    . S  Y% B4 J  F4 s4 N4 I9 B. u9 U
  24. DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ; //DMA模式:一次传输,循环" p( ]  p- R+ [) [* b
  25. DMA_InitStructure.DMA_Priority = DMA_Priority_Medium ; //优先级:高
    , H; |5 {$ P: e4 \. Z; W
  26. DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止内存到内存的传输7 D& y0 W/ j6 X% o/ G# A4 R4 a( }

  27. 3 R3 A, s" n* u8 k; X
  28. DMA_Init(DMA1_Channel4, &DMA_InitStructure); //配置DMA1的4通道
    + x1 w' S+ X/ I. ]  l
  29. DMA_Cmd(DMA1_Channel4,ENABLE);
    - _+ a- m; ]$ }6 J  \6 E, _: |
  30. DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);//配置DMA发送完成后产生中断) K& q1 }, w0 i6 M. S4 d
  31. ( K% e4 W3 g& J+ u/ X# A, j
  32. }( h5 ?9 }4 n5 ?( A7 z5 G

  33. 9 F; e- [) ^* a3 Y" y
  34. static void NVIC_Config(void)
    4 {: U) ]3 h7 |) _- z8 O
  35. {1 M+ |% s; M% ~7 ?+ t/ [
  36. NVIC_InitTypeDef NVIC_InitStructure;+ X4 m. Q$ H3 _* N! L# A
  37. /*中断配置*/' x# t& ~$ z, {4 }  i. Z' h9 F
  38. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);. Y- p- e' w6 G4 a3 V3 e

  39. & @+ }5 P3 ~/ c1 ^1 I% v- ?
  40. NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
    # z3 w4 u  X/ d" Y8 `6 t, X
  41. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    5 z  v( E# L' q1 |9 h
  42. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;5 f: q; `2 J  Z( [* v% c
  43. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    $ }& F0 p9 `, W0 R
  44. NVIC_Init(&NVIC_InitStructure);
    - K* ]- M8 [/ ~9 L. w. h3 |! Q
  45. }
复制代码

& 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
20170714101254287.png
20170714101311174.png

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
20170714101332872.png

; t" f# F6 `. q2 L3 A9 E

" I7 {8 h) Z, R, U
至于在配置DMA1通道时,那里为啥是通道4,而不是其它通道,请看下图:

( y' X& m: t, u0 \8 g1 L
20170714101852288.png
% g, a6 Z! H$ R  k$ V! Q
! x: i- E8 l& E' Q' a
dma.h:
  1. #ifndef _dma_H
    1 Q2 x8 Y( W6 X, R- j0 ?
  2. #define _dma_H
    / A0 D, |  ^- w4 J# k9 S
  3. #include "stm32f10x.h"  W0 h/ x4 |" c8 t
  4. #define USART1_DR_Base 0x40013804;* x- E/ X/ Q& e
  5. extern uint8_t sendbuff[500];1 h* j% D$ Z: Q
  6. static void NVIC_Config(void);
    ) X/ P1 S3 S6 l3 o
  7. void dma_init(void);
    3 j! k  N4 T" P2 `
  8. ( _5 n8 `. l  @$ ]2 q! m( y
  9. #endif
复制代码

, M  g+ a! G1 G' S' |
串口配置:printf.c
  1. #include "printf.h"
    8 l" q( L6 d, d& a' v* E7 s/ E( E
  2. #include "stm32f10x.h"
    9 _9 |$ b6 P, \. [
  3. #include "stm32f10x_rcc.h"
    & `' p( @$ g% t0 L0 L
  4. #include "stm32f10x_gpio.h"
    $ Q1 T# l: Y, q- g+ _/ |
  5. #include "stm32f10x_usart.h"2 H& G: P: ]# P7 F  A2 C
  6. #include "misc.h"
    6 j6 X0 k' f1 O8 E8 I
  7. 0 R" \( j' p' U& j$ z
  8. int fputc(int ch,FILE *f)
      z# ?8 H6 l" L; r( I
  9. {
    0 {9 W& v2 N, L$ W
  10. while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);' p5 e' J6 v- k0 t% z. m; R& Z
  11. USART_SendData(USART1,(unsigned char)ch);
    ! K/ v3 `/ \2 _, ^, R8 ^7 T
  12. while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
    ) f$ W" y1 Q& I! R! A
  13. return (ch);
    2 E) }* M7 I8 G
  14. }
    ! d% Q7 u9 j3 }! }* I* [* n0 X

  15. ' b3 \8 R5 v0 h6 ?$ U1 c! L% K
  16. void printf_init(void)  y+ ~7 q& l7 r; v2 T$ E. \9 }
  17. {9 \6 X# ^9 i4 Y, Z- g+ Q
  18. GPIO_InitTypeDef GPIO_InitStructure;# @* c9 z% ]- i
  19. USART_InitTypeDef USART_InitStructure;' x: Q! R0 }7 c) o$ U
  20. 9 s2 L6 b* A% B6 O6 A
  21. /*config USART clock*/- M2 B/ k- v$ k% B$ w$ ]) q2 e
  22. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);+ e5 A+ X/ Q" Y7 o; E3 k
  23. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
    % X8 n* i8 i  x4 ~! w- N+ a) d9 _
  24. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
    0 @8 n7 J  u4 r
  25. /*USART1 GPIO config*/
    0 l" T) k& I- r7 ~1 n/ D
  26. GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9;1 \2 a# j! T' i& G! U# A( {' ]& ~$ E9 U
  27. GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP; //复用推挽输出4 y1 V& S" u6 [, F
  28. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;! R$ S  M9 Y, Y( e3 U% e
  29. GPIO_Init(GPIOA,&GPIO_InitStructure);
    5 m4 r* u8 E6 K( t+ T
  30. 1 Y6 r+ }9 R  \+ Q: l
  31. GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10;
    # m" ]4 Q! K  |) h$ _
  32. GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING; //复用开漏输入. j0 r9 ~- i$ G! L1 f1 B* Y. d
  33. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;, K, F4 q+ ~) c6 o) ]
  34. GPIO_Init(GPIOA,&GPIO_InitStructure);
    - |$ D  u/ c! r' G2 @4 _& n% i

  35. 9 `1 R, a. U# ?2 |/ e
  36. GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;) w4 n- g6 F1 z* M
  37. GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;$ j% w, ^& ]  t, H
  38. GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;* O- A; B" I( X! {, D, P- x
  39. GPIO_Init(GPIOD,&GPIO_InitStructure);
      E% g3 s( ~: `% t& o
  40. /*USART1 mode Config*/
    / d0 F& |9 C2 t7 [3 D8 H
  41. USART_InitStructure.USART_BaudRate = 9600;, j+ K& X0 m' L6 _/ z% o: B2 X
  42. USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    8 P% o9 V2 c) S5 ~
  43. USART_InitStructure.USART_StopBits = USART_StopBits_1;5 n$ s* C0 M. N
  44. USART_InitStructure.USART_Parity = USART_Parity_No;0 N/ [0 K% d% v. a6 Q& `0 X$ P8 m9 ?
  45. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    , M; T' Z1 s3 x8 i
  46. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    & o! x" h, @  M$ R# |
  47. USART_Init(USART1,&USART_InitStructure);
    / `3 `$ M) H9 C4 ^5 Y9 h4 Q$ d
  48. USART_Cmd(USART1,ENABLE);
复制代码
) f; {- Z- _; {  Q1 g+ ~* `, ~7 m
printf.h:
  1. #ifndef __printf_H
    . _8 _$ |/ S% [2 q7 Z3 E
  2. #define __printf_H
    5 h+ r1 @4 V9 e/ B3 i9 E0 R
  3. 5 k6 F' v' a) t' ]& d4 n
  4. #include "stm32f10x.h"
    # a! ~( `) z3 m" t; ^
  5. #include <stdio.h>
    ! Q4 e8 ?4 z: H/ R3 q7 G
  6. #define LED3_ON GPIO_SetBits(GPIOD,GPIO_Pin_3)6 P8 e' c( Q) D5 f& H5 V. P! \% C
  7. #define LED3_OFF GPIO_ResetBits(GPIOD,GPIO_Pin_3), |: E- E3 y5 A0 r9 @- J9 A
  8. void printf_init(void);3 n$ G) f1 J, ^7 k' N# q# s. q; Z0 R
  9. int fputc(int ch,FILE *f);
    : L1 z& b) s) _, ]8 g5 i: L

  10. & Y1 {% o1 u7 x* e% i
  11. #endif
复制代码

5 y# _) M% Q; }9 q5 D" ?
中断代码:
  1. #include "stm32f10x_it.h"  u6 Y+ K6 U! u/ ~' r- x% e1 ]
  2. #include "stm32f10x.h"$ g% M+ Z8 ^8 b
  3. #include "printf.h", k, r; t7 y, O- W
  4. void DMA1_Channel4_IRQHandler(void)
    , }: ^8 Y4 S* [+ n- J
  5. {( t9 X+ y2 L- T. e; p1 q) F; c
  6. if(DMA_GetFlagStatus(DMA1_FLAG_TC4)==SET)
    * N! I* G2 D8 g( z
  7. {
    3 E$ Q# v& @) }4 l
  8. LED3_OFF;% C% Z8 d1 H! B: M
  9. DMA_ClearFlag(DMA1_FLAG_TC4);
    7 w2 R$ L9 W8 U- {: u+ U7 o4 p
  10. }
    ; S2 b4 B; H. k  `' X( K# g
  11. }
复制代码
  _+ H6 t9 f. V' P4 x, S! N* E
效果图:
20170713220118597.png
/ R3 Z, W+ j6 [0 {: x+ v+ W
, @+ C0 U( o" d% `  v/ `7 b
赞 收藏 评论0 发布时间:2022-2-1 18:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版