最近做数据传输要用到DMA,通过验证测试并分析官方的Demo,移植到自己的代码中,通过学习为我所用。 官方提供的Demo里有FlashToRAM的例程,先从这个例程下手。通过阅读手册可以知道,H503有2个DMA,感觉能扩展应用还挺多的,如图1所示:
F4 w% A' h0 E' }# Q1 K+ q
# d/ M$ A8 | s/ U* y# V
图1, H, I: E2 Q6 Q$ f, X, a2 {# U o
简单分析一下代码,先从初始化部分开始,这部分做了部分注释,如下所示:
6 m! W ?, v0 r9 q( z, b5 H8 i- t7 s9 t- b2 t. X' w. ~. ]$ c8 e7 q8 k
- handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1; //通道号) K+ F! Z3 Q% \" C7 r
- handle_GPDMA1_Channel1.Init.Request = DMA_REQUEST_SW; //请求来源,软件触发
4 Z# c' `1 C9 z& j5 R# y - handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;//硬件请求类型,单次触发模式
~7 I; T$ f9 O - handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_MEMORY; //传输方向,内存到内存
z/ |- e8 l% u - handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_INCREMENTED; // 源地址增量模式
& u5 a& P) p- z6 `$ I! O9 M* s - handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_INCREMENTED; //目标地址增量模式) X" n: K5 Q9 p, q
- handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD; // 数据源的宽度,以字为单位
% o( ~* X1 T/ [ - handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD; //3 P# [8 E5 \- d3 G1 Q+ I' [ f0 _
- handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_HIGH_WEIGHT; // DMA的优先级,低优先级高权重 l" @' ^9 r u: m( ^2 J. u
- handle_GPDMA1_Channel1.Init.SrcBurstLength = 1; //Burst长度以数据单位表示
5 h; S7 G; |5 r1 ~: v; h7 p" w - handle_GPDMA1_Channel1.Init.DestBurstLength = 1; //
7 J3 r9 V: w s0 i- i! Z& Y - handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0; //分配给DMA传输的源和目标端口,都分配到PORT0
- c7 z: d- V, H0 U% ? - handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER; //传输事件的模式为Block块传输模式7 Q6 r$ F7 Y9 J! \& I( K1 t
- handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL; //工作模式为正常模式
复制代码 8 A. T; R7 m, f3 ^$ S$ r
然后就是主函数,先定义了两个callback函数,一个用于发送完成,一个用于发送错误。传输完成,中断响应后调用回调函数。7 r6 d$ `' P4 o6 C. B" z9 @+ d
: K0 c3 j9 p) E- HAL_DMA_RegisterCallback(&handle_GPDMA1_Channel1, HAL_DMA_XFER_CPLT_CB_ID, TransferComplete);5 E9 \' ^/ J4 X) Q2 L* N+ R( T) L7 R
- HAL_DMA_RegisterCallback(&handle_GPDMA1_Channel1, HAL_DMA_XFER_ERROR_CB_ID, TransferError);
复制代码 通过两个变量标志位的状态,来判断是否完成和是否继续进行,并有两个回调函数来完成,如下:- static void TransferComplete(DMA_HandleTypeDef *handle_GPDMA1_Channel1)
' h3 r0 Z" f3 i% p - {5 q o; o/ ~6 b/ h9 E9 f
- transferCompleteDetected = 1;6 H$ a* ? @: P# |
- }
6 j2 k4 B. k/ z: u
复制代码- static void TransferError(DMA_HandleTypeDef *handle_GPDMA1_Channel1)
7 x" G( @% V( w# o& w3 W( g# ` - {
7 k) U/ f* y, E$ C - transferErrorDetected = 1;
2 |( F0 B4 _/ K+ @ - }
复制代码 其实官方的注释中写的也是比较详细的。然后就是配置源和目的地址,启动传输,如下:) ]5 Y1 g/ j z! l6 `; F6 Z5 c
- if (HAL_DMA_Start_IT(&handle_GPDMA1_Channel1, (uint32_t)&aSRC_Const_Buffer, (uint32_t)&aDST_Buffer, (BUFFER_SIZE * 4U)) != HAL_OK)" ]5 } z9 b& d4 B
- {
$ {* r& I8 O- }3 i4 [) _% y7 j - /* Transfer Error */$ `1 x8 P) y c" j' R
- Error_Handler();
, w: r6 \" e. S y) _8 q# S9 B - }
复制代码 其他部分可以参考源程序,测试结果如图2所示:
7 P0 M/ P) ?# T1 i) f/ V9 D6 y
$ j! w4 w1 m5 G1 X; o# D, Z
图22 @9 M7 Y( s* P
软件部分相对要简单一点,主要是通过测试和分析,理解和了解DMA的使用,为后续工作方便调用准备。
$ u1 y2 g' W! j6 ?) A; Z$ U5 G |
必须得整,得深入挖掘。