GPIO模拟类I2C总线基础函数实例

我们先了解一下I2C总线基础知识.

I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。是微电子通信控制领域广泛采用的一种总线标准。它是同步通信的一种特殊形式,具有接口线少,控制方式简单,器件封装形式小,通信速率较高等优点。

I2C 总线的概念

  I2C 总线支持任何 IC 生产过程(NMOS CMOS、双极性)。两线――串行数据(SDA)和串行时钟 (SCL)线在连接到总线的器件间传递信息。每个器件都有一个唯一的地址识别(无论是 微控制器——MCU、 LCD 驱动器、存储器或键盘接口),而且都可以作为一个发送器或接收器(由器件的功能决定)。很明显,LCD 驱动器只是一个接收器,而存储器则既可以接收又可以发送数据。除了发送器和接收器外器件在执行数 据传输时也可以被看作是主机或从机(见表1)。主机是初始化总线的数据传输并产生允许传输的时钟信号 的器件。此时,任何被寻址的器件都被认为是从机。


I2C总线特征

  1、只要求两条总线线路:一条串行数据线SDA,一条串行时钟线SCL;  2、每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机/从机关系软件设定地址,主机可以作为主机发送器或主机接收器;  3、它是一个真正的多主机总线,如果两个或更多主机同时初始化,数据传输可以通过冲突检测和仲裁防止数据被破坏;  4、串行的8 位双向数据传输位速率在标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s;  5、连接到相同总线的IC 数量只受到总线的最大电容400pF 限制。


I2C总线位传输

  由于连接到I2C 总线的器件有不同种类的工艺( CMOS、 NMOS、 PMOS、双极性),逻辑0(低)和逻辑1(高)的电平不是固定的,它由电源VCC的相关电平决定,每传输一个数据位就产生一个时钟脉冲。

数据的有效性

  在传输数据的时候,SDA线必须在时钟的高电平周期保持稳定,SDA的高或低电平状态只
   GPIO模拟类I2C总线基础函数实例_第1张图片

I2C位传输数据有效性

有在SCL 线的时钟信号是低电平时才能改变 。

起始和停止条件

  SCL 线是高电平时,SDA 线从高电平向低电平切换,这个情况表示起始条件;  SCL 线是高电平时,SDA 线由低电平向高电平切换,这个情况表示停止条件。  起始和停止条件一般由主机产生,总线在起始条件后被认为处于忙的状态
   起始和停止条件

起始和停止条件

,在停止条件的某段时间后总线被认为再次处于空闲状态。  如果产生重复起始条件而不产生停止条件,总线会一直处于忙的状态,此时的起始条件(S)和重复起始条件(Sr) 在功能上是一样的。


I2C总线数据传输

字节格式

  发送到SDA 线上的每个字节必须为8 位,每次传输可以发送的字节数量不受限制。每个字节后必须跟一个响应位。首先传输的是数据的最高位(MSB),如果从机要完成一些其他功能后(例如一个内部中断服务程序)才能接收或发送下一个完整的数据字节,可以使时钟线SCL 保持低电平,迫使主机进入等待状态,当从机准备好接收下一个数据字节并释放时钟线SCL 后数据传输继续。

应答响应

  数据传输必须带响应,相关的响应时钟脉冲由主机产生。在响应的时钟脉冲期间发送器释放SDA 线(高)。  在响应的时钟脉冲期间,接收器必须将SDA 线拉低,使它在这个时钟脉冲的高电平期间保持稳定的低电平。  通常被寻址的接收器在接收到的每个字节后,除了用C BUS 地址开头的数
   I2C总线数据传输和应答

I2C总线数据传输和应答

据,必须产生一个响应。当从机不能响应从机地址时(例如它正在执行一些实时函数不能接收或发送),从机必须使数据线保持高电平,主机然后产生一个停止条件终止传输或者产生重复起始条件开始新的传输。  如果从机接收器响应了从机地址,但是在传输了一段时间后不能接收更多数据字节,主机必须再一次终止传输。这个情况用从机在第一个字节后没有产生响应来表示。从机使数据线保持高电平,主机产生一个停止或重复起始条件。  如果传输中有主机接收器,它必须通过在从机不产生时钟的最后一个字节不产生一个响应,向从机发送器通知数据结束。从机发送器必须释放数据线,允许主机产生一个停止或重复起始条件。


所谓类I2C总线,是指信号只通过SCL, SDA传输. 即两线――串行数据(SDA)和串行时钟 (SCL)线在连接到总线的器件间传递信息。

我们可以把这2条线连接到主控芯片的2条GPIO引脚上,用GPIO模拟方式来和I2C连接的外部器件通信. 

