• 基于 FPGA 实现 IIC(I2C) 协议控制 EEPROM 读写操作


    本文内容:FPGA 作为主机,控制 EEPROM 芯片,进行数据读写,同时将写入或读出的数据和地址显示在数码管上,并有标记

    • 前一篇文章:基于 FPGA 使用 Verilog 实现 IIC(I2C) 协议回环数据传输 的 FPGA 作为从机,PC 作为主机,所以 FPGA 是以从机的形式写的源码
    • 本篇文章,是以 FPGA 作为主机,控制 EEPROM 芯片数据读写,区别也不是很大,协议也完全相同,原理就不介绍了,直接上源代码

    一、源码

    1.1 顶层模块

    IIC_top.v

    /*========================================*\
        filename        : IIC_top.v
        description     : IIC顶层模块
        up file         : 
        reversion       : 
            v1.0 : 2022-8-22 16:15:32
        author          : 张某某
    \*========================================*/
    
    module IIC_top (
        input                               clk             ,
        input                               rst_n           ,
        input           [ 1:0]              key_in          ,
    
        inout                               SDA             ,
    
        output                              SCL             ,
        output          [ 5:0]              SEL             ,
        output          [ 7:0]              DIG
    );
    
    // Parameter definition
        wire            [ 1:0]              press           ;
        wire                                SDA_in          ;        
        wire                                SDA_out         ;
        wire                                SDA_oe          ;
        wire            [ 7:0]              data            ;
        wire            [ 7:0]              address         ;
        wire            [ 1:0]              eeprom_state    ;
    
    // Signal definition
    
    
    // Module calls
        key_filter          U_key_filter(
            /*input                 */  .clk            (clk            ),
            /*input                 */  .rst_n          (rst_n          ),
            /*input           [ 1:0]*/  .key_in         (key_in         ),
            /*output  reg     [ 1:0]*/  .press          (press          )
        );
    
        iic_eeprom          U_iic_eeprom(
            /*input                 */  .clk            (clk            ),
            /*input                 */  .rst_n          (rst_n          ),
            /*input           [ 1:0]*/  .press          (press          ),
            /*input                 */  .SDA_in         (SDA_in         ),
            /*output  reg           */  .SCL            (SCL            ),
            /*output  reg           */  .SDA_out        (SDA_out        ),
            /*output  reg           */  .SDA_oe         (SDA_oe         ),
            /*output  reg     [ 7:0]*/  .data_out       (data           ),
            /*output  reg     [ 7:0]*/  .address_out    (address        ),
            /*output  reg     [ 1:0]*/  .state_out      (eeprom_state   )  // 0表示空闲,1表示写,2表示读
        );
    
        display             U_display(
            /*input                 */  .clk            (clk            ), // 50MHz
            /*input                 */  .rst_n          (rst_n          ), // 复位信号
            /*input           [ 1:0]*/  .eeprom_state   (eeprom_state   ),
            /*input           [ 7:0]*/  .data           (data           ),
            /*input           [ 7:0]*/  .address        (address        ),
            /*output  reg     [ 5:0]*/  .SEL            (SEL            ), // SEL信号
            /*output  reg     [ 7:0]*/  .DIG            (DIG            )  // DIG信号
        );
    
    // Logic description
        assign SDA_in = SDA;
        assign SDA = SDA_oe ? SDA_out : 1'bz;
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    1.2 按键模块

    key_filter.v

    /*========================================*\
        filename        : key_filter.v
        description     : 按键消抖模块
        up file         : IIC_top.v
        reversion       : 
            v1.0 : 2022-8-22 16:15:09
        author          : 张某某
    \*========================================*/
    
    module key_filter #(parameter MS_20 = 20'd1000_000)(
        input                               clk         ,
        input                               rst_n       ,
        input           [ 1:0]              key_in      ,
      
        output  reg     [ 1:0]              press
    );
    // 全局变量定义
    
    // 信号定义
        reg             [ 1:0]              key_0       ; // 按键信号当前时钟周期电平
        reg             [ 1:0]              key_1       ; // 按键信号下一个时钟周期电平
    
        wire            [ 1:0]              key_nedge   ; // 下降沿使能信号
        reg                                 add_flag    ; // 计数使能信号
        reg             [19:0]              delay_cnt   ; // 延时计数器
    
    // 模块功能
        //打拍器
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                key_0 <= 'b1;
                key_1 <= 'b1;
            end
            else begin
                key_0 <= key_in;
                key_1 <= key_0;
            end
        end
    
        // 检测下降沿
        assign key_nedge = ~key_0 & key_1;
    
        // 计数使能信号
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                add_flag <= 'b0;
            end
            else if (key_nedge) begin
                add_flag <= 'b1;
            end
            else if (delay_cnt >= MS_20 - 1) begin
                add_flag <= 'b0;
            end
            else begin
                add_flag <= add_flag;
            end
        end
    
        // 计数20ms
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                delay_cnt <= 20'd0;
            end
            else if (add_flag) begin
                if (delay_cnt >= MS_20 - 1) begin
                    delay_cnt <= 20'd0;
                end
                else begin
                    delay_cnt <= delay_cnt + 1;
                end
            end
            else begin
                delay_cnt <= 20'd0;
            end
        end
    
        // 输出
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                press <= 'd0;
            end
            else if (delay_cnt >= MS_20 - 1) begin
                press <= ~key_in;
            end
            else begin
                press <= 'd0;
            end
        end
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90

    1.3 IIC模块

    iic_eeprom.v

    /*========================================*\
        filename        : iic_eeprom.v
        description     : IIC控制EEPROM
        up file         : IIC_top.v
        reversion       : 
            v1.0 : 2022-8-22 16:14:06
        author          : 张某某
    \*========================================*/
    
    module iic_eeprom (
        input                               clk                 ,
        input                               rst_n               ,
        input           [ 1:0]              press               ,
        input                               SDA_in              ,
    
        output  reg                         SCL                 ,
        output  reg                         SDA_out             ,
        output  reg                         SDA_oe              ,
        output  reg     [ 7:0]              data_out            ,
        output  reg     [ 7:0]              address_out         ,
        output  reg     [ 1:0]              state_out             // 0表示空闲,1表示写,2表示读
    );
    
    // Parameter definition
        parameter       IDLE        =       7'b000_0001         ,
                        START       =       7'b000_0010         ,
                        WR_DATA     =       7'b000_0100         ,
                        RD_DATA     =       7'b000_1000         ,
                        CHECK_ACK   =       7'b001_0000         ,
                        SEND_NACK   =       7'b010_0000         ,
                        STOP        =       7'b100_0000         ;
    
    // Signal definition    
        reg             [ 6:0]              state_c             ;
        reg             [ 6:0]              state_n             ;
    
        wire                                idle2start          ;
        wire                                start2wr_data       ;
        wire                                wr_data2check_ack   ;
        wire                                check_ack2start     ;
        wire                                check_ack2wr_data   ;
        wire                                check_ack2rd_data   ;
        wire                                rd_data2send_nack   ;
        wire                                check_ack2stop      ;
        wire                                send_nack2stop      ;
        wire                                stop2idle           ;
    
        reg             [ 6:0]              cnt_400KHz          ; // SCL频率计数器
        wire                                add_cnt_400KHz      ;
        wire                                end_cnt_400KHz      ;
    
        reg                                 SCL_0               ; // SCL打拍器
        wire                                SCL_podge           ; // SCL上升沿
        wire                                SCL_nedge           ; // SCL下降沿
    
        reg             [ 2:0]              cnt_bit             ; // 比特计数器
        wire                                add_cnt_bit         ;
        wire                                end_cnt_bit         ;
    
        reg             [ 1:0]              cnt_byte            ; // 字节计数器
        wire                                add_cnt_byte        ;
        wire                                end_cnt_byte        ;
    
        reg             [ 1:0]              eeprom_state        ; // 0表示空闲,1表示写,2表示读
        reg             [ 7:0]              data_buffer         ; // 数据缓冲器
        reg             [ 7:0]              pointer_wr          ; // 写数据指针
        reg             [ 7:0]              pointer_rd          ; // 读数据指针
    
        reg             [ 7:0]              data_generate       ; // 生成写入EEPROM的数据
        wire                                add_data_generate   ;
        wire                                end_data_generate   ;
    
    // Logic description
        /*************************************
            第一段 状态转移
        *************************************/
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                state_c <= IDLE;
            end
            else begin
                state_c <= state_n;
            end
        end
    
        /*************************************
            第二段 状态转移规律
        *************************************/
        always @(*) begin
            case (state_c)
                IDLE : 
                    begin
                        if (idle2start) begin
                            state_n = START;
                        end
                        else begin
                            state_n = state_c;
                        end
                    end
                START :
                    begin
                        if (start2wr_data) begin
                            state_n = WR_DATA;
                        end
                        else begin
                            state_n = state_c;
                        end
                    end
                WR_DATA :
                    begin
                        if (wr_data2check_ack) begin
                            state_n = CHECK_ACK;
                        end
                        else begin
                            state_n = state_c;
                        end
                    end
                RD_DATA :
                    begin
                        if (rd_data2send_nack) begin
                            state_n = SEND_NACK;
                        end
                        else begin
                            state_n = state_c;
                        end
                    end
                CHECK_ACK :
                    begin
                        if (check_ack2start) begin
                            state_n = START;
                        end
                        else if (check_ack2rd_data) begin
                            state_n = RD_DATA;
                        end
                        else if (check_ack2wr_data) begin
                            state_n = WR_DATA;
                        end
                        else if (check_ack2stop) begin
                            state_n = STOP;
                        end
                        else begin
                            state_n = state_c;
                        end
                    end
                SEND_NACK :
                    begin
                        if (send_nack2stop) begin
                            state_n = STOP;
                        end
                        else begin
                            state_n = state_c;
                        end
                    end
                STOP :
                    begin
                        if (stop2idle) begin
                            state_n = IDLE;
                        end
                        else begin
                            state_n = state_c;
                        end
                    end
            endcase
        end
    
        assign idle2start           = state_c == IDLE && press;
        assign start2wr_data        = state_c == START && SCL_nedge;
        assign wr_data2check_ack    = state_c == WR_DATA && end_cnt_bit && cnt_byte <= 'd3;
        assign check_ack2start      = state_c == CHECK_ACK && cnt_byte == 'd1 && SCL_nedge && eeprom_state == 'd2;
        assign check_ack2wr_data    = state_c == CHECK_ACK && SCL_nedge && cnt_byte < 'd2;
        assign check_ack2rd_data    = state_c == CHECK_ACK && cnt_byte == 'd2 && SCL_nedge && eeprom_state == 'd2;
        assign rd_data2send_nack    = state_c == RD_DATA && end_cnt_bit && cnt_byte <= 'd3;
        assign check_ack2stop       = state_c == CHECK_ACK && cnt_byte == 'd2 && SCL_nedge && eeprom_state == 'd1;
        assign send_nack2stop       = state_c == SEND_NACK && cnt_byte == 'd3 && SCL_nedge;
        assign stop2idle            = state_c == STOP && cnt_400KHz == 'd57;
    
        /*************************************
            第三段 描述输出
        *************************************/
        // 按键控制EEPROM读写
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                eeprom_state <= 'd0;
            end
            else if (stop2idle) begin
                eeprom_state <= 'd0;
            end
            else if (state_c == IDLE) begin
                case (press)
                    2'b10 : eeprom_state <= 'd1; // 写状态
                    2'b01 : eeprom_state <= 'd2; // 读状态
                    default: eeprom_state <= eeprom_state;
                endcase
            end
            else begin
                eeprom_state <= eeprom_state;
            end
        end
    
        // 按键控制指针
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                pointer_wr <= 'd0;
                pointer_rd <= 'd0;
            end
            else if (stop2idle) begin
                case (eeprom_state)
                    'd1 : pointer_wr <= pointer_wr + 'd1;
                    'd2 : pointer_rd <= pointer_rd + 'd1;
                    default: begin pointer_wr <= pointer_wr; pointer_rd <= pointer_rd;end
                endcase
            end
            else begin
                pointer_wr <= pointer_wr;
                pointer_rd <= pointer_rd;
            end
        end
    
        // SCL打拍器
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                SCL_0 <= 'd1;
            end
            else begin
                SCL_0 <= SCL;
            end
        end
    
        assign SCL_podge = SCL & ~SCL_0; // SCL上升沿
        assign SCL_nedge = ~SCL & SCL_0; // SCL下降沿
    
        // SCL计数器
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                cnt_400KHz <= 'd0;
            end
            else if (add_cnt_400KHz) begin
                if (end_cnt_400KHz) begin
                    cnt_400KHz <= 'd0;
                end
                else begin
                    cnt_400KHz <= cnt_400KHz + 'd1;
                end
            end
            else begin
                cnt_400KHz <= 'd0;
            end
        end
        assign add_cnt_400KHz = state_c != IDLE;
        assign end_cnt_400KHz = add_cnt_400KHz && cnt_400KHz >= 'd124;
    
        // SCL输出
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                SCL <= 'd1;
            end
            else begin
                case (cnt_400KHz)
                    'd0  : SCL <= 'd1;
                    'd59 : SCL <= 'd0;
                    default: SCL <= SCL;
                endcase
            end
        end
    
        // 比特计数器
        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 + 'd1;
                end
            end
            else begin
                cnt_bit <= cnt_bit;
            end
        end
        assign add_cnt_bit = (state_c == WR_DATA || state_c == RD_DATA) && SCL_nedge;
        assign end_cnt_bit = add_cnt_bit && cnt_bit >= 'd7;
    
        // 字节计数器
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                cnt_byte <= 'd0;
            end
            else if (check_ack2stop || send_nack2stop) 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 + 'd1;
                end
            end
            else begin
                cnt_byte <= cnt_byte;
            end
        end
        assign add_cnt_byte = check_ack2wr_data || check_ack2rd_data;
        assign end_cnt_byte = add_cnt_byte && cnt_byte >= 'd3;
    
        // 生成顺序数据
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                data_generate <= 'd0;
            end
            else if (add_data_generate) begin
                if (end_data_generate) begin
                    data_generate <= 'd1;
                end
                else begin
                    data_generate <= data_generate + 'd1;
                end
            end
            else begin
                data_generate <= data_generate;
            end
        end
        assign add_data_generate = state_c == IDLE && press == 2'b10;
        assign end_data_generate = add_data_generate && data_generate >= 8'hff;
    
        // 数据缓冲器
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                data_buffer <= 'd0;
            end
            else if (state_c == WR_DATA || state_c == RD_DATA) begin
                case (eeprom_state)
                    'd1 : // 1表示写数据到EEPROM中
                        begin
                            case (cnt_byte)
                                'd0 : data_buffer <= 8'ha0;
                                'd1 : data_buffer <= pointer_wr;
                                'd2 : data_buffer <= data_generate;
                                default: data_buffer <= 'd0;
                            endcase
                        end
                    'd2 : // 2表示从EEPROM中读数据
                        begin
                            case (cnt_byte)
                                'd0 : data_buffer <= 8'ha0;
                                'd1 : data_buffer <= pointer_rd;
                                'd2 : data_buffer <= 8'ha1;
                                'd3 : 
                                    begin
                                        if (SCL) begin
                                            data_buffer[7 - cnt_bit] <= SDA_in;
                                        end
                                        else begin
                                            data_buffer <= data_buffer;
                                        end
                                    end
                                default: data_buffer <= 'd0;
                            endcase
                        end
                    default: data_buffer <= 'd0;
                endcase
            end
            else begin
                data_buffer <= 'd0;
            end
        end
    
        // SDA_out输出
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                SDA_oe <= 'd1;
                SDA_out <= 'd1;
            end
            else begin
                case (state_c)
                    START : 
                        begin
                            if (cnt_400KHz == 'd29) begin
                                SDA_oe <= 'd1;
                                SDA_out <= 'd0;
                            end
                            else if (cnt_400KHz == 'd69) begin
                                SDA_oe <= 'd1;
                                SDA_out <= 'd1;
                            end
                            else begin
                                SDA_oe <= SDA_oe;
                                SDA_out <= SDA_out;
                            end
                        end
                    WR_DATA :
                        begin
                            if (cnt_400KHz == 'd69) begin
                                SDA_oe <= 'd1;
                                SDA_out <= data_buffer[7 - cnt_bit];
                            end
                            else begin
                                SDA_oe <= SDA_oe;
                                SDA_out <= SDA_out;
                            end
                        end
                    RD_DATA :
                        begin
                            SDA_oe <= 'd0;
                            SDA_out <= 'd0;
                        end
                    CHECK_ACK :
                        begin
                            if (cnt_400KHz == 'd69) begin
                                SDA_oe <= 'd0;
                                SDA_out <= 'd0;
                            end
                            else begin
                                SDA_oe <= SDA_oe;
                                SDA_out <= SDA_out;
                            end
                        end
                    SEND_NACK :
                        begin
                            if (cnt_400KHz == 'd69) begin
                                SDA_oe <= 'd1;
                                SDA_out <= 'd1;
                            end
                            else begin
                                SDA_oe <= SDA_oe;
                                SDA_out <= SDA_out;
                            end
                        end
                    STOP :
                        begin
                            if (cnt_400KHz == 'd69) begin
                                SDA_oe <= 'd1;
                                SDA_out <= 'd0;
                            end
                            else if (cnt_400KHz == 'd29) begin
                                SDA_oe <= 'd1;
                                SDA_out <= 'd1;
                            end
                            else begin
                                SDA_oe <= SDA_oe;
                                SDA_out <= SDA_out;
                            end
                        end
                    default: 
                        begin
                            SDA_oe <= 'd1;
                            SDA_out <= 'd1;
                        end
                endcase
            end
        end
    
        // 输出数据给数码管显示
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                data_out <= 'd0;
                address_out <= 'd0;
                state_out <= 'd0;
            end
            else if (eeprom_state == 'd1 && wr_data2check_ack && cnt_byte == 'd2) begin
                data_out <= data_buffer;
                address_out <= pointer_wr;
                state_out <= 'd1;
            end
            else if (rd_data2send_nack) begin
                data_out <= data_buffer;
                address_out <= pointer_rd;
                state_out <= 'd2;
            end
            else begin
                data_out <= data_out;
                address_out <= address_out;
                state_out <= state_out;
            end
        end
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480

    1.4 数码管模块

    display.v

    /*========================================*\
      filename        : display.v
      description     : 滚动显示输入的数据
      up file         : 
      reversion       : 
          v1.0 : 2022-7-27 18:49:34
      author          : 张某某
    \*========================================*/
    
    module display #(parameter  MS_1= 16'd50000)(
        input                               clk                 , // 50MHz
        input                               rst_n               , // 复位信号
        input           [ 1:0]              eeprom_state        ,
        input           [ 7:0]              data                ,
        input           [ 7:0]              address             ,
    
        output  reg     [ 5:0]              SEL                 , // SEL信号
        output  reg     [ 7:0]              DIG                   // DIG信号
    );
    
    // 信号定义
        reg             [15:0]              cnt_flicker         ; // 计数1ms
        wire                                SEL_change          ; // cnt_flicker计满使能信号
        reg             [ 4:0]              tmp_data            ; // 当前DIG的值
    
        wire            [ 3:0]              num_2               ; // 百位
        wire            [ 3:0]              num_1               ; // 十位
        wire            [ 3:0]              num_0               ; // 个位
    
        reg                                 pointer             ;
        
    // 逻辑描述
        // 闪烁频率计数器
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                cnt_flicker <= 'd0;
            end
            else if (SEL_change) begin
                cnt_flicker <= 'd0;
            end
            else begin
                cnt_flicker <= cnt_flicker + 'd1; 
            end
        end
        assign SEL_change = cnt_flicker >= MS_1 - 'd1 ? 1'b1 : 1'b0;
    
        // SEL信号输出
        always @(posedge clk or negedge rst_n) begin
            if (!rst_n) begin
                SEL <= 6'b011_111;
            end
            else if (SEL_change) begin
                SEL <= {SEL[4:0], SEL[5]};
            end
            else begin
                SEL <= SEL;
            end
        end
    
        // 获取地址的百位、十位、个位
        assign num_2 = address / 100;
        assign num_1 = address % 100 / 10;
        assign num_0 = address % 100 % 10;
    
        // tmp_data当前SEL位选所对应的DIG十进制值
        always @(*) begin
            case (SEL)
                6'b011_111 : begin tmp_data = data[3:0]; pointer = 'd1;end
                6'b101_111 : begin tmp_data = data[7:4]; pointer = 'd1;end
                6'b110_111 : begin tmp_data = num_0; pointer = 'd0;end
                6'b111_011 :
                    begin
                        pointer = 'd1;
                        if (num_2 == 'd0 && num_1 == 'd0) begin
                            tmp_data = 'd16;
                        end
                        else begin
                            tmp_data = num_1;
                        end
                    end
                6'b111_101 : 
                    begin
                        pointer = 'd1;
                        if (num_2) begin
                            tmp_data = num_2;
                        end
                        else begin
                            tmp_data = 'd16;
                        end
                    end
                6'b111_110 : 
                    begin
                        pointer = 'd1;
                        case (eeprom_state)
                            'd1 : tmp_data = 'd17;
                            'd2 : tmp_data = 'd18;
                            default: tmp_data = 'd16;
                        endcase
                    end
                default: begin tmp_data = 'd0; pointer = 'd1;end
            endcase
        end
    
        // DIG输出各数字对应的二进制
        always @(*) begin
            case (tmp_data)
                5'd0 : DIG = {pointer, 7'b100_0000};
                5'd1 : DIG = {pointer, 7'b111_1001};
                5'd2 : DIG = {pointer, 7'b010_0100};
                5'd3 : DIG = {pointer, 7'b011_0000};
                5'd4 : DIG = {pointer, 7'b001_1001};
                5'd5 : DIG = {pointer, 7'b001_0010};
                5'd6 : DIG = {pointer, 7'b000_0010};
                5'd7 : DIG = {pointer, 7'b111_1000};
                5'd8 : DIG = {pointer, 7'b000_0000};
                5'd9 : DIG = {pointer, 7'b001_0000};
                5'd10: DIG = {pointer, 7'b000_1000};
                5'd11: DIG = {pointer, 7'b000_0011};
                5'd12: DIG = {pointer, 7'b100_0110};
                5'd13: DIG = {pointer, 7'b010_0001};
                5'd14: DIG = {pointer, 7'b000_0110};
                5'd15: DIG = {pointer, 7'b000_1110};
                5'd16: DIG = {pointer, 7'b111_1111}; // 全黑
                5'd17: DIG = {pointer, 7'b001_1101}; // 写数据标识符
                5'd18: DIG = {pointer, 7'b010_1011}; // 读数据标识符
                default : DIG = 8'b1111_1111;
            endcase
        end
    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130

    二、仿真文件

    这里只需要对 iic_eeprom.v 文件进行仿真即可
    tb_iic_eeprom.v

    /*========================================*\
        filename        : tb_iic_eeprom.v
        description     : iic_eeprom仿真
        up file         : 
        reversion       : 
            v1.0 : 2022-8-22 16:13:37
        author          : 张某某
    \*========================================*/
    
    `timescale 1ns/1ns
    
    module tb_iic_eeprom;
    
    // Parameter definition
        parameter       CYC_CLK             = 20            ;
    
    // Drive signal
        reg                                 tb_clk          ;
        reg                                 tb_rst_n        ;
        reg             [ 1:0]              tb_press        ;
        reg                                 tb_SDA_in       ;
    
    // Observation signal
        wire                                tb_SCL          ;
        wire                                tb_SDA_out      ;
        wire                                tb_SDA_oe       ;
        wire            [ 7:0]              tb_data_out     ;
    
    // Module calls
        iic_eeprom              U_iic_eeprom(
            /*input                 */  .clk            (tb_clk         ),
            /*input                 */  .rst_n          (tb_rst_n       ),
            /*input           [ 1:0]*/  .press          (tb_press       ),
            /*input                 */  .SDA_in         (tb_SDA_in      ),
            /*output  reg           */  .SCL            (tb_SCL         ),
            /*output  reg           */  .SDA_out        (tb_SDA_out     ),
            /*output  reg           */  .SDA_oe         (tb_SDA_oe      ),
            /*output  reg     [ 7:0]*/  .data_out       (tb_data_out    ),
            /*output  reg     [ 7:0]*/  .address_out    (tb_address_out ),
            /*output  reg     [ 7:0]*/  .state_out      (tb_state_out   )
        );
    
    // System initialization
        initial begin
            tb_clk = 1'b1;
            tb_rst_n = 1'b0;
            #(CYC_CLK) tb_rst_n = 1'b1;
        end
        always #(CYC_CLK >> 1) tb_clk = ~tb_clk;
    
        initial begin
            tb_press = 2'b00;
            tb_SDA_in = 1'b0;
    
            #(1000 * CYC_CLK);
    
            // 写数据
            tb_press = 2'b10;
            #(CYC_CLK) tb_press = 2'b00;
    
            #(5000 * CYC_CLK);
    
            // 读数据
            tb_press = 2'b01;
            #(CYC_CLK) tb_press = 2'b00;
    
            #(10000 * CYC_CLK);
    
            $stop;
        end
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    三、管脚配置文件

    • 根据开发板原理图可以知道,EEPROM 采用 24LC04 芯片,再在原理图中找到对应管脚即可
      在这里插入图片描述

    tcl 文件

    set_location_assignment PIN_E1  -to clk
    set_location_assignment PIN_E15 -to rst_n
    set_location_assignment PIN_E16 -to key_in[0]
    set_location_assignment PIN_M15 -to key_in[1]
    set_location_assignment PIN_L1  -to SCL
    set_location_assignment PIN_L2  -to SDA
    set_location_assignment PIN_A4  -to SEL[0]
    set_location_assignment PIN_B4  -to SEL[1]
    set_location_assignment PIN_A3  -to SEL[2]
    set_location_assignment PIN_B3  -to SEL[3]
    set_location_assignment PIN_A2  -to SEL[4]
    set_location_assignment PIN_B1  -to SEL[5]
    set_location_assignment PIN_B7  -to DIG[0]
    set_location_assignment PIN_A8  -to DIG[1]
    set_location_assignment PIN_A6  -to DIG[2]
    set_location_assignment PIN_B5  -to DIG[3]
    set_location_assignment PIN_B6  -to DIG[4]
    set_location_assignment PIN_A7  -to DIG[5]
    set_location_assignment PIN_B8  -to DIG[6]
    set_location_assignment PIN_A5  -to DIG[7]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    四、验证结果

    • 仿真结果,前面一部分是写的波形,后面一部分是读的波形
      在这里插入图片描述
    • 板上结果

    IIC控制EEPROM

    • 按左边的按键表示写操作,按右边按键表示读操作
    • 最左边的符号,“u” 表示写数据到 EEPROM 中,“n” 表示从 EEPROM 中读出写入的数据
    • 小数点左边的数字表示当前操作的 EEPROM 地址
    • 小数点右边的数字表示写入或读出的数据
  • 相关阅读:
    关于linux的一点好奇心(四):tail -f文件跟踪实现
    JMeter笔记9 | JMeter参数化
    VUE必知必会
    阿里云 E-MapReduce 全面开启 Serverless 时代
    逻辑回归算法
    变分(Calculus of variations)的概念及运算规则(二)
    【Java面试】异常常见面试题
    Apache POI处理Miscrosoft Office 各种文件格式的开源项目
    doris的单节点安装部署(详细)
    音频基础模型LTU(Listen, Think, and Understand)
  • 原文地址:https://blog.csdn.net/ssj925319/article/details/126479261