好久没有更新博客了,这期间一个月都在家里休息,也没怎么看书,8月24号才回学校,这几天忙着在研究IIC总线,用FPGA来模拟IIC接口的确比单片机要复杂的多,开始,自己写的程序怎么也在板子上运行不了,我就参考来了特权同学的程序,果然还是没有很深刻理解IIC接口的时序,导致程序不能运行!现在,觉得学一种硬件,必须将它的数据手册看透彻,时序要弄的很清楚,特别是对于FPGA来说!

 

//程序实现的功能:向IIC中写一个字节的数据,然后读出来,显示在8个LED灯上;

module iic_driver
      (
         clk,
         rst_n,
         key1,
         key2,
         scl,
         sda,
         led_data
      );

input      clk;         //系统时钟50MHZ;
input      rst_n;       //复位信号,低电平有效;
input      key1;        //两个按键;
input      key2;
output     scl;         //IIC时钟端口 100KHZ-400KHZ;
inout      sda;         //IIC数据端口;
output[7:0]led_data;

//读、写时序状态;
parameter  IDLE        =4'd0;
parameter  START1      =4'd1;
parameter  DEVICE_ADDR1=4'd2;
parameter  ACK1        =4'd3;
parameter  WORD_ADDR   =4'd4;
parameter  ACK2        =4'd5;
parameter  START2      =4'd6;
parameter  DEVICE_ADDR2=4'd7;
parameter  ACK3        =4'd8;
parameter  DATA        =4'd9;
parameter  ACK4        =4'd10;
parameter  STOP1       =4'd11;
parameter  STOP2       =4'd12;

parameter   DEVICE_WRITE=8'b1010_0010;      //被寻址的器件地址(写操作);
parameter   DEVICE_READ =8'b1010_0011;      //被寻址的器件地址(读操作);
parameter   BYTE_ADDR   =8'b0000_0010;      //器件的字节地址;
parameter   WRITE_DATA  =8'b1010_0101;      //要写入AT24C02里的数据;

//按键检测:一般延时20MS
reg [19:0] cnt_20ms;
always @ (posedge clk or negedge rst_n)
   begin
         if(!rst_n)
            cnt_20ms<=20'h0;
         else 
            cnt_20ms<=cnt_20ms+1'b1; 
   end

