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

【经验分享】STM32之DMA实例

[复制链接]
STMCU小助手 发布时间:2022-2-1 18:00
DMA简介:
  DMA(Direct Memory Access,直接存储器存取),是一种可以减轻CPU工作量的数据存取方式,如今被广泛的使用。它在传输数据的同时,CPU可以做其他事,比如数据运算或者响应中断等,DMA就给CPU分担了不少的工作量!
DMA工作分析:
9 z7 a$ c+ x7 @* a. t% x
20170713211811342.png
                                                            $ 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:
  1. #include <stdio.h>: Q- V/ m, }; k+ r& B1 ~
  2. uint8_t sendbuff[500];
    . ^: e- p3 r) q3 ^. ?9 b$ t. M
  3. uint16_t i;9 Q" x! y1 _& I+ a
  4. int main()5 e9 }) g! ?% C
  5. {+ }; e# w  Y8 i
  6. printf_init();
    $ I' W( ]) h( ^2 i- P3 B& n
  7. dma_init();
      a9 J3 N) i+ s3 s7 `" A4 v; l
  8. for(i=0;i<500;i++)' G( x( s7 q! \& k: g3 o
  9. {8 ?4 C* L9 ?3 b( C( U" d
  10. sendbuff[i] =0xaf;
    4 _3 J' F3 U2 J& @# w
  11. }& {* d. j# n$ A7 L8 ?" c
  12. USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);8 \8 V. p# D/ {0 e0 b9 V$ i8 s
  13. LED3_ON;. n( c1 r6 [7 n/ [

  14. . [3 u/ [- c1 e0 |
  15. while(1);
复制代码

% \& }/ o; W7 W/ L- X' c0 b4 L
这个主函数实现的功能是利用DMA把数据(数组,数组里面的存放了500个AF字符)从内存转移到外设(串口),最后通过串口传输到我们的PC上显示。为了证明DMA在搬运数据的同时,CPU还可以做其他事,于是将LED3点亮,来测试一下。主函数至于为啥加while(1),才会产生中断?还不明白。。。
DMA配置dma.c:
  1. #include "stm32f10x.h", Z7 Q5 w- H6 |
  2. #include "stm32f10x_rcc.h"
    - S6 }. N8 w! o. {
  3. #include "stm32f10x_usart.h". l4 G$ O- f4 n9 G$ b# C: I
  4. #include "stm32f10x_dma.h"
    1 s8 `' S# q2 |2 ^
  5. #include "misc.h"" c; w; _. h+ D6 v" g+ l! W% c
  6. #include "dma.h"
    4 q% A% ]9 x8 {" P1 Q+ Z
  7. void dma_init()  H6 [3 F5 B! H: z5 ?6 ]  d; D
  8. {* O: e/ L* E* }( |# t; U# T
  9. % b" n. i0 Z  s: M  X1 u+ q" p
  10. DMA_InitTypeDef DMA_InitStructure;1 J; M/ |& M0 s1 D7 s3 j& l. i
  11. RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
    6 @* R( G$ B# H
  12. NVIC_Config();( n# m* y* W4 Y3 O+ d! g: G( k
  13. /*DMA配置*/. p# w# O' C/ W- b. H- y, ^
  14. DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;//串口数据寄存器地址2 Y. Z& S! u6 x$ y8 v
  15. DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)sendbuff; //内存地址(要传输的变量的指针)
    $ U0 l3 D0 q1 u2 }3 u1 O9 K
  16. DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //方向(从内存到外设)
    ; ]( ^( S& U& b9 X( @* G, @" z2 k- k
  17. DMA_InitStructure.DMA_BufferSize = 500; //传输内容的大小
    8 f4 @) O, a" L2 E- h* p
  18. DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不增
    $ K1 I& x9 v0 B- O% D9 y
  19. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址自增- i/ X( J( z( g/ ]9 F) G- B! Q
  20. DMA_InitStructure.DMA_PeripheralDataSize =1 w; X% }9 D) k$ ]+ m2 x$ B+ U
  21. DMA_PeripheralDataSize_Byte ; //外设数据单位
    " ~+ u. S% `6 c1 T
  22. DMA_InitStructure.DMA_MemoryDataSize =2 |) z4 A( r& V  ]
  23. DMA_MemoryDataSize_Byte ; //内存数据单位. k# @& c6 U$ D0 F
  24. DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ; //DMA模式:一次传输,循环0 ?! E3 o9 S8 @9 S' |
  25. DMA_InitStructure.DMA_Priority = DMA_Priority_Medium ; //优先级:高
    # v' L- E4 b# f( A' }, ~: ~: L
  26. DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止内存到内存的传输2 m- b6 G2 L' J2 E! ?
  27. 0 ~+ Z' b0 r. ]* E* L+ N7 z
  28. DMA_Init(DMA1_Channel4, &DMA_InitStructure); //配置DMA1的4通道
    # s7 E- w  c5 q; x  n) Q4 Z5 G
  29. DMA_Cmd(DMA1_Channel4,ENABLE);
      D* E* A  W9 o8 |2 s9 k( {! _
  30. DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);//配置DMA发送完成后产生中断; x8 O+ U, Q; ?3 X

  31. * P# w0 I3 T% w& I
  32. }
    : ^1 I$ @7 e- G, m  ~
  33. : L3 G  N# a! _
  34. static void NVIC_Config(void)
    ) [' s8 q8 |3 t' X5 ^
  35. {/ \8 Z) V- f/ L3 d
  36. NVIC_InitTypeDef NVIC_InitStructure;9 `! @$ m7 a! |0 c' D+ {
  37. /*中断配置*/* Y4 p$ D3 Q# ^+ F
  38. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    9 F; k' [0 s3 e  I

  39. , Z! W% v4 j1 }( [5 {+ R
  40. NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;* J# z, c; P" u- h
  41. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;9 I$ X- d. Z6 R9 |. \! y* m
  42. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    3 j( a6 F, M, F* h$ s( k' R" h3 h
  43. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    % `, R% E/ p/ a" f
  44. NVIC_Init(&NVIC_InitStructure);  [: Z; U4 B8 c% U& c
  45. }
复制代码
( 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 |% ~
20170714101254287.png
20170714101311174.png
) 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
20170714101332872.png

8 l) S! ~' v& {% J, @- X  Y5 f

