首页 » 智能 » 正点原子开拓者FPGA开拓板资料连载第二十八章EEPROM读写测尝尝验_暗记_地址

正点原子开拓者FPGA开拓板资料连载第二十八章EEPROM读写测尝尝验_暗记_地址

雨夜梧桐 2024-12-10 08:44:09 0

扫一扫用手机浏览

文章目录 [+]

2)摘自《开拓者FPGA开拓指南》关注官方微旗子暗记"大众年夜众号,获取更多资料:正点原子

3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-13912-1-1.html

正点原子开拓者FPGA开拓板资料连载第二十八章EEPROM读写测尝尝验_暗记_地址 正点原子开拓者FPGA开拓板资料连载第二十八章EEPROM读写测尝尝验_暗记_地址 智能

第二十八章 EEPROM读写测尝尝验

正点原子开拓者FPGA开拓板资料连载第二十八章EEPROM读写测尝尝验_暗记_地址 正点原子开拓者FPGA开拓板资料连载第二十八章EEPROM读写测尝尝验_暗记_地址 智能
(图片来自网络侵删)

EEPROM是一种用于打算机系统的非易失落性存储器,也常在嵌入式领域中作为数据的存储设

备,在物联网及可穿着设备等须要存储少量数据的场景中也有广泛运用。
本章我们学习EEPROM

的读写操作并进行EEPROM读写实验。

本章包括以下几个部分:

28.1 EEPROM简介

28.2 实验任务

28.3 硬件设计

28.4 程序设计

28.5 下载验证

EEPROM简介

EEPROM (Electrically Erasable Progammable Read Only Memory,E2PROM)即电可擦除

可编程只读存储器,是一种常用的非易失落性存储器(掉电数据不丢失),EEPROM有多种类型的

产品,我们开拓者FPGA开拓板上利用的是ATMEL公司生产的AT24C系列的AT24C64这一型号。

AT24C64具有高可靠性,可对所存数据保存100年,并可多次擦写,擦写次数达一百万次。

一样平常而言,对付存储类型的芯片,我们比较关注其存储容量。
我们这次实验所用的

AT24C64存储容量为64Kbit,内部分成256页,每页32字节,共有8192个字节,且其读写操作都

因此字节为基本单位。
可以把AT24C64看作一本书,那么这本书有256页,每页有32行,每行有

8个字,统共有256328=65536个字,对应着AT24C64的641024=65536个bit。

知道了AT24C64的存储容量,就知道了读写的空间大小。
那么我们该如何对AT24C64进行读

写操作呢?

由于AT24C64采取两线串行接口的双向数据传输协议——I2C协议实现读写操作,以是我们

有必要理解一下I2C协议。

I2C即Inter-Integrated Circuit(集成电路总线),是由Philips半导体公司(现在的NXP

半导体公司)在八十年代初设计出来的一种大略、双向、二线制总线标准。
多用于主机和从机

在数据量不大且传输间隔短的场合下的主从通信。
主机启动总线,并产生时钟用于传送数据,

此时任何吸收数据的器件均被认为是从机。

I2C总线由数据线SDA和时钟线SCL构成通信线路,既可用于发送数据,也可吸收数据。

主控与被控IC之间可进行双向数据传送,数据的传输速率在标准模式下可达100kbit/s,在快

速模式下可达400kbit/s,在高速模式下可达3.4Mbit/s,各种被控器件均并联在总线上,通过

器件地址(SLAVE ADDR,详细可查器件手册)识别。
我们开拓者I2C总线物理拓扑构造如下图

所示。

图 28.1.1 开拓者I2C总线物理拓扑构造图

图中的I2C_SCL是串行时钟线,I2C_SDA是串行数据线,由于I2C器件一样平常采取开漏构造与

总线相连,以是I2C_SCL和I2C_SDA均需接上拉电阻,也恰是以,当总线空闲时,这两条线路都

处于高电平状态,当连到总线上的任一器件输出低电平,都将使总线拉低,即各器件的SDA及

SCL都是“线与”关系。

I2C总线支持多主和主从两种事情办法,常日事情在主从事情办法,我们的开拓板就采取

主从事情办法。
在主从事情办法中,系统中只有一个主机,其它器件都是具有I2C总线的外围

从机。
在主从事情办法中,主机启动数据的发送(发出启动旗子暗记)并产生时钟旗子暗记,数据发送

完成后,发出停滞旗子暗记。

I2C总线构造虽然大略,利用两线传输,然而要实现器件间的通信,须要通过掌握SCL和SDA

的时序,使其知足I2C的总线传输协议,方可实现器件间的数据传输。
那么I2C协议的时序是怎