reg key1_r;
always @ (posedge clk or negedge rst_n)
  begin
        if(!rst_n)
         key1_r<=1'b1;
        else if(cnt_20ms==20'hfffff)
         key1_r<=key1;

  end

reg key2_r;
always @ (posedge clk or negedge rst_n)
  begin
        if(!rst_n)
         key2_r<=1'b1;
        else if(cnt_20ms==20'hfffff)
         key2_r<=key2;

  end

//分频:50MHZ/500=100KHZ; 
reg [8:0] time_cnt;             
always @ (posedge clk or negedge rst_n)
  begin
        if(!rst_n)
          time_cnt<=9'd0;
       else if(time_cnt==9'd499)
          time_cnt<=9'd0;
       else
         time_cnt<=time_cnt+1'b1;
  end

reg [2:0]  num_r;

always @ (*)
   begin
        
       
         
              case(time_cnt)
                 9'd124 :  num_r=3'd0;
                 9'd249 :  num_r=3'd1;
                 9'd373 :  num_r=3'd2;
                 9'd499 :  num_r=3'd3;
                 default:  num_r=3'd4;
              endcase   
           
   end
wire [2:0]  num;
assign   num=num_r; 
reg scl_r;                           //时钟寄存器;
always @ (posedge clk or negedge rst_n)
   begin
         if(!rst_n)
           scl_r<=1'b0;
         else if(num==3'd1)
           scl_r<=1'b0;         //时钟下降沿;
         else if(num==3'd3) 
          scl_r<=1'b1;          //时钟上升沿; 
   end

assign   scl=scl_r;          //产生IIC所需要的时钟;

reg      sda_r;         //输出数据寄存器;
reg      sda_link;      //输出数据sda信号inout方向控制位;
reg[3:0] temp;
reg[7:0] data_r;        //在IIC上传输的数据寄存器;
reg[7:0] read_data ;    //读出EEPROM的数据寄存器;
reg[3:0] state;       //状态寄存器;

always @ (posedge clk or negedge rst_n)
   begin
         if(!rst_n)
            begin
                sda_r<=1'b1;          //总现在空闲时,将sda拉高来释放总线;
                sda_link<=1'b0;       //复位时,将sda设成高阻态,作为输入;
                temp=4'd0;
                read_data<=8'h0;
            end 
         else 
           begin
                case(state)
                   IDLE   :   begin
                                   sda_r<=1'b1;
                                   sda_link<=1'b0;
                                   if((!key1_r)||(!key2_r))
                                      begin
                                       state<=START1; 
                                       data_r<=DEVICE_WRITE;
                                      end 
                                   else 
                                     state<=IDLE;
                              end 
 
                   START1 :   begin
                                    if(num==3'd0)            //scl高电平中间时,拉低sda,产生启动信号  
                                      begin
                                         sda_r<=1'b0;
                                         sda_link<=1'b1;     //sda作为output;
                                         state<=DEVICE_ADDR1;
                                         temp<=4'd0;
                                      end 
                                    else
                                        state<=START1;
                              end 

                  DEVICE_ADDR1: begin
                                       if(num==3'd2)   //scl低电平中间时,开始写器件地址 ;
                                          if(temp==4'd8)
                                             begin
                                                 sda_r<=1'b1;
                                                 sda_link<=1'b0;
                                                 temp<=4'd0;
                                                 state<=ACK1;
                                             end
                                          else
                                             begin
                                                  sda_link<=1'b1;     //sda为output;
                                                  state<=DEVICE_ADDR1;
                                                  temp<=temp+1'b1;
                                                 case(temp) 
                                                    4'd0   : sda_r<=data_r[7];  //高位在前;
                                                    4'd1   : sda_r<=data_r[6];
                                                    4'd2   : sda_r<=data_r[5]; 
                                                    4'd3   : sda_r<=data_r[4];
                                                    4'd4   : sda_r<=data_r[3];
                                                    4'd5   : sda_r<=data_r[2]; 
                                                    4'd6   : sda_r<=data_r[1];    
                                                    4'd7   : sda_r<=data_r[0]; 
                                                    default: sda_r<=1'bx;
                                                  endcase;    
                                              end 
 
                                       else  
                                            state<=DEVICE_ADDR1; 
                                  end
                   
                    ACK1  :   begin
                                      if(num==3'd1)             // scl下降沿时;
                                        begin
                                            state<=WORD_ADDR;
                                            data_r<=BYTE_ADDR;  //字节存储地址;   
                                        end
                                      else 
                                        state<=ACK1;         //等待数据传送完;
                              end

                   WORD_ADDR: begin
                                     if(num==3'd2)         //scl下降沿的中间时刻;
                                         if(temp==4'd8)
                                           begin
                                            sda_r<=1'b1;
                                            sda_link<=1'b0;
                                            temp<=4'd0;
                                            state<=ACK2; 
                                           end
                                         else
                                           begin
                                             sda_link<=1'b1;         //sda为output;
                                             state<=WORD_ADDR;
                                             temp<=temp+1'b1;
                                             case(temp)
                                               4'd0     : sda_r<=data_r[7];
                                               4'd1     : sda_r<=data_r[6];
                                               4'd2     : sda_r<=data_r[5]; 
                                               4'd3     : sda_r<=data_r[4];
                                               4'd4     : sda_r<=data_r[3];
                                               4'd5     : sda_r<=data_r[2]; 
                                               4'd6     : sda_r<=data_r[1];    
                                               4'd7     : sda_r<=data_r[0]; 
                                               default: sda_r<=1'bx;
                                             endcase  
                                                
                                           end  
                                            
                                     else 
                                          state<=WORD_ADDR;
                                          
                               end

                    ACK2 :   begin                                  
                                      if(num==3'd1)             // scl下降沿时;
                                        begin
                                            if(!key1)
                                              begin
                                                state<=DATA;
                                                data_r<=WRITE_DATA;
                                              end 
                                            else if(!key2)
                                              begin
                                                state<=START2;
                                                data_r<=DEVICE_READ;
                                              end
                                        end
                                      else 
                                        state<=ACK2;        
                              end

                      START2:  begin
                                     if(num==3'd0)              //scl高电平中间时,拉低sda,产生启动信号  
                                        begin
                                          sda_r<=1'b0;
                                          state<=DEVICE_ADDR2; 
                                        end 
                                     else if(num==3'd2)
                                        begin
                                            sda_link<=1'b1;
                                            sda_r<=1'b1;
                                            state<=START2;   
                                        end   
                                     else 
                                           state<=START2;          
                               end
                      
                      DEVICE_ADDR2: begin
                                          if(num==3'd2)
                                              begin
                                                  if(temp==4'd8)
                                                     begin
                                                         temp<=4'd0;
                                                         sda_link<=1'b0;   //将sda设成高阻态,作为输入
                                                         sda_r<=1'b1;
                                                         state<=ACK3;
                                                     end 
                                                  else
                                                     begin
                                                      case(temp)
                                                        4'd0     : sda_r<=data_r[7];
                                                        4'd1     : sda_r<=data_r[6];
                                                        4'd2     : sda_r<=data_r[5]; 
                                                        4'd3     : sda_r<=data_r[4];
                                                        4'd4     : sda_r<=data_r[3];
                                                        4'd5     : sda_r<=data_r[2]; 
                                                        4'd6     : sda_r<=data_r[1];    
                                                        4'd7     : sda_r<=data_r[0]; 
                                                        default: sda_r<=1'bx;
                                                      endcase 
                                                     end    
                                              end 
                                              
                                           else
                                                state<=DEVICE_ADDR2;
                                                 
                                     end
                                     
                       ACK3    :   begin
                                          if(num==2'd1)
                                            begin
                                                state<=DATA;
                                                sda_link<=1'b0;
                                                
                                            end 
                                          else
                                               state<=ACK3;   
                                   end  
                                   
                       DATA    :    begin
                                           if(!key1_r)       // 写操作 
                                              begin
                                                 sda_r<=1'b1;
                                                 if(temp==4'd7)
                                                    begin
                                                      state<=DATA;
                                                      if(num==3'd2)
                                                        begin
                                                          sda_link<=1'b1;
                                                          temp<=temp+1'b1;
                                                          case(temp)
                                                            4'd0  :  sda_r<=data_r[7];
                                                            4'd1  :  sda_r<=data_r[6];
                                                            4'd2  :  sda_r<=data_r[5]; 
                                                            4'd3  :  sda_r<=data_r[4];
                                                            4'd4  :  sda_r<=data_r[3];
                                                            4'd5  :  sda_r<=data_r[2]; 
                                                            4'd6  :  sda_r<=data_r[1];    
                                                            4'd7  :  sda_r<=data_r[0]; 
                                                            default: sda_r<=1'bx;
                                                          endcase  
                                                        end
                                                     end 
                                                   else if((num==3'd2)&&(temp==4'd8))
                                                        begin
                                                            temp<=4'd0;
                                                            sda_r<=1'b1;
                                                            sda_link<=1'b0;
                                                            state<=ACK4;
                                                        end  
                                                   else
                                                        state<=DATA;
                                                   
                                                 
                                               end
                                                
                                             else if(!key2_r)      // 读操作;
                                                begin
                                                   if(temp==4'd7)
                                                     begin
                                                      state<=DATA;
                                                      if(num==3'd2)
                                                        begin
                                                          temp<=temp+1'b1;
                                                          case(temp)
                                                             4'd0  :   read_data[7]<=sda;
                                                             4'd1  :   read_data[6]<=sda;
                                                             4'd2  :   read_data[5]<=sda;
                                                             4'd3  :   read_data[4]<=sda;
                                                             4'd4  :   read_data[3]<=sda;
                                                             4'd5  :   read_data[2]<=sda;
                                                             4'd6  :   read_data[1]<=sda;
                                                             4'd7  :   read_data[0]<=sda;
                                                             default: ;
                                                          endcase      
                                                        end  
                                                     end 
                                                   else if((num==3'd2)&&(temp==4'd8))
                                                      begin
                                                         temp<=4'd0;
                                                         state<=ACK4;  
                                                      end 
                                                   else
                                                       state<=DATA;          
                                                end 
                                                                
                                                 
                                    end 
                             
                          ACK4     :   begin
                                             if(num==3'd1)
                                                begin
                                                   sda_r<=1'b1;
                                                   state<=STOP1;
                                                end
                                             else
                                                state<=ACK4;   
                                       end
                         STOP1    :    begin
                                             if(num==3'd2)
                                               begin
                                                  sda_link<=1'b1;
                                                  sda_r<=1'b0;
                                                  state<=STOP1;
                                               end 
                                             else if(num==3'd0) 
                                               begin
                                                  sda_r<=1'b1;
                                                  state<=STOP2;
                                               end 
                                             else
                                                 state<=STOP1;  
                                       end   
                                       
                          STOP2   :    begin
                                            if(num==3'd2)
                                              sda_r<=1'b1;
                                            else if(cnt_20ms==20'hffff0)
                                              state<=IDLE;
                                            else
                                              state<=STOP2;    
                                       
                                       end 
                                       
                         default  :    state<=IDLE ;
                         
                         endcase                      
       end                                                      
                   

           
          
           
   end
 
 assign  sda=sda_link? sda_r:1'bz;
 assign  led_data=read_data;
 
endmodule