分享

一天一个设计实例-万字长文E2PROM接口电路、时序及应用程序设计

 比特波特 2021-02-26

因为IIC 接口的E2PROM存储芯片比较常见和实用,所以本节主要以I2C的接口电路为主,后期有需要再添加。

1.1.1I2C 总线规范简介

1) I2C 总线特性介绍

在现代电子产品开发过程中,为了简化系统,提高系统的可靠性,缩产品开发周期,增加硬件构成的灵活性,推出了一种高效、可靠、方便的 I2C串行总线。线制的 I2C 串行总线使得各电路单只需要简单的两线连接,总线接口都集成在器件中, 可实现电路系统的模块化、标准化设计。在 I2C 总线上,各单元电路相互之间没有其它连线,常用的单元电路基本上与系统电路无,极易形成自己的标准化、模块化设计。

2) I2C 标准模式总线规范

IIC总线可通过串行数据(SDA)和串行时钟(SCL)线与总线上的任何一个器件连接,每 个器件都有一个唯一的地址,都可以作为一个发送器或接收器。各器件在执行数据传输时也都可被看做是一个主机或从机。

发送器 :本次传送中发送数据(不包括地址和命令)到总线的器件。

接收器次传送中从总线接收数据(不包括地址和命令)的器件。

主机: 初始化发送、产生时钟信号和终止发送的器件,它可以是发送器或接收器, 主机通常是微控制器。

从机: 被主机寻址的器件,它可以是发送器或接收器。

IIC总线是一个多主机的总线,可以连接多个能控制总线的器件到总线。当两个以上能控制总线的器件同时发生传输时,只能有一个器件能真正控制总线而成为主机,并使报文被破坏,这个过程叫做仲裁。与此同时,能使多个能控制总线的器件产生同步的时钟信号。

SDA 和 SCL 都是双向线路,连接到总线的器件的输出级必须漏极开路或集电极开路,该器件通过一个电流源或上拉电阻连接到正的电源电压,实现线与功能。当总线空闲时,这两条线路都是高电平。标准模式下,数据传输的速度为 0~100kb/s

3) 位传输

I2C 总线上每传输一个数据位必须产生一个时钟脉冲 。

(1) 数据的有效性。SDA 线上的数据必须在时钟线 SCL 的高电平周期保持稳定, 数据线的电平状态只有在 SCL 线的时钟信号低电平时才能改变,如图3‑34所示。在标准模式下,高低电平宽度必须大 4.7us

334 I2C 总线控制的位传输

(2) 起始和停止条件。在 I2C 总线中,唯一违反上述数据有效性的是起始(S)和停止(P)条件,图3‑35所示。

起始条件(重复起始条件):在 SCL 线是高电平时,SDA 线从高电平向低电平切换。

停止条件: 在 SCL 线是高电平时,SDA 线由低电平向高电平切换。

335 起始位和停止条件

起始和停止条件一般由主机产生。起始条件是作为一次传送的开始,在起始条件后,总线被认为处于忙的状态。停止条件是作为次传送的结束,在停止条件的某段时间后,总线被认为再次处于空闲状态。重复起始条件既作为上次传送的结束,也作为下次传送的开始。

4) 数据传输

(1) 字节格式。发送到 SDA 线上的每个字节必须为 8 位。每次传输可以发送的字节数量不受限制,每个字节后必须跟一个应答位。首先传输的是数据的最高位(MSB), 如图3‑36所示。

336 I2C 总线的数据传输

(2) 应答。相应的应答时钟脉冲由从机产生。在应答的时钟脉冲期间,发送器释放 SDA线(高),与此同时,接收器必须将 SDA 线拉低 ,使它在这个时钟脉冲的高电平期间保持稳定的低电平,如图 2.13 时钟信号SCL 的第 9 位。

一般说来,被寻址匹配的从机或可继续接收下一字节的接收器将产生一个应答。若作为发送器的主机在发送完一个字节后没有收到应答位(或收到一个非应答位),或作为接收器的主机没有发送应答位(或发送一个非应答位),那么主机必须产生一个停止条件或重复起始条件来结束木次传输。若从机(接收器)不能接收更多的数据字节,将不产生这个应答位 :主机(接收器)在接收完最后一个字节后不产生应答, 通知从机(发送器)数据结束。

5) 仲裁与时钟发生

(1) 同步。时钟同步是通过各个能产生时钟的器件“ 线与” 连接到 SCL 线上来实现的,各器件可能都有自己独立的时钟 各个时钟信号的频率、周期、相位和占空比可能都不相同,由于 “线与”的结果,在 SCL 线上产生的实际时钟的低电平宽度由低电平持续时间最长的器件决定,他高电平宽度由高电平持续时间最短的器件决定。

(2) 仲裁。总线空闲时,多个主机同时启动传输,可能检测到不止一个主机满足起始条件,而同时获得主机权,这样就要进行仲裁。SCL 线是高电平时,仲裁在 SDA 线发生,当其他主机发送低平时,发送高电平的主机将丢失仲裁,因为总线上的电平与它自己的电平不同。

仲裁可以持续多位,它的第个阶段是比较地址位,如果每个主机都尝试寻址相同的器件,仲裁会继续比较数据位,或者比较响应位。因为 I2C 总线的地址和数据信息赢得仲裁的主机决定,所以在仲裁过程中不会丢失信息。

(3) 用时钟同步机制作伪握手。器件可以快速接收数据字节,但可能需要多时间保存接收到的字节或准备一个要发送的字节。此时,这个器件可以使 SCL 线保持低电平, 迫使与之交换数据的器件进入等待状态,直到准备好下一字节的发送或接收 。

6) 传输协议

(1) 寻址字节。主机产生起始条件后,发送的第一个字节为寻址字节。该字节的头 7(7 位)为从机地址,最低位(LSB)决定报文的方向,“0”表示主机写信息到从机, “1”表示主机读从机中的信息,图3‑37所示。当发送了一个地址后,系统中的每个器件都将 7 位与它已的地址比较。如果一样,器件会应答主机的寻址,至是从机-接收器还是从机-发送器都由R / W 位决定。

337 起始条件后的笫一个字节

从机地址由一个固定的部分和一个可编程的部分构成。例如,某些器件有 4 个固定的(高4位)和 3 个可编程的地址位(低 3 位),那么同一总线上共可以连接 8 个相同的器件。I2C 总线委员会协调 I2C 地址的分配,保留了 2 组 8 位地址(0000XXX 和 1111XXX), 这 2组地址的用途可查阅有关资料。

(2) 传输格式。主机产生起始条件后,发送一个寻址字节,收到应答跟着就是数椐传输,数据传输一般由主机产生的停止位来终止。但是,如果主机希望在总线上通信,它以产重复起始条件(Sr)且寻址一个从机,而不是首先产一个停止条件。在这种传输中,可能有不同的读/写格式结合。

数据的传输格式有以下三种 :

主机-发送器发送数据到从机-接收器,见图 2.15, 寻址字节的 “R/`W” 位为 0,数据传输的方向不改变。

寻址字节后,主机-接收器立即读从机-发送器中的数据,见图3‑38,寻址字节的R/`W位为1。在第一次从机产生响应时, 主机-发送器变成主机-接收器,从机-接收器变成从机-发送器。之后,数据由从机发送,主机接收,每个应答由主机产生,时钟信号 CLK仍由主机产生。若主机要终止本次传输,则发送一个非应答信号(`A /A),接着主机产生停止条件。

338 寻址字节后,主机-接收器立即读数据

复合格式,见图3‑39。传输改变方向的时候,起始条件和从机地址都会被重复, 但位取反。如果主机-接收器发送一个重复起始条件,它之前应该要发送一个非应答信号(`A/A )。

339 复合模式

1.1.224LC04 存储芯片简介

24LC04 是MicroChip公司生产的低功耗 CMOS 串行 E2PR0M, 采用 12C 总线,提供 256 bytes(2 x 256 x 8)电可擦除、对编程只读存储器,工作电源电压范围 2.5~5.5 V, 擦写次数大于10000 次,写入速度小于 2ms。

一、 24LC04 存储芯片引脚功能及封装形式

24LC04 存储芯片引脚功能介绍如3‑2,24LC04存储芯片采用常用的, 8-lead or 14-lead SOIC 或者DIP封装,引脚排列如图3‑40所示。

32 24LC04引脚配置表

引脚名称

功能

A0~A2

地址输入端

SDA

串行数据

SCL

串行时钟输入

WP

写保护

NC

空连接

340 24LC02芯片引脚封装

4. AT24C02 存储芯片内部结构

AT24C02 存储芯片内部结构如图3‑41所示。

341 AT24C02 芯片内部结构

各信号功能介绍如下:

串行时钟(SCL): 当 SCL 输入在上升沿时,数据写入 E2PROM 设备;当 SCL 输入在下降沿时,数据被读出 E2PROM 设备。

串行数据(SDA): SDA 引脚是漏极开路输出,且可以与任何数目的漏极开路或集电极开路输出 “线或” (wire-Ored)连接。

