winCE中采用DMA传输数据的方法

来源:互联网 发布:空姐收入 知乎 编辑:程序博客网 时间:2024/05/19 13:57

作者:Jackchenyj

转自:http://blog.csdn.net/chenyujing1234/article/details/7520971


转载请标明是引用于 http://blog.csdn.net/chenyujing1234

对于文章中有什么意见或是需要代码的可以留言联系我。

1、DMA入口

[cpp] view plaincopy
  1. // DDM控制器能服务的外围设备配置信息的结构体  
  2. typedef struct {  
  3.  BOOL  Valid;  //  表明此逻辑设备是否可用  
  4.  ULONG ChanCfg;  // DDMA 通道配置  (每个通道特有的)  
  5.  ULONG DescCmd;  // 描述符 cmd,即 dsr_cmd0的设置     
  6.  ULONG DescSrc1;  // 描述符 source1, striding / burst sizes etc.  
  7.  ULONG DescDest1; // 描述符 dest1, striding / burst sizes etc.  
  8.  ULONG FifoAddr;  // FIFO 地址 for peripheral  
  9.  BOOL  DevIsRead; // 它是一个读事务吗 ?  
  10.  WCHAR *Name;        // 设备名  
  11. } DDMA_DEVICE_CONFIG;  


 

[cpp] view plaincopy
  1. static DDMA_DEVICE_CONFIG DdmaConfig[MAX_DMA_LOGICAL_DEVICES];  


 

[cpp] view plaincopy
  1. /*++ 
  2.  
  3. Routine Description: 
  4.  
  5.     这个函数在BCEDDK为每个进程加载时调用. 
  6.  它表现了初始化来让BCEDDK的DMA portion 不可用 
  7.     performs initialization to make the DMA portion of the BCEDDK usable. 
  8.  
  9. Arguments: 
  10.  
  11.     None. 
  12.  
  13. Return Value: 
  14.  
  15.     None. 
  16.  
  17. --*/  
  18. VOID  
  19. DmaEntry(  
  20.    VOID  
  21.    )  
  22. {  
  23.   
  24. //为每个要用DMA的设备 初始化数据,通过DdmaConfig管理这些设备  
  25.   
  26. //  下面以SPI设备为例  
  27.   
  28.  // SPI (PSC0) Transmit  
  29.  DdmaConfig[DMA_SPI_TX].Valid     = TRUE;  
  30.  DdmaConfig[DMA_SPI_TX].Name      = TEXT("SPI TX");  
  31.  DdmaConfig[DMA_SPI_TX].ChanCfg   = DDMA_CHANCFG_DFN;  
  32.  DdmaConfig[DMA_SPI_TX].DescCmd   = DDMA_DESCCMD_SID_N(DDMA_ALWAYS_HIGH_ID) |  
  33.   DDMA_DESCCMD_DW_HWORD | DDMA_DESCCMD_SW_WORD |  
  34.   DDMA_DESCCMD_DN | DDMA_DESCCMD_SN;  
  35.  DdmaConfig[DMA_SPI_TX].DescSrc1  = DDMA_DESCSRC_STRIDE_STS_1 | DDMA_DESCSRC_STRIDE_SAM_INC;  
  36.  DdmaConfig[DMA_SPI_TX].DescDest1 = DDMA_DESCDST_STRIDE_DTS_8 | DDMA_DESCDST_STRIDE_DAM_STATIC;  
  37.  DdmaConfig[DMA_SPI_TX].DevIsRead = FALSE;  
  38.   
  39.  // SPI (PSC0) Receive  
  40.  DdmaConfig[DMA_SPI_RX].Valid     = TRUE;  
  41.  DdmaConfig[DMA_SPI_RX].Name      = TEXT("SPI RX");  
  42.  DdmaConfig[DMA_SPI_RX].ChanCfg   = DDMA_CHANCFG_DFN;  
  43.  DdmaConfig[DMA_SPI_RX].DescCmd   = DDMA_DESCCMD_DID_N(DDMA_ALWAYS_HIGH_ID) |  
  44.   DDMA_DESCCMD_DW_WORD | DDMA_DESCCMD_SW_WORD |  
  45.   DDMA_DESCCMD_DN | DDMA_DESCCMD_SN;  
  46.  DdmaConfig[DMA_SPI_RX].DescSrc1  = DDMA_DESCSRC_STRIDE_STS_8 | DDMA_DESCSRC_STRIDE_SAM_STATIC;  
  47.  DdmaConfig[DMA_SPI_RX].DescDest1 = DDMA_DESCDST_STRIDE_DTS_1 | DDMA_DESCDST_STRIDE_DAM_INC;  
  48.  DdmaConfig[DMA_SPI_RX].DevIsRead = TRUE;  
  49.  DdmaConfig[DMA_SPI_TX].DescCmd  |= DDMA_DESCCMD_DID_N(DDMA_PSC0_TX_ID);  
  50.  DdmaConfig[DMA_SPI_TX].FifoAddr  = DDMA_PSC0_TX_ADDR;  
  51.  DdmaConfig[DMA_SPI_RX].DescCmd  |= DDMA_DESCCMD_SID_N(DDMA_PSC0_RX_ID);  
  52.  DdmaConfig[DMA_SPI_RX].FifoAddr  = DDMA_PSC0_RX_ADDR;  
  53. ........  
  54.   
  55. }  

