【FPGA】FPGA实现IIC协议读写EEPROM(三) ----- 汇总篇
创始人
2024-04-08 15:10:43
0

IIC协议读写EEPROM

  • 一、功能分析/模块划分
  • 二、状态转移图
    • 1、EEPROM读写控制状态转移图
    • 2、IIC接口驱动状态转移图
  • 三、工程代码实现
    • 1、顶层模块
    • 2、EEPROM读写控制模块
    • 3、IIC接口驱动模块
    • 4、参数配置
    • 5、其他模块
  • 四、仿真测试
  • 五、上板验证

写在前面
FPGA实现IIC协议读写EEPROM相关文章:

IIC通信协议
【FPGA】FPGA实现IIC协议读写EEPROM(一) ----- IIC接口驱动实现
【FPGA】FPGA实现IIC协议读写EEPROM(二) -----EEPROM读写控制模块实现
【FPGA】FPGA实现IIC协议读写EEPROM(三) ----- 汇总篇

在前面几篇文章中介绍了IIC通信协议的相关时序、IIC接口驱动以及EEPROM读写控制模块的实现,在本项目中所使用的开发板型号:Cyclone IV E (EP4CE6F17C8),EEPROM型号:24LC04B,本文将对IIC协议读写EEPROM工程进行综合。

一、功能分析/模块划分

下面介绍此工程需要实现哪些功能,以此进行功能模块划分。

  1. 通过按键发送读/写请求给EEPROM读写控制模块控制读写时序。
  2. 通过串口发送和接收读写数据。
  3. 通过数码管显示正在进行操作。
  4. 编写IIC接口驱动实现EEPROM和控制模块的通信时序。
  5. 编写EEPROM读写控制模块,实现读写时序控制。

通过上述功能分析,我们不难把工程进行模块划分,系统框图划分如下图所示:
在这里插入图片描述

二、状态转移图

1、EEPROM读写控制状态转移图

在这里插入图片描述
EEPROM读写控制状态机实现参考文章:EEPROM读写控制状态机

2、IIC接口驱动状态转移图

在这里插入图片描述
IIC接口驱动状态机参考文章:IIC接口驱动状态机

三、工程代码实现

1、顶层模块

顶层模块verilog代码如下:

