FPGA之EEPROM实验

该博客介绍了如何在FPGA中进行EEPROM(AT24C64)实验,重点是理解I2C协议。实验涉及向EEPROM写入和读取数据,通过LED状态验证操作正确性。内容涵盖了I2C的起始和结束信号、器件地址、字地址等概念,以及读写数据的时序。文章还提到了实验的模块划分,包括顶层模块、EEPROM读写模块、I2C驱动模块和LED模块。
摘要由CSDN通过智能技术生成

FPGA之EEPROM实验

一、简介

EEPROM,即电可擦除可编程只读存储器,本次实验所用的是AT24C64型号,其存储容量为64Kbit,内部分为256页,每一页32字节(Byte),共有8192个字节,且其读写操作都以字节为基本单位。而关于如何对AT24C64进行读写操作呢?AT24C64采用两线串行接口的双向数据传输协议–I2C协议来实现读写操作,因此本次实验的重点则是了解I2C协议。(本次实验基于正点原子所设置的实验)

实验任务:

本次实验的任务是先向EEPROM的存储器地址0-255分别写入数据0-255,写完后再读取存储器地址0-255中的数据,若读取的值全部正确则LED灯常亮,反之LED灯闪烁。

关于I2C:

I2C总线由数据线SDA和SCL构成通信线路,即可以发送数据,也可以接收数据。
在进行总体介绍之前,先要解释几个重要的概念:
(1)起始信号和结束信号:顾名思义,这个I2C传输开始和结束的标志,当起始信号出现时,意味着可以开始进行数据的传输,当结束信号出现时,意味着单次数据传输的结束。
起始信号的产生:在SCL为高时,拉低SDA(SCL = 1 && SDA = 0)
结束信号的产生:在SCL为高时,SDA从低变为高(SCL = 1 && SDA = 1)

I2C整体时序图(图源正点原子手册):
时序
(2)器件地址: 每个I2C器件都有一个器件地址,本次实验中器件地址的构成为固定部分和可编程部分。器件地址的存在非常必须,因为有的实验中可能用到多个I2C器件,而如果没有器件地址,就不知道要传输数据到哪个器件中。

(3)字地址: I2C器件内部会有可供读写的寄存器或者存储器,因此我们进行读写时,需要指定存储单元的地址,即字地址。器件地址和字地址是不一样的,器件地址是用于分别数据要传输到哪个器件,字地址是器件中存储单元的地址,即要传输数据到器件的哪个存储单元。
(注意:字地址的判断也必不可少,即判断是1Byte还是2Byte的数据)

那么基本概念介绍完毕,接着介绍I2C具体时序
在SCL为低时,允许SDA改变数据位,SCL为高时,SDA不能够改变;相当于一个时钟周期传输1bit,经过8个时钟周期传输8bit,而在第8个时钟周期末,主机释放SDA以使从机应答(即主机释放控制权),在第9个时钟周期,从机将SDA拉低以应答;如果在第9个时钟周期,SCL为高时SDA未检测到低电平,则视为非应答,表明此次数据传输失败。在第9个时钟周期末,从机释放SDA使主机继续传输数据,若主句发送停止信号,则此次传输结束。(注意:数据以8bit即一个字节为单位串行发出,其最先发送的是字节的最高位)

发送顺序:(写数据–单次写)
起始信号--------器件地址+写命令------从机应答-------字地址--------从机应答--------8bit数据--------从机应答--------从机发送停止信号

发送顺序:(读数据–随机读)
起始信号--------器件地址+写命令--------从机应答--------字地址--------从机应答--------主机发送起始信号--------器件地址+读命令--------从机应答--------从机发送8bit数据--------主机非应答--------主机发送停止信号

介绍完以上,接下来介绍总体模块分布
(1)顶层模块
(2)EEPROM读写模块:主要进行总体读写的控制切换,并输出比较结果
(3)I2C驱动模块:I2C的具体时序
(4)LED模块:通过结果控制灯亮或是闪烁

二、代码及分析

顶层模块:eeprom_top

module eeprom_top(
input sys_clk,
input sys_rst_n,
output iic_scl,
inout iic_sda,
output led
    );
    
