SylixOS -- imx6ul平台SylixOS I2C总线驱动开发

  1. 原理概述

  2. I2C总线驱动概述

    I2C总线驱动是I2C适配器的软件实现,提供I2C适配器与从设备间完成数据通信的能力,比如起始,停止,应答信号和MasterXfer的实现函数。驱动程序包含初始化I2C总线控制器__i2cHwInit函数,操作函数集(总线传输__i2cTransfer函数,总线控制__i2cMasterCtl函数)。

  3. 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寄存器来获取。

  4. I2C总线传输编程状态图

    21 I2C编程状态

  5. 技术实现

  6. I2C总线驱动框架

    imx6ul的I2C总线驱动代码在bspimx6ul/driver_module/iic_drv/src/目录下,如图 31所示:

    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的具体实现
  16. VOIDi2cBusCreate (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]);

    }



  17. __i2cIomuxConfig的具体实现

    staticVOID__i2cIomuxConfig (UINTuiChannel)

    {

    ……

    case 0:/* i2c1的管脚复用 */

    IomuxConfig(__I2C1_SCL_REG,

    __I2C1_SCL_MASK,

    __I2C1_SCL_VAL);

    IomuxConfig(__I2C1_SDA_REG,

    __I2C1_SDA_MASK,

    __I2C1_SDA_VAL);

    break;

    ……

    }

     

  18. __i2cInit,__i2cHwInit的具体实现

    staticINT__i2cInit (__IMX6UL_I2C_CHANNELpI2cChannel)

    {

    ……

    /*

    *初始化 I2C控制器

    */

    if (__i2cHwInit(pI2cChannel->uiChannel) != ERROR_NONE) {

    printk(KERN_ERR"imx6ulI2cInit(): failed toinit!\n");

    goto__error_handle;

    }

    ……

    }

     

    staticINT__i2cHwInit (UINTuiChannel)

    {

    ……

    /*

    *设置时钟频率

    */

    __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));

    ……

    }

     

  19. __i2cTransfer,__i2cTryTransfer的具体实现

    staticINT__i2cTransfer (UINTuiChannel,

    PLW_I2C_ADAPTERpI2cAdapter,

    PLW_I2C_MESSAGEpI2cMsg,

    INTiNum)

    {

    ……

    /*

    *这里使用了错误重传的功能,若传输失败则多次传输,由于实际应用中传输失败是小概率事件,

    *建议此功能放在用户层实现,在驱动方便仅仅完成数据传输和接收更合适。

    */

    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);/*等待一个机器周期重试 */

    }

    }

    ……

    }

     

    staticINT__i2cTryTransfer (UINTuiChannel,

    PLW_I2C_ADAPTERpI2cAdapter,

    PLW_I2C_MESSAGEpI2cMsg,

    INTiNum)

    {

    ……

    /*

    *设置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);

    ……

    }

     

  20. __i2cTransferEnable的具体实现

    staticINT__i2cTransferEnable (UINTuiChannel)

    {

    UINTuiValue = 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);

    }

     

    staticVOID__i2cSetI2cClk (UINTuiChannel,UINT32uiBaud)

    {

    /*

    *获取系统时钟

    */

    UINT32uiSrcClk =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));

    }

     

    staticINT__i2cTransferBusFree (UINTuiChannel)

    {

    INTi =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);

    }

     

  21. __i2cTransferStart的具体实现

    staticINT__i2cTransferStart (UINTuiChannel)

    {

    UINTuiValue = 0;

     

    /*

    * Select master mode, assert START signal and also indicate TX mode

    * (选择主机模式,表明传输模式,开始信号)

    */

    uiValue =readw(REG_I2C_I2CR(uiChannel));

    uiValue &= ~BIT_I2C_I2CR_MSTA_MTX_MASK;

    uiValue |=BIT_I2C_I2CR_MSTA_MTX;

    writew(uiValue,REG_I2C_I2CR(uiChannel));

     

    /*

    * make sure bus is busy after the START signal

    * (确保开始后的总线信号保持繁忙,否则返回-1)

    */

    if (__i2cTransferBusBusy(uiChannel) != 0) {

    return (PX_ERROR);

    }

     

    return (ERROR_NONE);

    }

     

    staticINT__i2cTransferBusBusy (UINTuiChannel)

    {

    INTi =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);

    }

     

  22. __i2cTransferMsg的具体实现

    staticINT__i2cTransferMsg ( UINTuiChannel,

    PLW_I2C_MESSAGEpI2cMsg,

    INTiNUM)

    {

    ……

    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++;

    }

    }

    ……

    }

     

  23. __i2cTransferTxByte的具体实现

    staticINT__i2cTransferTxByte (UINT8 *pChar,UINTuiChannel)

    {

    UINTuiValue = 0;

     

    /*

    * clear both IAL and IIF bits

    * (清除IALIIF)

    */

    ……

    /*

    * 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);

    }

     

    staticINT__i2cTransferWaitOpDone (UINTuiChannel,INTiIsTx)

    {

    ……

    /*

    * 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 noack received\n");

    __i2cTransferStop(uiChannel);/*停止 /将主从模式位设置为0 */

     

    return (ERR_NO_ACK);

    }

    }

    ……

    }

     

  24. __i2cTransferRxBytes的具体实现

    staticINT__i2cTransferRxBytes (UINT8 *pChar,

    UINTuiChannel,

    INTiSize)

    {

    ……

    /*

    *等待传输完成

    */

    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));

    }

    ……

    }

     

  25. __i2cTransferStop的具体实现

    staticVOID__i2cTransferStop (UINTuiChannel)

    {

    ……

    /*

    * 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));

    }

     

  26. __i2cTransferDisable的具体实现

    staticVOID__i2cTransferDisable (UINTuiChannel)

    {

    ……

    /*

    * disable the controller

    * (禁能I2C)

    */

    uiValue =readw(REG_I2C_I2SR(uiChannel));

    uiValue &= ~CLEAR_ALL_MASK;

    uiValue |=CLEAR_ALL;

    writew(uiValue,REG_I2C_I2SR(uiChannel));

    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Evan_ZGYF丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值