//  **************************************************************
//  Author: Zhang JunYi
//  Create Date: 2022.11.15                        
//  Design Name: i2c_eeprom    
//  Module Name: top         
//  Target Device: Cyclone IV E (EP4CE6F17C8), EEPROM(24LC04B)                
//  Tool versions: Quartus Prime 18.1             
//  Description: I2C读写EEPROM工程顶层设计模块
//  **************************************************************
module top (input           clk             ,input           rst_n           ,//  key_filterinput   [1:0]   key_in          ,// input           wr_req          ,// input           rd_req          ,//  uartinput           uart_rxd        ,output          uart_txd        ,//  eeprominout           sda             ,output          scl             ,//  数码管output  [5:0]   seg_sel         ,output  [7:0]   seg_dig           
);wire            wr_req          ;wire            rd_req          ;wire            req             ;wire   [7:0]    wr_dout         ; wire   [3:0]    cmd             ;wire   [7:0]    rdout           ;wire            rdout_vld       ;wire            rw_done         ;wire            sda_in          ;wire            sda_out         ;wire            sda_out_en      ;wire    [1:0]   rw_flag         ;//  uart_rxwire    [7:0]   rx_dout         ;wire            rx_dout_vld     ;//  uart_txwire    [7:0]   tx_din          ;wire            tx_din_vld      ;wire            ready           ;assign sda = sda_out_en ? sda_out : 1'bz ;assign sda_in = sda ;key_filter u_key_filter1 (/*input           */.clk         (clk           ),/*input           */.rst_n       (rst_n         ),/*input           */.key_in      (key_in[0]     ),/*output    reg   */.key_out     (wr_req        ));key_filter u_key_filter2 (/*input           */.clk         (clk           ),/*input           */.rst_n       (rst_n         ),/*input           */.key_in      (key_in[1]     ),/*output    reg   */.key_out     (rd_req        ));uart_rx u_uart_rx (/*input               */.clk             (clk       ),/*input               */.rst_n           (rst_n     ),/*input               */.uart_rx         (uart_rxd  ),       //  接收到的串口数据
/**//*output  [7:0]       */.rx_dout         (rx_dout   ),       //  串行数据转换成并行数据后输出给control模块/*output              */.rx_dout_vld     (rx_dout_vld));uart_tx u_uart_tx (/*input               */.clk             (clk       ),/*input               */.rst_n           (rst_n     ),/*//  control*//*input   [7:0]       */.tx_din          (tx_din    ),/*input               */.tx_din_vld      (tx_din_vld),/*output              */.ready           (ready     ),       //  给control模块的握手信号,表示可以接收数据进行发送 /*//  上位机*//*output              */.uart_tx         (uart_txd  ));eeprom_ctrl eeprom_ctrl (/*input               */.clk                 (clk           ),/*input               */.rst_n               (rst_n         ),/*//  key_filter*//*input               */.wr_req              (wr_req        ),       //  读请求/*input               */.rd_req              (rd_req        ),       //  写请求/*//  uart_rx*//*input   [7:0]       */.rx_din              (rx_dout       ),       //  uart接收到的要发送的数据/*input               */.rx_din_vld          (rx_dout_vld   ),       //  数据有效标志/*//  uart_tx*//*output  [7:0]       */.tx_dout             (tx_din        ),       //  读回的数据给到串口发送模块/*output              */.tx_dout_vld         (tx_din_vld    ),       //  数据有效/*input          m    */.ready               (ready         ),/*//  i2c_interface*//*input   [7:0]       */.rdin                (rdout         ),       //  从I2C接口读回的数据/*input               */.rdin_vld            (rdout_vld     ),/*input               */.rw_done             (rw_done       ),       //  读写一字节数据完成标志/*output              */.req                 (req           ),       //  读写请求/*output  [7:0]       */.wr_dout             (wr_dout       ),       //  要发送的数据/*output  [3:0]       */.cmd                 (cmd           ),/*output  [1:0]       */.rw_flag             (rw_flag       )       //  读写标志 00:空闲   01:写   10:读       );i2c_interface u_i2c_interface (/*input               */.clk                 (clk           ),/*input               */.rst_n               (rst_n         ),/*//  eeprom_ctrl*//*input               */.req                 (req           ),       //  读写请求/*input   [7:0]       */.wr_din              (wr_dout       ),       //  需要发送的一字节数据/*input   [3:0]       */.cmd                 (cmd           ),       //  控制命令组合/*output  [7:0]       */.rdout               (rdout         ),       //  读取的数据/*output              */.rdout_vld           (rdout_vld     ),       //  读取数据有效标志/*output              */.rw_done             (rw_done       ),       //  读写一字节完成标志/*//  EEPROM*//*input               */.sda_in              (sda_in        ),/*output              */.sda_out             (sda_out       ),/*output              */.sda_out_en          (sda_out_en    ),.scl                 (scl           ));seg_driver u_seg_driver (/*input           */.clk             (clk           ),/*input           */.rst_n           (rst_n         ),/*//  eeprom_ctrl*//*// input   [7:0]din             ,  ()     //  要显示的数据*//*// input        din_vld         ,  ()     //  显示数据有效标志*//*input   [1:0]   */.rw_flag         (rw_flag       ),       //  读写标志/*//  数码管*//*output  [5:0]   */.seg_sel         (seg_sel       ),/*output  [7:0]   */.seg_dig         (seg_dig       )             );endmodule

2、EEPROM读写控制模块

EEPROM读写控制模块verilog代码如下:

