FPGA之EEPROM实验

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;
                    i2
  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
FPGA读写EEPROM芯片AT24C02实验Verilog逻辑源码Quartus11.0工程文件, FPGA型号为CYCLONE4E系列中的EP4CE6E22C8,可以做为你的学习设计参考。 module iic_com( clk,rst_n, sw1,sw2, scl,sda, dis_data ); input clk; // 50MHz input rst_n; //复位信号,低有效 input sw1,sw2; //按键1、2,(1按下执行写入操作,2按下执行读操作) output scl; // 24C02的时钟端口 inout sda; // 24C02的数据端口 output[7:0] dis_data; //数码管显示的数据 //按键检测 reg sw1_r,sw2_r; //键值锁存寄存器,每20ms检测一次键值 reg[19:0] cnt_20ms; //20ms计数寄存器 always @ (posedge clk or negedge rst_n) begin if(!rst_n) cnt_20ms <= 20'd0; else cnt_20ms <= cnt_20ms+1'b1; //不断计数 end always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin sw1_r <= 1'b1; //键值寄存器复位,没有键盘按下时键值都为1 sw2_r <= 1'b1; end else if(cnt_20ms == 20'hfffff) begin sw1_r <= sw1; //按键1值锁存 sw2_r <= sw2; //按键2值锁存 end end //--------------------------------------------- //分频部分 reg[2:0] cnt; // cnt=0:scl上升沿,cnt=1:scl高电平中间,cnt=2:scl下降沿,cnt=3:scl低电平中间 reg[8:0] cnt_delay; //500循环计数,产生iic所需要的时钟 reg scl_r; //时钟脉冲寄存器 always @ (posedge clk or negedge rst_n) begin if(!rst_n) cnt_delay <= 9'd0; else if(cnt_delay == 9'd499) cnt_delay <= 9'd0; //计数到10us为scl的周期,即100KHz else cnt_delay <= cnt_delay+1'b1; //时钟计数 end always @ (posedge clk or negedge rst_n) begin if(!rst_n) cnt <= 3'd5; else begin case (cnt_delay) 9'd124: cnt <= 3'd1; //cnt=1:scl高电平中间,用于数据采样 9'd249: cnt <= 3'd2; //cnt=2:scl下降沿 9'd374: cnt <= 3'd3; //cnt=3:scl低电平中间,用于数据变化 9'd499: cnt <= 3'd0; //cnt=0:scl上升沿 default: cnt <= 3'd5; endcase end end `define SCL_POS (cnt==3'd0) //cnt=0:scl上升沿 `define SCL_HIG (cnt==3'd1) //cnt=1:scl高电平中间,用于数据采样 `define SCL_NEG (cnt==3'd2) //cnt=2:scl下降沿 `define SCL_LOW (cnt==3'd3) //cnt=3:scl低电平中间,用于数据变化 always @ (posedge clk or negedge rst_n) begin if(!rst_n) scl_r <= 1'b0; else if(cnt==3'd0) scl_r <= 1'b1; //scl信号上升沿

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值