[嵌入式]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并结合示波器量出的实际信号分析, ...
随机推荐
- layui弹窗宽度固定高度自适应界面
//默认上下15px的边距高度,可根据需要修改function layuiDialogFitScreen(){ var layui_title_height=$(window.parent.docum ...
- 51nod-1179-最大的gcd(数学)
1179 最大的最大公约数 题目来源: SGU 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 收藏 关注 给出N个正整数,找出N个数两两之间最大公约数的最大 ...
- mysql学习之旅
1,mysql安装脚本 #!/bin/bash RSYNC='10.39.2.120::wenhui/database/install' CMAKE='cmake-2.8.6.tar.gz' MYSQ ...
- 【问题解决记录】无法识别的标志“-sdlMode”,在“p2”中
本文为大便一箩筐的原创内容,转载请注明出处,谢谢:http://www.cnblogs.com/dbylk/ 这是本人第一次使用MarkDown编辑器,试试看效果-w-,下面是正文: 一.报错原因 昨 ...
- [Android]如何减小APK的大小
能不引用的外部包就不用,删除没用的图片.xml,优化代码去掉没用的部分,能异步下载的资源就运行时从网络上下载.
- LeetCode OJ:Ugly Number II(丑数II)
Write a program to find the n-th ugly number. Ugly numbers are positive numbers whose prime factors ...
- React 与 可视化
一般会想到 canvas 和 svg ; svg更适合画图, 但由于cavans在移动端的良好兼容性, 使用的更广; 什么是svg, scalable vector graphics 全称 可缩放矢 ...
- 【PL/SQL编程】循环语句
1. loop语句 loop plsql_sentence; exit when end_condition_exp; end loop; loop语句会先执行一次循环体,然后再判断“exit whe ...
- js函数的伪重载
这也是今天写东西是遇到的一个问题,导致我联想起了函数重载的问题. 在javascript中是没有函数重载机制的,对于用惯了java开发的同学可能就表示吃惊了,我屮艸芔茻,函数 没有重载?那怎么搞?!! ...
- GPU编程自学5 —— 线程协作
深度学习的兴起,使得多线程以及GPU编程逐渐成为算法工程师无法规避的问题.这里主要记录自己的GPU自学历程. 目录 <GPU编程自学1 -- 引言> <GPU编程自学2 -- CUD ...