i2c协议简要分析(转载)
声明
本文大部分内容为转载,因此标定为转载
源地址:
http://www.cnblogs.com/zym0805/archive/2011/07/31/2122890.html
http://blog.csdn.net/lxl123/article/details/22884719
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位数据,应答,停止位。
注意事项
- SCL一直由Master控制,SDA依照数据传送的方向,读数据时由Slave控制SDA,写数据时由Master控制SDA。当8位数据传送完毕之后,应答位或者否应答位的SDA控制权与数据位传送时相反。
- 开始位“Start”和停止位“Stop”,只能由Master来发出。
- 地址的8位传送完毕后,成功配置地址的Slave设备必须发送“ACK”。否则否则一定时间之后Master视为超时,将放弃数据传送,发送“Stop”。
- 当写数据的时候,Master每发送完8个数据位,Slave设备如果还有空间接受下一个字节应该回答“ACK”,Slave设备如果没有空间接受更多的字节应该回答“NACK”,Master当收到“NACK”或者一定时间之后没收到任何数据将视为超时,此时Master放弃数据传送,发送“Stop”。
- 当读数据的时候,Slave设备每发送完8个数据位,如果Master希望继续读下一个字节,Master应该回答“ACK”以提示Slave准备下一个数据,如果Master不希望读取更多字节,Master应该回答“NACK”以提示Slave设备准备接收Stop信号。
- 当Master速度过快Slave端来不及处理时,Slave设备可以拉低SCL不放(SCL=0将发生“线与”)以阻止Master发送更多的数据。此时Master将视情况减慢或结束数据传送。
伪代码实现
起始位,停止位
/*
函数:I2C_Start()
功能:产生I2C总线的起始状态
说明:
SCL处于高电平期间,当SDA出现下降沿时启动I2C总线
不论SDA和SCL处于什么电平状态,本函数总能正确产生起始状态
本函数也可以用来产生重复起始状态
本函数执行后,I2C总线处于忙状态
*/
void I2C_Start()
{
EA=0;
I2C_SCL = 1;
I2C_Delay();
I2C_SDA = 1;
I2C_Delay(); //起始条件建立时间大于4.7us延时
I2C_SDA = 0; //发送起始信号
I2C_Delay();
I2C_SCL = 0; //钳住I2C总线,准备发送或接收数据
I2C_Delay();
I2C_Delay();
I2C_Delay();
}
/*
函数:I2C_Stop()
功能:产生I2C总线的停止状态
说明:
SCL处于高电平期间,当SDA出现上升沿时停止I2C总线
不论SDA和SCL处于什么电平状态,本函数总能正确产生停止状态
本函数执行后,I2C总线处于空闲状态
*/
void I2C_Stop()
{
unsigned int t = I2C_STOP_WAIT_VALUE;
I2C_SDA = 0; //发送结束条件的数据信号
I2C_Delay();
I2C_SCL = 1; //发送结束条件的时钟信号
I2C_Delay();
I2C_SDA = 1; //发送I2C总线结束信号
I2C_Delay();
EA=1;
while ( --t != 0 ); //在下一次产生Start之前,要加一定的延时
}
单字节读和写
/*
函数:I2C_Write()
功能:向I2C总线写1个字节的数据
参数:
dat:要写到总线上的数据
*/
void I2C_Write(unsigned char dat)
{
/*发送1,在SCL为高电平时使SDA信号为高*/
/*发送0,在SCL为高电平时使SDA信号为低*/
unsigned char t ;
for(t=0;t<8;t++)
{
I2C_SDA = (bit)(dat & 0x80);
I2C_Delay();
I2C_SCL = 1; //置时钟线为高,通知被控器开始接收数据位
I2C_Delay();
I2C_SCL = 0;
I2C_Delay();
dat <<= 1;
}
}
/*
函数:I2C_Read()
功能:从从机读取1个字节的数据
返回:读取的一个字节数据
*/
unsigned char I2C_Read()
{
unsigned char dat=0;
unsigned char t ;
bit temp;
I2C_Delay();
I2C_Delay();
I2C_SDA = 1; //在读取数据之前,要把SDA拉高
I2C_Delay();
for(t=0;t<8;t++)
{
I2C_SCL = 0; /*接受数据*/
I2C_Delay();
I2C_SCL = 1;//置时钟线为高使数据线上升沿数据有效
I2C_Delay();
temp = I2C_SDA;
dat <<=1;
if (temp==1) dat |= 0x01;
}
I2C_SCL = 0;
I2C_Delay();
return dat;
}
ACK & NACK
/*
函数:I2C_GetAck()
功能:读取从机应答位
返回:
0:从机应答
1:从机非应答
说明:
从机在收到每个字节的数据后,要产生应答位
从机在收到最后1个字节的数据后,一般要产生非应答位
*/
bit I2C_GetAck()
{
bit ack;
unsigned char Error_time=255;
I2C_Delay();
I2C_SDA = 1; /*8位发送完后释放数据线,准备接收应答位 释放总线*/
I2C_Delay();
I2C_SCL = 1; /*接受数据*/
I2C_Delay();
do
{
ack = I2C_SDA;
Error_time--;
if(Error_time==0)
{
I2C_SCL = 0;
I2C_Delay();
return 1;
}
}while(ack); //判断是否接收到应答信号
I2C_SCL = 0; //清时钟线,钳住I2C总线以便继续接收
I2C_Delay();
I2C_Delay();
I2C_Delay();
return 0;
}
/*
函数:I2C_PutAck()
功能:主机产生应答位或非应答位
参数:
ack=0:主机产生应答位
ack=1:主机产生非应答位
说明:
主机在接收完每一个字节的数据后,都应当产生应答位
主机在接收完最后一个字节的数据后,应当产生非应答位
*/
void I2C_PutAck(bit ack)
{
I2C_SDA = ack; //在此发出应答或非应答信号
I2C_Delay();
I2C_SCL = 1; //应答
I2C_Delay();
I2C_SCL = 0; //清时钟线,钳住I2C总线以便继续接收 ,继续占用
I2C_Delay(); //等待时钟线的释放
I2C_Delay();
I2C_Delay();
I2C_Delay();
}
多字节读和写
/*
函数:I2C_Puts()
功能:主机通过I2C总线向从机发送多个字节的数据
参数:
SlaveAddr:从机地址(高7位是从机地址,最低位是写标志0)
SubAddr:从机的子地址
Size:数据的字节数
*dat:要发送的数据
返回:
0:发送成功
1:在发送过程中出现异常
*/
bit I2C_Puts(unsigned char SlaveAddr, unsigned char SubAddr, unsigned char Size, char *dat)
{
//检查长度
if ( Size == 0 ) return 0;
//确保从机地址最低位是0
SlaveAddr &= 0xFE;
//启动I2C总线
I2C_Start();
//发送从机地址
I2C_Write(SlaveAddr);
if ( I2C_GetAck() )
{
I2C_Stop();
return 1;
}
//发送子地址
I2C_Write(SubAddr);
if ( I2C_GetAck() )
{
I2C_Stop();
return 1;
}
//发送数据
do
{
I2C_Write(*dat++);
if ( I2C_GetAck() )
{
I2C_Stop();
return 1;
}
} while ( --Size != 0 );
//发送完毕,停止I2C总线,并返回结果
I2C_Stop();
return 0;
}
/*
函数:I2C_Put()
功能:主机通过I2C总线向从机发送1个字节的数据
参数:
SlaveAddr:从机地址(高7位是从机地址,最低位是写标志0)
SubAddr:从机的子地址
dat:要发送的数据
返回:
0:发送成功
1:在发送过程中出现异常
*/
bit I2C_Put(unsigned char SlaveAddr, unsigned char SubAddr, char dat)
{
return I2C_Puts(SlaveAddr,SubAddr,1,&dat);
}
/*
函数:I2C_Gets()
功能:主机通过I2C总线从从机接收多个字节的数据
参数:
SlaveAddr:从机地址(高7位是从机地址,最低位是读标志1)
SubAddr:从机的子地址
Size:数据的字节数
*dat:保存接收到的数据
返回:
0:接收成功
1:在接收过程中出现异常
*/
bit I2C_Gets(unsigned char SlaveAddr, unsigned char SubAddr, unsigned char Size, unsigned char *dat)
{
//检查长度
if ( Size == 0 ) return 0;
//确保从机地址最低位是0
SlaveAddr &= 0xFE; //确保最低位是0
//启动I2C总线
I2C_Start();
//发送从机地址
I2C_Write(SlaveAddr);
if ( I2C_GetAck() )
{
I2C_Stop();
return 1;
}
//发送子地址
I2C_Write(SubAddr);
if ( I2C_GetAck() )
{
I2C_Stop();
return 1;
}
//发送重复起始条件
I2C_Start();
//发送从机地址
SlaveAddr |= 0x01;
I2C_Write(SlaveAddr);
if ( I2C_GetAck() )
{
I2C_Stop();
return 1;
}
//接收数据
for (;;)
{
*dat++ = I2C_Read();
if ( --Size == 0 )
{
I2C_PutAck(1);
break;
}
I2C_PutAck(0);
}
//接收完毕,停止I2C总线,并返回结果
I2C_Stop();
return 0;
}
/*
函数:I2C_Get()
功能:主机通过I2C总线从从机接收1个字节的数据
参数:
SlaveAddr:从机地址(高7位是从机地址,最低位是读标志1)
SubAddr:从机的子地址
*dat:保存接收到的数据
返回:
0:接收成功
1:在接收过程中出现异常
*/
bit I2C_Get(unsigned char SlaveAddr, unsigned char SubAddr, unsigned char *dat)
{
return I2C_Gets(SlaveAddr,SubAddr,1,dat);
}
i2c协议简要分析(转载)的更多相关文章
- InnoDB多版本(MVCC)实现简要分析(转载)
http://hedengcheng.com/?p=148 基本知识 假设对于多版本(MVCC)的基础知识,有所了解.InnoDB为了实现多版本的一致读,采用的是基于回滚段的协议. 行结构 InnoD ...
- 微店APP协议简要分析
1.通过抓包软件charles进行抓包,点击微信收款后,抓包内容都是加密处理过 2.加载分析定位这些字段的加密函数. WDTNThorParameterProcessor HTTPBody:task ...
- I2C总线协议图解(转载)
转自:http://blog.csdn.net/w89436838/article/details/38660631 另外,https://blog.csdn.net/qq_38410730/arti ...
- iNode协议再次分析
iNode协议再次分析 声明: 1)本报告由博客园bitpeach撰写,版权所有,免费转载,请注明出处,并请勿作商业用途. 2)若本文档内有侵权文字或图片等内容,请联系作者bitpeach删除相应部分 ...
- 转:InnoDB多版本(MVCC)实现简要分析
InnoDB多版本(MVCC)实现简要分析 基本知识 假设对于多版本(MVCC)的基础知识,有所了解.InnoDB为了实现多版本的一致读,采用的是基于回滚段的协议. 行结构 InnoDB表数据的组织方 ...
- Android IOS WebRTC 音视频开发总结(八十六)-- WebRTC中RTP/RTCP协议实现分析
本文主要介绍WebRTC中的RTP/RTCP协议,作者:weizhenwei ,文章最早发表在编风网,微信ID:befoio 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID ...
- (原创) 巩固理解I2C协议(MCU,经验)
题外话:这几天天气突然转冷了.今天已是11月23日了,查查黄历,昨天(11月22日)刚好是小雪,一夜温度骤降,果然老祖先的经验有灵验!冬天来了,还是多加加衣服,注意保暖! 1.Abstract ...
- Google发布SSLv3漏洞简要分析报告
今天上午,Google发布了一份关于SSLv3漏洞的简要分析报告.根据Google的说法,该漏洞贯穿于所有的SSLv3版本中,利用该漏洞,黑客可以通过中间人攻击等类似的方式(只要劫持到的数据加密两端均 ...
- CVE-2015-5122 简要分析(2016.4)
CVE-2015-5122 简要分析 背景 最近在学习Flash漏洞的分析,其与IE漏洞的分析还是有诸多的不同(不便)之处,折腾了一阵子终于克服了没有符号表.Flash的超时定时器等问题.所以找到了去 ...
随机推荐
- [手机取证] Apple正在行动起来封堵后门?
在被爆出“后门”事件后,苹果似乎已经开始了自己的行动,在最新发布的iOS 8 Beta5版本中,iOS取证专家Zdziarski提及的众多后台服务中的packet sniffer服务已经被禁用,体现出 ...
- Solaris 自动挂载
修改文件:vim /etc/vfstab添加命令:/dev/dsk/c2t0d0p0:1 - /media/PARTITION1 pcfs ...
- LibreOffice 打开中文乱码
From: http://hi.baidu.com/designhouse/item/746130c01f98a42dee4665de 1:文件->打开 2:文件类型:已编码文本
- Partitioning & Archiving tables in SQL Server (Part 1: The basics)
Reference: http://blogs.msdn.com/b/felixmar/archive/2011/02/14/partitioning-amp-archiving-tables-in- ...
- Android: R cannot be resolved to a varia...
Android: R cannot be resolved to a varia... 2012-07-27 10:58:32 上传者: wangdao下载(0) 浏览(57568) 评论(0 ...
- qt之mapx组件编程c2248和c2512错误
mapx组件利用qt工具dumpcpp到处头文件和.cpp文件后将其加入到新建的qt项目中即可. 不过本人遇到问题知道今天偶然的解决了.记下来,以免忘记. demo的项目结构如下: 然后在.pro文件 ...
- 如何为datagridview加上序号
最简单的方法是在Datagridview的事件RowPostPaint事件下面添加如下代码即可 private void dataGridView1_RowPostPaint(object sende ...
- linux 时间同步
[转自 qing_gee的专栏 :http://blog.csdn.net/qing_gee/article/details/42234997 ] 前言:在我们的项目中,需要同步Linux服务器的时间 ...
- C++中输入输出的重定向
重定向,就是: 把 原来的 cin 从键盘输入 改为从文件输入. 把 原来的 cout 向屏幕输出 改为输出到文件. 例如: cin>>line; 原来要从键盘拍入.现在自动到某文件读取, ...
- android开发--下载图片
1.背景介绍 网络上图片的请求,是我们最常见的网络请求之一,不亚于对json/xml数据的请求.一般要展示给用户看的,都不会是纯粹的文字,往往都是图文信息.而在移动互联网时代,图文又往往需要最新的资讯 ...