SylixOS iMX6平臺I2C總線驅動

原理概述

I2C總線驅動概述

I2C總線驅動是I2C適配器的軟件實現,提供I2C適配器與從設備間完成數據通信的能力,比如起始,停止,應答信號和MasterXfer的實現函數。驅動程序包含初始化I2C總線控制器__i2cHwInit函數,操作函數集(總線傳輸__i2cTransfer函數,總線控制__i2cMasterCtl函數)。

Imx6ul控制器的硬件描述

imx6ul處理器內部集成了一個I2C控制器,通過五個寄存器來進行控制。

I2Cx_IADR

I2C地址寄存器

I2Cx_IFDR

I2C分頻寄存器

I2Cx_I2CR

I2C控制寄存器

I2Cx_I2SR

I2C狀態寄存器

I2Cx_I2DR

I2C數據寄存器

通過I2Cx_I2CR,I2Cx_IFDR,I2Cx_I2DR,I2Cx_IADR寄存器操作,可在I2C總線上產生開始位、停止位、數據和地址,而傳輸的狀態則通過I2Cx_I2SR寄存器來獲取。

I2C總線傳輸編程流程

I2C總線驅動傳輸函數,主要編程流程如圖 21所示。

圖 21 I2C編程狀態

傳輸大致流程:

1.使能I2C控制器

2.設置爲主模式(佔用總線)

3.傳輸消息(總線傳輸完成產生IIF中斷,在中斷中判斷是否傳輸完成)

4.傳輸完成後設置爲從模式(釋放總線)