设备/页面地址(A2、A1、A0) :A2、A1、A0脚是No Internal Connection设

写保护(WP): AT24C02有一个写保护引脚,提供硬件数据保护。当写保护脚接地时,允许正常的读/写操作;当写保护脚接 Vcc 时,写保护作用生效,操作如3‑3所示。

33 写保护

WP引脚状态

被保护数据单元

24C02A

24C04A

接电源

前面的一半(1KB)单元

前面一半(2KB)单元

接地

普通读写操作

5. AT24C02 存储芯片与 FPGA 的连接电路

AT24C02 存储芯片电路连接如图3‑42所示,它使用串行 lIC 总线,只需要使用串行数据线 SDA 和串行时钟线 SCL 即可完成对串行 AT24C02 芯片的读/写操作。

342 存储芯片电路连接图

6.设备地址(硬件地址)。

任何 I2C 的从机设备都有设备地址(硬件地址)。如图3‑42所示, 其中的 A0~A2 就是设备地址了,在这里,图3‑42 中的设备地址是 A0~A2 — 3'b000(接地关系)。此外设备地址是 2 的 3 次幂(23),这也表示 I2C总线里最大的从机(设备)数是 8

7.IIC总线时序FPGA实现分析

343 IIC 总线与 IIC 设备

图3‑43是 IIC 总线与 IIC 设备常见的示意图。理想上,一条 IIC 总线允许千万 IIC 设备占据在上 ... 物理下,一条 IIC 总监究竟允许多少 IIC 设备占据其中必须根据设备地址的长度。默认下,设备地址为八位宽,因此设备地址也称为设备字节。设备地址的高四位,即[7..4]记录硬件 ID,接续三位即 [3..1] 则记录硬件地址,最后一位则是设备的访问方向。结果如3‑4所示:

34 设备地址的位分配

[7]

[6]

[5]

[4]

[3]

[2]

[1]

[0]

硬件ID

硬件地址

访问方向

所谓硬件 ID 就是 IIC 设备的辨识 ID,硬件 ID 会随着厂商还有设备的种类而有所改变。开发板上的 IIC 设备是某厂商的 IIC 储存器,即 24LC04,硬件 ID 为 4’b1010。至于硬件地址就是 IIC 设备在总线上辨识地址,默认下为 3 位,即同类的 IIC 设备在同一条 IIC总线上仅允许占据 8 个而已。然而,开发板上的 24LC04 为 3’b000。最后的访问方向位则是主机用来通知从机,此刻的访问目的是读还是写。

总结来说,设备地址除了访问方向以外,前七位一般都是固定的,例如开发板的 IIC 储存器 24LC04,设备地址就是 8’b1010_000_×。

344 24LC04 的写操作(主机视角)

IIC 总线的时序,感觉上一组完成的操作宛如是一堆拼图。如图3‑44所示,那是 24LC04的写操作,时序先填上为起始位,再来是设备地址,余下是应答位,随之是数据地址,然后又是应答位,接着是写如数据,再一次应答位,最后挂上结束位以示一次性的写操作已经完成。那么,写操作的经过如下所示:

(一) 主机发送起始位;

(二) 主机发送设备地址(写);

(三) 等待从机应答;

(四) 主机发送数据地址;

(五) 等待从机应答;

(六) 主机发送数据;

(七) 等待从机应答;

(八) 主机发送结束位。

稍微注意一下设备地址的最低位,笔者稍微用蓝色将其高亮。由于此刻是写操作,所以设备地址的访问方向是“写”,所以访问方向位设置为 0。

345 24LC04 的读操作(主机视角)

图3‑45是 24LC04 的读时序,同样它也是由一堆“拼图”组合而成。相较写操作,读操作不仅多了许多“拼图”,而且途中也改变访问方向。那么,读操作的经过如下所示:

(一) 主机发送起始位;

(二) 主机发送设备地址(写);

(三) 等待从机应答;

(四) 主机发送数据地址;

(五) 主机发送起始位;

(六) 主机发送设备地址(读);

(七) 等待从机应答;

(八) 主机读取数据;

(九) 从机没有应答(主机无视应答);

(十) 主机发送结束位。

I2C总线上数据的传输速率在标准模式下可达100kb/s,在快速模式下可达400kb/s,在高速模式下可达3.4Mb/s。实验十六会以 400Khz 的速率作为标准。连接到总线的接口数量由总线电容是400pF的限制决定。在此,实验会以 400Khz 的速率作为标准。

在进行IIC时序分析时,必须要考虑芯片的时序,见下表:

346 芯片 24LC04 的时序表

