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仿真模型引起的)
上板验证:
胡萝卜鸡...
相关文章:
IIC驱动EEPROM
代码参考正点原子 i2c_dri:主要是三段式状态机的编写 module iic_dri#(parameter SLAVE_ADDR 7b1010000 , //EEPROM从机地址parameter CLK_FREQ 26d50_000_000, //模块输入的时钟频率parameter I2C_FREQ 18d250_000 //IIC_SCL的时钟频率)( …...
目标检测——基于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.项目…...
JVM系列(十三) -常用调优工具介绍
最近对 JVM 技术知识进行了重新整理,再次献上 JVM系列文章合集索引,感兴趣的小伙伴可以直接点击如下地址快速阅读。 JVM系列(一) -什么是虚拟机JVM系列(二) -类的加载过程JVM系列(三) -内存布局详解JVM系列(四) -对象的创建过程JVM系列(五) -对象的内存分…...
酷睿i7和i5哪个好?i5和i7的区别介绍
在英特尔酷睿处理器家族中,i7与i5作为面向不同用户群体的主流产品,各自承载着不同的性能定位与使用价值。在面对“酷睿i7和i5哪个好”的问题时,答案并非一概而论,而是取决于具体的应用需求、预算考量以及对性能与效率的期待。本文…...
实现用户登录系统的前后端开发
**一、**实验名称 实现用户登录系统的前后端开发。 **二、**参考资料 Web开发技术第一章课件。 **三、**实验目的 1.练习前端基本技术的使用。 2.练习使用Servlet/JSP开发简单后端程序。 3.练习使用Tomcat发布Web应用。 4.练习使用Spring Boot开发简单的后端程序。 **…...
Log4j1.27配置日志输出级别不起效
起因:构建独立版本debezuim使用时,日志一直打印debug信息。 原因:包冲突问题,进行排包操作。 参考log4j日志级别配置完成后不生效 系统一直打印debug日志_log4j不起作用-CSDN博客 1、application.properties logging.configc…...
一、后端到摄像头(监控摄像头IOT)
前言: 开发流程从 后端到摄像头 打通是第一步,那么我们可以着手设计 后端实现 的具体步骤,确保能够稳定地接收和处理来自摄像头的视频流,并提供后续的功能扩展,如视频流转发、存储和控制。 1. 后端系统架构设计 在开始…...
H3C MPLS跨域optionB
实验拓扑 实验需求 如图,VPN1 和 VPN2 分别通过运营商 MPLS VPN 连接各自分支机构按照图示配置 IP 地址,VPN1 和 VPN2 连接同一个 PE 设备的私网 IP 网段存在地址复用,使用多 VRF 技术来防止 IP 冲突AS 100 和 AS 200 内部的公共网络中各自运行 OSPF 使 AS 内各设备的 Loo…...
微信小程序中momentjs无法切换中文问题处理
微信小程序中momentj.s无法切换中文问题处理. 表现为 使用 locale(“zh-cn”)无效。 处理方法 # 1、先删除 miniprogram_npm\moment\index.js # 2、将 node_modules\moment\min\moment-with-locales.min.js 复制到 miniprogram_npm\moment下 并重命名为index.js # 3、修改mi…...
Linux零基础速成篇一(理论+实操)
前言:本教程适合Linux零基础学习,也适合Linux期末考试的小伙伴,从头到尾理论与实操相结合,让你快速对Linux进行了解和掌握。 一、Linux概述 为什么要学习Linux操作系统? 完全免费-开源 任何用户均可下载使用 安全…...
【087】基于51单片机智能宠物喂食器【Proteus仿真+Keil程序+报告+原理图】
☆、设计硬件组成:51单片机最小系统LCD1602液晶显示MY1680语音播放模块DS1302时钟芯片SG90舵机AT24C02存储芯片LED灯按键设置。 1、设计采用STC89C52、AT89C52、AT89S52作为主控芯片; 2、系统采用DS1302时钟芯片实现对日期时间计时并通过LCD1602液晶显…...
全局流量管理:提升用户体验与保障服务稳定性
全局流量管理的工作原理 全局流量管理主要依赖于 DNS(域名系统)技术,通过将全球用户的访问请求引导至离他们最近的服务器或数据中心,极大减少了访问延迟和带宽负载,从而提升了用户体验。同时,GTM 还结合健…...
达梦数据守护搭建
主备库初始化 ./dminit path/dmdata/data db_nameDM01 instance_nameDMSVR01 port_num5236 page_size16 extent_size32 log_size500 case_sensitive1 SYSDBA_PWDDM01SYSDBA ./dminit path/dmdata/data db_nameDM02 instance_nameDMSVR02 port_num5236 page_size16 extent_size3…...
【C++】容器适配器全知道
亲爱的读者朋友们😃,此文开启知识盛宴与思想碰撞🎉。 快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。 目录 一、前言 二、什么是适配器 (一)现实生…...
企业资源规划系统(ERP)服务器上线项目实施指南
企业资源规划系统(ERP)服务器上线项目实施指南 项目背景 在当今竞争激烈的商业环境中,企业资源规划系统(ERP)已成为企业提升内部管理效率和响应市场变化的重要工具。为了实现业务流程的整合和优化,公司决…...
Kubernetes(k8s)离线部署DolphinScheduler3.2.2
1.环境准备 1.1 集群规划 本次安装环境为:3台k8s现有的postgreSql数据库zookeeper服务 1.2 下载及介绍 DolphinScheduler-3.2.2官网:https://dolphinscheduler.apache.org/zh-cn/docs/3.2.2 官网安装文档:https://dolphinscheduler.apach…...
嵌入式linux驱动框架 从0到1编写设备驱动 i2c_driver i2c_client
一、编写最简单的IIC驱动框架 在之前的文章中,我们已经深入探讨了I2C通信的基本原理、硬件架构以及时序等基础知识。现在,我们将进入一个更加实际和深入的层面,构建一个完整的I2C驱动程序,并阐述驱动框架的设计。这将帮助你不仅能…...
小程序canvas画环形百分比进度图
组件封装 component/canvas-ring目录下 canvas-ring.js <canvas style"width:{{canvasWidth}}px;height:{{canvasWidth}}px; margin:0 auto;position:relative" type"2d" id"myCanvas"><view class"circle-bar" style&quo…...
面试经验分享 | 北京渗透测试岗位
更多大厂面试经验的视频经验分享看主页 目录: 所面试的公司:安全大厂 所在城市:北京 面试职位:渗透测试工程师 面试方式:腾讯会议线上面试线下面试 面试过程: 面试官的问题: 1、说一下XSS有哪…...
安卓 SystemServer 启动流程
目录 引言 Android系统服务启动顺序 zygote fork SystemServer 进程 SystemServer启动流程 1、SystemServer.main() 2、SystemServer.run() 3、初始化系统上下文 4、创建系统服务管理 5、启动系统各种服务 总结 引言 开机启动时 PowerManagerService 调用 AudioSer…...
opencv存图速度测试
以下测试的图片,均为5488x3672分辨率的三通道彩色图。 分别使用opencv和halcon存图,测试速度,存100次取平均值,结果如下: image size:5488 3672 opencv jpg save time 0.12809s opencv bmp save time 0.02197s hal…...
[ffmpeg]编译 libx264
步骤 下载 libx264 git clone https://code.videolan.org/videolan/x264.git cd x264环境搭建 然后在开始菜单中找到并打开 x64 Native Tools Command Prompt for VS 2019 : 打开 msys2_shell.cmd -use-full-path 这时会打开 MSYS 的新窗口,先把一些汇…...
常见API
1.API 1.1API概述 什么是API API (Application Programming Interface) :应用程序编程接口 java中的API 指的就是 JDK 中提供的各种功能的 Java类,这些类将底层的实现封装了起来,我们不需要关心这些类是如何实现的,只需要…...
vscode写python,遇到问题:ModuleNotFoundError: No module named ‘pillow‘(已解决 避坑)
1 问题: ModuleNotFoundError: No module named pillow 2 原因: 原因1:安装Pillow的pip命令所处的python版本与vscode调用的python解释器版本不同。 如: 原因2:虽然用的是pillow,但是写代码的时候只能用…...
【mysql】id主键列乱了之后,重新排序(可根据日期顺序)
一、ID中断不连续的,重新设置为连续的ID alter table table_name drop id; alter table table_name add id int not null first; alter table table_name modify column id int not null auto_increment, add primary key(id); select * from table_name order by …...
SO-CNN-LSTM-MATT蛇群算法优化注意力机制深度学习多特征分类预测
SO-CNN-LSTM-MATT蛇群算法优化注意力机制深度学习多特征分类预测(多输入单输出) 目录 SO-CNN-LSTM-MATT蛇群算法优化注意力机制深度学习多特征分类预测(多输入单输出)分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matl…...
原点安全再次入选信通院 2024 大数据“星河”案例
近日,中国信息通信研究院和中国通信标准化协会大数据技术标准推进委员会(CCSA TC601)共同组织开展的 2024 大数据“星河(Galaxy)”案例征集活动结果正式公布。由工银瑞信基金管理有限公司、北京原点数安科技有限公司联…...
Hadoop
HDFS一键启动/停止 start-dfs.sh stop-dfs.sh 单进程启动/停止 $HADOOP_HOME/bin/hdfs,此程序也可以用以单独控制所在机器的进程的启停 hdfs --daemon (start|status|stop) (namenode|secondarynamenode|datanode)#例如启动namenode,去到需要启动的服…...
【Ambari】使用 Knox 进行 LDAP 身份认证
目录 一、knox介绍 二、Ambari配置LDAP认证 三、验证Knox网关 3.1YARNUI 3.2 HDFSUI 3.3 HDFS RestFULL 3.4 SparkHistoryserver 3.5 HBASEUI 一、knox介绍 Apache Knox网关是一个用于与Apache Hadoop部署的REST api和ui交互的应用程序网关。Knox网关为所有与Apache Ha…...
计算机网络习题( 第3章 物理层 第4章 数据链路层 )
第3章 物理层 一、单选题 1、下列选项中,不属于物理层接口规范定义范畴的是( )。 A、 接口形状 B、 引脚功能 C、 传输媒体 D、 信号电平 正确答案: C 2、在物理层接口特性中,用于描述完成每种功能的事件发…...
Windows系统中mt6.dll文件缺失是什么原因?mt6.dll文件缺失详解与修复指南
mt6.dll文件的作用 mt6.dll是一个动态链接库文件,它通常与特定的软件或游戏相关联,用于支持这些程序中的某些功能。虽然它并非Windows系统的核心文件,但对于依赖于它的程序来说,mt6.dll的缺失可能导致程序无法正常运行或启动。 …...
homebrew,gem,cocoapod 换源,以及安装依赖
安装homebrew /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" 再按照成功提示配置环境变量 ruby 更新ruby到最新 brew install ruby 如果安装了会自动升级 安装完成后根据提示配置环境变量 再执行命令使其生效 s…...
uni-app开发商品详情页面实现
目录 一:功能描述 二:功能实现 一:功能描述 商品详情页主要展示商品的图片,基础信息,详细描述信息,以及销量,库存信息等。 首先在顶部以轮播图形式展示图片信息,下面展示商品价格和商品名称和描述信息,然后显示商品的关键卖点信息,最后展示商品详情信息。 二:功…...
mvn install:install-file jar 打入本地仓库
安装指定文件到本地仓库命令:mvn http://install:install-file -DgroupId : 设置上传到仓库的包名 -DartifactId : 设置该包所属的模块名 -Dversion1.0.0 : 设置该包的版本号 -Dpackagingjar : 设置该包的类型(很显然jar包) -Dfile : 设置该jar包文件所在的路径…...
亚式期权定价模型Turnbull-Wakeman进行delta对冲
Turnbull-Wakeman Model是一种用于定价和对冲亚式期权的数学模型。该模型由David Turnbull和Keith Wakeman在1990年提出,用于解决亚式期权的定价问题。 亚式期权是一种路径依赖类型的期权,其期权价格与标的资产价格某个期间内的平均值有关,假…...
Qt之CAN设计(十三)
Qt开发 系列文章 - CAN(十三) 目录 前言 一、CAN 二、实现方式 1.创建类 2.相关功能函数 3.用户使用 4.效果演示 5.拓展应用-实时刷新 总结 前言 Qt框架中并没有提供关于CAN接口的相关模块,需要用户自己根据CAN接口硬件模块&#…...
windows10/windows11运行ps1脚本报错的解决方法,签名错误解决方法
使用win10/win11运行ps1脚本时报错,提示“禁止运行此脚本”,错误如图所示: 此问题通常是由于windows默认的策略导致,解决方法是重新设置权限策略。 1. 设置windows配置 1). 使用管理员权限运行powershell 搜索power…...
在 Mac M2 上安装 PyTorch 并启用 MPS 加速的详细教程与性能对比
1. 安装torch 在官网上可以查看安装教程,Start Locally | PyTorch 作者安装了目前最新的torch版本2.5.1,需要提前安装python3.9及以上版本,作者python版本是python3.11最新版本 使用conda安装torch,在终端进入要安装的环境&…...
vulnhub matrix-breakout靶场
1.搭建靶机 这样就是装好了 获取靶机IP nmap -O 192.168.47.129/24 2.信息收集 dirb http://192.168.47.128 dirb 首页 81端口一个登录页面 gobuster dir -u http://192.168.152.154 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,txt,html gra…...
Hive SQL 窗口函数 `ROW_NUMBER() ` 案例分析
一文彻底搞懂 ROW_NUMBER() 和 PARTITION BY 1. 引言 在处理大规模数据集时,Hive SQL 提供了强大的窗口函数(Window Function),如 ROW_NUMBER(),用于为结果集中的每一行分配唯一的行号。当与 PARTITION BY 和 ORDER …...
windows C++ TCP客户端
demo有一下功能 1、心跳包 2、断开重连 3、非阻塞 4、接受数据单独线程处理 #include <iostream> #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> #include <string> #include <process.h> // 用于Windows下的线程相…...
【C++】初识C++之C语言加入光荣的进化(上)
写在前面 本篇笔记作为C的开篇笔记,主要是讲解C关键字(C98)连带一点点(C11)的知识。掌握的C新语法新特性,当然C是兼容C的,我们学习C的那套在C中也是受用。 文章目录 写在前面一、命名空间域1.1、命名空间域的定义与使用1.2、命名空间域的细节…...
Linux文件目录 --- 文件时间戳、atime、mtime、ctime、指定格式查看
三、文件时间戳 1. atime 文件最近被访问时间,是在读取文件或者执行文件时更改的,如果只cd进入一个目录然后cd . .不会引起atime的改变,要是使用ll命令进行查看后,再cd . . 离开就不同了。 2. mtime 文件最近内容修改时间,在目录中有文件…...
网页博客风格未完
实现类似的博客风格: 学习前端开发基础: HTML & CSS:掌握网页结构和样式设计的基础知识。JavaScript:增强网页的互动性和动态效果。响应式设计:确保您的博客在不同设备上都有良好的显示效果。 使用开源模板&#x…...
LeetCode 2545.根据第 K 场考试的分数排序:考察编程语言的排序
【LetMeFly】2545.根据第 K 场考试的分数排序:考察编程语言的排序 力扣题目链接:https://leetcode.cn/problems/sort-the-students-by-their-kth-score/ 班里有 m 位学生,共计划组织 n 场考试。给你一个下标从 0 开始、大小为 m x n 的整数…...
软考:系统架构设计师教材笔记(持续更新中)
教材中的知识点都会在。其实就是将教材中的废话删除,语言精练一下,内容比较多,没有标注重点 系统架构概述 定义 系统是指完成某一特定功能或一组功能所需要的组件集,而系统架构则是对所有组件的高层次结构表示,包括各…...
安卓环境配置及打开新项目教程,2024年12月20日最新版
1.去官网下载最新的Android Studio,网址:https://developer.android.com/studio?hlzh-cn 2.下载加速器,注册账号,开启加速器。网址:放在文末。 3.下载安卓代码,项目的路径上不能有中文,特别是…...
基于Spring Boot的电影售票系统
一、系统概述 该系统采用Spring Boot框架开发,充分利用其简化配置、快速部署和生产级别的性能监控等特点,为电影售票业务提供高效、可靠的技术支持。同时,系统采用前后端分离架构,前端使用Vue.js等框架,后端使用Sprin…...
【linux】 unshare -user -r /bin/bash命令详解
命令解析 unshare -user -r /bin/bash 是一个 Linux 命令,它用于在新的用户命名空间中运行一个进程(在这个例子中是 /bin/bash)。以下是这个命令的详细解释: 【1. 命令解析】 unshare: unshare 是一个工具,用于从调用…...
uniappX 移动端单行/多行文字隐藏显示省略号
在手机端不能多行省略使用 -webkit-line-clamp 属性所以移动端多行省略不会生效改为 lines 属性即可 /**单行文本溢出显示省略号*/ .text-ov1 {white-space: nowrap;overflow: hidden;text-overflow: ellipsis;height: auto; } /**APP多行文本溢出显示省略号*/ // #ifdef APP-…...