2 ~( }; _. g2 E/ W9 p% C, _
至于在配置DMA1通道时,那里为啥是通道4,而不是其它通道,请看下图:

, _. e! r# a1 a( k5 P5 v8 Z
20170714101852288.png
" ~' [+ f; F; h! R) ~

) B7 ^: Y  @3 S9 Y
dma.h:
  1. #ifndef _dma_H
    " ~7 Q) ~+ N) p; \& S1 y
  2. #define _dma_H
    ; [4 i0 ~: G8 x
  3. #include "stm32f10x.h"
    ( Q5 K# t/ s6 k
  4. #define USART1_DR_Base 0x40013804;$ M( W! [+ E8 D* s( E) z
  5. extern uint8_t sendbuff[500];. H; M. T1 s+ [/ k3 ^. S/ J+ g
  6. static void NVIC_Config(void);
    5 c1 N) A) e- J" M7 j% v
  7. void dma_init(void);
    % I  z$ A, N* X/ T) o

  8. 7 |- R& [, c& a- w, D5 @( |' x; p
  9. #endif
复制代码
+ U7 @& @2 y! O& P0 `1 r0 @
串口配置:printf.c
  1. #include "printf.h"! g7 g( V# V) v' j3 k  ]8 S
  2. #include "stm32f10x.h"
    4 j0 |, S5 r) i( R! Q& R
  3. #include "stm32f10x_rcc.h"2 e( v& |3 J2 ]0 `, @: Q3 e
  4. #include "stm32f10x_gpio.h"0 M0 I4 y. U0 e& f9 h9 ^/ N
  5. #include "stm32f10x_usart.h"
    + g. z, M. }/ E2 p* M* e
  6. #include "misc.h"
    : A5 z6 k0 {. j8 U

  7. / x8 X: Q3 f) ^9 y& Y* a
  8. int fputc(int ch,FILE *f)
    % Q, [) P" v9 f5 ?3 A; N* [$ C
  9. {
    * Z6 s. r3 I& N
  10. while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
    % S8 [% ]$ G% Q6 s9 S( D( T
  11. USART_SendData(USART1,(unsigned char)ch);7 ^' D8 n& ^/ W) z2 _
  12. while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);& ^8 b: k# r6 g6 Y  b" u9 K
  13. return (ch);/ E: H2 I+ M2 |
  14. }
    9 l4 b, q6 L4 o. g. y- J* E1 r: J

  15. 2 k+ C3 l  v- b
  16. void printf_init(void)
    $ V5 ?5 W3 }& |: L: P4 J' v" W$ X8 n
  17. {
    7 ]2 z6 ]9 J# c! r: b
  18. GPIO_InitTypeDef GPIO_InitStructure;" ^$ i5 A" [4 |4 x  r
  19. USART_InitTypeDef USART_InitStructure;
    & y1 t& m' v4 X1 U

  20. 3 x1 V; @6 E4 f3 }* L: O, j7 C5 M$ }
  21. /*config USART clock*/+ F1 p% I8 H8 x4 O$ T, ?( ?. a: \
  22. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);/ X4 ^/ L- c) j1 Q3 N: N' s
  23. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);8 d* v5 i& {; p& T9 e: ?
  24. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
    6 {; j  a# Z$ Z# a- L! f
  25. /*USART1 GPIO config*/
    6 u( M4 t! V1 t$ G$ T
  26. GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9;
    ) j$ r/ {7 H* B+ \: ^9 p
  27. GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP; //复用推挽输出
    ' _  C: p' E* t2 i) V! h9 ~
  28. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;' P4 o4 ~/ R, y) i( L, Y: R
  29. GPIO_Init(GPIOA,&GPIO_InitStructure);9 z3 l& M  v0 m! B1 y6 @; z$ F$ O

  30. 6 Z4 q: a) p, M* X  H4 J; b
  31. GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10;
    / s4 X; E3 j; h2 j! N
  32. GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING; //复用开漏输入
    ' x( C; F5 \9 F' Y, f
  33. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    , g8 G+ e  {9 i+ x
  34. GPIO_Init(GPIOA,&GPIO_InitStructure);
    ' c; {- @. J  C; s1 n7 i  E$ F# S* N
  35. / r; g  Y5 b8 Q, }( R
  36. GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3;, g* M$ v) g6 W8 B9 x. b% b' m
  37. GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;" y( H' w7 v! I
  38. GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
    6 E! R( A- ?  q7 s
  39. GPIO_Init(GPIOD,&GPIO_InitStructure);8 E9 f9 R8 L7 S1 h* w
  40. /*USART1 mode Config*/3 q6 g. x1 G$ ^* }* T
  41. USART_InitStructure.USART_BaudRate = 9600;
    / G  ]7 C& Y  I# e1 T
  42. USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    - F! U( ^3 [/ Z
  43. USART_InitStructure.USART_StopBits = USART_StopBits_1;
    * }: a) M. A2 `7 W% }) y
  44. USART_InitStructure.USART_Parity = USART_Parity_No;) b+ G; r- K. Z
  45. USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    ' a6 M  _9 [) U) k* D2 b
  46. USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    8 L: K8 J9 r+ f- C: U1 a1 R
  47. USART_Init(USART1,&USART_InitStructure);
    3 |9 Q* g6 e" Y. W+ p6 ]
  48. USART_Cmd(USART1,ENABLE);
复制代码

/ `  ]" j( Y- ^/ \' ^
printf.h:
  1. #ifndef __printf_H
    8 e2 R7 F% O% }; n  U/ `9 \  _
  2. #define __printf_H! Y7 g2 y: |5 _( f" ^5 {2 y
  3. 4 A7 ^8 D2 K) V3 c
  4. #include "stm32f10x.h"
    4 F) M* R$ ^0 K# u1 H+ |" u/ b
  5. #include <stdio.h>
    / R4 Z2 m, k9 r3 @: `( S+ W
  6. #define LED3_ON GPIO_SetBits(GPIOD,GPIO_Pin_3)
    # Y9 n$ }4 q, k; J# [# h. e
  7. #define LED3_OFF GPIO_ResetBits(GPIOD,GPIO_Pin_3)6 T7 I" O2 N$ H% r$ f9 U0 l1 h; k
  8. void printf_init(void);
    ) j) Z/ k3 e! f, x9 c% V
  9. int fputc(int ch,FILE *f);
    3 @+ x" w" a) L
  10. * f* ~) x7 C! Q6 C! p- r2 L) B
  11. #endif
