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

2020-06-30 10:20:41 浏览数 (1)

2.1 IIC协议的FPGA实现

2.1.2 IIC协议的FPGA实现

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

代码语言:javascript复制
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起始位产生代码

代码语言:javascript复制
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结束位代码实现

代码语言:javascript复制
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结束位代码修改

代码语言:javascript复制
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 总线写一个字节

代码语言:javascript复制
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应答位

代码语言:javascript复制
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读一字节

代码语言:javascript复制
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第二次起始位

代码语言:javascript复制
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拉低再拉高,这样也是可以的。

0 人点赞