`timescale 1ns/1ps
module IIC
(
input clk,
input rst_n,
input key_w, //启动写使能
//input [:0]add_w, //输入的读写地址,如果测试一个数据,则直接给add_w赋值
//input [7:0]data_in, //输入的8位写数据,如果测试一个数据,则直接给data_in赋值
input key_r, //启动读使能
inout sda1, //IIC数据线
output reg scl, //IIC时钟线
output [7:0]data_out1 //输出8位数据
);
//---------------------------消抖,输入信号打三拍
reg key_rd1;
reg key_rd2;
reg key_rd;
always@(posedge clk or negedge rst)
begin
if(~rst)
begin
key_rd1<=1'b0;
key_rd2<=1'b0;
key_rd<=1'b0;
end
else
begin
key_rd1<=key_r;
key_rd2<=key_rd1;
key_rd<=key_rd2;
end
end
reg key_wr1;
reg key_wr2;
reg key_wr;
always@(posedge clk or negedge rst)
begin
if(~rst)
begin
key_wr1<=1'b0;
key_wr2<=1'b0;
key_wr<=1'b0;
end
else
begin
key_wr1<=key_w;
key_wr2<=key_wr1;
key_wr<=key_wr2;
end
end
//-------------------------跨时钟域,打两拍
reg sda,sda2,sda3;
always@(posedge clk or negedge rst)
begin
if(~rst)
begin
sda<=1'b0;
sda2<=1'b0;
sda3<=1'b0;
end
else
begin
sda2<=sda1;
sda3<=sda2;
sda<=sda3;
end
end
//---------------------------
reg sda_buffer; //中间数据寄存器,通过给sda_buffer值,然后赋给SDA
reg flag; //总线控制信号
assign sda=(flag) ? sda_buffer:1'bz; //if(flag==1),sda=sda_buffer(控制总线) else sda=1'bz(释放总线)
reg [7:0]data_in=8'b10101001; //测试单个数据,直接给data_in赋值,8'b10101001为随意数
reg [7:0]data_out;
//------------------------分频,800kHZ
reg clk_sys;
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
begin
clk_sys<=1'b0;
count<=8'd0;
end
else
begin
if(count<31) //系统时钟为50mHZ*20ns, (50mHZ*20ns)/0.8mHZ=1200ns=20ns*60
else
begin
count<=8'd0;
clk_sys<=~clk_sys; //一周期为,20*30*2ns
end
end
end
reg [5:0]state; //状态寄存器,当有读信号或写信号时,state开始从0跳转
always@(negedge clk_sys or negedge rst_n)
begin
if(~rst_n)
begin
scl<=1'b1; //当没有信号时,scl为高
end
else
begin
if(state>0)
scl<=~scl; //当读或写开始时,每遇到clk_sys一个下降沿时scl跳变,换为400KHZ时钟
else
scl<=1'b1;
end
end
//--------------------读写使能中间寄存器
reg [1:0]en;
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
begin
en<=2'b00;
end
else
begin
if(~key_wr)
en<=2'b01;
else if(~key_rd)
en<=2'b10;
end
end
//----------------------------------状态机
reg [3:0]cnt; //位数计数器
reg [1:0]temp; //读写使能中间寄存器
reg [7:0]memory; //接收发送数据的中间寄存器
always@(posedge clk_sys or negedge rst_n)
begin
if(~rst_n)
begin
data_out<=8'd0;
flag<=1'b1; //开始时控制SDA为高,此时scl也为高
sda_buffer<=1'b1; //开始时控制SDA为高,此时scl也为高
state<=0; //开始时控制SDA为高,此时scl也为高
temp<=2'b00; //没有读写使能,与en初始值一样
end
else
case(state)
0: begin
if(scl) //在scl为高时进行判断
begin
if(en!=temp) //如果读或写使能进来,准备状态转换
begin
sda_buffer<=1'b0; //此时,flag<=1'b1,所以sda<=0,而scl<=1,为启动信号
state<=1; //状态跳转
temp<=en; //进来的读写使能赋给temp,当控制字和高低地址都发送完后,判断是读或写操作
memory<=8'b1010_000_0; //1010为fpga板子型号,000为片选信号,0为方向即写
end
else
state<=0;
end
else
state<=0;
end
1: begin //发送控制信号,此时,flag=1,sda=sda_buffer
if((scl==0)&&(cnt<8)) //scl=0时发送,发送时钟变为200KHz
begin
sda_buffer<=memory[7];
cnt<=cnt+1;
state<=1;
memory<={memory[6:0],memory[7]};
end
else
begin
if((scl==0)&&(cnt==8))
begin
cnt<=0; //数据发完,释放总线,等待主机的应答信号
flag<=0; //数据发完,释放总线,等待主机的应答信号
state<=2;
end
else
state<=1;
end
end
2: begin
if(~sda) //sda为低,即有应答
begin
state<=3;
memory<=8'd0; //赋值高位地址
end
else
state<=0;
end
3: begin //发送高位地址
if((scl==0)&&(cnt<8))
begin
flag<=1;
sda_buffer<=memory[7];
cnt<=cnt+1;
memory<={memory[6:0],memory[7]};
state<=3;
end
else
begin
if((scl==0)&&(cnt==8)) //释放总线
begin
cnt<=0;
flag<=0;
state<=4;
end
else
state<=3;
end
end
4: begin //检测应答
if(~sda)
begin
state<=5;
memory<=8'd150; //低位地址
end
else
state<=0;
end
5: begin //发送低位地址
if((scl==0)&&(cnt<8))
begin
flag<=1;
sda_buffer<=memory[7];
cnt<=cnt+1;
memory<={memory[6:0],memory[7]};
state<=5;
end
else
begin
if((scl==0)&&(cnt==8)) //释放总线
begin
cnt<=0;
flag<=0;
state<=6;
end
else
state<=5;
end
end
6: begin //检测应答,判断读或写
if(~sda)
begin
if(temp==2'b01)
begin
state<=7;
memory<=data_in[7:0]; //写数据
end
if(temp==2'b10)
state<=11;
end
else
state<=0;
end
//==================================================================写
7: begin //发送写数据
if((scl==0)&&(cnt<8))
begin
flag<=1;
sda_buffer<=memory[7];
cnt<=cnt+1;
memory<={memory[6:0],memory[7]};
state<=7;
end
else
begin
if((scl==0)&&(cnt==8)) //释放总线
begin
cnt<=0;
flag<=0;
state<=8;
end
else
state<=7;
end
end
8: begin //检测应答
if(~sda)
begin
state<=9;
end
else
state<=0;
end
9: begin //拉低sda,为停止信号做准备
if(scl==0)
begin
flag<=1;
sda_buffer<=0;
state<=10;
end
else
state<=9;
end
10: begin //发送停止信号,控制总线
if(scl==1)
begin
sda_buffer<=1;
state<=0;
end
else
state<=10;
end
//---------------------------------------------------------------------读
11: begin //控制总线,在5 6状态时已经释放总线
flag<=1;
sda_buffer<=1; //拉高sda,为读的启动信号做准备
state<=12;
end
12: begin
sda_buffer<=0; //启动信号
state<=13;
memory<=8'b1010_000_1; //控制字
end
13: begin //发送控制字
if((scl==0)&&(cnt<8))
begin
flag<=1;
sda_buffer<=memory[7];
cnt<=cnt+1;
memory<={memory[6:0],memory[7]};
state<=13;
end
else
begin
if((scl==0)&&(cnt==8)) //释放总线
begin
cnt<=0;
flag<=0;
state<=14;
end
else
state<=13;
end
end
14: begin //检测应答
if(~sda)
begin
state<=15;
end
else
state<=0;
end
15: begin
if((scl==1)&&(cnt<8)) //接收sda数据
begin
cnt<=cnt+1;
memory<={memory[6:0],sda};
state<=15;
end
else
begin
if((scl==0)&&(cnt==8))
begin
cnt<=0;
flag<=1;
state<=16;
sda_buffer<=1; //发送应答信号
end
else
state<=15;
end
end
16: begin
data_out<=memory; //读出数据
state<=17;
end
17: begin
if(scl==0)
begin
sda_buffer<=0; //拉低sda,为停止信号做准备
state<=18;
end
else
state<=17;
end
18: begin
if(scl==1) //停止信号
begin
sda_buffer<=1;
state<=0;
end
else
state<=18;
end
default:state<=0;
endcase
end
assign data_out1=data_out;
endmodule