I2C协议指东
来源:互联网 发布:grub2手动引导ubuntu 编辑:程序博客网 时间:2024/05/01 18:53
最近闲来无聊,入了一块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=1;IIC_SCL=1;delay_us(IIC_DELAY);IIC_SDA=0;//SCL高电平时SDA的下降沿delay_us(IIC_DELAY);}
2、结束信号
结束信号定义为:SCL高电平时,SDA的上升沿。
//结束信号void IIC_Stop(void){SDA_OUT();IIC_SDA=0;IIC_SCL=1;delay_us(IIC_DELAY);IIC_SDA=1;//SCL高电平时SDA的上升沿delay_us(IIC_DELAY);}
其中的SDA_OUT()是STM32的IO口模式设置,其他MCU可忽略或更改。IIC_DELAY是定义的宏,可以控制延迟时间从而控制IIC速率。
这里的写一个字节是说,控制了IIC总线的主机往总线上写数据。
void IIC_Send_Byte(u8 data){u8 i;SDA_OUT();//输出模式for(i=0;i<8;i++){IIC_SCL=0;//拉低时钟 占据总线delay_us(IIC_DELAY);IIC_SDA=(data&0x80)>>7;//每次1位,先高位data<<=1;delay_us(IIC_DELAY);IIC_SCL=1;delay_us(IIC_DELAY);}IIC_SCL=0;}这里默认是先MSB后LSB,IIC_SDA根据数据位依次置1或0,传输数据时,SCL必须拉低,以此告诉其他器件“传输进行中”,在传输结束后,还需要再次拉高SCL总线。在送完一个字节后,拉低SCL,等待应答。
4、IIC读一个字节
//IIC读一个BYTEu8 IIC_Read_Byte(void){u8 i,receive=0;SDA_IN();//输入模式READ_SDA=1;for(i=0;i<8;i++){receive<<=1;//先接收的是高位IIC_SCL=0;delay_us(IIC_DELAY);IIC_SCL=1;delay_us(IIC_DELAY);receive|=READ_SDA; }IIC_SCL=0;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=0;delay_us(IIC_DELAY);IIC_SDA=0;delay_us(IIC_DELAY);IIC_SCL=1;delay_us(IIC_DELAY);IIC_SCL=0;//SDA为低时 拉低时钟线delay_us(IIC_DELAY);}//不产生ACK应答void IIC_NAck(void){IIC_SCL=0;SDA_OUT();IIC_SDA=1;delay_us(IIC_DELAY);IIC_SCL=1;delay_us(IIC_DELAY);IIC_SCL=0;// SDA为高时 SCL的脉冲delay_us(IIC_DELAY);}
6、应答检测
经过我的检验,当STM32写MPU6050时,是不需要进行应答检测的;但是当STM32读MPU6050时,如果不进行应答检测,就会出现数据出错/检测不到MPU6050等奇怪的错误,所以在应用IIC总线协议时,一律增加应答检测是比较好的一种规范做法。
应答检测返回一个值,但是大多数情况中不需要用到这个返回值。
//应答信号确认//1有ACK//0无ACKu8 IIC_Wait_Ack(void){u8 ucErrTime=0;SDA_IN();// SDA输入模式IIC_SCL=0;delay_us(IIC_DELAY);IIC_SDA=1;delay_us(IIC_DELAY);IIC_SCL=1;delay_us(IIC_DELAY);while(READ_SDA){ucErrTime++;if(ucErrTime>250){IIC_Stop();return 0;}}IIC_SCL=0;//关闭时钟return 1;}
如果SDA一直是高电平没有被从设备【此处为MPU6050】拉低,则说明MPU没有应答,此时停止传输,并返回0.
如果接收到应答了,则把时钟线拉低,等待下一次开始信号。
7、MPU6050相关。
关于IIC的所有函数已经讲完了,下面贴一下MPU6050相关的操作。
//写MPU60X0u8 IIC_Write_One_Byte(u8 regaddr, u8 data){IIC_Start(); //起始信号 IIC_Send_Byte(SlaveAddress); //发送设备地址+写信号 if(IIC_Wait_Ack()==0) {IIC_Stop();return 0;}IIC_Send_Byte(regaddr); //内部寄存器地址 //IIC_Wait_Ack();IIC_Send_Byte(data); //内部寄存器数据 //IIC_Wait_Ack();IIC_Stop(); //发送停止信号return 1;}//读MPU60X0u8 IIC_Read_One_Byte(u8 regaddr){u8 REG_data=0;IIC_Start(); //起始信号 IIC_Send_Byte(SlaveAddress); //发送设备地址+写信号 if(IIC_Wait_Ack()==0) {IIC_Stop();return 0;}IIC_Send_Byte(regaddr); //发送存储单元地址,从0开始IIC_Wait_Ack();IIC_Start(); //起始信号IIC_Send_Byte(SlaveAddress+1); //发送设备地址+读信号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 协议
- I2C协议
- I2C协议
- I2C 协议
- I2C协议
- i2c协议
- I2C协议
- I2C协议
- I2C协议
- I2C 协议
- I2C 协议
- I2C协议
- I2C协议
- I2C协议
- I2C协议
- I2C 协议
- js 递归 返回
- sqlserver脚本时间计算
- Android:异步调用详解
- 怎样才能检测到链表中循环
- 装机工具软件收藏
- I2C协议指东
- android 上传图片
- UITableView分割线样式与颜色
- 【记录】cygwin下折腾个人配置环境
- 用C语言获取任意文件的长度(可能大于2GB)
- Flex中的Bindable的用法
- 为IEnumerable接口添加增删查等操作
- 以log(n)的时间求矩形内的点
- 关于HASH和MD5