【协议项目之 I2C】(一) 基本时序与实现

一、基本介绍

  I2C协议(集成电路总线)使用两根线SDASCL实现数据传输,其连接如下图所示,总线上通过上拉电阻可以挂载各种低速外设,例如EEPROM 24C02,传感器等。
在这里插入图片描述
  使用I2C,可以将多个从机(Slave)连接到单个主设备(Master),并且还可以有多个主设备(Master)控制一个或多个从机(Slave)

一、启动时序与时钟产生(START condition)

  启动时序如下图所示,
在这里插入图片描述
  在总线空闲时,SDA,SCL都处于高电平。而在启动I2C传输时,主设备先将SDA拉低,再将SCL拉低。开始标志之后,就开始传输数据,传输数据要求保证在SCL的高电平时期保持不变,否则会被误识别为停止标志或者其他标志。
  假设SDA是sda_clk的时钟下降沿发生变化,其中sda_clk是I2C模块的内部时钟之一。带有sda_clk的参考时序图如下所示。
1
  该图的绘制代码如下,可以在这个网站使用js进行绘制

{signal: [
  {name:'scl_clk',wave:'p.......|',data:['data1','data2','data3']},
  {name:'SDA',wave:'1.0====|.',data:['data1','data2','data3'],phase:-0.5},
  {name:'SCL',wave:'1.0p....',data:['data1','data2','data3'],phase:-0.75},
]}

  如上图,以scl_clk为基准参考,SDA是由该时钟生成的同步电路,并且我们就假设SDA都是在时钟下降沿发生变化。由于SCL需要满足I2C协议中的以下两个条件

1.在启动时序中,SCL其拉低是在SDA之后的,而SDA拉低是在时钟下降沿(以上升沿为参考,下降沿的相位移动是phase:-0.5,遵循函数左加右减规则)
2.SCL的高电平区域在传输数据时不允许SDA发生跳变,即SDA的数据变化必须落在SCL的低电平区域

  基于以上的要求,我们可以推导出时钟相关信号的生成步骤

在这里插入图片描述

  时钟产生的逻辑是,首先对高速时钟进行1000分频(即添加500计数器clk_div,每数到500就进行一次翻转),这样就生成了时钟scl_clk;

//scl_clk生成(驱动sda)
always@(posege clk_i)
	if(clkdiv < CLK_DIV)
		clkdiv=clkdiv+1'b1;
	else begin
		clkdiv=16'd0;
		scl_clk<=!scl_clk;
	end

  接着,根据scl_clk生成下降沿转换的状态机IIC_S。SDA由状态机的状态组合逻辑产生,也是在scl_clk下降沿跳变。
  由于SCL需要满足两个条件(上文提到)。我们先产生与时钟下降沿对齐的scl_r中间信号,然后再进行调整。scl_r的逻辑就是在IDLE或者STOP1/2这些状态为高,其余状态为scl_clk的取反。

//scl_r生成逻辑,IIC_S为状态机状态
always@(*)begin
	if(IIC_S==IDLE || IIC_S ==STOP1 || IIC_S ==STOP2)
		scl_r<=1'b1;
	else
		scl_r<=~scl_clk;

  为了满足两个条件,我们发现只要把scl_r略微右移就可以实现。为了实现略微右移,我们先生成scl_offset信号。

parameter OFFSET=CLKDIV-CLK_DIV/4;
wire scl_offset=(clkdiv==OFFSET);

  scl_clk是在clk_div==CLKDIV时候翻转,而scl_offset比其早了0.25个CLKDIV计数,其关系如上图所示。
  我们基于scl_offsetscl_r进行滞后采样,根据图上关系所示,从而生成SCL波形(iic_scl),这样可以满足两个条件

	iic_scl<=scl_offset?scl_r:iic_scl;

二、停止时序(STOP condition)

  I2C协议中对停止时序的要求是,SCL先拉高,然后SDA再拉高。这个时序刚好与启动时序相反。由于上一节中提到,SCL是scl_r滞后采样得到的,因此需要SCL率先拉高,就需要scl_r比SDA率先拉高。因此可以将STOP状态定义为两个状态,STOP1和STOP2.
