基于FPGA的IIC读写EEPROM

本次所用开发板FPGA芯片型号为:EP4CE6F17C8 EEPROM

                                                                      芯片型号为:24LC04B UART串口设置为:

                                                                       波特率11520,无校验位。

        本次仅实现了EEPROM的单字节读写。若要实现连续读写可以在下文的EEPROM控制模块设置计数器控制读写字数。

一、I2C协议

1.1 I2C协议简介

         ①IIC:(Inter-Integrated Circuit)即集成电路总线,是一种两线 式串行总线,由 PHILIPS 公司开发,用于连接微控制器及其外围设备。 多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。

         ②IIC 总线由数据线 SDA 和时钟线 SCL 构成通信线路,既可用于 发送数据,也可接收数据,是一种半双工通信协议。
        ③需要注意的是:总线上的主设备与从设备之间以字节(8bit) 为单位进行双向的数据传输的
         下面将通过两个方面:1.物理层 2.协议层 讲解 IIC 通信协议
1.2 物理层

物理层的特点:

(1) 它是一个支持多设备的总线(支持多主机多从机)。

(2) IIC 总线只使用两条总线线路,一条双向串行数据线(SDA) 一条串行时钟线(SCL)。数据线即用来表示数据,时钟线用于数据收发同步。

(3) 每个连接到 IIC 总线的设备都有一个独立的地址,主机可以利用设备独立地址访问不同设备。

(4) IIC 总线通过上拉电阻接到电源。当 IIC 设备空闲时,设备会输出高阻态,当所有设备都空闲,都输出高阻态时,由上拉电阻把 IIC总线拉成高电平。

(5) IIC 总线具有仲裁机制。当多个主机同时发起传输时,触发仲裁机制,最终只给一个主机授权。

(6)具有三种传输模式:

1.3 IIC 协议层:

IIC 协议空闲状态

IIC 协议的起始信号

IIC 协议的数据传输(数据读写)状态(应答信号(ACK))

IIC 协议的停止信号

图中标注的①②③④表示 IIC 协议的 4 个状态,

分别为“总线空闲状态”、“起始信号”、“数据读/写状态”和“停止信号”。

空闲状态:图中标注①表示“总线空闲状态”,在此状态下串口时钟信号SCL和串行数据信号 SDA均保持高电平(都为 1),此时无IIC设备工作。

起始位:

        图中标注②表示“起始信号”,在 IIC 总线处于“空闲状态”时, 时钟信号线 SCL 为高电平时,数据信号线 SDA 被拉低(由高电平变为低电平),出现下降沿,表示产生了一个起始信号。起始信号是由主机(本实验中为 FPGA)主动建立的,在建立该信号之前IIC总线必须处于空闲状态。

数据传输:

        同时,协议规定在SCL高电平时期SDA数据线必须保持稳定,在SCL低电平时,SDA才允许发生改变。主机进行数据读写时,I2C协议规定,数据传输时先发送寻址字节,即7位从机地址+0/1。其中0表示主机写,1表示主机读。寻址字节发送完成后才是数据字节。

应答位:

        I2C协议规定,主机每次向从机传输1字节数据后,需要接收一次从机的应答信号。0为接收成功;1为接受失败,没有响应。

停止位:

        当SCL为高电平时,数据线SDA拉高,则认为检测到停止位,一次数据传输结束。

注意:SCL高电平数据必须保持,SCL低电平数据才能改变。

二、EEPROM

2.1 基本信息

        EEPROM的全称是“电可擦除可编程只读存储器”,即Electrically Erasable Programmable Read-Only Memory。本次项目中使用的EEPROM型号为24LC04B。

        24LC04B 的存储容量为 4Kbit,其内部有两个 Block,每个Block中有256个字节(一个字节为 8bit)。其读写操作都是以字节(8bit)为基本单位。24LC04B EEPROM存储芯片的器件地址包括厂商设置的高4 1010和用户需自主设置的低3A0A1A2 

        在IIC主从设备通讯时,主机在发送了起始信号后,接着会向从机发送控制命令。控制命令长度为1个字节,它的高7位为上文讲解的IIC设备的器件地址,最低位为读写控制位。EEPROM储存芯片控制命令格式示意图,具体见下图:

读写控制位为 0 时,表示主机( FPGA )要对从机( EEPROM )进行数据写入操作;读写控制位为 1 时,表示主机( FPGA )要对从机( EEPROM )进行数据读出操作。

三、IIC协议读写EEPROM 

        EEPROM的写操作有:

①单字节写(Byte WRITE) ②页写(Page WRITE)

        EEPROM的读操作有:

①当前地址读(Current Address READ)

②随机地址读(RANDOM READ)

③顺序地址读(SEQUENTIAL READ)

3.1 EEPROM单字节写(Byte WRITE)操作时序

        单字节写操作:在数据信号线SDA上,发起始 -> 控制字节,从机接收到发应答信号 -> 写数据地址,从机接收到发应答信号,如果数据地址是2位的话,就继续写数据地址,从机接收到发应答信号 -> 写数据,从机接收到发应答信号 -> 最后发停止位。

3.2 EEPROM页写操作时序

页写(PAGE WRITE)操作:一次写入16个字节(每字节为8bit)数据。

        在数据信号线SDA上,发起始位 -> 写控制字节,从机接收到应答信号 -> 写数据地址,从机接收到应答信号,如果数据地址是2位的话,就继续写数据地址,从机接收到应答信号 -> 写数据,从机接收到应答信号 -> 继续写数据,直到写完全部的数据 -> 最后发停止位。

3.3 EEPROM当前地址读(Current Address READ)操作时序
        

        当前地址读(Curren Address READ)操作:在数据线SDA上,发起始位 -> 写控制字节,从机接收到应答信号,然后读数据,无应答信号(发No ACK) ->  发停止位。

3.4 EEPROM 随机地址读 (RANDOM READ) 操作时序

        随机地制读(RANDOM READ)操作:在数据信号线SDA上,发起始位 -> 写控制字节,从机接收到应答信号 -> 再发起始位 -> 读控制字节,从机接收到应答信号 -> 接收读数据 -> 发无应答(No ACK)-> 最后发停止位。