下面是一些GPIO模拟I2C协议通信的基础函数.


#define SUCCESS       (0)
#define FAILED        (1)

#define CLK_OUT              *(volatile unsigned int *)(GPIO_BASE_ADDR+0xC0  |= (1<<30)
#define CLK_HI                 *(volatile unsigned int *)(GPIO_BASE_ADDR+0xC4) |= (1<<30)
#define CLK_LO                  *(volatile unsigned int *)(GPIO_BASE_ADDR+0xC4) &= ~(1<<30)

#define DATA_OUT             *(volatile unsigned int *)(GPIO_BASE_ADDR+0xF8) |=(1<<31)
#define DATA_IN           *(volatile unsigned int *)(GPIO_BASE_ADDR+0xF8) &=~(1<<31)
#define DATA_HI             *(volatile unsigned int *)(GPIO_BASE_ADDR+0xF4) |= 1<<31
#define DATA_LO             *(volatile unsigned int *)(GPIO_BASE_ADDR+0xF4) &=~(1<<31)

void Delay(char cDelay)
{
      while(cDelay>0) {
          cDelay--;
      }    
}

void Clockhigh(void)
{
       Delay(1);
    CLK_HI;
    Delay(1);
}

void Clocklow(void)
{
    Delay(1);
    CLK_LO;
    Delay(1);
}

void ClockCycle(void)
{
    Delay(1);
    CLK_LO;
    Delay(2);
    CLK_HI;
    Delay(1);
}

void ClockCycles(char cCount)
{
    char i;
    for (i = 0; i < cCount; ++i) ClockCycle();
}

void PowerOn(void)   
{
    int i;
    GPIO_ENABLE(SCL_GPIO);   
    GPIO_ENABLE(SDA_GPIO);
        DATA_OUT;                              
        CLK_OUT;
    CLK_LO;                               
    DATA_HI;                               
    ClockCycles(10);
}

void PowerOff(void)
{
        Delay(1);
        CLK_LO;
        Delay(6);
}

void Start(void)
{
      DATA_OUT;                         
      Clocklow();
      DATA_HI;
      Delay(4);
      Clockhigh();
      Delay(4);
      DATA_LO;
      Delay(8);
      Clocklow();
      Delay(8);
}

void Stop(void)
{
        DATA_OUT;                         
        Clocklow();
        DATA_LO;
        Delay(4);
        Clockhigh();
        Delay(8);
        DATA_HI;
        Delay(4);
}
 
char Write(char cData)
{
    char i;

    DATA_OUT;                       
    for(i=0; i<8; i++) {                
        Clocklow();
        if (cData&0x80)
            DATA_HI;
        else             
            DATA_LO;
        Clockhigh();
        cData = cData<<1;
    }
    Clocklow();

    DATA_IN;                     
    Delay(8);
    Clockhigh();
    while(i>1) {                    
        Delay(2);
        if (DATA_RD) i--;        
        else i = 0;
    }      
    Clocklow();
    DATA_OUT;                     
    return i;
}

char Read(void)
{
        char i;
        char rByte = 0;
        
        DATA_IN;                         
        DATA_HI;
        for(i=0x80; i; i=i>>1)
        {
            ClockCycle();
            if (DATA_RD)
                rByte |= i;
            Clocklow();
        }
        DATA_OUT;                       
        return rByte;
}

void WaitClock(char loop)
{
        char i, j;
        
        DATA_LO;
        for(j=0; j<loop; j++) {
            Start();
            for(i = 0; i<15; i++)
                ClockCycle();
            Stop();
        }
}

void AckNak(char cAck)
{
        DATA_OUT;                         
        Clocklow();
        if (cAck)
            DATA_LO;              
        else       
            DATA_HI;              
        Delay(2);
        Clockhigh();
        Delay(8);
        Clocklow();
}

void Ack(void)
{
    DATA_OUT;                         
    Delay(1);
    CLK_LO;
    Delay(1);
    DATA_LO;                          
    Delay(3);
    CLK_HI;
    Delay(9);
    Clocklow();
}

char ReceiveData(char *cRecBuf, char cLen)
{
    int i;

    for(i = 0; i < (cLen-1); i++) {
        cRecBuf[i] = Read();
        AckNak(TRUE);
    }
    cRecBuf[i] = Read();
    AckNak(FALSE);
    Stop();
    return SUCCESS;
}

char SendData(char *cSendBuf, char cLen)
{
        int i;
        
        for(i = 0; i< cLen; i++) {
                if (Write(cSendBuf[i])==1)
                        return FAIL_WRDATA;
        }
        Stop();
        
        return SUCCESS;
}





你可能感兴趣的:(c,存储,delay)