I2C由时钟线(SCL)和数据线(SDA)两根线构成通信线路,总线空闲时通过上拉电阻拉高电平。I2C是一种主从结构(Master/Slave)总线,总线上每个设备都可以作为主机或从机,主设备通常是CPU,用来产生传输的时钟信号,并初始化总线的数据传输,而从设备只能被动响应主机请求。
因为一个I2C总线可以接多个从设备,这就需要主机通过地址来确定与哪个从机进行通信。I2C总线上的每个从设备都有一个唯一的7bit物理识别地址。因为I2C地址全0表示广播地址,所以一个I2C总线理论最多能挂载2^7 - 1=127个从设备。
起始信号:
SCL为高电平,SDA产生一次下降沿。
结束信号:
SCL为高电平,SDA产生一次上升沿。
数据传输:
SCL为低电平区间,SDA为高则传输1,为低则传输0.
主机发送数据:
1.主机发送起始信号
2.主机发送从机地址,相应地址的从机发出ACK进行响应。
3.主机向从机发送数据,每位数据从机接收到就响应一次ACK。
4.主机发送完数据后,发送一个结束信号,结束本次发送。
从机发送数据:
1.主机发送起始信号
2.主机发送从机地址,相应地址的从机发出ACK进行响应。
3.从机向主机发送数据,每位数据主机接收到就响应一次ACK。
4.主机接收完数据后,发送一个结束信号,结束本次发送。
#include"i2c.h"
#include "intrins.h"
/*******************************************************************************
* 函数名 : Delay10us()
* 函数功能 : 延时10us
* 输入 : 无
* 输出 : 无
*******************************************************************************/
void Delay10us()
{
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
}
/*scl高电平,然后sda产生一次下降沿信号*/
void i2c_start()
{
SDA = 1;
SCL = 1;
Delay10us();//保持一段时间
SDA = 0;
Delay10us();//延时,以便下降沿明显
SCL = 0;//为下面的发送数据做准备
}
/*scl高电平,然后sda产生一次上升沿信号*/
void i2c_stop()
{
SDA = 0;
SCL = 1;
Delay10us();//保持一段时间
SDA = 1;
Delay10us();//延时,以便下降沿明显
SCL = 1;//重新释放总线
}
/*发送一字节的数据到从机,数据位的改变要在SCL的低电平区间,高电平表示1,低电平表示0*/
static u8 i2c_sendbyte(u8 datas)
{
u8 i = 0;
for(i=0;i<8;i++)
{
SDA = datas>>7;//MSB字节序,所以从高位读起
datas <<= 1;
Delay10us();
SCL = 1;
Delay10us();
SCL = 0;
}
SDA = 1;//准备接收ACK
SCL = 1;
Delay10us();
i = 0;
while(SDA && i<200)//超时时间内ACK响应
{
i++;
}
if(i>=200)
{
SCL = 0;
Delay10us();
return 0;
}
SCL = 0;
Delay10us();
return 1;
}
/*接收从机发送的一字节的数据,数据位的改变要在SCL的低电平区间,所以每一位数据都需要主机先释放总线,再拉低总线*/
static u8 i2c_readbyte()
{
u8 i=0,datas=0;
for(i=0;i<8;i++)//接收8个字节
{
SCL=1;
Delay10us();
dat<<=1;
dat|=SDA;
Delay10us();
SCL=0;
Delay10us();
}
return datas;
}
/*向At24c02发送数据*/
void at24c02_write(unsigned char addr,unsigned char dat)
{
i2c_start();
i2c_sendbyte(0xa0);//发送写器件地址
i2c_sendbyte(addr);//发送要写入内存地址
i2c_sendbyte(dat); //发送数据
i2c_stop();
}
/*接收At24c02发送的数据*/
unsigned char at24c02_read(unsigned char addr)
{
unsigned char num;
i2c_start();
i2c_sendbyte(0xa0); //发送写器件地址
i2c_sendbyte(addr); //发送要读取的地址
i2c_start();
i2c_sendbyte(0xa1); //发送读器件地址
num=i2c_readbyte(); //读取数据
i2c_stop();
return num;
}