目录
简介
I2C 物理层
一个 I2C 总线两条线组成,一个双向串行数据线SDA用来表示数据,一个串行时钟线SCL用于数据收发同步
“总线”指多个设备共用的信号线,在一个 I2C 通讯总线中,可连接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机
多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线
每个连接到总线的设备(主、从机)都有一个独立的地址,主机可通过该地址来访问不同设备
总线通过上拉电阻接到电源,当挂在I2C中某设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平
具有三种传输模式:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s ,高速模式下可达3.4Mbit/s,但目前大多 I2C 设备尚不支持高速模式,一般用快速模式
因为I²C协议比较简单,常常用GPIO来模拟I²C时序,这种方法称为模拟I²C。如果使用MCU的I²C控制器,设置好I²C控制器, I²C控制器就自动实现协议时序,这种方式称为硬件I²C。因为I²C设备的速率比较低,通常两种方式都可以,模拟I²C方便移植,硬件I²C工作效率相对较高
协议层
协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环
节,这里主要讲起始、停止信号、发送、接收、应答、数据有效性、地址及数据
①②:起始信号和结束信号
起始 (S) 和停止 (P) 信号是两种特殊的状态,表示开始通信和停止通信。当 SCL 线是高电平时 SDA 线从高电平向低电平切换,这个情况表示通讯的起始;当 SCL 是高电平时 SDA 线由低电平向高电平切换,表示通讯的停止。
/*起始信号: 初始SCL\SDA 为高,然后SDA\SCL由高拉低开始,H高L低*/
void i2c_start(void)
{
SDA_H();
delay();
SCL_H();
delay();
/*必须先拉低SDA再拉低SCL*/
SDA_L();
delay();
SCL_L();
delay();
}
/*停止信号:初始 SCL 为高,然后SDA由低拉高结束*/
void i2c_stop(void)
{
SDA_L();
delay();
SCL_H();
delay();
SDA_H();
delay();
}
③ 应答和非应答信号
I²C每次传输的8位数据后需要从机反馈一个应答位,以确认从机是否正常接收了数据。应答信号为低电平时,规定为有效应答位(ACK 简称应答位);应答信号为高电平时,规定为非应答位(NACK)。当设备 (无论主从机) 接收到 I2C 传输的一个字节数据或地址后,若希望对方继
续发送数据,则需要向对方发送“应答 (ACK)”信号,发送方会继续发送下一个数据;若接收端
希望结束数据传输,则向对方发送“非应答 (NACK)”信号,发送方接收到该信号后会产生一个
停止信号,结束信号传输。
在第 9 个时钟时,数据发送端会释放 SDA 的控制权,由数据接收端控制SDA,若 SDA 为高电平,表示非应答信号 (NACK),低电平表示应答信号 (ACK)
/*I2C发出应答/非应答信号*/
void i2c_ack(uint8_t AckBit)
{
if(1 == AckBit)
SDA_H();/*表示不应答*/
else
SDA_L();/*表示应答*/
delay();
SCL_H();/*读取应答*/
delay();
SCL_L();
delay();
}
④数据有效性
SDA为高电平表示“ 1”,低电平表示“0”;SCL为高电平时表示有效数据(读取SDA数据),为低电平时表示无效数据,等SDA会进行电平切换(可能是1可能是0),为下次数据表示做准备。
⑤数据传输
在 SCL 串行时钟的配合下,在SDA 上逐位地串行传送每一位数据
I2C 总线上的每个设备都有自己的独立地址,主机发起通讯时,通过 SDA 信号线发送设备地址来查找从机,设备地址一般是7 位的地址应用比较广泛。地址之后紧跟的一个数据位用来表示数据传输方向,数据方向位为“1”时表示主机由从机读数据,该位为“0”时表示主机向从机写数据;发送完之后从机就会等待主机的应答信号。
/*发送一个字节,data: 要发送的数据*/
void send_byte( unsigned char data)
{
unsigned char i;
for(i=0;i<8;i++)
{
if(data & (0x80 >> i))/*每次移位获取对应的数据位判断SDA是发0还是发1*/
{
SDA_H();/*拉高,发1*/
}
else{
SDA_L();/*拉低,发0*/
}
SCL_H();/*读取数据*/
delay();
SCL_L();
delay();
}
}
/*I2C读出一个字节*/
unsigned char iic_read_byte(void)
{
unsigned char i,data = 0x00;
SDA_H();
delay();
for(i=0;i<8;i++)
{
SCL_H();/*拉高开始读取数据*/
delay();
/*使用库函数去读取SDA的GPIO引脚电平,通过返回值进入循环*/
if(/*读取SDA的电平 == 1*/)
{
data |= (0x80 >> i);/*通过读取电平进行置1*/
}/*如果是0就不用进入if语句,因为data初始位0000 0000,通过上面对对应的位置1即可*/
SCL_L();/*等待读取数据*/
dalay();
}
return data;
}
/*I2C等待从机的应答信号,应答信号SDA为低*/
uint8_t i2c_wait_ack(void)
{
unsigned char wait_time=0;
/*发送完数据等待应答信号的状态*/
SDA_H();/*释放SDA,让发送应答信号方控制*/
delay();
SCL_H();/*开始读出SDA*/
delay();
while(/*使用库函数去读取SDA的GPIO引脚电平,通过返回值进入循环*/)/*等待应答*/
{
/*如果读取引脚电平为高,即1,表示不应答*/
wait_time++;/*等待时间*/
if(wait_time > 250)
{
/*认为从机没有发送应答信号*/
SCL_L();
return 1;/*表示接收失败*/
}
}
/*没进入循环,表示SDA拉低了,表示应答*/
SCL_L();
return 0;
}
⑥空闲状态
IIC 总线的 SDA 和 SCL 两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。