软件模拟I2C主从机作者:王金龙创建时间:2010年10月21日修改时间:2010年10月21日学校:西南科技大学目录1.I2C总线引脚定义:...........................................................................32.总线时序定义:......................................................................................33.模拟I2C主机程序编程:......................................................................54.模拟I2C从机编程(中断方式).........................................................95.后记:....................................................................................................131.1.1.1.I2CI2CI2CI2C总线引脚定义:SCL:时钟线SDA:数据线2.2.2.2.总线时序定义:a.空闲模式:SDA=1SCL=1b.总线的线与:连到总线上的任一器件输出的低电平,都将使总线的信号变低。c.数据位的有效性规定(DataValidity):I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定;只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。d.起始和终止信号:SCL为高电平时,SDA表现为下降沿则表示起始信号;SCL为高电平时,SDA表现为上升沿则表示终止信号。起始和终止信号都是由主机发出的,在起始信号产生后,总线就处于被占用的状态;在终止信号产生后,总线就处于空闲状态。e.从机事务处理:接收器件收到一个完整的数据字节后,有可能需要完成一些其它工作,如处理内部中断服务等,可能无法立刻接收下一个字节,这时接收器件可以将SCL线拉成低电平,从而使主机处于等待状态。直到接收器件准备好接收下一个字节时,再释放SCL线使之为高电平,从而使数据传送可以继续进行。f.字节传送与应答:每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。若因某种原因从机不能对主机应答时,从机应当释放SDA数据线(SDA=1),由主机产生一个终止信号以结束总线的数据传送。若从机已正常接收到数据,但是无力再接收数据时,应当对无法接收的第一字节产生无应答,主机则应发出终止信号以结束数据的继续传送。当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。g.数据帧格式:在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用“0”表示主机发送数据(T),“1”表示主机接收数据(R)。每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。在总线的一次数据传送过程中,可以有以下几种组合方式:1主机向从机发送数据,数据传送方向在整个传送过程中不变2主机在第一个字节后,立即从从机读数据3在传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复产生一次,但两次读/写方向位正好反相。注:有阴影部分表示数据由主机向从机传送,无阴影部分则表示数据由从机向主机传送。A表示应答,A非表示非应答(高电平)。S表示起始信号,P表示终止信号。3.3.3.3.模拟I2CI2CI2CI2C主机程序编程:/***************起始信号S*******************/voidstart(){scl=1;//SCL高电平delay_24C02();sda=1;//SDA下降沿delay_24C02();sda=0;delay_24C02();}/****************终止信号P******************/voidstop(){scl=1;//SCL高电平delay_24C02();sda=0;//SDA上升沿delay_24C02();sda=1;delay_24C02();}/***************应答信号A*******************/voidrespons(){uchari;scl=1;delay_24C02();while((sda==1)&&(i250))//保持两个SCL跳变沿时的SDA=1{i++;}scl=0;delay_24C02();}/**************向24C02写字节*******************/voidwrite_byte(uchardate){uchartemp=0,i;temp=date;for(i=0;i8;i++){temp=temp1;scl=0;sda=CY;delay_24C02();scl=1;delay_24C02();}scl=0;delay_24C02();sda=1;delay_24C02();}/*************从24C02读字节********************/ucharread_byte(){uchari,temp=0;scl=0;delay_24C02();sda=1;delay_24C02();for(i=0;i8;i++){scl=1;delay_24C02();temp=(temp1)|sda;scl=0;delay_24C02();}returntemp;}/*************向24C02写入一个完整数据*********************/voidwrite_24C02(uchardate){start();write_byte(0xa0);respons();write_byte(1);respons();write_byte(date);respons();stop();}/*************从24C02读出一个完整数据*********************/ucharread_24C02(){uchartemp=0;start();delay_24C02();write_byte(0xa0);respons();delay_24C02();write_byte(1);respons();start();delay(5);write_byte(0xa1);respons();delay_24C02();temp=read_byte();respons();stop();returntemp;}/*************向24C02写入N个字节*********************/voidcpu_rom(ucharN){uchari,temp;start();write_byte(0xa0);respons();write_byte(N);respons();delay(5);for(i=0;iN;i++){temp=wr[i];write_byte(temp);respons();}stop();}/************从24C02读出M个字节**********************/voidrom_cpu(ucharM){uchari,temp=0;start();delay_24C02();write_byte(0xa0);respons();delay_24C02();write_byte(M);respons();start();delay(5);write_byte(0xa1);respons();for(i=0;iM;i++){delay_24C02();wr[i]=read_byte();respons();}stop();}/************从24C02读出器件地址**********************/ucharread_adr(){uchartemp=0;start();write_byte(0xa1);respons();delay_24C02();temp=read_byte();returntemp;}4.4.4.4.模拟I2CI2CI2CI2C从机编程(中断方式)/********************************************************************函数功能:延时us入口参数:无返回:无。备注:函数是用来实现短暂延时,用于对接上主机时钟********************************************************************/voiddelay_24C02()//延时5us{;;;}/********************************************************************函数功能:从机应答函数入口参数:无返回:无。备注:应答函数,保证在两个时钟跳变沿SDA为低,则主机认为是从机应答********************************************************************/voidACK(void){while(!SCL);SDA=0;//第9个CLK变高的情况下,SDA输出0while(SCL);SDA=1;//第9个CLK变低的情况下,SDA输出1}/********************************************************************函数功能:初始化函数入口参数:无返回:无。备注:无。********************************************************************/voidinit(){SDA=1;//初始数据引脚SCL=1;//初始时钟引脚EA=1;//开总中断IP=0x90;//中断优先级设定,设为串口INTOT0INT1T1EX0=1;//开外部中断0}/********************************************************************函数功能:INT0中断服务子程序入口参数:无返回:无。备注:以Start信号作为中断触发********************************************************************/voidIIC()interrupt0using0{EX0=0;//关中断delay_24C02();if(SCL)//真的是Start信号吗?{//---------------------------读取器件地址---------------------------------while(SCL);//START时的SCL高电平状态就等待for(gg=8;gg0;gg--)//接收器件地址{while(!SCL);//SCL低电平状态就等待DEVICE_ADR=1;//高位在前if(SDA)//数据的第一个CLK高电平来临DEVICE_ADR|=0x01;//上升沿读取数据位while(SCL);//SCL高电平状态就等待}ACK();//对设备地址ACK应答信//-----------以上收到了设备地址,并知晓主机要对从机进行读还是写操作---//判断地址是否为0xa0,因为不是每一次中断进入都可以刚好是Startif((DEVICE_ADR&0xfe)==0xa0){//------------------