3.5 EEPROM 顺序地址读(SEQUENTIAL READ)操作时序

        顺序地址读(RANDOM READ)操作:在数据信号线SDA上,发起始位 -> 写控制字节,从机接收到应答信号 ->  虚写(dummuy write),写数据地址,从机接收到应答信号  -> 再发开始位 -> 读控制字节,从机接收到应答信号 -> 然后接收读数据 -> 发No ACK -> 最后发结束位

顺序地址读实质就是在随机地制读之后

四、状态机

4.1 IIC接口模块

4.2 EEPROM控制模块

 五、程序

5.1、IIC接口模块

/**************************************功能介绍***********************************
Date	: 2024年5月20日16:16:29
Author	: 
Version	: 1.0
Description: I2C接口模块

            接口命令列表(cmd):
                            0           1   
            bit0(起始位)  NO          YES
            bit1(写数据)  NO          YES
            bit2(读数据)  NO          YES
            bit3(停止位)  NO          YES
            bit4(响应位)  ACK         NO ACK

*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module I2C_driver( 
    input				clk		,
    input				rst_n	,

    input       [7:0]   wr_data ,//要写进的数据
    input       [4:0]   cmd     ,//命令信号
    input               cmd_vld ,//命令信号有效信号

    output      [7:0]   rd_data ,//读出的数据
    output              rd_data_vld,

    output  reg         rev_ack ,//记响应位为ack还是no ack

    output              done    ,//表示写/读完8bit的数据
    
    output  reg         scl     ,
    inout               sda      
);
//---------<参数定义>--------------------------------------------------------- 
    //状态机参数定义
    localparam  IDLE        = 7'b0000001,//空闲状态
                START       = 7'b0000010,//起始位
                WR_DATA     = 7'b0000100,//写状态(包括写控制字节,写地址和写数据)
                RD_DATA     = 7'b0001000,//读状态
                R_ACK       = 7'b0010000,//收ACK(由EEPROM发送给FPGA)
                T_ACK       = 7'b0100000,//发ACK(由FPGA发送给EEPROM)考虑为ACK还是NO ACK
                STOP        = 7'b1000000;//结束位

    parameter   T = 100_000,//速率 100k,400k,3.4M
                SCL_MAX = 50_000_000 / T;//速率为100k时,计1bit需要计数500
    parameter   SCL_LOW_HALF  = (SCL_MAX * 1 / 4) - 1,
                SCL_HIGH_HALF = (SCL_MAX * 3 / 4) - 1;
    
    `define     START_BIT   5'b00001
    `define     WRITE_BIT   5'b00010
    `define     READ_BIT    5'b00100
    `define     STOP_BIT    5'b01000
    `define     ACK_BIT     5'b10000

    `define     ACK         0
    `define     NO_ACK      1
//---------<内部信号定义>-----------------------------------------------------
    reg 	[6:0]	cstate     ;//现态
    reg	    [6:0]	nstate     ;//次态
    
    reg     [4:0]   cmd_r       ;//cmd打一拍
    reg     [7:0]   wr_data_r   ;
    reg     [7:0]   rd_data_r   ;

    reg                 sda_out     ;
    reg                 OE          ;//三态门使能信号
    wire                sda_in      ;
    
    reg			[8:0]	cnt_bit	   	;//计1bit(IIC工作时钟)
    wire				add_cnt_bit	;
    wire				end_cnt_bit	;

    reg			[3:0]	cnt_num	   	;//计8bit
    wire				add_cnt_num	;
    wire				end_cnt_num	;
    reg         [3:0]   num         ;

    //状态转移条件
    wire    IDLE_START      ;
    wire    START_WR_DATA   ;
    wire    WR_DATA_R_ACK   ;
    wire    R_ACK_IDLE      ;
    wire    IDLE_WR_DATA    ;
    wire    R_ACK_STOP      ;
    wire    STOP_IDLE       ;  

    wire    IDLE_RD_DATA    ;
    wire    RD_DATA_T_ACK   ;
    wire    T_ACK_IDLE      ;
    wire    T_ACK_STOP      ;

    //寄存wr_data_r和cmd_r
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            wr_data_r <= 'd0;
            cmd_r <= 'd0;
        end
        else if (cmd_vld) begin
            wr_data_r <= wr_data;
            cmd_r <= cmd;
        end
    end
/****************************************************************
                            计数器
****************************************************************/
    //计1bit(IIC工作时钟)
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_bit <= 'd0;
        end 
        else if(add_cnt_bit)begin 
            if(end_cnt_bit)begin 
                cnt_bit <= 'd0;
            end
            else begin 
                cnt_bit <= cnt_bit + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_bit = cstate != IDLE;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == SCL_MAX - 1'd1;
//IIC_SCL
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            scl <= 'd1;
        end
        else if (cnt_bit == (SCL_MAX - 1 ) >> 1 || STOP_IDLE) begin
            scl <= 'd1;
        end
        else if (end_cnt_bit) begin
            scl <= 'd0;
        end
    end
    //8bit计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_num <= 'd0;
        end 
        else if(add_cnt_num)begin 
            if(end_cnt_num)begin 
                cnt_num <= 'd0;
            end
            else begin 
                cnt_num <= cnt_num + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_num = end_cnt_bit;
    assign end_cnt_num = add_cnt_num && cnt_num == num - 1;
    //考察计bit数最大值
    always @(*) begin
        case (cstate)
            IDLE    : num = 1;
            START   : num = 1;
            WR_DATA : num = 8;
            RD_DATA : num = 8;
            R_ACK   : num = 1;
            T_ACK   : num = 1;
            STOP    : num = 1;
            default : num = 1;
        endcase
    end
