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 CE6.0 S3C2440A IIC驱动编码解析)