I2C协议FPGA verilog开发

说在前头

前两天老师布置了一个I2C协议的手绘图,我心想这么简单的协议我就用verilog写一个呗。
初步设想是双机,能够实现主从机相互转换,可以相互读写,但是现在只实现了主机写从机读从机,手头上目前还没有FPGA,主从任意转换就没有写了,今天就把目前做到的写下来,以后有机会就填坑。

I2C协议

瞌睡的洋葱博客
这位博主说的十分清楚了,再结合一下百度百科基本上就能搞清楚了。

时序图:

时序图

写寄存器的标准流程为:

  1. Master发起START
  2. Master发送I2C addr(7bit)和w操作0(1bit),等待ACK
  3. Slave发送ACK
  4. Master发送reg addr(8bit),等待ACK
  5. Slave发送ACK
  6. Master发送data(8bit),即要写入寄存器中的数据,等待ACK
  7. Slave发送ACK
  8. 第6步和第7步可以重复多次,即顺序写多个寄存器
  9. Master发起STOP

读寄存器的标准流程为:

  1. Master发送I2C addr(7bit)和w操作1(1bit),等待ACK
  2. Slave发送ACK
  3. Master发送reg addr(8bit),等待ACK
  4. Slave发送ACK
  5. Master发起START
  6. Master发送I2C addr(7bit)和r操作1(1bit),等待ACK
  7. Slave发送ACK
  8. Slave发送data(8bit),即寄存器里的值
  9. Master发送ACK
  10. 第8步和第9步可以重复多次,即顺序读多个寄存器

大家不懂的可以看看上面那一篇博客。

编程思路

既然了解了I2C协议的过程下面就是要解决问题了。
I2C协议的步骤如此清晰,我们自然而然想到使用状态机,状态机可以用FPGA系统时钟,这里为了方便我使用的是1Mhz的FPGA时钟I2C_SCL时钟为系统时钟的10分频.
状态机我分为6个状态:

  1. wait状态,完成部分初始化,SDA与SCL不工作时都为高;
  2. start状态,主机在SCL为高时将SDA拉低
  3. adder状态,主机输出7位设备地址和读写控制符
  4. adder2状态,主机输出数据存放地址
  5. doing状态,主机读写数据
  6. ask状态,每写完地址或者数据从机都需要应答
  7. stop状态,结束,回到等待状态
parameter s_wait=3'b000,s_start=3'b001,s_adder1=3'b010,s_adder2=3'b011,s_doing=3'b100,s_ask=3'b101,s_stop=3'b110;