以上DMA_SPI_RX这个ID的确定得根据实现硬件DDMA的要求,我的要求是这样的:

以上最重要的是参数是Physical Address,它是我们告诉DDMA从哪里接收数据,把数据发往哪里的根据。

从以上代码可以看出Physical Address给了FifoAddr变量。在接下文的2、3中会讲到怎么把它赋给DDMA寄存器。

 2、分配DMA对象并初始化DMA对象。可以在XXX_Init 里调用

 

[cpp] view plaincopy
  1. ULONG  
  2. XXX_Init(  
  3.     IN ULONG RegistryPath  
  4.     )  
  5.   
  6. {  
  7.   
  8.     PDEVICE_INSTANCE DeviceInstance;  
  9.   
  10.     DeviceInstance = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,  
  11.                                 sizeof(*DeviceInstance));  
  12.   
  13.     。。。。  
  14.   
  15.     //  
  16.     // Setup DMA operations  
  17.     //  
  18.     DeviceInstance->DmaBufferSize = 512;  
  19.   
  20.     // 为接收与发送分配DMA对象  
  21.   
  22.     DeviceInstance->TxDma = HalAllocateDMAChannel();  
  23.     DeviceInstance->RxDma = HalAllocateDMAChannel();  
  24.   
  25.     // 初始化DMA对象  
  26.   
  27.     HalInitDmaChannel(DeviceInstance->TxDma,  
  28.                    DMA_SPI_TX,  
  29.                 DeviceInstance->DmaBufferSize,  
  30.                     TRUE);  
  31.   
  32.     HalInitDmaChannel(DeviceInstance->RxDma,  
  33.                    DMA_SPI_RX,  
  34.        DeviceInstance->DmaBufferSize,  
  35.        FALSE);  
  36.   
  37.   
  38.     HalSetDMAForReceive(DeviceInstance->RxDma);  
  39.   
  40.     // 启动DMA  
  41.   
  42.     HalStartDMA(DeviceInstance->RxDma);  
  43.     HalStartDMA(DeviceInstance->TxDma);  
  44.   
  45.   
  46.     // 为Tx DMA完成设备中断  
  47.     HwIntr = HalGetDMAHwIntr(DeviceInstance->TxDma);  
  48.   
  49.     // 监视得到的中断  
  50.   
  51.     DeviceInstance->SysIntr = InterruptConnect(Internal,0,HwIntr,0);  
  52.   
  53.     DeviceInstance->hIsrEvent = CreateEvent(NULL,FALSE,FALSE,NULL);  
  54.   
  55.     if (!InterruptInitialize(DeviceInstance->SysIntr,DeviceInstance->hIsrEvent,NULL,0)) {  
  56.         RETAILMSG(1,(TEXT("SPI: Failed InterruptInitialize\r\n")));  
  57.         goto ErrorReturn;  
  58.     }  
  59.   
  60.     InterruptDone(DeviceInstance->SysIntr);  
  61.   
  62. }  

 


第二个步骤主要由两个函数实现:

HalAllocateDMAChannel:  分配DAM对象较为简单,先判断是不是对全局管理DMA的对象进行了初始化,没有就做初始化;

得到的DDMA对象是:

PDMA_CHANNEL_OBJECT

typedef PVOID PDMA_CHANNEL_OBJECT;