//  **************************************************************
//  Author: Zhang JunYi
//  Create Date: 2022.11.15                        
//  Design Name: i2c_eeprom    
//  Module Name: eeprom_ctrl         
//  Target Device: Cyclone IV E (EP4CE6F17C8) , EEPROM(24LC04B)               
//  Tool versions: Quartus Prime 18.1             
//  Description: 使用I2C协议读写EEPROM驱动控制模块
//  **************************************************************
module eeprom_ctrl (input               clk                 ,input               rst_n               ,//  key_filterinput               wr_req              ,       //  写请求input               rd_req              ,       //  读请求//  uart_rxinput   [7:0]       rx_din              ,       //  uart接收到的要发送的数据input               rx_din_vld          ,       //  数据有效标志//  uart_txoutput  [7:0]       tx_dout             ,       //  读回的数据给到串口发送模块output              tx_dout_vld         ,       //  数据有效input               ready               ,//  i2c_interfaceinput   [7:0]       rdin                ,       //  从I2C接口读回的数据input               rdin_vld            ,input               rw_done             ,       //  读写一字节数据完成标志output              req                 ,       //  读写请求output  [7:0]       wr_dout             ,       //  要发送的数据output  [3:0]       cmd                 ,//  seg_driveroutput  [1:0]       rw_flag                     //  读写标志 00:空闲   01:写   10:读         
);//  参数定义//  状态机参数定义localparam  IDLE        =   7'b000_0001 ,SEND_WRREQ  =   7'b000_0010 ,   //  发送写请求SEND_RDREQ  =   7'b000_0100 ,   //  发送读请求WAIT_WRDONE =   7'b000_1000 ,   //  等待写数据完成WAIT_RDDONE =   7'b001_0000 ,   //  等待读数据完成WR_DONE     =   7'b010_0000 ,   //  写数据完成RD_DONE     =   7'b100_0000 ;   //  读数据完成//  信号定义reg     [6:0]       state_c                 ;reg     [6:0]       state_n                 ;reg     [3:0]       byte_num                ;   //  字节数reg     [3:0]       cnt_byte                ;   //  字节计数器wire                add_cnt_byte            ;wire                end_cnt_byte            ;reg     [7:0]       wrdata                  ;   //  寄存将要发送的数据reg     [3:0]       command                 ;   //  寄存将要发送的命令reg                 rw_req                  ;reg     [1:0]       rwflag                  ;//  状态转移条件wire                idle2sendwrreq          ;wire                idle2sendrdreq          ;wire                sendwrreq2waitwrdone    ;wire                sendrdreq2waitrddone    ;wire                waitwrdone2wrdone       ;wire                waitwrdone2sendwrreq    ;wire                waitrddone2rddone       ;wire                waitrddone2sendrdreq    ;wire                wrdone2idle             ;wire                rddone2idle             ;reg     [7:0]       rxdata                  ;       //  串口接收数据寄存reg     [7:0]       txdata                  ;       //  串口发送数据寄存//  wrfifo// wire                wrfifo_empty            ;// wire                wrfifo_full             ;// wire    [2:0]       wrfifo_usedw            ;// wire    [7:0]       wrfifo_q                ;// wire                wrfifo_rdreq            ;// //  rdfifo// wire                rdfifo_empty            ;// wire                rdfifo_full             ;// wire    [2:0]       rdfifo_usedw            ;// wire    [7:0]       rdfifo_q                ;// wire                rdfifo_rdreq            ;//  状态机always @(posedge clk or negedge rst_n)beginif(!rst_n)beginstate_c <= IDLE ;endelse beginstate_c <= state_n ;endendalways @(*)begincase (state_c)IDLE: beginif(idle2sendwrreq)state_n = SEND_WRREQ ;else if(idle2sendrdreq)state_n = SEND_RDREQ ;elsestate_n = state_c ;endSEND_WRREQ: beginif(sendwrreq2waitwrdone)state_n = WAIT_WRDONE ;elsestate_n = state_c ;endSEND_RDREQ: beginif(sendrdreq2waitrddone)state_n = WAIT_RDDONE ;elsestate_n = state_c ;endWAIT_WRDONE: beginif(waitwrdone2wrdone)state_n = WR_DONE ;else if(waitwrdone2sendwrreq)state_n = SEND_WRREQ ;elsestate_n = state_c ;endWAIT_RDDONE: beginif(waitrddone2rddone)state_n = RD_DONE ;else if(waitrddone2sendrdreq)state_n = SEND_RDREQ ;elsestate_n = state_c ;endWR_DONE: beginif(wrdone2idle)state_n = IDLE ;elsestate_n = state_c ;endRD_DONE: beginif(rddone2idle)state_n = IDLE ;elsestate_n = state_c ;enddefault: state_n = IDLE ;endcaseend//  状态转移assign idle2sendwrreq      = state_c == IDLE        && wr_req                       ;assign idle2sendrdreq      = state_c == IDLE        && rd_req                       ;  assign sendwrreq2waitwrdone= state_c == SEND_WRREQ  && (1'b1)                       ;assign sendrdreq2waitrddone= state_c == SEND_RDREQ  && (1'b1)                       ;assign waitwrdone2wrdone   = state_c == WAIT_WRDONE && end_cnt_byte                 ;assign waitwrdone2sendwrreq= state_c == WAIT_WRDONE && (cnt_byte <= 2) && rw_done   ;assign waitrddone2rddone   = state_c == WAIT_RDDONE && end_cnt_byte                 ;assign waitrddone2sendrdreq= state_c == WAIT_RDDONE && (cnt_byte <= 3) && rw_done   ;assign wrdone2idle         = state_c == WR_DONE     && (1'b1)                       ;assign rddone2idle         = state_c == RD_DONE     && (1'b1)                       ;//  byte_numalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginbyte_num <= 0 ;endelse if(wr_req)beginbyte_num <= 3 ;endelse if(rd_req)beginbyte_num <= 4 ;end// else if(wrdone2idle | rddone2idle)begin//     byte_num <= 0 ;// endend//  cnt_bytealways @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_byte <= 0 ;endelse if(add_cnt_byte)beginif(end_cnt_byte)begincnt_byte <= 0 ;end				else begin	    cnt_byte <= cnt_byte + 1 ;end 		    endend                   assign	add_cnt_byte = rw_done ;assign	end_cnt_byte = add_cnt_byte && (cnt_byte == byte_num - 1) ;//  rw_reqalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginrw_req <= 1'b0 ;endelse if(idle2sendwrreq | idle2sendrdreq | sendwrreq2waitwrdone | sendrdreq2waitrddone)beginrw_req <= 1'b1 ;endelse beginrw_req <= 1'b0 ;endend//  wrdata   commandalways @(*)beginif(state_c == SEND_WRREQ || state_c == WAIT_WRDONE)begincase (cnt_byte)0: begin wrdata = 8'b1010_0000 ;command = 4'b1010 ; end    //  写控制字1: begin wrdata = 8'b1101_1001 ;command = 4'b1000 ; end    //  写地址2: begin wrdata = rxdata ;command = 4'b1001 ; end    //  写数据default: begin wrdata = 8'b0 ;command = 4'b0 ;endendcaseendelse if(state_c == SEND_RDREQ || state_c == WAIT_RDDONE)begincase (cnt_byte)0: begin wrdata = 8'b1010_0000 ;command = 4'b1010 ; end    //  写控制字1: begin wrdata = 8'b1101_1001 ;command = 4'b1000 ; end    //  读地址2: begin wrdata = 8'b1010_0001 ;command = 4'b1010 ; end    //  写读控制字3: begin wrdata = 8'b0 ;command = 4'b0101 ;      end    //  读数据default: begin wrdata = 8'b0 ;command = 4'b0 ;end     endcaseendelse beginwrdata = wrdata ;command = command ;endend//  rwflagalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginrwflag <= 0 ;endelse if(wr_req)beginrwflag <= 2'b01 ;endelse if(rd_req)beginrwflag <= 2'b10 ;endend//  rxdataalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginrxdata <= 0 ;endelse if(rx_din_vld)beginrxdata <= rx_din ;endend//  txdataalways @(posedge clk or negedge rst_n)beginif(!rst_n)begintxdata <= 0 ; endelse if(rdin_vld)begintxdata <= rdin ;endend//  wrfifo例化// wrfifo	wrfifo_inst (//     .aclr       ( ~rst_n        ),//     .clock      ( clk           ),//     .data       ( rx_din        ),//     .rdreq      ( wrfifo_rdreq  ),//     .wrreq      ( rx_din_vld    ),//     .empty      ( wrfifo_empty  ),//     .full       ( wrfifo_full   ),//     .q          ( wrfifo_q      ),//     .usedw      ( wrfifo_usedw  )// );// assign  wrfifo_rdreq = wr_req && ~wrfifo_empty ;// //  rdfifo例化// rdfifo	rdfifo_inst (//     .aclr       ( ~rst_n        ),//     .clock      ( clk           ),//     .data       ( rdin          ),//     .rdreq      ( rdfifo_rdreq  ),//     .wrreq      ( rdin_vld      ),//     .empty      ( rdfifo_empty  ),//     .full       ( rdfifo_full   ),//     .q          ( rdfifo_q      ),//     .usedw      ( rdfifo_usedw  )// );// assign  rdfifo_rdreq = rddone2idle && ~rdfifo_empty && ready;//  输出assign  req = rw_req ;assign  cmd = command ;assign  wr_dout = wrdata ;assign  rw_flag = rwflag ;assign  tx_dout = txdata ;assign  tx_dout_vld = rddone2idle ;endmodule                               

