IIC驱动EEPROM

代码参考正点原子

i2c_dri:主要是三段式状态机的编写

module iic_dri#(parameter   SLAVE_ADDR = 7'b1010000   ,  //EEPROM从机地址parameter   CLK_FREQ   = 26'd50_000_000, //模块输入的时钟频率parameter   I2C_FREQ   = 18'd250_000     //IIC_SCL的时钟频率)(                                                            input                clk        ,    input                rst_n      ,   //i2c interface                      input                i2c_exec   ,  //I2C触发执行信号input                bit_ctrl   ,  //字地址位控制(16b/8b),1代表字地址是16位的,0是8位的input                i2c_rh_wl  ,  //I2C读写控制信号     ,1代表读数据,0代表写数据input        [15:0]  i2c_addr   ,  //I2C器件内地址input        [ 7:0]  i2c_data_w ,  //I2C要写的数据output  reg  [ 7:0]  i2c_data_r ,  //I2C读出的数据output  reg          i2c_done   ,  //I2C一次操作完成output  reg          i2c_ack    ,  //I2C应答标志 0:应答 1:未应答output  reg          scl        ,  //I2C的SCL时钟信号inout                sda        ,  //I2C的SDA信号//user interface                   output  reg          dri_clk       //驱动I2C操作的驱动时钟);//localparam define
localparam  st_idle     = 8'b0000_0001; //空闲状态
localparam  st_sladdr   = 8'b0000_0010; //发送器件地址(slave address)
localparam  st_addr16   = 8'b0000_0100; //发送16位字地址
localparam  st_addr8    = 8'b0000_1000; //发送8位字地址
localparam  st_data_wr  = 8'b0001_0000; //写数据(8 bit)
localparam  st_addr_rd  = 8'b0010_0000; //发送器件地址读
localparam  st_data_rd  = 8'b0100_0000; //读数据(8 bit)
localparam  st_stop     = 8'b1000_0000; //结束I2C操作//reg define
reg            sda_dir   ; //I2C数据(SDA)方向控制
reg            sda_out   ; //SDA输出信号
reg            st_done   ; //状态结束
reg            wr_flag   ; //写标志
reg    [ 6:0]  cnt       ; //计数
reg    [ 7:0]  cur_state ; //状态机当前状态
reg    [ 7:0]  next_state; //状态机下一状态
reg    [15:0]  addr_t    ; //地址
reg    [ 7:0]  data_r    ; //读取的数据
reg    [ 7:0]  data_wr_t ; //I2C需写的数据的临时寄存
reg    [ 9:0]  clk_cnt   ; //分频时钟计数//wire define
wire          sda_in     ; //SDA输入信号
wire   [8:0]  clk_divide ; //模块驱动时钟的分频系数//*****************************************************
//**                    main code
//*****************************************************//SDA控制
assign  sda     = sda_dir ?  sda_out : 1'bz;     //SDA数据输出或高阻
assign  sda_in  = sda ;                          //SDA数据输入
assign  clk_divide = (CLK_FREQ/I2C_FREQ) >> 2'd2;//模块驱动时钟的分频系数//生成I2C的SCL的四倍频率的驱动时钟用于驱动i2c的操作
always @(posedge clk or negedge rst_n) beginif(!rst_n) begindri_clk <=  1'b0;clk_cnt <= 10'd0;endelse if(clk_cnt == clk_divide[8:1] - 1'd1) beginclk_cnt <= 10'd0;dri_clk <= ~dri_clk;endelseclk_cnt <= clk_cnt + 1'b1;
end//(三段式状态机)同步时序描述状态转移
always @(posedge dri_clk or negedge rst_n) beginif(!rst_n)cur_state <= st_idle;elsecur_state <= next_state;
end//组合逻辑判断状态转移条件
always @(*) beginnext_state = st_idle;    //清除锁存器case(cur_state)st_idle: begin                          //空闲状态if(i2c_exec) beginnext_state = st_sladdr;  //器件地址endelsenext_state = st_idle;endst_sladdr: beginif(st_done) beginif(bit_ctrl)                    //判断是16位还是8位字地址next_state = st_addr16;      //字地址的高8位elsenext_state = st_addr8 ;      //字地址的低8位endelsenext_state = st_sladdr;endst_addr16: begin                        //写16位字地址if(st_done) beginnext_state = st_addr8;endelse beginnext_state = st_addr16;endendst_addr8: begin                         //8位字地址if(st_done) beginif(wr_flag==1'b0)               //读写判断next_state = st_data_wr;elsenext_state = st_addr_rd;endelse beginnext_state = st_addr8;endendst_data_wr: begin                       //写数据(8 bit)if(st_done)next_state = st_stop;elsenext_state = st_data_wr;endst_addr_rd: begin                       //写地址以进行读数据if(st_done) beginnext_state = st_data_rd;endelse beginnext_state = st_addr_rd;endendst_data_rd: begin                       //读取数据(8 bit)if(st_done)next_state = st_stop;elsenext_state = st_data_rd;endst_stop: begin                          //结束I2C操作if(st_done)next_state = st_idle;elsenext_state = st_stop ;enddefault: next_state= st_idle;endcase
end//时序电路描述状态输出
always @(posedge dri_clk or negedge rst_n) begin//复位初始化if(!rst_n) beginscl       <= 1'b1;sda_out   <= 1'b1;sda_dir   <= 1'b1;                          i2c_done  <= 1'b0;                          i2c_ack   <= 1'b0;                          cnt       <= 1'b0;                          st_done   <= 1'b0;                          data_r    <= 1'b0;                          i2c_data_r<= 1'b0;                          wr_flag   <= 1'b0;                          addr_t    <= 1'b0;                           data_wr_t <= 1'b0;                          end                                              else begin                                       st_done <= 1'b0 ;                            cnt     <= cnt +1'b1 ;                       case(cur_state)                              st_idle: begin                          //空闲状态scl     <= 1'b1;                     sda_out <= 1'b1;                     //当sda_dir为1时,sda_out就是sda  sda_dir <= 1'b1;                     i2c_done<= 1'b0;                     cnt     <= 7'b0;               if(i2c_exec) begin                   wr_flag   <= i2c_rh_wl ;         addr_t    <= i2c_addr  ;         data_wr_t <= i2c_data_w;  i2c_ack <= 1'b0;                      end                                  end                                      st_sladdr: begin                         //写地址(器件地址和字地址)case(cnt)                            7'd1 : sda_out <= 1'b0;          //开始I2C    起始信号 7'd3 : scl <= 1'b0;              //scl在3、5、7、9...时0 1 0 1变化,四分频7'd4 : sda_out <= SLAVE_ADDR[6]; //传送器件地址7'd5 : scl <= 1'b1;              7'd7 : scl <= 1'b0;              7'd8 : sda_out <= SLAVE_ADDR[5]; 7'd9 : scl <= 1'b1;              7'd11: scl <= 1'b0;              7'd12: sda_out <= SLAVE_ADDR[4]; 7'd13: scl <= 1'b1;              7'd15: scl <= 1'b0;              7'd16: sda_out <= SLAVE_ADDR[3]; 7'd17: scl <= 1'b1;              7'd19: scl <= 1'b0;              7'd20: sda_out <= SLAVE_ADDR[2]; 7'd21: scl <= 1'b1;              7'd23: scl <= 1'b0;              7'd24: sda_out <= SLAVE_ADDR[1]; 7'd25: scl <= 1'b1;              7'd27: scl <= 1'b0;              7'd28: sda_out <= SLAVE_ADDR[0]; 7'd29: scl <= 1'b1;              7'd31: scl <= 1'b0;              7'd32: sda_out <= 1'b0;          //0:写  表示接着写 7'd33: scl <= 1'b1;              7'd35: scl <= 1'b0;              7'd36: begin                     sda_dir <= 1'b0;       //拉低释放总线       sda_out <= 1'b1;                         end                              7'd37: scl     <= 1'b1;            7'd38: begin                     //从机应答 st_done <= 1'b1;if(sda_in == 1'b1)           //高电平表示未应答  0表示有效应答i2c_ack <= 1'b1;         //拉高应答标志位     end                                          7'd39: begin                     scl <= 1'b0;                 cnt <= 1'b0;                 end                              default :  ;                     endcase                              end                                      st_addr16: begin                         case(cnt)                            7'd0 : begin                     sda_dir <= 1'b1 ;            sda_out <= addr_t[15];       //传送字地址i2c_addrend                              7'd1 : scl <= 1'b1;              //四分频          7'd3 : scl <= 1'b0;              7'd4 : sda_out <= addr_t[14];    7'd5 : scl <= 1'b1;              7'd7 : scl <= 1'b0;              7'd8 : sda_out <= addr_t[13];    7'd9 : scl <= 1'b1;              7'd11: scl <= 1'b0;              7'd12: sda_out <= addr_t[12];    7'd13: scl <= 1'b1;              7'd15: scl <= 1'b0;              7'd16: sda_out <= addr_t[11];    7'd17: scl <= 1'b1;              7'd19: scl <= 1'b0;              7'd20: sda_out <= addr_t[10];    7'd21: scl <= 1'b1;              7'd23: scl <= 1'b0;              7'd24: sda_out <= addr_t[9];     7'd25: scl <= 1'b1;              7'd27: scl <= 1'b0;              7'd28: sda_out <= addr_t[8];     7'd29: scl <= 1'b1;              7'd31: scl <= 1'b0;              7'd32: begin                     sda_dir <= 1'b0;             sda_out <= 1'b1;   end                              7'd33: scl  <= 1'b1;             7'd34: begin                     //从机应答st_done <= 1'b1;     if(sda_in == 1'b1)           //高电平表示未应答i2c_ack <= 1'b1;         //拉高应答标志位    end        7'd35: begin                     scl <= 1'b0;                 cnt <= 1'b0;                 end                              default :  ;                     endcase                              end                                      st_addr8: begin                          case(cnt)                            7'd0: begin                      sda_dir <= 1'b1 ;             sda_out <= addr_t[7];         //字地址end                              7'd1 : scl <= 1'b1;              7'd3 : scl <= 1'b0;              7'd4 : sda_out <= addr_t[6];   //  7'd5 : scl <= 1'b1;              7'd7 : scl <= 1'b0;              7'd8 : sda_out <= addr_t[5];     7'd9 : scl <= 1'b1;              7'd11: scl <= 1'b0;              7'd12: sda_out <= addr_t[4];     7'd13: scl <= 1'b1;              7'd15: scl <= 1'b0;              7'd16: sda_out <= addr_t[3];     7'd17: scl <= 1'b1;              7'd19: scl <= 1'b0;              7'd20: sda_out <= addr_t[2];     7'd21: scl <= 1'b1;              7'd23: scl <= 1'b0;              7'd24: sda_out <= addr_t[1];     7'd25: scl <= 1'b1;              7'd27: scl <= 1'b0;              7'd28: sda_out <= addr_t[0];     7'd29: scl <= 1'b1;              7'd31: scl <= 1'b0;              7'd32: begin                     sda_dir <= 1'b0;         sda_out <= 1'b1;                    end                              7'd33: scl     <= 1'b1;          7'd34: begin                     //从机应答st_done <= 1'b1;     if(sda_in == 1'b1)           //高电平表示未应答i2c_ack <= 1'b1;         //拉高应答标志位    end   7'd35: begin                     scl <= 1'b0;                 cnt <= 1'b0;                 end                              default :  ;                     endcase                              end                                      st_data_wr: begin                        //写数据(8 bit)case(cnt)                            7'd0: begin                      sda_out <= data_wr_t[7];     //I2C写8位数据     data_wr_t要写的数据sda_dir <= 1'b1;             end                              7'd1 : scl <= 1'b1;              7'd3 : scl <= 1'b0;              7'd4 : sda_out <= data_wr_t[6];  7'd5 : scl <= 1'b1;              7'd7 : scl <= 1'b0;              7'd8 : sda_out <= data_wr_t[5];  7'd9 : scl <= 1'b1;              7'd11: scl <= 1'b0;              7'd12: sda_out <= data_wr_t[4];  7'd13: scl <= 1'b1;              7'd15: scl <= 1'b0;              7'd16: sda_out <= data_wr_t[3];  7'd17: scl <= 1'b1;              7'd19: scl <= 1'b0;              7'd20: sda_out <= data_wr_t[2];  7'd21: scl <= 1'b1;              7'd23: scl <= 1'b0;              7'd24: sda_out <= data_wr_t[1];  7'd25: scl <= 1'b1;              7'd27: scl <= 1'b0;              7'd28: sda_out <= data_wr_t[0];  7'd29: scl <= 1'b1;              7'd31: scl <= 1'b0;              7'd32: begin                     sda_dir <= 1'b0;           sda_out <= 1'b1;                              end                              7'd33: scl <= 1'b1;              7'd34: begin                     //从机应答st_done <= 1'b1;     if(sda_in == 1'b1)           //高电平表示未应答i2c_ack <= 1'b1;         //拉高应答标志位    end          7'd35: begin                     scl  <= 1'b0;                cnt  <= 1'b0;                end                              default  :  ;                    endcase                              end                                      st_addr_rd: begin                        //写地址以进行读数据case(cnt)                            7'd0 : begin                     sda_dir <= 1'b1;             sda_out <= 1'b1;             end                              7'd1 : scl <= 1'b1;              7'd2 : sda_out <= 1'b0;          //重新开始7'd3 : scl <= 1'b0;              7'd4 : sda_out <= SLAVE_ADDR[6]; //传送器件地址7'd5 : scl <= 1'b1;              7'd7 : scl <= 1'b0;              7'd8 : sda_out <= SLAVE_ADDR[5]; 7'd9 : scl <= 1'b1;              7'd11: scl <= 1'b0;              7'd12: sda_out <= SLAVE_ADDR[4]; 7'd13: scl <= 1'b1;              7'd15: scl <= 1'b0;              7'd16: sda_out <= SLAVE_ADDR[3]; 7'd17: scl <= 1'b1;              7'd19: scl <= 1'b0;              7'd20: sda_out <= SLAVE_ADDR[2]; 7'd21: scl <= 1'b1;              7'd23: scl <= 1'b0;              7'd24: sda_out <= SLAVE_ADDR[1]; 7'd25: scl <= 1'b1;              7'd27: scl <= 1'b0;              7'd28: sda_out <= SLAVE_ADDR[0]; 7'd29: scl <= 1'b1;              7'd31: scl <= 1'b0;              7'd32: sda_out <= 1'b1;          //1:读7'd33: scl <= 1'b1;              7'd35: scl <= 1'b0;              7'd36: begin                     sda_dir <= 1'b0;            sda_out <= 1'b1;                    end7'd37: scl     <= 1'b1;7'd38: begin                     //从机应答st_done <= 1'b1;     if(sda_in == 1'b1)           //高电平表示未应答i2c_ack <= 1'b1;         //拉高应答标志位    end   7'd39: beginscl <= 1'b0;cnt <= 1'b0;enddefault : ;endcaseendst_data_rd: begin                        //读取数据(8 bit)case(cnt)7'd0: sda_dir <= 1'b0;7'd1: begindata_r[7] <= sda_in;scl       <= 1'b1;end7'd3: scl  <= 1'b0;7'd5: begindata_r[6] <= sda_in ;scl       <= 1'b1   ;end7'd7: scl  <= 1'b0;7'd9: begindata_r[5] <= sda_in;scl       <= 1'b1  ;end7'd11: scl  <= 1'b0;7'd13: begindata_r[4] <= sda_in;scl       <= 1'b1  ;end7'd15: scl  <= 1'b0;7'd17: begindata_r[3] <= sda_in;scl       <= 1'b1  ;end7'd19: scl  <= 1'b0;7'd21: begindata_r[2] <= sda_in;scl       <= 1'b1  ;end7'd23: scl  <= 1'b0;7'd25: begindata_r[1] <= sda_in;scl       <= 1'b1  ;end7'd27: scl  <= 1'b0;7'd29: begindata_r[0] <= sda_in;scl       <= 1'b1  ;end7'd31: scl  <= 1'b0;7'd32: beginsda_dir <= 1'b1;             sda_out <= 1'b1;end7'd33: scl     <= 1'b1;7'd34: st_done <= 1'b1;          //非应答7'd35: beginscl <= 1'b0;cnt <= 1'b0;i2c_data_r <= data_r;enddefault  :  ;endcaseendst_stop: begin                           //结束I2C操作case(cnt)7'd0: beginsda_dir <= 1'b1;             //结束I2Csda_out <= 1'b0;end7'd1 : scl     <= 1'b1;7'd3 : sda_out <= 1'b1;7'd15: st_done <= 1'b1;7'd16: begincnt      <= 1'b0;i2c_done <= 1'b1;            //向上层模块传递I2C结束信号enddefault  : ;endcaseendendcaseend
end

EEPROM读写测试模块:

module e2prom_rw(input              clk ,input              rst_n ,output reg        i2c_rh_wl,output reg        i2c_exec,output reg [15:0] i2c_addr, //i2c器件内地址output reg [7:0]  i2c_data_w,input       [7:0]  i2c_data_r,input              i2c_done,input              i2c_ack,output reg         rw_done, //E2PROM读写测试完成 output reg         rw_result  //E2PROM读写测试结果  0:失败   1:成功             );//parameter define//EEPROM写数据需要添加间隔时间,读数据则不需要parameter   WR_WAIT_TIME = 14'd50000;  //5ms写入数据的间隔时间为5msparameter   MAX_BYTE     = 16'd256;    //读写测试的字节个数//reg definereg [1:0] flow_cnt;  //状态流控制reg [13:0] wait_cnt;  //延时计数器//EEPROM读写测试,先写后读,并比较读出的值与输入的值是否一致always @(posedge clk or negedge rst_n)beginif(!rst_n) begin flow_cnt   <= 2'b0;i2c_rh_wl  <= 1'b0;i2c_exec   <= 1'b0;i2c_addr   <= 16'b0;i2c_data_w <= 8'b0;wait_cnt   <= 14'b0;rw_done    <= 1'b0;rw_result  <= 1'b0;endelse begin i2c_exec <= 1'b0;rw_done  <= 1'b0;case(flow_cnt)2'd0 : beginwait_cnt <= wait_cnt + 1'b1;if(wait_cnt == (WR_WAIT_TIME  - 1'b1))beginwait_cnt <= 14'b0;if(i2c_addr == MAX_BYTE)begini2c_addr <= 16'b0;i2c_rh_wl <= 1'b1;flow_cnt  <= 2'd2;endelse beginflow_cnt <= flow_cnt + 2'b1;i2c_exec <= 1'b1;  //启动i2cendendend2'd1 : beginif(i2c_done == 1'b1)beginflow_cnt <= 2'd0;i2c_addr <= i2c_addr + 16'b1;       //0-255i2c_data_w <= i2c_data_w + 8'b1;    //0-255endend2'd2 : beginflow_cnt <= flow_cnt + 2'b1;i2c_exec <= 1'b1;end2'd3 : beginif(i2c_done == 1'b1)begin//读出的值错误或者i2c未应答,读写测试失败if((i2c_addr[7:0] != i2c_data_r) || (i2c_ack == 1'b1)) beginrw_done <= 1'b1;rw_result <=1'b0;endelse if(i2c_addr == (MAX_BYTE - 16'b1))begin  //读写测试成功rw_done <= 1'b1;rw_result <=1'b1;endelse beginflow_cnt <= 2'd2;i2c_addr <= i2c_addr + 16'b1;endendenddefault : ;endcaseend

 读写结果判定:

reg          rw_done_flag;    //读写测试完成标志
reg  [16:0]  led_cnt     ;    //led计数//*****************************************************
//**                    main code
//*****************************************************//读写测试完成标志rw_done_flag
always @(posedge clk or negedge rst_n) beginif(!rst_n)rw_done_flag <= 1'b0;else if(rw_done)rw_done_flag <= 1'b1;
end        //错误标志为1时PL_LED0闪烁,否则PL_LED0常亮
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginled_cnt <= 17'd0;led <= 1'b0;endelse beginif(rw_done_flag) beginif(rw_result)                          //读写测试正确led <= 1'b1;                       //led灯常亮else begin                             //读写测试错误led_cnt <= led_cnt + 17'd1;if(led_cnt == (L_TIME - 17'b1)) beginled_cnt <= 17'd0;led <= ~led;                   //led灯闪烁endelseled <= led;endendelseled <= 1'b0;                           //读写测试完成之前,led灯熄灭end    
endendmodule

 top:

module top_e2prom(input               sys_clk    ,      //系统时钟input               sys_rst_n  ,      //系统复位//eeprom interfaceoutput              iic_scl    ,      //eeprom的时钟线sclinout               iic_sda    ,      //eeprom的数据线sda//user interfaceoutput              led               //led显示eeprom读写测试结果
);//parameter define
parameter    SLAVE_ADDR = 7'b1010000     ; //器件地址(SLAVE_ADDR)
parameter    BIT_CTRL   = 1'b1           ; //字地址位控制参数(16b/8b)
parameter    CLK_FREQ   = 26'd50_000_000 ; //i2c_dri模块的驱动时钟频率(CLK_FREQ)
parameter    I2C_FREQ   = 18'd250_000    ; //I2C的SCL时钟频率
parameter    L_TIME     = 17'd125_000    ; //led闪烁时间参数
parameter    MAX_BYTE   = 16'd256        ; //读写测试的字节个数//wire define
wire           dri_clk   ; //I2C操作时钟
wire           i2c_exec  ; //I2C触发控制
wire   [15:0]  i2c_addr  ; //I2C操作地址
wire   [ 7:0]  i2c_data_w; //I2C写入的数据
wire           i2c_done  ; //I2C操作结束标志
wire           i2c_ack   ; //I2C应答标志 0:应答 1:未应答
wire           i2c_rh_wl ; //I2C读写控制
wire   [ 7:0]  i2c_data_r; //I2C读出的数据
wire           rw_done   ; //E2PROM读写测试完成
wire           rw_result ; //E2PROM读写测试结果 0:失败 1:成功 //*****************************************************
//**                    main code
//*****************************************************//e2prom读写测试模块
e2prom_rw #(.MAX_BYTE    (MAX_BYTE  )   //读写测试的字节个数
) u_e2prom_rw(.clk         (dri_clk   ),  //时钟信号.rst_n       (sys_rst_n ),  //复位信号//i2c interface.i2c_exec    (i2c_exec  ),  //I2C触发执行信号.i2c_rh_wl   (i2c_rh_wl ),  //I2C读写控制信号.i2c_addr    (i2c_addr  ),  //I2C器件内地址.i2c_data_w  (i2c_data_w),  //I2C要写的数据.i2c_data_r  (i2c_data_r),  //I2C读出的数据.i2c_done    (i2c_done  ),  //I2C一次操作完成.i2c_ack     (i2c_ack   ),  //I2C应答标志 //user interface.rw_done     (rw_done   ),  //E2PROM读写测试完成.rw_result   (rw_result )   //E2PROM读写测试结果 0:失败 1:成功
);//i2c驱动模块
iic_dri #(.SLAVE_ADDR  (SLAVE_ADDR),  //EEPROM从机地址.CLK_FREQ    (CLK_FREQ  ),  //模块输入的时钟频率.I2C_FREQ    (I2C_FREQ  )   //IIC_SCL的时钟频率
) u_i2c_dri(.clk         (sys_clk   ),  .rst_n       (sys_rst_n ),  //i2c interface.i2c_exec    (i2c_exec  ),  //I2C触发执行信号.bit_ctrl    (BIT_CTRL  ),  //器件地址位控制(16b/8b).i2c_rh_wl   (i2c_rh_wl ),  //I2C读写控制信号.i2c_addr    (i2c_addr  ),  //I2C器件内地址.i2c_data_w  (i2c_data_w),  //I2C要写的数据.i2c_data_r  (i2c_data_r),  //I2C读出的数据.i2c_done    (i2c_done  ),  //I2C一次操作完成.i2c_ack     (i2c_ack   ),  //I2C应答标志.scl         (iic_scl   ),  //I2C的SCL时钟信号.sda         (iic_sda   ),  //I2C的SDA信号//user interface.dri_clk     (dri_clk   )   //I2C操作时钟
);//led指示模块
rw_result_led #(.L_TIME(L_TIME  )   //控制led闪烁时间
) u_rw_result_led(.clk         (dri_clk   ),  .rst_n       (sys_rst_n ), .rw_done     (rw_done   ),  .rw_result   (rw_result ),.led         (led       )    
);endmodule

