I2C E2PROM通信

这个程序写了一天,大概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


 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值