好久没有更新博客了,这期间一个月都在家里休息,也没怎么看书,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