SylixOS iMX6平台I2C总线驱动
2017-03-10 14:33
281 查看
原理概述
I2C总线驱动概述
I2C总线驱动是I2C适配器的软件实现,提供I2C适配器与从设备间完成数据通信的能力,比如起始,停止,应答信号和MasterXfer的实现函数。驱动程序包含初始化I2C总线控制器__i2cHwInit函数,操作函数集(总线传输__i2cTransfer函数,总线控制__i2cMasterCtl函数)。
Imx6ul控制器的硬件描述
imx6ul处理器内部集成了一个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
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总线驱动代码
i2cBusCreate,i2cBusFuncs的具体实现
i2cBusCreate函数与i2cBusFuncs函数初始化I2C,并将返回的操作函数集与i2c适配器绑定。如程序清单
32,程序清单 33所示。
程序清单 32 i2cBusCreate的具体实现
程序清单 33 i2cBusFuns的具体实现
__i2cInit,__i2cHwInit的具体实现
__i2cInit函数用于初始化I2C控制器,主要包括了初始化I2C使用的信号量,设置时钟频率,指定作从设备时的地址。如程序清单
34,程序清单 35所示。
程序清单 34 __i2cInit的具体实现
程序清单 35 __i2cHwInit的具体实现
__i2cTransfer,__i2cTryTransfer的具体实现
__i2cTransfer函数为I2C传输函数,用于在I2C总线上传输和接收数据。如程序清单
36,程序清单 37所示。
程序清单 36 __i2cTransfer的具体实现
程序清单 37 __i2cTryTransfer的具体实现
__i2cTransferEnable的具体实现
__i2cTransferEnable函数使能I2C,设置时钟频率。
__i2cTransferStart的具体实现
__i2cTransferStart函数设置I2C控制器为主模式(占用总线)。
__i2cTransferMsg的具体实现
i2cTransferMsg函数判断读/写模式,对应不同操作。如程序清单
38所示。
程序清单 38 __i2cTransferMsg的具体实现
__i2cTransferTxByte的具体实现
如程序清单 39,程序清单 310所示。
程序清单 39 __i2cTransferTxByte的具体实现
程序清单 310 __i2cTransferWaitOpDone的具体实现
__i2cTransferRxBytes的具体实现
如程序清单 311所示。
程序清单 311 __i2cTransferRxBytes的具体实现
__i2cTransferStop的具体实现
__i2cTransferStop函数设置I2C控制器为从模式(释放总线)。
__i2cTransferDisable的具体实现
__i2cTransferDisable函数失能I2C。
附录:
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数据寄存器 |
I2C总线传输编程流程
I2C总线驱动传输函数,主要编程流程如图 21所示。
图 21 I2C编程状态
传输大致流程:
1.使能I2C控制器
2.设置为主模式(占用总线)
3.传输消息(总线传输完成产生IIF中断,在中断中判断是否传输完成)
4.传输完成后设置为从模式(释放总线)
5.失能I2C
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。
附录:
/********************************************************************************************************* ** ** 中国软件开源组织 ** ** 嵌入式实时操作系统 ** ** SylixOS(TM) LW : long wing ** ** Copyright All Rights Reserved ** **--------------文件信息-------------------------------------------------------------------------------- ** ** 文 件 名: iic_drv.c ** ** 创 建 人: ZhuGe.YiFan (诸葛一帆) ** ** 文件创建日期: 2016 年 12 月 17 日 ** ** 描 述: imx6ulrm I2C 总线驱动 *********************************************************************************************************/ #define __SYLIXOS_KERNEL #include #include #include "bspboard.h" #include "pinmux/iomuxConfig.h" /********************************************************************************************************* I2C 控制器相关定义 ********************************************************************************************************/ #define I2C_CHAN_NUM (4) /* IIC 通道个数 */ #define I2C_CHAN_1 (0) /* IIC 通道1 */ #define I2C_CHAN_2 (1) /* IIC 通道2 */ #define I2C_CHAN_3 (2) /* IIC 通道3 */ #define I2C_CHAN_4 (3) /* IIC 通道4 */ #define I2C_WRITE (0) /* 传输从机地址时+写位 */ #define I2C_READ (1) /* 传输从机地址时+读位 */ #define I2C_BUS_FREQ_MAX (400000) /* 设置时钟频率 */ #define SPECIFIED_RATE (100000) /* 设置时钟频率 */ #define BIT_I2C_IFDR_IC_MASK (0x3f) /* 时钟频率参数掩码 */ #define BIT_I2C_I2SR_IBB (0x0020) /* I2SR寄存器IBB位 */ #define BIT_I2C_I2SR_IAL (0x0010) /* I2SR寄存器IAL位 */ #define BIT_I2C_I2SR_IIF (0x0002) /* I2SR寄存器IIF位 */ #define BIT_I2C_I2SR_RXAK (0x0001) /* I2SR寄存器RXAK位 */ #define BIT_I2C_I2SR_TXAK (0x0008) /* I2CR寄存器TXAK位 */ /********************************************************************************************************* I2C 总线状态 *********************************************************************************************************/ #define __I2C_BUS_STATE_IDLE 0 /* 总线空闲 */ #define __I2C_BUS_STATE_START 1 /* 总线启动 */ #define __I2C_BUS_STATE_READ 2 /* 读数据 */ #define __I2C_BUS_STATE_WRITE 3 /* 写数据 */ #define __I2C_BUS_STATE_STOP 4 /* 总线结束 */ /********************************************************************************************************* I2C 寄存器移位相关宏 ********************************************************************************************************/ #define BIT_I2C_I2CR_RSTA (1 I2CMSG_usLen - 1)) /* 消息的最后一个字节 */ #define __I2C_BUS_IS_MSGEND(pI2cChannel) (pI2cChannel->iMsgPtr >= pI2cChannel->pi2cmsg->I2CMSG_usLen) /* 消息结束 */ /********************************************************************************************************* 全局变量 *********************************************************************************************************/ static __I2C_CHANNEL __Gi2cChannel[I2C_CHAN_NUM] = { /* I2C通道号数组全局 */ {I2C_CHAN_1}, {I2C_CHAN_2}, {I2C_CHAN_3}, {I2C_CHAN_4} }; /********************************************************************************************************* I2C 时钟频率设置参数数组 *********************************************************************************************************/ static UINT16 __GusI2cClkDiv[50][2] = { { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 }, { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 }, { 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 }, { 52, 0x05 }, { 56, 0x29 }, { 60, 0x06 }, { 64, 0x2A }, { 72, 0x2B }, { 80, 0x2C }, { 88, 0x09 }, { 96, 0x2D }, { 104, 0x0A }, { 112, 0x2E }, { 128, 0x2F }, { 144, 0x0C }, { 160, 0x30 }, { 192, 0x31 }, { 224, 0x32 }, { 240, 0x0F }, { 256, 0x33 }, { 288, 0x10 }, { 320, 0x34 }, { 384, 0x35 }, { 448, 0x36 }, { 480, 0x13 }, { 512, 0x37 }, { 576, 0x14 }, { 640, 0x38 }, { 768, 0x39 }, { 896, 0x3A }, { 960, 0x17 }, { 1024, 0x3B }, { 1152, 0x18 }, { 1280, 0x3C }, { 1536, 0x3D }, { 1792, 0x3E }, { 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D }, { 3072, 0x1E }, { 3840, 0x1F } }; /********************************************************************************************************* ** 函数名称: __imx6ulI2cStart ** 功能描述: i2c 控制器发送启动字节 ** 输 入 : usAddr 地址 ** usFlag 标志 ** uiChannel 通道号 ** 输 出 : NONE ** 全局变量: ** 调用模块: *********************************************************************************************************/ static VOID __imx6ulI2cStart (UINT16 usAddr, UINT16 usFlag, UINT uiChannel) { UINT uiValue; __Gi2cChannel[uiChannel].iStatus = __I2C_BUS_STATE_START; /* 启动总线 */ switch(uiChannel) { case I2C_CHAN_1: API_InterVectorEnable(IMX_INT_I2C1); /* 启动总线中断 */ break; case I2C_CHAN_2: API_InterVectorEnable(IMX_INT_I2C2); /* 启动总线中断 */ break; case I2C_CHAN_3: API_InterVectorEnable(IMX_INT_I2C3); /* 启动总线中断 */ break; case I2C_CHAN_4: API_InterVectorEnable(IMX_INT_I2C4); /* 启动总线中断 */ break; default: break; } uiValue = readw(REG_I2C_I2CR(uiChannel)); /* 使能I2C中断 */ uiValue |= BIT_I2C_I2CR_IIEN; writew(uiValue, REG_I2C_I2CR(uiChannel)); if (usFlag & LW_I2C_M_RD) { /* 读操作 */ uiValue = readw(REG_I2C_I2CR(uiChannel)); /* 重启总线 需要有此操作 */ uiValue |= BIT_I2C_I2CR_RSTA; writew(uiValue, REG_I2C_I2CR(uiChannel)); writew(((usAddr uiChannel)); /* 清中断 */ uiValue = readw(REG_I2C_I2SR(pI2cChannel->uiChannel)); if ((uiValue & BIT_I2C_I2SR_IAL) > 0) { /* 中断仲裁 */ errno = ENXIO; __imx6ulI2cStop(pI2cChannel->uiChannel); /* 停止总线 */ } switch (pI2cChannel->iStatus) { /* 处理不同状态 */ case __I2C_BUS_STATE_IDLE: break; /* 直接退出 */ case __I2C_BUS_STATE_START: if ((__I2C_BUS_IS_LASTMSG(pI2cChannel) && /* 没有待处理的消息 */ (pI2cChannel->pi2cmsg->I2CMSG_usLen == 0))) { errno = ENXIO; __imx6ulI2cStop(pI2cChannel->uiChannel); /* 停止总线 */ break; } if (pI2cChannel->pi2cmsg->I2CMSG_usFlag & LW_I2C_M_RD) { /* 进入读状态 */ pI2cChannel->iStatus = __I2C_BUS_STATE_READ; uiValue = readw(REG_I2C_I2CR(pI2cChannel->uiChannel)); /* 主收模式启动 */ uiValue &= ~BIT_I2C_I2CR_MTX; writew(uiValue, REG_I2C_I2CR(pI2cChannel->uiChannel)); if (pI2cChannel->pi2cmsg->I2CMSG_usLen == 1) { /* 若为最后一个消息,不发送ACK */ uiValue = readw(REG_I2C_I2CR(pI2cChannel->uiChannel)); uiValue |= BIT_I2C_I2CR_TXAK; writew(uiValue, REG_I2C_I2CR(pI2cChannel->uiChannel)); } readw(REG_I2C_I2DR(pI2cChannel->uiChannel)); /* dummy read 空读 */ } else { pI2cChannel->iStatus = __I2C_BUS_STATE_WRITE; /* 进入写状态 */ goto __prepare_write; } break; case __I2C_BUS_STATE_READ: if (__I2C_BUS_IS_MSGEND(pI2cChannel)) { /* 当前为最后一个字节 */ if (__I2C_BUS_IS_LASTMSG(pI2cChannel)) { /* 这是最后一个消息块 */ pI2cChannel->iMsgPtr = 0; pI2cChannel->iMsgIndex++; /* 保证与消息数量相同 */ __imx6ulI2cStop(pI2cChannel->uiChannel); break; } else { pI2cChannel->iMsgPtr = 0; pI2cChannel->iMsgIndex++; pI2cChannel->pi2cmsg++; /* 开始接收下一个消息 */ } } else if (__I2C_BUS_IS_MSGLAST(pI2cChannel)) { /* 当前为倒数第二字节 */ if (__I2C_BUS_IS_LASTMSG(pI2cChannel)) { /* 这是最后一个消息块 */ uiValue = readw(REG_I2C_I2CR(pI2cChannel->uiChannel)); uiValue |= BIT_I2C_I2CR_TXAK; writew(uiValue, REG_I2C_I2CR(pI2cChannel->uiChannel)); /* 下一个字节不发送ACK */ } } ucByte = (BYTE)readw(REG_I2C_I2DR(pI2cChannel->uiChannel)); /* 读取数据 */ pI2cChannel->pi2cmsg->I2CMSG_pucBuffer[pI2cChannel->iMsgPtr] = ucByte; pI2cChannel->iMsgPtr++; break; case __I2C_BUS_STATE_WRITE: if ((ulStatus & BIT_I2C_I2SR_RXAK) && /* 需要 ACK 但没有接收到 ACK */ !(pI2cChannel->pi2cmsg->I2CMSG_usFlag & LW_I2C_M_IGNORE_NAK)) { errno = ECONNREFUSED; __imx6ulI2cStop(pI2cChannel->uiChannel); /* 停止总线 */ break; } __prepare_write: if (!__I2C_BUS_IS_MSGEND(pI2cChannel)) { /* 当前消息还没有发送完 */ ucByte = pI2cChannel->pi2cmsg->I2CMSG_pucBuffer[pI2cChannel->iMsgPtr]; pI2cChannel->iMsgPtr++; writew((ucByte), (REG_I2C_I2DR(pI2cChannel->uiChannel))); } else if (!__I2C_BUS_IS_LASTMSG(pI2cChannel)) { /* 还有剩余的消息没有发送完 */ pI2cChannel->iMsgPtr = 0; pI2cChannel->iMsgIndex++; pI2cChannel->pi2cmsg++; /* 开始发送下一个消息 */ if (pI2cChannel->pi2cmsg->I2CMSG_usFlag & LW_I2C_M_NOSTART) { /* 不需要起始位 */ if (pI2cChannel->pi2cmsg->I2CMSG_usFlag & LW_I2C_M_RD) { /* 读操作 */ /* * 控制器换向, 必须重启总线 */ __imx6ulI2cStop(pI2cChannel->uiChannel); /* 不能进行读操作 */ } goto __prepare_write; } else { __imx6ulI2cStart(pI2cChannel->pi2cmsg->I2CMSG_usAddr, pI2cChannel->pi2cmsg->I2CMSG_usFlag, pI2cChannel->uiChannel); /* 重启总线 */ } } else { pI2cChannel->iMsgPtr = 0; pI2cChannel->iMsgIndex++; /* 保证与消息数量相同 */ __imx6ulI2cStop(pI2cChannel->uiChannel); /* 发送结束 */ } break; case __I2C_BUS_STATE_STOP: break; default: break; } return (LW_IRQ_HANDLED); } /********************************************************************************************************* ** 函数名称: __i2cTryTransfer ** 功能描述: i2c 尝试传输函数 ** 输 入 : uiChannel i2c 通道 ** pI2cAdapter i2c 适配器 ** pI2cMsg i2c 传输消息组 ** iNum 消息数量 ** 输 出 : 完成传输的消息数量 ** 全局变量: ** 调用模块: *********************************************************************************************************/ static INT __i2cTryTransfer (UINT uiChannel, PLW_I2C_ADAPTER pI2cAdapter, PLW_I2C_MESSAGE pI2cMsg, INT iNum) { __Gi2cChannel[uiChannel].iStatus = __I2C_BUS_STATE_START; __Gi2cChannel[uiChannel].iBpsParam = SPECIFIED_RATE; __Gi2cChannel[uiChannel].pi2cmsg = pI2cMsg; __Gi2cChannel[uiChannel].iMsgPtr = 0; __Gi2cChannel[uiChannel].iMsgNum = iNum; __Gi2cChannel[uiChannel].iMsgIndex = 0; /* 从第一个开始发送 */ __imx6ulI2cStart(pI2cMsg->I2CMSG_usAddr, pI2cMsg->I2CMSG_usFlag, uiChannel); /* 启动总线 */ API_SemaphoreBPend(__Gi2cChannel[uiChannel].I2C_hSignal, LW_OPTION_WAIT_A_SECOND * 3); /* 最多等待 3 秒钟 */ switch(uiChannel) { case I2C_CHAN_1: API_InterVectorDisable(IMX_INT_I2C1); /* 关闭总线中断 */ break; case I2C_CHAN_2: API_InterVectorDisable(IMX_INT_I2C2); /* 关闭总线中断 */ break; case I2C_CHAN_3: API_InterVectorDisable(IMX_INT_I2C3); /* 关闭总线中断 */ break; case I2C_CHAN_4: API_InterVectorDisable(IMX_INT_I2C4); /* 关闭总线中断 */ break; default: break; } return (__Gi2cChannel[uiChannel].iMsgIndex); /* 返回传输成功的数量 */ } /********************************************************************************************************* ** 函数名称: __i2cTransfer ** 功能描述: i2c 传输函数 ** 输 入 : uiChannel i2c 通道 ** pI2cAdapter i2c 适配器 ** pI2cMsg i2c 传输消息组 ** iNum 消息数量 ** 输 出 : ERROR_CODE ** 全局变量: ** 调用模块: *********************************************************************************************************/ static INT __i2cTransfer (UINT uiChannel, PLW_I2C_ADAPTER pI2cAdapter, PLW_I2C_MESSAGE pI2cMsg, INT iNum) { INT i; 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); /* 等待一个机器周期重试 */ } } return (PX_ERROR); } /********************************************************************************************************* ** 函数名称: __i2c1Transfer ** 功能描述: i2c1 传输函数 ** 输 入 : pI2cAdapter i2c 适配器 ** pI2cMsg i2c 传输消息组 ** iNum 消息数量 ** 输 出 : i2c 传输函数 ** 全局变量: ** 调用模块: *********************************************************************************************************/ static INT __i2c1Transfer (PLW_I2C_ADAPTER pI2cAdapter, /* 抽象成Transfer */ PLW_I2C_MESSAGE pI2cMsg, INT iNum) { return (__i2cTransfer(I2C_CHAN_1, pI2cAdapter, pI2cMsg, iNum)); } /********************************************************************************************************* ** 函数名称: __i2c2Transfer ** 功能描述: i2c2 传输函数 ** 输 入 : pI2cAdapter i2c 适配器 ** pI2cMsg i2c 传输消息组 ** iNum 消息数量 ** 输 出 : i2c 传输函数 ** 全局变量: ** 调用模块: *********************************************************************************************************/ static INT __i2c2Transfer (PLW_I2C_ADAPTER pI2cAdapter, /* 抽象成Transfer */ PLW_I2C_MESSAGE pI2cMsg, INT iNum) { return (__i2cTransfer(I2C_CHAN_2, pI2cAdapter, pI2cMsg, iNum)); } /********************************************************************************************************* ** 函数名称: __i2c3Transfer ** 功能描述: i2c3 传输函数 ** 输 入 : pI2cAdapter i2c 适配器 ** pI2cMsg i2c 传输消息组 ** iNum 消息数量 ** 输 出 : i2c 传输函数 ** 全局变量: ** 调用模块: *********************************************************************************************************/ static INT __i2c3Transfer (PLW_I2C_ADAPTER pI2cAdapter, /* 抽象成Transfer */ PLW_I2C_MESSAGE pI2cMsg, INT iNum) { return (__i2cTransfer(I2C_CHAN_3, pI2cAdapter, pI2cMsg, iNum)); } /********************************************************************************************************* ** 函数名称: __i2c4Transfer ** 功能描述: i2c4 传输函数 ** 输 入 : pI2cAdapter i2c 适配器 ** pI2cMsg i2c 传输消息组 ** iNum 消息数量 ** 输 出 : i2c 传输函数 ** 全局变量: ** 调用模块: *********************************************************************************************************/ static INT __i2c4Transfer (PLW_I2C_ADAPTER pI2cAdapter, /* 抽象成Transfer */ PLW_I2C_MESSAGE pI2cMsg, INT iNum) { return (__i2cTransfer(I2C_CHAN_4, pI2cAdapter, pI2cMsg, iNum)); } /********************************************************************************************************* i2c 驱动程序(操作函数集) *********************************************************************************************************/ static LW_I2C_FUNCS __Gimx6ulI2cFuncs[I2C_CHAN_NUM] = { { __i2c1Transfer, /* I2C1传输函数 */ }, { __i2c2Transfer, }, { __i2c3Transfer, }, { __i2c4Transfer, } }; /********************************************************************************************************* ** 函数名称: __i2cSetI2cClk ** 功能描述: 设置时钟频率 ** 输 入 : uiChannel i2c 通道号 ** uiBaud 时钟频率参数 ** 输 出 : NONE ** 全局变量: ** 调用模块: *********************************************************************************************************/ static VOID __i2cSetI2cClk (UINT uiChannel, UINT32 uiBaud) { /* * 获取系统时钟 */ // UINT32 uiSrcClk = ccmMainClkGet(IPG_PER_CLK); UINT32 uiSrcClk = 75 * 1000 * 1000; UINT32 uiDivider = uiSrcClk / uiBaud; UINT8 ucIndex = 0; UINT uiValue = 0; /* * 波特率处理后与数组中对应值匹配,将对应值写入分频寄存器 */ if (uiDivider < __GusI2cClkDiv[0][0]) { uiDivider = __GusI2cClkDiv[0][0]; ucIndex = 0; printk("Warning :can't find a smaller divider than %d.\n", uiDivider); printk("SCL frequency is set at %d - expected was %d.\n", uiSrcClk / uiDivider, uiBaud); } else if (uiDivider > __GusI2cClkDiv[49][0]) { uiDivider = __GusI2cClkDiv[49][0]; ucIndex = 49; printk("Warning: can't find a bigger divider than %d.\n", uiDivider); printk("SCL frequency is set at %d - expected was %d.\n", uiSrcClk / uiDivider, uiBaud); } else { for (ucIndex = 0; __GusI2cClkDiv[ucIndex][0] < uiDivider; ucIndex++); uiDivider = __GusI2cClkDiv[ucIndex][0]; } /* * 设置I2C时钟频率 */ uiValue = readw(REG_I2C_IFDR(uiChannel)); uiValue &= ~BIT_I2C_IFDR_IC_MASK; uiValue |= __GusI2cClkDiv[ucIndex][1]; writew(uiValue, REG_I2C_IFDR(uiChannel)); } /********************************************************************************************************* ** 函数名称: __i2cHwInit ** 功能描述: i2c 控制器初始化 ** 输 入 : uiChannel i2c 通道 ** 输 出 : ERROR_CODE ** 全局变量: ** 调用模块: *********************************************************************************************************/ static INT __i2cHwInit (UINT uiChannel) { UINT uiValue = 0; __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)); writew(0, REG_I2C_I2SR(uiChannel)); /* 清零状态寄存器 */ uiValue = readw(REG_I2C_I2CR(uiChannel)); /* 使能I2C */ uiValue |= BIT_I2C_I2CR_IEN; writew(uiValue, REG_I2C_I2CR(uiChannel)); return (ERROR_NONE); } /********************************************************************************************************* ** 函数名称: __i2cInit ** 功能描述: 初始化 i2c 通道 ** 输 入 : pI2cChannel i2c 通道 ** 输 出 : ERROR_CODE ** 全局变量: ** 调用模块: *********************************************************************************************************/ static INT __i2cInit (UINT uiChannel) { __PI2C_CHANNEL pI2cChannel = &__Gi2cChannel[uiChannel]; if (!pI2cChannel->I2C_bIsInit) { /* 判断通道是否已初始化 */ /* * 初始化 I2C 使用的信号量 */ pI2cChannel->I2C_hSignal = API_SemaphoreBCreate("i2c_signal", LW_FALSE, LW_OPTION_OBJECT_GLOBAL, LW_NULL); if (pI2cChannel->I2C_hSignal == LW_OBJECT_HANDLE_INVALID) { printk(KERN_ERR "imx6qI2cInit(): failed to create i2c_signal!\n"); goto __error_handle; } /* * 初始化 I2C 控制器 */ if (__i2cHwInit(pI2cChannel->uiChannel) != ERROR_NONE) { printk(KERN_ERR "imx6ulI2cInit(): failed to init!\n"); goto __error_handle; } switch(pI2cChannel->uiChannel) { case I2C_CHAN_1: API_InterVectorConnect(IMX_INT_I2C1, (PINT_SVR_ROUTINE)__i2cIsr, (PVOID)uiChannel, "i2c1_isr"); break; case I2C_CHAN_2: API_InterVectorConnect(IMX_INT_I2C2, (PINT_SVR_ROUTINE)__i2cIsr, (PVOID)uiChannel, "i2c2_isr"); break; case I2C_CHAN_3: API_InterVectorConnect(IMX_INT_I2C3, (PINT_SVR_ROUTINE)__i2cIsr, (PVOID)uiChannel, "i2c3_isr"); break; case I2C_CHAN_4: API_InterVectorConnect(IMX_INT_I2C4, (PINT_SVR_ROUTINE)__i2cIsr, (PVOID)uiChannel, "i2c4_isr"); break; default: printk(KERN_ERR "__i2cInit(): I2C channel invalid!\n"); break; } pI2cChannel->I2C_bIsInit = LW_TRUE; /* 设置已初始化标志 */ } return (ERROR_NONE); __error_handle: if (pI2cChannel->I2C_hSignal) { API_SemaphoreBDelete(&pI2cChannel->I2C_hSignal); pI2cChannel->I2C_hSignal = 0; } return (PX_ERROR); } /********************************************************************************************************* ** 函数名称: i2cIomuxConfig ** 功能描述: i2c管脚复用 ** 输 入 : uiChannel 通道号 ** 输 出 : NONE ** 全局变量: NONE ** 调用模块: NONE ********************************************************************************************************/ static VOID __i2cIomuxConfig (UINT uiChannel) { switch (uiChannel) { case I2C_CHAN_1: /* i2c1的管脚复用 */ IomuxConfig(__I2C1_SCL_REG, __I2C1_SCL_MASK, __I2C1_SCL_VAL); IomuxConfig(__I2C1_SDA_REG, __I2C1_SDA_MASK, __I2C1_SDA_VAL); break; case I2C_CHAN_2: /* i2c2的管脚复用 */ IomuxConfig(__I2C2_SCL_REG, __I2C2_SCL_MASK, __I2C2_SCL_VAL); IomuxConfig(__I2C2_SDA_REG, __I2C2_SDA_MASK, __I2C2_SDA_VAL); break; case I2C_CHAN_3: /* i2c3的管脚复用 */ IomuxConfig(__I2C3_SCL_REG, __I2C3_SCL_MASK, __I2C3_SCL_VAL); IomuxConfig(__I2C3_SDA_REG, __I2C3_SDA_MASK, __I2C3_SDA_VAL); break; case I2C_CHAN_4: /* i2c4的管脚复用 */ IomuxConfig(__I2C4_SCL_REG, __I2C4_SCL_MASK, __I2C4_SCL_VAL); IomuxConfig(__I2C4_SDA_REG, __I2C4_SDA_MASK, __I2C4_SDA_VAL); break; default: printk(KERN_ERR "i2cBusFuns(): I2C channel invalid!\n"); } return; } /********************************************************************************************************* ** 函数名称: i2cBusFuns ** 功能描述: 初始化 i2c 总线并获取操作函数集 ** 输 入 : uiChannel 通道号 ** 输 出 : 总线操作函数集 ** 全局变量: ** 调用模块: *********************************************************************************************************/ PLW_I2C_FUNCS i2cBusFuns (UINT uiChannel) { /* * 设置芯片管脚分配,SylixOS 计数从零开始,而 IMX6UL 手册是从 1 开始,需要注意 */ __i2cIomuxConfig(uiChannel); if (__i2cInit(uiChannel) != ERROR_NONE) { /* 初始化控制器 */ return (LW_NULL); } return (&__Gimx6ulI2cFuncs[uiChannel]); /* 返回操作函数集 */ } /********************************************************************************************************* ** 函数名称: i2cBusCreate ** 功能描述: 初始化目标电路板i2c总线系统 ** 输 入 : NONE ** 输 出 : NONE ** 全局变量: ** 调用模块: *********************************************************************************************************/ VOID i2cBusCreate (VOID) { /* * 具体配置在common中 */ PLW_I2C_FUNCS pI2cFuncs; API_I2cLibInit(); /* 初始化 i2c 组件库 */ #if CONFIG_BSP_I2C0 pI2cFuncs = i2cBusFuns(0); /* 创建 i2c0总线适配器 */ if (pI2cFuncs) { API_I2cAdapterCreate("/bus/i2c/0", pI2cFuncs, 10, 1); /* 0 对应 I2C0 */ } #endif #if CONFIG_BSP_I2C1 pI2cFuncs = i2cBusFuns(1); /* 创建 i2c1总线适配器 */ if (pI2cFuncs) { API_I2cAdapterCreate("/bus/i2c/1", pI2cFuncs, 10, 1); /* 1 对应 I2C1 */ } #endif #if CONFIG_BSP_I2C2 pI2cFuncs = i2cBusFuns(2); /* 创建 i2c2总线适配器 */ if (pI2cFuncs) { API_I2cAdapterCreate("/bus/i2c/2", pI2cFuncs, 10, 1); /* 2 对应 I2C2 */ } #endif #if CONFIG_BSP_I2C3 pI2cFuncs = i2cBusFuns(3); /* 创建 i2c3总线适配器 */ if (pI2cFuncs) { API_I2cAdapterCreate("/bus/i2c/3", pI2cFuncs, 10, 1); /* 3 对应 I2C3 */ } #endif } /********************************************************************************************************* ** 函数名称: i2cBusDelete ** 功能描述: 卸载目标电路板i2c总线系统 ** 输 入 : NONE ** 输 出 : NONE ** 全局变量: ** 调用模块: *********************************************************************************************************/ VOID i2cBusDelete (VOID) { #if CONFIG_BSP_I2C0 API_I2cAdapterDelete("/bus/i2c/0"); /* 0 对应 I2C0 */ #endif #if CONFIG_BSP_I2C1 API_I2cAdapterDelete("/bus/i2c/1"); /* 1 对应 I2C1 */ #endif #if CONFIG_BSP_I2C2 API_I2cAdapterDelete("/bus/i2c/2"); /* 2 对应 I2C2 */ #endif #if CONFIG_BSP_I2C3 API_I2cAdapterDelete("/bus/i2c/3"); /* 3 对应 I2C3 */ #endif } /********************************************************************************************************* ** 函数名称: module_init ** 功能描述: module_init()驱动加载模块 ** 输 入 : NONE ** 输 出 : NONE ** 全局变量: ** 调用模块: *********************************************************************************************************/ VOID module_init (VOID) { printf("hello_module init!\n"); i2cBusCreate(); } /********************************************************************************************************* ** 函数名称: module_exit ** 功能描述: module_exit()驱动卸载模块 ** 输 入 : NONE ** 输 出 : NONE ** 全局变量: ** 调用模块: *********************************************************************************************************/ VOID module_exit (VOID) { printf("hello_module exit!\n"); i2cBusDelete(); } /********************************************************************************************************* END *********************************************************************************************************/
相关文章推荐
- SylixOS iMX6平台I2C总线驱动
- iMX6平台SylixOS I2C总线驱动开发
- SylixOS NUC970平台SPI总线驱动移植
- SylixOS NUC970平台SPI总线驱动移植
- SylixOS 基于STM32平台的GPIO模仿I2C总线的驱动开发流程
- SylixOS基于Nuc970平台的SD驱动移植
- SylixOS 基于STM32平台的GPIO模仿I2C总线的驱动开发流程
- SylixOS基于Nuc970平台的SD驱动移植
- SylixOS 里NUC970平台上SPI总线驱动移植
- SylixOS基于Nuc970平台的SD驱动移植
- SylixOS的imx1050平台PWM捕获驱动
- SylixOS 基于STM32平台的GPIO模仿I2C总线的驱动开发流程
- Linux内核平台总线设备驱动模型浅析
- 三星6410 led平台驱动
- Linux Platform devices 平台设备驱动
- 木其工作室(专业程序代写服务)[原]ok6410学习笔记(15.platform平台总线驱动模型之混杂设备驱动led)
- 请把Camera hold住 - Android高通平台调试Camera驱动全纪录
- UBOOT-2012-10在OK6410平台的移植(九)MMC驱动
- FS_S5PC100平台上Linux Camera驱动开发详解(二)
- Android平台下驱动的开发及测试框架概述(一)