历史上的今天

今天是:2024年10月15日(星期二)

正在发生

2019年10月15日 | 51单片机I2C详解与程序源码

发布者:EtherealEssence 来源: eefocus关键字:51单片机  I2C  串行数据  通信协议 手机看文章 扫描二维码
随时随地手机看文章

I2C是由Philips公司发明的一种串行数据通信协议,仅使用两根信号线:SerialClock(简称SCL)和SerialData(简称SDA)。I2C是总线结构,1个Master,1个或多个Slave,各Slave设备以7位地址区分,地址后面再跟1位读写位,表示读(=1)或者写(=0),所以我们有时也可看到8位形式的设备地址,此时每个设备有读、写两个地址,高7位地址其实是相同的。


I2C数据格式如下:

无数据:SCL=1,SDA=1;

开始位(Start):当SCL=1时,SDA由1向0跳变;

停止位(Stop):当SCL=1时,SDA由0向1跳变;

数据位:当SCL由0向1跳变时,由发送方控制SDA,此时SDA为有效数据,不可随意改变SDA;

当SCL保持为0时,SDA上的数据可随意改变;

地址位:定义同数据位,但只由Master发给Slave;

应答位(ACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=0;

否应答位(NACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=1。

当数据为单字节传送时,格式为:

开始位,8位地址位(含1位读写位),应答,8位数据,应答,停止位。

当数据为一串字节传送时,格式为:

开始位,8位地址位(含1位读写位),应答,8位数据,应答,8位数据,应答,……,8位数据,应答,停止位。


需要注意的是:

1,SCL一直由Master控制,SDA依照数据传送的方向,读数据时由Slave控制SDA,写数据时由Master控制SDA。当8位数据传送完毕之后,应答位或者否应答位的SDA控制权与数据位传送时相反。


2,开始位“Start”和停止位“Stop”,只能由Master来发出。


3,地址的8位传送完毕后,成功配置地址的Slave设备必须发送“ACK”。否则否则一定时间之后Master视为超时,将放弃数据传送,发送“Stop”。


4,当写数据的时候,Master每发送完8个数据位,Slave设备如果还有空间接受下一个字节应该回答“ACK”,Slave设备如果没有空间接受更多的字节应该回答“NACK”,Master当收到“NACK”或者一定时间之后没收到任何数据将视为超时,此时Master放弃数据传送,发送“Stop”。


5,当读数据的时候,Slave设备每发送完8个数据位,如果Master希望继续读下一个字节,Master应该回答“ACK”以提示Slave准备下一个数据,如果Master不希望读取更多字节,Master应该回答“NACK”以提示Slave设备准备接收Stop信号。


6,当Master速度过快Slave端来不及处理时,Slave设备可以拉低SCL不放(SCL=0将发生“线与”)以阻止Master发送更多的数据。此时Master将视情况减慢或结束数据传送。


在实际应用中,并没有强制规定数据接收方必须对于发送的8位数据做出回应,尤其是在Master和Slave端都是用GPIO软件模拟的方法来实现的情况下,编程者可以事先约定数据传送的长度,slave不检查NACK,有时可以起到减少系统开销的效果。但是如果slave方是硬件i2c要求一定要标准的NACK,master方是GPIO软件模拟i2c并没有正确的发送NACK,就会出现“slave收不到stop”导致i2c挂死。


在正常情况下,I2C总线协议能够保证总线正常的读写操作。但是,当I2C主设备异常复位时(看门狗动作,板上电源异常导致复位芯片动作,手动按钮复位等等)有可能导致I2C总线死锁产生。下面详细说明一下总线死锁产生的原因。


在I2C主设备进行读写操作的过程中.主设备在开始信号后控制SCL产生8个时钟脉冲,然后拉低SCL信号为低电平,在这个时候,从设备输出应答信号,将SDA信号拉为低电平。如果这个时候主设备异常复位,SCL就会被释放为高电平。此时,如果从设备没有复位,就会继续I2C的应答,将SDA一直拉为低电平,直到SCL变为低电平,才会结束应答信号。而对于I2C主设备来说.复位后检测SCL和SDA信号,如果发现SDA信号为低电平,则会认为I2C总线被占用,会一直等待SCL和SDA信号变为高电平。这样,I2C主设备等待从设备释放SDA信号,而同时I2C从设备又在等待主设备将SCL信号拉低以释放应答信号,两者相互等待,I2C总线进人一种死锁状态。同样,当I2C进行读操作,I2C从设备应答后输出数据,如果在这个时刻I2C主设备异常复位而此时I2C从设备输出的数据位正好为0,也会导致I2C总线进入死锁状态。


方法


(1)尽量选用带复位输人的I2C从器件。


(2)将所有的从I2C设备的电源连接在一起,通过MOS管连接到主电源,而MOS管的导通关断由I2C主设备来实现。


(3)在I2C从设备设计看门狗的功能。


(4)在I2C主设备中增加I2C总线恢复程序。


每次I2C主设备复位后,如果检测到SDA数据线被拉低,则控制I2C中的SCL时钟线产生9个时钟脉冲(针对8位数据的情况,“9个clk可以激活”的方法来自NXP的文档,NXP(Philips)作为I2C总线的鼻祖,这样的说法是可信的),这样I2C从设备就可以完成被挂起的读操作,从死锁状态中恢复过来。


这种方法有很大的局限性,因为大部分主设备的I2C模块由内置的硬件电路来实现,软件并不能够直接控制SCL信号模拟产生需要时钟脉冲。


或者,发送I2C_Stop条件也能让从设备释放总线。


如果是GPIO模拟I2C总线实现,那么在I2C操作之前,加入I2C总线状态检测I2C_Probe,如果总线被占用,则可尝试恢复总线,待总线释放后,再进行操作。要保证I2C操作最小单元的完整性,不被其他事件(中断、高优先级线程,等)打断。


(5)在I2C总线上增加一个额外的总线恢复设备。这个设备监视I2C总线。当设备检测到SDA信号被拉低超过指定时间时,就在SCL总线上产生9个时钟脉冲,使I2C从设备完成读操作,从死锁状态上恢复出来。总线恢复设备需要有具有编程功能,一般可以用单片机或CPLD实现这一功能。


(6)在I2C上串人一个具有死锁恢复的I2C缓冲器,如Linear公司的LTC4307是一个双向的I2C总线缓冲器,并且具有I2C总线死锁恢复的功能。LTC4307总线输入侧连接主设备,总线输出侧连接所有从设备。当LTC4307检测到输出侧SDA或SCL信号被拉低30ms时,就自动断开I2C总线输入侧与输出侧的连接.并且在输出侧SCL信号上产生16个时钟脉冲来释放总线。当总线成功恢复后,LTC4307会再次连接输入输出侧,使总线能够正常工作。

void I2Cstart()//开始标志  

{     

    SDA=1;  

    SCL=1;  

    SDA=0;  

    delay1ms(4);  

    SCL=0;  

    delay1ms(4);  

}  

void I2Cstop()//结束标志  

{  

    SCL=0;  

    delay1ms(4);  

    SDA=0;  

    delay1ms(4);  

    SCL=1;  

    delay1ms(4);  

    SDA=1;  

    delay1ms(4);  

}  

unsigned char I2Creadack()  

{  

    unsigned char i,byte;  

    byte=0;  

    for(i=0;i<8;i++)  

    {  

        SCL=0;  

        SDA=1;  

        delay1ms(4);  

        byte<<=1;  

        if(SDA==1)  

        {  

            byte|=0x01;  

            delay1ms(4);  

        }  

    }  

    SCL=0;  

    delay1ms(4);  

    SDA=0;  

    delay1ms(4);  

    SCL=1;  

    delay1ms(4);  

    SCL=0;  

    return byte;  

}  

void I2Csend(unsigned char byte)//I2C写数据的过程  

{  

  

    unsigned char mask,i;  

    for(i=0;i<8;i++)  

    {     

        SCL=0;  

        if((mask&byte)==0)  

        {  

            SDA=0;  

        }  

        else  

        {  

            SDA=1;  

        }  

        mask>>=1;  

        delay1ms(4);  

        SCL=1;//给足够时间让数据读取  

        delay1ms(4);  

  

    }  

    SCL=0;  

    SDA=1;      //因为总线上有一个信号为低则低  

    delay1ms(4);  

    SCL=1;  

    delay1ms(4);//等待应答位  

    SCL=0;  

}  

unsigned char I2Cread(void)  

{  

    unsigned char i,byte;  

    byte =0;  

    for(i=0;i<8;i++)  

    {  

        SCL=0;  

        SDA=1;//读数据必须拉高  

        delay1ms(4);  

        SCL=1;//数据稳定  

        delay1ms(4);  

        byte<<=1;  

        if(SDA==1)  

        {  

            byte|=0x01;  

        }  

        delay1ms(4);  

    }  

    SCL=0;  

    delay1ms(4);  

    SDA=0;//发送的应答位  

    delay1ms(4);  

    SCL=1;  

    delay1ms(4);  

    SCL=0;  

    return byte;  

}  

unsigned char I2Cread_eeprom(unsigned char addr)//I2C读取数据  

{  

    unsigned char datebyte,datebyte2;  

    I2Cstart();  

    I2Csend(0xa0);//写数据  

    I2Csend(addr);  

    I2Cstart();  

    I2Csend(0xa1);//读数据  

    datebyte2=I2Creadack();  

    datebyte=I2Cread();  

    I2Cstop();  

    return datebyte;  

  

}  

  

void write_eeprom(unsigned char addr,unsigned char datebyte)  

{  

    I2Cstart();  

    I2Csend(0xa0);  

    I2Csend(addr);  

    I2Csend(datebyte);  

    I2Cstop();  

}  


关键字:51单片机  I2C  串行数据  通信协议 引用地址:51单片机I2C详解与程序源码

上一篇:51单片机DS18B20温度计制作 带仿真和源码
下一篇:单片机国旗自动升降系统仿真及程序

推荐阅读

问题描述:在使用USART做串口通讯时,我只把接收中断打开,并设置抢占优先级为最低一个级别,而接收中断上一个优先级处理事情比较多,可能占用了2ms时间。当我使用9600波特率往下位机发送数据,速度非常快,就是一直按回车发送!问题就出来,不到1分钟时间,通讯没有反应了,死机了。USART配置代码如下:void uart_config(void){USART_InitTypeDef USART...
去年十一前夕 TI(德州仪器)取消了新晔的代理权,时隔一年,今年十一期间 TI 再次提出将在 2020 年 12 月 31 日前终止安富利、文晔和大联大子公司世平的代理权。至此,在 2021 年之后 TI 的代理商有可能只剩下了艾睿一家。 近日,两家被 TI 抛弃的电子元器件分销商大联大及文晔在电子产业传统旺季效应的带动下,第三季度营收双双走高。其中...
10月14日,赣锋锂业发布前三季度业绩预告,归属上市公司股东的净利润为33,000.00万元-36,500.00万元,比上年同期增长0.25%-10.89%。其中,第三季度净利润为17,351.48万元-20,851.48万元,比上年同期增长419.87%-524.74%,Q3业绩暴增。对于业绩增长,赣锋锂业表示,本报告期内,公司锂盐产品产销量同比增长,但锂盐产品价格同比下降,影响公司营业收入及利...
  新浪数码讯 10月14日上午消息,消费科技品牌 Nothing 宣布,公司近期完成了一笔 5000万美元的A+轮融资,并与高通达成多方面合作。这笔融资将主要应用于新产品研发,未来 Nothing会有多款搭载高通骁龙移动处理平台的产品上市。  Nothing的创始人是Carl Pei ,他之前是 OnePlus 的海外业务负责人,离职后创业。Nothing 此前于2021年2月...

史海拾趣

问答坊 | AI 解惑

求在keilc环境下编辑的AT89C2051的C语言程序。

求一个在AT89C2051下能够正常运行的keilc的C语言工程。 哪位大侠,手头上有现成的,比较简单的,给我一个成熟的工程。 因为我要编一个AT89C2051的小程序,无论如何下载到片机上以后,程序都不能正常的运行,所以希望能够参考一下成熟的程序。 一 ...…

查看全部问答∨

Palm软件设计前的六问

当我屁颠屁颠地跟在老师后面不厌其烦询问如何编写某个程序的时候,老师总是对我说先画出你程序的 控制流程序图吧。当时觉得麻烦,也就把这经验之谈当成了一阵风吹过。现在,走上了程序开发这条路 ,才知道开发之前系统的规化一下自已的思路是 ...…

查看全部问答∨

ADS1.2问题

   编译程序时,始终出现:"  Duplicate input file D:\\ARM\\test\\pro4\\pro4_Data\\DebugInRAM\\ObjectCode\\main.o ignored " 的错误。还有就是不能在 *.C 程序中使用printf()函数,他出现:“Could not place stdio ...…

查看全部问答∨

6410(或2440)上, 向GPIO口写1(或置高) 的步骤是不是以下这样:

  6410(或2440)上, 向GPIO口写1(或置高) 的步骤是不是以下这样:     Port X Pull-up/down Register     --> pull-up enabled (置高使能)     Port X Configuration Register    - ...…

查看全部问答∨

在ARM9下wince系统的视频监控如何实现

请教一下在ARM9下wince系统的视频监控如何实现,有没有大虾们做过类似的。最好可以给出实例代码,万分感谢!可以发送到本人邮箱:jhlovebb@126.com…

查看全部问答∨

VHDL程序的一个小问题

在下初学VHDL,下面是一个D触发器的VHDL代码,感觉没错,但是Quartus2在编译的时候总是不能成功,提示: Error (10533): VHDL Wait Statement error at text.vhd(13): Wait Statement must contain condition clause with UNTIL keyword PS: 1. ...…

查看全部问答∨

Lcd  problem

现在要在彩色的lcd显示图片,比如彩色的lcd像素128 128的,图片确是256 256 怎么把缩小呀.又不能失真.…

查看全部问答∨

一个不很专业的问题……

今天刚用NRF24L01无线模块做了一个程控小车,但目前还是初级阶段,只能控制运动,我想再加一些辅助功能,像通过无线传送图像,就是把摄像头采集到的图像通过无线传到电脑,达到一个监控的作用,但是不知道这个无线模块能不能实现,刚网上订购的摄像 ...…

查看全部问答∨

我又回来了!!

考试复习,招聘会,外地面试... 现在有时间了重新拾起单片机…

查看全部问答∨

关于PIC控制LCD 显示字符串问题

我以前用的51的一段显示字符串子函数:   void print(uchar *str) {         while(*str!=\'\\0\')         {                 LCD_Write(*str); ...…

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved