资讯专栏INFORMATION COLUMN

IIC协议详解,附单片机软件模拟源码

CrazyCodes / 2347人阅读

摘要:协议物理层原理总体特征协议层起始和停止条件数据有效性响应应答寻址读数据写数据单片机通讯软件模拟硬件外设一物理层原理总线,分别由串行数据线和串行时钟线及上拉电阻组成。第八步,主机发送一个校验码,此校验值为步数据产生的校验码。

I2C协议


(一)物理层

1. 原理

I2C 总线,分别由SDA(串行数据线)SCL(串行时钟线)上拉电阻组成。

通信原理是通过对SCL和SDA线高低电平时序的控制,来 产生I2C总线协议所需要的信号进行数据的传递。在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平

2. 总体特征

​ 连接到总线的器件输出级必须是漏极开路或集电极开路才能执行线与的功能。

​ I2C 总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址(可以从I2C器件的数据手册得知),主从设备之间就通过这 个地址来确定与哪个器件进行通信。

​ I2C 总线上数据的传输速率在标准模式下可达 100kbit/s ,在快速模式下可达 400kbit/s ,在高速模式下可达 3.4Mbit/s ,连接到总线的接口数量只由总线电容是 400pF 的限制决定。

​ I2C 总线上的主设备与从设备之间以字节(8位)为单位进行双向的数据传输。


(二)协议层

1. 起始和停止条件

I2C协议规定,总线上数据的传输必须以一个起始信号作为开始条件,以一个结束信号作为传输的停止条件。

起始和结束信号总是由主设备产生

  • 总线在空闲状态 时,SCL和SDA都保持着高电平,
  • 当SCL为高而SDA由高到低的跳变,表示产生一个起始条件
  • 当SCL为高而SDA由低到高的跳变,表示产生一个 停止条件
  • 在起始条件产生后,总线处于忙状态,由本次数据传输的主从设备独占,其他I2C器件无法访问总线;
  • 而在停止条件产生后,本次数据传输的主从设备将释放总线,总线再次处于空闲状态。

2. 数据有效性

SDA线上的数据必须在SCL的高电平时保持稳定。

SDA线的高或低电平状态只有在 SCL 线的时钟信号是低电平时才能改变。

发送到 SDA 线上的每个字节必须为 8 位,每次传输可以发送的字节数量不受限制 ,每个字节后必须跟一个响应位 ,首先传输的是数据的最高位(见下图MSB )

如果从机要完成一些其他功能后(如中断)才能接收或发送下一个完整的数据字节 ,可以使时钟线 SCL 保持低电平迫使主机进入等待状态 ,当从机准备好接收下一个数据字节时释放时钟线 SCL, 数据传输继续。

3. 响应

数据传输必须带响应,相关的响应SCL时钟脉冲由主机产生,在响应的时钟脉冲期间,发送器释放 SDA 线(输出高阻态使SDA线被上拉电阻拉高)。在响应的时钟脉冲期间,接收器必须将 SDA 线拉低,使它在这个时钟脉冲的高电平期间保持稳定的低电平。 必须考虑建立和保持时间。

4. 寻址

7位地址格式

起始条件 S 后 ,发送了一个从机地址SLAVE ADDRESS, 这个地址共有 7 位,紧接着的第 8 位是数据方向位[R/W], 0 表示写,1表示读。接下来的一个bit是应答位NACK/ACK,当这个帧中前面8bits发送完后,接收端获得SDA控制权,此时接收设备应该在第9个时钟脉冲之前回复一个ACK(将SDA拉低)以表示接收正常,如果接收设备没有将SDA拉低,则说明接收设备可能没有收到数据(如寻址的设备不存在或设备忙)或无法解析收到的消息,如果是这样,则由master来决定如何处理(stop或repeated start condition)。

10位地址格式

10 位从机地址是由在起始条件 S 或重复起始条件 Sr 后的头两个字节组成。

第一个字节的头 7 位是 11110XX 的组合 ,其中:最后两位 XX 是 10 位地址的两个最高位 MSB

第一个字节的第 8 位是 R/ W 位, 决定了报文的方向 :0 表示写, 1 表示读。

如果 R/ W 位是 0 则下一个字节是 10 位从机地址剩下的 8 位;

如果 R/ W 位是 1 则下一个字节是从机发送给主机的数据。

仲裁过程

主机只能在总线空闲的时侯启动传输。两个或多个主机可能在起始条件的最小持续时间内产生一个起始条件,结果在总线上产生一个规定的起始条件。

当 SCL 线是高电平时,仲裁在 SDA 线发生。这样,在其他主机发送低电平时,发送高电平的主机将退出竞争, 因为总线上的电平与它自己的电平不相同