复制代码

2 N' w8 E  k% z* e; g" q9 b2 X
中断代码:
  1. #include "stm32f10x_it.h"
    / `1 E! I0 \; A
  2. #include "stm32f10x.h"
    % `8 S1 r( L/ |! j6 f4 P% ^) U* h  H
  3. #include "printf.h"; O0 ]# a/ `3 e
  4. void DMA1_Channel4_IRQHandler(void)# X- ]- ]+ t8 I; T: Y1 I3 w
  5. {, c" R, p0 O  d/ q# ^. C! L- i
  6. if(DMA_GetFlagStatus(DMA1_FLAG_TC4)==SET)1 T7 [! A0 W: D/ l5 }9 y& _& }
  7. {
    9 r* U# f0 L! W/ I4 ~( P1 l: W" `: A
  8. LED3_OFF;! |' w- @& ^3 Q$ a/ c) [8 [
  9. DMA_ClearFlag(DMA1_FLAG_TC4);
    & Z  L8 h) y  o
  10. }$ O- J8 m% D6 j* s% g
  11. }
复制代码

; c2 l0 b: p& p  U3 I) N
效果图:
20170713220118597.png
# S  C1 u- X/ y) _
+ m  q6 |7 \, X1 O( J
赞 收藏 评论0 发布时间:2022-2-1 18:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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