硬件I2C

之前有抄过别人的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 )
这个函数,这个参数就很明显。

你可能感兴趣的:(硬件I2C)