仲裁可以持续多位。第一个阶段是比较地址位,如果每个主机都尝试寻址相同的器件,当主机作发送器时仲裁会继续比较数据位,当主机作接收器时仲裁会继续比较响应位。

IIC总线的地址和数据信息由赢得仲裁的主机决定,因此在仲裁过程中不会丢失信息

丢失仲裁的主机可以产生时钟脉冲,直到丢失仲裁的该字节末尾。

可以看到在起始信号的第3个时钟周期低电平时,DATA1的SDA输出高电平,而DATA2向SDA输出低电平,SDA总线保持低电平,仲裁给到DATA2,即DATA1的主机退出竞争,不再发送数据。DATA1主机赢得仲裁,SDA总线已传送的数据和DATA1发送的数据保持一致。

5. 读数据

7位寻址为例 CPU作为主接收器

第一步,主机发送一个起始信号S。

第二步,主机发送7bit从机地址。此处需要注意,发送数据时,无法发送7bit数据,此处发送了7bit地址+1bit读写选择位,即发送7bit+R/W。最低位为1表示读,为0表示写。

第三步,从机产生一个ACK应答信号。

//第四步,主机发送寄存器地址。
//第五步,从机产生一个ACK应答信号。
//第六步,主机再次发送一个起始信号。
//第七步,主机发送7bit从机地址,即7bit+R/W。最低位为1表示读,为0表示写。
//第八步,从机产生一个ACK应答信号。

第九步,主机读取一个字节(8bit)的数据(相当于从机发送一个字节)。
第十步,CPU产生一个ACK应答信号。

//第十一步,读取一个CRC校验码。

第十二步,CPU产生一个NACK无应答信号。
第十三步,主机产生一个停止信号。

常规的使用就是第一、二、三、九、十、十二、十三步。
注释掉的是单片机集成的硬件IIC配置通信的整个完整的通讯过程。

6. 写数据

CPU作为主发送器

第一步,主机发送一个起始信号。

第二步,发送7bit从机地址。此处需要注意,发送数据时,无法发送7bit数据,此处发送了7bit地址+1bit读写选择位,即发送7bit+R/W。最低位为1表示读,为0表示写。

第三步,从机产生一个ACK应答信号。

//第四步,主机发送寄存器地址,8bit数据。
//第五步,从机产生一个ACK应答信号。

第六步,主机发送一个字节(8bit)数据。
第七步,从机产生一个ACK应答信号。

//第八步,主机发送一个CRC校验码,此CRC校验值为2、4、6步数据产生的校验码。

第九步,从机既可以发送一个应答信号,也可以发送一个无应答信号。
第十步,主机发送一个停止信号。

(三)单片机IIC通讯


1. 软件模拟

CPU与EEPROM通讯为例

相关函数:

void I2C_Delay(void);	//延时,以防单片机速度太快void I2C_Start(void);	//起始信号void I2C_Stop(void); 	//终止信号u8 I2C_SendByte(u8 data);	//写一个字节u8 I2C_ReadByte(void);	//读一个字节void EEPROM_Write(u8 address, u8 data);	//往EEPROM的一个地址写一个数据u8 EEPROM_Read (u8 address);	//读EEPROM的一个地址的数据

stm32软件模拟配置流程:

  1. 初始化GPIO

    • void I2c_Init(void); //初始化GPIO

    • 模拟总线SDA, SCL ,GPIO引脚开漏输出

  2. 写起始信号函数

    • void I2C_Start(void); //起始信号

    • SDA, SCL拉高 , 延时

      • SDA = 1; SCL = 1; delay();
    • SDA由高变低 , 延时

      • SDA = 0; delay();
    • SCL拉低,延时等待发送/接收

      • SCL = 0; delay();
  3. 写终止信号函数

    • void I2C_Stop(void); //终止信号

    • SCL拉低后将SDA拉低 (SCL低电平时SDA可变),延时

      • SCL = 0; SDA = 0; delay();
    • SCL拉高 , 延时

      • SCL = 1; delay();
    • SDA由低变高 , 延时

      • SDA = 1; delay();
  4. 等待应答信号函数

    • u8 I2C_WaitToAck(void); //读取器件ACK应答
  5. 产生/不产生ACK应答

    • void I2C_Ack(void);
    • void I2C_NoAck(void);
  6. 写一个字节

  7. 读一个字节