将上表进行解读如下:

35 各种时序参数( 50Mhz 量化)

Clock Frequency,既是频率也是速率,在此是 400Khz。

Clock High Time,既 SCL 信号保持高电平所需的最小时间。

Clock Low Time,既 SCL 信号保持低电平所需的最小时间。

Rise Time,既信号由底变高所需最大的时间。

Fall Time,既信号又高变低所需最小的时间。

Start Hold Time,既起始位所需最小的保持时间。

Start Setup Time,既起始位所需最小的建立时间。

Data Input Hold Time,既数据位所需最小的保持时间。

Data Input Setup Time,既数据位所需最小的建立时间。

Stop Setup Time,既结束位所需的最小保持时间。

Ouput Valid From Clock,既数据位经时钟沿触发以后的有效时间。

Bus Free Time,既释放总线的最小时间。

IIC 总线是一种串行传输协议,既有时钟信号 SCL,还有数据信号 SDA。Clock Frequency表示 SCL 信号的频率, Clock High Time 表示 SCL 信号保持高电平所需的最小时间,Clock Low Time 则表示 SCL 信号保持低电平所需的最小的时间。

至于 Rise Time 与 Fall Time 表示, SCL 信号还有 SDA 信号由高变低或者由低变高时所需的最小时间,即上山与下山时间。Hold Time 与 Setup Time 是用来评估数据是否成功打入寄存器的时序参数,算是典型中的典型。Setup Time 表示建立时间,即数据写入寄存器之前所需的稳定时间;反之, Hold Time 则是保持时间,即数据打入寄存器之后所需的稳定时间。只要两者得到满足,那么数据的寄存活动就得到确保。

1.1.324LC04存储芯片的FPGA控制

Start 是 IIC 总线的起始位, Stop 是 IIC 总线的结束位, Data 是 IIC 总线的数据位,为了确保三者成功写入从机, Setup Time 与 Hold Time 必须得到满足。Ouput Valid FromClock 是关系数据位的时序参数,还有 Bus Free Time 是关系结束位的时序参数。此外, 为了简化时序,将各种参数的实际时间转换为 50Mhz 量化以后的结果。对此, Verilog 可以这样表示,结果如代码3‑13所示:

代码313 将各种参数的实际时间转换为 50Mhz 量化结果

1.parameter FCLK = 10'd125, FHALF = 10'd62, FQUARTER = 10'd31; //(1/400E+3)/(1/50E+6) 

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;  

4.parameter WRFUNC1 = 5'd7;  

5.parameter WRFUNC2 = 5'd9, RDFUNC = 5'd19;  

代码3‑13所示, FCLK 表示 400Khz 的周期, FHALF 表示 1/2 周期, FQUARTER 表示 1/4 周期。

347 起始位

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

代码314 起始位

1.begin  

2.isQ = 1;  

3.rSCL <= 1'b1;  

4.                        

5.if( C1 == 0 ) rSDA <= 1'b1;   

6.    lse if( C1 == (TR + THIGH) ) rSDA <= 1'b0;    

7.                        

8.if( C1 == (FCLK) -1) begin C1 <= 10'd0; i <= i + 1'b1; end  

9.    else C1 <= C1 + 1'b1;  

10.end  

代码3‑14所示,第 2 行的 isQ = 1 表示设置 SDA 为输出状态(即时结果),第 3 行则表示 SCL 一直持续拉高状态,第 5~6行表示 C1 为 0 的时候 SDA 拉高,直到 C1 为TR+THIGH 才拉低 SDA。第 8~9 行表示一个步骤所逗留的时间。

348 结束位

图 16.5 是结束位的时序图, IIC 设备的操作好坏一般都取决结束位。保险起见, SCL 与SDA 都事先拉低 1/4 周期,紧接着 SCL 会拉高 TR+TSU_STO(或者 1/2 周期),最后又保持高电平 1/2 周期。反之, SDA 会拉低 1/2 周期,随之拉高 TR+THIGH(或者 1/2周期)。对此, Verilog 可以这样表示,结果如代码3‑15所示:

代码315 结束位

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  

代码3‑15所示,第 2 行表示 SDA 为输出状态(即时),第 4~5 行表示 C1 为 0 拉高SCL, C1 为 1/4 周期就拉高。第 7~8 行表示, C1 为 0 拉低 SDA, C1 为 1/4 周期 + TR +TSU_STO 就拉高 SDA。第 7~8 行表示该步骤所逗留的时间。

349 释放总线

