作者:Jackchenyj
转自:http://blog.youkuaiyun.com/chenyujing1234/article/details/7520971
转载请标明是引用于 http://blog.youkuaiyun.com/chenyujing1234
对于文章中有什么意见或是需要代码的可以留言联系我。
1、DMA入口
- // DDM控制器能服务的外围设备配置信息的结构体
- typedef struct {
- BOOL Valid; // 表明此逻辑设备是否可用
- ULONG ChanCfg; // DDMA 通道配置 (每个通道特有的)
- ULONG DescCmd; // 描述符 cmd,即 dsr_cmd0的设置
- ULONG DescSrc1; // 描述符 source1, striding / burst sizes etc.
- ULONG DescDest1; // 描述符 dest1, striding / burst sizes etc.
- ULONG FifoAddr; // FIFO 地址 for peripheral
- BOOL DevIsRead; // 它是一个读事务吗 ?
- WCHAR *Name; // 设备名
- } DDMA_DEVICE_CONFIG;
- static DDMA_DEVICE_CONFIG DdmaConfig[MAX_DMA_LOGICAL_DEVICES];
- /*++
- Routine Description:
- 这个函数在BCEDDK为每个进程加载时调用.
- 它表现了初始化来让BCEDDK的DMA portion 不可用
- performs initialization to make the DMA portion of the BCEDDK usable.
- Arguments:
- None.
- Return Value:
- None.
- --*/
- VOID
- DmaEntry(
- VOID
- )
- {
- //为每个要用DMA的设备 初始化数据,通过DdmaConfig管理这些设备
- // 下面以SPI设备为例
- // SPI (PSC0) Transmit
- DdmaConfig[DMA_SPI_TX].Valid = TRUE;
- DdmaConfig[DMA_SPI_TX].Name = TEXT("SPI TX");
- DdmaConfig[DMA_SPI_TX].ChanCfg = DDMA_CHANCFG_DFN;
- DdmaConfig[DMA_SPI_TX].DescCmd = DDMA_DESCCMD_SID_N(DDMA_ALWAYS_HIGH_ID) |
- DDMA_DESCCMD_DW_HWORD | DDMA_DESCCMD_SW_WORD |
- DDMA_DESCCMD_DN | DDMA_DESCCMD_SN;
- DdmaConfig[DMA_SPI_TX].DescSrc1 = DDMA_DESCSRC_STRIDE_STS_1 | DDMA_DESCSRC_STRIDE_SAM_INC;
- DdmaConfig[DMA_SPI_TX].DescDest1 = DDMA_DESCDST_STRIDE_DTS_8 | DDMA_DESCDST_STRIDE_DAM_STATIC;
- DdmaConfig[DMA_SPI_TX].DevIsRead = FALSE;
- // SPI (PSC0) Receive
- DdmaConfig[DMA_SPI_RX].Valid = TRUE;
- DdmaConfig[DMA_SPI_RX].Name = TEXT("SPI RX");
- DdmaConfig[DMA_SPI_RX].ChanCfg = DDMA_CHANCFG_DFN;
- DdmaConfig[DMA_SPI_RX].DescCmd = DDMA_DESCCMD_DID_N(DDMA_ALWAYS_HIGH_ID) |
- DDMA_DESCCMD_DW_WORD | DDMA_DESCCMD_SW_WORD |
- DDMA_DESCCMD_DN | DDMA_DESCCMD_SN;
- DdmaConfig[DMA_SPI_RX].DescSrc1 = DDMA_DESCSRC_STRIDE_STS_8 | DDMA_DESCSRC_STRIDE_SAM_STATIC;
- DdmaConfig[DMA_SPI_RX].DescDest1 = DDMA_DESCDST_STRIDE_DTS_1 | DDMA_DESCDST_STRIDE_DAM_INC;
- DdmaConfig[DMA_SPI_RX].DevIsRead = TRUE;
- DdmaConfig[DMA_SPI_TX].DescCmd |= DDMA_DESCCMD_DID_N(DDMA_PSC0_TX_ID);
- DdmaConfig[DMA_SPI_TX].FifoAddr = DDMA_PSC0_TX_ADDR;
- DdmaConfig[DMA_SPI_RX].DescCmd |= DDMA_DESCCMD_SID_N(DDMA_PSC0_RX_ID);
- DdmaConfig[DMA_SPI_RX].FifoAddr = DDMA_PSC0_RX_ADDR;
- ........
- }
以上DMA_SPI_RX这个ID的确定得根据实现硬件DDMA的要求,我的要求是这样的:
以上最重要的是参数是Physical Address,它是我们告诉DDMA从哪里接收数据,把数据发往哪里的根据。
从以上代码可以看出Physical Address给了FifoAddr变量。在接下文的2、3中会讲到怎么把它赋给DDMA寄存器。
2、分配DMA对象并初始化DMA对象。可以在XXX_Init 里调用
- ULONG
- XXX_Init(
- IN ULONG RegistryPath
- )
- {
- PDEVICE_INSTANCE DeviceInstance;
- DeviceInstance = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
- sizeof(*DeviceInstance));
- 。。。。
- //
- // Setup DMA operations
- //
- DeviceInstance->DmaBufferSize = 512;
- // 为接收与发送分配DMA对象
- DeviceInstance->TxDma = HalAllocateDMAChannel();
- DeviceInstance->RxDma = HalAllocateDMAChannel();
- // 初始化DMA对象
- HalInitDmaChannel(DeviceInstance->TxDma,
- DMA_SPI_TX,
- DeviceInstance->DmaBufferSize,
- TRUE);
- HalInitDmaChannel(DeviceInstance->RxDma,
- DMA_SPI_RX,
- DeviceInstance->DmaBufferSize,
- FALSE);
- HalSetDMAForReceive(DeviceInstance->RxDma);
- // 启动DMA
- HalStartDMA(DeviceInstance->RxDma);
- HalStartDMA(DeviceInstance->TxDma);
- // 为Tx DMA完成设备中断
- HwIntr = HalGetDMAHwIntr(DeviceInstance->TxDma);
- // 监视得到的中断
- DeviceInstance->SysIntr = InterruptConnect(Internal,0,HwIntr,0);
- DeviceInstance->hIsrEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
- if (!InterruptInitialize(DeviceInstance->SysIntr,DeviceInstance->hIsrEvent,NULL,0)) {
- RETAILMSG(1,(TEXT("SPI: Failed InterruptInitialize\r\n")));
- goto ErrorReturn;
- }
- InterruptDone(DeviceInstance->SysIntr);
- }
第二个步骤主要由两个函数实现:
HalAllocateDMAChannel: 分配DAM对象较为简单,先判断是不是对全局管理DMA的对象进行了初始化,没有就做初始化;
得到的DDMA对象是:
PDMA_CHANNEL_OBJECT
typedef PVOID PDMA_CHANNEL_OBJECT;
可以看到它只是一个指针而已,但传到下面讲到的重要函数HalInitDmaChannel 时就转化成
PCHANNEL_OBJECT
typedef struct _CHANNEL_OBJECT CHANNEL_OBJECT, *PCHANNEL_OBJECT;
- //
- // CHANNEL_OBJECT structure definition. A forward declaration for this
- // structure is in bceddk.h. This is the real definition.
- //
- struct _CHANNEL_OBJECT {
- // These are the parts from ceddk.h
- USHORT ObjectSize; // Size of structure (versioning).
- INTERFACE_TYPE InterfaceType; // Adapter bus interface.
- ULONG BusNumber; // Adapter bus number.
- // Au1550 specific bits
- ULONG DmaChannel;
- ULONG BufferSize;
- HANDLE hChannelMutex;
- DDMA_DEVICE_CONFIG *pDevCfg;
- BOOL InterruptsEnabled;
- DMA_LOGICAL_DEVICE DeviceID;
- DDMA_CHANNEL* DmaRegPtr;
- PVOID pBufferA;
- PVOID pBufferB;
- PHYSICAL_ADDRESS BufferAPhys;
- PHYSICAL_ADDRESS BufferBPhys;
- DDMA_DESCRIPTOR_STD* pDescA;
- DDMA_DESCRIPTOR_STD* pDescB;
- PHYSICAL_ADDRESS DescAPhys;
- PHYSICAL_ADDRESS DescBPhys;
- BUFFER_STATE BufferStateA;
- BUFFER_STATE BufferStateB;
- WCHAR *Name; // Device name
- // Entries for MDL DMA
- DDMA_DESCRIPTOR_STD *pDescriptors;
- PHYSICAL_ADDRESS DescriptorsPhysAddr;
- ULONG AllocatedDescriptors;
- PVOID pAlignedBuffer;
- PHYSICAL_ADDRESS AlignedPhysAddr;
- ULONG AlignedByteCount;
- ULONG NextDMABuffer; // The Next DMA Buffer to be serviced by interrupt
- // Added to remove hardware state dependencies
- // DWORD dwGet, dwPut, dwCur;
- };
- //
- // BCEDDK Routines
- //
- PDMA_CHANNEL_OBJECT HalAllocateDMAChannel()
- /*++
- Routine Description:
- 此函数分配一个DMA通道.
- Arguments:
- None.
- Return Value:
- 指向一个新的DMA通道对象,如果失败就返回NULL
- --*/
- {
- PCHANNEL_OBJECT pNewChannel;
- int i;
- BOOL bIsInitialized=FALSE;
- if (!bIsInitialized)
- {
- bIsInitialized = TRUE;
- DmaEntry();
- }
- // 分配对象
- pNewChannel = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
- sizeof(*pNewChannel));
- if (pNewChannel == NULL) {
- goto ErrorReturn;
- }
- // 找一个空的通道
- for (i=0;i<DDMA_NUM_CHANNELS;i++)
- {
- pNewChannel->hChannelMutex = CreateMutex(NULL,
- FALSE,
- DmaChannelMutexNames[i]);
- if (pNewChannel->hChannelMutex == NULL) {
- goto ErrorReturn;
- }
- if(GetLastError()==ERROR_ALREADY_EXISTS)
- {
- CloseHandle(pNewChannel->hChannelMutex);
- }
- else
- {
- RETAILMSG(MSGS,(L"DDMA channel %d is not in use, create new Mutex\r\n",i));
- pNewChannel->DmaChannel = i;
- break;
- }
- }
- if (i==DDMA_NUM_CHANNELS)
- {
- RETAILMSG(1,(L"No DDMA Channels available\r\n"));
- goto ErrorReturn;
- }
- //
- // 等待通道变成可用的
- WaitForSingleObject(pNewChannel->hChannelMutex, INFINITE);
- //
- // 获得DMA通道指针
- pNewChannel->DmaRegPtr = (DDMA_CHANNEL*)(0xB4002000 + (0x100*pNewChannel->DmaChannel));
- //
- // 确保通道是关闭的
- WRITE_REGISTER_ULONG((PULONG)&pNewChannel->DmaRegPtr->cfg, 0); // 使EN位为0,表通道使能关闭
- return (PDMA_CHANNEL_OBJECT)pNewChannel;
- ErrorReturn:
- // cleanup
- if (pNewChannel) {
- LocalFree(pNewChannel);
- }
- return NULL;
- }
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绑定起来了。
- // 如果传0 或NULL就为通道用默认的,有时默认的不能被用且一个值被要求
- BOOL HalInitDmaChannel(PDMA_CHANNEL_OBJECT DmaChannelObject,
- DMA_LOGICAL_DEVICE Device,
- ULONG BufferSize,
- BOOL InterruptEnable)
- {
- PCHANNEL_OBJECT ChannelObject = (PCHANNEL_OBJECT)DmaChannelObject;
- DDMA_DEVICE_CONFIG *pDevCfg;
- ULONG descCmd;
- // Check Device is in range
- if (Device >= MAX_DMA_LOGICAL_DEVICES) {
- RETAILMSG(1,(TEXT("HalInitDmaChannel: Device %d is out of range\r\n"),Device));
- goto errorReturn;
- }
- // 获得设备的配置
- pDevCfg = &DdmaConfig[Device];
- // 检查设备有可用的配置
- if (!pDevCfg->Valid) {
- RETAILMSG(1,(TEXT("HalInitDmaChannel: Device %d does not have valid configuration\r\n"),Device));
- goto errorReturn;
- }
- ChannelObject->pDevCfg = pDevCfg;
- ChannelObject->BufferSize = BufferSize;
- ChannelObject->Name = pDevCfg->Name;
- ChannelObject->DeviceID = Device;
- // 分配两个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;
- // 分配两个描述符
- 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));
- // 设置描述符
- descCmd = pDevCfg->DescCmd;
- if (InterruptEnable)
- {
- ChannelObject->InterruptsEnabled = TRUE;
- descCmd |= DDMA_DESCCMD_IE;
- }
- descCmd |= DDMA_DESCCMD_CV; // 在描述符完成时清除可用Bit
- // 配置DDMA寄存器(根据实际的机器来配置)
- if (pDevCfg->DevIsRead)
- {
- WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescA->source0, pDevCfg->FifoAddr); //把寄存器DDMA_PSC1_TX_ADDR 的地址读到源当之
- WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescB->source0, pDevCfg->FifoAddr);
- /*
- 从以下可以看出:
- // 分配两个描述符ChannelObject->DescAPhys 与 ChannelObject->pDescA是联的
- 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));
- */
- 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);
- WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescA->cmd0, descCmd);
- WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescB->cmd0, descCmd);
- // 设置 loop
- WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescA->nxt_ptr, ChannelObject->DescBPhys.LowPart >> 5); // 除以32
- WRITE_REGISTER_ULONG((PULONG)&ChannelObject->pDescB->nxt_ptr, ChannelObject->DescAPhys.LowPart >> 5); // 除以32
- // 设置通道配置
- WRITE_REGISTER_ULONG((PULONG)&ChannelObject->DmaRegPtr->cfg, pDevCfg->ChanCfg);
- // 设置描述符指向A
- WRITE_REGISTER_ULONG((PULONG)&ChannelObject->DmaRegPtr->des_ptr,ChannelObject->DescAPhys.LowPart);
- ChannelObject->NextDMABuffer = ChannelObject->DescAPhys.LowPart;// ????
- RETAILMSG(1,(TEXT("HalInitDmaChannel: Channel %d initialized for %s\r\n"),ChannelObject->DmaChannel,ChannelObject->Name));
- return TRUE;
- }
3、应用层如何获得DMA的数据
3、1 设计驱动层获取DMA数据的接口函数(这里以获得8bit为例)
- BOOL DoTransferDma8(
- ULONG Size,
- PBYTE pInBuffer,
- PBYTE pOutBuffer)
- {
- PDEVICE_INSTANCE DeviceInstance = g_DeviceInstance;
- PSC_SPI *pSPI = DeviceInstance->pSPI;
- ULONG SPIConfiguration = 0;
- ULONG data = 0;
- PBYTE pRxDmaBuffer,pTxDmaBuffer;
- BOOL status = TRUE;
- ULONG intStat;
- int timeout;
- WRITE_REGISTER_ULONG((PULONG)&pSPI->msk,0xffffffff);
- SPIConfiguration = READ_REGISTER_ULONG((PULONG)&pSPI->cfg);
- SPIConfiguration &= ~PSC_SPI_CFG_DE;
- WRITE_REGISTER_ULONG((PULONG)&pSPI->cfg, SPIConfiguration);
- //SPIConfiguration |= (PSC_SPI_CFG_TRD_N(0) | PSC_SPI_CFG_RRD_N(0));
- SPIConfiguration &= ~PSC_SPI_CFG_DD;
- //SPIConfiguration |= PSC_SPI_CFG_CDE;// delay
- WRITE_REGISTER_ULONG((PULONG)&pSPI->cfg, SPIConfiguration);
- WRITE_REGISTER_ULONG((PULONG)&pSPI->cfg, SPIConfiguration | PSC_SPI_CFG_DE);
- timeout = 50;
- // 等待DMA寄存器的状态指示器指示有数据
- while (timeout && !((READ_REGISTER_ULONG((PULONG)&pSPI->sts)) & PSC_SPI_STS_DR))
- {
- StallExecution(1000);
- timeout--;
- }
- if (!timeout)
- {
- status = FALSE;
- RETAILMSG(1,(TEXT("SPI: DoTransferDMA: Timeout waiting for DR!\r\n")));
- }
- //Rx Data Clear and Tx Data Clear
- WRITE_REGISTER_ULONG((PULONG)&pSPI->pcr,PSC_SPI_PCR_RC | PSC_SPI_PCR_TC);
- // 启动DMA的Rx和Tx
- if (pOutBuffer!=NULL)
- {
- HalSetDMAForReceive(DeviceInstance->RxDma);
- HalStartDMA(DeviceInstance->RxDma);
- }
- HalStartDMA(DeviceInstance->TxDma);
- // 获得下一个Tx的DMA Buffer
- pTxDmaBuffer = (PBYTE)HalGetNextDMABuffer(DeviceInstance->TxDma);
- // 复制要发送的Tx数据,如果Tx为NULL,那么给它0xFF.这样就仅有Rx传送
- if (pInBuffer==NULL)
- {
- memset(pTxDmaBuffer, 0xFF, Size);
- }
- else
- {
- memcpy(pTxDmaBuffer, pInBuffer, Size);
- }
- // Give buffer back to DMA
- HalActivateDMABuffer(DeviceInstance->TxDma,pTxDmaBuffer,Size);
- // Start Master xfer
- WRITE_REGISTER_ULONG((PULONG)&pSPI->pcr, PSC_SPI_PCR_MS);
- // 等待中断
- WaitForSingleObject(DeviceInstance->hIsrEvent,INFINITE);
- // 把Rx接收的数据复制出来
- if (pOutBuffer!=NULL)
- {
- ULONG RxSize;
- pRxDmaBuffer = HalGetNextDMABuffer(DeviceInstance->RxDma);
- RxSize = HalGetDMABufferRxSize(DeviceInstance->RxDma,pRxDmaBuffer);
- //for (timeout=0; timeout<8000; timeout++)
- //for (timeout=0; timeout<1000; timeout++)
- //;
- // RX
- memcpy(pOutBuffer,pRxDmaBuffer,Size);
- HalActivateDMABuffer(DeviceInstance->RxDma,pRxDmaBuffer,DeviceInstance->DmaBufferSize);
- }
- // 回复中断
- intStat = HalCheckForDMAInterrupt(DeviceInstance->TxDma);
- HalAckDMAInterrupt(DeviceInstance->TxDma,intStat);
- InterruptDone(DeviceInstance->SysIntr);
- // 停止DMA的Rx与Tx
- if (pOutBuffer!=NULL)
- HalStopDMA(DeviceInstance->RxDma);
- HalStopDMA(DeviceInstance->TxDma);
- return status;
- }
3、2 应用层与驱动层的调用(获得数据),我们以IOCTL方式为例
3、2、1 驱动层设计
- BOOL
- SPI_IOControl(
- IN OUT ULONG DeviceContext,
- IN ULONG Code,
- IN PUCHAR InputBuffer,
- IN ULONG InputBufferLength,
- OUT PUCHAR OutputBuffer,
- IN ULONG OutputBufferLength,
- OUT PULONG ActualOutput
- )
- {
- PDEVICE_INSTANCE DeviceInstance = (PDEVICE_INSTANCE)DeviceContext;
- BOOL Status = FALSE;
- switch (Code) {
- case IOCTL_SPI_RECEIVE:
- SPIReadData(0, 0, (PULONG)OutputBuffer);
- Status = TRUE;
- break;
- default:
- SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
- Status = FALSE;
- break;
- }
- if (!Status) {
- DEBUGMSG(ZONE_ERROR|ZONE_IOCTL, (
- TEXT("SPI: SSO_IOControl returning FALSE.\r\n")));
- }
- return Status;
- }
接下来我们就来设计SPIReadData接口:
- void SPIReadData(BYTE UartInternalReg, int Channel, PULONG Val)
- {
- //BYTE InitTemp;
- BYTE tempVal = 0;
- //InitTemp = 0x80;
- //InitTemp |= (UartInternalReg << 3) | (Channel << 1);
- //GPIO_CTL(51) = GPIO_CTL_OUTPUT0; //CS0
- //SET_CS_LOW();
- //DoTransferDma8(1,(PBYTE)&InitTemp,NULL);
- DoTransferDma8(1, NULL, (PBYTE)&tempVal);
- *Val |= tempVal;
- }
3、2、2 应用层设计
Handle句柄怎么得来的,你应该知道的,这里不讲了。
- BOOL CSPIPort::SPI_DoTransfer( HANDLE Handle,
- ULONG Size,
- PULONG pTxBuffer,
- PULONG pRxBuffer )
- {
- ULONG Tmp;
- BOOL RetVal;
- enum {
- IOCTL_SPI_TRANSFER = 0x80002000, // some arbirary base
- IOCTL_SPI_TRANSFER_DMA = 0x80002001,
- IOCTL_SPI_RECEIVE = 0x80002002
- };
- RetVal = DeviceIoControl(
- Handle,
- IOCTL_SPI_RECEIVE,
- pTxBuffer,
- Size,
- pRxBuffer,
- Size,
- &Tmp,
- NULL);
- return RetVal;
- }