parameter SLAVE_ADDR = 7'b1010000;
parameter BIT_CTRL = 1'b1;
parameter CLK_FREQ = 26'd50_000_000;
parameter I2C_FREQ = 18'd250_000;
parameter L_TIME   = 17'd125_000;

wire dri_clk;
wire i2c_exec;
wire [15:0] i2c_addr;
wire [7:0]  i2c_data_w;
wire i2c_done;
wire i2c_ack;
wire i2c_rh_wl;
wire [7:0] i2c_data_r;
wire rw_done;
wire rw_result;

eeprom_rw u_eeprom_rw(
.clk        (sys_clk) ,                   
.rst_n      (sys_rst_n) ,                 
.i2c_rh_wl  (i2c_rh_wl) ,        
.i2c_exec   (i2c_exec) ,         
.i2c_addr   (i2c_addr) ,  
.i2c_data_w (i2c_data_w) , 
.i2c_data_r (i2c_data_r) ,      
.i2c_done   (i2c_done) ,              
.i2c_ack    (i2c_ack) ,               
.rw_done    (rw_done) ,          
.rw_result  (rw_result)       
);

i2c_dri #(
.SLAVE_ADDR (SLAVE_ADDR),
.CLK_FREQ   (CLK_FREQ) ,
.I2C_FREQ   (I2C_FREQ)
)
u_i2c_dri(
.clk (sys_clk),
.rst_n (sys_rst_n),
.i2c_exec (i2c_exec),
.bit_ctrl (BIT_CTRL),
.i2c_rh_wl (i2c_rh_wl),
.i2c_addr (i2c_addr),
.i2c_data_w (i2c_data_w),
.i2c_data_r (i2c_data_r),
.i2c_done   (i2c_done),
.i2c_ack    (i2c_ack),
.scl        (iic_scl),
.sda        (iic_sda),
.dri_clk    (dri_clk)
);

led_alarm #(.L_TIME(L_TIME))
u_led_alarm(
.clk    (dri_clk), // !!
.rst_n  (sys_rst_n),
.rw_done (rw_done),
.rw_result (rw_result),
.led     (led)
);    

endmodule

EEPROM读写模块:eeprom_rw

module eeprom_rw(
input clk,
input rst_n,
output reg i2c_rh_wl,//读写控制位
output reg i2c_exec,//执行信号,每出现一次就执行一位数据的读写
output reg [15:0] i2c_addr,//地址
output reg [7:0]  i2c_data_w,//写数据(我们造的)
input [7:0] i2c_data_r,//(读数据)
input i2c_done,//整个I2C进行了一整次读/写完毕的信号
input i2c_ack,//应答信号
output reg rw_done,//判断读写测试是否完毕
output reg rw_result//判断读写测试是否成功
    );
    
parameter WR_WAIT_TIME = 14'd5000;//每写一位数据都要等待的时长
parameter MAX_BYTE     = 16'd256;

reg [1:0] flow_cnt;//计数器,用于状态转换
reg [13:0] wait_cnt;

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        flow_cnt  <= 2'b0;
        i2c_rh_wl <= 1'b0;
        i2c_exec  <= 1'b0;
        i2c_addr  <= 16'b0;
        i2c_data_w<= 8'b0;
        wait_cnt  <= 14'b0;
        rw_done   <= 1'b0;
        rw_result <= 1'b0; 
    end
    else begin
        i2c_exec <= 1'b0;
        rw_done  <= 1'b0;
        case(flow_cnt)
        //2'd0:写数据等待时间+状态切换(读/写)
            2'd0 : begin
                wait_cnt <= wait_cnt + 1'b1;
                if(wait_cnt == WR_WAIT_TIME - 1'b1) begin
                    wait_cnt <= 14'b0;
                    if(i2c_addr == MAX_BYTE)begin
                        i2c_rh_wl <= 1'b1;
                        i2c_addr  <= 16'b0;
                        flow_cnt  <= 2'd2;
                    end
                    else begin
                        flow_cnt  <= flow_cnt + 1'b1;
                        i2c_exec  <= 1'b1;
                    end
                end
            end
            //2'd1:写数据
            2'd1 : begin
                if(i2c_done == 1'b1)begin //??????????????????????????
                    flow_cnt <= 2'd0;
                    i2c_addr <= i2c_addr +
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值