最近闲来无聊,入了一块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协议指东的更多相关文章

  1. i2c协议

    i2c协议 http://blog.csdn.net/g_salamander/article/details/8016698 总线设备驱动模型 http://blog.csdn.net/u01395 ...

  2. i2c协议简要分析(转载)

    声明 本文大部分内容为转载,因此标定为转载 源地址: http://www.cnblogs.com/zym0805/archive/2011/07/31/2122890.html http://blo ...

  3. (原创) 巩固理解I2C协议(MCU,经验)

        题外话:这几天天气突然转冷了.今天已是11月23日了,查查黄历,昨天(11月22日)刚好是小雪,一夜温度骤降,果然老祖先的经验有灵验!冬天来了,还是多加加衣服,注意保暖! 1.Abstract ...

  4. I2C协议(转)

    1.I2C协议   2条双向串行线,一条数据线SDA,一条时钟线SCL.   SDA传输数据是大端传输,每次传输8bit,即一字节.   支持多主控(multimastering),任何时间点只能有一 ...

  5. 硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器(转)

    源:硬件和软件兼容i2c协议的24Cxx系列EEPROM存储器 硬件上由于24c01的A0A1A2管脚不允许悬空,故暂时的想法是兼容24c02 ---24c16 使用一个dip8封装的芯片插座,A0 ...

  6. 很清晰的解读i2c协议【转】

    转自:https://blog.csdn.net/weixin_41718085/article/details/79376823 转载:http://dpinglee.blog.163.com/bl ...

  7. 很清晰的解读i2c协议

    很清晰的解读i2c协议 转载:http://dpinglee.blog.163.com/blog/static/14409775320112239374615/ 1.I2C协议 2条双向串行线,一条数 ...

  8. UART、SPI、I2C协议异同点

    I2C.SPI.UART都是常见的低速板级通信协议,目前主流的SoC都内置了这些通讯协议的控制器,同样,各种传感器.Touch控制器.指纹模块.蓝牙模块.WIFI模块也都兼容这三种通信方式的一种或几种 ...

  9. GPIO实现I2C协议模拟(1)

    最近需要用GPIO模拟I2C协议,如果是在Linux下面比较简单,但在Windows下面,是否有没Linux那么简单了. 索性自己对I2C协议还有一些了解,翻了SPEC并结合示波器量出的实际信号分析, ...

随机推荐

  1. 设计模式--享元模式C++实现

    1定义 使用共享对象可有效的支持大量细粒度的对象 2类图 角色分析 Flyweight抽象享元角色,一个产品的抽象,定义内部状态和外部状态的接口或者实现 ConcreteFlyweight具体享元角色 ...

  2. 1-25-循环控制符break、continue和函数详解

    大纲: 1-for循环补充 1-1-for循环实战---类C格式应用 2-break.continue循环控制符 2-1实战:帮助理解break.continue作用 3-函数详解 3-1.脚本文件中 ...

  3. Python之匿名函数(filter,map,reduce)

    参考博客:Python匿名函数详解--http://blog.csdn.net/csdnstudent/article/details/40112803 Python内建函数之——filter,map ...

  4. day35 爬虫简述

    爬虫概要 - pip3 install requests - pip3 install beautifulsoup4 基本爬虫: - Python实现浏览器行为,requests - beautifu ...

  5. 【Raspberry Pi】USB无线网卡自动连接

    Raspberry Pi 使用USB无线网卡的时候不会因为路由重启而掉线. #!/bin/bash while true ; do if ifconfig wlan0 | grep -q " ...

  6. tkinter模块常用参数

    tkinter模块常用参数   1.使用tkinter.Tk() 生成主窗口(root=tkinter.Tk()):root.title('标题名')    修改框体的名字,也可在创建时使用class ...

  7. 应用Fiddler对手机应用来抓包

    Fiddler是一款非常流行并且实用的http抓包工具,它的原理是在本机开启了一个http的代理服务器,然后它会转发所有的http请求和响应,因此,它比一般的firebug或者是chrome自带的抓包 ...

  8. 部署Jsp网站

    最近在实习,记录一下第一次部署网站的详情. 首先在服务器上安装jdk环境,我安装的是Java1.8版本. yum search java |grep -i JDK //查看Jdk版本 yum inst ...

  9. 网页重构中区分IE6、IE7、IE8及标准浏览器的最佳方法

    由于万恶的IE6和IE7,我们在页面重构时不免要对其进行各种bug修复及差异化处理.在标准浏览器中可实现的效果在IE里却有各种离奇问题,例如IE6.IE7不能良好应对的inline-block和.cl ...

  10. qt QTableWidget&&QTableView 导出数据到excel

    通常情况下,我们在开发过程中比较常用的方法是将表格的数据到处到excel文件.我也在这个点上头疼了很长时间,不过功夫不负苦心人,最终还是勉强达到效果,为了后面再次用到时不手忙脚乱现在将方法寄存在此,如 ...