Advertisement

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)

还没有任何评论哟~