SylixOS iMX6平臺I2C總線驅動

原理概述 編程

I2C總線驅動概述 框架

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

Imx6ul控制器的硬件描述 oop

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

I2Cx_IADRspa

I2C地址寄存器指針

I2Cx_IFDR事件

I2C分頻寄存器get

I2Cx_I2CRit

I2C控制寄存器

I2Cx_I2SR

I2C狀態寄存器

I2Cx_I2DR

I2C數據寄存器

經過I2Cx_I2CRI2Cx_IFDRI2Cx_I2DRI2Cx_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總線驅動代碼

    i2cBusCreatei2cBusFuncs的具體實現

    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

相關文章
相關標籤/搜索