FPGA学习之---IIC总线协议

去年0基础参加高云赛道跟着野火手搓了一个IIC,但是调用OV5640时一直不出图,又没学会仿真IIC,一直以为没有写出来,头痛炸裂半年的阴影。现在跟着小梅哥课程学习到可以仿真EEPROM器件。本实验采用的是镁光官网提供的 EEPROM 仿真模型,仿真模型为24LC04B.v 文件。

开发板:7a35t

EEPROM:器件id:1010_000

实验中系统时钟为50Mhz,SCL频率为250Khz。

仿真时先对8'hB1地址写入8'h11,再读出数据。

实验中i2c_sda的三态门控制关系如下:

对应代码:

下图中红框中为发送区域。两个箭头分别为开始和停止时刻:

下图中红框中为接收区域。两个箭头分别为开始和停止时刻:

i2c控制模块:

module i2c_ctrl
/*
#(
    parameter DEVICE_ADDR =7'b1111_000 ,
    parameter SYS_CLK_FREQ ='d50_000_000 ,
    parameter SCL_FREQ = 'd250_000
)
*/
(
     input sys_clk,
     input sys_rst_n,
     input wr_en,
     input rd_en,
     input i2c_start,
     input addr_num,  //1:两个字节的存储地址  0:一个字节的存储地址
     input [15:0]byte_addr,
     input [7:0]wr_data,

     output  reg i2c_clk, // i2c工作时钟 1Mhz
     output  reg i2c_end,
     output  reg [7:0]rd_data,
     output  reg i2c_scl,  //250khz
     inout i2c_sda


);

    parameter DEVICE_ADDR =7'b1010_000 ;
    parameter SYS_CLK_FREQ ='d50_000_000 ;
    parameter SCL_FREQ = 'd250_000;
    parameter CNT_CLK_MAX ='d25 ;// 输入时钟为50Mhz时,SCL为

    parameter IDLE =4'd0 ;
    parameter START =4'd1 ;
    parameter SEND_D_A =4'd2 ;
    parameter ACK_1 =4'd3 ;
    parameter SEND_B_H =4'd4 ;
    parameter ACK_2 =4'd5 ;
    parameter SEND_B_L =4'd6 ;
    parameter ACK_3 =4'd7 ;
    parameter WR_DATA =4'd8 ;
    parameter ACK_4 =4'd9 ;
    parameter START_2 =4'd10 ;
    parameter SEND_R_A =4'd11 ;
    parameter ACK_5 =4'd12 ;
    parameter RD_DATA =4'd13 ;
    parameter N_ACK =4'd14 ;
    parameter STOP =4'd15 ;


    wire  sda_en ;
    wire sda_in ;

    reg [7:0] cnt_clk ;  //从0计数到24,对系统时钟50Mhz分频成为 1Mhz时钟
    reg [3:0] state ;
    reg [1:0] cnt_i2c_clk ; //从 0计数到3  i2c_scl的一个周期为 i2c_clk的 4 倍,所以计数4个i2c_clk 周期就是一个i2c_scl周期
    reg cnt_i2c_clk_en ;
    reg [3:0] cnt_bit ;  //计数从 0 到 7 ,总共 8 个i2c_scl周期,即传送了 8 个bit数据
    reg sda_out ; //等价于i2c_sda_reg
    reg [7:0]rd_data_reg ;

    reg ack ;


    always @(posedge sys_clk or negedge sys_rst_n) 
    begin
        if(~sys_rst_n)
            cnt_clk<=8'd0 ;
        else if(cnt_clk==CNT_CLK_MAX-1)
            cnt_clk<=8'd0 ;
        else
            cnt_clk<=cnt_clk+8'd1 ;
    end


    always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n)
            i2c_clk<=1'b1 ;
        else if(cnt_clk==CNT_CLK_MAX-1)
            i2c_clk<=~i2c_clk ;
        else 
            i2c_clk<=i2c_clk ; //产生1Mhz时钟
    end

      
    always@(posedge i2c_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n)
            state<=IDLE ;
        else case(state)
            IDLE:
                begin
                    if(i2c_start==1'b1)
                        state<=START ;
                    else
                        state<=state ;

                end
            START:
                begin
                    if(cnt_i2c_clk==2'd3)
                        state<=SEND_D_A ;
                    else
                        state<=state ;
                end
            SEND_D_A:
                begin
                    if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
                        state<=ACK_1 ;
                    else    
                        state<=state ;
                end 
            ACK_1:
                begin
                    if((cnt_i2c_clk==2'd3) && (ack==1'b0))
                        begin
                            if(addr_num)
                                state<=SEND_B_H ;
                            else
                                state<=SEND_B_L ;
                        end
                    else
                        state<=state ;
                end 
            SEND_B_H:
                begin 
                    if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
                        state<=SEND_B_L ;
                    else
                        state<=state ;
                end
            ACK_2:
                begin 
                    if((cnt_i2c_clk==2'd3) && (ack==1'b0))
                        state<=SEND_B_L ;
                    else
                        state<=state ;
                end
            SEND_B_L:
                begin 
                    if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
                        state<=ACK_3 ;
                    else
                        state<=state ;
                end
            ACK_3:
                begin
                    if((cnt_i2c_clk==2'd3) && (ack==1'b0))
                        begin
                            if(wr_en)
                                state<=WR_DATA ;
                            else if(rd_en)
                                state<=START_2 ;
                            else
                                state<=state ;
                        end
                    else
                        state<=state ;
                end
            WR_DATA:
                begin
                    if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
                        state<=ACK_4;
                    else
                        state<=state ;
                end
            ACK_4:
                begin
                    if((cnt_i2c_clk==2'd3) && (ack==1'b0))
                        state<=STOP ;
                    else
                        state<=state ;
                end
            START_2:
                begin
                    if(cnt_i2c_clk==2'd3)
                        state<=SEND_R_A ;
                    else
                        state<=state ;   
                end
            SEND_R_A:
                begin
                    if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
                        state<=ACK_5;
                    else
                        state<=state ;
                end
            ACK_5:
                begin
                    if((cnt_i2c_clk==2'd3) && (ack==1'b0))
                        state<=RD_DATA ;
                    else
                        state<=state ;
                end
            RD_DATA:
                begin 
                    if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd7))
                        state<=N_ACK ;
                    else
                        state<=state ;
                end
            N_ACK:
                begin
                    if(cnt_i2c_clk==2'd3)  //?
                        state<=STOP ;
                    else
                        state<=state ;
                end
            STOP:
                begin
                    if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd3))   //对 SCL 计数 4 个周期 保证时间充足
                        state<=IDLE ;
                    else
                        state<=state ;
                end
            default : state<=IDLE ;
                
        endcase

    end


    always@(posedge i2c_clk or negedge sys_rst_n ) //cnt_i2c_clk循环计数从0到3 4个i2c_clk周期为 1个scl周期
    begin
        if(~sys_rst_n)
            cnt_i2c_clk<=2'd0 ;
        else if(cnt_i2c_clk_en)
            cnt_i2c_clk<=cnt_i2c_clk+1'b1 ;
    end
