I2C總線驅動是I2C適配器的軟件實現,提供I2C適配器與從設備間完成數據通訊的能力,好比起始,中止,應答信號和MasterXfer的實現函數。驅動程序包含初始化I2C總線控制器__i2cHwInit函數,操做函數集(總線傳輸__i2cTransfer函數,總線控制__i2cMasterCtl函數)。編程
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寄存器來獲取。事件
圖 21 I2C編程狀態ci
imx6ul的I2C總線驅動代碼在bspimx6ul/driver_module/iic_drv/src/目錄下,如圖 31所示:get
圖 31 I2C總線驅動目錄
imx6ul的I2C總線驅動代碼在bspimx6ul/driver_module/iic_drv/src/目錄下,如__所示:
I2C總線驅動實現基本功能,只要實現如圖 32中的四個函數便可。
圖 32 I2C總線驅動四個基本函數
該函數初始化目標電路板i2c總線系統,調用i2cBusFuns函數初始化相應I2C總線系統並建立對應I2C適配器。根據在bspimx6ul/bsp/SylixOS/bsp/ulevk_board/bspboard.h中的I2C配置,初始化相應的I2C總線。
該函數用於初始化 i2c 總線並獲取操做函數集,主要包括了設置芯片管腳複用__i2cIomuxConfig函數,初始化I2C控制器__i2cInit函數,返回操做函數集(總線傳輸Transfer函數,總線控制MasterCtl函數)。
該函數用於初始化I2C控制器,主要包括了初始化I2C使用的信號量,設置時鐘頻率,指定做從設備時的地址。
該函數爲I2C傳輸函數,用於在I2C總線上傳輸和接收數據。
整個驅動程序的框架如圖 33所示:
圖 33 驅動程序流程框架
根據imx6ul相關芯片手冊,配置寄存器地址並定義I2C通道相關信息結構。如圖 34所示:
圖 34 I2C通道信息
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]); }
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)); …… }
__i2cTransfer,__i2cTryTransfer的具體實現
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); …… }
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); }
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++; } } …… }
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); } } …… }
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)); } …… }
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)); }
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)); }