I2C+时钟树

I2C模块

参考博客链接
https://blog.csdn.net/weixin_42881419/article/details/104288391

相关介绍

I2C Bus——Inter-Integrated Circuit 即集成电路总线。
I2C总线端口为漏开结构,因此一般需要加上拉电阻Rp,通常选用5K~10KΩ
I2C总线以字节为单位收发数据,一个字节为一帧,数据传输的次序为从最高位到最低位
I2C由**数据线(SDA)**与 时钟线(SCL) 组成
SDA:Synchronous Data Adapter (同步数据衔接器)
SCL:Serial Communication Loop(串行通讯环路)

原理图

【主从结构:一主[Master]多从[Slave]】
在这里插入图片描述

数据格式

在这里插入图片描述
主机写数据:
在这里插入图片描述

主机读数据:
在这里插入图片描述
1.4信号传输时序图
在这里插入图片描述
SCL上拉电阻的作用:在第9个时钟之后,双方中若有一方处理还未完成,它可以一直把SCL拉低,当SCL一直为低电平时,双方都不应该操作SDA总线

读写流程

1.前8个clk(7bit地址 + 1bit读/写)
从设备不影响总线,从设备不去动三极管
主设备决定数据
2.第9个CLK,由从设备决定数据
主设备不驱动三极管
从设备决定数据
SDA高:无ACK
SDA低:有ACK

信号解析

起始信号[Start]:在SCL保持高电平时,SDA下降沿。
读写位:【0写1读】
数据位:SCL低电平时,SDA可变化;SCL高电平,SDA不变
终止信号:在SCL高电平时,SDA上升沿。
回应信号[ACK]:SDA低电平,低电平

在这里插入图片描述

如何在SDA上实现双线传输

1.主设备发送时,从设备不发送:可以通过SCL区分控制
2.主设备发送时,从设备的”发送“引脚不影响数据:使用开极电路。

开极电路

在这里插入图片描述
A B SDA
0 0 1(由上拉电阻决定)
0 1 0
1 0 0
1 1 1
SDA上拉电阻的作用:当A、B都低电平时,三极管都不导通时,SDA取决于外界电路,当什么都不接时,SDA悬空,无法获取电平,故接上拉电阻。

开极电路驱动思想

当某个设备不想影响SDA总线时,就不驱动三极管。
想输出高电平,都不驱动三极管。
想输出低电平,就驱动三极管。

主从关系

在这里插入图片描述

IIC控制器流程框图

在这里插入图片描述

IIC读写操作步骤

在这里插入图片描述
在发送模式下,当数据已经发送出去后,IIC会等待IICDS寄存器接收新的数据

1.IICDS = val;
2.发完数据,产生中断,拉低SCL
3.中断程序里,判断状态,IICDS = val2,IIC继续工作
在接收模式下,IIC接口会等待直到你把IICDS中的数据读走,在此期间,SCL拉低

1.发起传输,接收DATA
2.接收到数据之后,产生中断,SCL拉低
3.中断程序中,判断,设置中断,val = IICDS,IIC继续工作,继续接收收据

I2C协议标准代码

起始信号&停止信号

起始信号:当 SCL 线是高电平时 SDA 线从高电平向低电平切换。
  停止信号:当 SCL 线是高电平时 SDA 线由低电平向高电平切换。

起始信号代码


void I2C_Start(void)
{
    I2C_SDA_High();     //SDA=1
    I2C_SCL_High();     //SCL=1
    I2C_Delay();
    I2C_SDA_Low();
    I2C_Delay();
    I2C_SCL_Low();
    I2C_Delay();
}

停止信号代码

void I2C_Stop(void)
{
I2C_SDA_Low();
I2C_SCL_High();
I2C_Delay();
I2C_SDA_High();
I2C_Delay();
}
下面展示一些 内联代码片

void I2C_Stop(void)
{
    I2C_SDA_Low();
    I2C_SCL_High();
    I2C_Delay();
    I2C_SDA_High();
    I2C_Delay();
}

发送一个字节

CPU向I2C总线设备发送一个字节(8bit)数据
 u8 I2C_SendByte(uint8_t Byte)
{
uint8_t i;

/* 先发送高位字节 */
for(i = 0 ; i < 8 ; i++)
{
    if(Byte & 0x80)
    {
        I2C_SDA_High();
    }
    else
    {
        I2C_SDA_Low();
    }
    I2C_Delay();
    I2C_SCL_High();
    I2C_Delay();
    I2C_SCL_Low();
    I2C_Delay();

    if(i == 7)
    {
        I2C_SDA_High();                     /* 释放SDA总线 */
    }
    Byte <<= 1;                             /* 左移一位  */

    I2C_Delay();
}

}

