10-verilog的EEPROM驱动-单字节读写
verilog的EEPROM驱动-单字节读写
1.读写
module i2c_dri( input clk , input rst_n , //i2c interface input i2c_rw , //0是写,1是读input i2c_begin , //I2C触发执行信号input [15:0] i2c_addr , //I2C器件内地址input [ 7:0] i2c_data_w , //I2C要写的数据output reg [ 7:0] i2c_data_r , //I2C读出的数据output reg i2c_busy , //I2C忙信号output reg i2c_done , //I2C一次操作完成output reg scl , //I2C的SCL时钟信号inout sda , //I2C的SDA信号output reg led2 , //output reg dri_clk //I2C数据(SDA)方向控制 );
parameter SLAVE_ADDR = 7'b1010000 ; //EEPROM从机地址
parameter CLK_FREQ = 26'd50_000_000; //模块输入的时钟频率
parameter I2C_FREQ = 18'd250_000; //IIC_SCL的时钟频率//reg define
reg [ 9:0] clk_cnt ; //分频时钟计数
reg [ 7:0] data_wr_t ; //I2C需写的数据的临时寄存
reg [15:0] addr_t ; //读写地址临时寄存
reg [ 7:0] data_r ; //数据
reg [ 7:0] data_w ; //数据
reg r_or_w ; //读写
reg sda_dir ; //I2C数据(SDA)方向控制
reg sda_out ; //reg [ 3:0] flow_cnt ; // 流转计数
//wire define
wire sda_in ; //SDA输入信号
wire [8:0] clk_divide ; //模块驱动时钟的分频系数assign sda = sda_out ; //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
//////////////////////////////////
reg [ 7:0] data_temp ; //数据
reg flow_cnt_ok ; // 流转计数
reg [ 6:0] cnt ; //计数
////////////////////////////////////////////////
/*
写
0:空闲
1:加载器件地址
2:写字节
3:加载16为地址高字节
4:写字节
5:加载16为地址低字节
6:写字节
7:加载数据
8:写字节
9:发送停止位读
0:空闲
1:加载器件地址
2:写字节
3:加载16为地址高字节
4:写字节
5:加载16为地址低字节
6:写字节
10:加载器件地址+读
8:写字节
11:读数据
12:发送停止*/
always @(posedge dri_clk or negedge rst_n) beginif(!rst_n) beginscl <= 1'b1;sda_out <= 1'b1;cnt<= 7'b0;flow_cnt<=4'b0;i2c_busy<=1'b0;flow_cnt_ok<= 1'b0;cnt<= 1'b0;end////////////////////////////////////////////else if (flow_cnt_ok&&(r_or_w==0))begin flow_cnt<=((flow_cnt>=4'd9)? (4'd0):(flow_cnt+4'd1));//状态技术+1i2c_done<=1'b0;flow_cnt_ok<= 1'b0;end else if (flow_cnt_ok&&(r_or_w==1))begin if(flow_cnt<=4'd5)flow_cnt<=flow_cnt+1'b1;else if (flow_cnt==4'd6)flow_cnt<=4'd10;else if (flow_cnt==4'd10)flow_cnt<=4'd8;else if (flow_cnt==4'd8)flow_cnt<=4'd11; else if (flow_cnt==4'd11)flow_cnt<=4'd12;else if (flow_cnt==4'd12)flow_cnt<=4'd0;i2c_done<=1'b0;flow_cnt_ok<= 1'b0;end ///////////////////////////////////////////////else if(flow_cnt == 4'b0) beginif(i2c_begin==1'b1&&i2c_busy==1'b0) beginr_or_w<=i2c_rw;addr_t<=i2c_addr;data_w<=i2c_data_w;i2c_busy<=1'b1;flow_cnt_ok<=1'b1;endelse flow_cnt = 4'b0;endelse if(flow_cnt == 4'd1) begin//加载写字节data_temp<={SLAVE_ADDR,1'b0};flow_cnt_ok<=1'b1;endelse if(flow_cnt == 4'd3) begin//加载eeprom地址data_temp<=addr_t[15:8];flow_cnt_ok<=1'b1;endelse if(flow_cnt == 4'd5) begin//加载低8位地址data_temp<=addr_t[7:0];flow_cnt_ok<=1'b1;endelse if(flow_cnt == 4'd7) begin//加载要写入的数据data_temp<=data_w[7:0];flow_cnt_ok<=1'b1;endelse if(flow_cnt == 4'd9) begin//发送STOPcnt <= cnt +1'b1 ;case(cnt)7'd1 : sda_out <= 1'b0; //开始I2C7'd2 : scl <= 1'b1; 7'd3 : sda_out <= 1'b1; //传送器件地址7'd4 : begini2c_busy<=1'b0;i2c_done<=1'b1;scl <= 1'b1;sda_out <= 1'b1;flow_cnt_ok<=1'b1;cnt <= 1'b0;enddefault : ; endcaseendelse if(flow_cnt == 4'd2||flow_cnt == 4'd4||flow_cnt == 4'd6||flow_cnt == 4'd8) begin//发送一个字节cnt <= cnt +1'b1 ;case(cnt)//7'd0 :begin scl <= 1'b1;sda_out <= 1'b1;end7'd1 : sda_out <= 1'b0; //开始I2C7'd3 : scl <= 1'b0; 7'd4 : sda_out <= data_temp[7]; //传送器件地址7'd5 : scl <= 1'b1; 7'd7 : scl <= 1'b0; 7'd8 : sda_out <= data_temp[6]; 7'd9 : scl <= 1'b1; 7'd11: scl <= 1'b0; 7'd12: sda_out <= data_temp[5]; 7'd13: scl <= 1'b1; 7'd15: scl <= 1'b0; 7'd16: sda_out <= data_temp[4]; 7'd17: scl <= 1'b1; 7'd19: scl <= 1'b0; 7'd20: sda_out <= data_temp[3]; 7'd21: scl <= 1'b1; 7'd23: scl <= 1'b0; 7'd24: sda_out <= data_temp[2]; 7'd25: scl <= 1'b1; 7'd27: scl <= 1'b0; 7'd28: sda_out <= data_temp[1]; 7'd29: scl <= 1'b1; 7'd31: scl <= 1'b0; 7'd32: sda_out <= data_temp[0]; //0:写7'd33: scl <= 1'b1; 7'd35: scl <= 1'b0; 7'd36: sda_out <= 1'bz; // 7'd37: scl <= 1'b1; 7'd38: begin //从机应答 if(sda == 1'b1) begin //高电平表示未应答cnt <= 1'b0;flow_cnt = 4'b0;end end 7'd39: begin scl <= 1'b0; cnt <= 1'b0;flow_cnt_ok<=1'b1;enddefault : ; endcaseendelse if(flow_cnt == 4'd10) begin//加载要读的芯片地址data_temp<={SLAVE_ADDR,1'b1};flow_cnt_ok<=1'b1;scl <= 1'b1;//添加起始位endelse if(flow_cnt == 4'd12) begin//发送STOPcnt <= cnt +1'b1 ;case(cnt)7'd1 : sda_out <= 1'b0; //开始I2C7'd2 : scl <= 1'b1; 7'd3 : sda_out <= 1'b1; //传送器件地址7'd4 : begini2c_busy<=1'b0;i2c_done<=1'b1;scl <= 1'b1;sda_out <= 1'b1;flow_cnt_ok<=1'b1;cnt <= 1'b0;enddefault : ; endcaseendelse if(flow_cnt == 4'd11) begin//加载要读的芯片地址cnt <= cnt +1'b1 ;case(cnt)7'd1 : sda_out <= 1'bz; //开始I2C7'd3 : scl <= 1'b0; 7'd5 : scl <= 1'b1; 7'd6 : data_r[7]<=sda;7'd7 : scl <= 1'b0; 7'd9 : scl <= 1'b1; 7'd10 : data_r[6]<=sda;7'd11: scl <= 1'b0; 7'd13: scl <= 1'b1; 7'd14 : data_r[5]<=sda;7'd15: scl <= 1'b0; 7'd17: scl <= 1'b1; 7'd18: data_r[4]<=sda;7'd19: scl <= 1'b0; 7'd21: scl <= 1'b1; 7'd22: data_r[3]<=sda;7'd23: scl <= 1'b0; 7'd25: scl <= 1'b1; 7'd26: data_r[2]<=sda;7'd27: scl <= 1'b0; 7'd29: scl <= 1'b1; 7'd30:data_r[1]<=sda;7'd31: scl <= 1'b0; 7'd33: scl <= 1'b1; 7'd34: data_r[0]<=sda;7'd35: scl <= 1'b0; 7'd36: sda_out <= 1'b0; 7'd37: scl <= 1'b1; 7'd39: scl <= 1'b0; 7'd40: sda_out <= 1'b1; 7'd42: begin cnt <= 1'b0;flow_cnt_ok<=1'b1;enddefault : ; endcase endelse ;
end
endmodule
2.例化
module e2prom_top(input sys_clk , //系统时钟input sys_rst_n , //系统复位//eeprom interfaceoutput iic_scl , //eeprom的时钟线sclinout iic_sda , //eeprom的数据线sdaoutput iic_scl_b , output iic_sda_b ,output dri_clk ,output reg led0 , output reg led1 , output led2 , input key0 ,input key1
);assign iic_scl_b=iic_scl;
assign iic_sda_b=iic_sda;
//parameter define
parameter SLAVE_ADDR = 7'b1010000 ; //器件地址(SLAVE_ADDR)
parameter CLK_FREQ = 26'd50_000_000; //i2c_dri模块的驱动时钟频率(CLK_FREQ)
parameter I2C_FREQ = 18'd250_000 ; //I2C的SCL时钟频率//*****************************************************
//** main code
//****************************************************//
reg i2c_rw;
reg i2c_begin;
reg [15:0] i2c_addr;
reg [ 7:0] i2c_data_w ; //I2C要写的数据
wire [ 7:0] i2c_data_r ; //I2C读出的数据always @(posedge dri_clk or negedge sys_rst_n) begin if (!sys_rst_n)beginled0<=1'b0;i2c_begin<=1'b0;i2c_rw<=1'b0;//写数据i2c_addr<=16'h0000;i2c_data_w<=8'h00;endelse if (key0_flag&&key0_value&&(!i2c_busy))begini2c_begin<=1'b1;i2c_rw<=1'b0;//写数据i2c_addr<=16'h0001;i2c_data_w<=8'h12;led0<=~led0;endelse if (key1_flag&&key1_value&&(!i2c_busy))begini2c_begin<=1'b1;i2c_rw<=1'b1;//读数据i2c_addr<=16'h0001;i2c_data_w<=8'h12;led1<=~led1;endelsei2c_begin<=1'b0;
end//i2c驱动模块
i2c_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_rw (i2c_rw ),.i2c_begin (i2c_begin ), //I2C触发执行信号.i2c_addr (i2c_addr ), //I2C器件内地址.i2c_data_w (i2c_data_w ), //I2C要写的数据.i2c_data_r (i2c_data_r ), //I2C读出的数据.i2c_busy (i2c_busy ), //I2C忙信号 .i2c_done (i2c_done ), //I2C忙信号 .led2 (led2 ), //I2C忙信号 .scl (iic_scl ), //I2C的SCL时钟信号.sda (iic_sda ) , //I2C的SDA信号.dri_clk (dri_clk)
);/*
always @(posedge dri_clk or negedge sys_rst_n) begin if (!sys_rst_n)led0<=1'b0;else if (key0_flag&&key0_value)begin//led0<=~led0;;endelse//led0<=led0;;
end always @(posedge dri_clk or negedge sys_rst_n) begin if (!sys_rst_n)led1<=1'b0;else if (key1_flag&&key1_value)led1<=~led1;elseled1<=led1;
end
*/
wire key0_value;
wire key0_flag;
key_in u0_key_in(.sys_clk (dri_clk ), .rst_n (sys_rst_n ), .key_in (key0 ), .key_flag (key0_flag ), .key_value (key0_value )
);key_in u1_key_in(.sys_clk (dri_clk ), .rst_n (sys_rst_n ), .key_in (key1 ), .key_flag (key1_flag ), .key_value (key1_value )
);endmodule
module key_in(input sys_clk, //外部50M时钟input rst_n, //外部复位信号,低有效input key_in, //外部按键输入output reg key_flag, //按键数据有效信号output reg key_value //按键消抖后的数据 );reg [31:0] delay_cnt;
reg key_reg;always @(posedge sys_clk or negedge rst_n) begin if (!rst_n) begin key_reg <= 1'b1;delay_cnt <= 32'd0;endelse beginkey_reg <= key_in; //非阻塞赋值,按键输入是wire类型的,随时可能有值的变化if(key_reg != key_in) //一旦检测到按键状态发生变化(有按键被按下或释放)delay_cnt <= 32'd500_000; //给延时计数器重新装载初始值(计数时间为10ms)else if(key_reg == key_in) begin //在按键状态稳定时,计数器递减,开始10ms倒计时if(delay_cnt > 32'd0)delay_cnt <= delay_cnt - 1'b1;elsedelay_cnt <= delay_cnt;end end
end
always @(posedge sys_clk or negedge rst_n) begin if (!rst_n) begin key_flag <= 1'b0;key_value <= 1'b1; endelse beginif(delay_cnt == 32'd1) begin //当计数器递减到1时,说明按键稳定状态维持了20mskey_flag <= 1'b1; //此时消抖过程结束,给出一个时钟周期的标志信号key_value <= key_in; //并寄存此时按键的值endelse beginkey_flag <= 1'b0;key_value <= key_value; end end
end
endmodule
3.波形