[嵌入式]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并结合示波器量出的实际信号分析, ...
随机推荐
- wget下载指定目录下的文件
wget -r -np -k -P ~/tmp/ http://xxx.com/download -P 表示下载到哪个目录-r 表示递归下载-np 表示不下载旁站连接.-k 表示将下载的网页里 ...
- js中的数据类型和判断数据类型
js中的数据类型和判断数据类型 基本数据类型,六大基本数据类型:字符串(String).数字(Number).布尔(Boolean).对象(Object).空(Null).未定义(Undefined) ...
- 027——VUE中事件修饰符:stop prevent self capture
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- SPOJ 5152 Brute-force Algorithm EXTREME && HDU 3221 Brute-force Algorithm 快速幂,快速求斐波那契数列,欧拉函数,同余 难度:1
5152. Brute-force Algorithm EXTREME Problem code: BFALG Please click here to download a PDF version ...
- *SCM-MANAGERtomcat寄宿使用
采用的部署方式 TomCat 一个端口下部署多个 Application供不同部门使用 初始部署详参见 SCM-MANAGER 博文 日常使用添加部门操作步骤 从“D:\tomcat\webapps” ...
- 利用国内镜像下载Android源码,并编译生成image镜像文件
为了编译安卓源码,首先需要一个Linux,本次采用Ubuntu Kylin14.04,内核版本3.13.装在四核.4G内存.1T硬盘的虚拟机上查看内核版本号:$uname -all清华镜像地址清华镜像 ...
- idea 配置springmvc+mybatis(图文教程)
idea配置 spirngmvc+maven+mybatis 数据库采用的是mysql 服务器容器用的是tomcat8 废话不多说直接干! 首先新建一个 maven工程, "File&qu ...
- New Concept English three(10)
The great ship, Titanic, sailed for New York from Southampton on April 10th, 1912. She was carrying ...
- 防火墙---iptables
一.iptables的说明及环境安装 (1)理论基础:当主机收到一个数据包后,数据包先在内核空间中处理,若发现目的地址是自身,则传到用户空间中交给对应的应用程序处理,若发现目的不是自身,则会将包丢弃或 ...
- vue.js 源代码学习笔记 ----- 工具方法 env
/* @flow */ /* globals MutationObserver */ import { noop } from 'shared/util' // can we use __proto_ ...