读取一个字节

CPU从I2C总线设备上读取一个字节(8bit数据)
u8 I2C_ReadByte(void)
{
uint8_t i;
uint8_t value;

/* 先读取最高位即bit7 */
value = 0;
for(i = 0 ; i < 8 ; i++)
{
    value <<= 1;
    I2C_SCL_High();
    I2C_Delay();
    if(I2C_SDA_READ())
    {
        value++;
    }
    I2C_SCL_Low();
    I2C_Delay();
}

return value;

}

应答

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产生一个非ACK信号

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;
}   

I2C通信时序图解析

写数据

结合I2C总线协议的知识,我们可以知道I2C写数据由一下10个步骤组成。
  第一步,发送一个起始信号。
  第二步,发送7bit从机地址,即OZ9350的地址。此处需要注意,发送数据时,无法发送7bit数据,此处发送了7bit地址+1bit读写选择位,即发送7bit+r/w。最低位为1表示读,为0表示写。
  第三步,产生一个ACK应答信号,此应答信号为从机器件产生的应答。
  第四步,发送寄存器地址,8bit数据。
  第五步,产生一个ACK应答信号,此应答信号为从机器件产生的应答。
  第六步,发送一个数据,8bit数据。
  第七步,产生一个ACK应答信号,此应答信号为从机器件产生的应答信号。
  第八步,发送一个CRC校验码,此CRC校验值为2、4、6步数据产生的校验码。
  第九步,既可以发送一个应答信号,也可以发送一个无应答信号,均有从机器件产生。
  第十步,发送一个停止信号。
  接下来,按照以上是个步骤,可以写出OZ9350的i2c写数据的函数。代码如下:

u8 I2C_WriteBytes(void)
{
    I2C_Start();                    //1
 
    I2C_SendByte(Slaver_Addr | 0);  //2
    I2C_WaitToAck();                //3
 
    I2C_SendByte(Reg_Addr);         //4
    I2C_WaitToAck();                //5
 
    I2C_SendByte(data);             //6
    I2C_WaitToAck();                //7
 
    I2C_SendByte(crc);              //8
    I2C_WaitToAck();                //9
 
    I2C_Stop();                     //10
}       

读数据

通过分解后的时序图,可以看到OZ9350的读数据由以下13个步骤组成。
  第一步,发送一个起始信号。
  第二步,发送7bit从机地址,即OZ9350的地址。此处需要注意,发送数据时,无法发送7bit数据,此处发送了7bit地址+1bit读写选择位,即发送7bit+r/w。最低位为1表示读,为0表示写。
  第三步,产生一个ACK应答信号,此应答信号为从机器件产生的应答。
  第四步,发送寄存器地址。
  第五步,产生一个ACK应答信号,此应答信号为从机器件产生的应答。
  第六步,再次发送一个起始信号。
  第七步,发送7bit从机地址,即OZ9350的地址。此处需要注意,发送数据时,无法发送7bit数据,此处发送了7bit地址+1bit读写选择位,即发送7bit+r/w。最低位为1表示读,为0表示写。
  第八步,产生一个ACK应答信号,此应答信号为从机器件产生的应答。
  第九步,读取一个字节(8bit)的数据。
  第十步,产生一个ACK应答信号,此应答信号为CPU产生。
  第十一步,读取一个CRC校验码。
  第十二步,产生一个NACK信号。此无应答信号由CPU产生。
  第十三步,产生一个停止信号。
  下面展示一些 内联代码片

u8 I2C_ReadBytes(void)
{
    u8 data;
    u8 crc;
 
    I2C_Start();                    //1
 
    I2C_SendByte(Slaver_Addr | 0);  //2
    I2C_WaitToAck();                //3
 
    I2C_SendByte(Reg_Addr);         //4
    I2C_WaitToAck();                //5
 
    I2C_Start();                   //6
 
    I2C_SendByte(Slaver_Addr | 1);  //7 1-读
    I2C_WaitToAck();                //8
 
    data=I2C_ReadByte();            //9
 
    I2C_Ack();                      //10
 
    crc=I2C_ReadByte();             //11
 
    I2C_NoAck();                    //12
 
    I2C_Stop();                     //13
}  
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值