可以看到它只是一个指针而已,但传到下面讲到的重要函数HalInitDmaChannel 时就转化成

PCHANNEL_OBJECT

typedef struct _CHANNEL_OBJECT CHANNEL_OBJECT, *PCHANNEL_OBJECT;

[cpp] view plaincopy
  1. //  
  2. // CHANNEL_OBJECT structure definition.  A forward declaration for this  
  3. // structure is in bceddk.h.  This is the real definition.  
  4. //  
  5. struct _CHANNEL_OBJECT {  
  6.     // These are the parts from ceddk.h  
  7.     USHORT ObjectSize;                  // Size of structure (versioning).  
  8.     INTERFACE_TYPE InterfaceType;       // Adapter bus interface.  
  9.     ULONG BusNumber;                    // Adapter bus number.  
  10.   
  11.     // Au1550 specific bits  
  12.     ULONG   DmaChannel;  
  13.     ULONG   BufferSize;  
  14.     HANDLE  hChannelMutex;  
  15.   
  16.     DDMA_DEVICE_CONFIG *pDevCfg;  
  17.   
  18.     BOOL    InterruptsEnabled;  
  19.   
  20.     DMA_LOGICAL_DEVICE  DeviceID;  
  21.     DDMA_CHANNEL* DmaRegPtr;  
  22.   
  23.     PVOID   pBufferA;  
  24.     PVOID   pBufferB;  
  25.     PHYSICAL_ADDRESS BufferAPhys;  
  26.     PHYSICAL_ADDRESS BufferBPhys;  
  27.   
  28.     DDMA_DESCRIPTOR_STD*   pDescA;  
  29.     DDMA_DESCRIPTOR_STD*   pDescB;  
  30.     PHYSICAL_ADDRESS DescAPhys;  
  31.     PHYSICAL_ADDRESS DescBPhys;  
  32.   
  33.     BUFFER_STATE BufferStateA;  
  34.     BUFFER_STATE BufferStateB;  
  35.   
  36.     WCHAR *Name;        // Device name  
  37.   
  38.     // Entries for MDL DMA  
  39.     DDMA_DESCRIPTOR_STD *pDescriptors;  
  40.     PHYSICAL_ADDRESS     DescriptorsPhysAddr;  
  41.     ULONG AllocatedDescriptors;  
  42.   
  43.     PVOID pAlignedBuffer;  
  44.     PHYSICAL_ADDRESS    AlignedPhysAddr;  
  45.     ULONG AlignedByteCount;  
  46.     ULONG NextDMABuffer; // The Next DMA Buffer to be serviced by interrupt  
  47.   
  48.     // Added to remove hardware state dependencies  
  49. //  DWORD dwGet, dwPut, dwCur;  
  50.   
  51. };  


 

 

