IIC协议(含详细注释)

`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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yang_wei_bk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值