实际上单片机中并不严格区分驱动和应用(当然也有一些代码即使在单片机程序中也分了很多层,抽象出了驱动层和应用层,甚至还有中间层),因此在这里,列举一个常见的51单片机下的i2c应用程序。
51系列单片机下的i2c程序
总线初始化:将总线拉高以释放总线
时序如下:
代码如下:
void delay(void)
{
;
;
}
void delay_1ms(uint z)
{
uint x, y;
for ( x = z; x > 0; x -- )
for ( y = 110; y > 0; y -- );
}
void init_I2C ()
{
sda = 1;
delay ();
scl = 1;
delay ();
}
I2C启动:在SCL为高电平期间,SDA出现下降沿
时序如下:
代码如下:
void start()
{
sda = 1;
delay ();
scl= 1;
delay ();
sda = 0;
delay ();
}
I2C停止:在SCL高电平期间,SDA上产生一上升沿
时序如下:
代码如下:
void stop()
{
sda = 0;
delay ();
scl = 1;
delay ();
sda = 1;
delay ();
}
应答信号:在SCL高电平期间,SDA被从设备拉低表示应答
时序如下:
代码如下:
void ack()
{
uchar i = 0;
sda = 0;
_nop_();
_nop_();
scl = 1;
delay ();
while ( i < 250 )
i++;
scl = 0;
delay ();
}
非应答信号:在SCL高电平期间,SDA保持高表示非应答
时序如下:
代码如下:
void nack()
{
uchar i = 0;
sda = 1;
_nop_();
_nop_();
scl = 1;
delay ();
while ( i < 250 )
i++;
scl = 0;
delay ();
}
写入一个字节
void write_byte(uchar date)
{
uchar i, temp;
temp = date;
for (i = 0; i < 8; i++)
{
temp = temp << 1;
scl = 0;
delay ();
sda = CY;
delay ();
scl = 1;
delay ();
}
scl = 0;
delay ();
sda = 1;
delay ();
}
读取一个字节
uchar read_byte()
{
uchar i, k = 0;
scl = 0;
delay ();
sda = 1;
delay ();
for (i = 0; i < 8; i++)
{
scl = 1;
delay ();
k = (k << 1) | sda;
delay ();
scl = 0;
delay ();
}
return k;
}
向AT24C02指定单元地址写入一字节数据
void write_at24c02(uchar addr, uchar data)
{
start();
write_byte(AT24C02_ADDR + 0);
ack();
write_byte(addr);
ack();
write_byte(data);
ack();
end();
}
从AT24C02指定单元地址读取一字节数据
uchar read_at24c02(uchar addr)
{
uchar data;
start();
write_byte(AT24C02_ADDR + 0);
ack();
write_byte(addr);
ack();
start();
write_byte(AT24C02_ADDR + 1);
ack();
data = read_byte();
end();
return data;
}
向存储单元211写入数据0xae,然后在再从存储单元211中读取出来,送给P1口led显示
void main()
{
write_at24c02(211, 0xae);
delay_10ms(); //写完后,必须延时一定的时间才可以读取,否则不行
P1 = read_at24c02(211);
……
}
这里只是列举了一个51单片机下的I2C应用程序。实际上单片机I2C应用程序有很多,因为单片机的种类本身就有很多,如:51系列单片机、AVR单片机、PIC单片机、STM系列、NXP LPC系列等等;而I2C的方法又包括硬件I2C和软件I2C;即使同一种单片机,不同人编写的程序也可能差异较大。因此,单片机I2C应用程序可以说千差万别、种类繁多。在此,由于不是本文重点,只介绍一种最为基本的51单片机,采用软件I2C的方式就可以了。