2.1 IIC协议的FPGA实现(二)IIC协议的FPGA实现

2.1 IIC协议的FPGA实现

2.1.2 IIC协议的FPGA实现

在这里插入图片描述

              图2 13 IIC模块的建模图
  图2 13是 IIC 储存模块的建模图,左边是顶层信号,右边则是沟通用的问答信号,写入地址 iAddr,写入数据 iData,还有读出数据 oData。 Call/Done 有两位,即表示该模块有读功能还有些功能。具体内容,我们还是来看代码吧:
                代码2 1 IIC代码声明

1.	parameter FCLK = 10'd125, FHALF = 10'd62, FQUARTER = 10'd31;  
2.	parameter THIGH = 10'd30, TLOW = 10'd65, TR = 10'd15, TF = 10'd15;  
3.	parameter THD_STA = 10'd30, TSU_STA = 10'd30, TSU_STO = 10'd30;  

  如代码2 1 所示, FCLK 表示 400Khz 的周期, FHALF 表示 1/2 周期, FQUARTER 表示 1/4 周期。
在这里插入图片描述
                      图2 14 起始位
  首先让我们先瞧瞧起始位这枚拼图。如图2 14所示,左图是起始位的理想时序,右图是起始位的物理时序。 IIC 总线的起始位也就类似串口或者 PS/2 等传输协议的起始位,然而不同的是, IIC 总线的起始位是 SCL 拉高 TR + TSU_STA + THD_STA + TF 之久,换之 SDA 则是拉高 TR + THIGH 然后拉低 TF + TLOW。起始位总和所用掉的时间,恰恰好有一个速率的周期。对此, Verilog 则可以这样描述,结果如下所示:
                  代码2 2 IIC起始位产生代码

1.	begin  
2.	     isQ = 1;  
3.	     rSCL <= 1'b1;  
4.	     if( C1 == 0 ) rSDA <= 1'b1;  
5.	     else if( C1 == (TR + THIGH) ) rSDA <= 1'b0;  
6.	     if( C1 == (FCLK) -1) begin C1 <= 10'd0; i <= i + 1'b1; end  
7.	     else C1 <= C1 + 1'b1;  
8.	end  

  如代码2 2所示,第 2 行的 isQ = 1 表示设置 SDA 为输出状态(即时结果),第 3 行则表示 SCL 一直持续拉高状态,第 4~5 行表示 C1 为 0 的时候 SDA 拉高,直到 C1 为TR+THIGH 才拉低 SDA。第 6~7 行表示一个步骤所逗留的时间。
在这里插入图片描述
                      图2 15 结束位
  图2 15是结束位的时序图, IIC 设备的操作好坏一般都取决结束位。保险起见, SCL 与SDA 都事先拉低 1/4 周期,紧接着 SCL 会拉高 TR+TSU_STO(或者 1/2 周期),最后又保持高电平 1/2 周期。反之, SDA 会拉低 1/2 周期,随之拉高 TR+THIGH(或者 1/2周期)。对此, Verilog 可以这样表示,结果如代码2 3所示:
                    代码2 3 IIC结束位代码实现

