I2C总线属于半双工,有一条SDA线和SCL线,前者数据线,后者时钟线,内部使用开漏驱动,只能拉低不能拉高,但可以给SDA和SCL加上拉电阻将其拉高,两者初始电平都是高电平

具体流程如图

android i2c通讯APP i2c通信流程_嵌入式硬件

1.起始位:当SCL高电平时,将SDA拉低,表示准备开始接受信号

2.地址位:主机发送从机的地址进行匹配,每个从机的地址唯一,只有匹配才会开启传输

3.读写位:从机的七位地址发送完后跟一个读写位,0为写(主到从),1为读(从到主)。

3.ACK/NACK:将主机发送的地址与从机匹配,若匹配成功则从机将SDA拉低表示返回一个ACK信号,反之将SDA拉高表示发送一个NACK信号。

4.数据位:发送或接收的数据,当主机发送数据完成后将SDA设为输入模式,等待从机发送ACK信号(SDA拉低)反之则发送NACK将SDA位拉高,并让主机重启或停止。

5.停止位:当SCL高电平时,SDA为高则表示数据停止发送

另外,在数据传输时只有当SCL为低时,SDA才允许被改变。否则SDA都应该稳定不变。

在用BH1750的时候用到了i2c,记录一下代码和大致流程。

#define	 SlaveAddress   0x46 
/*定义器件在IIC总线中的从地址,根据ALT  ADDRESS地址引脚不同修改 	
由于地址位是七位的,所以在换算2进制要在后面加一个0.也就是0x46是 
01000110,在数据手册里只写了前七位,注意别看差了*/

uint8_t  BUF[4];                  //接收数据缓存区

/***************************************************************
** 功能:     产生IIC起始信号,,当SCL为高时,SDA从高变低
** 参数:	  无参数
** 返回值:    无
****************************************************************/
void BH1750_Start()
{
    SDA_OUT();     //SDA设置为输出
    IIC_SDA=1;
    IIC_SCL=1;
    delay_us(4);
    IIC_SDA=0;//START:when CLK is high,DATA change form high to low
    delay_us(4);
    IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
/***************************************************************
** 功能:     产生IIC停止信号,在SCL为高时,将SDA拉高表示停止
** 参数:	  无参数
** 返回值:    无
****************************************************************/
void BH1750_Stop()
{
    SDA_OUT();
    IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
    IIC_SCL=1;
    delay_us(4);
    IIC_SDA=1;//发送I2C总线结束信号
    delay_us(4);
}
/***************************************************************
** 功能:     产生IIC应答信号
** 参数:	  无参数
** 返回值:    无
****************************************************************/
void BH1750_SendACK(u8 ack)
{
    SDA_OUT();          
    if(ack)IIC_SDA=1;   //写应答信号
    else IIC_SDA=0;
    IIC_SCL=1;          //拉高时钟线
    delay_us(2);        //延时
    IIC_SCL=0;          //拉低时钟线
    delay_us(2);        //延时
}

/***************************************************************
** 功能:     产生IIC接收信号,注意接收要设置成输入模式
** 参数:	  无参数
** 返回值:    无
****************************************************************/
u8 BH1750_RecvACK()
{
    u8 data;
    SDA_IN();         //SDA设置为输入
    IIC_SCL=1;        //拉高时钟线
    delay_us(2);      //延时
    data = READ_SDA;  //读应答信号
    IIC_SCL=0;        //拉低时钟线
    delay_us(2);      //延时
    return data;
}
/***************************************************************
** 功能:     向IIC总线发送一个字节数据
** 参数:	  dat:一字节数据
** 返回值:    无
****************************************************************/
void BH1750_SendByte(u8 dat)
{
    u8 i,bit;
    SDA_OUT();               //sda线输出
    for (i=0; i<8; i++)      //8位计数器
    {
        bit=dat&0x80;
        if(bit) IIC_SDA=1;
        else IIC_SDA=0;
        dat <<= 1;           //移出数据的最高位
        IIC_SCL=1;           //拉高时钟线
        delay_us(2);         //延时
        IIC_SCL=0;           //拉低时钟线
        delay_us(2);         //延时
    }
    BH1750_RecvACK();
}
/***************************************************************
** 功能:     从IIC总线接收一个字节数据
** 参数:	  无参数
** 返回值:   dat:接收一字节数据
****************************************************************/
u8 BH1750_RecvByte()
{
    u8 i;
    u8 dat = 0;
    SDA_IN();               //SDA设置为输入
    IIC_SDA=1;              //使能内部上拉,准备读取数据,
    for (i=0; i<8; i++)     //8位计数器
    {
        dat <<= 1;
        IIC_SCL=1;          //拉高时钟线
        delay_us(2);        //延时
        if(READ_SDA) dat+=1;
        IIC_SCL=0;          //拉低时钟线
        delay_us(2);        //延时
    }
    return dat;
}

/***************************************************************
** 功能:     向bh1750写入命令
** 参数:	  无参数
** 返回值:   无
****************************************************************/
void Single_Write_BH1750(u8 REG_Address)
{
    BH1750_Start();                  //起始信号
    BH1750_SendByte(SlaveAddress);   //发送设备地址+写信号 0
    BH1750_SendByte(REG_Address);    //内部寄存器地址,
    BH1750_Stop();                   //发送停止信号
}

/***************************************************************
** 功能:     连续读出BH1750内部数据,根据数据手册知,测量后返回的数据是十六位的
** 参数:	  无参数
** 返回值:   无
****************************************************************/
void Multiple_Read_BH1750(void)
{   u8 i;
    BH1750_Start();                          //起始信号
    BH1750_SendByte(SlaveAddress+1);         //发送设备地址+读信号 1

    for (i=0; i<3; i++)                      //连续读取2个地址数据,存储中BUF
    {
        BUF[i] = BH1750_RecvByte();          //BUF[0]存返回的高8位,BUF[1]存返回的低8位
        if (i == 3)
        {
            BH1750_SendACK(1);                //最后一个数据需要回NOACK
        }
        else
        {
            BH1750_SendACK(0);                //回应ACK
        }
    }
    BH1750_Stop();                          //停止信号
    //   delay_ms(150);
}

/***************************************************************
** 功能:     初始化BH1750
** 参数:	  无参数
** 返回值:   无
****************************************************************/
void BH1750_Configure(void)
{
    BH1750_PortInit();
    Single_Write_BH1750(0x01);      //给bh1750发通电指令0x01
    ADDR = 0;  						//将ADDR位初始化拉低,此时为0100011
}

/***************************************************************
** 功能:     读取光照度
** 参数:	  无参数
** 返回值:   data:返回光照度值
****************************************************************/
uint16_t Get_Bh_Value(void)
{
    float temp;
    unsigned int data;
    int dis_data ;
    Single_Write_BH1750(0x01);   // 发送通电指令
    Single_Write_BH1750(0x10);   // 发送高分辨率指令 0x10
    delay_ms(200);    //等待测量结束,数据手册中显示此测量模式要120ms时间
    Multiple_Read_BH1750();       //连续读出两个数据,存储在BUF中
    dis_data=BUF[0];
    dis_data=(dis_data<<8)+BUF[1];//合成数据,即光照数据,16位的
    temp=(float)dis_data/1.2;
    data=(int)temp;
    return data;
}