I2C总线时序模拟
- #include<reg52.h>
- #define uchar unsigned char
- sbit sda=P2^0;
- sbit scl=P2^1;//用单片机的两个I/O口模拟I2C接口
- uchar a;
- ***************************************************************************
- 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();
- 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;//返回数据
- }
对I2C总线协议的理解
1、无论读与写,都是在时钟线为低时把数据送到数据总线上,在高时采样数据,把数据锁存到内部,所以读之前先把时钟线拉低,做好准备(数据线为高表示释放数据线),为接下来读数据做好准备。也就是时钟信号为低时,数据线上的高低电平才允许变化,时钟信号为高时,数据总线上的数据必须保持稳定。
2、起始信号产生后,总线为被占用状态(SDA拉低);终止信号产生后,总线为空闲状态(SDA被释放了)。
3、接收器件收到一个完整的数据字节后有可能需要完成一些其它工作,如处理中断,可能无法立刻接收下一字节,这时从机将SCL拉成低电平,从而使主机处于等待状态。直到从机准备好接收下一字节,再释放SCL线使之为高,从而数据传送可继续进行。数据线上的数据是在时钟信号为高时被采样。
4、当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一结束信号。这个信号是由对从机的非应答来实现的。然后,从机释放SDA线,以允许主机产生终止信号。