linuxsir首页 LinuxSir.Org | Linux、BSD、Solaris、Unix | 开源传万世,因有我参与欢迎您!
网站首页 | 设为首页 | 加入收藏
您所在的位置:主页 > Linux基础建设 >

iMX6平台SylixOS I2C总线驱动开发

时间:2017-03-15  来源:未知  作者:linuxsir首页


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的具体实现

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

}
 

 


16.__i2cIomuxConfig的具体实现

 


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;

……

}
 

 


17.__i2cInit,__i2cHwInit的具体实现

 


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;

}

……

}
 

 

 


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

……

}
 

 


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

……

}
 

 


19.__i2cTransferEnable的具体实现

 


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

}
 

 


20.__i2cTransferStart的具体实现

 


static INT __i2cTransferStart (UINT uiChannel)

{

UINT uiValue = 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);

}
 

 

 


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

}
 

 


21.__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++;

}

}

……

}
 

 


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

}
 

 

 


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

}

}

……

}
 

 


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

}

……

}
 

 


24.__i2cTransferStop的具体实现

 


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

}
 

 


25.__i2cTransferDisable的具体实现

 


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

}

友情链接