天天看點

Windows CE6.0 S3C2440A IIC驅動編碼解析

S3C2440A RISC微處理器可以支援多主裝置IIC總線串行接口。專用串行總線(SDA)和串行時鐘線(SCL)承載總線主機裝置和連接配接IIC總線的外圍裝置之間的資訊。SDA和SCL線都是雙向的。本章采用TQ2440開發闆進行分析,我們先來看看其硬體電路圖;

<a></a>

從這裡可以看的出 TQ2440 采用的是AT24C02A IIC器件,其中I2CSCL和I2CSDA分别表示時鐘線和資料線。接下來看看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;

在這個結構體中pI2CReg、pIOPReg和pCLKPWRReg都扮演着非常重要的角色,如下來看看如何實作這上個寄存器初始化工作,後面将介紹IIC驅動的幾個關鍵部分代碼;

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

       pI2C-&gt;pI2CReg = (volatile S3C2440A_IICBUS_REG*)(pVMem);

        pVMem += PAGE_SIZE;

VirtualCopy(pVMem,(LPVOID)(S3C2440A_BASE_REG_PA_IOPORT&gt;&gt;8),

     PAGE_SIZE,PAGE_READWRITE | PAGE_NOCACHE |PAGE_PHYSICAL);

pI2C-&gt;pIOPReg = (volatile S3C2440A_IOPORT_REG*)(pVMem);

pVMem += PAGE_SIZE;

VirtualCopy(pVMem,(LPVOID)(S3C2440A_BASE_REG_PA_CLOCK_POWER&gt;&gt;8),

PAGE_SIZE,PAGE_READWRITE | PAGE_NOCACHE |PAGE_PHYSICAL);

pI2C-&gt;pCLKPWRReg = (volatile S3C2440A_CLKPWR_REG*)(pVMem);

   在Windows CE當中中斷處理的常用的幾個函數有KernelIoControl、InterruptInitialize、InterruptDisable和InterruptDone函數,如下所示是其處理過程。

Irq = IRQ_IIC;

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

// initialize the interrupt

InterruptInitialize(gIntrIIC, pI2C-&gt;ISTEvent, NULL, 0) ) ;

InterruptDone(gIntrIIC);

// create the IST

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

CeSetThreadPriority(pI2C-&gt;IST, I2C_THREAD_PRIORITY));

IST函數

    PI2C_CONTEXT pI2C = (PI2C_CONTEXT)Context;

    DWORD i2cSt;

    BOOL bDone = FALSE;

    do  {

        if (pI2C-&gt;Mode == INTERRUPT) {

            DWORD we;

            bDone = FALSE;

            we = WaitForSingleObject(pI2C-&gt;ISTEvent, INFINITE);

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

            InterruptDone(gIntrIIC);

            switch(pI2C-&gt;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;

                default:

                    if (pI2C-&gt;State != WRITE_ACK &amp;&amp;

                        pI2C-&gt;State != RESUME &amp;&amp;

                        pI2C-&gt;DataCount == INVALID_DATA_COUNT) {

                        continue;

                    }

            }

        }

//        EnterCriticalSection(&amp;pI2C-&gt;RegCS);

        __try {

            switch(pI2C-&gt;State)

                case SUSPEND:

                    continue;

                case RESUME:

                    InitRegs(pI2C);

                    pI2C-&gt;LastError = ERROR_OPERATION_ABORTED;

                    SetEvent(pI2C-&gt;DoneEvent);

                case SET_READ_ADDR:

                    if ( (pI2C-&gt;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-&gt;Flags.WordAddr) {

                        rIICDS = pI2C-&gt;WordAddr;

                        // clear interrupt pending bit (resume)

                        rIICCON = RESUME_IIC_CON;

                        pI2C-&gt;Flags.WordAddr = FALSE;

                case READ_DATA:                   

                    ASSERT(pI2C-&gt;Data);

                        *pI2C-&gt;Data = (UCHAR)rIICDS;

                        pI2C-&gt;Data++;

                        rIICSTAT = MRX_STOP;   

                        rIICCON  = RESUME_IIC_CON;  // resume operation.                   

break;   

                    // Drop the returned Slave WordAddr?

                    if ( pI2C-&gt;Flags.DropRxAddr )

                        pI2C-&gt;RxRetAddr = (UCHAR)rIICDS;

                        pI2C-&gt;Flags.DropRxAddr = FALSE;                       

                    } else {

                    // The last data is read with no ack.

                    if ( pI2C-&gt;DataCount == 0 ) {

                        rIICCON = RESUME_NO_ACK;    // resume operation with NOACK. 

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

                        rIICCON = RESUME_IIC_CON;       // resume operation with ACK

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

                case WRITE_DATA:                       

                        rIICSTAT = MTX_STOP;   

                        rIICCON  = RESUME_IIC_CON;  // resume operation.

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

                        break;   

                        rIICDS = (UCHAR)*pI2C-&gt;Data;

                    rIICCON = RESUME_IIC_CON;   // resume operation.

            }

        } _except(EXCEPTION_EXECUTE_HANDLER) {

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

            rIICCON  = RESUME_IIC_CON;

            pI2C-&gt;DataCount = INVALID_DATA_COUNT;

            pI2C-&gt;LastError = GetExceptionCode();

        if (bDone) {

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

            SetEvent(pI2C-&gt;DoneEvent);

        }           

} while (pI2C-&gt;Mode == INTERRUPT);   

return ERROR_SUCCESS;

}

驅動采用的是中斷方式讀取資料,其中資料指針儲存在pI2C-&gt;pData當中,其中為了保證使用者區緩沖和驅動核心區緩沖一緻,還必須調用GetCallerProcess()、 MapPtrToProcess()和GetCurrentProcessID()函數,這幾個函數的具體用法可以查詢MSDN幫助即可,這部分代碼如下;

pI2C-&gt;State     = WRITE_DATA;

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

pI2C-&gt;WordAddr  = WordAddr;

pI2C-&gt;Flags.WordAddr = TRUE;   

pI2C-&gt;Data = pData;

// write slave address

rIICDS   = (UCHAR)SlaveAddr;

rIICSTAT = MTX_START;

// IST writes the slave word address &amp; data

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

goto _done;

寫操作和讀操作方法很類似,是其反過程,代碼很簡單,這裡就不多講了,具體在S3C2440A BSP中可以看到。

這個IIC驅動是一個典型的Windows CE流接口驅動程式,是一個很好的學習範例,特寫至此,希望對來客有寫幫助。

上一篇: 2014.5.14

繼續閱讀