IIC总线通信协议

一、概述

IIC(Inter-Integrated Circuit)总线是由飞利浦(PHILIPS)公司开发的两线式串行总线,多用于连接微处理器及其外围芯片。I2C总线的主要特点是接口方式简单,两条线可以挂多个参与通信的器件,即多机模式,而且任何一个器件都可以作为主机,当然同一时刻只能有一个主机。
1、从原理上来讲,IIC属于同步通信,SCL时钟线负责收发双方的时钟节拍,SDA数据线负责传输数据。IIC的发送方和接收方都以SCL这个时钟节拍为基准进行数据的发送和接收。
2、从应用上来讲,I2C多用于板内通信,比如单片机和EEPROM等外围芯片之间的通信。

二、IIC时序

在硬件上,IIC总线是由时钟总线SCL和数据总线SDA两条线构成,连接到总线上的所有器件的SCL都连到一起,所有SDA都连到一起。IIC总线是开漏引脚并联的结构,因此我们外部要添加上拉电阻。对于开漏电路外部加上拉电阻,就组成了线“与”的关系。总线上线“与”的关系就是说,所有接入的器件保持高电平,这条线才是高电平,而任何一个器件输出一个低电平,那这条线就会保持低电平,因此可以做到任何一个器件都可以拉低电平,也就是任何一个器件都可以作为主机,如图1所示,在电路中添加了R63和R64两个上拉电阻,常用的上下拉电阻值大多选取在1K到10K之间,具体到底多大通常要根据实际需求来选,通常情况下在标准范围内就可以了,不是一个固定的值。在实际的应用中,对于5V系统的单片机,通常选择4.7KΩ的电阻作为上拉电阻;对于3.3V系统的单片机,通常选择10KΩ的电阻作为上拉电阻。

IIC总线的上拉电阻电路

图1 IIC总线的上拉电阻电路
虽然说任何一个设备都可以作为主机,但绝大多数情况下我们都是选择单片机来做主机,而总线上挂的多个IIC元件。每一个元件都像电话机一样有自己唯一的地址,在信息传输的过程中,通过这唯一的地址就可以正常识别到属于自己的信息。
IIC分为起始信号、数据传输部分、停止信号。其中,数据传输部分,可以一次通信过程传输很多个字节,字节数是不受限制的,而每个字节的数据最后也跟了一位,这一位叫做应答位,通常用ACK表示。 IIC每次通信时,不管是发送还是接收,SCL和SDA两条线都必须都参与工作才能完成。下面结合图2的时序流程图来介绍IIC串口数据通信的过程。

IIC时序流程图

图2 IIC时序流程图

1、起始信号:IIC通信的起始信号的定义是SCL为高电平期间,SDA由高电平向低电平变化产生一个下降沿,表示起始信号,如图2中的Start部分所示。
2、数据传输:(1)IIC通信是高位在前,低位在后。 (2)IIC没有固定波特率,但是有时序的要求,要求当SCL在低电平的时候,SDA允许变化,也就是说,发送方必须先保持SCL是低电平,才可以改变数据线SDA,输出要发送的当前数据的一位;而当SCL在高电平的时候,SDA绝对不可以变化,因为这个时候,接收方要来读取当前SDA的电平信号是0还是1,因此要保证SDA的稳定,如图2中的每一位数据的变化,都是在SCL的低电平位置,8位数据位后边跟着的是一位应答位。
3、停止信号:IIC通信停止信号的定义是SCL为高电平期间,SDA由低电平向高电平变化产生一个上升沿,表示结束信号,如图2中的Stop部分所示。

三、IIC寻址模式

1、IIC通信在字节级的传输中,也有固定的时序要求。I2C通信的起始信号(Start)后,首先要发送一个从机的地址,这个地址一共有7位,紧跟着的第8位是数据方向位(R/W):
0”表示接下来要发送数据(),
1”表示接下来是接收数据()。
2、我们知道,打电话的时候,当拨通电话,接听方捡起电话肯定要回一个“喂”,这就是告诉拨电话的人,这边有人了。同理,这个第九位ACK实际上起到的就是这样一个作用。当我们发送完了这7位地址和1位方向后,
如果发送的这个地址确实存在,那么对应这个地址的元件应该回应ACK(拉低SDA即输出“0”),
如果发送的这个地址不存在,那么对应这个地址的元件就不会回应ACK(SDA将保持高电平即“1”)。

四、IIC通信速度

