DMA的英文拼写是“Direct Memory Access”,是一种数据不通过CPU处理,直接由DMA控制器从一块物理内存搬运到另外一块物理内存的数据交换模式。在DMA模式下,CPU只须向DMA控制器下达指令,让DMA控制器来处理数据的传送,数据传送完毕再把信息反馈给CPU,这样就很大程度上减轻了CPU资源占有率,能够大大节省系统资源。架构
SylixOS中的DMA架构位于“libsylixos/SylixOS/system/device/dma/”下,DMA驱动多用于外设驱动或总线驱动中。主要功能是从一块物理地址向另外一块物理地址搬运数据。本文以mini2440的通用DMA驱动为例。函数
DMA设备库对DMA设备进行了封装,设备驱动仅须要提供初始化函数和回调函数便可。ui
在注册DMA设备驱动以前,须要先安装DMA设备库,其原型如程序清单 2‑1:spa
程序清单 2‑1指针
#include <SylixOS.h>索引 INT API_DmaDrvInstall (UINT uiChannel,队列 PLW_DMA_FUNCS pdmafuncs,内存 size_t stMaxDataBytes)ci |
函数API_DmaDrvInstall原型分析:资源
l 此函数执行成功返回PLW_DMA_FUNCS的地址。
DMA设备须要调用dmaGetFuncs函数绑定驱动并建立设备,其函数原型如程序清单 2‑2:
程序清单 2‑2
#include <SylixOS.h> PLW_DMA_FUNCS dmaGetFuncs (UINT iChannel, ULONG *pulMaxBytes) |
函数dmaGetFuncs原型分析:
l 此函数成功返回ERROR_NONE,失败返回PX_ERROR;
l 参数iChannel:DMA通道号;
l 参数pulMaxBytes:最大传输字节数;
结构体PLW_DMA_FUNCS 详细描述如程序清单 2‑3:
程序清单 2‑3
typedef struct lw_dma_funcs { VOID (*DMAF_pfuncReset)( UINT uiChannel, struct lw_dma_funcs *pdmafuncs); INT (*DMAF_pfuncTrans)( UINT uiChannel, struct lw_dma_funcs *pdmafuncs, PLW_DMA_TRANSACTION pdmatMsg); INT (*DMAF_pfuncStatus)(UINT uiChannel, Struct lw_dma_funcs *pdmafuncs); } LW_DMA_FUNCS; typedef LW_DMA_FUNCS *PLW_DMA_FUNCS; |
结构体中包含三个须要提供给DMA设备库的操做函数,其功能分别是复位当前DMA操做、启动一次DMA传输、得到当前DMA工做状态。这三个函数的第一个传入参数都是DMA的通道号,第二个参数都是指向DMA驱动结构体的指针。
使用回调函数的目的是将DMA设备库中的结构体保存到驱动中以供缓冲区数据操做,所以在驱动结构体中须要提供三个变量以保存数据,示例如程序清单 2‑4:
程序清单 2‑4
typedef struct { … VOID (*DMAT_pfuncStart)(UINT uiChannel, PVOID pvArg); /* 启动本次传输以前的回调*/
PVOID *DMAT_pvArg; /* 回调函数参数 */ VOID (*DMAT_pfuncCallback)(UINT uiChannel, PVOID pvArg); /* 本次传输完成后的回调函*/ … } LW_DMA_TRANSACTION; |
在LW_DMA_TRANSACTION结构体中除了一些DMA回调函数,还有一些重要的DMA参数,实例如程序清单 2‑5:
程序清单 2‑5
typedef struct { UINT8 *DMAT_pucSrcAddress; /* 源端缓冲区地址 */ UINT8 *DMAT_pucDestAddress; /* 目的端缓冲区地址 */ size_t DMAT_stDataBytes; /* 传输的字节数 */ INT DMAT_iSrcAddrCtl; /* 源端地址方向控制 */ INT DMAT_iDestAddrCtl; /* 目的地址方向控制 */ INT DMAT_iHwReqNum; /* 外设请求端编号 */ BOOL DMAT_bHwReqEn; /* 是否为外设启动传输*/ BOOL DMAT_bHwHandshakeEn; /* 是否使用硬件握手 * INT DMAT_iTransMode; /* 传输模式, 自定义 */ PVOID DMAT_pvTransParam; /* 传输参数, 自定义 */ ULONG DMAT_ulOption; /* 体系结构相关参数 */ PVOID DMAT_pvArgStart; /* 启动回调参数 */ … (回调函数上小节已经单独说明) } LW_DMA_TRANSACTION; typedef LW_DMA_TRANSACTION *PLW_DMA_TRANSACTION; |
DMA 操做的是物理地址, 因此 Src 和 Dest 地址均为物理地址。有些系统 CPU 体系构架的 CACHE 是使用虚拟地址做为索引的, 有些是使用物理地址作索引的。因此 DMA 软件层不处理任何 CACHE 相关的操做, 将这些操做留给驱动程序或应用程序完成。
程序清单 2‑6
LW_API INT API_DmaDrvInstall(UINT uiChannel, PLW_DMA_FUNCS pdmafuncs, size_t stMaxDataBytes); /* 安装指定通道的 DMA 驱动程序 */
LW_API INT API_DmaReset(UINT uiChannel); /* 复位指定的 DMA 通道 */
LW_API INT API_DmaJobNodeNum(UINT uiChannel, INT *piNodeNum); /* 得到当前队列节点数 */
LW_API INT API_DmaMaxNodeNumGet(UINT uiChannel, INT *piMaxNodeNum); /* 得到最大队列节点数 */
LW_API INT API_DmaMaxNodeNumSet(UINT uiChannel, INT iMaxNodeNum); /* 设置最大队列节点数 */
LW_API INT API_DmaJobAdd(UINT uiChannel, PLW_DMA_TRANSACTION pdmatMsg); /* 添加一个 DMA 传输请求 */
LW_API INT API_DmaGetMaxDataBytes(UINT uiChannel); /* 得到一次能够传输的最大字节数*/
LW_API INT API_DmaFlush(UINT uiChannel); /* 删除全部被延迟处理的传输请求*/
/********************************************************************************************************* API 中断服务函数 *********************************************************************************************************/
LW_API INT API_DmaContext(UINT uiChannel); /* DMA 传输完成后的中断处理函数*/
#define dmaDrv API_DmaDrvInstall #define dmaReset API_DmaReset #define dmaMaxNodeNumGet API_DmaMaxNodeNumGet #define dmaMaxNodeNumSet API_DmaMaxNodeNumSet #define dmaJobAdd API_DmaJobAdd #define dmaGetMaxDataBytes API_DmaGetMaxDataBytes #define dmaFlush API_DmaFlush |
如程序清单 2‑6是SylixOS中内核定义的DMA模块,具体函数使用方法和调用流程在下篇文章中会作详细介绍。