[cpp] view plaincopy
  1. //  
  2. // BCEDDK Routines  
  3. //  
  4.   
  5. PDMA_CHANNEL_OBJECT HalAllocateDMAChannel()  
  6.   
  7. /*++ 
  8.  
  9. Routine Description: 
  10.  
  11.     此函数分配一个DMA通道. 
  12.  
  13. Arguments: 
  14.     None. 
  15.  
  16. Return Value: 
  17.  
  18.     指向一个新的DMA通道对象,如果失败就返回NULL 
  19.  
  20. --*/  
  21. {  
  22.     PCHANNEL_OBJECT pNewChannel;  
  23.     int i;  
  24.     BOOL bIsInitialized=FALSE;  
  25.   
  26.     if (!bIsInitialized)  
  27.     {  
  28.         bIsInitialized = TRUE;  
  29.         DmaEntry();  
  30.     }  
  31.   
  32.     // 分配对象  
  33.     pNewChannel = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,  
  34.         sizeof(*pNewChannel));  
  35.   
  36.     if (pNewChannel == NULL) {  
  37.         goto ErrorReturn;  
  38.     }  
  39.   
  40.     // 找一个空的通道  
  41.     for (i=0;i<DDMA_NUM_CHANNELS;i++)  
  42.     {  
  43.   
  44.         pNewChannel->hChannelMutex = CreateMutex(NULL,  
  45.             FALSE,  
  46.             DmaChannelMutexNames[i]);  
  47.         if (pNewChannel->hChannelMutex == NULL) {  
  48.             goto ErrorReturn;  
  49.         }  
  50.         if(GetLastError()==ERROR_ALREADY_EXISTS)   
  51.         {  
  52.             CloseHandle(pNewChannel->hChannelMutex);  
  53.         }  
  54.         else   
  55.         {  
  56.             RETAILMSG(MSGS,(L"DDMA channel %d is not in use, create new Mutex\r\n",i));  
  57.             pNewChannel->DmaChannel = i;  
  58.             break;  
  59.         }  
  60.   
  61.   
  62.     }  
  63.   
  64.     if (i==DDMA_NUM_CHANNELS)  
  65.     {  
  66.         RETAILMSG(1,(L"No DDMA Channels available\r\n"));  
  67.         goto ErrorReturn;  
  68.     }  
  69.   
  70.     //  
  71.     // 等待通道变成可用的  
  72.     WaitForSingleObject(pNewChannel->hChannelMutex, INFINITE);  
  73.   
  74.     //  
  75.     // 获得DMA通道指针  
  76.   
  77.     pNewChannel->DmaRegPtr = (DDMA_CHANNEL*)(0xB4002000 + (0x100*pNewChannel->DmaChannel));  
  78.     //  
  79.     // 确保通道是关闭的  
  80.   
  81.     WRITE_REGISTER_ULONG((PULONG)&pNewChannel->DmaRegPtr->cfg, 0); // 使EN位为0,表通道使能关闭  
  82.   
  83.     return (PDMA_CHANNEL_OBJECT)pNewChannel;  
  84.   
  85.   
  86. ErrorReturn:  
  87.     // cleanup  
  88.   
  89.     if (pNewChannel) {  
  90.         LocalFree(pNewChannel);  
  91.     }  
  92.   
  93.     return NULL;  
  94.   
  95.   
  96. }  


 

HalInitDmaChannel   :

2、1  根据Device从DdmaConfig数组中取得预先配置好的DMA信息初始化DMA对象。

DdmaConfig数组信息的填充在第一步中就完成了。

2、2 分配描述符A与描述符B。

当第(1)中的信息填充了我们的DDMA对象时,对象中有些内容还是得另外填充,eg: pBufferA、pBufferB、pDescA、pDescB内容的申请。

// Allocate two buffers
    ChannelObject->BufferAPhys.QuadPart = 0;
    ChannelObject->BufferBPhys.QuadPart = 0;

    ChannelObject->pBufferA = AllocPhysMem(BufferSize*2,
                                           PAGE_READWRITE | PAGE_NOCACHE,
                                           DMA_ALIGNMENT_REQUIREMENT,
                                           0,
                                           &ChannelObject->BufferAPhys.LowPart);

 ChannelObject->pBufferB = (PUCHAR)(ChannelObject->pBufferA) + BufferSize;
 ChannelObject->BufferBPhys.LowPart = ChannelObject->BufferAPhys.LowPart + BufferSize;

============================

// Allocate two descriptors
 ChannelObject->DescAPhys.QuadPart = 0;
 ChannelObject->DescBPhys.QuadPart = 0;

 ChannelObject->pDescA = AllocPhysMem(sizeof(DDMA_DESCRIPTOR_STD) * 4, // IMR TEST
                                           PAGE_READWRITE | PAGE_NOCACHE,
                                           DMA_ALIGNMENT_REQUIREMENT,
                                           0,
                                           &ChannelObject->DescAPhys.LowPart);
 ChannelObject->pDescB = (PVOID)((PUCHAR)(ChannelObject->pDescA) + (2*sizeof(DDMA_DESCRIPTOR_STD)));
 ChannelObject->DescBPhys.LowPart = ChannelObject->DescAPhys.LowPart + (2*sizeof(DDMA_DESCRIPTOR_STD));

2、3  接下来把描述符与我拉配置中的FIFO地址及Buffer地址绑定在一起。

描述符:是源和目的间传送的内存结构。这些描述符包含了指导DDMA控制器如何传送数据的域

DDMA支持3类传送:

源到目的的传关、比较和分支、逐字的写。

标准的描述符成员有:传送的命令信息cmd0、cmd1、源地址的低32位source0、source1.

