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 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
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 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 <
* 0x07 + ack
* 0x0e <
* 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。