1. DMA子系统简介
1.1 DMA简介。
DMA的英文拼写是“DirectMemory Access”,是一种数据不经过CPU处理,直接由DMA控制器从一块物理内存搬运到另一块物理内存的数据交换模式。在DMA模式下,CPU只须向DMA控制器下达指令,让DMA控制器来处理数据的传送,数据传送完毕再把信息反馈给CPU,这样就很大程度上减轻了CPU资源占有率,可以大大节省系统资源。
2. DMA设备驱动模型
2.1 DMA驱动模型简介
SylixOS中的DMA架构位于“libsylixos/SylixOS/system/device/dma/”下,DMA驱动多用于外设驱动或总线驱动中。主要功能是从一块物理地址向另一块物理地址搬运数据。本文以mini2440的通用DMA驱动为例。
DMA设备库对DMA设备进行了封装,设备驱动仅需要提供初始化函数和回调函数即可。
在注册DMA设备驱动之前,需要先安装DMA设备库,其原型如程序清单 21:
程序清单21
#include <SylixOS.h> INT API_DmaDrvInstall (UINT uiChannel, PLW_DMA_FUNCS pdmafuncs, size_t stMaxDataBytes) |
函数API_DmaDrvInstall原型分析:
l 此函数执行成功返回PLW_DMA_FUNCS的地址。
DMA设备需要调用dmaGetFuncs函数绑定驱动并创建设备,其函数原型如程序清单 22:
程序清单22
#include <SylixOS.h> PLW_DMA_FUNCS dmaGetFuncs (UINT iChannel, ULONG *pulMaxBytes) |
函数dmaGetFuncs原型分析:
- 此函数成功返回ERROR_NONE,失败返回PX_ERROR;
- 参数iChannel:DMA通道号;
- 参数pulMaxBytes:最大传输字节数;
结构体PLW_DMA_FUNCS详细描述如程序清单 23:
程序清单23
typedefstruct 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驱动结构体的指针。
2.2 DMA回调函数
使用回调函数的目的是将DMA设备库中的结构体保存到驱动中以供缓冲区数据操作,因此在驱动结构体中需要提供三个变量以保存数据,示例如程序清单 24:
程序清单24
typedefstruct { … VOID (*DMAT_pfuncStart)(UINT uiChannel, PVOID pvArg); /* 启动本次传输之前的回调*/ PVOID *DMAT_pvArg; /* 回调函数参数 */ VOID (*DMAT_pfuncCallback)(UINT uiChannel, PVOID pvArg); /* 本次传输完成后的回调函*/ … } LW_DMA_TRANSACTION; |
2.3 DMA传输参数
在LW_DMA_TRANSACTION结构体中除了一些DMA回调函数,还有一些重要的DMA参数,实例如程序清单 25:
程序清单25
typedefstruct { 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; typedefLW_DMA_TRANSACTION *PLW_DMA_TRANSACTION; |
DMA 操作的是物理地址, 所以 Src和 Dest地址均为物理地址。有些系统CPU 体系构架的 CACHE 是使用虚拟地址作为索引的, 有些是使用物理地址做索引的。所以 DMA 软件层不处理任何 CACHE 相关的操作, 将这些操作留给驱动程序或应用程序完成。
2.4 DMA API函数简介
程序清单26
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 |
如程序清单 26是SylixOS中内核定义的DMA模块,具体函数使用方法和调用流程在下篇文章中会做详细介绍。