if (pDevCfg->DevIsRead) {
  WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescA->source0, pDevCfg->FifoAddr);
  WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescB->source0, pDevCfg->FifoAddr);
  WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescA->dest0, ChannelObject->BufferAPhys.LowPart);
  WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescB->dest0, ChannelObject->BufferBPhys.LowPart);
 } else {
  WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescA->dest0, pDevCfg->FifoAddr);
  WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescB->dest0, pDevCfg->FifoAddr);
  WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescA->source0, ChannelObject->BufferAPhys.LowPart);
  WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescB->source0, ChannelObject->BufferBPhys.LowPart);
 }
 WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescA->source1, pDevCfg->DescSrc1);
 WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescB->source1, pDevCfg->DescSrc1);
 WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescA->dest1, pDevCfg->DescDest1);
 WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescB->dest1, pDevCfg->DescDest1);


 以上加红部分中的FifoAddr是从上文  1、 中获得的,它是我们告诉DDMA从哪里接收数据,把数据发往哪里的根据。

在这里与DDMA的source0或dest0绑定起来了。

 

[cpp] view plaincopy
  1. // 如果传0 或NULL就为通道用默认的,有时默认的不能被用且一个值被要求  
  2. BOOL HalInitDmaChannel(PDMA_CHANNEL_OBJECT DmaChannelObject,  
  3.                        DMA_LOGICAL_DEVICE  Device,  
  4.                        ULONG               BufferSize,  
  5.                        BOOL                InterruptEnable)  
  6. {  
  7.     PCHANNEL_OBJECT ChannelObject = (PCHANNEL_OBJECT)DmaChannelObject;  
  8.     DDMA_DEVICE_CONFIG *pDevCfg;  
  9.     ULONG descCmd;  
  10.   
  11.     // Check Device is in range  
  12.     if (Device >= MAX_DMA_LOGICAL_DEVICES) {  
  13.         RETAILMSG(1,(TEXT("HalInitDmaChannel: Device %d is out of range\r\n"),Device));  
  14.         goto errorReturn;  
  15.     }  
  16.   
  17.     // 获得设备的配置  
  18.     pDevCfg = &DdmaConfig[Device];  
  19.     // 检查设备有可用的配置  
  20.     if (!pDevCfg->Valid) {  
  21.         RETAILMSG(1,(TEXT("HalInitDmaChannel: Device %d does not have valid configuration\r\n"),Device));  
  22.         goto errorReturn;  
  23.     }  
  24.   
  25.     ChannelObject->pDevCfg = pDevCfg;  
  26.     ChannelObject->BufferSize = BufferSize;  
  27.     ChannelObject->Name = pDevCfg->Name;  
  28.     ChannelObject->DeviceID = Device;  
  29.   
  30.   
  31.     // 分配两个buffers  
  32.     ChannelObject->BufferAPhys.QuadPart = 0;  
  33.     ChannelObject->BufferBPhys.QuadPart = 0;  
  34.   
  35.   
  36.     ChannelObject->pBufferA = AllocPhysMem(BufferSize*2,  
  37.         PAGE_READWRITE | PAGE_NOCACHE,  
  38.         DMA_ALIGNMENT_REQUIREMENT,  
  39.         0,  
  40.         &ChannelObject->BufferAPhys.LowPart);  
  41.   
  42.     ChannelObject->pBufferB = (PUCHAR)(ChannelObject->pBufferA) + BufferSize;  
  43.     ChannelObject->BufferBPhys.LowPart = ChannelObject->BufferAPhys.LowPart + BufferSize;  
  44.   
  45.   
  46.   
  47.     // 分配两个描述符  
  48.     ChannelObject->DescAPhys.QuadPart = 0;  
  49.     ChannelObject->DescBPhys.QuadPart = 0;  
  50.   
  51.     ChannelObject->pDescA = AllocPhysMem(sizeof(DDMA_DESCRIPTOR_STD) * 4, // IMR TEST  
  52.         PAGE_READWRITE | PAGE_NOCACHE,  
  53.         DMA_ALIGNMENT_REQUIREMENT,  
  54.         0,  
  55.         &ChannelObject->DescAPhys.LowPart);  
  56.     ChannelObject->pDescB = (PVOID)((PUCHAR)(ChannelObject->pDescA) + (2*sizeof(DDMA_DESCRIPTOR_STD)));  
  57.     ChannelObject->DescBPhys.LowPart = ChannelObject->DescAPhys.LowPart + (2*sizeof(DDMA_DESCRIPTOR_STD));  
  58.   
  59.   
  60.     // 设置描述符  
  61.     descCmd = pDevCfg->DescCmd;  
  62.   
  63.     if (InterruptEnable)   
  64.     {  
  65.         ChannelObject->InterruptsEnabled = TRUE;  
  66.         descCmd |= DDMA_DESCCMD_IE;  
  67.     }  
  68.     descCmd |= DDMA_DESCCMD_CV;     // 在描述符完成时清除可用Bit  
  69.   
  70.   
  71.     // 配置DDMA寄存器(根据实际的机器来配置)  
  72.     if (pDevCfg->DevIsRead)   
  73.     {  
  74.         WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescA->source0, pDevCfg->FifoAddr);   //把寄存器DDMA_PSC1_TX_ADDR 的地址读到源当之  
  75.         WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescB->source0, pDevCfg->FifoAddr);  
  76.         /* 
  77.          从以下可以看出: 
  78.         // 分配两个描述符ChannelObject->DescAPhys 与 ChannelObject->pDescA是联的 
  79.         ChannelObject->DescAPhys.QuadPart = 0; 
  80.         ChannelObject->DescBPhys.QuadPart = 0; 
  81.  
  82.         ChannelObject->pDescA = AllocPhysMem(sizeof(DDMA_DESCRIPTOR_STD) * 4, // IMR TEST 
  83.         PAGE_READWRITE | PAGE_NOCACHE, 
  84.         DMA_ALIGNMENT_REQUIREMENT, 
  85.         0, 
  86.         &ChannelObject->DescAPhys.LowPart); 
  87.         ChannelObject->pDescB = (PVOID)((PUCHAR)(ChannelObject->pDescA) + (2*sizeof(DDMA_DESCRIPTOR_STD))); 
  88.         ChannelObject->DescBPhys.LowPart = ChannelObject->DescAPhys.LowPart + (2*sizeof(DDMA_DESCRIPTOR_STD)); 
  89.  
  90.         */  
  91.         WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescA->dest0, ChannelObject->BufferAPhys.LowPart);  // 目的  
  92.         WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescB->dest0, ChannelObject->BufferBPhys.LowPart);  
  93.     }   
  94.     else  
  95.     {  
  96.         WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescA->dest0, pDevCfg->FifoAddr);  
  97.         WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescB->dest0, pDevCfg->FifoAddr);  
  98.         WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescA->source0, ChannelObject->BufferAPhys.LowPart);  
  99.         WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescB->source0, ChannelObject->BufferBPhys.LowPart);  
  100.     }  
  101.     WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescA->source1, pDevCfg->DescSrc1);  
  102.     WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescB->source1, pDevCfg->DescSrc1);  
  103.     WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescA->dest1, pDevCfg->DescDest1);  
  104.     WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescB->dest1, pDevCfg->DescDest1);  
  105.   
  106.   
  107.     WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescA->cmd0, descCmd);  
  108.     WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescB->cmd0, descCmd);  
  109.   
  110.   
  111.     // 设置 loop  
  112.     WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescA->nxt_ptr, ChannelObject->DescBPhys.LowPart >> 5); // 除以32  
  113.     WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescB->nxt_ptr, ChannelObject->DescAPhys.LowPart >> 5); // 除以32  
  114.   
  115.     // 设置通道配置  
  116.     WRITE_REGISTER_ULONG((PULONG)&ChannelObject->DmaRegPtr->cfg, pDevCfg->ChanCfg);  
  117.   
  118.     // 设置描述符指向A  
  119.     WRITE_REGISTER_ULONG((PULONG)&ChannelObject->DmaRegPtr->des_ptr,ChannelObject->DescAPhys.LowPart);  
  120.     ChannelObject->NextDMABuffer = ChannelObject->DescAPhys.LowPart;// ????  
  121.   
  122.     RETAILMSG(1,(TEXT("HalInitDmaChannel: Channel %d initialized for %s\r\n"),ChannelObject->DmaChannel,ChannelObject->Name));  
  123.   
  124.     return TRUE;  
  125.   
  126.   
  127. }  