//对cnt_i2c_clk_en 限制
    always@(posedge i2c_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n)
            cnt_i2c_clk_en<=1'b0 ;
        else if((state==STOP) && (cnt_i2c_clk==2'd3) && (cnt_bit==4'd3))
            cnt_i2c_clk_en<=1'b0 ;
        else if(i2c_start==1'b1)
            cnt_i2c_clk_en<=1'b1 ;
    end
//规定什么时候对 cnt_bit 进行计数
    always@(posedge i2c_clk or negedge sys_rst_n)
    begin 
        if(~sys_rst_n)
            cnt_bit<=4'd0 ;
        else if((state<=IDLE) || (state==START) || (state==ACK_1) ||(state==ACK_2)
                || (state==ACK_3) || (state==ACK_4) || (state==ACK_5) ||(state==START_2)
                 ||(state==N_ACK))
            cnt_bit<=4'd0 ;
        else if((cnt_i2c_clk==2'd3) && (cnt_bit== 4'd7))
            cnt_bit<=4'd0 ;
        else if((cnt_i2c_clk==2'd3) && (state!=IDLE)) //? 怎么让cnt_bit在STOP状态加到3
            cnt_bit<=cnt_bit+1'b1 ;
    end


    always@(*)
    begin 
        case(state)
            IDLE:
                sda_out<=1'b1 ;
            START:
                begin
                    if(cnt_i2c_clk==2'b0)
                        sda_out<=1'b1 ;
                    else
                        sda_out<=1'b0 ;
                end
            SEND_D_A:
                begin
                    if(cnt_bit<=4'd6)
                        sda_out<=DEVICE_ADDR[6-cnt_bit] ;
                    else
                        sda_out<=1'b0 ; //写控制字

                end
            AC
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值