最近做数据传输要用到DMA,通过验证测试并分析官方的Demo,移植到自己的代码中,通过学习为我所用。 官方提供的Demo里有FlashToRAM的例程,先从这个例程下手。通过阅读手册可以知道,H503有2个DMA,感觉能扩展应用还挺多的,如图1所示:
8 N: _; ~/ t7 ^
' }; D% B0 y# ~$ k% I# B$ D
图1
- l1 D; ]* p8 a3 H 简单分析一下代码,先从初始化部分开始,这部分做了部分注释,如下所示:
: L8 ?( l; ?2 `. v+ B
- o; ~, H0 @$ b! A' b7 o9 n- handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1; //通道号
, E6 v9 Y( P" i+ p0 _8 l$ O I0 Z8 b5 }8 @5 w - handle_GPDMA1_Channel1.Init.Request = DMA_REQUEST_SW; //请求来源,软件触发6 d: O8 V3 w. f: a
- handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;//硬件请求类型,单次触发模式; F5 Y& r# l% d# E3 k8 K0 F; T
- handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_MEMORY; //传输方向,内存到内存2 z4 X4 n$ t/ S4 n+ O8 ~
- handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_INCREMENTED; // 源地址增量模式$ U7 y0 r# P+ n, j- M2 Z
- handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_INCREMENTED; //目标地址增量模式, D6 O0 L6 d3 T+ h+ @7 s0 l
- handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD; // 数据源的宽度,以字为单位
3 t. V5 J0 j+ ~7 I: V" { - handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD; //
. o0 x; I* {1 [- E, O! I' o; h+ W - handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_HIGH_WEIGHT; // DMA的优先级,低优先级高权重
1 D, A& x A1 C G - handle_GPDMA1_Channel1.Init.SrcBurstLength = 1; //Burst长度以数据单位表示# f9 M& E" c1 T& }6 F+ z
- handle_GPDMA1_Channel1.Init.DestBurstLength = 1; //
7 X: a6 J' _5 ?) V; M' G - handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0; //分配给DMA传输的源和目标端口,都分配到PORT0 Q& C; ~' s$ o$ e2 F- v
- handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER; //传输事件的模式为Block块传输模式
* _ W* M& A4 J X2 @ - handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL; //工作模式为正常模式
复制代码
9 @3 }8 J7 |% k$ R* Q1 s) g 然后就是主函数,先定义了两个callback函数,一个用于发送完成,一个用于发送错误。传输完成,中断响应后调用回调函数。0 @% V0 o) [6 | K1 p7 {0 p5 @5 u
8 O8 o& N+ f5 V4 `2 g, Y. F1 i- HAL_DMA_RegisterCallback(&handle_GPDMA1_Channel1, HAL_DMA_XFER_CPLT_CB_ID, TransferComplete);! R: q2 k0 K) w0 q! j+ X
- HAL_DMA_RegisterCallback(&handle_GPDMA1_Channel1, HAL_DMA_XFER_ERROR_CB_ID, TransferError);
复制代码 通过两个变量标志位的状态,来判断是否完成和是否继续进行,并有两个回调函数来完成,如下:- static void TransferComplete(DMA_HandleTypeDef *handle_GPDMA1_Channel1)
% k* ]6 t3 G" I3 L6 j8 I* _5 J - {
8 h0 G, b% e: p( Z9 g - transferCompleteDetected = 1;- ]( K1 K# q2 e8 c$ r& i5 W" U' B
- }
0 d/ U; L7 W! i9 d6 w1 q
复制代码- static void TransferError(DMA_HandleTypeDef *handle_GPDMA1_Channel1)
% G1 [; b. q% H4 s/ H: b - {
$ j& D- B2 t: ~9 o* \4 z" ^ - transferErrorDetected = 1;- k. a! |& C4 M5 ?
- }
复制代码 其实官方的注释中写的也是比较详细的。然后就是配置源和目的地址,启动传输,如下:3 q, ~! S+ T! C n
- if (HAL_DMA_Start_IT(&handle_GPDMA1_Channel1, (uint32_t)&aSRC_Const_Buffer, (uint32_t)&aDST_Buffer, (BUFFER_SIZE * 4U)) != HAL_OK)
4 c8 b7 N6 l* F, c( B$ B - {
# T4 L* j: f6 N! o - /* Transfer Error */
+ c' K6 F$ c! \' ?8 H( h3 N% Q4 g, v - Error_Handler();
! A/ c! I, C+ W1 a - }
复制代码 其他部分可以参考源程序,测试结果如图2所示:* b: e' p7 a' ^0 _
/ }) R q C8 d8 x; f8 [图29 W6 R# I! _4 S' s2 {9 G
软件部分相对要简单一点,主要是通过测试和分析,理解和了解DMA的使用,为后续工作方便调用准备。
, |6 g; y: A! B( g5 g+ p0 r, b6 L |
必须得整,得深入挖掘。