完整代码如下

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2020/05/13 17:34:09
// Design Name: 
// Module Name: I2Cpro_top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module I2Cpro_top(
    input clk,
    input rst_n,
    input enable, //使能I2C模块
    input wr_cont,//0写,1读
    input [6:0]Device_addr,//设备地址
    input [7:0]Store_addr,//储存地址
    input [7:0]data_in,
    output [7:0]data_out,
    inout SDA,
    inout SCL);
    parameter s_wait=3'b000,s_start=3'b001,s_adder1=3'b010,s_adder2=3'b011,s_doing=3'b100,s_ask=3'b101,s_stop=3'b110;
    parameter data_size=8;
   

    reg doing=1'b0;         //SDA数据有效,开始到结束
    reg ready=1'b0;         //进行数据读写

    reg [7:0]data_reg[7:0];                 //暂存输入的所有数据
    reg [7:0]data_wr=8'h00,data_rd=8'h00;             //暂存收发数据
    reg [7:0]rDevice_wr_addr=8'b000;  //设备地址和读写控制
    reg [7:0]rStore_addr=8'h00;       //读写地址


    reg rSDA=1'b1;                          //SDA
    reg [2:0]rState=s_wait;                 //I2C控制状态机
    reg [2:0]bit_cnt=3'b000;                //读写bit计数器
    reg [7:0]data_cnt=data_size+2;          //读写byte计数器
    reg [4:0]buffer=5'b00001;               //在clk驱动的状态机中与SCL时钟规则匹配

    reg s_cnt=1'b0;                         //用于控制两次数据传输间隙

    //调用偶分频div模块
    //SCL时钟由clk系统时钟10分频产生
    sysclk2I2CSCL #(
        .N(10)      //分频数为10
    )sysclk2I2CSCL2(
        .clk(clk),
        .rst_n(rst_n),
        .SCL(SCL)
    );

    //  如果SCL高电平的时候检测到了SDA的高转低说明开始
    always@(negedge SDA)begin
        if(SCL==1)
        doing<=1'b1;
    end
    //  如果SCL高电平的时候检测到SDA的低转高说明结束
    always@(posedge SDA)begin
        if(SCL==1)
        doing<=1'b0;
    end

    always@(posedge clk)begin
        if(enable)begin
            if(!rst_n)begin
                rSDA<=1;
                rState<=s_wait;
                buffer<=5'b00001;
                data_cnt<=data_size+2;
                bit_cnt<=3'b000;                
            end
            else begin
                case(rState)
                s_wait:begin
                if(SCL)begin
                    buffer<={buffer[3:0],buffer[4]};
                    if(buffer[2])begin
                        if(s_cnt==1)begin
                        rSDA<=1'b1;
                        rState<=s_start;
                        rDevice_wr_addr<={Device_addr,wr_cont};
                        rStore_addr<=Store_addr;
                        data_wr<=data_in;
                        data_cnt<=data_size+2;
                        bit_cnt<=3'b000;
                        end
                        buffer<=5'b00001;  
                        s_cnt<=s_cnt+1; 
                    end
                end  
                end
                s_start:begin
                if(SCL)begin
                    buffer<={buffer[3:0],buffer[4]};
                    if(buffer[2])
                    begin
                    rSDA<=1'b0;
                    rState<=s_adder1;
                    buffer<=5'b00001;
                    end
                end
                end
                s_adder1:begin
                if(!SCL)begin
                    buffer<={buffer[3:0],buffer[4]};
                    if(buffer[2])
                    begin
                    rSDA<=rDevice_wr_addr[0];
                    rDevice_wr_addr<={rDevice_wr_addr[0],rDevice_wr_addr[7:1]};
                    bit_cnt<=bit_cnt+1'b1;
                    if(bit_cnt==7)
                    begin
                    data_cnt<=data_cnt-1;
                    rState<=s_ask;
                    buffer<=5'b00001;
                    end
                    end
                end                

                end
                s_adder2:begin
                if(!SCL)begin
                    buffer<={buffer[3:0],buffer[4]};
                    if(buffer[2])
                    begin
                    rSDA<=rStore_addr[0];
                    rStore_addr<={rStore_addr[0],rStore_addr[7:1]};
                    bit_cnt<=bit_cnt+1'b1;
                    if(bit_cnt==7)
                    begin
                    data_cnt<=data_cnt-1;
                    rState<=s_ask;
                    buffer<=5'b00001;     
                    end               
                    end
                end       
                end
                s_doing:begin
                    if(wr_cont==0)begin//如果是写,写入数据改变SDA
                        if(!SCL)begin
                            buffer<={buffer[3:0],buffer[4]};
                            if(buffer[2])begin
                            rSDA<=data_wr[0];
                            data_wr<={data_wr[0],data_wr[7:1]};
                            bit_cnt<=bit_cnt+1'b1;
                            if(bit_cnt==7)begin
                                data_cnt<=data_cnt-1;
                                rState<=s_ask;
                                buffer<=5'b00001;   
                            end               
                            end
                        end                   
                    end
                    else begin
                        if(SCL)begin
                            buffer<={buffer[3:0],buffer[4]};
                            if(buffer[2])begin
                                data_rd[0]<=SDA;
                                data_rd<={data_rd[0],data_rd[7:1]};
                                bit_cnt<=bit_cnt+1;
                                if(bit_cnt==7)begin
                                    data_reg[data_cnt]<=data_rd;
                                    data_cnt<=data_cnt+1;
                                end
                            end
                        end
                    end
                end
                s_ask:begin
                    if(wr_cont==0)begin //等待回复ask
                        if(SCL && !SDA)begin
                            buffer<={buffer[3:0],buffer[4]};
                            if(buffer[2])begin
                                if(data_cnt==data_size+1)
                                rState<=s_adder2;
                                else if(data_cnt==0)
                                rState<=s_stop;
                                else
                                rState<=s_doing;
                            end                       
                        end
                    end/*
                    else begin          //主动回复ask  从机时候
                        if(!SCL)begin
                            buffer<={buffer[3:0],buffer[4]};
                            if(buffer[2])begin
                            rSDA<=1'b0;
                            buffer<=5'b00001;
                            if(data_cnt==data_size+1)
                                rState<=s_adder2;
                            else if(data_cnt==0)
                                rState<=s_stop;
                            else
                                rState<=s_doing;
                            end
                        end   
                    end */                  
                end
                s_stop:begin            //结束
                if(SCL)begin
                    buffer<={buffer[3:0],buffer[4]};
                    if(buffer[2])
                    begin
                    if(s_cnt==1)begin
                    rSDA<=1'b1;
                    rState<=s_wait;
                    end
                    s_cnt<=s_cnt+1;
                    buffer<=5'b00001; 
                    end
                end
                end
                endcase
            end
        end
    end
//  读数据
    assign SDA=rSDA;
    assign data_out=data_rd;

endmodule

tsetbench

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2020/05/11 15:07:11
// Design Name: 
// Module Name: I2Ctb
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module I2Ctb();
reg clk;
reg rst_n;
reg enable;
reg wr_cont;
reg[6:0]Device_addr;//设备地址
reg[7:0]Store_addr;//储存地址
reg[7:0]data_in;
wire [7:0]data_out;
wire SDA;
wire SCL;


I2Cpro_top I2C_top1(
    .clk(clk),
    .rst_n(rst_n),
    .enable(enable),
    .wr_cont(wr_cont),
    .Device_addr(Device_addr),
    .Store_addr(Store_addr),
    .data_in(data_in),
    .data_out(data_out),
    .SDA(SDA),
    .SCL(SCL)
);

always #5 clk=~clk;
initial 
begin
    clk=0;
    rst_n=0;
    enable=0;
    wr_cont=0;
    Device_addr=7'h78;
    Store_addr=8'haa;
    data_in=8'hff;
    #10 
    enable=1;
    #10
    rst_n=1;
    #20000
    wr_cont=1;
end

endmodule

有人读的话再继续完善

  • 7
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Bigbeea

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

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

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

打赏作者

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

抵扣说明:

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

余额充值