IIC 随机写+多次写 可以控制写几次
```verilog
module icc_tx#(parameter SIZE = 2 , //用来控制写多少次 比如地址是0000 一个地址只能存放8bit数据 超出指针就会到下一个地址0001parameter CLK_DIV= 50_000_000 ,parameter SPEED = 100_000 ,parameter LED = 50
)( input wire clk ,//系统层input wire rst_n ,inout wire sda ,//物理侧output wire scl ,input wire valid ,//用户侧(写使能)input wire [15:0] addr ,input wire [8*SIZE-1:0] data ,output wire led ,output wire a //ASK状态 1的时候就是该状态否则不是该状态
);
reg scl_r ;
reg sda_o ;
reg sda_i ;
reg [5:0] state ;
reg [((SIZE+3)*8-1):0] data_r ;//SIZE+1 因为还有带上地址加写命令0 不减一是为了产生空白的位不然移位会多移一位
reg [9:0] cunt_0 ;//开始位的计数器 记到500 start
reg [9:0] cunt_1 ;//BUSY与ASK的计数器 用来产生时钟 也方便cunt_2也就是cunt_bit计数
reg [9:0] cunt_2 ;//每一bit数据维持的时间 只在BUSY状态下计数
reg [3:0] cunt_3 ;// 每一次应答加一 计数发了多少数据
reg [30:0] cunt_4 ;//计数小灯点亮时间 1s
reg [9:0] cunt_5 ;
parameter CUNT_MAX=CLK_DIV/SPEED ;
parameter ADDR =7'b101_0000 ;//地址
parameter IDEL =6'b000_001 ;
parameter START =6'b000_010 ;
parameter BUSY =6'b000_100 ;
parameter ASK =6'b001_000 ;
parameter ERROR =6'b010_000 ;
parameter STOP =6'b100_000 ;assign scl=scl_r ;
assign a =(state==ASK)?1'b1:1'b0 ;
assign sda=(state==ASK)?1'bz:sda_o;
//assign sda = sda_o;
//错误状态下led一直亮
assign led=(state==ERROR)?1:0 ;//
always @(posedge clk ) beginif(state==ASK&&cunt_1==(CUNT_MAX/4*3))sda_i<=sda;elsesda_i<=sda_i;end
//数据的缓存 加移位
always @(posedge clk ) begin if(state==IDEL&&valid==1)data_r<={ADDR,1'b0,addr,data}; //单次写else if(cunt_1==4&&state==BUSY) ///data_r<=(data_r<<1); //先赋值再移位了elsedata_r<=data_r;
end
//开始位的 start计数器 记到500
always @(posedge clk ) beginif(state==START)cunt_0<=cunt_0+1;elsecunt_0<=0;
end
//cunt_1
always @(posedge clk ) beginif(state==BUSY||state==ASK)beginif(cunt_1==CUNT_MAX-1)cunt_1<=0;elsecunt_1<=cunt_1+1;endelsecunt_1<=0;
end
//cunt_2
always @(posedge clk ) beginif(state==BUSY)beginif(cunt_1==CUNT_MAX-1)cunt_2<=cunt_2+1;else cunt_2<=cunt_2;endelsecunt_2<=0;
end
//cunt_3 计数发送了几个bit数据
always @(posedge clk ) beginif(state==ASK)beginif(cunt_1==66)cunt_3<=cunt_3+1;elsecunt_3<=cunt_3;endelse if(state==BUSY)cunt_3<=cunt_3;elsecunt_3<=0;
end
//cunt_4
always @(posedge clk ) beginif(state==ERROR)cunt_4<=cunt_4+1;elsecunt_4<=0;
end
//结束位计数 cunt_5
always @(posedge clk ) beginif(state==STOP)cunt_5<=cunt_5+1;elsecunt_5<=0;
end
//状态的转移
always @(posedge clk or negedge rst_n) beginif(!rst_n)state<=IDEL;else begincase (state)IDEL :beginif(valid==1)state<=START;elsestate<=state;endSTART:beginif(cunt_0==CUNT_MAX-1)state<=BUSY;elsestate<=state;endBUSY : beginif(cunt_2==10'd7&&cunt_1==CUNT_MAX-1)state<=ASK;elsestate<=state;endASK :beginif(cunt_1==CUNT_MAX-1)beginif(sda_i==0)beginif(cunt_3==(SIZE+3))state<=STOP;elsestate<=BUSY;endelsestate<=ERROR;endelsestate<=state;endERROR :beginif(cunt_4==LED)state<=IDEL;elsestate<=state;endSTOP :beginif(cunt_5==CUNT_MAX-1)state<=IDEL;elsestate<=state;enddefault: state<=state;endcaseend
end
//时钟线scl的描述
always @(posedge clk ) beginif(state == IDEL || state == START)scl_r <= 1'b1;else if(state == BUSY || state == ASK)beginif(cunt_1 >=0 && cunt_1 <= (CUNT_MAX / 2)) scl_r <= 1'b0;else scl_r <= 1'b1;endelse if(state == STOP)beginscl_r <= 1'b1;endelse scl_r <= 1'b1;
end
//数据线sda的描述
always @(posedge clk ) begincase (state)IDEL : sda_o<=1'b1;START: beginif(cunt_0<CUNT_MAX/2) //也可以起始位状态直接置低置低时间就是cunt_0==250 sda_o<=1;elsesda_o<=0;endBUSY :beginif(cunt_1==1) ///一定要等于0//*************sda_o<=data_r[((SIZE+3)*8-1)];elsesda_o<=sda_o;endASK :beginif(cunt_1==CUNT_MAX-1)sda_o<=1'b0; //给0才可以 因为busy中的保持导致给1会在结束位sda也是一个脉冲1elsesda_o<=1'bz;endERROR:sda_o<=1'b1;STOP :beginif(cunt_5<CUNT_MAX/2)sda_o<=1'b0;elsesda_o<=1'b1;enddefault: sda_o<=1'b1;endcase
end
endmodule
仿真
`timescale 1ns / 1psmodule tb_icc_tx();
reg clk ;///
reg rst_n;///
wire sda ;
wire scl ;
reg valid;///
reg [15:0] data ;///
wire led ;
wire a ;
reg [15:0] addr ;
initial beginclk =1;rst_n<=0;valid<=0;data <=0;#100rst_n<=1;#100valid<=1;data <=16'b1111_0000_0000_1111; addr <=16'b1111_0000_0000_0000;#20valid<=0;data <=0;
endassign sda= (a==1)?1'b0:1'bz;//从机发的
always #10 clk=~clk;
icc_tx#(/*parameter */. SIZE (2 ),/*parameter */. CLK_DIV(50_000_000 ),/*parameter */. SPEED (100_000 ),/*parameter */. LED (50 )
)u_icc_tx( /*input wire */ .clk (clk ),//系统层/*input wire */ .rst_n(rst_n),/*inout wire */ .sda (sda ),//物理侧/*output wire */ .scl (scl ),/*input wire */ .valid(valid),//用户侧/*input wire [8*SIZE-1:0] */ .data (data ),/*output wire */ .led (led ),/*input wire [15:0] */ .addr (addr ) ,/*output wire */ .a (a ) //ASK状态 1的时候就是该状态否则不是该状态
);
endmodule