博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SylixOS iMX6平台I2C总线驱动
阅读量:6927 次
发布时间:2019-06-27

本文共 10175 字,大约阅读时间需要 33 分钟。

hot3.png

原理概述

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。

转载于:https://my.oschina.net/u/3248800/blog/856288

你可能感兴趣的文章
hash类型的应用场景 —— Redis实战经验
查看>>
RISC-V架构受重视,上海公开扶持政策
查看>>
设计模式——策略模式
查看>>
全志 A64 Linux内核等待队列poll ---阻塞与非阻塞
查看>>
Python小技巧:Python3中利用tab键进行代码提示
查看>>
NERD_commenter插件快捷键
查看>>
分布式关系型数据库服务 DRDS 支持跨 Schema 联机查询及 DML 事务写入等多项能力发布...
查看>>
Nikola Moto宣布完成超额融资,将于氢能源卡车市场正面对战特斯拉
查看>>
源的理解
查看>>
Java的String和StringBuffer和StringBuilder详解
查看>>
我的生命该如何度过?
查看>>
复杂事件处理(CEP)语句
查看>>
华为设备默认console密码
查看>>
Apache Spark 系列技术直播 - 从 Spark Streaming 到 Structured Streaming
查看>>
Android SoundTouch(处理音频)
查看>>
linux下使用 boost.python 调用c++动态库
查看>>
自定义美化UL OL发光列表
查看>>
单点登录CAS系列8-客户端配置单点登出
查看>>
Linux性能监控之vmstat和dstat
查看>>
IIS&ASP.NET 站点IP跳转到域名
查看>>