3、IIC接口驱动模块

EEPROM读写控制模块verilog代码如下:

//  **************************************************************
//  Author: Zhang JunYi
//  Create Date: 2022.11.15                        
//  Design Name: i2c_eeprom    
//  Module Name: i2c_interface         
//  Target Device: Cyclone IV E (EP4CE6F17C8), EEPROM(24LC04B)             
//  Tool versions: Quartus Prime 18.1             
//  Description: I2C接口驱动模块
//  **************************************************************
`include "param.v"module i2c_interface (input               clk                 ,input               rst_n               ,//  eeprom_ctrlinput               req                 ,       //  读写请求input   [7:0]       wr_din              ,       //  需要发送的一字节数据input   [3:0]       cmd                 ,       //  控制命令组合output  [7:0]       rdout               ,       //  读取的数据output              rdout_vld           ,       //  读取数据有效标志output              rw_done             ,       //  读写一字节完成标志//  EEPROMinput               sda_in              ,output              sda_out             ,output              sda_out_en          ,output              scl
);//  参数定义localparam  IDLE    =   7'b000_0001 ,START   =   7'b000_0010 ,WR_DATA =   7'b000_0100 ,RD_DATA =   7'b000_1000 ,REC_ACK =   7'b001_0000 ,SEND_ACK=   7'b010_0000 ,STOP    =   7'b100_0000 ;//  信号定义reg     [6:0]       state_c         ;reg     [6:0]       state_n         ;reg     [7:0]       cnt_scl         ;       //  scl周期计数器wire                add_cnt_scl     ;wire                end_cnt_scl     ;reg     [3:0]       bit_num         ;       //  bit数reg     [3:0]       cnt_bit         ;       //  bit计数器wire                add_cnt_bit     ;wire                end_cnt_bit     ;reg     [7:0]       dout_data       ;       //  发送数据寄存reg     [3:0]       command         ;       //  控制命令寄存reg                 scl_dout         ;       //  SDA寄存     reg                 sda_dout        ;reg                 sda_dout_en     ;reg     [7:0]       rx_data         ;       //  接收读回的数据reg                 ack_flag        ;       //  ack响应标志//  状态转移条件wire                idle2start      ;wire                idle2wrdata     ;wire                idle2rddata     ;wire                start2wrdata    ;wire                start2rddata    ;wire                wrdata2recack   ;wire                rddata2sendack  ;wire                recack2idle     ;wire                recack2stop     ;wire                sendack2idle    ;wire                sendack2stop    ;wire                stop2idle       ;//  状态机always @(posedge clk or negedge rst_n)beginif(!rst_n)beginstate_c <= IDLE ;endelse beginstate_c <= state_n ;endendalways @(*)begincase (state_c)IDLE: beginif(idle2start)state_n = START ;else if(idle2wrdata)state_n = WR_DATA ;else if(idle2rddata)state_n = RD_DATA ;elsestate_n = state_c ;endSTART: beginif(start2wrdata)state_n = WR_DATA ;else if(start2rddata)state_n = RD_DATA ;elsestate_n = state_c ;endWR_DATA: beginif(wrdata2recack)state_n = REC_ACK ;elsestate_n = state_c ;endRD_DATA: beginif(rddata2sendack)state_n = SEND_ACK ;elsestate_n = state_c ;endREC_ACK: beginif(recack2idle)state_n = IDLE ;else if(recack2stop)state_n = STOP ;elsestate_n = state_c ;endSEND_ACK: beginif(sendack2idle)state_n = IDLE ;else if(sendack2stop)state_n = STOP ;elsestate_n = state_c ;endSTOP: beginif(stop2idle)state_n = IDLE ;elsestate_n = state_c ;enddefault: state_n = IDLE ;endcaseend//  状态转移条件assign  idle2start      = state_c == IDLE       && req && (cmd & `STA)                      ;assign  idle2wrdata     = state_c == IDLE       && req && (cmd & `WRITE)                    ;assign  idle2rddata     = state_c == IDLE       && req && (cmd & `READ)                     ;assign  start2wrdata    = state_c == START      && end_cnt_bit && (command & `WRITE)        ;   assign  start2rddata    = state_c == START      && end_cnt_bit && (command & `READ)         ;   assign  wrdata2recack   = state_c == WR_DATA    && end_cnt_bit                              ;assign  rddata2sendack  = state_c == RD_DATA    && end_cnt_bit                              ;assign  recack2idle     = state_c == REC_ACK    && end_cnt_bit && (command & `STO) == 0     ;assign  recack2stop     = state_c == REC_ACK    && end_cnt_bit && (command & `STO)          ;assign  sendack2idle    = state_c == SEND_ACK   && end_cnt_bit && (command & `STO) == 0     ;assign  sendack2stop    = state_c == SEND_ACK   && end_cnt_bit && (command & `STO)          ;assign  stop2idle       = state_c == STOP       && end_cnt_bit                              ;//  cnt_scl  200KHz  一个SCL周期为250个系统时钟周期always @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_scl <= 0 ;endelse if(add_cnt_scl)beginif(end_cnt_scl)begincnt_scl <= 0 ;end				else begin	    cnt_scl <= cnt_scl + 1 ;end 		    endend                 assign	add_cnt_scl	= state_c != IDLE ;assign	end_cnt_scl	= add_cnt_scl && (cnt_scl == `SCL_PERIOD - 1) ;//  bit_numalways @(*)beginif(state_c == RD_DATA || state_c == WR_DATA)beginbit_num = 8 ;endelse beginbit_num = 1 ;endend//  cnt_bitalways @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_bit <= 0 ;endelse if(add_cnt_bit)beginif(end_cnt_bit)begincnt_bit <= 0 ;end				else begin	    cnt_bit <= cnt_bit + 1 ;end 		    endend           assign	add_cnt_bit	= end_cnt_scl ;assign	end_cnt_bit	= add_cnt_bit && (cnt_bit == bit_num - 1) ;//  dout_dataalways @(posedge clk or negedge rst_n)beginif(!rst_n)begindout_data <= 0 ;endelse if(req)begindout_data <= wr_din ;endend//  commandalways @(posedge clk or negedge rst_n)beginif(!rst_n)begincommand <= 0 ;endelse if(req)begincommand <= cmd ;endend//  scl_doutalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginscl_dout <= 1'b1 ;endelse if(idle2start | idle2wrdata | idle2rddata)beginscl_dout <= 1'b0 ;endelse if(add_cnt_scl && cnt_scl == `SCL_HALF)beginscl_dout <= 1'b1 ;endelse if(end_cnt_scl && ~stop2idle)beginscl_dout <= 1'b0 ;endend//  sda_dout_enalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginsda_dout_en <= 1'b0 ;endelse if(idle2start || idle2wrdata || rddata2sendack || sendack2stop || recack2stop)beginsda_dout_en <= 1'b1 ;endelse if(idle2rddata || start2rddata || wrdata2recack || stop2idle)beginsda_dout_en <= 1'b0 ;endend//  sda_doutalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginsda_dout <= 1'b1 ;endelse if(state_c == START)beginif(cnt_scl == `HIGH_HALF)beginsda_dout <= 1'b0 ;endelse if(cnt_scl == `LOW_HALF)beginsda_dout <= 1'b1 ;endendelse if(state_c == WR_DATA && cnt_scl == `LOW_HALF)beginsda_dout <= dout_data[7 - cnt_bit] ;endelse if(state_c == SEND_ACK && cnt_scl == `LOW_HALF)beginsda_dout <= (command & `STO) ? 1'b1 : 1'b0 ;endelse if(state_c == STOP)beginif(cnt_scl == `LOW_HALF)begin       sda_dout <= 1'b0;endelse if(cnt_scl == `HIGH_HALF)begin    sda_dout <= 1'b1;               end endelse if(wrdata2recack | rddata2sendack)beginsda_dout <= 1'b1;  endend//  rx_dataalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginrx_data <= 0 ;endelse if(state_c == RD_DATA && cnt_scl == `HIGH_HALF)beginrx_data[7 - cnt_bit] <= sda_in ;endend//  ack_flagalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginack_flag <= 1'b0 ;endelse if(state_c == REC_ACK && cnt_scl == `HIGH_HALF)beginack_flag <= ~sda_in ;endelse beginack_flag <= 1'b0 ;endend//  输出assign rdout = rx_data ;   assign rdout_vld = rddata2sendack ;assign rw_done = stop2idle | sendack2idle | recack2idle ;assign sda_out = sda_dout ; assign sda_out_en = sda_dout_en ;assign scl = scl_dout ;endmodule     