样的呢?

在I2C器件开始通信(传输数据)之前,串行时钟线SCL和串行数据线SDA线由于上拉的原

因处于高电平状态,此时I2C总线处于空闲状态。
如果主机(此处指FPGA)想开始传输数据,

只需在SCL为高电平时将SDA线拉低,产生一个起始旗子暗记,从机检测到起始旗子暗记后,准备吸收数

据,当数据传输完成,主机只需产生一个停滞旗子暗记,见告从机数据传输结束,停滞旗子暗记的产生

是在SCL为高电平时,SDA从低电平跳变到高电平,从机检测到停滞旗子暗记后,停滞吸收数据。
I2C

整体时序如下图。
起始旗子暗记之前为空闲状态,起始旗子暗记之后到停滞旗子暗记之前的这一段为数据传

输状态,主机可以向从机写数据,也可以读取从机输出的数据,数据的传输由双向数据线(SDA)

完成。
停滞旗子暗记产生后,总线再次处于空闲状态。

图 28.1.2 I2C整体时序图

理解到了整体时序之后,我们可能有疑问,数据因此什么样的格式传输的呢?知足若何的

时序哀求呢?是在任何时候改变都可以吗?怎么知道从机有没有吸收到数据呢?带着这些疑

问,我们连续学习I2C。

由于只有一根数据线进行数据的传输,如果不规定好传输规则肯定会导致信息错乱,犹如

在单条道路上驾驶,没有交通规则,再好的道路也会发生拥堵乃至更糟。
采取两线构造的I2C

虽然只有一根数据线,但由于还有一条时钟线,可以让数据线在时钟线的带领下有顺序的传送,

就彷佛单条道路上的车辆在交警或旗子暗记指示灯的指示下有规则的通畅。
那么I2C遵照若何的规

则呢?

图 28.1.3 I2C详细时序图

如果要想回答这些问题,我们得读懂图 28.1.3。
由图 28.1.3可知,我们在起始旗子暗记之后,

主机开始发送传输的数据;在串行时钟线SCL为低电平状态时,SDA许可改变传输的数据位(1

为高电平,0为低电平),在SCL为高电平状态时,SDA哀求保持稳定,相称于一个时钟周期传

输1bit数据,经由8个时钟周期后,传输了8bit数据,即一个字节。
第8个时钟周期末,主机释

放SDA以使从机应答,在第9个时钟周期,从机将SDA拉低以应答;如果第9个时钟周期,SCL为

高电平时,SDA未被检测到为低电平,视为非应答,表明这次数据传输失落败。
第9个时钟周期末,

从机开释SDA以使主机连续传输数据,如果主机发送停滞旗子暗记,这次传输结束。
我们要把稳的是数据以8bit即一个字节为单位串行发出,其最先发送的是字节的最高位。

I2C的时序部分已经基本先容完了,但还有一个小问题,便是当多个I2C器件挂接在总线上

时,若何才能与我们想要传输数据的器件进行通信。
这就涉及到了器件地址(也称从机地址,

SLAVE ADDRESS)。

每个I2C器件都有一个器件地址,有些I2C器件的器件地址是固定的,而有些I2C器件的器

件地址由一个固定部分和一个可编程的部分构成,这是由于很可能在一个别系中有几个同样的

器件,器件地址的可编程部分能最大数量的使这些器件连接到I2C总线上,例如EEPROM器件,

为了增加系统的EEPROM容量,可能须要多个EEPROM。
器件可编程地址位的数量由它可利用的管

脚决定,比如EEPROM器件一样平常会留下3个管脚用于可编程地址位。
但有些I2C器件在出厂时器件

地址就设置好了,用户不可以变动(如实时时钟PCF8563的器件地址为固定的7’h51)。
以是

当主机想给某个器件发送数据时,只需向总线上发送吸收器件的器件地址即可。

对付AT24C64而言,其器件地址为1010加3位的可编程地址,3位可编程地址由器件上的3个

管脚A2、A1、A0(见图 28.3.2)的硬件连接决定。
当硬件电路上分别将这三个管脚连接到GND

或VCC时,就可以设置不同的可编程地址。
对付我们的开拓板,这3个管脚连接到地。

进行数据传输时,主机首先向总线上发出开始旗子暗记,对应开始位S,然后按照从高到低的

位序发送器件地址,一样平常为7bit,第8bit位为读写掌握位R/W,该位为0时表示主机对从机进行

写操作,当该位为1时表示主机对从机进行读操作,然后吸收从机相应。
对付AT24C64来说,其

传输器件地址格式如下图所示。

图 28.1.4 器件地址格式示意图