/****************************************************************
                            状态机
****************************************************************/
    //第一段:时序逻辑描述状态转移
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cstate <= IDLE;
        end 
        else begin 
            cstate <= nstate;
        end 
    end
    //第二段:组合逻辑描述状态转移规律和状态转移条件
    always @(*) begin
        case(cstate)
            IDLE    : begin
                if (IDLE_START) begin
                    nstate = START;
                end
                else if (IDLE_WR_DATA) begin
                    nstate = WR_DATA;
                end
                else if (IDLE_RD_DATA) begin
                    nstate = RD_DATA;
                end
                else begin
                    nstate = cstate;
                end
            end 
            START   : begin
                if (START_WR_DATA) begin
                    nstate = WR_DATA;
                end
                else begin
                    nstate = cstate;
                end
            end 
            WR_DATA : begin
                if (WR_DATA_R_ACK) begin
                    nstate = R_ACK;
                end
                else begin
                    nstate = cstate;
                end
            end 
            RD_DATA : begin
                if (RD_DATA_T_ACK) begin
                    nstate = T_ACK;
                end
                else begin
                    nstate = cstate;
                end
            end 
            R_ACK   : begin
                if (R_ACK_STOP) begin
                    nstate = STOP;
                end
                else if (R_ACK_IDLE) begin
                    nstate = IDLE;
                end
                else begin
                    nstate = cstate;
                end
            end 
            T_ACK   : begin
                if (T_ACK_STOP) begin
                    nstate = STOP;
                end
                else if (T_ACK_IDLE) begin
                    nstate = IDLE;
                end
                else begin
                    nstate = cstate;
                end
            end 
            STOP    : begin
                if (STOP_IDLE) begin
                    nstate = IDLE;
                end
                else begin
                    nstate = cstate;
                end
            end 
            default : nstate = cstate;
        endcase
    end

 assign  IDLE_START      = (cstate == IDLE)      && cmd_vld      && (cmd & `START_BIT)   ;//存在起始位
 assign  START_WR_DATA   = (cstate == START)     && end_cnt_num  && (cmd_r & `WRITE_BIT)   ;//计1bit起始位并写数据有效
 assign  WR_DATA_R_ACK   = (cstate == WR_DATA)   && end_cnt_num                          ;//计8bit数据位
 assign  R_ACK_IDLE      = (cstate == R_ACK)     && end_cnt_num  && !(cmd_r & `STOP_BIT) ;//计1bit响应位,没有停止位
 assign  IDLE_WR_DATA    = (cstate == IDLE)      && cmd_vld      && (cmd & `WRITE_BIT) ;//写数据有效
 assign  R_ACK_STOP      = (cstate == R_ACK)     && end_cnt_num  && (cmd_r & `STOP_BIT)  ;//计1bit响应位,有停止位
 assign  STOP_IDLE       = (cstate == STOP)      && end_cnt_num                          ;//计1bit停止位
 assign  IDLE_RD_DATA    = (cstate == IDLE)      && cmd_vld      && (cmd & `READ_BIT)    ;//读数据有效
 assign  RD_DATA_T_ACK   = (cstate == RD_DATA)   && end_cnt_num                          ;//计8bit数据位
 assign  T_ACK_IDLE      = (cstate == T_ACK)     && end_cnt_num  && !(cmd_r & `STOP_BIT) ;//计1bit响应位,没有停止位
 assign  T_ACK_STOP      = (cstate == T_ACK)     && end_cnt_num  && (cmd_r & `STOP_BIT)  ;//计1bit响应位,有停止位               
/****************************************************************
                            三态门
****************************************************************/
    assign sda = OE ? sda_out : 1'bz;
    assign sda_in = sda;

    //考察OE
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            OE <= 'b0;
        end
        else if (IDLE_START || START_WR_DATA || IDLE_WR_DATA || R_ACK_STOP || RD_DATA_T_ACK) begin//FPGA发送,EEPROM接收
            OE <= 'b1;
        end
        else if (IDLE_RD_DATA || WR_DATA_R_ACK || STOP_IDLE) begin//FPGA接收,EEPROM发送
            OE <= 'b0;
        end
    end