3、应用层如何获得DMA的数据

3、1  设计驱动层获取DMA数据的接口函数(这里以获得8bit为例)
[cpp] view plaincopy
  1. BOOL DoTransferDma8(  
  2.                  ULONG            Size,  
  3.                  PBYTE           pInBuffer,  
  4.                  PBYTE           pOutBuffer)  
  5. {  
  6.     PDEVICE_INSTANCE DeviceInstance = g_DeviceInstance;          
  7.     PSC_SPI *pSPI = DeviceInstance->pSPI;  
  8.     ULONG   SPIConfiguration = 0;  
  9.     ULONG   data = 0;  
  10.     PBYTE pRxDmaBuffer,pTxDmaBuffer;  
  11.     BOOL status = TRUE;  
  12.     ULONG intStat;  
  13.     int timeout;  
  14.   
  15.     WRITE_REGISTER_ULONG((PULONG)&pSPI->msk,0xffffffff);  
  16.   
  17.       
  18.     SPIConfiguration =  READ_REGISTER_ULONG((PULONG)&pSPI->cfg);      
  19.     SPIConfiguration &= ~PSC_SPI_CFG_DE;   
  20.     WRITE_REGISTER_ULONG((PULONG)&pSPI->cfg, SPIConfiguration);  
  21.     //SPIConfiguration |= (PSC_SPI_CFG_TRD_N(0) | PSC_SPI_CFG_RRD_N(0));   
  22.     SPIConfiguration &= ~PSC_SPI_CFG_DD;   
  23.     //SPIConfiguration |= PSC_SPI_CFG_CDE;// delay   
  24.     WRITE_REGISTER_ULONG((PULONG)&pSPI->cfg, SPIConfiguration);  
  25.     WRITE_REGISTER_ULONG((PULONG)&pSPI->cfg, SPIConfiguration | PSC_SPI_CFG_DE);  
  26.     timeout = 50;  
  27.     // 等待DMA寄存器的状态指示器指示有数据  
  28.     while (timeout && !((READ_REGISTER_ULONG((PULONG)&pSPI->sts)) & PSC_SPI_STS_DR))  
  29.     {  
  30.         StallExecution(1000);  
  31.         timeout--;  
  32.     }  
  33.     if (!timeout)   
  34.     {  
  35.         status = FALSE;  
  36.         RETAILMSG(1,(TEXT("SPI: DoTransferDMA: Timeout waiting for DR!\r\n")));  
  37.     }  
  38.       
  39.           
  40.     //Rx Data Clear and  Tx Data Clear  
  41.     WRITE_REGISTER_ULONG((PULONG)&pSPI->pcr,PSC_SPI_PCR_RC | PSC_SPI_PCR_TC);  
  42.       
  43.     // 启动DMA的Rx和Tx  
  44.     if (pOutBuffer!=NULL)  
  45.     {  
  46.         HalSetDMAForReceive(DeviceInstance->RxDma);  
  47.         HalStartDMA(DeviceInstance->RxDma);   
  48.     }  
  49.     HalStartDMA(DeviceInstance->TxDma);  
  50.         
  51.     // 获得下一个Tx的DMA Buffer  
  52.     pTxDmaBuffer = (PBYTE)HalGetNextDMABuffer(DeviceInstance->TxDma);  
  53.     // 复制要发送的Tx数据,如果Tx为NULL,那么给它0xFF.这样就仅有Rx传送  
  54.     if (pInBuffer==NULL)   
  55.     {  
  56.         memset(pTxDmaBuffer, 0xFF, Size);    
  57.     }   
  58.     else   
  59.     {  
  60.         memcpy(pTxDmaBuffer, pInBuffer, Size);        
  61.     }  
  62.   
  63.     // Give buffer back to DMA  
  64.     HalActivateDMABuffer(DeviceInstance->TxDma,pTxDmaBuffer,Size);   
  65.   
  66.     // Start Master xfer  
  67.     WRITE_REGISTER_ULONG((PULONG)&pSPI->pcr, PSC_SPI_PCR_MS);  
  68.   
  69.     // 等待中断  
  70.     WaitForSingleObject(DeviceInstance->hIsrEvent,INFINITE);  
  71.   
  72.     // 把Rx接收的数据复制出来  
  73.     if (pOutBuffer!=NULL)  
  74.     {  
  75.         ULONG RxSize;         
  76.         pRxDmaBuffer = HalGetNextDMABuffer(DeviceInstance->RxDma);  
  77.         RxSize = HalGetDMABufferRxSize(DeviceInstance->RxDma,pRxDmaBuffer);  
  78.               
  79.             //for (timeout=0; timeout<8000; timeout++)  
  80.             //for (timeout=0; timeout<1000; timeout++)  
  81.             //;  
  82.               
  83.         // RX     
  84.         memcpy(pOutBuffer,pRxDmaBuffer,Size);  
  85.   
  86.         HalActivateDMABuffer(DeviceInstance->RxDma,pRxDmaBuffer,DeviceInstance->DmaBufferSize);  
  87.     }  
  88.     // 回复中断  
  89.     intStat = HalCheckForDMAInterrupt(DeviceInstance->TxDma);  
  90.     HalAckDMAInterrupt(DeviceInstance->TxDma,intStat);  
  91.     InterruptDone(DeviceInstance->SysIntr);  
  92.       
  93.     // 停止DMA的Rx与Tx  
  94.     if (pOutBuffer!=NULL)  
  95.         HalStopDMA(DeviceInstance->RxDma);  
  96.     HalStopDMA(DeviceInstance->TxDma);  
  97.   
  98.     return status;  
  99. }  


 