此外,结束位还有 Bus Free Time 这个时序参数,IIC 总线在闲置的状态下 SCL 与 SDA等信号都持续高电平。主机发送结束位以示结束操作,然而主机持续拉高 SCL 信号与SDA 信号 TBUF 以示总线释放。TBUF 的有效时间从 SCL 信号与 SDA 信号拉高那一刻开始算起

根据3‑5所示, TBUF 是 65 个时钟,结果如图3‑49所示, SDA 信号拉高之后, SCL与 SDA 信号只要持续保持 1/2 周期(即 62 个时),基本上就能满足 TBUF。如果笔者是一位紧密控时狂人,可能无法接受这样的结果,因为满足 TBUF 少了 3 个时钟,为此代码3‑15需要更动一下:

代码316 代码3-15更改

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

2.    else C1 <= C1 + 1'b1;  

代码3‑16所示,笔者为第 10 行写下 +3 表示该步骤多逗留 3 个时钟,以致满足 TBUF。

350 物理IIC传输协议

不管对象是设备地址,数据地址,写入数据,读出数据,还是应答位,大伙都视为数据位。IIC 总线类似其他传输协议,它有时钟信号也有上升沿与下降沿。如图3‑50所示,SCL 信号的下降沿导致设备设置(更新)数据,上升沿则是锁存(读取)数据。期间,TF+TLOW 表示时钟信号的前半周期, TR+THIGH 则表示后半周期。此外,为了确保数据成功打入寄存器,数据被上升沿锁存哪一刻起, TSU_DAT 还有 THD_DAT 必须得到满足。

351 数据位更新有效

除此之外,为了确保数据有效被更新,我们也必须确保 TAA 得到满足,结果如图3‑51所示。理解完毕以后,我们就可以开始学习,写一字节数据与读一字节数据,还有应答位。

352 写一字节

IIC 总线一般都是一个字节一个字节读写数据,如图3‑52所示,那是写一字节的理想时序图,一字节数据是从最高位开始写起。对此, Verilog 可以这样描述,结果如代码3‑17所示:

代码317 写一字节代码

1.7,8,9,10,11,12,13,14:  

2.begin  

3.    isQ = 1'b1;  

4.      rSDA <= D1[14-i];  

5.        

6.      if( C1 == 0 ) rSCL <= 1'b0;  

7.     else if( C1 == (TF + TLOW) ) 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  

代码3‑17所示,第 1 行有 8 个步骤,表示写一个字节。第 3 行 isQ 为 1 表示 SDA 为输出状态。第 4 行表示从最高位开始更新 SDA 的数据位。第 5~6 行表示, C1 为 0 拉低SCL, C1 为 TF+TLOW 则拉高 SCL。第 7~8 行表示该步骤逗留一个周期的时间。

353 应答位

应答位是从机给予主机的回答, 0 为是 1 为否。然而,从旁观看,读取应答位也是读取一位数据位。当主机完成写入一个字节或者读取一个字节数据的时候,从机都会产生应答位。主机拉低 SCL 那刻,从机便会发送应答位,然后主机会借由上升沿读取应答位。如图3‑53所示,上升沿会产生在 TF + TLOW 之后,也是 1/2 周期。对此, Verilog 可以这样表示,结果如代码3‑18所示:

代码318 应答位代码

1.begin  

2.    isQ = 1'b0;  

3.    if( C1 == FHALF ) isAck <= SDA;  

4.        

5.      if( C1 == 0 ) rSCL <= 1'b0;  

6.      else if( C1 == FHALF ) rSCL <= 1'b1;  

7.        

8.      if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end  

9.      else C1 <= C1 + 1'b1;   

10.end  

代码3‑18所示,第 2 行表示 SDA 为输入状态。第 5~6 行表示, C1 为 0 拉低 SCL,C1 为 1/2 周期则拉高 SCL。第 3 行表示, C1 为 1/2 周期的时候读取应答位。第 8~9 行表示该步骤逗留 1 个周期的时间。

354 读一字节

所谓读一字节数据就是重复读取 8 次应答位。如图 16.11 所示, SCL 的下降沿导致从机更新数据,然后主机在 SCL 的上升沿读取数据。此外,从机也会由高至低更新数据位。至于 Verilog 则可以这样表示,结果如代码3‑19所示:

代码319 读一字节代码

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     

代码3‑19所示,第 1 行表示读取一字节。第 3 行表示 SDA 为输入状态,第 7~8 行表示, C1 为 0 拉低 SCL, C1 为 1/2 周期则拉高 SCL。第 4 行表示, C1 为 1/2 周期的时候读取数据,而且数据位由高至低存入 D1。第 10~11 行表示该步骤逗留一个周期的时间。