tb:时钟激励

`timescale  1ns/1ns                     //定义仿真时间单位1ns和仿真时间精度为1nsmodule  tb_e2prom_top;              //parameter  define
parameter  T = 20                          ; //时钟周期为20ns
parameter  SLAVE_ADDR     = 7'b1010000     ; //器件地址(SLAVE_ADDR)
parameter  BIT_CTRL       = 1'b1           ; //字地址位控制参数(16b/8b)
parameter  CLK_FREQ       = 26'd50_000_000 ; //i2c_dri模块的驱动时钟频率(CLK_FREQ),周期就是20纳秒
parameter  I2C_FREQ       = 18'd250_000    ; //I2C的SCL时钟频率,周期是4us
parameter  L_TIME         = 17'd1          ; //led闪烁时间参数
parameter  MAX_BYTE       = 16'd3          ; //读写测试的字节个数//reg define
reg          sys_clk  ;                 //时钟信号
reg          sys_rst_n;                 //复位信号//wire define
wire         iic_scl;
wire         iic_sda;
wire         led    ;//*****************************************************
//**                    main code
//*****************************************************//给输入信号初始值
initial beginsys_clk            = 1'b0;sys_rst_n          = 1'b0;     //复位#(T+1)  sys_rst_n  = 1'b1;     //在第21ns的时候复位信号信号拉高
end//50Mhz的时钟,周期则为1/50Mhz=20ns,所以每10ns,电平取反一次
always #(T/2) sys_clk = ~sys_clk;//将SDA数据线上拉
pullup(iic_sda);//例化e2prom_top模块
top_e2prom  #(.MAX_BYTE   (MAX_BYTE )   //读写测试的字节个数
) u_top_e2prom(.sys_clk    (sys_clk  ),  //系统时钟.sys_rst_n  (sys_rst_n),  //系统复位//eeprom interface.iic_scl    (iic_scl  ),  //eeprom的时钟线scl.iic_sda    (iic_sda  ),  //eeprom的数据线sda//user interface.led        (led      )   //led显示
);//例化e2prom仿真模型
EEPROM_AT24C64 u_EEPROM_AT24C64(.scl         (iic_scl),.sda         (iic_sda));endmodule

 上拉电阻:

wire abc;
pullup(abc);
assign abc = enable ? 1’b0 : 1’bz;

enable为1的时候 abc信号为0
enable 为0的时候 assign语句的输出为高阻,但是因为pullup了abc,所以abc的值为1

EEPROM仿真模型:

`timescale 1ns/1ns
`define timeslice 1250
module EEPROM_AT24C64(
scl,
sda
);
input scl; 
inout sda; 
reg out_flag; 
reg[7:0] memory[8191:0]; 
reg[12:0]address; 
reg[7:0]memory_buf; 
reg[7:0]sda_buf; 
reg[7:0]shift; 
reg[7:0]addr_byte_h; 
reg[7:0]addr_byte_l; 
reg[7:0]ctrl_byte; 
reg[1:0]State;
integer i;
//---------------------------
parameter
r7 = 8'b1010_1111, w7 = 8'b1010_1110, //main7
r6 = 8'b1010_1101, w6 = 8'b1010_1100, //main6
r5 = 8'b1010_1011, w5 = 8'b1010_1010, //main5
r4 = 8'b1010_1001, w4 = 8'b1010_1000, //main4
r3 = 8'b1010_0111, w3 = 8'b1010_0110, //main3
r2 = 8'b1010_0101, w2 = 8'b1010_0100, //main2
r1 = 8'b1010_0011, w1 = 8'b1010_0010, //main1
r0 = 8'b1010_0001, w0 = 8'b1010_0000; //main0
assign sda = (out_flag == 1) ? sda_buf[7] : 1'bz;initial
begin
addr_byte_h = 0;
addr_byte_l = 0;
ctrl_byte = 0;
out_flag = 0;
sda_buf = 0;
State = 2'b00;
memory_buf = 0;
address = 0;
shift = 0;
for(i=0;i<=8191;i=i+1)
memory[i] = 0;
end
always@(negedge sda)
begin
if(scl == 1)
begin
State = State + 1;
if(State == 2'b11)
disable write_to_eeprom;
end
endalways@(posedge sda)
begin
if(scl == 1) 
stop_W_R;
else
begin
casex(State)
2'b01:begin
read_in;
if(ctrl_byte == w7 || ctrl_byte == w6
|| ctrl_byte == w5 || ctrl_byte == w4
|| ctrl_byte == w3 || ctrl_byte == w2
|| ctrl_byte == w1 || ctrl_byte == w0)
begin
State = 2'b10;
write_to_eeprom; 
end
else
State = 2'b00;
end
2'b11:
read_from_eeprom;
default:
State = 2'b00;
endcase
end
end 
task stop_W_R;
begin
State = 2'b00;
addr_byte_h = 0;
addr_byte_l = 0;
ctrl_byte = 0;
out_flag = 0;
sda_buf = 0;
end
endtasktask read_in;
begin
shift_in(ctrl_byte);
shift_in(addr_byte_h);
shift_in(addr_byte_l);
end
endtasktask write_to_eeprom;
begin
shift_in(memory_buf);
address = {addr_byte_h[4:0], addr_byte_l};
memory[address] = memory_buf;
State = 2'b00;
end
endtasktask read_from_eeprom;
begin
shift_in(ctrl_byte);
if(ctrl_byte == r7 || ctrl_byte == w6
|| ctrl_byte == r5 || ctrl_byte == r4
|| ctrl_byte == r3 || ctrl_byte == r2
|| ctrl_byte == r1 || ctrl_byte == r0)
begin
address = {addr_byte_h[4:0], addr_byte_l};
sda_buf = memory[address];
shift_out;
State = 2'b00;
end
end
endtask
task shift_in;
output[7:0]shift;
begin
@(posedge scl) shift[7] = sda;
@(posedge scl) shift[6] = sda;
@(posedge scl) shift[5] = sda;
@(posedge scl) shift[4] = sda;
@(posedge scl) shift[3] = sda;
@(posedge scl) shift[2] = sda;
@(posedge scl) shift[1] = sda;
@(posedge scl) shift[0] = sda;
@(negedge scl)
begin
#(`timeslice);
out_flag = 1;
sda_buf = 0;
end
@(negedge scl)
begin
#(`timeslice-250);
out_flag = 0;
end
end
endtask
task shift_out;
begin
out_flag = 1;
for(i=6; i>=0; i=i-1)
begin
@(negedge scl);
#`timeslice;
sda_buf = sda_buf << 1;
end
@(negedge scl) #`timeslice sda_buf[7] = 1;
@(negedge scl) #`timeslice out_flag = 0;
end
endtask
endmodule

