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的超时定时器等问题.所以找到了去 ...
随机推荐
- string相关总结
一 <string> 1 string类常见成员函数 (1)属性相关 s.empty() s为空时返回true,否则返回false s.size() 返回s中字符的个数,不包 ...
- 【linux编程】linux中HZ和Jiffies的关系
读cubic源码的时候遇到了HZ和jiffies,不懂这两者代表什么.网上描述的是这样的 全局变量jiffies用来记录自系统启动以来产生的节拍的总数.启动时,内核将该变量初始化为0,此后,每次时钟中 ...
- nyoj 82 迷宫寻宝(二)
http://acm.nyist.net/JudgeOnline/problem.php?pid=83 题目解法主要在于判断两线段是否相交,思路是穷举所有地图四周的点,其中每一个边界上的点和终点构成一 ...
- 洛谷P2409 Y的积木
P2409 Y的积木 77通过 491提交 题目提供者zhouyonglong 标签云端评测 难度普及+/提高 提交 讨论 题解 最新讨论 这组数据几乎可以卡掉所有程- 第一个题解有点问题 求教大 ...
- JS数组转成json字符串的注意事项
在js中常常会将一个数组转成json字符串发送给后端. 这时候在定义数组数据结构的时候需要格外注意,意味json中是有集合和对象的区别的. 集合的定义是[];对象的的定义是{}. 这时候,在创建数组时 ...
- 分拆素数和 HDU - 2098
把一个偶数拆成两个不同素数的和,有几种拆法呢? Input输入包含一些正的偶数,其值不会超过10000,个数不会超过500,若遇0,则结束.Output对应每个偶数,输出其拆成不同素数的个数,每个结果 ...
- android开发--sqlite数据库
一.SQLite简介 Google为Andriod的较大的数据处理提供了SQLite,他在数据存储.管理.维护等各方面都相当出色,功能也非常的强大.SQLite具备下列特点: 1.轻量级 使用 SQL ...
- Web APi之安装配置实现Cors跨域
参考:http://www.cnblogs.com/CreateMyself/p/4836628.html 1.通过NuGet下载程序包,搜索程序包[Microsoft.AspNet.WebApi.C ...
- cx_Oracle 报错 Reason: image not found
(Study_env) ➜ DAL python -c "import cx_Oracle"Traceback (most recent call last): File &quo ...
- sql 用union合并合并查询结果
合并操作与连接相似,因为他们都是将两个表合并起来的另一个表的方法,然而他们的合并方法有本质的区别, 合并是两个表的相加,连接时时两个表的相乘: 01 在合并中两个表原列的数量与数据类型必须相同:在连接 ...