发送完第一个字节(7位器件地址和一位读写掌握位)并收到从机精确的应答后就开始发

送字地址(Word Address)。
一样平常而言,每个兼容I2C协议的器件,内部总会有可供读写的寄

存器或存储器,对付我们本次实验用到的EEPROM存储器,内部便是一系列顺序编址的存储单元。

以是,当我们对一个器件中的存储单元(包括寄存器)进行读写时,首先要指定存储单元的地

址即字地址,然后再向该地址写入内容。
该地址为一个或两个字节长度,详细长度由器件内部

的存储单元的数量决定,当存储单元数量不超过一个字节所能表示的最大数量(2^8=256)时,

用一个字节表示,超过一个字节所能表示的最大数量时,就须要用两个字节来表示,例犹如是

EEPROM存储器,AT24C02的存储单元容量为2Kbit=256Byte(一样平常bit缩写为b,Byte缩写为B),用一个字节地址即可寻址所有的存储单元,而AT24C64的存储单元容量为64Kb=8KB,须要13位

(2^13=8KB)的地址位,而I2C又因此字节为单位进行传输的,以是须要用两个字节地址来寻

址全体存储单元。
图 28.1.4 和图 28.1.5分别为单字节字地址和双字节字地址器件的地址分

布图,个中单字节字地址的器件因此存储容量为2Kb的EEPROM存储器AT24C02为例,双字节字地

址的器件因此存储容量为64Kb的EEPROM存储器AT24C64为例,WA7即字地址Word Address的第7

位,以此类推,用WA是为了差异前面器件地址中的A。

图 28.1.4 单字节字地址分布

图 28.1.5 双字节字地址分布

主机发送完字地址,从机精确应答后就把内部的存储单元地址指针指向该单元。
如果读写

掌握位R/W位为“0”即写命令,从机就处于吸收数据的状态,此时,主机就开始写数据了。

数据分为单次写(对付EEPROM而言,称为字节写)和连续写(对付EEPROM而言,称为页写),

那么这两者有什么差异呢?比拟图 28.1.6和图 28.1.7可知,两者的差异在于发送完一字节

数据后,是发送结束旗子暗记还是连续发送下一字节数据,如果发送的是结束旗子暗记,就称为单次写,

如果连续发送下一字节数据,就称为连续写。
图 28.1.6是AT24C64的单次写(字节写)时序,对付字地址为单字节的I2C器件而言,在发送完字地址(对应图 28.1.6的字地址高位),且从

机应答后即可串行发送8bit数据。
图 28.1.7是AT24C64连续写(页写)时序。
要把稳的是,对

于AT24C64的页写,是不能发送超过一页的单元容量的数据的,而AT24C64的一页的单元容量为

32Byte,当写完一页的末了一个单元时,地址指针指向该页的开头,如果再写入数据,就会覆

盖该页的起始数据。

图 28.1.6 单次写(字节写)时序

图 28.1.7 连续写(页写)时序

如果读写掌握位R/W位为“1”即读命令,主机就处于吸收数据的状态,从机从该地址单元

输出数据。
读数据有三种办法:当前地址读、随机读和连续读。
当前地址读是指在一次读或写

操作后发起读操作。
由于I2C器件在读写操作后,其内部的地址指针自动加一,因此当前地址

读可以读取下一个字地址的数据。
也便是说上次读或写操作的单元地址为02时,当前地址读的内容便是地址03处的单元数据,时序图如图 28.1.8所示。

图 28.1.8 当前地址读时序

由于当前地址读极未便利读取任意的地址单元的数据,以是就有了随机读,随机读的时序

有点奇怪,见图 28.1.9,发送完器件地址和字地址后,竟然又发送起始旗子暗记和器件地址,而

且第一次发送器件地址时后面的读写掌握位为“0”,也便是写命令,第二次发送器件地址时

后面的读写掌握位为“1”,也便是读。
为什么会有这样奇怪的操作呢?这是由于我们须要使

从机内的存储单元地址指针指向我们想要读取的存储单元地址处,以是首先发送了一次Dummy

Write也便是虚写操作,只以是称为虚写,是由于我们并不是真的要写数据,而是通过这种虚

写操作使地址指针指向虚写操作中字地址的位置,等从机应答后,就可以以当前地址读的办法

读数据了,如图 28.1.9所示,随机地址读是没有发送数据的单次写操作和当前地址读操作的

结合体。

图 28.1.9 随机地址读时序

至于连续读,对应的是当前地址读和随机读都是一次读取一个字节而言的,它是将当前地

址读或随机读的主机非应答改成应答,表示连续读取数据,图 28.1.10是在当前地址读下的连

续读。

图 28.1.10 顺序读时序