//延时static void I2c_Delay(void){    /*CPU主频72MHz      10: SCL频率205KHz      7:  SCL频率347KHz,高电平1.5us,低电平2.87us;      5:  SCL频率421KHz,高电平1.25us,低电平2.375us;*/    u8 i;    for(i = 0; i < 10; i++);}//起始信号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();}//写一个字节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();    }} //读取一个字节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;}//产生一个ACK应答信号void I2C_Ack(void){    I2C_SDA_Low();    I2C_Delay();    I2C_SCL_High();	//cpu产生一个时钟    I2C_Delay();    I2C_SCL_Low();    I2C_Delay();     I2C_SDA_High();}//产生一个非ACK信号void I2C_NoAck(void){    I2C_SDA_High();    I2C_Delay();    I2C_SCL_High();	//cpu产生一个时钟    I2C_Delay();    I2C_SCL_Low();    I2C_Delay();}//产生时钟读取器件ACK应答信号u8 I2C_WaitToAck(void){    u8 redata;     I2C_SDA_High();	//CPU释放SDA总线    I2C_Delay();    I2C_SCL_High();	//SCL=1此时器件会返回ACK应答    I2C_Delay();     if(I2C_SDA_READ())	//CPU读取SDA口线状态    {        redata = 1;    }    else    {        redata = 0;    }    I2C_SCL_Low();    I2C_Delay();     return redata;}  

2. 硬件IIC控制器配置(stm32)

(1)主发送器

(2)主接收器

(3)配置流程

与EEPROM通讯为例

  1. 初始化IIC相关GPIO
  2. 配置IIC外设的工作模式
  3. 编写IIC写入EEPROM的函数
  4. 编写IIC读取EEPROM的函数
  5. 使用read函数及write函数进行读写校验
  6. 编写page write及seq read函数并校验(页写)
(4)重要函数
  1. IIC初始化结构体
typedef struct{    uint32_t I2C_ClockSpeed;	//设置SCL时钟频率 <40,000     uint32_t I2C_Mode;	//工作模式 I2C模式或SMBUS模式    uint32_t I2C_DutyCycle;	//指定时钟占空比 低/高=2/1或16/9    uint32_t I2C_OwnAddress1;	//指定自身的I2C设备地址    uint32_t I2C_Ack;	//使能或关闭响应(一般使能)    uint32_t I2C_AcknowledgedAddress;	//指定地址长度 7位/10位} I2C_InitTypeDef;

参考:

原理:《IIC总线协议中文版》

代码实现:博客园

学习:野火、正点原子、普中科技
部分图片来源于网站,侵删

修改时间:2021年09月27日

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/121624.html

相关文章

  • stm32通过I2C接口实现温湿度(AHT20)的采集

    摘要:如图所示在了解起始条件和停止条件后,我们再来看看在这个过程中数据的传输是如何进行的。四参考资料通过接口实现温湿度的采集硬件和软件区别 stm32通过I2C接口实现...

    chengtao1633 评论0 收藏0
  • SPI、PCI、IIC、UART、GPIO、AXI4协议总结大全

    摘要:从设备使能信号,由主设备控制,只有该信号为预先规定的使能信号,对该芯片的操作才有效,这就允许再统一总线上连接多个设备。点对点通信中,接口不需要进行寻址操作,但是没有指定的流控制,没有应答机制确认是否接收到数据。 1、GPIO 通用可编程IO接口,对于简单的外围设备或者电路,需要CPU为之提供...

    zhangrxiang 评论0 收藏0
  • STM32CubeMX学习教程之硬件I2C读取光照度

    摘要:使用库读写环境光照度传感器本文将教大家如何快速使用库读取光照度数据。五实验样机测试展示通过之前配置好的面板,通过涂鸦智能进行配网实时采集光照度传感器的数据。 使用STM32 HAL库读写环境光照度传感器(BH1750) 本文将教大家如何快速使用STM32HAL库读取光照度数据。 实现功能:通...

    tinylcy 评论0 收藏0
  • Q3面试嵌入式软件工程师的面试经验

    摘要:问你是否用过什么操作系统,,等,这个是加分项,然后会问一些的协议,进程与线程区别,还有一些电机算法,或者视觉等等。 大家好,我是Q3。我是一名2022届的一名电气工程及其自动化的学生,学校是二本就不说啦。   我今天主要是来浅谈一下我的面试经验以及一些遇到的问题。   首先我来聊一下我的学习...

    不知名网友 评论0 收藏0
  • 基于STM32平台的数字温度显示器系统设计

    摘要:带中文字库的是一种具有位位并行线或线串行多种接口方式,内部含有国标一级二级简体中文字库的点阵图形液晶显示模块其显示分辨率为。贴片按键模块支持标准的协议,完整的协议栈。以最低成本提供最大实用性,为功能嵌入其他系统提供无限可能。 项目简介 开发环境: Keil5.14,CubeMX6.0.1,主...

    villainhr 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<