Windows CE6.0 S3C2440A IIC驱动编码解析

S3C2440A RISC微处理器可以支持多主设备IIC总线串行接口。专用串行总线(SDA)和串行时钟线(SCL)承载总线主机设备和连接IIC总线的外围设备之间的信息。SDASCL线都是双向的。本章采用TQ2440开发板进行分析,我们先来看看其硬件电路图;

从这里可以看的出TQ2440 采用的是AT24C02A IIC器件,其中I2CSCLI2CSDA分别表示时钟线和数据线。接下来看看IIC寄存器的相关结构体;

typedef struct _I2C_CONTEXT {

DWORD Sig; // Signature

volatile S3C2440A_IICBUS_REG *pI2CReg; // I2C Registers

volatile S3C2440A_IOPORT_REG *pIOPReg; // GPIO Ports

volatile S3C2440A_CLKPWR_REG *pCLKPWRReg; // Clock / Power

CRITICAL_SECTION RegCS; // Register CS

I2C_MODE Mode; // State

I2C_STATE State;

int Status;

FLAGS Flags;

// Data

PUCHAR Data; // pointer to R/W data buffer

int DataCount; // nBytes to R/W to/from data buffer

UCHAR WordAddr; // slave word address

UCHAR RxRetAddr; // returned slave address on Rx

DWORD SlaveAddress; // Our I2C Slave Address

HANDLE DoneEvent; // I/O Done Event

HANDLE ISTEvent; // IST Event

HANDLE IST; // IST Thread

DWORD OpenCount;

DWORD LastError;

HANDLE hProc;

CEDEVICE_POWER_STATE Dx;

} I2C_CONTEXT, *PI2C_CONTEXT;

在这个结构体中pI2CRegpIOPRegpCLKPWRReg都扮演着非常重要的角色,如下来看看如何实现这上个寄存器初始化工作,后面将介绍IIC驱动的几个关键部分代码;

bMapReturn = VirtualCopy( pVMem,(LPVOID)(S3C2440A_BASE_REG_PA_IICBUS>>8), PAGE_SIZE,PAGE_READWRITE | PAGE_NOCACHE |PAGE_PHYSICAL);

pI2C->pI2CReg = (volatile S3C2440A_IICBUS_REG*)(pVMem);

pVMem += PAGE_SIZE;

VirtualCopy(pVMem,(LPVOID)(S3C2440A_BASE_REG_PA_IOPORT>>8),

PAGE_SIZE,PAGE_READWRITE | PAGE_NOCACHE |PAGE_PHYSICAL);

pI2C->pIOPReg = (volatile S3C2440A_IOPORT_REG*)(pVMem);

pVMem += PAGE_SIZE;

VirtualCopy(pVMem,(LPVOID)(S3C2440A_BASE_REG_PA_CLOCK_POWER>>8),

PAGE_SIZE,PAGE_READWRITE | PAGE_NOCACHE |PAGE_PHYSICAL);

pI2C->pCLKPWRReg = (volatile S3C2440A_CLKPWR_REG*)(pVMem);

中断模式处理

Windows CE当中中断处理的常用的几个函数有KernelIoControlInterruptInitializeInterruptDisableInterruptDone函数,如下所示是其处理过程。

Irq = IRQ_IIC;

KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &Irq, sizeof(UINT32), &gIntrIIC, sizeof(UINT32), NULL))

// initialize the interrupt

InterruptInitialize(gIntrIIC, pI2C->ISTEvent, NULL, 0) ) ;

InterruptDone(gIntrIIC);

// create the IST

pI2C->IST = CreateThread(NULL, 0, I2C_IST, (LPVOID)pI2C, 0, NULL));

CeSetThreadPriority(pI2C->IST, I2C_THREAD_PRIORITY));

IST函数

PI2C_CONTEXT pI2C = (PI2C_CONTEXT)Context;

DWORD i2cSt;

BOOL bDone = FALSE;

