学习笔记8-I2C协议


一、关于I2C

I2C通讯协议 (Inter Integrated Circuit) 是由 Phiilps 公司开发的一种简单、双向二线制同步串行总线,只需要两根线即可在连接于总线上的器件之间传送信息。由于它引脚少,硬件实现简单,可扩展性强,不需要 USART 、 CAN 等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路 ( IC)间的通讯。I2C是半双工!
在这里插入图片描述
上拉电阻可直接使用经典值4.7kΩ。使用I2C设备的灌电流不得超过3mA。逻辑0电压不得高于0.4V。

I2C相关术语的定义如下。在这里插入图片描述
STM32的 I2C 架构剖析如下图。
在这里插入图片描述

  1. 通讯引脚
  2. 时钟控制逻辑:在快速模式下可选择 SCL 时钟的占空比,可选 T low /T high =2 或
    T low /T high =16/9 模式(随意选即可)。I2C 外设输入时钟源为 PCLK1。
  3. 数据控制逻辑
  4. 整体控制逻辑

为了避免总线信号的混乱,要求各设备连接到总线的输出端时必须是漏极开路(OD)输出或集电极开路(OC)输出。(见下)

二、硬件结构

I2C总线器件内部的SDA、SCL引脚电路结构都是一样的,引脚的输出驱动与输入缓冲连在一起。
其中输出为漏极开路的场效应管、输入缓冲为一只高输入阻抗的同相器。
在这里插入图片描述
SCLKN1 OUT:串行时钟输出
SCLKN IN:串行时钟输入
DATAN1 OUT:串行数据输出
DATAN1 IN:串行数据输出

(1)由于 SDA、SCL 为漏极开路结构,借助于外部的上拉电阻实现了信号的“线与”逻辑;
(2)引脚在输出信号的同时还将引脚上的电平进行检测,检测是否与刚才输出一致。为 “时钟同步”和“总线仲裁”提供硬件基础。

三、I2C协议

3.1、I2C的三种状态

  • 空闲状态:SCL与SDA同时为高电平。
  • 工作开始状态:SDA由高电平到低电平,SCL由高电平到低电平。(开始信号)
  • 工作停止状态:SDA由低电平到高电平,SCL由高电平到低电平。(结束信号)
    在这里插入图片描述

3.2、数据有效性

  • SCL为高电平时,SDA表示的数据有效。
  • SCL为低电平时,SDA表示的数据无效,此时正在“DATA CHANGE”。

在这里插入图片描述

3.3、应答信号

含“应答(ACK)”与“非应答(NACK)”。
在这里插入图片描述

在第9个时钟时,数据发送端会释放 SDA 的控制权,由数据接收端控制 SDA ,若 SDA 为高电平,表示非应答信号(NACK),低电平表示应答信号(ACK)。

3.4、总线时序

在这里插入图片描述
每一帧数据由9bit组成,

  • 如果是发送数据,则包含8bit数据+1bit ACK
  • 如果是设备地址数据,则8bit包含7bit设备地址 1bit方向

3.5、读写时序

在这里插入图片描述

  • 主机发送开始信号给从机,唤醒所有主从机
  • 主机发送7位的地址位+1位的读写位给从机
  • 等待地址匹配从机的应答信号
  • 主机发送或接收数据到从机
  • 在传输完每个数据帧后,接收设备将另一个ACK位返回给发送方,以确认已成功接收到该帧
  • 结束之后主机发出停止信号

读写位

  • 0表示主设备向从设备(write)写数据
  • 1表示主设备向从设备(read)读数据

3.5.1、读

在这里插入图片描述

3.5.2、写

在这里插入图片描述

3.6、死锁

I2C总线写操作过程中,主机在产生启动信号后控制SCL产生8个时钟脉冲,然后拉低SCL信号为低电平,这时从机输出应答信号,将SDA信号拉为低电平。
如果这时主机异常复位,SCL就会被释放为高电平。此时,如果从机没有复位,就会继续I2C的应答,将SDA一直拉为低电平,直到SCL变为低电平,才会结束应答信号。对于主机来说,复位后检测SCL和SDA信号,如果发现SDA信号为低电平,则会认为I2C总线被占用,会一直等待SCL和SDA信号变为高电平。这样,主机等待从机释放SDA信号,而同时从机又在等待主机将SCL信号拉低以释放应答信号,两者相互等待,I2C总线进人一种死锁状态。同样,当I2C进行读操作时,从机应答后输出数据,如果在这个时刻主机异常复位而此时从机输出的数据位正好为0,也会导致I2C总线进入死锁状态。

解决方法:
(1)从机电源设计为可控,总线死锁时将从机复位。
(2)从机程序添加监测功能,总线长时间被拉低则释放对总线控制。
(3)主机中增加I2C总线恢复程序。每次主机复位后,如果检测到SDA被拉低,则控制SCL产生<=9个时钟脉冲(针对8位数据的情况),每发送一个时钟脉冲就检测SDA是否被释放,如果SDA已经被释放就再模拟产生一个停止信号,这样从机就可以完成被挂起的读写操作,从死锁状态中恢复过来。(有局限性——软件并不能够直接控制SCL信号模拟产生需要时钟脉冲)