至此,I2C协议就基本讲完了,本章我们紧张采取单次写和随机读办法进行EEPROM读写测

试。

实验任务

本次实验任务是先通过FPGA从EEPROM(AT24C64)的存储器地址0至存储器地址255分别写

入数据0~255;写完之后再开始读取存储器地址0~ 255中的数据,若读取的值精确则LED灯常亮,

否则LED灯闪烁。

硬件设计

AT24C64芯片的常用封装形式有直插(DIP8)式和贴片(SO-8)式两种,无论是直插式还

是贴片式,其引脚功能与序号都一样,我们开拓板上采取的是贴片式,实物图和引脚图分别如

图 28.3.1和图 28.3.2所示。

图 28.3.1 开拓板上的AT24C64实物图

AT24C64的引脚功能如下:

A2,A1,A0:可编程地址输入端。

GND:电源地引脚

SDA:SDA(Serial Data,串行数据)是双向串行数据输入/输出端。

SCL:SCL(Serial clock,串行时钟)串行时钟输入端。

WP(写保护):AT24C64有一个写保护引脚用于供应数据保护,当写保护引脚连接至GND时,

芯片可以正常写,当写保护引脚连接至VCC时,使能写保护功能,此时禁止向芯片写入数据,

只能进行读操作。

VCC:电源输入引脚

我们的开拓者FPGA开拓板上EEPROM的事理图如图 28.3.3所示:

图 28.3.3 EEPROM事理图

由上图可知,我们开拓板上的EEPROM可编程地址A2、A1、A0连接到地,以是AT24C64的器

件地址为1010000,如下图所示:

图 28.3.4 AT24C64的器件地址

本实验中,系统时钟、按键复位以及EEPROM的SCL和SDA的管脚分配如下表所示:

表 28.3.1 串口通信实验管脚分配

程序设计

根据实验任务,我们可以大致方案出系统的掌握流程:首先FPGA向EEPROM写数据,写完之

后从EEPROM读出所写入的数据,并判断读出的数据与写入的数据是否相同,如果相同则LED灯

常亮,否则LED灯闪烁。
由此画出系统的功能框图如下图所示:

图 28.4.1 EEPROM读写实验系统框图

由系统总体框图可知,FPGA部分包括四个模块,顶层模块(e2prom_top)、读写模块

(e2prom_rw)、I2C驱动模块(i2c_dri)和LED灯显示模块(led_alarm)。
个中在顶层模块

中完成对I2C驱动模块的例化。

各模块端口及旗子暗记连接如图 28.4.2所示:

图 28.4.2 顶层模块事理图

i2c_dri为I2C驱动模块,用来驱动I2C的读写操作。
当FPGA通过EEPROM读写模块e2prom_rw

向EEPROM读写数据时,拉高i2c触发掌握旗子暗记i2c_exec以使能I2C驱动模块,并利用读写掌握信

号i2c_rh_wl掌握读写操作,当i2c_rh_wl为低电平时,I2C驱动模块i2c_dri实行写操作,当

i2c_rh_wl为高电平时,I2C驱动模块i2c_dri实行读操作。
此外,e2prom_rw模块通过i2c_addr

接口向i2c_dri模块输入器件字地址,通过i2c_data_w接口向i2c_dri模块输入写的数据,并通

过i2c_data_r接口读取i2c_dri模块读到的数据。
error_flag是缺点标志,用来掌握led的显示

状态。

顶层模块的代码如下:

1 module e2prom_top(

2 //system clock

3 input sys_clk , // 系统时钟

4 input sys_rst_n , // 系统复位

5 //eeprom interface

6 output scl , // eeprom的时钟线scl

7 inout sda , // eeprom的数据线sda

8 //user interface

9 output [3:0] led // led显示

10 );

11

12 //parameter define

13 parameter SLAVE_ADDR = 7'b1010000 ; // 器件地址

14 parameter BIT_CTRL = 1'b1 ; // 字地址位掌握参数(16b/8b)

15 parameter CLK_FREQ = 26'd50_000_000; // i2c_dri模块的驱动时钟频率

16 parameter I2C_FREQ = 18'd250_000 ; // I2C的SCL时钟频率

17 parameter L_TIME = 17'd125_000 ; // led闪烁韶光参数

18

19 //reg define

20 reg [25:0] cnt ; // 计数

21 reg [ 1:0] flow_cnt ; // 状态流掌握

22 reg [13:0] wait_cnt ; // 等待计数

23

24 //wire define

25 wire clk ; // I2C操作时钟

26 wire i2c_exec ; // i2c触发掌握

27 wire [15:0] i2c_addr ; // i2c操作地址

