iMX6平臺 SylixOS I2C總線驅動開發

  1. 原理概述

  2. I2C總線驅動概述

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

  3. Imx6ul控制器的硬件描述

    imx6ul處理器內部集成了一個I2C控制器,經過五個寄存器來進行控制:框架

    I2Cx_IADR I2C地址寄存器函數

    I2Cx_IFDR I2C分頻寄存器oop

    I2Cx_I2CR I2C控制寄存器ui

    I2Cx_I2SR I2C狀態寄存器spa

    I2Cx_I2DR I2C數據寄存器code

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

  4. I2C總線傳輸編程狀態圖

    圖 21 I2C編程狀態ci

  5. 技術實現

  6. I2C總線驅動框架

    imx6ul的I2C總線驅動代碼在bspimx6ul/driver_module/iic_drv/src/目錄下,如圖 31所示:get

    圖 31 I2C總線驅動目錄

    imx6ul的I2C總線驅動代碼在bspimx6ul/driver_module/iic_drv/src/目錄下,如__所示:

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

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

  7. 函數i2cBusCreate

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

  8. 函數i2cBusFuns

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

  9. 函數__i2cInit

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

  10. 函數__i2cTransfer

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

  11. 驅動程序框架

    整個驅動程序的框架如圖 33所示:

    圖 33 驅動程序流程框架

  12. BSP中驅動配置

    根據imx6ul相關芯片手冊,配置寄存器地址並定義I2C通道相關信息結構。如圖 34所示:

    圖 34 I2C通道信息

  13. 代碼實現

  14. I2C總線驅動代碼

  15. i2cBusCreate,i2cBusFuncs的具體實現

    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
    ……
    }
    PLW_I2C_FUNCS  i2cBusFuns (UINT  uiChannel)
    {
        /*
         *  設置芯片管腳分配,SylixOS 計數從零開始,而 IMX6UL 手冊是從 1 開始,須要注意
         */
        __i2cIomuxConfig(uiChannel);
    
        /*
         *  初始化控制器
         */
        if (__i2cInit(&__Gimx6ulI2cChannels[uiChannel]) != ERROR_NONE) {
            return  (LW_NULL);
        }
    
        /*
         *  返回操做函數集
         */
        return  (&__Gimx6ulI2cFuncs[uiChannel]);
    }
  16. __i2cIomuxConfig的具體實現

    static VOID  __i2cIomuxConfig (UINT  uiChannel)
    {
    ……
        case 0:                                                             /*  i2c1的管腳複用              */
           IomuxConfig(__I2C1_SCL_REG,
                       __I2C1_SCL_MASK,
                       __I2C1_SCL_VAL);
           IomuxConfig(__I2C1_SDA_REG,
                       __I2C1_SDA_MASK,
                       __I2C1_SDA_VAL);
           break;
        ……
    }
    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));
    ……
    }

     

  17. __i2cTransfer,__i2cTryTransfer的具體實現

  18. 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);                       /*  等待一個機器週期重試        */
            }
        }
        ……
    }
    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);
    ……
    }

     

  19. __i2cTransferEnable的具體實現

    static INT  __i2cTransferEnable (UINT  uiChannel)
    {
        UINT    uiValue = 0;
    
        /*
         *  If the request has device info attached and it has a non-zero bit rate, then
         *  change the clock to the specified rate.
         *  (若是請求的設備爲非零波特率,改變爲指定時鐘頻率)
         */
        __i2cSetI2cClk(uiChannel, SPECIFIED_RATE);
    
        /*
         *  clear the status register
         *  (清空狀態寄存器)
         */
        uiValue  =  readw(REG_I2C_I2SR(uiChannel));
        uiValue &= ~CLEAR_ALL_MASK;
        uiValue |=  CLEAR_ALL;
        writew(uiValue, REG_I2C_I2SR(uiChannel));
    
        /*
         *  enable the I2C controller
         *  (使能I2c控制器)
         */
        uiValue  = readw(REG_I2C_I2CR(uiChannel));
        uiValue &= ~BIT_I2C_I2CR_IEN_MASK;
        uiValue |= BIT_I2C_I2CR_IEN;
        writew(uiValue, REG_I2C_I2CR(uiChannel));
    
        /*
         *  Check if bus is free, if not return error
         *  (檢測總線是否空閒,若被佔用返回-1)
         */
        if (__i2cTransferBusFree(uiChannel) != 0) {
            return (PX_ERROR);
        }
    
        return (ERROR_NONE);
    }
    static VOID  __i2cSetI2cClk (UINT  uiChannel, UINT32  uiBaud)
    {
        /*
         *  獲取系統時鐘
         */
        UINT32 uiSrcClk = ccmMainClkGet(IPG_PER_CLK);
        ……
        /*
         *  設置I2C時鐘頻率
         */
        uiValue  =  readw(REG_I2C_IFDR(uiChannel));
        uiValue &= ~BIT_I2C_IFDR_IC_MASK;
        uiValue |=  i2c_clk_div[ucIndex][1];
        writew(uiValue, REG_I2C_IFDR(uiChannel));
    }
    static INT  __i2cTransferBusFree (UINT  uiChannel)
    {
        INT i = WAIT_RXAK_LOOPS;
    
        /*
         *  一段時間內循環判斷
         */
        while ((readw(REG_I2C_I2SR(uiChannel)) & IBB) && (--i > 0));
    
        if (i <= 0) {
            printk("Error: I2C Bus not free!\n");
            return (ERROR);
        }
    
        return (ERROR_NONE);
    }
    static INT  __i2cTransferBusBusy (UINT  uiChannel)
    {
        INT i = WAIT_BUSY_LOOPS;
    
        while (!(readw(REG_I2C_I2SR(uiChannel)) & IBB) && (--i > 0))
    
        if (i <= 0) {
            printk("I2C Error: timeout in \n");
            return (PX_ERROR);
        }
    
        return (ERROR_NONE);
    }

     

  20. __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++;
            }
    }
    ……
    }

     

  21. __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);
    }
    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);
            }
        }
        ……
    }
     

     

  22. __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));
        }
        ……
    }

     

  23. __i2cTransferStop的具體實現

    static VOID  __i2cTransferStop (UINT  uiChannel)
    {
        ……
        /*
         *  MSTA位設置爲0,便可中止傳輸
         */
        uiValue  =  readw(REG_I2C_I2CR(uiChannel));
        uiValue &= ~BIT_I2C_I2CR_MSTA_MASK;
        uiValue |=  BIT_I2C_I2CR_MSTA_0;
        writew(uiValue, REG_I2C_I2CR(uiChannel));
    }

     

  24. __i2cTransferDisable的具體實現

    static VOID  __i2cTransferDisable (UINT  uiChannel)
    {
        ……
        /*
         *  disable the controller
         *  (禁能I2C)
         */
        uiValue  =  readw(REG_I2C_I2SR(uiChannel));
        uiValue &= ~CLEAR_ALL_MASK;
        uiValue |=  CLEAR_ALL;
        writew(uiValue, REG_I2C_I2SR(uiChannel));
    }
相關文章
相關標籤/搜索