i2c状态机的编写:

 

读数据: 

i2c第一次写入地址0的字节数据0:

 i2c第二次写入地址1的字节数据1: 

 i2c第三次写入地址2的字节数据2: 

读数据的时候sda总线由输入变为输出状态,需要给从机一个非应答信号,不能给应答信号!给应答信号就不再是任意地址读,就变成了从某个地址连续读,不符合本次设计的任意地址读模式:

 

 

两个毛刺信号:(由EEPROM仿真模型引起的)

上板验证:

        胡萝卜鸡... 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/495467.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《计算机组成及汇编语言原理》阅读笔记:p86-p115

《计算机组成及汇编语言原理》学习第 6 天&#xff0c;p86-p115 总结&#xff0c;总计 20 页。 一、技术总结 1.if statement 2.loop 在许多编程语言中&#xff0c;有类种循环&#xff1a;一种是在程序开头检测条件(test the condition),另一种是在程序末尾检测条件。 3.C…

(带源码)宠物主题商场系统 计算机项目 P10083

项目说明 本号所发布的项目均由我部署运行验证&#xff0c;可保证项目系统正常运行&#xff0c;以及提供完整源码。 如需要远程部署/定制/讲解系统&#xff0c;可以联系我。定制项目未经同意不会上传&#xff01; 项目源码获取方式放在文章末尾处 注&#xff1a;项目仅供学…

