之前有抄过别人的IO模拟I2C,其实很多的芯片内部都有硬件I2C,这个的操作相对就比较简单。这里我们以Telechip8902 为例,在Wince系统下面如何构建I2C的驱动程序。
1:I2C的驱动是基于流驱动模式。
2:无非是显示一下接口函数:
LIBRARY TCC_I2C
EXPORTS
I2C_Init
I2C_Deinit
I2C_Open
I2C_Close
I2C_IOControl
I2C_PowerUp
I2C_PowerDown
I2C_Read
I2C_Write
I2C_Seek
3:首先是初始化,因为都是GPIO口复用,所以首先需要配置IO口,使之成为SDA,SCL接口模拟。
void I2C_GpioSetting(void)
{
PGPIO pGPIO;
pGPIO = (PGPIO)tcc_allocbaseaddress((unsigned int)&HwGPIO_BASE);
//SCL0-GPIOA0, SDA0-GPIOA1
BITCSET(pGPIO->GPAFN0, (Hw5-Hw0), Hw0); //GPIOA[0] function set 1
BITCSET(pGPIO->GPAFN0, (Hw8-Hw4), Hw4); //GPIOA[1] function set 1
//SCL1-GPIOA8, SDA1-GPIOA9
BITCSET(pGPIO->GPAFN1, (Hw5-Hw0), Hw0); //GPIOA[8] function set 1
BITCSET(pGPIO->GPAFN1, (Hw8-Hw4), Hw4); //GPIOA[9] function set 1
}
这个地方的许多的配置都是不一样的。这里说明有两个I2C接口
4:初始化完了就得实际配置下I2C,可以参加一下函数:
void I2C_Initialize()
{
pI2C = (PI2C)tcc_allocbaseaddress((unsigned int)&HwI2CMASTER0_BASE);
//I2C Channel 0 setting 100kHz
pI2C->PRES0 = 7;
//Enable I2C core and Enable I2C Core Interrupt bit
pI2C->CTRL0 = HwI2CM_CMD_STA_EN | HwI2CM_CMD_STO_EN|HwI2CM_CTRL_MOD_8;
BITSET( pI2C->CMD0, HwI2CM_CMD_IACK_CLR);
//Sleep(1);
BITSET( pI2C->CMD0,HwI2CM_CMD_IACK_CLR);
//I2C Channel 1 setting 400kHz
/* Required Frequency */
//pI2C->PRES1 = 2; // (400K)
pI2C->PRES1 = 7; // (100K)
pI2C->CTRL1 = HwI2CM_CMD_STA_EN | HwI2CM_CMD_STO_EN|HwI2CM_CTRL_MOD_8;
BITSET( pI2C->CMD1, HwI2CM_CMD_IACK_CLR);
//Sleep(1);
BITSET( pI2C->CMD1, HwI2CM_CMD_IACK_CLR);
}
5:前面的步骤已经完成了配置工作,我们使用I2C最主要还是来通讯的。所以要写读写函数。我们一般定义一下结构体:
ypedef struct _I2C_Param{
BYTE DeviceAddr;
BYTE nPort;
BYTE nMode;
BYTE DUMMY;
DWORD nWriteByte;
DWORD nReadByte;
BYTE *pWriteBuffer;
BYTE *pReadBuffer;
DWORD nTimeout;
} I2C_Param;
typedef struct _RetParam{
DWORD nReadByte;
BYTE *pReadBuffer;
} RetParam;
第一个结构用来包装我们的传递的参数。第二个用来包装我们的返回值。
6:读写函数:
DWORD ReadChannel0(BYTE deviceAddr, DWORD writeBytes, BYTE* pWriteBuffer, DWORD readBytes, BYTE* pReadBuffer, BYTE nMode, DWORD nTimeout)
{
DWORD nCount=0;
if(writeBytes > 0)
{
pI2C->TXR0 = deviceAddr | I2C_WR;
pI2C->CMD0 = HwI2CM_CMD_STA_EN | HwI2CM_CMD_WR_EN;
while( !(pI2C->IRQSTR & Hw0) )
{
nCount++;
if(nCount > 100000)
{
RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C ]TIMEOUT /n")));
return FALSE;
}
}
BITSET( pI2C->CMD0, Hw0); //Clear a pending interrupt
for(unsigned int i=0; i< writeBytes; i++) {
RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C ]WriteData[%d]=[%x]/n"), i, pWriteBuffer[i]));
pI2C->TXR0 = pWriteBuffer[i];
pI2C->CMD0 = HwI2CM_CMD_WR_EN;
nCount=0;
while( !(pI2C->IRQSTR & Hw0) )
{
nCount++;
if(nCount > 100000)
{
RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C ]TIMEOUT /n")));
return FALSE;
}
}
BITSET( pI2C->CMD0, Hw0); //Clear a pending interrupt
}
if(!nMode || ((readBytes <= 0)&&(nMode != 2)&&(nMode != 4)))
{
pI2C->CMD0 = HwI2CM_CMD_STO_EN;
nCount=0;
while( !(pI2C->IRQSTR & Hw0) )
{
nCount++;
if(nCount > 100000)
{
RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C ]TIMEOUT[STOP] /n")));
return FALSE;
}
}
BITSET( pI2C->CMD0, Hw0); //Clear a pending interrupts
}
}// writeBytes
if(readBytes > 0)
{
pI2C->TXR0 = deviceAddr | I2C_RD;
pI2C->CMD0 = HwI2CM_CMD_STA_EN | HwI2CM_CMD_WR_EN;
nCount=0;
while( !(pI2C->IRQSTR & Hw0) )
{
nCount++;
if(nCount > 100000)
{
RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C ]TIMEOUT /n")));
return FALSE;
}
}
BITSET( pI2C->CMD0, Hw0); //Clear a pending interrupt
for(unsigned int i=0; i< readBytes; i++) {
if(nMode)
{
if(i==readBytes-1)
pI2C->CMD0 = HwI2CM_CMD_RD_EN |HwI2CM_CMD_ACK_EN;
else
pI2C->CMD0 = HwI2CM_CMD_RD_EN ;
}
else
pI2C->CMD0 = HwI2CM_CMD_RD_EN |HwI2CM_CMD_ACK_EN;
nCount=0;
while( !(pI2C->IRQSTR & Hw0) )
{
nCount++;
if(nCount > 100000)
{
RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C ]TIMEOUT /n")));
return FALSE;
}
}
BITSET( pI2C->CMD0, Hw0); //Clear a pending interrupt
pReadBuffer[i] =(BYTE)pI2C->RXR0;
RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C ]BUFFER[%d]:%x /n/r"),i,pReadBuffer[i]));
}
if(nMode != 4)
pI2C->CMD0 = HwI2CM_CMD_STO_EN;
nCount=0;
while( !(pI2C->IRQSTR & Hw0) )
{
nCount++;
if(nCount > 100000)
{
RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C ]TIMEOUT /n")));
return FALSE;
}
}
BITSET( pI2C->CMD0, Hw0); //Clear a pending interrupt
}//readBytes
return TRUE;
}
//这个函数是重点,在于如何告诉你读写操作。可以看的出来,以写为例:
//写
首先把要写数据导入到Transmit Data Register 也就是TX 里面。首先需要写入的是目标地址和控制位,这里是些,所以
pI2C->TXR0 = deviceAddr | I2C_WR;这样需要发送的数据就准备好了,下次就可以直接发送这个信息。
pI2C->CMD0 = HwI2CM_CMD_STA_EN | HwI2CM_CMD_WR_EN;上面的这句话是表示,允许写操作,并且开始Start Condition Generation
这样第一个语句就发送出去了,记住,这个只是类似于握手操作,我们真正需要的数据还没有开始传递。
当Master发送出去数据后,Salve需要有反应,如果没有反应,说明地址不对,或者线路不对。所以我们需要检查是否有反应。
while( !(pI2C->IRQSTR & Hw0) )
{
nCount++;
if(nCount > 100000)
{
RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C ]TIMEOUT /n")));
return FALSE;
}
}
实际上就是不停得检查IRQSTR第一位死否为1,如果是,则说明已经有了反馈信号。下面的这段话是Telechips8902的DataSheet关于I2C寄存器的:
IRQ Status of I2C Master Controller Channel 0
0/1
IRQ Status Flag for I2C Master Controller of Channel 0.
This would be read as ‘1’ when the IRQ status of I2C master controller
channel 0 is activated.
当检查到这个标志后,说明完成,我们也需要手动清除这个标志:
BITSET( pI2C->CMD0, Hw0); //Clear a pending interrupt那写其他数据就是同样的道理了,只是注意,已经Start 了,不用重新Start.
读也是一样的,发出读命令后,不停得检查ACK信息,当得到ACK信息后,就读取寄存器值RXR0即可。
记住,读写完成后需要STOP操作。
一般暴露给用户的是
BOOL I2C_IOControl( DWORD hOpenContext, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut )
这个函数,这个参数就很明显。