28 wire [ 7:0] i2c_data_w; // i2c写入的数据

29 wire i2c_done ; // i2c操作结束标志

30 wire i2c_rh_wl ; // i2c读写掌握

31 wire [ 7:0] i2c_data_r; // i2c读出的数据

32 wire error_flag; // 缺点标志

33

34 //

35 // main code

36 //

37

38 //例化e2prom读写模块

39 e2prom_rw u_e2prom_rw (

40 //global clock

41 .clk (clk ), // 时钟旗子暗记

42 .rst_n (sys_rst_n ), // 复位旗子暗记

43 //i2c interface

44 .i2c_exec (i2c_exec ), // I2C触发实行旗子暗记

45 .i2c_rh_wl (i2c_rh_wl ), // I2C读写掌握旗子暗记

46 .i2c_addr (i2c_addr ), // I2C器件内地址

47 .i2c_data_w (i2c_data_w), // I2C要写的数据

48 .i2c_data_r (i2c_data_r), // I2C读出的数据

49 .i2c_done (i2c_done ), // I2C一次操作完成

50 //user interface

51 .error_flag (error_flag) // 缺点标志

52 );

53

54 //例化i2c_dri

55 i2c_dri #(

56 .SLAVE_ADDR (SLAVE_ADDR), // slave address从机地址,放此处方便参数通报

57 .CLK_FREQ (CLK_FREQ ), // i2c_dri模块的驱动时钟频率(CLK_FREQ)

58 .I2C_FREQ (I2C_FREQ ) // I2C的SCL时钟频率

59 ) u_i2c_dri(

60 //global clock

61 .clk (sys_clk ), // i2c_dri模块的驱动时钟(CLK_FREQ)

62 .rst_n (sys_rst_n ), // 复位旗子暗记

63 //i2c interface

64 .i2c_exec (i2c_exec ), // I2C触发实行旗子暗记

65 .bit_ctrl (BIT_CTRL ), // 器件地址位掌握(16b/8b)

66 .i2c_rh_wl (i2c_rh_wl ), // I2C读写掌握旗子暗记

67 .i2c_addr (i2c_addr ), // I2C器件内地址

68 .i2c_data_w (i2c_data_w), // I2C要写的数据

69 .i2c_data_r (i2c_data_r), // I2C读出的数据

70 .i2c_done (i2c_done ), // I 2C一次操作完成

71 .scl (scl ), // I2C的SCL时钟旗子暗记

72 .sda (sda ), // I2C的SDA旗子暗记

73 //user interface

74 .dri_clk (clk ) // I2C操作时钟

75 );

76

77 //例化led_alarm模块

78 led_alarm #(.L_TIME(L_TIME ) // 掌握led闪烁韶光

79 ) u_led_alarm(

80 //system clock

81 .clk (sys_clk ), // 时钟旗子暗记

82 .rst_n (sys_rst_n ), // 复位旗子暗记

83 //led interface

84 .led (led ), // LED灯

85 //user interface

86 .error_flag (error_flag) // 缺点标志

87 );

88 endmodule

顶层模块中紧张完成对别的模块的例化,须要把稳的是程序第13行到第17行定义了五个参

数,在模块例化时会将这些变量通报到相应的模块。

当程序用于读写不同器件地址的EEPROM时将SLAVE_ADDR修正为新的器件地址;字地址位控

制参数(16b/8b)BIT_CTRL是用来掌握不同字地址的I2C器件读写时序中字地址的位数,当I2C器

件的字地址为16位时,参数BIT_CTRL设置为“1”,当I2C器件的字地址为8位时,参数BIT_CTRL

设置为“0”;i2c_dri模块的驱动时钟频率CLK_FREQ是指在例化I2C驱动模块i2c_dri时,驱动

i2c_dri模块的时钟频率;I2C的SCL时钟频率参数I2C_FREQ是用来掌握I2C协议中的SCL的频率,

一样平常不超过400KHz;led闪烁韶光参数L_TIME用来掌握led的闪烁间隔韶光,参数值与驱动该模

块的clk时钟频率有关。
例如,掌握led闪烁的间隔韶光为0.25s,clk的频率为1MHz时,

0.25s/1us=250000,由于代码中当计数器计数到L_TIME的值时,LED的状态改变一次,LED高电

平加上低电平的韶光才是一次闪烁的韶光,以是L_TIME的值应定义成125000。

由前面的I2C读写操作时序图我们可以创造,I2C驱动模块非常适宜采取状态机来编写。

论是字节写(图 28.1.6)还是随机读(图 28.1.9),都要先从空闲状态开始,先发送起始信