3.7、仲裁

当SCL 线是电平时,仲裁在SDA 线发生;在其他主机发送低电平时,发送高电平的主机将断开它的数据输出级,因为总线上的电平与它自己的电平不相同。
在这里插入图片描述

四、软件I2C&硬件I2C

  • 软件IIC:指用单片机的两个I/O端口模拟出来的IIC,用软件控制管脚状态以模拟I2C通信波形,软件模拟寄存器的工作方式。
  • 硬件IIC:一块硬件电路,硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的,直接调用内部寄存器进行配置。

相对来说,硬件I2C效率更高,软件I2C接口更灵活。

五、AT24C02

基本描述:2k bit串行EEPROM存储器,内含256字节。里面有一个8字节页写缓冲器。

在这里插入图片描述
从器件地址如下图。(前四位固定为1010)
在这里插入图片描述

  1. 芯片寻址:
    A2~A0为由管脚电平(为000),最后一位表示读写操作。所以AT24Cxx的读地址为0xA1,写地址为0xA0。
    也即
    写24C02的时候,从器件地址为10100000(0xA0);
    读24C02的时候,从器件地址为10100001(0xA1)。
  2. 片内地址寻址:
    芯片寻址可对内部256B中的任一个进行读/写操作,其寻址范围为00~FF,共256个寻址单位。
    对应修改A2A1A0即可。

六、代码编写

6.1、I2C初始化

void IIC_init()//IIC初始化
{
       SCL=1; //首先把时钟线拉高
       delay_us(4);//延时函数
       SDA=1; //在SCL为高的情况下把SDA拉高
       delay_us(4); //延时函数
}

6.2、开始信号

//产生IIC起始信号
//首先拉高SDA,再拉高SCL,空闲状态
//最后拉低SDA
void IIC_Start()//启动信号
{
       SDA=1; //确保SDA线为高电平
       delay_us(5);
       SCL=1;  //确保SCL高电平
       delay_us(5);
       //以下这一步产生了开始信号的脉冲
       SDA=0; //在SCL为高时拉低SDA线,即为起始信号
       delay_us(5);
}

6.3、ACK/NACK信号

//CPU产生ACK信号
void I2C_Ack(void)
{
    I2C_SDA_Low();
    I2C_Delay();
    I2C_SCL_High();
    I2C_Delay();
    I2C_SCL_Low();
    I2C_Delay();
    I2C_SDA_High();
}
//CPU产生NACK信号
void I2C_NoAck(void)
{
    I2C_SDA_High();
    I2C_Delay();
    I2C_SCL_High();
    I2C_Delay();
    I2C_SCL_Low();
    I2C_Delay();
}
//CPU产生一个时钟,并读取器件的ACK应答信号
uint8_t I2C_WaitToAck(void)
{
    uint8_t redata;
    I2C_SDA_High();
    I2C_Delay();
    I2C_SCL_High();
    I2C_Delay();
    if(I2C_SDA_READ())
    {
        redata = 1;
    }
    else
    {
        redata = 0;
    }
    I2C_SCL_Low();
    I2C_Delay();
    return redata;

}   

6.4、发送1byte数据

//IIC发送一个字节
//返回: 从机有无应答
//0,无应答   
//1,有应答         

//只有当SCL被拉低后,SDA才能被改变
//总结:在SCL为低电平期间,发送数据,发送8次数据,数据为1,SDA被拉高,数据为0,SDA被拉低。
//传输期间保持传输稳定,所以数据线仅可以在时钟SCL为低电平时改变。
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
    SDA_OUT();         
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        //IIC_SDA=(txd&0x80)>>7;   //获取最高位
        //获取数据的最高位,然后左移7位
        //如果某位为1,则SDA为1,否则相反
        if((txd&0x80)>>7)
            IIC_SDA=1;
        else
            IIC_SDA=0;
        txd<<=1;       
        delay_us(2);   
        IIC_SCL=1;
        delay_us(2); 
        IIC_SCL=0;    
        delay_us(2);
    }     
}       

6.5、读取1字节

//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();        //SDA设置为输入
    for(i=0;i<8;i++ )
	{
        IIC_SCL=0; 
        delay_us(2);
		IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)
        	receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();        //发送NACK,表示不再接收数据
    else
        IIC_Ack();         //发送ACK   
    return receive;
}

6.5、停止信号

//产生IIC停止信号
//1.先拉低SDA,再拉低SCL
//2.拉高SCL
//3.拉高SDA
//4.停止接收数据
void IIC_Stop(void)
{

	IIC_SCL=0;
	IIC_SDA=0;    //STOP:当SCL高时,数据由低变高
 	delay_us(4);
	IIC_SCL=1; 
	IIC_SDA=1;    //发送I2C总线结束信号
	delay_us(4);							   	
}

参考自:123


  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ChristianLuu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值