FPGA实现IIC协议(二)—-IIC总线的FPGA实现(单次读写驱动)

1、写在前面 IIC协议系列博文: FPGA实现IIC协议(一)&#…

1、写在前面

IIC协议系列博文:

FPGA实现IIC协议(一)—-初识IIC总线

FPGA实现IIC协议(二)—-IIC总线的FPGA实现(单次读写驱动)

上一篇文章已经对IIC总线做了详细的介绍,了解了IIC总线的读写方式。这篇文章我们编写一个基于FPGA的IIC驱动模块,并对这个模块进行仿真及上板验证。

2、单次读写时序

首先来回顾一下IIC总线单次读写时序。

单次写时序如下:

单次读时序如下:

大致总结一下单次写时序的过程(假设从机均正确响应,若响应不正确或不响应则跳转到初始状态重新开始写操作):

  1. 发送起始信号,一次传输开始
  2. 发送器件地址+低电平(表示写),等待从机正确回应
  3. 发送寄存器地址(16位或者8位地址),等待从机正确回应
  4. 发送8位数据写入从机,等待从机正确回应
  5. 发送停止信号,终结本次传输

单次读时序的过程(假设从机均正确响应,若响应不正确或不响应则跳转到初始状态重新开始读操作):

  1. 发送起始信号,一次写传输开始
  2. 发送器件地址+低电平(表示写,这次写操作为虚写,真正目的是将地址指针指向需要读取的地址),等待从机正确回应
  3. 发送寄存器地址(16位或者8位地址),等待从机正确回应
  4. 再次发送起始信号,一次读传输开始
  5. 发送器件地址+高电平(表示读,这次读操作才是真正的读操作),等待从机正确回应
  6. 接收总线上由从机发送的8位数据,发送非响应信号给从机,表示不需要再接收数据
  7. 发送停止信号,终结本次传输

3、状态机

本文的目的是设计一个IIC的驱动模块,包含单次读、单次写,也兼容16位寄存器与8位寄存器。据次及上述时序图,可以绘制出IIC单次读写驱动的状态机如下:

上图状态机将单字节写操作和随机读操作相结合,可以实 现 I2C 设备单次写操作和单次随机读操作的状态跳转。需要说明的是状态ACK1、ACK2、ACK3、ACK4、ACK5还在主机没有正确接收到从机的响应信号时跳转回初始状态。

对各个状态、状态跳转条件、输出进行说明(假设主机为FPGA):

  1. 上电后处于 IDLE(初始状态),主机接收到有效的单字节数据读/写开始信号后,跳转到 START1(起始状态),在该状态主机发送起始信号;
  2. 随后跳转到 SEND_D_ADDR_W(发送器件地址状态+写标志),在此状态下主机发送控制指令,控制指令高7位为器件地址,最低位为读写控制字,写入“0”,表示执行写操作;然后跳转到 ACK1(应答状态)。
  3. 在 ACK1状态下,根据从机寄存器地址字节数进行不同状态的跳转。
    1. 当主机正确接收到应答信号且从机寄存器为16位 , 状态机跳转到SEND_R_ADDR_H(发送高字节地址状态),将寄存器地址的高 8 位写入从机,然后状态机跳转到 ACK2(应答状态);主机正确接收到应答信号后,跳转到 SEND_R_ADDR_L(发送低字节地址状态);
    2. 当主机正确接收到应答信号且从机寄存器为8位,状态机状态机直接跳转到 SEND_R_ADDR_L(发送低字节地址状态);将寄存器地址的低8位写入从机完成后,跳转到 ACK3(应答 状态)。
  4. 在 ACK3状态下,要根据读写使能信号做不同的状态跳转(判断此次操作为写操作还是读操作,1–读;0–写)。
    1. 当主机正确接收到从机的应答信号且读写使能信号为低,状态机跳转到WR_DATA(写数据状态);
      1. 在WR_DATA状态, 向从机写入单字节数据后,跳转到 ACK4(应答状态);
      2. 在 ACK4状态下,当主机正确接收到从机的应答信号后,跳转到 STOP(停止状态);
    2. 当主机正确接收到从机的应答信号且读写使能信号为高, 状态机跳转到 START_2(起始状态);
      1. START_2(起始状态);主机再次发送起始信号,状态跳转到 SEND_D_ADDR_R(发送器件地址状态+读标志);
      2. 主机再次发送控制字节,高 7 位器件地址不变,读写控制位写入“1”,表示进行读操作,控制字节写入完毕后,状态机跳转到 ACK5(应答状态);
      3. 当主机正确接收到应答信号,状态机跳转到 RD_DATA(读数据状态);
      4. 在 RD_DATA(读数据状态)状态,主机分8次读取总线上的数据,待数据读取完成后跳转到 NACK(无应答状态),在此状态下向从机写入一个时钟的高电平,表示数据读取完成,随后状态机跳转到 STOP(停止状态)。
  5. 在 STOP(停止状态)状态,FPGA 向 EEPROM 发送停止信号,一次单字节数据读/写操作完成,随后状态机跳回 IDLE(初始状态),等待下一次单字节数据读/写开始信号。