3、2   应用层与驱动层的调用(获得数据),我们以IOCTL方式为例
3、2、1  驱动层设计
[cpp] view plaincopy
  1. BOOL  
  2. SPI_IOControl(  
  3.     IN OUT ULONG DeviceContext,  
  4.     IN ULONG Code,  
  5.     IN PUCHAR InputBuffer,  
  6.     IN ULONG InputBufferLength,  
  7.     OUT PUCHAR OutputBuffer,  
  8.     IN ULONG OutputBufferLength,  
  9.     OUT PULONG ActualOutput  
  10.     )  
  11. {  
  12.     PDEVICE_INSTANCE DeviceInstance = (PDEVICE_INSTANCE)DeviceContext;  
  13.     BOOL Status = FALSE;  
  14.   
  15.     switch  (Code) {  
  16.         case IOCTL_SPI_RECEIVE:  
  17.                 SPIReadData(0, 0, (PULONG)OutputBuffer);  
  18.                 Status = TRUE;  
  19.             break;  
  20.         default:  
  21.             SetLastError(ERROR_CALL_NOT_IMPLEMENTED);  
  22.             Status = FALSE;  
  23.             break;  
  24.     }  
  25.   
  26.     if (!Status) {  
  27.         DEBUGMSG(ZONE_ERROR|ZONE_IOCTL, (  
  28.                  TEXT("SPI: SSO_IOControl returning FALSE.\r\n")));  
  29.     }  
  30.     return Status;  
  31. }  