目标检测——基于yolov8和pyqt的螺栓松动检测系统

目录 1.项目克隆和环境配置1.1 我这里使用的是v8.0.6版本1.2 项目代码结构介绍 2.数据集介绍2.1 数据集采集2.2采集结果介绍 3.模型训练4.pyqt界面设计4.1 界面内容介绍4.2 界面实现 5.操作中的逻辑实现5.1 图片检测5.2 文件夹检测5.3 视频检测和摄像头检测 6. 效果展示 1.项目…

宠物行业的出路:在爱与陪伴中寻找增长新机遇

在当下的消费市场中&#xff0c;如果说有什么领域能够逆势而上&#xff0c;宠物行业无疑是一个亮点。当人们越来越注重生活品质和精神寄托时&#xff0c;宠物成为了许多人的重要伴侣。它们不仅仅是家庭的一员&#xff0c;更是情感的寄托和生活的调剂。然而&#xff0c;随着行业…

原点安全再次入选信通院 2024 大数据“星河”案例

近日&#xff0c;中国信息通信研究院和中国通信标准化协会大数据技术标准推进委员会&#xff08;CCSA TC601&#xff09;共同组织开展的 2024 大数据“星河&#xff08;Galaxy&#xff09;”案例征集活动结果正式公布。由工银瑞信基金管理有限公司、北京原点数安科技有限公司联…

