当前位置: 首页 > news >正文

SylixOS iMX6平台I2C总线驱动

2019独角兽企业重金招聘Python工程师标准>>> 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_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

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

相关文章:

  • HTML初学者常用标签及属性
  • jquery特效 商品SKU属性规格选择实时联动
  • centos
  • Greenplum 简单性能测试与分析
  • 智障的对话
  • 一维数组打乱顺序shuffle函数
  • seajs
  • 完成数据的打通-豌豆荚被阿里巴巴收购后的168天
  • 神奇的模块--谷歌开源 Python Fire:自动生成命令行接口
  • PHP快速入门 如何操作MySQL
  • 【Java基础】5、java中的匿名内部类
  • Android textview及其子类
  • 正则表达式匹配IP和月日
  • Oracle 快速插入1000万条数据的实现方式
  • Fedora 25上安装微软SQL Server过程
  • [数据结构]链表的实现在PHP中
  • 【划重点】MySQL技术内幕:InnoDB存储引擎
  • exports和module.exports
  • Java 最常见的 200+ 面试题:面试必备
  • Java到底能干嘛?
  • Quartz初级教程
  • Sass Day-01
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • 工作中总结前端开发流程--vue项目
  • 缓存与缓冲
  • 判断客户端类型,Android,iOS,PC
  • 如何胜任知名企业的商业数据分析师?
  • 一道闭包题引发的思考
  • 再谈express与koa的对比
  • “十年磨一剑”--有赞的HBase平台实践和应用之路 ...
  • AI算硅基生命吗,为什么?
  • UI设计初学者应该如何入门?
  • # MySQL server 层和存储引擎层是怎么交互数据的?
  • #Js篇:单线程模式同步任务异步任务任务队列事件循环setTimeout() setInterval()
  • (2)(2.10) LTM telemetry
  • (iPhone/iPad开发)在UIWebView中自定义菜单栏
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (附源码)springboot青少年公共卫生教育平台 毕业设计 643214
  • (附源码)SSM环卫人员管理平台 计算机毕设36412
  • (免费分享)基于springboot,vue疗养中心管理系统
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用
  • (未解决)macOS matplotlib 中文是方框
  • (一)插入排序
  • (原)本想说脏话,奈何已放下
  • (转)Windows2003安全设置/维护
  • (转)利用ant在Mac 下自动化打包签名Android程序
  • (转)母版页和相对路径
  • .[hudsonL@cock.li].mkp勒索加密数据库完美恢复---惜分飞
  • .mkp勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET Core中Emit的使用
  • .Net mvc总结
  • .net web项目 调用webService
  • .NET 读取 JSON格式的数据