do {

if (pI2C->Mode == INTERRUPT) {

DWORD we;

bDone = FALSE;

we = WaitForSingleObject(pI2C->ISTEvent, INFINITE);

// clear the interrupt here because we re-arm another below

InterruptDone(gIntrIIC);

switch(pI2C->State)

{

case OFF:

DEBUGMSG(ZONE_IST|ZONE_TRACE,(TEXT("I2C_IST: ExitThread /r/n")));

ExitThread(ERROR_SUCCESS);

break;

case IDLE:

DEBUGMSG(ZONE_IST|ZONE_TRACE,(TEXT("I2C_IST: IDLE /r/n")));

continue;

break;

default:

if (pI2C->State != WRITE_ACK &&

pI2C->State != RESUME &&

pI2C->DataCount == INVALID_DATA_COUNT) {

continue;

}

break;

}

}

// EnterCriticalSection(&pI2C->RegCS);

__try {

switch(pI2C->State)

{

case IDLE:

case SUSPEND:

continue;

break;

case RESUME:

InitRegs(pI2C);

pI2C->LastError = ERROR_OPERATION_ABORTED;

SetEvent(pI2C->DoneEvent);

break;

case SET_READ_ADDR:

if ( (pI2C->DataCount--) == 0 )

{

bDone = TRUE;

break;

}

// write word address

// For setup time of SDA before SCL rising edge, rIICDS must be written

// before clearing the interrupt pending bit.

if (pI2C->Flags.WordAddr) {

rIICDS = pI2C->WordAddr;

// clear interrupt pending bit (resume)

rIICCON = RESUME_IIC_CON;

pI2C->Flags.WordAddr = FALSE;

}

break;

case READ_DATA:

ASSERT(pI2C->Data);

if ( (pI2C->DataCount--) == 0 )

{

bDone = TRUE;

*pI2C->Data = (UCHAR)rIICDS;

pI2C->Data++;

rIICSTAT = MRX_STOP;

rIICCON = RESUME_IIC_CON; // resume operation.

break;

}

// Drop the returned Slave WordAddr?

if ( pI2C->Flags.DropRxAddr )

{

pI2C->RxRetAddr = (UCHAR)rIICDS;

pI2C->Flags.DropRxAddr = FALSE;

} else {

*pI2C->Data = (UCHAR)rIICDS;

pI2C->Data++;

}

// The last data is read with no ack.

if ( pI2C->DataCount == 0 ) {

rIICCON = RESUME_NO_ACK; // resume operation with NOACK.

DEBUGMSG(ZONE_READ|ZONE_TRACE,(TEXT("R1:0x%X /r/n"), r));

} else {

rIICCON = RESUME_IIC_CON; // resume operation with ACK

DEBUGMSG(ZONE_READ|ZONE_TRACE,(TEXT("R2:0x%X /r/n"), r));

}

break;

case WRITE_DATA:

ASSERT(pI2C->Data);

if ( (pI2C->DataCount--) == 0 )

{

bDone = TRUE;

rIICSTAT = MTX_STOP;

rIICCON = RESUME_IIC_CON; // resume operation.

//The pending bit will not be set after issuing stop condition.

break;

}

rIICDS = (UCHAR)*pI2C->Data;

pI2C->Data++;

}

rIICCON = RESUME_IIC_CON; // resume operation.

break;

}

} _except(EXCEPTION_EXECUTE_HANDLER) {

rIICSTAT = (pI2C->State == READ_DATA) ? MRX_STOP : MTX_STOP;

rIICCON = RESUME_IIC_CON;

pI2C->DataCount = INVALID_DATA_COUNT;

pI2C->LastError = GetExceptionCode();

}

if (bDone) {

DEBUGMSG(ZONE_IST, (TEXT("SetEvent DONE/r/n")));

SetEvent(pI2C->DoneEvent);

}

} while (pI2C->Mode == INTERRUPT);

return ERROR_SUCCESS;

}

读取数据操作

驱动采用的是中断方式读取数据,其中数据指针保存在pI2C->pData当中,其中为了保证用户区缓冲和驱动内核区缓冲一致,还必须调用GetCallerProcess() MapPtrToProcess()GetCurrentProcessID()函数,这几个函数的具体用法可以查询MSDN帮助即可,这部分代码如下;

pI2C->State = WRITE_DATA;

pI2C->DataCount = 1 + Count; // slave word address + data

pI2C->WordAddr = WordAddr;

pI2C->Flags.WordAddr = TRUE;

pI2C->Data = pData;

// write slave address

rIICDS = (UCHAR)SlaveAddr;

rIICSTAT = MTX_START;

// IST writes the slave word address & data

if (WAIT_OBJECT_0 != SyncIst(pI2C, TX_TIMEOUT)) {

goto _done;

}

写数据操作

写操作和读操作方法很类似,是其反过程,代码很简单,这里就不多讲了,具体在S3C2440A BSP中可以看到。

这个IIC驱动是一个典型的Windows CE流接口驱动程序,是一个很好的学习范例,特写至此,希望对来客有写帮助。

你可能感兴趣的:(windows)