【0x001D】HCI_Read_Remote_Version_Information命令详解

目录 一、命令概述 二、命令格式及参数说明 2.12. HCI_Read_Remote_Version_Information 命令格式 2.2. Connection_Handle 三、生成事件 3.1. HCI_Command_Status 事件 3.2. HCI_Read_Remote_Version_Information_Complete 事件 四、命令执行流程 4.1. 命令发起阶段(…

C语言-结构体内存大小

#include <stdio.h> #include <string.h> struct S1 { char a;//1 int b;//4 char c;//1 }; //分析 默认对齐数 成员对齐数 对齐数(前两个最小值) 最大对齐数 // 8 1 …

直流电源如何输出恒压源和恒流源

输出电流达到预定值时&#xff0c;变成稳流特性。 输出电压达到预定值时&#xff0c;变成稳压特性。 电流变大&#xff0c;成稳压。 电压变大&#xff0c;成稳流。

【软考高级】系统架构设计师复习笔记-精华版

文章目录 前言0 系统架构设计师0.1 考架构还是考系分0.2 架构核心知识0.3 架构教材变化 1 计算机操作系统1.1 cpu 组成1.2 内核的五大功能1.3 流水线技术1.4 段页式存储1.5 I/O 软件1.6 文件管理1.7 系统工程相关 2 嵌入式2.1 嵌入式技术2.2 板级支持包&#xff08;BSP&#xf…

如何识别钓鱼邮件和诈骗网站?(附网络安全意识培训PPT资料)

识别钓鱼邮件和诈骗网站是网络安全中的一个重要环节。以下是一些识别钓鱼邮件和诈骗网站的方法&#xff1a; 识别钓鱼邮件&#xff1a; 检查发件人地址&#xff1a; 仔细查看发件人的电子邮件地址&#xff0c;看是否与官方域名一致。 检查邮件内容&#xff1a; 留意邮件中是否…

查询 MySQL 默认的存储引擎(SELECT @@default_storage_engine;)

要查询 MySQL 默认的存储引擎&#xff0c;可以使用以下 SQL 查询语句&#xff1a; SELECT default_storage_engine;解释&#xff1a; SELECT: 表示你要执行一个查询。default_storage_engine: 这是一个 MySQL 系统变量&#xff0c;它存储着当前 MySQL 服务器的默认存储引擎。…

ROM修改进阶教程------修改刷机包init.rc 自启用户自定义脚本的一些基本操作 代码格式与注意事项

在很多定制化固件中。我们需要修改系统的rc文件来启动自己的一些脚本。但有时候修改会不起作用,其具体原因在于权限与代码格式的问题。博文将系统的解析代码操作编写的注意事项与各种权限分别。了解以上. 轻松编写自定义启动脚本. 通过博文了解💝💝💝 1-------💝💝…

openwrt 负载均衡方法 openwrt负载均衡本地源接口

openwrt 负载均衡方法 openwrt负载均衡本地源接口_mob6454cc647bdb的技术博客_51CTO博客 本人注重原理分析&#xff0c;要求对其原理掌握&#xff0c;否则按教程操作&#xff0c;你怕是什么都学不会&#xff0c;仔细看&#xff0c;认真记比较好。 首先确认一下基本细节 1、路由…

InnoDB引擎的内存结构

InnoDB擅长处理事务&#xff0c;具有自动崩溃恢复的特性 架构图&#xff1a; 由4部分组成&#xff1a; 1.Buffer Pool&#xff1a;缓冲池&#xff0c;缓存表数据和索引数据&#xff0c;减少磁盘I/O操作&#xff0c;提升效率 2.change Buffer&#xff1a;写缓冲区&#xff0c…

从 GitLab.com 到 JihuLab.com 的迁移指南

本文分享从 GitLab.com 到 JihuLab.com 的迁移指南。 近期&#xff0c;GitLab Inc. 针对其 SaaS 产品做了限制&#xff0c;如果被判定为国内用户&#xff0c;则会建议使用其在国内的发布版本极狐GitLab。从 GitLab SaaS 产品&#xff08;GitLab.com&#xff09;迁移到极狐GitL…

基于STM32F103控制L298N驱动两相四线步进电机

文章目录 前言一、模块参数二、接口说明三、准备工作四、直流电机驱动引脚接线效果展示 五、两相四线步进电机驱动步进电机相关概念拍数驱动时序引脚接线效果展示 六、参考示例 前言 L298N 是一种常见的双 H 桥电机驱动模块&#xff0c;广泛用于驱动直流电机和步进电机。它基于…

【赵渝强老师】MongoDB逻辑存储结构

MongoDB的逻辑存储结构是一种层次结构&#xff0c;主要包括了三个部分&#xff0c;即&#xff1a;数据库&#xff08;Database&#xff09;、集合&#xff08;Collection&#xff0c;也可以叫做表&#xff09;和文档&#xff08;Document&#xff0c;也可以叫做记录&#xff09…

观察者模式和发布-订阅模式有什么异同?它们在哪些情况下会被使用?

大家好&#xff0c;我是锋哥。今天分享关于【观察者模式和发布-订阅模式有什么异同&#xff1f;它们在哪些情况下会被使用&#xff1f;】面试题。希望对大家有帮助&#xff1b; 观察者模式和发布-订阅模式有什么异同&#xff1f;它们在哪些情况下会被使用&#xff1f; 1000道 …

多目标应用(一):多目标麋鹿优化算法(MOEHO)求解10个工程应用,提供完整MATLAB代码

一、麋鹿优化算法 麋鹿优化算法&#xff08;Elephant Herding Optimization&#xff0c;EHO&#xff09;是2024年提出的一种启发式优化算法&#xff0c;该算法的灵感来源于麋鹿群的繁殖过程&#xff0c;包括发情期和产犊期。在发情期&#xff0c;麋鹿群根据公麋鹿之间的争斗分…

Word窗体联动Excel实现级联组合框

在Word中的使用用户窗体&#xff08;UserForm&#xff09;定制界面如下图所示&#xff0c;其中控件如下&#xff08;忽略Label控件&#xff09;&#xff1a; CompanyName 组合框Attention 组合框CommandButton1 按钮 现在需要实现级联组合框效果&#xff0c;即用户在 CompanyN…