号,然后发送器件地址和读写命令(这里为了方便表示我们利用“掌握命令”来表示器件地址

和读写命令)。
发送完掌握命令并吸收应答旗子暗记后发送字地址,然后就可以进行读写数据的传

输了。
读写数据传输结束后吸收应答旗子暗记,末了发送停滞旗子暗记,此时I2C读写操作结束,再次

进入空闲状态。

状态机的状态跳转图如下所示,统共有8个状态,一开始状态机处于空闲状态st_idle,当

I2C触发实行旗子暗记触发(i2c_exec=1)时,状态机进入发送掌握命令状态st_sladdr;发送完控

制命令后就发送字地址,这里出于大略考虑,不对从机EEPROM的应答旗子暗记进行判断。
由于字地

址存在单字节和双字节的差异,我们通过bit_ctrl旗子暗记判断是单字节还是双字节字地址。
对付

双字节的字地址我们先发送高8位即第一个字节,发送完高8位后进入发送8位字地址状态st_addr8,也便是发送双字节地地址的低8位;对付单字节的字地址我们直接进入发送8位字地址

状态st_addr8。
发送完字地址后,根据读写判断标志来判断是读操作还是写操作。
如果是写

(wr_flag=0)就进入写数据状态st_data_wr,开始向EEPROM发送数据;如果是读(wr_flag=1)

就进入发送器件地址读状态st_addr_rd发送器件地址,此状态结束后就进入读数据状态

st_data_rd吸收EEPROM输出的数据。
读或写数据结束后就进入结束I2C操作状态st_done并发送

结束旗子暗记,此时,I2C总线再次进入空闲状态st_idle。

图 28.4.3 I2C驱动模块状态跳转图

在程序中我们采取三段式状态机。
由于代码较长,我们在这里将个中第二段的源代码粘贴

如下:

105 //组合逻辑判断状态转移条件

106 always @( ) begin

107 next_state = st_idle;

108 case(cur_state)

109 st_idle: begin // 空闲状态

110 if(i2c_exec) begin

111 next_state = st_sladdr;

112 end

113 else

114 next_state = st_idle;

115 end

116 st_sladdr: begin

117 if(st_done) begin

118 if(bit_ctrl) // 判断是16位还是8位字地址

119 next_state = st_addr16;

120 else

121 next_state = st_addr8 ;

122 end

123 else

124 next_state = st_sladdr;

125 end

126 st_addr16: begin // 写16位字地址

127 if(st_done) begin

128 next_state = st_addr8;

129 end

130 else begin

131 next_state = st_addr16;

132 end

133 end

134 st_addr8: begin // 8位字地址

135 if(st_done) begin

