[嵌入式]I2C协议指东
最近闲来无聊,入了一块MPU6050,手头本来就有一块原子的STM32 MINI开发板,凑活着学习了一下IIC,特此总结。
IIC,是集成电路总线【Inter-Intergrated Circuit】的缩写,属于飞利浦公司的原创。
主要用两根线:数据线SDA和时钟线SCL。
关于时序方面本文就不截图了,网上一大堆。
下面就具体说IIC的传输过程中,比较重要的几个方法,下文的代码均是在STM32中实现,是一种模拟IIC。
SCL为输出模式的PC(12),SDA则根据情况切换输入和输出模式,为PC(11)。
1、开始信号
开始信号定义为:SCL高电平时,SDA的下降沿。
//开始信号
void IIC_Start(void)
{
SDA_OUT(); //SDA输出模式
IIC_SDA=;
IIC_SCL=;
delay_us(IIC_DELAY);
IIC_SDA=;//SCL高电平时SDA的下降沿
delay_us(IIC_DELAY);
}
2、结束信号
结束信号定义为:SCL高电平时,SDA的上升沿。
//结束信号
void IIC_Stop(void)
{
SDA_OUT();
IIC_SDA=;
IIC_SCL=;
delay_us(IIC_DELAY);
IIC_SDA=;//SCL高电平时SDA的上升沿
delay_us(IIC_DELAY);
}
其中的SDA_OUT()是STM32的IO口模式设置,其他MCU可忽略或更改。IIC_DELAY是定义的宏,可以控制延迟时间从而控制IIC速率。
3、IIC写一个字节
这里的写一个字节是说,控制了IIC总线的主机往总线上写数据。
void IIC_Send_Byte(u8 data)
{
u8 i;
SDA_OUT();//输出模式
for(i=;i<;i++)
{
IIC_SCL=;//拉低时钟 占据总线
delay_us(IIC_DELAY);
IIC_SDA=(data&0x80)>>;//每次1位,先高位
data<<=;
delay_us(IIC_DELAY);
IIC_SCL=;
delay_us(IIC_DELAY);
}
IIC_SCL=;
}
这里默认是先MSB后LSB,IIC_SDA根据数据位依次置1或0,传输数据时,SCL必须拉低,以此告诉其他器件“传输进行中”,在传输结束后,还需要再次拉高SCL总线。在送完一个字节后,拉低SCL,等待应答。
4、IIC读一个字节
//IIC读一个BYTE
u8 IIC_Read_Byte(void)
{
u8 i,receive=; SDA_IN();//输入模式
READ_SDA=;
for(i=;i<;i++)
{
receive<<=;//先接收的是高位
IIC_SCL=;
delay_us(IIC_DELAY);
IIC_SCL=;
delay_us(IIC_DELAY);
receive|=READ_SDA;
}
IIC_SCL=;
return receive;
}
这里同样的默认是先高位后低位,使用receive|=READ_SDA;来组成数据,接收数据位时,需要先拉低SCL再拉高SCL,然后再读取SDA的数据。这里的READ_SDA和IIC_SDA都是PC(11),只不过是不同的模式。
5、应答
在IIC中,应答不是必须的,所以对于应答的检测其实也不是必须的。
下面是应答和不应答的代码。
//产生ACK应答
void IIC_Ack(void)
{
SDA_OUT();
IIC_SCL=;
delay_us(IIC_DELAY);
IIC_SDA=;
delay_us(IIC_DELAY);
IIC_SCL=;
delay_us(IIC_DELAY);
IIC_SCL=;//SDA为低时 拉低时钟线
delay_us(IIC_DELAY);
}
//不产生ACK应答
void IIC_NAck(void)
{
IIC_SCL=;
SDA_OUT();
IIC_SDA=;
delay_us(IIC_DELAY);
IIC_SCL=;
delay_us(IIC_DELAY);
IIC_SCL=;// SDA为高时 SCL的脉冲
delay_us(IIC_DELAY);
}
6、应答检测
经过我的检验,当STM32写MPU6050时,是不需要进行应答检测的;但是当STM32读MPU6050时,如果不进行应答检测,就会出现数据出错/检测不到MPU6050等奇怪的错误,所以在应用IIC总线协议时,一律增加应答检测是比较好的一种规范做法。
应答检测返回一个值,但是大多数情况中不需要用到这个返回值。
//应答信号确认
//1有ACK
//0无ACK
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=; SDA_IN();// SDA输入模式
IIC_SCL=;
delay_us(IIC_DELAY);
IIC_SDA=;
delay_us(IIC_DELAY);
IIC_SCL=;
delay_us(IIC_DELAY); while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>)
{
IIC_Stop();
return ;
}
}
IIC_SCL=;//关闭时钟
return ;
}
如果SDA一直是高电平没有被从设备【此处为MPU6050】拉低,则说明MPU没有应答,此时停止传输,并返回0.
如果接收到应答了,则把时钟线拉低,等待下一次开始信号。
7、MPU6050相关。
关于IIC的所有函数已经讲完了,下面贴一下MPU6050相关的操作。
//写MPU60X0
u8 IIC_Write_One_Byte(u8 regaddr, u8 data)
{
IIC_Start(); //起始信号
IIC_Send_Byte(SlaveAddress); //发送设备地址+写信号
if(IIC_Wait_Ack()==)
{
IIC_Stop();
return ;
}
IIC_Send_Byte(regaddr); //内部寄存器地址
//IIC_Wait_Ack();
IIC_Send_Byte(data); //内部寄存器数据
//IIC_Wait_Ack();
IIC_Stop(); //发送停止信号 return ;
}
//读MPU60X0
u8 IIC_Read_One_Byte(u8 regaddr)
{
u8 REG_data=;
IIC_Start(); //起始信号
IIC_Send_Byte(SlaveAddress); //发送设备地址+写信号
if(IIC_Wait_Ack()==)
{
IIC_Stop();
return ;
}
IIC_Send_Byte(regaddr); //发送存储单元地址,从0开始
IIC_Wait_Ack();
IIC_Start(); //起始信号
IIC_Send_Byte(SlaveAddress+); //发送设备地址+读信号
IIC_Wait_Ack();
REG_data=IIC_Read_Byte(); //读出寄存器数据,并且不应答
IIC_NAck(); //不回应
IIC_Stop(); //停止信号
return REG_data;
}
可以看到写一个字节的应答检测被我注释掉了,实验证明依旧可以正确写入MPU。
以上就是IIC的所有内容。
总结:IIC主要使用SDA,SCL两条线进行传输,其中SCL是独立的,SDA是接入总线的。当SCL为高时,说明有“事件”:比如开始信号、终止信号或者传输过程;当SCL为低时,说明总线闲,只要某一个设备拉高总线,并使得SDA总线产生一个下降沿,则主设备就可以得知是哪个设备的请求。这种通过独立SCL电平+SDA跳变的组合信号进行多设备整合的总线方案简单、有效,容错高,软件上易于实现,硬件上则更加方便。
[嵌入式]I2C协议指东的更多相关文章
- i2c协议
i2c协议 http://blog.csdn.net/g_salamander/article/details/8016698 总线设备驱动模型 http://blog.csdn.net/u01395 ...
- i2c协议简要分析(转载)
声明 本文大部分内容为转载,因此标定为转载 源地址: http://www.cnblogs.com/zym0805/archive/2011/07/31/2122890.html http://blo ...
- (原创) 巩固理解I2C协议(MCU,经验)
题外话:这几天天气突然转冷了.今天已是11月23日了,查查黄历,昨天(11月22日)刚好是小雪,一夜温度骤降,果然老祖先的经验有灵验!冬天来了,还是多加加衣服,注意保暖! 1.Abstract ...
- I2C协议(转)
1.I2C协议 2条双向串行线,一条数据线SDA,一条时钟线SCL. SDA传输数据是大端传输,每次传输8bit,即一字节. 支持多主控(multimastering),任何时间点只能有一 ...
- 硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器(转)
源:硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器 硬件上由于24c01的A0A1A2管脚不允许悬空,故暂时的想法是兼容24c02 ---24c16 使用一个dip8封装的芯片插座,A0 ...
- 很清晰的解读i2c协议【转】
转自:https://blog.csdn.net/weixin_41718085/article/details/79376823 转载:http://dpinglee.blog.163.com/bl ...
- 很清晰的解读i2c协议
很清晰的解读i2c协议 转载:http://dpinglee.blog.163.com/blog/static/14409775320112239374615/ 1.I2C协议 2条双向串行线,一条数 ...
- UART、SPI、I2C协议异同点
I2C.SPI.UART都是常见的低速板级通信协议,目前主流的SoC都内置了这些通讯协议的控制器,同样,各种传感器.Touch控制器.指纹模块.蓝牙模块.WIFI模块也都兼容这三种通信方式的一种或几种 ...
- GPIO实现I2C协议模拟(1)
最近需要用GPIO模拟I2C协议,如果是在Linux下面比较简单,但在Windows下面,是否有没Linux那么简单了. 索性自己对I2C协议还有一些了解,翻了SPEC并结合示波器量出的实际信号分析, ...
随机推荐
- 第八天 RHEL7.2 文件权限管理(第一部分)
一.文件的基本权限 文件有三种访问方式限制访问权限 第一种:文件所有者的访问权限 第二种:文件所有者同组的访问权限 第三种:其他人访问权限 当使用ls -l 或ll命令时,可查看此三种权限 在权限描述 ...
- VS2013 VC++的.cpp文件调用CUDA的.cu文件中的函数
CUDA 8.0在函数的调用中方便的让人感动.以下是从网上学到的VC++的.cpp文件调用CUDA的.cu文件中的函数方法,和一般的VC++函数调用的方法基本没差别. 使用的CUDA版本为CUDA 8 ...
- HDU5137-最短路-删点
How Many Maos Does the Guanxi Worth Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 512000/5 ...
- day19 Models补充+缓存+信号+序列化+分析抽屉页面
参考链接: http://www.cnblogs.com/wupeiqi/articles/5237704.html http://www.cnblogs.com/wupeiqi/articles/5 ...
- Python不同版本切换
2016年6月8日更新: 这是我早前写的一篇小文章,其实,后来也没有采用这种方法切换.电脑上安装了多个Python 版本,保证自己经常用的版本加入环境变量外,使用非系统的版本时一般使用 IDE 编辑器 ...
- urllib2.HTTPError: HTTP Error 403: Forbidden
这个问题主要是没有headers,加入一些内容就可以了 示例: # -*- coding: UTF-8 -*- import urllib2 site= "http://www.nseind ...
- p标签多行文字内容实现上下垂直居中兼容ie8
之前实现上下居中一般都是用height和line-height的来设置. 今天在修改样式的时候,p标签的文字内容可能是一行也可能是两行, 所以用height和line-height就没效果. 今天找到 ...
- [转载]java实现word转pdf
最近遇到一个项目需要把word 转成pdf,百度了一下网上的方案有很多,比如虚拟打印.给word 装扩展插件等,这些方案都依赖于ms word 程序,在java代码中也得使用诸如jacob或jcom这 ...
- New Concept English three(12)
25W/m 76% Most of us have formed an unrealistic picture of life on a desert island. We sometimes ima ...
- c# excel转换为DataTable
System.Data.DataTable GetDataFromExcelByCom(bool hasTitle, string fileName) { //OpenFileDialog openF ...