在这里插入图片描述

  如果只有一个停止·状态(例如只有stop1),那么SDA就要在STOP位置高,如上图SDA_wrong所示,而scl_r也同样在此时置高,SCL滞后于scl_r,这样就会导致SCL比SDA_wrong更晚拉高,不满足停止时序所要求的SCL率先拉高。
  因此我们分出两个停止状态STOP1,STOP2,将scl_r现在STOP1就拉高,而将SDA在STOP2再拉高,这样一来就满足了SCL先于SDA拉高的时序,并且两者拉高的间隔小于一个scl_clk时钟周期(由于滞后采样的原因)

三、状态机分析(基于EEPROM应用)

  以上只是简单写一个包(8bit)的状态演示,其状态还不够完备。
  由于是想要用于驱动E2PROM的,因此有些完备状态的补充,我们需要参考E2PROM的用户手册

字节写

  如下图所示,为字节写的时序,本质是实现了连续三次的写数据。首先是开始标志,标志着I2C传输的开始。然后紧接着是八位指令
在这里插入图片描述
  该指令构成如下
在这里插入图片描述
  其中1010是该器件24C02内部规定好的,用于标志总线的设备类型,其实属于地址的一部分,用来区分不同的设备。然后后面紧接着的A2 to A0是器件的地址,可以在器件的这三个引脚给出高低电平来设置某个EEPROM 24C02器件的地址。最后一位是度或者写指令,其中为0表示写为1表示读。
  以上就是指令的构成格式。指令结束后,经过一位ACK,然后主机在总线上写入连续8位(第二个包,指令是第一个包)作为写入E2PROM的地址,然后再经过一位ACK,主机在总线上写入连续8位(第三个包)作为数据,最后经过一位ACK产生停止标志,这样就完成了对特定地址的写操作。
  因此,从字节写操作中,我们发现对状态的要求有:在一个包内,需要能用重复发8bit数据。此外包与包之间是连续发送,仅隔了一个ACK,并没有经历新的START。所以发完一个包并不能进入STOP和IDLE状态。因此我们设计满足字节写操作的状态机如下图所示。
在这里插入图片描述

连续写(写一页(8BYTE))

在这里插入图片描述

  是写一个字节操作的扩展,可以发现上述状态机仍然满足连续写,只是再下一个包的循环中多循环几轮的差别,我们可以再状态转移逻辑中对其进行设置,例如设置一个写包计数器,来表征需要连续写入字节的个数,计数器归零后发送停止条件。

读当前字节

在这里插入图片描述

  EEPROM内部是有指针的,内部计数器指向上一个操作地址的下一地址空间。
  如时序图所示,主设备写入读命令后,接收到EEPROM的一个ACK回答,然后连续在总线上读取8位数据地址(其中高位在前),主器件接收完毕后,无需回送ACK,只需要发送停止标志即可。

读随机字节

在这里插入图片描述

  随机读,即读特定地址的一个字节的数据,时序如上图所示。主机先在总线上写1Byte的指令来表征要进行写操作,紧接着写需要读的8位地址。然后再重新给出起始条件,转为在总线上写1Byte的指令来表征要进行的读操作,然后再读取地址。

连续读字节

  连续读可以理解为读的扩展,不管是直接读还是随机读,都可以发送ACK来继续下一次读取,EEPROM内部计数器会自动加一。
在这里插入图片描述

  综上所述,其实主机发送的指令其实只有两种,分别是读指令和写指令,

76543210
1010A2A1A0W/R

  其中R为1,W为0.由于对应某一个特定的EEPROM器件,其指令高7位地址都是相同的,因此主机对其发送的指令只有最低位为1(读)和最低位为0(写)这两种指令。每次主机发送一种指令都需要一个起始位。因此读写转换,写读转换都需要重新发送起始位。
  我们以随机读为例,随机读相对于直接读,前面多了俩字节,而后面的两字节(主机往总线上写入读指令,主机接收读到的数据)是相同的。前面的两字节是往总线上写入写指令和写地址,我们比较一下写字节指令,只给出了要写的地址,并未给出要写的数据,因此它可以改变内部指针计数器,而并未真的写入,这样就实现了随机读的功能。
  基于以上分析,就有了下述状态机的设计。
在这里插入图片描述

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值