136 if(wr_flag==1'b0) // 读写判断

137 next_state = st_data_wr;

138 else

139 next_state = st_addr_rd;

140 end

141 else begin

142 next_state = st_addr8

143 end

144 end

145 st_data_wr: begin // 写数据(8 bit)

146 if(st_done)

147 next_state = st_stop;

148 else

149 next_state = st_data_wr;

150 end

151 st_addr_rd: begin // 写地址以进行读数据

152 if(st_done) begin

153 next_state = st_data_rd;

154 end

155 else begin

156 next_state = st_addr_rd;

157 end

158 end

159 st_data_rd: begin // 读取数据(8 bit)

160 if(st_done)

161 next_state = st_stop;

162 else

163 next_state = st_data_rd;

164 end

165 st_stop: begin // 结束I2C操作

166 if(st_done)

167 next_state = st_idle;

168 else

169 next_state = st_stop ;

170 end

171 default: next_state= st_idle;

172 endcase

173 end

我们可以对照着图 28.4.3来剖析程序中各状态之间是如何跳转的。

EEPROM读写模块紧张实现对I2C读写过程的掌握,包括给出字地址及须要写入该地址中的数据、启动I2C读写操作、判断读写数据是否同等等。
EEPROM读写模块的代码如下:

1 module e2prom_rw(

2 //global clock

3 input clk , // 时钟旗子暗记

4 input rst_n , // 复位旗子暗记

5

6 //i2c interface

7 output i2c_rh_wl , // I2C读写掌握旗子暗记

8 output reg i2c_exec , // I2C触发实行旗子暗记

9 output reg [15:0] i2c_addr , // I2C器件字地址

10 output reg [ 7:0] i2c_data_w , // I2C要写的数据

11 input [ 7:0] i2c_data_r , // I2C读出的数据

12 input i2c_done , // I2C一次操作完成

13

14 //user interface

15 output reg error_flag // 缺点标志

16 );

17

18 //parameter define

19 parameter WAIT = 14'd5000 ; // 读写等待韶光

20 parameter BYTE_N = 16'd255 ; // 读写的字节数

21

22 //reg define

23 reg addr_over ; // 地址结束标志

24 reg rom_w_done; // 字节全部写入e2prom的标志

25 reg [ 1:0] flow_cnt ; // 状态流掌握

26 reg [13:0] wait_cnt ; // 等待计数

27

28 //

29 // main code

30 //

31

32 //读写掌握

33 assign i2c_rh_wl = addr_over & rom_w_done;

34

35 //eeprom字节地址配置

36 always @(posedge clk or negedge rst_n) begin

37 if(rst_n == 1'b0) begin

38 i2c_addr <= 16'd0;

39 addr_over<= 1'b0;

40 end

41 else if(i2c_done == 1'b1) begin

42 if(i2c_rh_wl == 1'b1) begin

43 if(i2c_addr < BYTE_N)

44 i2c_addr <= i2c_addr + 1'd1;

45 else

46 i2c_addr <= i2c_addr;

47 end

48 else begin

49 if(i2c_addr == BYTE_N) begin

50 i2c_addr <= 16'd0;

51 addr_over<= 1'b1; //写完指定地址标志

52 end

53 else

54 i2c_addr <= i2c_addr + 1'd1;

55 end

56 end

57 else

58 i2c_addr <= i2c_addr;

59 end

60

61 //读写eeprom

62 always @(posedge clk or negedge rst_n) begin

63 if(rst_n == 1'b0) begin

64 flow_cnt <= 2'b0;

65 wait_cnt <= 14'b0;

66 i2c_exec <= 1'b0;

67 i2c_data_w <= 8'd0;

68 rom_w_done <= 1'b0;

69 error_flag <= 1'b1;

70 end

71 else begin

72 i2c_exec <= 1'b0;

73 //从eeprom的第1页的第1个字节到第16页的第16个字节(共256字节)写入数据0~255

74 if(i2c_rh_wl == 1'b0) begin

75 case(flow_cnt)

76 2'd0: begin

77 rom_w_done <= 1'b0;

78 wait_cnt <= wait_cnt + 1'b1;

79 if(wait_cnt == 14'd100) begin

80 wait_cnt <= 14'd0;

81 flow_cnt <= flow_cnt + 1'b1;

82 end

83 end

84 2'd1: begin

85 i2c_exec <= 1'b1;

86 i2c_data_w <= i2c_addr[7:0];

87 flow_cnt <= flow_cnt + 1'b1;

88 end

89 2'd2: begin

90 if(i2c_done == 1'b1)

91 flow_cnt <= flow_cnt + 1'b1;

92 end

93 2'd3:begin

94 if(wait_cnt == WAIT) begin //写间隔掌握

95 flow_cnt <= 2'b0;

96 wait_cnt <= 14'd0;

97 rom_w_done <= 1'b1;

98 end

99 else

100 wait_cnt <= wait_cnt + 1'b1;

101 end

102 endcase

103 end

104 //读取从eeprom的第1页的第1个字节开始的共256字节的值并判断值是否精确

105 else begin

106 case(flow_cnt)

107 2'd0: begin

108 wait_cnt <= wait_cnt + 1'b1;

109 if(wait_cnt == 14'd100) begin

110 flow_cnt <= flow_cnt + 1'b1;

111 wait_cnt <= 14'd0;

112 end

113 end

114 2'd1: begin

115 i2c_exec <= 1'b1;

116 flow_cnt <= flow_cnt + 1'b1;

117 end

118 2'd2: begin

119 if(i2c_done == 1'b1) begin // 判断I2C操作是否完成

120 if(i2c_addr[7:0] == i2c_data_r) begin // 判断读到的值精确与否

121 error_flag <= 1'b0; // 读到的值精确

122 flow_cnt <= 2'b0; // 返回状态0

123 end

124 else begin

125 error_flag <= 1'b1; // 读到的值缺点

126 end

127 end

128 end

129 default: flow_cnt <= 2'b0;

130 endcase

131 end

132 end

133 end

134

135 endmodule

程序中第62行的always块是读写掌握块,详细是读还是写由I2C读写掌握旗子暗记i2c_rh_wl决

定。
当该旗子暗记为低电平时,为写数据操作,从EEPROM的存储地址0开始,每写入一个字节的数

据,地址加1,直至写入指定的字节数(BYTE_N)。
当写末了一个存储地址结束后,写EEPROM

结束的标志旗子暗记rom_w_done拉高,写数据过程结束。
由程序第33行可知,此时i2c_rh_wl为高

电平,程序进入读数据过程。
由于写入每个存储单元的数据与该单元的地址相同,以是当读到

的数据与该存储单元的地址相等时,表明读写同等,缺点标志旗子暗记error_flag为低电平;若两

者不相等,则解释读写过程发生缺点,此时将error_flag拉高,结束读操作。
图 28.4.4为写

过程中SignalTap抓取的波形图:

图 28.4.4 写过程中SignalTap抓取的波形图

从该波形图中我们看到读写掌握旗子暗记i2c_rh_wl为低电平,表示处于写操作状态。
当I2C触

发实行旗子暗记i2c_exec为高电平时开始实行I2C写操作,从上图中可以看到当前写的存储单元地

址为0000h,写入的数据为00h。

图 28.4.5为读过程中SignalTap抓取的波形图。

图 28.4.5 读过程中SignalTap抓取的波形图

从该波形图中我们看到读写掌握旗子暗记i2c_rh_wl为高电平,表示处于读操作状态,当I2C触

发实行旗子暗记i2c_exec为高电平时开始实行I2C读操作,从上图中可以看到当前读的存储单元地

址为0001h,读到的数据为01h。

led显示模块利用LED灯的显示状态来标识读写过程是否出错,在模块中通过检测缺点标志

旗子暗记error_flag来改变LED灯的显示状态。

LED显示模块led_alarm代码如下:

1 module led_alarm #(parameter L_TIME = 25'd25_000_000 // 掌握led闪烁间隔韶光

2 )( // 此处为500ms

3 //system clock

4 input clk , // 时钟旗子暗记

5 input rst_n , // 复位旗子暗记

6

7 //led interface

8 output [3:0] led , // LED 灯

9

10 //user interface

11 input error_flag // 缺点标志

12 );

13

14 //reg define

15 reg led_t ; // 利用的led灯

16 reg [24:0] led_cnt; // led计数

17

18 //

19 // main code

20 //

21

22 //led输出

23 assign led = {3'b000,led_t};

24

25 //缺点标志为1时led闪烁,否则,LED0常亮

26 always @(posedge clk or negedge rst_n) begin

27 if(rst_n == 1'b0) begin

28 led_cnt <= 25'd0;

29 led_t <= 1'b0;

30 end

31 else begin

32 if(error_flag) begin // 读到的值缺点

33 led_cnt <= led_cnt + 25'd1;

34 if(led_cnt == L_TIME) begin // 数据缺点时LED灯每隔L_TIME韶光闪烁一次

35 led_cnt <= 25'd0;

36 led_t <= ~led_t;

37 end

38 end

39 else begin // 读完且读到的值精确

40 led_cnt <= 25'd0;

41 led_t <= 1'b1; // led灯常亮

42 end

43 end

44 end

45

46 endmodule

程序第一行的参数L_TIME用于掌握led闪烁间隔韶光,在例化时重新指定参数值,可以改

变led闪烁的快慢。
程序中第32行判断error_flag的值,当error_flag为高电平时表明读写数

据不一致,此时,led灯每隔L_TIME韶光闪烁一次;当error_flag为低电平时,表明读写数据

同等,EEPROM读写精确,led灯常亮。

下载验证

首先我们打开工程,在工程所在的路径下打开e2prom_top/par文件夹,在里面找到

“e2prom_top.qpf”并双击打开。
把稳工程所在的路径名只能由字母、数字以及下划线组成,

不能涌现中文、空格以及分外字符等。
EEPROM读写工程打开后如图 28.5.1所示。

图 28.5.1 EEPROM读写工程

接下来我们下载程序,验证EEPROM读写功能。
工程打开后通过点击工具栏中的“Programmer”

图 标 打 开 下 载 界 面 , 通 过 “ Add File ” 按 钮 选 择 EEPROM 读 写 工 程 中

e2prom_top/par/output_files 目录下的“e2prom_top.sof”文件。
开拓板电源打开后,在程

序下载界面点击“Hardware Setup”,在弹出的对话框中选择当前的硬件连接为“USB

Blaster[USB-0]”。
然后点击“Start”将工程编译完成后得到的 sof 文件下载到开拓板中,

如图 28.5.2所示。

图 28.5.2 程序下载界面

接下来我们下载程序,验证通过I2C协议读写EEPROM功能。
下载完成后不雅观察开拓板的led显

示如图 28.5.3所示,led灯常亮,解释通过EEPROM读写程序下载验证精确。

图 28.5.3 开拓板LED灯常亮

标签:

相关文章

软件咋控制芯片电路闭合?_指令_电路

芯片电路是由许多眇小的电子元件(类似于电子版的积木)组成的集成电路。这些元件可以像开关一样掌握电流的流动。软件通过发送特定的指令,...

智能 2024-12-13 阅读0 评论0