目录
一.EEPROM简介
PROM:只能写一次数据;
ROM:只读不写;
EPROM:可写可擦除,但是需要固定编辑器对其改写。
EEPROM:(Electrically Erasable Programmable Read Only Memory)带电可擦可编程只读存储器。
本实验中用到的启明星开发板中所带的EEPROM是是 ATMEL 公司生产的 AT24C 系列的 AT24C64 这一型号,AT24C64 具有高可靠性,可对所存数据保存 100 年,并可多次擦写,擦写次数达一百万次。AT24C64 采用两线串行接口的双向数据传输协议——I2C 协议实现读写操作。
二.IIC协议
2.1物理层
I2C 即 Inter-Integrated Circuit(集成电路总线),是一种简单、双向、二线制总线标准,多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。I2C 总线由数据线 SDA(双向) 和时钟线 SCL 构成通信线路,如下图所示。I2C 器件一般采用开漏结构与总线相连。开漏结构指的是低电平时照常拉低,高电平时由上拉电阻拉至高电平。
2.2协议层
2.2.1整体时序协议
SCL是时钟线,SDA是数据线。
当未传输数据时,SCL和SDA均为高电平,因为会被上拉电阻拉至高电平。
当传输数据时,当SCL中的CLK为高电平时,不允许SDA上的数据发生变化;当SCL中的CLK为低电平时,允许SDA上的数据发生变化。
2.2.2具体时序图
首先我们需要知道两点,第一:SCL,SDA未传输数据为高电平;第二:SCL为高电平的时候SDA数据不能发生变化,但是,两点除外,起始信号和停止信号。
当起始信号(低电平)来临时,在1~8个时钟的低电平处改变传输数据进行发送,当第9个clk的高电平下,SDA为低电平时代表1~8个clk下传输的8bit数据是有效完成的,随后发送停止信号。
2.3IIC器件地址
器件地址相当于从器件的专属ID号(这里假设这里EEPROM中的A2,A1,A0全为0)如2.1中的图所示,当主器件(ZYNQ)发出在某个从器件(EEPROM、RTC)读/写指令时,会指定器件地址,比如主器件指定器件地址为1010000,发送到从器件上,而EEPROM将A2,A1,A0传给IIC,IIC整合成1010000,回传给主器件的器件地址也为1010000,则此时主从器件就连接成功,下一步主器件就可以在从器件上进行读写数据了。
一句话,器件地址就是EEPROM的身份证,找到身份证就能指定这个人了 ,同理可以指定从器件。
EEPROM引脚图
2.4IIC读写操作种类
连续写操作(页写):例如指定从第32页开始写,从第一行写到第32行结束,若此时还没来停止信号,则会重新回到第32页的第1行开始写,一直循环到停止信号来临。
顺序读操作:例如EEPROM只有1000个地址,指定从第100个地址开始读,若停止信号没来,则继续读一直读到第1000行地址,然后返回第100行继续开始读。
2.5IIC读写时序
2.5.1单次写时序
起始信号:当SCL为高时,SDA来低电平,代表主机发送起始信号;
期间地址:7位期间地址加1位读写命令,总共是8位,参考2.3中的图。
从机应答:在第9个clk发送应答信号(低电平),代表前面是有效传输。
以上步骤完成了主机发送寻址信号,从机发送器件地址,两者相同,则可以开启后面的读写操作。
16位地址信号:该两个字节信号是在传递从机中需要读写的地址,将指针指向该地址方便后续读写操作。
8位数据:表示向前面指定的16位地址位写入该8位数据。
停止信号:当SCL为高电平时,SDA变化为1,代表主机发送停止信号。
2.5.2连续写时序
在连续写时,只要主寻址,从器件地址匹配,则只用发送一次16位地址,后续就可以在停止信号来临之前重复发送8位数据。
2.5.3当前地址读操作
当主寻址从地址一致时,只需发送8位数据,无需指定16位地址。
2.5.4随机地址读操作
因为该操作需要指定地址,因此需要先虚写再读。虚写的目的就是将指针指向需要读数据的地址。
2.5.5当前地址连读操作
因为是当前地址,故不需要传入16位地址。
2.5.6随即地址连读
无需赘述。
三.系统整体模块设计
dri_clk:驱动I2C操作的驱动时钟,I2C_dri和E2prom_rw公用该驱动时钟。
I2c_data_r[7:0]:读到的数据,这里没有返回写的数据是因为比较都读和写的数据是否一致时,是用地址和读数据作比较的,因为该实验地址和数据都是0~256,保持同步一致。
I2c_done:代表一次I2C操作完成。
I2c_ack:应答信号。0表示应答,1表示未应答。
I2c_exec:状态机驱动信号。让状态机从空闲状态进入运转状态。
bit_ctrl:控制字地址是16位还是8位。
I2c_rh_wl:0代表写数据,1代表读数据。
I2c_addr[15:0]:16位字地址。
I2C_data_w[7:0]:8位要写的数据。
Rw_done:读写比较完成,该实验代表256位读数据和写入的数据比较完成。
Rw_result:读写比较结果,一致灯亮,不一致灯闪烁。
四.代码解读
4.1 I2C_dri模块代码解读
4.1.1dri_clk
以下代码的作用就是生成驱动I2C操作的驱动时钟dri_clk=1Mhz。而系统时钟是50MHz,因此对系统分频就需要记到25的计数器。
assign语句得到clk_divide=50,这里的>>2'd2,是/4的作用。
else if()中的语句是得到clk_cnt=25,这里的clk_divide[8:1]和上面的作用一样,是/2。这里得到计数器25,每0~24对系统时钟翻转一次,从而得到50分频的驱动时钟dri_clk用于后续操作。
4.1.2不同状态机写法比较
一段式:整个状态机写到一个always模块里面,在该模块中既要描述状态转移,又要描述状态的输入与输出。
二段式:用两个always语句块来描述状态机,其中一个always模块采用同步时序描述状态转移;另一个模块采用组合逻辑判断状态转移条件,描述状态转移规律以及输出。
三段式:采用三个always语句块,一个采用同步时序描述状态转移,一个采用组合逻辑判断状态转移条件,描述状态转移规律,另外一个采用时序电路描述状态输出。
该实验采用三段式。
4.1.3同步时序描述状态转移
4.1.4组合逻辑判断状态转移条件
由以下状态转换图可知,一共有8种状态。分别是
空闲状态: st_idle;
写控制命令: st_sladdr;
发送16位字地址的高8位: st_addr16;
发送16位字地址的低8位: st_addr8;
写数据: st_data_wr;
读控制命令: st_addr_rd;
读数据: st_data_rd;
停止状态: st_stop.
4.1.5时序电路描述状态输出
4.1.5.1空闲状态
当状态是初始状态时,所有值赋初值。
scl:I2C中的SCL时钟,该实验中为25KHZ.
sda_out:主机FPGA发送的数据。
sda_dir: 表示I2C数据方向,为1时表示主机(FPGA)输出信号,为0时FPGA输出高阻态,表示释放控制权。接收从机发来的信号。
i2c_done:I2C一次操作完成。
cnt:帮助I2C一次操作中的索引值。
当I2C触发执行信号来临时,e2prom_rw模块传来读/写控制位,16位字地址,8位写数据。即3系统整体模块设计中的以下数据。
4.1.5.2写控制命令
在4.1.5.1中已经描述过,I2C中的SCL的时钟是25Khz,也就是I2C的驱动时钟的四分频,因此,scl在cnt下,每隔两个数翻转一次,即可得到四分频的SCL。
写控制命令波形图
每次在SCL的低电平时期传输器件地址,即cnt=4、8、12...时将器件地址赋值给sda_out,该实验的器件地址是1010000,在第8个SCL下,传送写控制信号。
在cnt=36时,即第9个SCL时钟信号,sda=0,释放控制权。接收从机发来的应答信号。sda_in代表从机发送来的应答信号,为1表示未应答,为0表示应答。
在cnt=39时,代表从机应答结束,一次I2C操作完毕,将SCL清零,同时帮助数据操作的索引值cnt也清零。
4.1.5.3 发送16位字地址的高8位
同4.1.5.2中发送器件地址数据一样,只不过这里是发送高8为字地址。
4.1.5.4发送8位字地址的低8位
4.1.5.5写数据
4.1.5.6读数据之读命令
相当于下图中的红色框内的步骤,因为读数据在经历过虚写操作后,需要重新传入器件地址以及读命令。
4.1.5.7读数据
需要注意,读数据时,sda_dir=0,因为要从从器件中读取数据,故要释放总线,接收数据。
注意:cnt=32时,sda_dir=1,sda_out=1,为主机非应答,因为看下图,如果主机应答,此时E2PROM会将指针继续指向下一个地址,从而继续读,防止继续读。
4.2E2PROM_rw模块代码解读
4.2.1写字节
case前面两行:因为i2c_exec,rw_done信号只在某case语句中出现,发生改变,这里赋值为0是确保该值只在case中有效,也就是确保其为脉冲信号。
wait_cnt:因为当E2PROM写数据时,每两个地址之间要间隔一会,这里间隔5ms。本模块中采用的时钟是dri_clk=1Mhz,Tclk=1us,5ms/1us=5000,故WR_WAIT_TIME=5000。
2'd0中,每当间隔5ms后,判断写地址是否到达最大值256,当小于该值时,i2c_exec=1表示启动4.1模块中的状态机,跳转到2'd1.
2'd1中,分别将写地址和写数据同步加1,并将这些控制信号传入到4.1.5.1模块中,控制E2PROM写入数据。
i2c_addr==MAX_BYTE:表示256(0~255)个字节写入完成,到256时,将地址清零,读/写控制信号改为读信号,状态转换至2'd2。
4.2.2读字节
2’d2: 当256个字写完后,进入该状态,i2c_exec=1,启动4.1模块中的状态机转换,同时将读信号传给4.1模块,此时,状态机完成写器件地址,写字节地址(这里字节地址为0,因为4.2.1中当256个字节写完后地址赋值为0),读数据传给2'd3进一步操作。
2’d3: 这里直接i2c_addr[7:0] != i2c_data_r是因为写数据和地址是同步赋值的,因此其值完全相等,所以就用地址代替写数据了。注意第一次比较只是比较了写入的第一个数据(总共256个),因为只要256个字中的任何一个不相同,整体的就不相同。但是当第1位相同时继续比较第二位直到第256位。当256位都相同时,rw_result=1.
4.3result_led灯模块
rw_done_flag:因为4.3模块中的rw_done信号是脉冲信号,不易处理,因此引入该信号。恒常值。
led_cnt:125000x1us=125ms,灯闪烁时间是125ms。
led:读写结果一致,灯常亮;不一致,灯闪烁。
五.Modelsim仿真
5.1写操作分析
整体分析,仿真中进行了3次写操作,每次写操作间隔5ms。
当写如第一个字节时,首先 4.2模块发出i2c_exec信号,以启动4.1模块中的状态机开始运转,同时向4.1模块传入以下信号,则4.1模块开始进行写操作,如图中标识的1、2、3、4所示,1中进行传输7位器件地址+一位写命令(i2c_rh_wl=0),2和3中传输16位字地址,因为这里是写第一个字,因此地址为00...00,写入的数据也为00..00。
当写入第二个字节时,可以看出3中的地址变为00...01,4中的数据也变为00...01。
5.2读操作分析
首先读地址0里面的数据,1,2,3为需写步骤,4为传输7位器件地址+1位读操作(i2c_rh_wl=1),5为读数据,读出的数据为00..00,在5中的第9个scl下,sda拉高,这意味着主机非应答。结合2.5.4的图,一致。注意:3和4之间有一个scl空着,这里因为读操作在需写完成后要重新发送一次起始信号。
接着读第三个数据,可以看出5中读出的数据为00..10,此时i2c_addr=2,意味着读和写数据一致,因此rw_result为高,与设计一致。
六.板级验证
读写数据一致,led常亮。