I2C总线协议的verilog实现
发布时间
阅读量:
阅读量
最近一直在学习各种接口,今天要讲的是I2C 总线。I2C是是一种简单的同步串行总线。 它只需要两根线即可在连接于总线上的器件之间传送信息。
主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件.在总线上主和从、发和收的关系不是恒定的,而取决于此时数据传送方向。如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送;如果主机要接收从器件的数据,首先由主器件寻址从器件.然后主机接收从器件发送的数据,最后由主机终止接收过程。在这种情况下.主机负责产生定时时钟和终止数据传送。这也就是主器件对于从器件的两种操作,即写操作和读操作。
I2C总线的写时序如下所示:

读时序如下所示:

下面是verilog实现,用状态机实现主器件对从器件的读写,由于主器件的数据是来自于并行总线,所以写期间会涉及到并串转换,用一个任务实现;读期间会涉及到串并转换也用一个任务实现。这两个任务使用一个子状态机实现,具体见下面的代码:
module i2c(clk,rst,rd,wr,w_data,w_addr,sda,scl,ack);
parameter size_addr=8;
parameter size_data=8;
//主状态机的各个状态
parameter idle=4'd0,wr_ready=4'd1,wr_ctrl=4'd2,
wr_addr=4'd3,wr_data=4'd4,rd_ctrl=4'd5,
rd_data=4'd6,stop=4'd7,ackn=4'd8;
//启动状态机的各个状态
parameter head_begin=2'b00,head_bit=2'b01,head_end=2'b10;
//停止状态及的各个状态
parameter stop_begin=2'b00,stop_bit=2'b01,stop_end=2'b10;
//并串转换状态机的各个状态
parameter sh8out_begin=4'd0,bit6=4'd1,bit5=4'd2,bit4=4'd3,
bit3=4'd4,bit2=4'd5,bit1=4'd6,bit0=4'd7,sh8out_end=4'd8;
//串并转换状态机的各个状态
parameter sh8in_begin=4'd0,bit70=4'd1,bit60=4'd2,bit50=4'd3,
bit40=4'd4,bit30=4'd5,bit20=4'd6,bit10=4'd7,bit00=4'd8,
sh8in_end=4'd9;
input clk,rst;
input rd,wr;
input [size_addr-1:0] w_addr;
input [size_data-1:0] w_data;
inout sda;
output scl;
output ack;
reg ack;
reg scl;
reg link_sda;//开关信号
reg link_write;
reg link_read;
reg link_start;
reg link_stop;
reg start_buf;
reg stop_buf;
reg [size_data-1:0]sh8out_buf;
reg [size_data-1:0]sh8in_buf;
wire [size_data-1:0]r_data;//读出的数据
reg FF;//任务完成标志
reg [3:0] mstate;//主状态机寄存器
reg [1:0] start_state;//启动状态寄存器
reg [1:0] stop_state;//结束状态寄存器
reg [3:0] sh8out_state;//并串转换状态寄存器
reg [3:0] sh8in_state;//串并转换状态寄存器
wire sda1,sda2,sda3,sda4;
assign sda1=(link_start==1)?1'b1:1'b0;
assign sda2=(link_stop==1) ?1'b1:1'b0;
assign sda3=(link_write==1)?sh8out_buf[7]:1'b0;
assign sda4=sda1|sda2|sda3;
assign sda=(link_sda==1)?sda4:1'bz;
assign r_data=(link_read==1)?sh8in_buf:8'hzz;
always@(posedge clk)//scl时钟产生逻辑
begin
if(!rst) scl<=0;
else scl<=~scl;
end
always@(posedge clk)
begin
if(!rst)
begin
mstate<=idle;
link_read<=0;
link_write<=0;
link_sda<=0;
link_start<=0;
ack<=0;
link_stop<=0;
FF<=0;
end
else
begin
case(mstate)
idle:
begin
mstate<=wr_ready;
start_state<=head_begin;
link_write<=0;
link_start<=1;
start_buf<=1;
ack<=0;
FF<=0;
end
wr_ready:
begin
if(FF==0) headstate;
else
begin
mstate<=wr_ctrl;
FF<=0;
sh8out_state<=sh8out_begin;
sh8out_buf<=8'b1100_1011;
end
end
wr_ctrl:
begin
if(FF==0)sh8out;
else
begin
mstate<=wr_addr;
FF<=0;
sh8out_state<=sh8out_begin;
sh8out_buf<=w_addr;
end
end
wr_addr:
begin
if (FF==0)sh8out;
else
begin
FF<=0;
if(rd)
begin
link_read<=1;
mstate<=rd_ctrl;
sh8out_buf<=8'b1001010;
sh8out_state<=sh8out_begin;
end
if(wr)
begin
mstate<=wr_data;
sh8out_buf<=w_data;
sh8out_state<=sh8out_begin;
link_write<=1;
end
end
end
wr_data:
begin
if(FF==0)sh8out;
else
begin
mstate<=stop;
stop_state<=stop_begin;
link_stop<=1;
stop_buf<=0;
FF<=0;
end
end
rd_ctrl:
begin
if(FF==0)sh8out;
else
begin
FF<=0;
mstate<=rd_data;
sh8in_state<=sh8in_begin;
end
end
rd_data:
begin
if(FF==0)sh8in;
else
begin
FF<=0;
mstate<=stop;
stop_buf<=0;
link_stop<=1;
stop_state<=stop_begin;
end
end
stop:
begin
if(FF==0)stopstate;
else
begin
ack<=1;
mstate<=ack;
FF<=0;
end
end
ackn:
begin
mstate<=idle;
ack<=0;
end
default:mstate<=idle;
endcase
end
end
task headstate;
begin
case(start_state)
head_begin:
begin
if(!scl)
begin
link_sda<=1;
start_state<=head_bit;
end
else
begin
start_state<=head_begin;
end
end
head_bit:
begin
if(scl)
begin
start_buf<=0;
start_state<=head_end;
end
else start_state<=head_bit;
end
head_end:
if(!scl)
begin
link_sda<=0;
link_start<=0;
FF<=1;
start_state<=head_begin;
end
else start_state<=head_end;
endcase
end
endtask
task stopstate;
begin
case(stop_state)
stop_begin:
if(!scl)
begin
link_sda<=1;
stop_state<=stop_bit;
end
else stop_state<=stop_begin;
stop_bit:
begin
if(scl)
begin
stop_state<=stop_end;
stop_buf<=1;
end
else stop_state<=stop_bit;
end
stop_end:
if(!scl)
begin
link_sda<=0;
link_stop<=0;
FF<=1;
stop_state<=stop_begin;
end
else stop_state<=stop_end;
endcase
end
endtask
task sh8out;
begin
case(sh8out_state)
sh8out_begin:
begin
link_sda<=1;
link_write<=1;
sh8out_state<=bit6;
end
bit6:
begin
if(!scl)
begin
sh8out_buf<=sh8out_buf<<1;
sh8out_state<=bit5;
end
else sh8out_state<=bit6;
end
bit5:
begin
if(!scl)
begin
sh8out_buf<=sh8out_buf<<1;
sh8out_state<=bit4;
end
else sh8out_state<=bit5;
end
bit4:
begin
if(!scl)
begin
sh8out_buf<=sh8out_buf<<1;
sh8out_state<=bit3;
end
else sh8out_state<=bit4;
end
bit3:
begin
if(!scl)
begin
sh8out_buf<=sh8out_buf<<1;
sh8out_state<=bit2;
end
else sh8out_state<=bit3;
end
bit2:
begin
if(!scl)
begin
sh8out_buf<=sh8out_buf<<1;
sh8out_state<=bit1;
end
else sh8out_state<=bit2;
end
bit1:
begin
if(!scl)
begin
sh8out_buf<=sh8out_buf<<1;
sh8out_state<=bit0;
end
else sh8out_state<=bit1;
end
bit0:
begin
if(!scl)
begin
sh8out_buf<=sh8out_buf<<1;
sh8out_state<=sh8out_end;
end
else sh8out_state<=bit0;
end
sh8out_end:
begin
sh8out_state<=sh8out_begin;
FF<=1;
link_sda<=0;
link_write<=0;
end
endcase
end
endtask
task sh8in;
begin
case (sh8in_state)
sh8in_begin:
begin
link_sda<=1;
link_read<=0;
sh8in_state<=bit70;
end
bit70:
begin
if(scl)
begin
sh8in_buf[7]<=sda;
sh8in_state<=bit60;
end
else sh8in_state<=bit70;
end
bit60:
begin
if(scl)
begin
sh8in_buf[6]<=sda;
sh8in_state<=bit50;
end
else sh8in_state<=bit60;
end
bit50:
begin
if(scl)
begin
sh8in_buf[5]<=sda;
sh8in_state<=bit40;
end
else sh8in_state<=bit50;
end
bit40:
begin
if(scl)
begin
sh8in_buf[4]<=sda;
sh8in_state<=bit30;
end
else sh8in_state<=bit40;
end
bit30:
begin
if(scl)
begin
sh8in_buf[3]<=sda;
sh8in_state<=bit20;
end
else sh8in_state<=bit30;
end
bit20:
begin
if(scl)
begin
sh8in_buf[2]<=sda;
sh8in_state<=bit10;
end
else sh8in_state<=bit20;
end
bit10:
begin
if(scl)
begin
sh8in_buf[1]<=sda;
sh8in_state<=bit00;
end
else sh8in_state<=bit10;
end
bit00:
begin
if(scl)
begin
sh8in_buf[0]<=sda;
sh8in_state<=sh8in_end;
end
else sh8in_state<=bit00;
end
sh8in_end:
begin
link_sda<=0;
link_read<=1;
FF<=1;
sh8in_state<=sh8in_begin;
end
default:begin
link_read<=0;
sh8in_state<=sh8in_begin;
end
endcase
end
endtask
endmodule
全部评论 (0)
还没有任何评论哟~