/****************************************************************
                            输出
****************************************************************/
    //考察输出
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            sda_out <= 1;
        end
        else begin
            case (cstate)
                IDLE    :sda_out <= 1;
                START   :begin//起始位scl一直为高电平,检测sda需要由高电平变低电平
                    if (cnt_bit == SCL_LOW_HALF) begin
                        sda_out <= 'b1;
                    end
                    else if (cnt_bit == SCL_HIGH_HALF) begin
                        sda_out <= 'b0;
                    end
                end
                WR_DATA :begin//高电平保持,低电平值变化采样
                    if (cnt_bit == SCL_LOW_HALF) begin
                        sda_out <= wr_data_r[7 - cnt_num];//高位先发
                    end
                end
                T_ACK   :begin
                    if (cnt_bit == SCL_LOW_HALF) begin
                        if (cmd & `ACK_BIT) begin//命令响应位为1:NO ACK
                            sda_out <= `NO_ACK;
                        end
                        else begin
                            sda_out <= `ACK;//命令响应位为0:ACK
                        end
                    end
                end
                STOP    :begin//检测sda需要由低电平变高电平
                    if (cnt_bit == SCL_LOW_HALF) begin
                        sda_out <= 'b0;
                    end
                    else if (cnt_bit == SCL_HIGH_HALF) begin
                        sda_out <= 'b1;
                    end
                end
                default: sda_out <= 'b1;
            endcase
        end
    end
/****************************************************************
                            ACK
****************************************************************/
    //数据接收(考察EEPROM发送数据给FPGA的情况)
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rev_ack <= 0;
            rd_data_r <= 8'b0;
        end
        else begin
            case (cstate)
                RD_DATA:begin
                    if (cnt_bit == SCL_HIGH_HALF) begin//高电平采样
                        rd_data_r[7-cnt_num] <= sda_in;
                    end
                end
                R_ACK  :begin
                    if (cnt_bit == SCL_HIGH_HALF) begin
                        rev_ack <= sda_in;
                    end
                end
                default:; 
            endcase
        end
    end

    assign done = R_ACK_IDLE || T_ACK_IDLE || STOP_IDLE;
    assign rd_data = rd_data_r;
    assign rd_data_vld = T_ACK_IDLE || T_ACK_STOP;
endmodule

5.2 IIC控制模块

/**************************************功能介绍***********************************
Date	: 2024年5月20日16:19:44
Author	:
Version	: 1.0
Description: I2C控制模块
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module I2C_control #(
    parameter ADDR_BIT = 8 //从机寄存器地址的位宽
)( 
    input				            clk		    ,
    input				            rst_n	    ,
    input                           wr_req      ,//写使能
    input                           rd_req      ,//读使能
    input       [6:0]               device_id   ,//从机设备ID
    input       [ADDR_BIT - 1:0]    reg_addr    ,//读写地址
    input                           reg_addr_vld,
    input       [7:0]               wr_data     ,
    input                           wr_data_vld ,

    output      [7:0]               rd_data     ,
    output                          rd_data_vld ,
    output                          ready       ,
    output                          scl         ,

    inout                           sda         
);								 
//---------<参数定义>--------------------------------------------------------- 
    //接口模块控制命令
    `define     START_BIT   5'b00001
    `define     WRITE_BIT   5'b00010
    `define     READ_BIT    5'b00100
    `define     STOP_BIT    5'b01000
    `define     ACK_BIT     5'b10000

    //状态机参数定义
    localparam  IDLE     = 6'b000001,//
                WR_REQ   = 6'b000010,//
                WR_WAIT  = 6'b000100,//
                RD_REQ   = 6'b001000,//
                RD_WAIT  = 6'b010000,//
                DONE     = 6'b100000;//

    localparam  WR_CTRL_BYTE = 8'b1010_0000;
    localparam  RD_CTRL_BYTE = 8'b1010_0001;
//---------<内部信号定义>-----------------------------------------------------
    reg 	[5:0]	cstate     ;//现态
    reg	    [5:0]	nstate     ;//次态
    
    wire    IDLE_WR_REQ     ;
    wire    IDLE_RD_REQ     ;
    wire    WR_REQ_WR_WAIT  ;
    wire    RD_REQ_RD_WAIT  ;
    wire    WR_WAIT_WR_REQ  ;
    wire    WR_WAIT_DONE    ;
    wire    RD_WAIT_RD_REQ  ;
    wire    RD_WAIT_DONE    ;
    wire    DONE_IDLE       ;

    reg         [2:0]   num             ;//字节计数器最大值(写3字节,读4字节)
    wire                done            ;
    reg         [4:0]   cmd             ;
    reg                 cmd_vld         ;
    reg         [7:0]   op_wr_data      ;

    reg         [15:0]  addr_r          ;
    reg         [7:0]   wr_data_r       ;

    reg			[2:0]	cnt_byte	   	;
    wire				add_cnt_byte	;
    wire				end_cnt_byte	;
    //同步数据打拍
    reg                 wr_req_r;
    reg                 rd_req_r;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            wr_req_r <=0;
            rd_req_r <= 0;
        end
        else begin
            wr_req_r <= wr_req;
            rd_req_r <= rd_req;
        end
    end
    
    //寄存
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            addr_r <= 'd0;
        end
        else if (reg_addr_vld) begin
            addr_r <= reg_addr;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            wr_data_r <= 'd0;
        end
        else if (wr_req) begin
            wr_data_r <= wr_data;
        end
    end

    //字节计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_byte <= 'd0;
        end 
        else if(add_cnt_byte)begin 
            if(end_cnt_byte)begin 
                cnt_byte <= 'd0;
            end
            else begin 
                cnt_byte <= cnt_byte + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_byte = done;
    assign end_cnt_byte = add_cnt_byte && cnt_byte == num - 1;
    
    //考察字节计数器最大值(写3字节,读4字节)
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            num <= 1;
        end
        else if (wr_req) begin
            num <= 4;
        end
        else if (rd_req) begin
            num <= 5;
        end
        else if (end_cnt_byte) begin
            num <= 1;
        end
    end

    assign IDLE_WR_REQ    = (cstate == IDLE)    && wr_req_r;//写使能拉高
    assign IDLE_RD_REQ    = (cstate == IDLE)    && rd_req_r;//读使能拉高
    assign WR_REQ_WR_WAIT = (cstate == WR_REQ)  && 1;
    assign RD_REQ_RD_WAIT = (cstate == RD_REQ)  && 1;
    assign WR_WAIT_WR_REQ = (cstate == WR_WAIT) && done;//写完1byte
    assign WR_WAIT_DONE   = (cstate == WR_WAIT) && end_cnt_byte;//写完3byte
    assign RD_WAIT_RD_REQ = (cstate == RD_WAIT) && done;//读完1byte
    assign RD_WAIT_DONE   = (cstate == RD_WAIT) && end_cnt_byte;//读完4byte
    assign DONE_IDLE      = (cstate == DONE)    && 1;

    //第一段:时序逻辑描述状态转移
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cstate <= IDLE;
        end 
        else begin 
            cstate <= nstate;
        end 
    end
    
    //第二段:组合逻辑描述状态转移规律和状态转移条件
    always @(*) begin
        case(cstate)
            IDLE    :begin
                if (IDLE_WR_REQ) begin
                    nstate = WR_REQ;
                end
                else if (IDLE_RD_REQ) begin
                    nstate = RD_REQ;
                end
                else begin
                    nstate = cstate;
                end
            end 
            WR_REQ  :begin
                if (WR_REQ_WR_WAIT) begin
                    nstate = WR_WAIT;
                end
                else begin
                    nstate = cstate;
                end
            end 
            WR_WAIT :begin
                if (WR_WAIT_DONE) begin
                    nstate = DONE;
                end
                else if (WR_WAIT_WR_REQ) begin
                    nstate = WR_REQ;
                end
                else begin
                    nstate = cstate;
                end
            end 
            RD_REQ  :begin
                if (RD_REQ_RD_WAIT) begin
                    nstate = RD_WAIT;
                end
                else begin
                    nstate = cstate;
                end
            end 
            RD_WAIT :begin
                if (RD_WAIT_DONE) begin
                    nstate = DONE;
                end
                else if (RD_WAIT_RD_REQ) begin
                    nstate = RD_REQ;
                end
                else begin
                    nstate = cstate;
                end
            end 
            DONE    :begin
                if (DONE_IDLE) begin
                    nstate = IDLE;
                end
                else begin
                    nstate = cstate;
                end
            end 
            default : nstate = cstate;
        endcase
    end
                
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            TX(0,4'h0,8'h00);
        end
        else begin
            case (cstate)
                RD_REQ:begin
                    case (cnt_byte)
                        0   :  TX(1,(`START_BIT | `WRITE_BIT),WR_CTRL_BYTE);
                        1   :  TX(1,(`WRITE_BIT             ),addr_r[15:8]);
                        2   :  TX(1,(`WRITE_BIT             ),addr_r[7:0] );
                        3   :  TX(1,(`START_BIT | `WRITE_BIT),RD_CTRL_BYTE);
                        4   :  TX(1,(`READ_BIT  | `STOP_BIT ),8'h00       );
                        default: TX(0,cmd,op_wr_data);
                    endcase
                end
                WR_REQ:begin
                    case (cnt_byte)
                        0   :  TX(1,(`START_BIT | `WRITE_BIT),WR_CTRL_BYTE);
                        1   :  TX(1,(`WRITE_BIT             ),addr_r[15:8]);
                        2   :  TX(1,(`WRITE_BIT             ),addr_r[7:0] );
                        3   :  TX(1,(`WRITE_BIT | `STOP_BIT ),wr_data_r   );
                        default: TX(0,cmd,op_wr_data);
                    endcase
                end
                default: TX(0,cmd,op_wr_data);
            endcase
        end
    end

    I2C_driver u_I2C_driver(
        .clk		 (clk),
        .rst_n	     (rst_n),
        .wr_data     (op_wr_data),
        .cmd         (cmd),
        .cmd_vld     (cmd_vld),
        .rd_data     (rd_data),
        .rd_data_vld (rd_data_vld),
        .done        (done),
        .scl         (scl),
        .sda         (sda)
    );

    task TX;
        input           task_cmd_vld    ;
        input   [3:0]   task_cmd        ;
        input   [7:0]   task_wr_data    ;

        begin
            cmd_vld     = task_cmd_vld  ;
            cmd         = task_cmd      ;
            op_wr_data  = task_wr_data  ;
        end
    endtask

    assign ready = cstate == IDLE;         
endmodule

5.3按键消抖模块(可以不用)

/**************************************功能介绍***********************************
Date	: 2024年5月20日16:33:32
Author	: 
Version	: 1.0
Description: 四位按键消抖模块
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module key_debounce #(parameter   WIDTH = 1,
                      parameter   DELAY_20MS = 1000_000
)( 
    input				           clk		,
    input				           rst_n	,
    input		   [WIDTH-1:0]     key_in	,
    output         [WIDTH-1:0]     key_out   
);								 
//---------<参数定义>--------------------------------------------------------- 
    
    reg [WIDTH-1:0] key_out_r;
    //状态机参数定义
    localparam  IDLE         = 4'b0001,//空闲状态
                FILETER_DOWN = 4'b0010,//按键按下抖动状态
                HOLD_DOWN    = 4'b0100,//按下稳定按下状态
                FILTER_UP    = 4'b1000;//按键释放抖动状态
    
//---------<内部信号定义>-----------------------------------------------------
    reg 	[3:0]	        cstate     ;//现态
    reg	    [3:0]	        nstate     ;//次态

    reg     [WIDTH-1:0]     key_r0      ;//同步打拍
    reg     [WIDTH-1:0]     key_r1      ;
    reg     [WIDTH-1:0]     key_r2      ;
    wire    [WIDTH-1:0]     n_edge      ;//下降沿
    wire    [WIDTH-1:0]     p_edge      ;//上升沿  

    reg		[19:0]	        cnt_20ms	;//20ms计数器
    wire				    add_cnt_20ms;
    wire				    end_cnt_20ms;

    //状态转移条件定义
    wire            idle2filter_down        ;
    wire            fiter_down2hold_down    ;
    wire            hold_down2filter_up     ;
    wire            filter_up2idle          ;

//****************************************************************
//--cstate
//****************************************************************
    //第一段:时序逻辑描述状态转移
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cstate <= IDLE;//复位的初始状态
        end 
        else begin 
            cstate <= nstate;
        end 
    end
    
    //第二段:组合逻辑描述状态转移规律和状态转移条件
    always @(*) begin
        case(cstate)
            IDLE         : begin 
                if(idle2filter_down)begin 
                    nstate = FILETER_DOWN;
                end
                else begin
                    nstate = cstate;
                    // state_n = IDLE;
                end
            end
            FILETER_DOWN : begin 
                if(fiter_down2hold_down)begin 
                    nstate = HOLD_DOWN;
                end
                else begin
                    nstate = cstate;
                end
            end
            HOLD_DOWN    : begin 
                if(hold_down2filter_up)begin 
                    nstate = FILTER_UP;
                end
                else begin
                    nstate = cstate;
                end
            end
            FILTER_UP    : begin 
                if(filter_up2idle)begin 
                    nstate = IDLE;
                end
                else begin
                    nstate = cstate;
                end
            end
            default : nstate = IDLE;
        endcase
    end

    assign idle2filter_down     = cstate == IDLE         && n_edge;
    assign fiter_down2hold_down = cstate == FILETER_DOWN && end_cnt_20ms;
    assign hold_down2filter_up  = cstate == HOLD_DOWN    && p_edge;
    assign filter_up2idle       = cstate == FILTER_UP    && end_cnt_20ms;
                
//****************************************************************
//--n_edge、p_edge 
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            key_r0 <= {WIDTH{1'b1}};
            key_r1 <= {WIDTH{1'b1}};
            key_r2 <= {WIDTH{1'b1}};
        end 
        else begin 
            key_r0 <= key_in;
            key_r1 <= key_r0;
            key_r2 <= key_r1;
        end 
    end      
          
    assign n_edge = ~key_r1 & key_r2;
    assign p_edge = ~key_r2 & key_r1;          
    
//****************************************************************
//--cnt_20ms
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_20ms <= 'd0;
        end 
        else if(add_cnt_20ms)begin 
            if(end_cnt_20ms)begin 
                cnt_20ms <= 'd0;
            end
            else begin 
                cnt_20ms <= cnt_20ms + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_20ms = cstate == FILETER_DOWN || cstate == FILTER_UP;
    assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == DELAY_20MS - 1;
    
//****************************************************************
//--key_out
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            key_out_r <= 'd0;
        end 
        else if(hold_down2filter_up)begin 
            key_out_r <= ~key_r2;
        end 
        else begin 
            key_out_r <= 'd0;
        end 
    end
    
    assign key_out = key_out_r;
endmodule

 5.4串口接收模块(非必要)

/**************************************功能介绍***********************************
Date	: 22024年5月20日16:38:29
Author	: 
Version	: 1.0
Description: FPGA收上位机发来的数据【1bit(波形)变8bit】
*********************************************************************************/

//---------<模块及端口声名>------------------------------------------------------
module uart_rx( 
    input				clk		,
    input				rst_n	,
    input               rx      ,
    output              rx_data_vld,
    output      [7:0]   rx_data            
);								 
//---------<参数定义>--------------------------------------------------------- 
    parameter   MAX_BPS = 115200;
    parameter   CLOCK = 50_000_000;
    parameter   MAX_1bit = CLOCK/MAX_BPS;//1bit要计434次
    parameter   CHECK_BIT = "None";//None无校验,Odd奇校验,Even偶校验
    //状态机参数定义
    localparam  IDLE   = 'b0001,//空闲状态
                START  = 'b0010,//起始位
                DATA   = 'b0100,//数据位
                CHECK  = 'b1000;
//---------<内部信号定义>-----------------------------------------------------    
    reg 	[3:0]	cstate     ;//现态
    reg	    [3:0]	nstate     ;//次态
    
    wire    IDLE_START;
    wire    START_DATA;
    wire    DATA_IDLE;
    wire    DATA_CHECK;
    wire    CHECK_IDLE;

    reg			[8:0]	cnt_baud	   	;//波特计数器,波特率115200
    wire				add_cnt_baud	;
    wire				end_cnt_baud	;

    reg			[2:0]	cnt_bit	   	;//bit计数器,起始位1bit,数据位8bit,结束位1bit
    wire				add_cnt_bit	;
    wire				end_cnt_bit	;
    
    reg         [3:0]   bit_max;//bit最大值,复用需要考察每个状态的bit值

    reg         [7:0]   rx_temp;
    reg                 rx_check;
    wire                check_val; 

    reg                 rx_r1;
    reg                 rx_r2;
    wire                rx_nege;

    //打两拍
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rx_r1 <= 1;
            rx_r2 <= 1;
        end
        else begin
            rx_r1 <= rx;
            rx_r2 <= rx_r1;
        end
    end

    assign rx_nege = ~rx_r1 && rx_r2;

    //计434次
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_baud <= 'd0;
        end 
        else if(add_cnt_baud)begin 
            if(end_cnt_baud)begin 
                cnt_baud <= 'd0;
            end
            else begin 
                cnt_baud <= cnt_baud + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_baud = cstate != IDLE;
    assign end_cnt_baud = add_cnt_baud && cnt_baud == MAX_1bit - 1'd1;
    
    //bit计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_bit <= 'd0;
        end 
        else if(add_cnt_bit)begin 
            if(end_cnt_bit)begin 
                cnt_bit <= 'd0;
            end
            else begin 
                cnt_bit <= cnt_bit + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_bit = end_cnt_baud;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == bit_max -1'd1;
    
    //计数器复用
    always @(*)begin 
        case (cstate)
            IDLE :bit_max = 'd0;
            START:bit_max = 'd1;//起始位1bit
            DATA :bit_max = 'd8;//数据位8bit
            CHECK:bit_max = 'd1;
            default: bit_max = 'd0;
        endcase
    end

    assign IDLE_START = (cstate == IDLE) && rx_nege;//识别到起始位0
    assign START_DATA = (cstate == START) && end_cnt_bit;//计1bit数据
    assign DATA_IDLE = (cstate == DATA) && end_cnt_bit && CHECK_BIT == "None";//计8bit数据
    assign DATA_CHECK = (cstate == DATA) && end_cnt_bit;
    assign CHECK_IDLE = (cstate == CHECK) && end_cnt_bit;

    //第一段:时序逻辑描述状态转移
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cstate <= IDLE;
        end 
        else begin 
            cstate <= nstate;
        end 
    end
    
    //第二段:组合逻辑描述状态转移规律和状态转移条件
    always @(*) begin
        case(cstate)
            IDLE  :begin
                if (IDLE_START) begin
                    nstate = START;
                end
                else begin
                    nstate = cstate;
                end
            end
            START :begin
                if (START_DATA) begin
                    nstate = DATA;
                end
                else begin
                    nstate = cstate;
                end
            end
            DATA  :begin
                if (DATA_IDLE) begin
                    nstate = IDLE;
                end
                else if (DATA_CHECK) begin
                    nstate = CHECK;
                end
                else begin
                    nstate = cstate;
                end
            end
            CHECK:begin
                if (CHECK_IDLE) begin
                    nstate = IDLE;
                end
                else begin
                    nstate = cstate;
                end
            end
            default : nstate = IDLE;
        endcase
    end

    //接受校验位
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rx_check <= 0;
        end
        else if (cstate == CHECK && cnt_baud == MAX_1bit >>1) begin
            rx_check <= rx_r1;
        end
    end

    //计算校验位
    assign check_val = (CHECK_BIT == "Odd") ? ~^rx_temp : ^rx_temp;

    //第三段:描述输出,时序逻辑或组合逻辑皆可
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rx_temp <= 0;
        end
        else if (cstate == DATA && cnt_baud == MAX_1bit >> 1) begin//电平中间值采样,边沿采样容易出错
            rx_temp[cnt_bit] <= rx_r1;
        end
        else begin
            rx_temp <= rx_temp;
        end
    end


    assign rx_data = rx_temp;
    assign rx_data_vld  = (CHECK_BIT == "None") ? DATA_IDLE
                          :(CHECK_IDLE && (check_val == rx_check)) ? 1
                          : 0;
    
endmodule

5.5串口发送模块(非必要)

/**************************************功能介绍***********************************
Date	: 2024年5月20日16:40:06
Author	: 
Version	: 2.0
Description: FPGA向上位机发送数据【8bit变1bit(波形)】
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module uart_tx( 
    input				clk		,
    input				rst_n	,
    input       [7:0]   tx_data ,
    input               tx_data_vld,
    output              ready   ,
    output  reg         tx              
);								 
//---------<参数定义>--------------------------------------------------------- 
    parameter   MAX_BPS = 115200;
    parameter   CLOCK = 50_000_000;
    parameter   MAX_1bit = CLOCK/MAX_BPS;//1bit要计434次
    parameter   CHECK_BIT = "None";//None无校验,Odd奇校验,Even偶校验
    //状态机参数定义
    localparam  IDLE   = 'b00001,//空闲状态
                START  = 'b00010,//起始位
                DATA   = 'b00100,//数据位
                CHECK  = 'b01000,//校验位
                STOP   = 'b10000;//停止位
//---------<内部信号定义>-----------------------------------------------------    
    reg 	[4:0]	cstate     ;//现态
    reg	    [4:0]	nstate     ;//次态
    
    wire    IDLE_START;
    wire    START_DATA;
    wire    DATA_CHECK;
    wire    CHECK_STOP;
    wire    STOP_IDLE;

    reg			[8:0]	cnt_baud	   	;//波特计数器,波特率115200
    wire				add_cnt_baud	;
    wire				end_cnt_baud	;

    reg			[2:0]	cnt_bit	   	;//bit计数器,起始位1bit,数据位8bit,结束位1bit
    wire				add_cnt_bit	;
    wire				end_cnt_bit	;
    
    reg         [3:0]   bit_max;//bit最大值,复用需要考察每个状态的bit值
    reg         [7:0]   tx_data_r;
    
    wire                check_val;
    
    //计434次
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_baud <= 'd0;
        end 
        else if(add_cnt_baud)begin 
            if(end_cnt_baud)begin 
                cnt_baud <= 'd0;
            end
            else begin 
                cnt_baud <= cnt_baud + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_baud = cstate != IDLE;
    assign end_cnt_baud = add_cnt_baud && cnt_baud == MAX_1bit - 1'd1;
    
    //bit计数器
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_bit <= 'd0;
        end 
        else if(add_cnt_bit)begin 
            if(end_cnt_bit)begin 
                cnt_bit <= 'd0;
            end
            else begin 
                cnt_bit <= cnt_bit + 1'd1;
            end 
        end
    end 
    
    assign add_cnt_bit = end_cnt_baud;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == bit_max -1'd1;
    
    //计数器复用
    always @(*)begin 
        case (cstate)
            IDLE :bit_max = 'd0;
            START:bit_max = 'd1;//起始位1bit
            DATA :bit_max = 'd8;//数据位7bit
            CHECK:bit_max = 'd1;//校验位1bit
            STOP :bit_max = 'd1;//结束位1bit
            default: bit_max = 'd0;
        endcase
    end

    assign IDLE_START = (cstate == IDLE) && tx_data_vld;//考察到开始传输信号
    assign START_DATA = (cstate == START) && end_cnt_bit;//计1bit数据
    assign DATA_STOP = (cstate == DATA) && end_cnt_bit && CHECK_BIT == "None";
    assign DATA_CHECK = (cstate == DATA) && end_cnt_bit;//计8bit数据
    assign CHECK_STOP = (cstate ==CHECK) && end_cnt_bit;//计1bit数据
    assign STOP_IDLE = (cstate == STOP) && end_cnt_bit;//计1bit数据

    //第一段:时序逻辑描述状态转移
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cstate <= IDLE;
        end 
        else begin 
            cstate <= nstate;
        end 
    end
    
    //第二段:组合逻辑描述状态转移规律和状态转移条件
    always @(*) begin
        case(cstate)
            IDLE  :begin
                if (IDLE_START) begin
                    nstate = START;
                end
                else begin
                    nstate = cstate;
                end
            end
            START :begin
                if (START_DATA) begin
                    nstate = DATA;
                end
                else begin
                    nstate = cstate;
                end
            end
            DATA  :begin
                if (DATA_CHECK) begin
                    nstate = CHECK;
                end
                else if (DATA_STOP) begin
                    nstate = STOP;
                end
                else begin
                    nstate = cstate;
                end
            end
            CHECK :begin
                if (CHECK_STOP) begin
                    nstate = STOP;
                end
                else begin
                    nstate = cstate;
                end
            end
            STOP  :begin
                if (STOP_IDLE) begin
                    nstate = IDLE;
                end
                else begin
                    nstate = cstate;
                end
            end
            default : nstate = cstate;
        endcase
    end

    //寄存一拍
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            tx_data_r <= 'd0;
        end
        else if (tx_data_vld) begin
            tx_data_r <= tx_data;
        end
        else begin
            tx_data_r <= tx_data_r;
        end
    end
    
    assign check_val = (CHECK_BIT == "Odd") ? ~^tx_data_r : ^tx_data_r;
    
    //第三段:描述输出,时序逻辑或组合逻辑皆可
    always @(*)begin 
        case (cstate)
            IDLE : tx = 1'b1;
            START: tx = 1'b0;//起始位为0
            DATA : tx = tx_data_r[cnt_bit];
            CHECK: tx = check_val;
            STOP : tx = 1'b1;//结束位为1
            default: tx = 1'b1;
        endcase
    end            
                
    assign ready = cstate == IDLE;//当状态为IDLE时,表示tx端可以接收数据
    
    
    
endmodule

5.6顶层模块: 

 

/**************************************功能介绍***********************************
Date	: 2024年5月20日16:54:32
Author	: 
Version	: 
Description: 
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module top( 
    input				clk		,
    input				rst_n	,
    input		    	key_in	,
    input		        rx      ,
    output		        tx      ,
    output              scl     ,

    inout               sda     
);								 
//---------<参数定义>--------------------------------------------------------- 
    wire            rd_req;
    wire            wr_req;
    wire    [7:0]   wr_data;
    wire    [7:0]   rd_data;
    wire            rd_data_vld;
//---------<内部信号定义>-----------------------------------------------------
    key_debounce u_key_debounce(
        .clk    (clk),
        .rst_n	(rst_n),
        .key_in	(key_in),
        .key_out(rd_req)   
    );
    
    uart_rx u_uart_rx(
        .clk		 (clk),
        .rst_n	     (rst_n),
        .rx          (rx),
        .rx_data_vld (wr_req),
        .rx_data     (wr_data)
    );
    
    uart_tx u_uart_tx(
        .clk		(clk),
        .rst_n	    (rst_n),
        .tx_data    (rd_data),
        .tx_data_vld(rd_data_vld),
        .ready      (),
        .tx         (tx)     
    );

    I2C_control u_I2C_control(
        .clk		 (clk),
        .rst_n	     (rst_n),
        .wr_req      (wr_req),
        .rd_req      (rd_req),
        .device_id   (7'b1010000),
        .reg_addr    (8'h03),
        .reg_addr_vld(1'b1),
        .wr_data     (wr_data),
        .wr_data_vld (wr_req),
        .rd_data     (rd_data),
        .rd_data_vld (rd_data_vld),
        .ready       (),
        .scl         (scl),
        .sda         (sda)
    );
endmodule

 

  • 25
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
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信号上升沿
FPGA 读写i2c_eeprom_Verilog逻辑源码Quartus工程文件+文档说明,EEPROM 型号24LC04,,FPGA型号Cyclone4E系列中的EP4CE6F17C8,Quartus版本17.1。 module i2c_eeprom_test( input clk, input rst_n, input key1, inout i2c_sda, inout i2c_scl, output [5:0] seg_sel, output [7:0] seg_data ); localparam S_IDLE = 0; localparam S_READ = 1; localparam S_WAIT = 2; localparam S_WRITE = 3; reg[3:0] state; wire button_negedge; reg[7:0] read_data; reg[31:0] timer; wire scl_pad_i; wire scl_pad_o; wire scl_padoen_o; wire sda_pad_i; wire sda_pad_o; wire sda_padoen_o; reg[ 7:0] i2c_slave_dev_addr; reg[15:0] i2c_slave_reg_addr; reg[ 7:0] i2c_write_data; reg i2c_read_req; wire i2c_read_req_ack; reg i2c_write_req; wire i2c_write_req_ack; wire[7:0] i2c_read_data; ax_debounce ax_debounce_m0 ( .clk (clk), .rst (~rst_n), .button_in (key1), .button_posedge (), .button_negedge (button_negedge), .button_out () ); wire[6:0] seg_data_0; seg_decoder seg_decoder_m0( .bin_data (read_data[3:0]), .seg_data (seg_data_0) ); wire[6:0] seg_data_1; seg_decoder seg_decoder_m1( .bin_data (read_data[7:4]), .seg_data (seg_data_1) ); seg_scan seg_scan_m0( .clk (clk), .rst_n (rst_n), .seg_sel (seg_sel), .seg_data (seg_data), .seg_data_0 ({1'b1,7'b1111_111}), .seg_data_1 ({1'b1,7'b1111_111}), .seg_data_2 ({1'b1,7'b1111_111}), .seg_data_3 ({1'b1,7'b1111_111}), .seg_data_4 ({1'b1,seg_data_1}), .seg_data_5 ({1'b1,seg_data_0}) ); always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) begin state <= S_IDLE; i2c_write_req <= 1'b0; read_data <= 8'h00; timer <= 32'd0; i2c_write_data <= 8'd0; i2c_slave_reg_addr <= 16'd0; i2c_slave_dev_addr <= 8'ha0;//1010 000 0(default address ‘000’ write operation) i2c_read_req <= 1'b0; en
FPGA上实现IIC总线驱动来读写EEPROM是一种常见的应用。通过引用和中的内容,我们可以了解到以下步骤和问题解决方案: 1. IIC协议简介:IIC协议是一种串行通信协议,它允许多个设备在同一总线上进行通信。IIC协议在硬件结构上包含了两根线路,即串行数据线(SDA)和串行时钟线(SCL)。 2. 设计思想:在FPGA上实现IIC读写EEPROM的设计思路是通过编写Verilog HDL代码来实现IIC总线的驱动功能,并通过IIC总线对EEPROM进行读写测试。 3. RTL代码重要部分:在RTL代码的实现中,需要考虑起始信号、应答信号以及数据的读写时序。根据引用的内容,可能会遇到从机无应答、应答正常但读出数据不对以及时钟问题等问题。对于从机无应答的情况,可以通过添加状态机中的等待应答的机制来解决。对于应答正常但读出数据不对的情况,可能是EEPROM本身的问题,可以尝试重新上电来解决。而时钟问题可以通过添加边沿检测程序来解决。 4. 开发过程遇到的问题:在开发过程中可能会遇到一些问题,如从机无应答、读出数据错误等。通过使用SignalTap抓取波形图可以帮助分析问题所在。 综上所述,实现IIC读写EEPROM的FPGA设计可以根据IIC协议的时序特点编写Verilog HDL代码,并解决可能出现的问题,如从机无应答、读出数据错误等。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [FPGA中 通过IIC读写EEPROM驱动代码示例](https://download.csdn.net/download/qq_20222919/12733052)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [FPGA实现IIC通信(读写EEPROM)](https://blog.csdn.net/xs_sd/article/details/114534036)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值