4、接口定义与整体设计

Verilog编写的IIC驱动的整体框图、输入输出信号如下所示:

其中信号描述如下表:

属性 名称 位宽 说明
系统接口 输入 sys_clk 1 输入系统时钟,50MHz
sys_rst_n 1 输入复位信号,低电平有效
I2C时序控制接口 i2c_rw 1 读写使能信号—-1:读;0:写
i2c_start 1 i2c开始信号
i2c_num 1 i2c字节地址字节数—-1:16位;0:8位
i2c_addr 16 i2c字节地址
i2c_data_w 8 写入i2c数据
i2c_clk 1 i2c驱动时钟
输出 i2c_end 1 i2c一次读/写操作完成
i2c_data_r 8 i2c读取数据
I2C物理接口 scl 1 输出至i2c设备的串行时钟信号scl
双向 sda 1 输出至i2c设备的串行数据信号sda

需要注意的是:i2c_clk是本驱动模块的工作时钟,由系统时钟 sys_clk 分频而来,它的时钟频率为串行时钟scl频率的 4 倍。为什么是4倍呢,不妨看一下下图的IIC基础时序:

在SCL为高电平器件,要求SDA上的数据保持稳定;在SCL为低电平器件,允许SDA上的数据变化。我们知道驱动模块对IIC总线上的数据进行读写肯定是需要一个驱动时钟的,根据上图就可以看到当驱动时钟是SCL的4倍频率时,操作起来是最方便的。如下图:

5、Verilog代码

根据上述状态机描述、整体设计不难编写出驱动模块的Verilog代码(注释很详细):

这里主要讲几点需要注意的和大概思路:

6、Testbench及仿真结果

现在我们的驱动模块就写好了,接下来分别用一个简单的和稍复杂的Testbench来对其进行测试。需要注意的是这里的仿真还需要一个EEPROM_AT24C64的仿真模型,这个模型在夏宇闻老师的书里可以找到。

6.1、ATAT24C64(EEPROM芯片)的简单介绍

AT24C64是一种非易失性存储器(断掉数据仍保留),可以通俗的理解为一个大的二维数组。

AT24C64 的存储空间为 64 Kbit(8Kbyte),一共有256页,每页32个数据。需要 13 位存储地址才可满足所有存储单元的寻址,存储地址为 2 字节,存储地址示意图如下:

最高支持400k通信速率,每次写入数据需间隔最少10ms.。

6.2、1个数据写入然后读取对比

编写Testbench实现如下目标:

  • 1.上电复位结束后,延时1ms(模拟器件稳定),开始往随机地址中写入随机数据
  • 2.写入数据10ms后对刚刚的地址进行读取操作,比对读取的数据是否与写入的数据一致

使用modelsi'm软件进行仿真,仿真结果如下图:

  • 在约1ms处,往从机的地址为‘h3524的寄存器写入数据’h81,一段时间后i2c_end拉高,表示数据写入完成
  • 在11ms处,向从机的地址为‘h3524的寄存器读取数据,一段时间后i2c_end拉高,表示数据读取完成,读到的数据为’h81
  • 读取的数据与写入的数据一致,证明仿真验证成功

截取写操作部分的详细仿真结果:

截取读操作部分的详细仿真结果:

6.3、若干个数据写入然后读取对比

在6.2节我们通过对AT24C64模型的某个地址写入随机数据然后读出对比验证的方式来对编写的IIC驱动代码做了验证,验证结果成功。可是上节的激励是来自编写的Testbench,而不是主机,也就是说无法模拟主机调用IIC总线,这样的测试难免不够周全。

在这一节,我们再编写两个Verilog文件–i2c_top、i2c_rw:

  • i2c_rw作为读写模块来调用IIC驱动对从机AT24C64模型实现写若干次操作后,再对操作过的地址进行读取操作,通过对结果的比对来验证主机对从机的IIC写读是否成功
  • i2c_top作为顶层模块,例化i2c_rw读写模块、及i2c_drive驱动模块

i2c_rw读写模块预期实现的功能:

  • 上电复位后每隔一定的时间(可设置,默认10ms)对IIC进行写入操作,地址从零开始,长度可设置(默认64),但不能大于8192(最大地址),写入数据与地址相同
  • 写入完成对上面操作过的地址进行读取操作

i2c_rw读写模块代码:

i2c_top顶层模块代码:

编写Testbench例化i2c_top顶层模块和AT24C64仿真模型,同时打印读写信息(读写状态;地址;数据):

使用modelsi'm软件进行仿真,仿真结果如下图:

  • 前一段时间i2c_rw信号是低电平,表示此时在进行数据写入操作
  • 前一段时间i2c_rw信号是高电平,表示此时在进行数据读取操作

接下来局部截图进行讲解:

下图中:

i2c_rw为低电平,代表在进行写操作,分别往地址d55中写入d55·······地址d62中写入d62等,直到往地址d63中写入d63后,i2c_rw被拉高,开始进行读操作。重新从地址0开始到63进行读取操作。

下图中:

i2c_rw为高电平,代表在进行读操作,分别从地址d42中读取d42·······直到地址d63中写入d63等后,i2c_rw被拉高,重新从地址0开始到63进行读取操作。需要注意的是,下图与上图在IIC时序上,sda信号存在蓝色(不定态)和红色(x状态),产生的原因我们最后再讲,这里只需要知道它们对时序没有影响即可。

还记得之前我们在testbench加入了命令打印行吗?接下来就看一下命令打印窗口:

首先对地址0-63写入数据0-63,然后重复对地址0-63读取,读取的数据为0-63,与写入数据一致

7、上板验证

至此已经顺利完成了IIC驱动的仿真验证,接下来使用一块Cyclone IV E的开发板上板验证,使用signaltap II抓一下波形。

因为每两次写入操作中间隔10ms,所以在FPGA芯片资源有限的情况下肯定没办法同时抓到两次写入的波形,只好随便抓几个个波形分析下IIC时序:

下图往地址d18写入数据d18:

下图往地址d50写入数据d50:

其他地址类推。

读操作的截图如下:

从地址48-63(实际上是0-63,截图问题)读取了数据48-63,然后进行新一轮的读取。读取的数据与写入的数据一致。


对地址50进行读操作的截图如下:

8、问题与思考

8.1、读写仿真出现的高阻态与未知态问题

在使用仿真模型进行读写仿真时出现了高阻态与未知态,见下图(写操作):

上图的蓝色高阻态均发生在sda_en为低电平期间,此时主机放弃了总线控制权,放弃总线的原因是为了等待从机发送响应信号(低电平),而高阻态的出现就是因为总线控制权的过渡需要时间。同时还可以发现高阻态发生在scl低电平(基本算是中间),此时是允许数据变化的,所以不会对写时序造成影响。

读操作出现的问题如下:

读操作的情况基本与写操作一致,不过还出现了x状态,但也不影响读写时序。

8.2、从机应答时间问题

在上板验证时发现在从机应答时出现了高电平,分别如下两图:

画图说明:

  • A部分—-1010_000是从机的器件地址;
  • B部分—-读写控制位;
  • C部分—-是从机的应答信号;

从A部分可以看出,发送数据的时候,数据的改变都是在低电平的中间,所以发送给从机的单bit数据时间宽度==scl时钟的半个低电平+scl时钟的一个高电平+scl时钟的半个低电平;我一开始在C部分检测从机的应答信号的时候,先是在前一个低电平的中间释放了总线,然后等待总线被从机拉低后再拉高。根据A部分的数据发送经验我以为,从机的响应应该也会持续到下一个低电平的中间才会发生变化(也就是从机的响应会维持一个scl时钟周期),可实际上从机提前了1/4个scl时钟周期,在scl时钟的下降沿就发生了变化。

这个问题我一开始一直以为是响应不成功,加上状态机的状态跳转我又是设计的在低电平的中间检测从机是否响应,导致我的状态跳转一直是错的。后面仔细查了数据手册,发现了这个响应的时序如下(AT24C64):

可以看到,时序图上的响应信号也确实是在scl的下降沿变化,而不是低电平的中间。

然后又搜了点资料,发现正点原子的开发板的教学资料上EEPROM章节,signaltap II 抓的时序如下:

图中黑圈都是从机响应后被上拉电阻拉高,而且从机都是在scl的下降沿将总线释放了,和我抓到的图是一致。

解决办法: 在响应信号的scl的上升沿中间进行采样,一旦在采样中检测到响应信号,就把标志信号拉高(ack_flag),这样状态的跳转就可以通过标志信号来判断了。

8.3、写入间隔(写回时间)

下图说明:

  • A部分—-第一次写操作;
  • B部分—-100us延时操作;
  • C部分—-第二次写操作;
  • D部分—-从机回应的无效响应信号

从上图可以看到,两次写操作的间隔时间只有100us,这就导致第二次写操作失败(回了非响应信号)。每个I2C从器件的写间隔时间不一样,具体以手册为准。

解决办法:查看AT24CC64的手册,发现写入时间间隔twr==10ms,所以把两次写入的间隔时间改成10ms就好了。

8.4、读操作要先发送写指令

读操作在发送“器件地址+读”之前应该先进行一次哑写操作,这样就可以把地址指针指向想写入数据的寄存器地址。

我一开始调试的时候就是忘了这点,直接发送的读操作,然后就收到了从机的非响应信号,导致时序错误。

解决办法:先进行写操作就可以了。

8.5、其他

想要整个工程的朋友可以在评论区留下邮箱。

本文来自网络,不代表软粉网立场,转载请注明出处:https://www.rfff.net/p/3766.html

作者: HUI

发表评论

您的电子邮箱地址不会被公开。

返回顶部