1.	begin  
2.	    isQ = 1'b1;  
3.	        
4.	    if( C1 == 0 ) rSCL <= 1'b0;  
5.	    else if( C1 == FQUARTER ) rSCL <= 1'b1;   
6.	       
7.	      if( C1 == 0 ) rSDA <= 1'b0;  
8.	      else if( C1 == (FQUARTER + TR + TSU_STO ) ) rSDA <= 1'b1;  
9.	        
10.	      if( C1 == (FQUARTER + FCLK) -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end  
11.	      else C1 <= C1 + 1'b1;   
12.	end  

  如代码2 3所示,第 2 行表示 SDA 为输出状态(即时),第 3~4 行表示 C1 为 0 拉高SCL, C1 为 1/4 周期就拉高。第 5~6 行表示, C1 为 0 拉低 SDA, C1 为 1/4 周期 + TR +TSU_STO 就拉高 SDA。第 7~8 行表示该步骤所逗留的时间。
在这里插入图片描述
                      图2 16 释放总线
  此外,结束位还有 Bus Free Time 这个时序参数,IIC 总线在闲置的状态下 SCL 与 SDA等信号都持续高电平。主机发送结束位以示结束操作,然而主机持续拉高 SCL 信号与SDA 信号 TBUF 以示总线释放。 TBUF 的有效时间从 SCL 信号与 SDA 信号拉高那一刻开始算起
  根据表2 2 所示, TBUF 是 65 个时钟,结果如图 16.6 所示, SDA 信号拉高之后, SCL与 SDA 信号只要持续保持 1/2 周期(即 62 个时),基本上就能满足 TBUF。如果笔者是一位紧密控时狂人,可能无法接受这样的结果,因为满足 TBUF 少了 3 个时钟,为此代码2 3需要更动一下:
                    代码 2 4 IIC结束位代码修改

1.	if( C1 == ( FQUARTER + FCLK + 3) -1 )   
2.	     begin C1 <= 10'd0; i <= i + 1'b1; end  
3.	else C1 <= C1 + 1'b1;  

  如代码 2 4所示,笔者为第 1 行写下 +3 表示该步骤多逗留 3 个时钟,以致满足 TBUF。
在这里插入图片描述
  不管对象是设备地址,数据地址,写入数据,读出数据,还是应答位,大伙都视为数据位。 IIC 总线类似其他传输协议,它有时钟信号也有上升沿与下降沿。如图 16.7 所示,SCL 信号的下降沿导致设备设置(更新)数据,上升沿则是锁存(读取)数据。期间,TF+TLOW 表示时钟信号的前半周期, TR+THIGH 则表示后半周期。此外,为了确保数据成功打入寄存器,数据被上升沿锁存哪一刻起, TSU_DAT 还有 THD_DAT 必须得到满足。
在这里插入图片描述
                      图2 17 数据位更新有效
  除此之外,为了确保数据有效被更新,也必须确保 TAA 得到满足,结果如图2 17所示。理解完毕以后,就可以开始学习,写一字节数据与读一字节数据,还有应答位。
在这里插入图片描述
                        图2 18 写一字节
  IIC 总线一般都是一个字节一个字节读写数据,如图2 18 所示,那是写一字节的理想时序图,一字节数据是从最高位开始写起。对此, Verilog 可以这样描述,结果如代码2 5所示:
                      代码2 5 IIC 总线写一个字节

1.	7,8,9,10,11,12,13,14:  
2.	begin  
3.	    isQ = 1'b1;  
4.	    rSDA <= D1[14-i];  
5.	    if( C1 == 0 ) rSCL <= 1'b0;  
6.	    else if( C1 == (TF + TLOW) ) rSCL <= 1'b1;   
7.	    if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end  
8.	    else C1 <= C1 + 1'b1;  
9.	end  

  如代码2 5 所示,第 1 行有 8 个步骤,表示写一个字节。第 3 行 isQ 为 1 表示 SDA 为输出状态。第 4 行表示从最高位开始更新 SDA 的数据位。第 5~6 行表示, C1 为 0 拉低SCL, C1 为 TF+TLOW 则拉高 SCL。第 7~8 行表示该步骤逗留一个周期的时间。
在这里插入图片描述
                        图2 19 应答位
  应答位是从机给予主机的回答, 0 为是,1 为否。然而,从旁观看,读取应答位也是读取一位数据位。当主机完成写入一个字节或者读取一个字节数据的时候,从机都会产生应答位。主机拉低 SCL 那刻,从机便会发送应答位,然后主机会借由上升沿读取应答位。如图2 19 所示,上升沿会产生在 TF + TLOW 之后,也是 1/2 周期。对此, Verilog 可以这样表示,结果如代码2 6所示:
                    代码2 6 IIC应答位

1.	begin  
2.	    isQ = 1'b0;  
3.	      
4.	  if( C1 == FHALF ) isAck <= SDA;  
5.	    
6.	  if( C1 == 0 ) rSCL <= 1'b0;  
7.	  else if( C1 == FHALF ) rSCL <= 1'b1;  
8.	    
9.	  if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end  
10.	  else C1 <= C1 + 1'b1;   
11.	end  

  如代码2 6所示,第 2 行表示 SDA 为输入状态。第 4~5 行表示, C1 为 0 拉低 SCL,C1 为 1/2 周期则拉高 SCL。第 3 行表示, C1 为 1/2 周期的时候读取应答位。第 6~7 行表示该步骤逗留 1 个周期的时间。
在这里插入图片描述
                    图2 20 读一字节
  所谓读一字节数据就是重复读取 8 次应答位。如图2 20所示, SCL 的下降沿导致从机更新数据,然后主机在 SCL 的上升沿读取数据。此外,从机也会由高至低更新数据位。至于 Verilog 则可以这样表示,结果如代码2 7所示:
                    代码2 7 IIC读一字节

1.	19,20,21,22,23,24,25,26: // Read  
2.	begin  
3.	    isQ = 1'b0;  
4.	    if( C1 == FHALF ) D1[26-i] <= SDA;  
5.	    
6.	  if( C1 == 0 ) rSCL <= 1'b0;  
7.	  else if( C1 == FHALF  ) rSCL <= 1'b1;   
8.	    
9.	  if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end  
10.	  else C1 <= C1 + 1'b1;  
11.	end     

  如代码2 7所示,第 1 行表示读取一字节。第 3 行表示 SDA 为输入状态,第 5~6 行表示, C1 为 0 拉低 SCL, C1 为 1/2 周期则拉高 SCL。第 4 行表示, C1 为 1/2 周期的时候读取数据,而且数据位由高至低存入 D1。第 7~8 行表示该步骤逗留一个周期的时间。
在这里插入图片描述
                    图2 21 第二次起始位
  知道主机向从机读取数据的时候,它必须改变设备地址的方向,因此读操作又第二次起始位。如图2 21所示,感觉上第二次起始位也是第一次起始位,不过为了促使改变方向成功,第二次起始位相较第一次起始位的前后都拉低 1/4 周期。对此, Verilog 可以这样表示,结果如代码2 8所示:
                    代码2 8 IIC第二次起始位

1.	begin  
2.	     isQ = 1'b1;  
3.	     if( C1 == 0 ) rSCL <= 1'b0;  
4.	     else if( C1 == FQUARTER ) rSCL <= 1'b1;  
5.	     else if( C1 == (FQUARTER + TR + TSU_STA + THD_STA + TF) ) rSCL <= 1'b0;  
6.	   
7.	     if( C1 == 0 ) rSDA <= 1'b0;  
8.	     else if( C1 == FQUARTER ) rSDA <= 1'b1;  
9.	     else if( C1 == ( FQUARTER + TR + THIGH) ) rSDA <= 1'b0;  
10.	   
11.	     if( C1 == (FQUARTER + FCLK + FQUARTER) -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end  
12.	     else C1 <= C1 + 1'b1;  
13.	end  

  如代码2 8 所示,第 2 行表示 SDA 为输出状态。第 3~5 行表示, C1 为 0 拉低 SCL,C1 为 1/4 周期拉高 SCL, C1 为 1/4 周期 + TR + TSU_STA + THD_STA + TF 便拉低SCL。第 7~9 行表示, C1 为 0 拉低 SDA, C1 为 1/4 周期拉高 SDA, C1 为 1/4 周期 + TR+ THIGH 便拉低 SDA。第 11~12 行表示该步骤停留一个周期的时间。
接下来是仿真验证,结果如下:
在这里插入图片描述
在这里插入图片描述
                  图 2 22 IIC总线仿真时序图
  结合上述仿真波形图和程序可以看出:
  起始位:SCLK为高电平时,SDAT由高到低,指示IIC总线传输数据的开始;
  之后,传送一个字节的数据,即4A,为从机的地址,随后,跟了一个高电平,为应答位;
  之后,传送一个字节的数据,即01,为从机地址的子地址,随后,跟了一个高电平,为应答位;
  之后,传送一个字节的数据,即08,为上面子地址寄存器配置的数据,随后,跟了一个高电平,为应答位;
  最后,为停止位,SCLK为高电平时,SDAT由低到高,指示该次IIC总线传输数据的结束。
  由仿真结果可知,当传送完一个字节后,SDAT为一个脉冲的高电平,而不是从器件先将SDAT拉低再拉高,这样也是可以的。

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章