接下来我们就来设计SPIReadData接口:

[cpp] view plaincopy
  1. void SPIReadData(BYTE UartInternalReg, int Channel, PULONG Val)  
  2. {  
  3.     //BYTE InitTemp;  
  4.     BYTE tempVal = 0;  
  5.     //InitTemp = 0x80;  
  6.     //InitTemp |= (UartInternalReg << 3) | (Channel << 1);  
  7.     //GPIO_CTL(51) = GPIO_CTL_OUTPUT0; //CS0  
  8.     //SET_CS_LOW();  
  9.     //DoTransferDma8(1,(PBYTE)&InitTemp,NULL);  
  10.   
  11.     DoTransferDma8(1, NULL, (PBYTE)&tempVal);  
  12.     *Val |= tempVal;  
  13. }  


 

3、2、2  应用层设计

Handle句柄怎么得来的,你应该知道的,这里不讲了。

[cpp] view plaincopy
  1. BOOL CSPIPort::SPI_DoTransfer( HANDLE Handle,  
  2.                ULONG  Size,  
  3.                PULONG pTxBuffer,  
  4.                PULONG pRxBuffer )  
  5. {  
  6.     ULONG Tmp;  
  7.     BOOL RetVal;  
  8.   
  9.     enum {  
  10.         IOCTL_SPI_TRANSFER = 0x80002000,  // some arbirary base  
  11.         IOCTL_SPI_TRANSFER_DMA = 0x80002001,  
  12.         IOCTL_SPI_RECEIVE = 0x80002002  
  13.     };  
  14.   
  15.     RetVal = DeviceIoControl(  
  16.         Handle,  
  17.         IOCTL_SPI_RECEIVE,  
  18.         pTxBuffer,  
  19.         Size,  
  20.         pRxBuffer,  
  21.         Size,  
  22.         &Tmp,  
  23.         NULL);  
  24.   
  25.     return RetVal;  
  26. }  


 


0 0