355 第二次起始位

我们知道主机向从机读取数据的时候,它必须改变设备地址的方向,因此读操作又第二次起始位。如图3‑55所示,感觉上第二次起始位也是第一次起始位,不过为了促使改变方向成功,第二次起始位相较第一次起始位的前后都拉低 1/4 周期。对此, Verilog 可以这样表示,结果如代码3‑20所示:

代码320 第二次起始位代码

1.begin  

2.    isQ = 1'b1;  

3.       

4.    if( C1 == 0 ) rSCL <= 1'b0;  

5.      else if( C1 == FQUARTER ) rSCL <= 1'b1;  

6.      else if( C1 == (FQUARTER + TR + TSU_STA + THD_STA + TF)  ) rSCL <= 1'b0;  

7.        

8.     if( C1 == 0 ) rSDA <= 1'b0;   

9.      else if( C1 == FQUARTER ) rSDA <= 1'b1;  

10.      else if( C1 == ( FQUARTER + TR + THIGH) ) rSDA <= 1'b0;    

11.        

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

13.      else C1 <= C1 + 1'b1;  

14.end  

代码3‑20所示,第 2 行表示 SDA 为输出状态。第 4~6 行表示, C1 为 0 拉低 SCL,C1 为 1/4 周期拉高 SCL, C1 为 1/4 周期 + TR + TSU_STA + THD_STA + TF 便拉低SCL。第 8~10 行表示, C1 为 0 拉低 SDA, C1 为 1/4 周期拉高 SDA, C1 为 1/4 周期 + TR+ THIGH 便拉低 SDA。第 12~13 行表示该步骤停留一个周期的时间。

程序设计

356 IIC实验建模图

图3‑56 IIC实验建模图,组合模块 iic_demo 内容包含 IIC 储存模块,核心操作还有 SMG 基础模块。首先核心操作会将数据纯如 IIC 储存模块,然后又从中读取,完后再将读出的数据驱动 SMG 基础模块

iic_savemod.v

357 IIC 储存模块的建模图

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

代码321 24LC04存储代码

1.//****************************************************************************//  

2.//# @Author: 碎碎思  

3.//# @Date:   2019-04-08 21:03:50  

4.//# @Last Modified by:   zlk  

5.//# @WeChat Official Account: OpenFPGA  

6.//# @Last Modified time: 2014-05-12 13:47:13  

7.//# Description:   

8.//# @Modification History: 2014-05-12 13:47:13  

9.//# Date                By             Version             Change Description:   

10.//# ========================================================================= #  

11.//# 2014-05-12 13:47:13  

12.//# ========================================================================= #  

13.//# |                                                                       | #  

14.//# |                                OpenFPGA                               | #  

15.//****************************************************************************//  

16.module iic_savemod  

17.(  

18.    input CLOCK, RESET,  

19.     output SCL,  

20.     inout SDA,  

21.     input [1:0]iCall,  

22.     output oDone,  

23.     input [7:0]iAddr,  

24.     input [7:0]iData,  

25.     output [7:0]oData  

26.);  

27.     parameter FCLK = 10'd125, FHALF = 10'd62, FQUARTER = 10'd31; //(1/400E+3)/(1/50E+6)  

28.     parameter THIGH = 10'd30, TLOW = 10'd65, TR = 10'd15, TF = 10'd15;  

29.     parameter THD_STA = 10'd30, TSU_STA = 10'd30, TSU_STO = 10'd30;  

30.     parameter WRFUNC1 = 5'd7;  

31.     parameter WRFUNC2 = 5'd9, RDFUNC = 5'd19;  

32.       

33.     reg [4:0]i;  

34.     reg [4:0]Go;  

35.    reg [9:0]C1;  

36.     reg [7:0]D1;  

37.     reg rSCL,rSDA;  

38.     reg isAck, isDone, isQ;  

39.       

40.     always @ ( posedge CLOCK or negedge RESET )  

41.         if( !RESET )  

42.              begin  

43.                    { i,Go } <= { 5'd0,5'd0 };  

44.                     C1 <= 10'd0;  

45.                     D1 <= 8'd0;  

46.                     { rSCL,rSDA,isAck,isDone,isQ } <= 5'b11101;  

47.                end  

48.          else if( iCall[1] )  

49.              case( i )  

50.                      

51.                    0: // Start  

52.                     begin  

53.                          isQ = 1;  

54.                          rSCL <= 1'b1;  

55.                            

56.                         if( C1 == 0 ) rSDA <= 1'b1;   

57.                          else if( C1 == (TR + THIGH) ) rSDA <= 1'b0;    

