这个程序写了一天,大概10个小时吧
首先:最重要的就是理解I2C的通讯协议,这点非常重要
I2C:有两个引脚:SCL(时钟) SDA(数据)
下面是我自己画的I2C的协议,
我还是讲讲如何写FPGA的代码吧
SCL时钟频率我设置为:250K
我们还需要一个时钟:CLOCK为:250K*4=1M ,这个时钟可以通过系统分频产生
写E2PROM的过程:
读E2PROM的过程:
我们需要注意的是SDA是个inout的信号,在从机应答的周期里,记得要提前切换sda的输出方向。
写程序的时候,一定要看时序图。看着时序图写。稳住。
module e2prom(
input clk ,
input rst_n,
output [1:0] led,
output scl, //时钟线,作为输出,驱动恶e2prom芯片工作
inout sda, // 数据线
output [5:0] seg_sel, // 数码管位选信号
output [7:0] seg_led // 数码管段选信号
);
wire clk_div;
wire read_en;
wire read_done;
wire [7:0] read_data;
wire [15:0] read_address;
wire write_en;
wire write_done;
wire [7:0] write_data;
wire [15:0] write_address;
//数码管动态显示模块
seg_num seg_num_inst(
.clk (clk ), // 时钟信号
.rst_n (rst_n), // 复位信号
.seg_sel (seg_sel ), // 位选
.segment (seg_led ), // 段选
.data (read_data) // 显示的数值
);
//读写E2PROM中的地址
rw_e2prom rw_e2prom_inst(
.clk (clk_div), // 时钟信号1M
.rst_n (rst_n), // 复位信号
.read_en (read_en),
.read_done (read_done),
.read_data (read_data),
.read_address (read_address),
.write_en (write_en),
.write_done (write_done),
.write_data (write_data),
.write_address (write_address)
);
//E2PROM I2C驱动代码
e2prom_dri e2prom_dri_inst(
.clk (clk),
.rst_n (rst_n),
.led (led),
.scl_clk (scl),
.sda (sda),
.read_en (read_en),
.read_done (read_done),
.read_data (read_data),
.read_address (read_address),
.write_en (write_en),
.write_done (write_done),
.write_data (write_data),
.write_address (write_address),
.clk_div (clk_div)
);
endmodule
///
module e2prom_dri(
input clk , //50M
input rst_n ,
output reg [1:0] led, //测试用的led
output reg scl_clk, //II2C时钟驱动,
inout sda , //数据线
input read_en, //读使能
output reg read_done, //读完成,一个脉冲信号
output reg [7:0] read_data, //读出来的数据
input [15:0] read_address,//读的地址
input write_en, //写使能
output reg write_done, //写完成,一个脉冲信号
input [7:0] write_data, //写入的数据
input [15:0] write_address, //要写的地址
output reg clk_div //1M时钟信号
);
parameter ADDRESS = 8'b1010_0000; //器件地址(7位)+写(0)
reg start_flag;
reg start_flag2;
reg sda_dir ; // I2C数据(SDA)方向控制
reg sda_out ; // SDA输出信号
wire sda_in ; // SDA输入信号
reg [4:0] cnt0;
wire add_cnt0;
wire end_cnt0;
reg [7:0] cnt1;
wire add_cnt1;
wire end_cnt1;
reg [7:0] cnt2;
wire add_cnt2;
wire end_cnt2;
//t=20ns
//1us=50*20ns; cnt=50/2
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1;
end
end
assign add_cnt0 = 1;
assign end_cnt0 = add_cnt0 && cnt0== 25-1;
//产生1M的信号,周期为1us
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
clk_div<=1'b0;
end
else if(end_cnt0) begin
clk_div<=~clk_div;
end
end
//写数据
always @(posedge clk_div or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1;
end
end
assign add_cnt1 = start_flag;
assign end_cnt1 = add_cnt1 && cnt1==153-1;
//读数据
always @(posedge clk_div or negedge rst_n)begin
if(!rst_n)begin
cnt2 <= 0;
end
else if(add_cnt2)begin
if(end_cnt2)
cnt2 <= 0;
else
cnt2 <= cnt2 + 1;
end
end
assign add_cnt2 = start_flag2;
assign end_cnt2 = add_cnt2 && cnt2== 194-1;
assign sda = sda_dir ? sda_out : 1'bz; // SDA数据输出或高阻
assign sda_in = sda ; // SDA数据输入
always @(posedge clk_div or negedge rst_n)begin
if(rst_n==1'b0)begin
scl_clk<=1;
sda_dir<=1; //刚开始设置为输出
sda_out<=1;
read_data<=0;
end
else if(start_flag)begin //写数据
case(cnt1)
8'd1:scl_clk<=1;
8'd2:sda_out<=1;
8'd3:sda_out<=0;
8'd4:scl_clk<=0;
8'd5:sda_out<=ADDRESS[7]; //器件地址
8'd6:scl_clk<=1;
8'd8:scl_clk<=0;
8'd9:sda_out<=ADDRESS[6];
8'd10:scl_clk<=1;
8'd12:scl_clk<=0;
8'd13:sda_out<=ADDRESS[5];
8'd14:scl_clk<=1;
8'd16:scl_clk<=0;
8'd17:sda_out<=ADDRESS[4];
8'd18:scl_clk<=1;
8'd20:scl_clk<=0;
8'd21:sda_out<=ADDRESS[3];
8'd22:scl_clk<=1;
8'd24:scl_clk<=0;
8'd25:sda_out<=ADDRESS[2];
8'd26:scl_clk<=1;
8'd28:scl_clk<=0;
8'd29:sda_out<=ADDRESS[1];
8'd30:scl_clk<=1;
8'd32:scl_clk<=0;
8'd33:sda_out<=ADDRESS[0];
8'd34:scl_clk<=1;
8'd36:begin scl_clk<=0; sda_dir<=0;end //应答信号
8'd38:scl_clk<=1;
8'd40:begin scl_clk<=0; sda_dir<=1;end
8'd41:sda_out<=write_address[15]; //数据地址
8'd42:scl_clk<=1;
8'd44:scl_clk<=0;
8'd45:sda_out<=write_address[14];
8'd46:scl_clk<=1;
8'd48:scl_clk<=0;
8'd49:sda_out<=write_address[13];
8'd50:scl_clk<=1;
8'd52:scl_clk<=0;
8'd53:sda_out<=write_address[12];
8'd54:scl_clk<=1;
8'd56:scl_clk<=0;
8'd57:sda_out<=write_address[11];
8'd58:scl_clk<=1;
8'd60:scl_clk<=0;
8'd61:sda_out<=write_address[10];
8'd62:scl_clk<=1;
8'd64:scl_clk<=0;
8'd65:sda_out<=write_address[9];
8'd66:scl_clk<=1;
8'd68:scl_clk<=0;
8'd69:sda_out<=write_address[8];
8'd70:scl_clk<=1;
8'd72:begin scl_clk<=0; sda_dir<=0;end //应答信号
8'd74:scl_clk<=1;
8'd76:begin scl_clk<=0; sda_dir<=1;end
8'd77:sda_out<=write_address[7]; //数据地址
8'd78:scl_clk<=1;
8'd80:scl_clk<=0;
8'd81:sda_out<=write_address[6];
8'd82:scl_clk<=1;
8'd84:scl_clk<=0;
8'd85:sda_out<=write_address[5];
8'd86:scl_clk<=1;
8'd88:scl_clk<=0;
8'd89:sda_out<=write_address[4];
8'd90:scl_clk<=1;
8'd92:scl_clk<=0;
8'd93:sda_out<=write_address[3];
8'd94:scl_clk<=1;
8'd96:scl_clk<=0;
8'd97:sda_out<=write_address[2];
8'd98:scl_clk<=1;
8'd100:scl_clk<=0;
8'd101:sda_out<=write_address[1];
8'd102:scl_clk<=1;
8'd104:scl_clk<=0;
8'd105:sda_out<=write_address[0];
8'd106:scl_clk<=1;
8'd108:begin scl_clk<=0; sda_dir<=0;end //应答信号
8'd110:scl_clk<=1;
8'd112:begin scl_clk<=0; sda_dir<=1;end
8'd113:sda_out<=write_data[7]; //存的数据
8'd114:scl_clk<=1;
8'd116:scl_clk<=0;
8'd117:sda_out<=write_data[6];
8'd118:scl_clk<=1;
8'd120:scl_clk<=0;
8'd121:sda_out<=write_data[5];
8'd122:scl_clk<=1;
8'd124:scl_clk<=0;
8'd125:sda_out<=write_data[4];
8'd126:scl_clk<=1;
8'd128:scl_clk<=0;
8'd129:sda_out<=write_data[3];
8'd130:scl_clk<=1;
8'd132:scl_clk<=0;
8'd133:sda_out<=write_data[2];
8'd134:scl_clk<=1;
8'd136:scl_clk<=0;
8'd137:sda_out<=write_data[1];
8'd138:scl_clk<=1;
8'd140:scl_clk<=0;
8'd141:sda_out<=write_data[0];
8'd142:scl_clk<=1;
8'd144:begin scl_clk<=0; sda_dir<=0;end //应答信号
8'd146:scl_clk<=1;
8'd148:begin scl_clk<=0; sda_dir<=1;end
8'd149:sda_out<=0;
8'd150:scl_clk<=1;
8'd151:sda_out<=1;
default : ;
endcase
end
else if (start_flag2)begin //读数据
case(cnt2)
8'd1:scl_clk<=1;
8'd2:sda_out<=1;
8'd3:sda_out<=0;
8'd4:scl_clk<=0;
8'd5:sda_out<=ADDRESS[7]; //器件地址
8'd6:scl_clk<=1;
8'd8:scl_clk<=0;
8'd9:sda_out<=ADDRESS[6];
8'd10:scl_clk<=1;
8'd12:scl_clk<=0;
8'd13:sda_out<=ADDRESS[5];
8'd14:scl_clk<=1;
8'd16:scl_clk<=0;
8'd17:sda_out<=ADDRESS[4];
8'd18:scl_clk<=1;
8'd20:scl_clk<=0;
8'd21:sda_out<=ADDRESS[3];
8'd22:scl_clk<=1;
8'd24:scl_clk<=0;
8'd25:sda_out<=ADDRESS[2];
8'd26:scl_clk<=1;
8'd28:scl_clk<=0;
8'd29:sda_out<=ADDRESS[1];
8'd30:scl_clk<=1;
8'd32:scl_clk<=0;
8'd33:sda_out<=ADDRESS[0]; //写状态
8'd34:scl_clk<=1;
8'd36:begin scl_clk<=0; sda_dir<=0;end //应答信号
8'd38:scl_clk<=1;
8'd40:begin scl_clk<=0; sda_dir<=1;end
8'd41:sda_out<=read_address[15]; //数据地址
8'd42:scl_clk<=1;
8'd44:scl_clk<=0;
8'd45:sda_out<=read_address[14];
8'd46:scl_clk<=1;
8'd48:scl_clk<=0;
8'd49:sda_out<=read_address[13];
8'd50:scl_clk<=1;
8'd52:scl_clk<=0;
8'd53:sda_out<=read_address[12];
8'd54:scl_clk<=1;
8'd56:scl_clk<=0;
8'd57:sda_out<=read_address[11];
8'd58:scl_clk<=1;
8'd60:scl_clk<=0;
8'd61:sda_out<=read_address[10];
8'd62:scl_clk<=1;
8'd64:scl_clk<=0;
8'd65:sda_out<=read_address[9];
8'd66:scl_clk<=1;
8'd68:scl_clk<=0;
8'd69:sda_out<=read_address[8];
8'd70:scl_clk<=1;
8'd72:begin scl_clk<=0; sda_dir<=0;end //应答信号
8'd74:scl_clk<=1;
8'd76:begin scl_clk<=0; sda_dir<=1;end
8'd77:sda_out<=read_address[7]; //数据地址
8'd78:scl_clk<=1;
8'd80:scl_clk<=0;
8'd81:sda_out<=read_address[6];
8'd82:scl_clk<=1;
8'd84:scl_clk<=0;
8'd85:sda_out<=read_address[5];
8'd86:scl_clk<=1;
8'd88:scl_clk<=0;
8'd89:sda_out<=read_address[4];
8'd90:scl_clk<=1;
8'd92:scl_clk<=0;
8'd93:sda_out<=read_address[3];
8'd94:scl_clk<=1;
8'd96:scl_clk<=0;
8'd97:sda_out<=read_address[2];
8'd98:scl_clk<=1;
8'd100:scl_clk<=0;
8'd101:sda_out<=read_address[1];
8'd102:scl_clk<=1;
8'd104:scl_clk<=0;
8'd105:sda_out<=read_address[0];
8'd106:scl_clk<=1;
8'd108:begin scl_clk<=0; sda_dir<=0;end //应答信号
8'd110:scl_clk<=1;
8'd112:begin scl_clk<=0; sda_dir<=1;end
8'd114:scl_clk<=1; //起始信号
8'd115:sda_out<=1;
8'd116:sda_out<=0;
8'd117:scl_clk<=0;
8'd118:sda_out<=ADDRESS[7];
8'd119:scl_clk<=1;
8'd121:scl_clk<=0;
8'd122:sda_out<=ADDRESS[6];
8'd123:scl_clk<=1;
8'd125:scl_clk<=0;
8'd126:sda_out<=ADDRESS[5];
8'd127:scl_clk<=1;
8'd129:scl_clk<=0;
8'd130:sda_out<=ADDRESS[4];
8'd131:scl_clk<=1;
8'd133:scl_clk<=0;
8'd134:sda_out<=ADDRESS[3];
8'd135:scl_clk<=1;
8'd137:scl_clk<=0;
8'd138:sda_out<=ADDRESS[2];
8'd139:scl_clk<=1;
8'd141:scl_clk<=0;
8'd142:sda_out<=ADDRESS[1];
8'd143:scl_clk<=1;
8'd145:scl_clk<=0;
8'd146:sda_out<=1; //读状态
8'd147:scl_clk<=1;
8'd149:begin scl_clk<=0; sda_dir<=0;end //应答信号
8'd151:scl_clk<=1;
8'd153:begin scl_clk<=0; sda_dir<=0;end
8'd155: scl_clk<=1;
8'd156: read_data[7]<=sda_in;
8'd157: scl_clk<=0;
8'd159: scl_clk<=1;
8'd160: read_data[6]<=sda_in;
8'd161: scl_clk<=0;
8'd163: scl_clk<=1;
8'd164: read_data[5]<=sda_in;
8'd165: scl_clk<=0;
8'd167: scl_clk<=1;
8'd168: read_data[4]<=sda_in;
8'd169: scl_clk<=0;
8'd171: scl_clk<=1;
8'd172: read_data[3]<=sda_in;
8'd173: scl_clk<=0;
8'd175: scl_clk<=1;
8'd176: read_data[2]<=sda_in;
8'd177: scl_clk<=0;
8'd179: scl_clk<=1;
8'd180: read_data[1]<=sda_in;
8'd181: scl_clk<=0;
8'd183: scl_clk<=1;
8'd184: read_data[0]<=sda_in;
8'd185: begin scl_clk<=0;sda_dir<=1;end
8'd186: sda_out<=1;
8'd187: scl_clk<=1;
8'd189: scl_clk<=0;
8'd190: sda_out<=0;
8'd191: scl_clk<=1;
8'd192: sda_out<=1;
default : ;
endcase
end
end
//给一个写使能信号,脉冲
always @(posedge clk_div or negedge rst_n)begin
if(rst_n==1'b0)begin
start_flag<=0;
end
else if(write_en) begin
start_flag<=1;
end
else if(end_cnt1) begin
start_flag<=0;
end
end
always @(posedge clk_div or negedge rst_n)begin
if(rst_n==1'b0)begin
write_done<=0;
end
else if(end_cnt1) begin
write_done<=1;
end
else begin
write_done<=0;
end
end
always @(posedge clk_div or negedge rst_n)begin
if(rst_n==1'b0)begin
read_done<=0;
end
else if(end_cnt2) begin
read_done<=1;
end
else begin
read_done<=0;
end
end
//给一个写使能信号,脉冲
always @(posedge clk_div or negedge rst_n)begin
if(rst_n==1'b0)begin
start_flag2<=0;
end
else if(read_en) begin
start_flag2<=1;
end
else if(end_cnt2) begin
start_flag2<=0;
end
end
//测试
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
led[0]<=0;
end
else if(start_flag) begin
led[0]<=1;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
led[1]<=0;
end
else if(start_flag2) begin
led[1]<=1;
end
end
endmodule
//
//功能说明:向0--200地址,写入数据之后,然后只读取其中一个地址的值,看看自己是否写对了
module rw_e2prom(
input clk , //50M
input rst_n ,
output reg read_en,
input read_done,
input read_data,
output reg [15:0] read_address,
output reg write_en,
input write_done,
output reg [7:0] write_data,
output reg [15:0] write_address
);
reg [15:0] cnt2;
wire add_cnt2;
wire end_cnt2;
reg flag;
reg [7:0] cnt;
wire add_cnt;
wire end_cnt;
reg [7:0] cnt1;
wire add_cnt1;
wire end_cnt1;
reg flag_cnt;
reg flag_cnt1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1;
end
end
assign add_cnt1 = flag_cnt==0;
assign end_cnt1 = add_cnt1 && cnt1==100-1;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
flag_cnt<=0;
end
else if(end_cnt1)begin
flag_cnt<=1;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
write_en<=0;
write_address<=0;
write_data<=12;
end
else begin
if(flag_cnt1==0) begin
if(end_cnt1)begin
write_en<=1;
end
else if(end_cnt2)begin
write_en<=1;
write_address<=write_address+1;
write_data<=write_data+1;
end
else begin
write_en<=0;
end
end
else begin
write_en<=0;
end
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt2 <= 0;
end
else if(add_cnt2)begin
if(end_cnt2)
cnt2 <= 0;
else
cnt2 <= cnt2 + 1;
end
end
assign add_cnt2 = flag ;
assign end_cnt2 = add_cnt2 && cnt2==5200-1 ;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
flag<=0;
end
else if(write_done) begin
flag<=1;
end
else if(end_cnt2) begin
flag<=0;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
flag_cnt1<=0;
end
else if(end_cnt)begin
flag_cnt1<=1;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1;
end
end
assign add_cnt = write_done;
assign end_cnt = add_cnt && cnt==200-1;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
read_en<=0;
read_address<=0;
end
else if(end_cnt2)begin
read_en<=1;
read_address<=2; //将写入地址2的值读出来,显示到数码管上
end
else begin
read_en<=0;
end
end
endmodule