4、参数配置

参数配置文件param.v如下:

//  IIC时钟参数
`define  SCL_PERIOD  250
`define  SCL_HALF    125
`define  LOW_HALF    65 
`define  HIGH_HALF   190//  控制命令
`define  WRITE    4'b1000     //  写
`define  READ     4'b0100     //  读
`define  STA      4'b0010     //  起始位
`define  STO      4'b0001     //  停止位//  UART参数配置文件
`define BAUD_RATE_115200`define CLK_FREQ 50_000_000`ifdef BAUD_RATE_9600`define BAUD `CLK_FREQ / 9600
`elsif BAUD_RATE_19200`define BAUD `CLK_FREQ / 19200
`elsif BAUD_RATE_38400`define BAUD `CLK_FREQ / 38400
`elsif BAUD_RATE_57600`define BAUD `CLK_FREQ / 57600
`elsif BAUD_RATE_115200`define BAUD `CLK_FREQ / 115200
`endif 

5、其他模块

按键消抖、数码管显示模块比较简单,这里就不过多赘述。
串口数据收发参考文章:UART串口通信

四、仿真测试

1、仿真文件如下:

`timescale 1ns/1psmodule test_tb ();reg     tb_clk          ;reg     tb_rst_n        ;reg     [1:0]       key_in;wire    sda             ;wire    scl             ;wire    [7:0]       seg_dig     ;wire    [5:0]       seg_sel     ;reg     [7:0]       data         ;reg                 data_vld     ;wire                ready       ;//  PCwire                uart_txd    ;wire                uart_rxd    ;uart_tx uart_tx_pc (/*input               */.clk             (tb_clk       ),/*input               */.rst_n           (tb_rst_n     ),/*//  control*//*input   [7:0]       */.tx_din          (data    ),/*input               */.tx_din_vld      (data_vld),/*output              */.ready           (ready     ),       //  给control模块的握手信号,表示可以接收数据进行发送 /*//  上位机*//*output              */.uart_tx         (uart_txd  ));top u_top (/*input           */.clk             (tb_clk    ),/*input           */.rst_n           (tb_rst_n  ),/*//  key_filter*///input           */.wr_req          (wr_req    ),//input           */.rd_req          (rd_req    ),/*input   [1:0]   */.key_in          (key_in    ),//  uart/*input           */.uart_rxd        (uart_txd  ),/*input           */.uart_txd        (uart_rxd  ),/*//  eeprom*//*inout           */.sda             (sda       ),/*output          */.scl             (scl       ),//  数码管/*output  [5:0]   */.seg_sel         (seg_sel   ),/*output  [7:0]   */.seg_dig         (seg_dig   ) );M24LC04B u_M24LC04B(.A0             (0              ),.A1             (0              ),.A2             (0              ),.WP             (0              ),.SDA            (sda            ),.SCL            (scl            ),.RESET          (~tb_rst_n         ));parameter CYCLE = 20;defparam  u_top.u_key_filter1.DELAY = 10 ;defparam  u_top.u_key_filter2.DELAY = 10 ;always #(CYCLE / 2) tb_clk = ~tb_clk;initial begintb_clk = 1'b1 ;tb_rst_n = 1'b1 ;key_in = 0 ;data = 0 ;data_vld = 1'b0 ;#(CYCLE * 2);#2;tb_rst_n = 1'b0 ;#(CYCLE * 2);tb_rst_n = 1'b1 ;#(CYCLE * 20);Send(8'ha9);key_in[0] = 1'b1 ;#(CYCLE * 20) ;key_in[0] = 1'b0 ;#(CYCLE * 100 * 100 * 30) ;key_in[1] = 1'b1 ;#(CYCLE * 20) ;key_in[1] = 1'b0 ;#(CYCLE * 100 * 100 * 10) ;$stop ;endtask Send;input   [7:0]   send_data ;begindata = send_data ;data_vld = 1'b1 ;# CYCLE ;data_vld = 1'b0 ;// @(posedge ready)# (CYCLE*10) ;endendtaskendmodule

2、仿真结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注意:
1. 24LC04B读数据只能读到0,无法读1,当读1的时候显示的是高阻状态。
2. 仿真的时候写操作完成后,延时5ms再开始读操作。

五、上板验证

在这里插入图片描述

总结:此工程主要实现IIC单字节读写EEPROM的操作,感兴趣的小伙伴可以尝试一下页写和其他几种方式的读操作。

相关内容

热门资讯

银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...