5.失能I2C

  1. I2C總線傳輸中斷處理

    I2C總線驅動中斷處理,編程流程如圖 22所示。

    圖 22中斷處理

    技術實現

    I2C總線驅動框架

    I2C總線驅動實現基本功能,只要實現如圖 31中的四個函數即可。

    圖 31 I2C總線驅動四個基本函數

    i2cBusCreate

    i2cBusCreate函數初始化目標電路板i2c總線系統,調用i2cBusFuns函數初始化相應I2C總線系統並創建對應I2C適配器。根據在bspimx6ul/bsp/SylixOS/bsp/ulevk_board/bspboard.h中的I2C配置,初始化相應的I2C總線。

    i2cBusFuncs

    i2cBusFuncs函數用於初始化 i2c總線並獲取操作函數集,主要包括了設置芯片管腳複用__i2cIomuxConfig函數,初始化I2C控制器__i2cInit函數,返回操作函數集(總線傳輸Transfer函數,總線控制MasterCtl函數)。

    __i2cInit

    __i2cInit函數用於初始化I2C控制器,主要包括了初始化I2C使用的信號量,設置時鐘頻率,指定作從設備時的地址。

    __i2cTransfer

    __i2cTransfer函數爲I2C傳輸函數,用於在I2C總線上傳輸和接收數據。

    驅動程序框架

    整個驅動程序的框架如圖 32所示。

    圖 32驅動程序流程框架

    BSP中驅動配置

    根據imx6ul相關芯片手冊,配置寄存器地址並定義I2C通道相關信息結構。如程序清單 31所示。

    程序清單 31 I2C通道信息

    /*********************************************************************************************************
      i2c 通道相關信息
    *********************************************************************************************************/
    struct __i2c_channel{
        UINT                uiChannel;                                      /*  I2C總線通道號               */
        LW_OBJECT_HANDLE    I2C_hSignal;                                    /*  信號量                      */
        BOOL                I2C_bIsInit;                                    /*  是否初始化                  */
    
        int                 iStatus;                                        /*  狀態                        */
        int                 iBpsParam;                                      /*  波特率參數                  */
        PLW_I2C_MESSAGE     pi2cmsg;                                        /*  需要處理的消息              */
        int                 iMsgPtr;                                        /*  消息內部指針                */
        int                 iMsgNum;                                        /*  消息數量                    */
        int                 iMsgIndex;                                      /*  當前處理的 msg 下標         */
    };
    typedef struct __i2c_channel   __I2C_CHANNEL;
    typedef struct __i2c_channel  *__PI2C_CHANNEL;


    代碼實現

    I2C總線驅動代碼

    i2cBusCreate,i2cBusFuncs的具體實現

    i2cBusCreate函數與i2cBusFuncs函數初始化I2C,並將返回的操作函數集與i2c適配器綁定。如程序清單 32,程序清單 33所示。

    程序清單 32 i2cBusCreate的具體實現

    VOID i2cBusCreate (VOID)
    {
        /*
         *  打開I2Cx的總線驅動配置,在bspimx6ul/bsp/SylixOS/bsp/ulevk_board/bspboard.h文件中配置
         */
    ……
    #ifdef  CONFIG_BSP_I2C0
        pI2cFuncs = i2cBusFuns(0);                                          /*  創建 i2c0總線適配器         */
        if (pI2cFuncs) {
            API_I2cAdapterCreate("/bus/i2c/0", pI2cFuncs, 10, 1);
        }
    #endif
    ……
    }

    程序清單 33 i2cBusFuns的具體實現

     PLW_I2C_FUNCS  i2cBusFuns (UINT  uiChannel)
    {
    ……
        if (__i2cInit(&__Gimx6ulI2cChannels[uiChannel]) != ERROR_NONE) {
            return  (LW_NULL);
        }
        return  (&__Gimx6ulI2cFuncs[uiChannel]);
    }

    __i2cInit,__i2cHwInit的具體實現

    __i2cInit函數用於初始化I2C控制器,主要包括了初始化I2C使用的信號量,設置時鐘頻率,指定作從設備時的地址。如程序清單 34,程序清單 35所示。

    程序清單 34 __i2cInit的具體實現

    static INT  __i2cInit (__IMX6UL_I2C_CHANNEL  pI2cChannel)
    {
        ……
            /*
             *  初始化 I2C 控制器
             */
            if (__i2cHwInit(pI2cChannel->uiChannel) != ERROR_NONE) {
                printk(KERN_ERR "imx6ulI2cInit(): failed to init!\n");
                goto  __error_handle;
            }
        ……
    }

    程序清單 35 __i2cHwInit的具體實現

    static INT  __i2cHwInit (UINT  uiChannel)
    {
        ……
        /*
         *  設置時鐘頻率
         */
    __i2cSetI2cClk(uiChannel, I2C_BUS_FREQ_MAX);
    
        /*
         *  指定從設備地址
         */
        uiValue  =  readw(REG_I2C_IADR(uiChannel));
        uiValue &= ~IMXUL_DEFAULT_SLAVE_ID_MASK;
        uiValue |=  IMXUL_DEFAULT_SLAVE_ID;
        writew(uiValue, REG_I2C_IADR(uiChannel));
    ……
    }

    __i2cTransfer,__i2cTryTransfer的具體實現

    __i2cTransfer函數爲I2C傳輸函數,用於在I2C總線上傳輸和接收數據。如程序清單 36,程序清單 37所示。

    程序清單 36 __i2cTransfer的具體實現

     static INT  __i2cTransfer (UINT                uiChannel,
                            PLW_I2C_ADAPTER  pI2cAdapter,
                            PLW_I2C_MESSAGE  pI2cMsg,
                            INT                 iNum)
    {
        ……
        /*
         *  這裏使用了錯誤重傳的功能,若傳輸失敗則多次傳輸,由於實際應用中傳輸失敗是小概率事件,
         *  建議此功能放在用戶層實現,在驅動方便僅僅完成數據傳輸和接收更合適。
         */
        for (i = 0; i < pI2cAdapter->I2CADAPTER_iRetry; i++) {
            if (__i2cTryTransfer(uiChannel, pI2cAdapter, pI2cMsg, iNum) == iNum) {
                return  (iNum);
            } else {
                API_TimeSleep(LW_OPTION_WAIT_A_TICK);                       /*  等待一個機器週期重試        */
            }
        }
        ……
    }

    程序清單 37 __i2cTryTransfer的具體實現

    static INT  __i2cTryTransfer (UINT                uiChannel,
                              PLW_I2C_ADAPTER  pI2cAdapter,
                              PLW_I2C_MESSAGE  pI2cMsg,
                              INT                 iNum)
    {
    ……
        /*
         *  設置I2C時鐘頻率,清狀態位,使能I2C
         *  並判斷總線狀態,若IBB位爲0 (總線空閒)繼續,否則取消本次傳輸
         */
        if (__i2cTransferEnable(uiChannel) != 0) {
            return (PX_ERROR);
        }
    
        /*
         *  設置爲主模式+傳輸模式
         *  設置完後IBB位自動置1(總線繁忙),開始傳輸
         */
        if (__i2cTransferStart(uiChannel) != 0) {
            return (PX_ERROR);
        }
    
        /*
         *  完成設備地址發送後,進入收發消息函數
         */
        for (i = 0; i < iNum; i++, pI2cMsg++) {
            if (__i2cTransferMsg(uiChannel, pI2cMsg, iNum) != ERROR_NONE) {
                break;
            }
        }
    
        /*
         *  generate STOP by clearing MSTA bit
         *  (清除MSTA位使其停止傳輸)
         */
        __i2cTransferStop(uiChannel);
    
        /*
         *  disable the controller
         *  (禁止I2C控制器)
         */
    __i2cTransferDisable(uiChannel);
    ……
    }

    __i2cTransferEnable的具體實現

    __i2cTransferEnable函數使能I2C,設置時鐘頻率。

    __i2cTransferStart的具體實現

    __i2cTransferStart函數設置I2C控制器爲主模式(佔用總線)。

    __i2cTransferMsg的具體實現

    i2cTransferMsg函數判斷讀/寫模式,對應不同操作。如程序清單 38所示。

    程序清單 38 __i2cTransferMsg的具體實現

    static INT  __i2cTransferMsg ( UINT               uiChannel,
                                   PLW_I2C_MESSAGE    pI2cMsg,
                                   INT                iNUM)
    {
        ……
        if (pI2cMsg->I2CMSG_usFlag & LW_I2C_M_RD) {                         /*  讀取操作                    */
            /*
             *  do repeat-start
             *  (重複啓動)    (IEN_MSTA_MTX_RSTA)
             */
            ……
            /*
             *  send slave address again, but indicate read operation
             *  (發送從機器件地址,表明爲讀操作)
             */
            ……
            if (__i2cTransferTxByte(pucData, uiChannel) != 0) {             /*  發送從機地址,等待返回ACK   */
                return -1;
            }
    
            /*
             *  change to receive mode
             *  (設置爲接收模式)
             */
            ……
            /*
             *  若只有一個字節,設置選擇不發送ACK(最後一次傳輸不發送ACK)
             */
            ……
    
            /*
             *  dummy read
             *  (行假讀)
             */
            *pucData = readw(REG_I2C_I2DR(uiChannel));
    
            /*
             *  開始讀...
             */
            if (__i2cTransferRxBytes(pI2cMsg->I2CMSG_pucBuffer,
                                     uiChannel,
                                     pI2cMsg->I2CMSG_usLen) != 0) {
                return (PX_ERROR);
            }
    
        } else {                                                            /*  發送操作                    */
            /*
             *  Step 2: send slave address + read/write at the LSB
             *  (發送從機地址+讀寫LSB   設置爲寫位)
             */
            ……
            /*
             *  將從機地址數據寫入寄存器,等待ACK返回
             */
            ……
            /*
             *  設定一個長度,循環往寄存器寫,等待ACK返回
             */
            pucData = pI2cMsg->I2CMSG_pucBuffer;
            for (i = 0; i < pI2cMsg->I2CMSG_usLen; i++) {
                /*
                 * send device register value
                 * (發送寄存器地址 / 信息)
                 */
                if ((iRet = __i2cTransferTxByte(pucData, uiChannel)) != 0) {
                    break;
                }
                pucData++;
            }
    }
    ……
    }

    __i2cTransferTxByte的具體實現

    如程序清單 39,程序清單 310所示。

    程序清單 39 __i2cTransferTxByte的具體實現

    static INT  __i2cTransferTxByte (UINT8  *pChar, UINT  uiChannel)
    {
        UINT uiValue = 0;
    
        /*
         *  clear both IAL and IIF bits
         *  (清除IAL和IIF位)
         */
        ……
        /*
         *  write to data register
         *  (向寄存器中寫入數據,從機地址 / 發送信息)
         *  0x0E << 1 + write + ack
         *  0x07 + ack
         *  0x0e << 1 + read + ack
         *  xx + ack
         */
        writew((*pChar), (REG_I2C_I2DR(uiChannel)));
    
        /*
         *  wait for transfer of byte to complete
         *  (等待傳輸完成)
         */
        return __i2cTransferWaitOpDone(uiChannel, 1);
    }

    程序清單 310 __i2cTransferWaitOpDone的具體實現

    static INT  __i2cTransferWaitOpDone (UINT  uiChannel, INT  iIsTx)
    {
        ……
        /*
         *  Loop until we get an interrupt
         *  (循環等待,直到我們得到一箇中斷,若沒有產生中斷,返回-10)
         */
        while (!(readw(REG_I2C_I2SR(uiChannel)) & IIF) && (--i > 0));
        if (i <= 0) {
            printk("I2C Error: timeout unexpected\n");
            return (ERR_NO_IIF);
        }
    
        /*
         *  Clear the interrupts
         *  (清除中斷位) 
         */
        ……
        /*
         *  Check for arbitration lost
         *  (檢查仲裁位,產生1爲仲裁丟失,返回-3)
         */
        if (readw(REG_I2C_I2SR(uiChannel)) & IAL) {
            printk("Error  Arbitration lost\n");
            return (ERR_IAL_LOST);
        }
    
        /*
         *  Check for ACK received in transmit mode
         *  (傳輸模式中檢查是否收到ACK)
         */
        if (iIsTx) {                                                        /*  iIsTx參數傳入爲1            */
            if (readw(REG_I2C_I2SR(uiChannel)) & RXAK) {
                /*
                 * 沒有收到ACK,清除MSTA位使其停止傳輸
                 */
                printk("Error no ack received\n");
                __i2cTransferStop(uiChannel);                               /*  停止 / 將主從模式位設置爲0  */
    
                return (ERR_NO_ACK);
            }
        }
        ……
    }

    __i2cTransferRxBytes的具體實現

    如程序清單 311所示。

    程序清單 311 __i2cTransferRxBytes的具體實現

    static INT  __i2cTransferRxBytes (UINT8  *pChar,
                                  UINT    uiChannel,
                                  INT     iSize)
    {
        ……
        /*
         *  等待傳輸完成
         */
        for (i = 0; iSize > 0; iSize--, i++) {
            if (__i2cTransferWaitOpDone(uiChannel, 0) != 0) {
                return (PX_ERROR);
            }
    
            /*
             *  接下來的兩個if指令設置爲下一個讀取控制寄存器的值
             *  若iSize == 1則此次爲最後一次且已完成傳輸(清除MSTA位)
             *  若iSize == 2則下次爲最後一次傳輸,不發送ACK信號(禁止TXAK位)
             */
            ……
    
            /*
             *  真正開始讀取數據
             */
            pChar[i] = readw(REG_I2C_I2DR(uiChannel));
        }
        ……
    }

    __i2cTransferStop的具體實現

    __i2cTransferStop函數設置I2C控制器爲從模式(釋放總線)。

    __i2cTransferDisable的具體實現

    __i2cTransferDisable函數失能I2C。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章