58.                            

59.                          if( C1 == (FCLK) -1) begin C1 <= 10'd0; i <= i + 1'b1; end  

60.                          else C1 <= C1 + 1'b1;  

61.                     end  

62.                        

63.                     1: // Write Device Addr  

64.                     begin D1 <= {4'b1010, 3'b000, 1'b0}; i <= 5'd7; Go <= i + 1'b1; end  

65.                       

66.                     2: // Wirte Word Addr  

67.                     begin D1 <= iAddr; i <= WRFUNC1; Go <= i + 1'b1; end  

68.                      

69.                    3: // Write Data  

70.                     begin D1 <= iData; i <= WRFUNC1; Go <= i + 1'b1; end  

71.                       

72.                     /*************************/  

73.                       

74.                     4: // Stop  

75.                     begin  

76.                         isQ = 1'b1;  

77.                            

78.                         if( C1 == 0 ) rSCL <= 1'b0;  

79.                         else if( C1 == FQUARTER ) rSCL <= 1'b1;   

80.                            

81.                          if( C1 == 0 ) rSDA <= 1'b0;  

82.                          else if( C1 == (FQUARTER + TR + TSU_STO ) ) rSDA <= 1'b1;  

83.                            

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

85.                          else C1 <= C1 + 1'b1;   

86.                     end  

87.                       

88.                     5:  

89.                     begin isDone <= 1'b1; i <= i + 1'b1; end  

90.                       

91.                     6:   

92.                     begin isDone <= 1'b0; i <= 5'd0; end  

93.                       

94.                     /*******************************/ //function  

95.                       

96.                     7,8,9,10,11,12,13,14:  

97.                     begin  

98.                         isQ = 1'b1;  

99.                          rSDA <= D1[14-i];  

100.                            

101.                          if( C1 == 0 ) rSCL <= 1'b0;  

102.                          else if( C1 == (TF + TLOW) ) rSCL <= 1'b1;   

103.                            

104.                          if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end  

105.                          else C1 <= C1 + 1'b1;  

106.                     end  

107.                       

108.                     15: // waiting for acknowledge  

109.                     begin  

110.                         isQ = 1'b0;  

111.                         if( C1 == FHALF ) isAck <= SDA;  

112.                            

113.                          if( C1 == 0 ) rSCL <= 1'b0;  

114.                          else if( C1 == FHALF ) rSCL <= 1'b1;  

115.                            

116.                          if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end  

117.                          else C1 <= C1 + 1'b1;   

118.                     end  

119.                       

120.                     16:  

121.                     if( isAck != 0 ) i <= 5'd0;  

122.                     else i <= Go;   

123.                       

124.                     /*******************************/ // end function  

125.                          

126.                endcase  

127.                  

128.          else if( iCall[0] )   

129.              case( i )  

130.                  

131.                    0: // Start  

132.                     begin  

133.                         isQ = 1;   

134.                         rSCL <= 1'b1;  

135.                            

136.                         if( C1 == 0 ) rSDA <= 1'b1;   

137.                          else if( C1 == (TR + THIGH) ) rSDA <= 1'b0;    

138.                            

139.                          if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end  

140.                          else C1 <= C1 + 1'b1;  

141.                     end  

142.                        

143.                     1: // Write Device Addr  

144.                     begin D1 <= {4'b1010, 3'b000, 1'b0}; i <= 5'd9; Go <= i + 1'b1; end  

145.                       

146.                     2: // Wirte Word Addr  

147.                     begin D1 <= iAddr; i <= WRFUNC2; Go <= i + 1'b1; end  

148.                      

149.                     3: // Start again  

150.                     begin  

151.                         isQ = 1'b1;  

152.                            

153.                         if( C1 == 0 ) rSCL <= 1'b0;  

154.                          else if( C1 == FQUARTER ) rSCL <= 1'b1;  

155.                          else if( C1 == (FQUARTER + TR + TSU_STA + THD_STA + TF)  ) rSCL <= 1'b0;  

156.                            

157.                          if( C1 == 0 ) rSDA <= 1'b0;   

158.                          else if( C1 == FQUARTER ) rSDA <= 1'b1;  

159.                          else if( C1 == ( FQUARTER + TR + THIGH) ) rSDA <= 1'b0;    

160.                            

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

162.                          else C1 <= C1 + 1'b1;  

163.                     end  

164.                       

165.                     4: // Write Device Addr ( Read )  

166.                     begin D1 <= {4'b1010, 3'b000, 1'b1}; i <= 5'd9; Go <= i + 1'b1; end  

167.                      

168.                     5: // Read Data  

169.                     begin D1 <= 8'd0; i <= RDFUNC; Go <= i + 1'b1; end  

170.                       

171.                     6: // Stop  

172.                     begin  

173.                         isQ = 1'b1;  

174.                       

175.                    if( C1 == 0 ) rSCL <= 1'b0;  

176.                         else if( C1 == FQUARTER ) rSCL <= 1'b1;   

177.                            

178.                          if( C1 == 0 ) rSDA <= 1'b0;  

179.                          else if( C1 == (FQUARTER + TR + TSU_STO) ) rSDA <= 1'b1;                           

180.                            

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

182.                          else C1 <= C1 + 1'b1;   

183.                     end  

184.                       

185.                     7:  

186.                     begin isDone <= 1'b1; i <= i + 1'b1; end  

187.                       

188.                     8:   

189.                     begin isDone <= 1'b0; i <= 5'd0; end  

190.                       

191.                     /*******************************/ //function  

192.                      

193.                     9,10,11,12,13,14,15,16:  

194.                     begin  

195.                         isQ = 1'b1;  

196.                            

197.                          rSDA <= D1[16-i];  

198.                            

199.                          if( C1 == 0 ) rSCL <= 1'b0;  

200.                         else if( C1 == (TF + TLOW) ) rSCL <= 1'b1;   

201.                            

202.                          if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end  

203.                          else C1 <= C1 + 1'b1;  

204.                     end  

205.                     

206.                     17: // waiting for acknowledge  

207.                     begin  

208.                         isQ = 1'b0;  

209.                           

210.                          if( C1 == FHALF ) isAck <= SDA;  

211.                            

212.                          if( C1 == 0 ) rSCL <= 1'b0;  

213.                          else if( C1 == FHALF ) rSCL <= 1'b1;  

214.                            

215.                          if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end  

216.                          else C1 <= C1 + 1'b1;   

217.                     end  

218.                       

219.                     18:  

220.                     if( isAck != 0 ) i <= 5'd0;  

221.                     else i <= Go;  

222.                       

223.                     /*****************************/  

224.                       

225.                     19,20,21,22,23,24,25,26: // Read  

226.                     begin  

227.                         isQ = 1'b0;  

228.                         if( C1 == FHALF ) D1[26-i] <= SDA;  

229.                            

230.                          if( C1 == 0 ) rSCL <= 1'b0;  

231.                          else if( C1 == FHALF  ) rSCL <= 1'b1;   

232.                            

233.                          if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end  

234.                          else C1 <= C1 + 1'b1;  

235.                     end        

236.                       

237.                     27: // no acknowledge  

238.                     begin  

239.                         isQ = 1'b1;  

240.                         //if( C1 == 100 ) isAck <= SDA;  

241.                            

242.                          if( C1 == 0 ) rSCL <= 1'b0;  

243.                          else if( C1 == FHALF ) rSCL <= 1'b1;  

244.                            

245.                          if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= Go; end  

246.                          else C1 <= C1 + 1'b1;   

247.                     end  

248.                       

249.                     /*************************************/ // end fucntion  

250.                  

251.                endcase  

252.          

253.     /***************************************/  

254.      

255.    assign SCL = rSCL;  

256.     assign SDA = isQ ? rSDA : 1'bz;      

257.    assign oDone = isDone;  

258.     assign oData = D1;  

259.      

260.    /***************************************/     

261.                  

262.endmodule  

实验:实验结果。

编辑完毕便下载程序,如果数码管从左至右显示 “ABCDEF” ,那么表示实验成功。

细节:100Khz 与 400Khz 速率
IIC 储存器——24LC04 有两种速率供我们选择, 100Khz 是比较规格的速率,因为 SCL有 50%的占空比,反之 400Khz 则是比较不规格的速率,因为 SCL 的前半周期为 36%,后半周期为 64%。审美而言, 100Khz 比 400Khz 美丽 ... 速度而言, 400Khz 比 100Khz快 4 倍。100Khz 的时序参数还有 50Mhz 量化结果如表 16.3 所示:

36 100Khz相关的时序参数( 50Mhz 量化)

Verilog 的常量声明如代码3‑22所示:

代码322 Verilog 的常量声明如代码

1.parameter FCLK = 10'd500, FHALF = 10'd250, FQUARTER = 10'd125;  

2.parameter THIGH = 10'd200, TLOW = 10'd235, TR = 10'd50, TF = 10'd15;  

3.parameter THD_STA = 10'd200, TSU_STA = 10'd235, TSU_STO = 10'd200;  



    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多