為了加深對I2C總線的了解,用C語言模拟IIC總線,邊看源代碼邊讀波形:
如下圖所示的寫操作的時序圖
讀時序的了解同理。對于時序不了解的朋友請參考 I2C總線概述之時序(二)
#include<reg51.h>
#define uchar unsigned char
#define uint unsigned int
#define write_ADD 0xa0
#define read_ADD 0xa1
uchar a;
sbit SDA=P2^0;
sbit SCL=P2^1;
void SomeNop(); //短延時
void init(); //初始化
void check_ACK(void);
void I2CStart(void);
void I2cStop(void);
void write_byte(uchar dat);//寫位元組
void delay(uint z);
uchar read_byte(); //讀位元組
void write(uchar addr,uchar dat); //指定位址寫
uchar read(uchar addr); //指定位址讀
bit flag; //應答标志位
void main()
{
init();
write_add(5,0xaa); //向位址5寫入0xaa
delay(10); //延時,否則被坑呀!!!
P1=read_add(5); //讀取位址5的值
while(1);
}
//***************************************************************************
void delay()//簡單延時函數
{ ;; }
//***************************************************************************
void start() //開始信号 SCL在高電平期間,SDA一個下降沿則表示啟動信号
{
sda=1; //釋放SDA總線
delay();
scl=1;
delay();
sda=0;
delay();
}
//***************************************************************************
void stop() //停止 SCL在高電平期間,SDA一個上升沿則表示停止信号
{
sda=0;
delay();
scl=1;
delay();
sda=1;
delay();
}
//***************************************************************************
void respons() //應答 SCL在高電平期間,SDA被從裝置拉為低電平表示應答
{
uchar i;
scl=1;
delay();
//至多等待250個CPU時鐘周期
while((sda==1)&&(i<250))i++;
scl=0;
delay();
}
//***************************************************************************
void init()//總線初始化 将總線都拉高一釋放總線 發送啟動信号前,要先初始化總線。即總有檢測到總線空閑才開始發送啟動信号
{
sda=1;
delay();
scl=1;
delay();
}
//***************************************************************************
void write_byte(uchar date) //寫一個位元組
{
uchar i,temp;
temp=date;
for(i=0;i<8;i++)
{
temp=temp<<1;
scl=0;//拉低SCL,因為隻有在時鐘信号為低電平期間按資料線上的高低電平狀态才允許變化;并在此時和上一個循環的scl=1一起形成一個上升沿
delay();
sda=CY;
delay();
scl=1;//拉高SCL,此時SDA上的資料穩定
delay();
}
scl=0;//拉低SCL,為下次資料傳輸做好準備
delay();
sda=1;//釋放SDA總線,接下來由從裝置控制,比如從裝置接收完資料後,在SCL為高時,拉低SDA作為應答信号
delay();
}
//***************************************************************************
uchar read_byte()//讀一個位元組
{
uchar i,k;
scl=0;
delay();
sda=1;
delay();
for(i=0;i<8;i++)
{
scl=1;//上升沿時,IIC裝置将資料放在sda線上,并在高電平期間資料已經穩定,可以接收啦
delay();
k=(k<<1)|sda;
scl=0;//拉低SCL,使發送端可以把資料放在SDA上
delay();
}
return k;
}
//***************************************************************************
void write_add(uchar address,uchar date)//任意位址寫一個位元組
{
start();//啟動
write_byte(0xa0);//發送從裝置位址
respons();//等待從裝置的響應
write_byte(address);//發出晶片内位址
respons();//等待從裝置的響應
write_byte(date);//發送資料
respons();//等待從裝置的響應
stop();//停止
}
//***************************************************************************
uchar read_add(uchar address)//讀取一個位元組
{
uchar date;
start();//啟動
write_byte(0xa0);//發送發送從裝置位址 寫操作
respons();//等待從裝置的響應
write_byte(address);//發送晶片内位址
respons();//等待從裝置的響應
start();//啟動
write_byte(0xa1);//發送發送從裝置位址 讀操作
respons();//等待從裝置的響應
date=read_byte();//擷取資料
stop();//停止
return date;//傳回資料
}