IIC通信分为低速模式100kbit/s、快速模式400kbit/s和高速模式3.4Mbit/s。因为所有的I2C器件都支持低速,但却未必支持另外两种速度,所以作为通用的IIC程序,选择100kbit/s这个速率来实现,也就是说实际程序产生的时序必须小于等于100kbit/s的时序参数,所以在程序中通过插入总线延时函数,来达到这个速度限制。如果以后需要提高速度,那么只需要减小这里的总线延时时间即可。

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是基于Keil开发环境的IIC总线连接6050和OLED的示例程序: ```c #include <reg51.h> #include <intrins.h> #define SCL P1_5 #define SDA P1_4 sbit OLED_SCL = P2^0; // OLED SCL引脚 sbit OLED_SDA = P2^1; // OLED SDA引脚 void delay(unsigned int n) // 延时函数 { unsigned int i,j; for(i=0;i<n;i++) for(j=0;j<110;j++); } void IIC_Start() // IIC起始信号 { SDA = 1; SCL = 1; _nop_(); _nop_(); _nop_(); SDA = 0; _nop_(); _nop_(); _nop_(); SCL = 0; } void IIC_Stop() // IIC停止信号 { SDA = 0; SCL = 1; _nop_(); _nop_(); _nop_(); SDA = 1; } void IIC_WriteByte(unsigned char dat) // 向IIC总线写入一个字节 { unsigned char i; for(i=0;i<8;i++) { if(dat & 0x80) SDA = 1; else SDA = 0; dat <<= 1; SCL = 1; _nop_(); _nop_(); _nop_(); SCL = 0; } SDA = 1; SCL = 1; _nop_(); _nop_(); _nop_(); SCL = 0; } unsigned char IIC_ReadByte() // 从IIC总线读取一个字节 { unsigned char i,dat = 0; for(i=0;i<8;i++) { SDA = 1; SCL = 1; _nop_(); _nop_(); _nop_(); dat <<= 1; if(SDA) dat |= 0x01; SCL = 0; } SDA = 1; SCL = 1; _nop_(); _nop_(); _nop_(); SCL = 0; return dat; } void OLED_WriteCmd(unsigned char cmd) // OLED写命令 { IIC_Start(); IIC_WriteByte(0x78); // OLED地址+写命令 IIC_WriteByte(0x00); // 写命令 IIC_WriteByte(cmd); IIC_Stop(); } void OLED_WriteData(unsigned char dat) // OLED写数据 { IIC_Start(); IIC_WriteByte(0x78); // OLED地址+写命令 IIC_WriteByte(0x40); // 写数据 IIC_WriteByte(dat); IIC_Stop(); } void OLED_Init() // OLED初始化 { OLED_WriteCmd(0xAE); // 关闭显示 OLED_WriteCmd(0x20); // 设置内存地址模式 OLED_WriteCmd(0x10); // 垂直寻址模式 OLED_WriteCmd(0xb0); // 设置页地址 OLED_WriteCmd(0xc8); // 设置列地址 OLED_WriteCmd(0x00); // 设置起始列地址 OLED_WriteCmd(0x10); // 设置高位列地址 OLED_WriteCmd(0x40); // 设置起始行地址 OLED_WriteCmd(0x81); // 对比度设置 OLED_WriteCmd(0xff); // 对比度值 OLED_WriteCmd(0xa1); // 设置段重映射 OLED_WriteCmd(0xa6); // 正常显示 OLED_WriteCmd(0xa8); // 多路复用设置 OLED_WriteCmd(0x3f); // 多路复用值 OLED_WriteCmd(0xa4); // 全局显示开启 OLED_WriteCmd(0xd3); // 显示偏移 OLED_WriteCmd(0x00); // 显示偏移值 OLED_WriteCmd(0xd5); // 显示时钟分频 OLED_WriteCmd(0xf0); // 显示时钟分频值 OLED_WriteCmd(0xd9); // 设置预充电周期 OLED_WriteCmd(0x22); // 预充电周期值 OLED_WriteCmd(0xda); // 设置COM硬件配置 OLED_WriteCmd(0x12); // COM硬件配置值 OLED_WriteCmd(0xdb); // 设置VCOMH OLED_WriteCmd(0x20); // VCOMH值 OLED_WriteCmd(0x8d); // 设置电荷泵 OLED_WriteCmd(0x14); // 电荷泵值 OLED_WriteCmd(0xaf); // 开启显示 } void MPU6050_Init() // MPU6050初始化 { IIC_Start(); IIC_WriteByte(0xD0); // MPU6050地址+写命令 IIC_WriteByte(0x6B); // PWR_MGMT_1寄存器地址 IIC_WriteByte(0x00); // 复位MPU6050 IIC_Stop(); } void main() { unsigned char i; OLED_Init(); // OLED初始化 MPU6050_Init(); // MPU6050初始化 while(1) { OLED_WriteCmd(0xb0); // 页地址0~7循环显示 for(i=0;i<8;i++) { OLED_WriteCmd(0xb0+i); // 设置页地址 OLED_WriteCmd(0x00); // 设置列地址低位 OLED_WriteCmd(0x10); // 设置列地址高位 OLED_WriteData('M'); // 在OLED上显示'M' OLED_WriteData('P'); // 在OLED上显示'P' OLED_WriteData('U'); // 在OLED上显示'U' OLED_WriteData('6'); // 在OLED上显示'6' OLED_WriteData('0'); // 在OLED上显示'0' OLED_WriteData('5'); // 在OLED上显示'5' OLED_WriteData('0'); // 在OLED上显示'0' OLED_WriteData(' '); // 在OLED上显示' ' } delay(1000); // 延时1秒 } } ``` 在该示例程序中,使用了两个函数分别用于向IIC总线写入一个字节和从IIC总线读取一个字节。同时,还使用了OLED_WriteCmd()和OLED_WriteData()函数分别用于向OLED写入命令和数据。在主函数中,先进行OLED和MPU6050的初始化,然后循环显示MPU6050的地址,最后延时1秒。其中,MPU6050的地址为0x68,可以通过更改IIC_WriteByte()和IIC_ReadByte()函数中的地址实现连接其他IIC设备。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值