您的位置:首页 > 其它

I2C通讯协议详解

2020-06-06 05:30 585 查看

I2C协议总结

  • 通讯信号的判断
  • 通讯过程
  • 部分代码讲解
  • 总结
  • 两个方面

    不知道大家是不是有我这种情况,学完STM32之后,感觉学了个寂寞。

    大佬说的话听都听不懂,所以复习一波深入了解一下原理

    今天要说的就是I2C通讯协议,刚学不知道这些协议是干嘛用的,了解了之后才发现,不得不会呀,就比如OLED mpu6050 EEPROM巴拉巴拉巴拉。。。总之就是很常用。好了废话说完了。
    我们从两个方面来介绍I2C协议
    1–>物理层
    2–>协议层

    物理层电气特性


    1: 它是一个支持多设备的总线,总线的意思就是多个设备共用的信号线,一个总线中可以挂载多个设备。

    2: 包括两条总线,即串行时钟线(SCL)C代表clock的意思就是时钟,以及串行数据线(SDA)D代表Data也就是数据的意思。顾名思义,SDA用来传输数据,而SCL用来保持收发同步。

    3: I2C总线上可以挂载多个设备,那么是怎么来确认发到哪个从机上呢?**其实每个设备在I2C总线上都有自己的地址,来确保不同设备之间访问的准确性。**地址可以是7位或者11位。

    4: 总线通过上拉电阻接到电源。这一点可能很多人不了解,我对照着上面的图详细说一下。在计算机逻辑里:
    高电平代表 1 (高阻态)
    低电平代表 0
    在设备空闲或者输出1时,我们输出的并不是高电平而是高阻态!

    高阻态,顾名思义嘛,电阻很大,那么就取无穷大喽。
    表示空闲状态时假设图中触摸屏空闲时输出零电压, SCL总线就会被拉低,此时如果传感器 工作,输出一个高电平,此时的触摸屏是0V,传感器是高电平,就会造成短路。如果我们给触摸屏一个高阻态,相当于断路。此时我们令传感器的电压为高电平,那么整个SCL总线就被拉高了。

    **表示高电平时:**假设其他设备空闲(高阻态),就可以忽略了,只有传感器工作,如果输出高阻态,又由于存在上拉电阻,所以整个SCL线也就是1。

    5: 具有三种传输模式:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多 I 2C 设备尚不支持高速模式。我们一般就使用标准模式就可以了。

    6: 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制 。
    PS: 纯属被迫营业,没学过电路

    协议层

    I2C 的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。通过总线的状态来表述不同的信号。

    I2C

    I2C基本读写过程

    先放一张图:

    #写过程

    先不用了解具体怎么产生的信号,先来抽象地理解一下,拿写来说:
    主机先说一句开始传输(S),传输到哪呢??很明显就是从机了(ADRESS),主机再次确认是读别人数据还是写数据呢(R/W),然后从机表示晓得了你要读/写(A),主机发送数据巴拉巴拉巴拉,发送完成之后,从机表示不接受数据了(非应答 那个符号不会打).然后主机停止写数据(P)。
    --------------------------------------- 原谅我的文笔,太感人了。----------------------------------------------

    S: 表示开始传输,先说一声开始。
    SLAVE_ADDRESS: 这个图上也有说,就是从机地址
    R/W: 0为写,1为读
    DATA: 发送的数据
    A: 应答信号
    P: 停止信号。结束了

    通讯复合格式


    这个过程其实和前面讲的写过程差不多,假设第一个R/W是写,那么主机先产生开始信号,找到从机地址,开始写,从机应答,此时后边的这个DATA表示的是要写的从机的地址,比如EEPROM,写到哪里呢,就是由这个DATA决定的,后边的都差不多就不啰嗦啦。

    通讯信号的判断

    通讯的起始和停止信号


    1 当SCL为高电平,SDA从高变为低,表示起始信号
    2 当SCL为高电平,SDA从低变高,表示停止信号

    数据有效性


    1: 当SCL为高电平时,如果SDA为高电平,表示数据1,如果SDA为低电平,表示数据0.
    2 当SCL为低,SDA进行电平转换,表示传输下一个数据。

    地址及数据方向


    前面我们说到地址为7位或者10位,一般都用七位。
    八位设备地址=7位从机地址+读/写地址,以EEPROM为例,他的七位设备地址为1111000十六进制转换之后就是0x78
    八位设备的读地址=1111 0001=0xF1
    八位设备的写地址=1111 0000=0xF0

    响应信号


    在传输时,主机产生时钟,在产生的第九个时钟时(8个时钟8个字节,就穿输了一个位(byte)了)
    数据发送端就会放弃对SDA的控制权,转为数据接收端控制SDA,此时若为低电平则为应答,高电平为非应答

    通讯过程



    这里就不多说了,了解完前面看这里就很轻松,上面的和读写过程基本一样。
    需要特别说的就是下面的EV5,EV6之类的了,其实下面的表示的是状态,比如产生一个起始信号,就会有对应的事件产生,如果此时我们检测状态寄存器(想了解的可以参加《STM32中文参考手册》),如果检测到事件发生,就判断产生了起始信号,算是一个保障吧。

    部分代码讲解

    #include "myiic.h"
    #include "delay.h"
    
    void IIC_Init(void)
    {
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7);
    }
    
    void IIC_Start(void)
    {
    SDA_OUT();     //sdaÏßÊä³ö
    IIC_SDA=1;
    IIC_SCL=1;
    delay_us(4);
    IIC_SDA=0;//START:when CLK is high,DATA change form high to low
    delay_us(4);
    IIC_SCL=0;
    }
    //²úÉúIICÍ£Ö¹ÐźÅ
    void IIC_Stop(void)
    {
    SDA_OUT();//sdaÏßÊä³ö
    IIC_SCL=0;
    IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
    delay_us(4);
    IIC_SCL=1;
    IIC_SDA=1;
    delay_us(4);
    }
    
    u8 IIC_Wait_Ack(void)
    {
    u8 ucErrTime=0;
    SDA_IN();
    IIC_SDA=1;delay_us(1);
    IIC_SCL=1;delay_us(1);
    while(READ_SDA)
    {
    ucErrTime++;
    if(ucErrTime>250)
    {
    IIC_Stop();
    return 1;
    }
    }
    IIC_SCL=0;//ʱÖÓÊä³ö0
    return 0;
    }
    
    void IIC_Ack(void)
    {
    IIC_SCL=0;
    SDA_OUT();
    IIC_SDA=0;
    delay_us(2);
    IIC_SCL=1;
    delay_us(2);
    IIC_SCL=0;
    }
    
    void IIC_NAck(void)
    {
    IIC_SCL=0;
    SDA_OUT();
    IIC_SDA=1;
    delay_us(2);
    IIC_SCL=1;
    delay_us(2);
    IIC_SCL=0;
    }

    IIC_Start(); 起始信号
    IIC_Stop();停止信号
    IIC_Wait_Ack(); 等待应答
    IIC_Ack()应答信号
    IIC_NAck 非应答信号
    这些根据前面的时序图理解就可以了;

    着重讲一下发送与读取字节的函数

    void IIC_Send_Byte(u8 txd)
    {
    u8 t;
    SDA_OUT();
    IIC_SCL=0;//À­µÍʱÖÓ¿ªÊ¼Êý¾Ý´«Êä
    for(t=0;t<8;t++)
    {
    IIC_SDA=(txd&0x80)>>7;
    txd<<=1;
    delay_us(2);   //¶ÔTEA5767ÕâÈý¸öÑÓʱ¶¼ÊDZØÐëµÄ
    IIC_SCL=1;
    delay_us(2);
    IIC_SCL=0;
    delay_us(2);
    }
    }
    //¶Á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;
    }

    先看发送数据 txd为要发送的数据,0x80就是1000 0000
    如果txd第一位为1,则 txd&0x80=1000 0000
    如果txd第一位为0 则 txd&0x80=0000 0000 这个与运算的作用就是提取txd最高位的数据;
    (txd&0x80)>>7 表示(txd&0x80)右移七位 ,假设数据是1000 0000右移后补零就是0000 00001,
    这样就得到了最高位(第八位)的数据;之后令数据左移一位,循环得到第七位的数据
    接受数据的同理。

    总结

    经过最近学习发现通讯真的很重要,前几天做了关于OLED 和mpu6050的实验,都是用的I2C通讯协议。ESP8266连接云服务器也要用的协议(MQTT)。一定要把通讯吃透哦,不然后期很难学的。

    在了解完这些之后代码大部分就可以看懂了,其中I2C的初始化之类的东西我就不说了哈,大家有兴趣可以采用I2C通讯协议与EEPROM进行通讯。

    如果学到的话,麻烦各位看官老爷点个赞再走吧,ball ball 你们了。

    顺便找一下玩过stm32+ESP8266+onenet的大佬

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: