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
            ACK_1:
                sda_out<=1'b1 ;
            SEND_B_H:
                sda_out<=byte_addr[15-cnt_bit] ;
            ACK_2:
                sda_out<=1'b1 ;
            SEND_B_L:
                sda_out<=byte_addr[7-cnt_bit] ;
            ACK_3:
                sda_out<=1'b1 ;
            WR_DATA:
                sda_out<=wr_data[7-cnt_bit] ;
            ACK_4:
                sda_out<=1'b1 ;
            START_2:
                begin
                    if(cnt_i2c_clk<=2'd1)
                        sda_out<=1'b1 ;
                    else
                        sda_out<=1'b0 ;

                end
            SEND_R_A:
                begin
                    if(cnt_bit<=4'd6)
                        sda_out<=DEVICE_ADDR[6-cnt_bit] ;
                    else
                        sda_out<=1'b1;//写控制字
                end
            ACK_5:
                sda_out<=1'b1 ;
            RD_DATA:
                sda_out<=1'b1 ;
            N_ACK:
                sda_out<=1'b1 ;
            STOP:
                begin
                    if((cnt_bit==4'd0) && (cnt_i2c_clk<=2'd2))
                        sda_out<=1'b0 ;
                    else
                        sda_out<=1'b1 ;
                end
            default: sda_out<=1'b1 ;

        endcase        

    end

    //assign sda_en = ((state==RD_DATA) || (state==ACK_1) || (state==ACK_2) || (state==ACK_3) || (state==ACK_4) || (state==ACK_5))? 1'b0 : 1'b1;

    always@(posedge i2c_clk)
    begin 
        case(state)
            ACK_1,ACK_2,ACK_3,ACK_4,ACK_5: 
                begin
                    if(cnt_i2c_clk==2'd0)
                        ack<=sda_in ;
                    else
                        ack<=ack ;

                end
            default : ack<=1'b1 ;


        endcase 

    end

    assign sda_en = ((state==RD_DATA) || (state==ACK_1) || (state==ACK_2) || (state==ACK_3) || (state==ACK_4) || (state==ACK_5))? 1'b0 : 1'b1;
    assign sda_in=i2c_sda ;  //sda_in 在三态门中作为 i2c_sda 的输入
    assign i2c_sda=(sda_en)? sda_out: 1'bz ;

    always@(posedge i2c_clk)
    begin
        case(state)
            IDLE:
                rd_data_reg<=8'd0 ;
            RD_DATA:
            begin
                if(cnt_i2c_clk==2'd1)
                    rd_data_reg[7-cnt_bit]<=sda_in ; //先传输最高位
            end
            default :rd_data_reg<=rd_data_reg ;

        endcase


    end


    always@(posedge i2c_clk)
    begin 
        case(state)
        IDLE:
            i2c_scl<=1'b1 ;
        START:
            begin
                if(cnt_i2c_clk<=2'd1) //?有问题
                    i2c_scl<=1'b1 ;
                else
                    i2c_scl<=1'b0 ;
            end
        SEND_D_A,ACK_1,SEND_B_H,ACK_2,SEND_B_L,ACK_3,WR_DATA,ACK_4,START_2,SEND_R_A,ACK_5,RD_DATA,N_ACK:
            begin
                if((cnt_i2c_clk==2'd0)  || (cnt_i2c_clk==2'd1))
                    i2c_scl<=1'b1 ;
                else
                    i2c_scl<=1'b0 ;
            end
        STOP:
            begin
                if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd0))
                    i2c_scl<=1'b0 ;
                else
                    i2c_scl<=1'b1 ;
            end

        default : i2c_scl<=1'b1 ;

        endcase


    end



    //assign  i2c_sda=(sda_en)? sda_out: 1'bz ;

    always@(posedge i2c_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n)
            i2c_end<=1'b0 ;
        else if((state==STOP) && (cnt_bit==4'd3) &&(cnt_i2c_clk==2'd3))
            i2c_end<=1'b1;
        else
            i2c_end<=1'b0 ;

    end


    always@(posedge i2c_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n)
            rd_data<=8'd0 ;
        else if((state==RD_DATA) && (cnt_bit==4'd7) && (cnt_i2c_clk==2'd3))
            rd_data<=rd_data_reg ;
        else 
            rd_data<=rd_data ;


    end

endmodule

测试模块:

`timescale  1ns / 1ps

module tb_i2c_ctrl;



// i2c_ctrl Inputs
reg   sys_clk                              ;
reg   sys_rst_n                             ;
reg   wr_en                                 ;
reg   rd_en                                ;
reg   i2c_start                            ;
reg   addr_num                             = 0;
reg   [15:0]  byte_addr                    ;
reg   [7:0]  wr_data                       ;

// i2c_ctrl Outputs
wire  i2c_clk                              ;
wire  i2c_end                              ;
wire  [7:0]  rd_data                       ;
wire  i2c_scl                              ;


// i2c_ctrl Bidirs
wire  i2c_sda                              ;
pullup(i2c_sda);

always #10 sys_clk=~sys_clk;
initial
begin
    sys_clk=1'b0;
    sys_rst_n=1'b0;
    wr_en=1'b0;
    rd_en=1'b0;
    
    #200;
    sys_rst_n=1'b1;
    write_byte(8'hB1,8'h11);

    read_byte(8'hB1);
    
  
    
    
    $stop;
    


end

task write_byte;
    input [7:0]men_addr;
    input [7:0]data;
    begin
        byte_addr=men_addr;
        wr_data=data;
        i2c_start=1'b1;
        wr_en=1'b1;

        @(posedge i2c_end);
        i2c_start=1'b0;
        wr_en=1'b0;
        #50000;
    end

endtask

task read_byte;
    input [7:0]men_addr;
    begin
        byte_addr=men_addr;
        i2c_start=1'b1;
        rd_en=1'b1;

        @(posedge i2c_end);
        i2c_start=1'b0;
        rd_en=1'b0;
        #50000;
    end

endtask
/*
always@(posedge sys_clk or negedge sys_rst_n)
begin 

    if(~sys_rst_n)
        i2c_start<=1'b0;

    else
        i2c_start<=1'b1;

end
*/
/*
always@(posedge sys_clk or negedge sys_rst_n)
begin 
    if(~sys_rst_n)
    begin
        wr_en<=1'b1;
        rd_en<=1'b0;
    end
    else
    begin
        wr_en<=wr_en;
        rd_en<=rd_en;
    end

end
*/
M24LC04B M24LC04B(
    .A0(0),
    .A1(0),
    .A2(0),
    .WP(0),
    .SDA(i2c_sda),
    .SCL(i2c_scl),
    .RESET(sys_rst_n)
);

i2c_ctrl u_i2c_ctrl (
    .sys_clk                 ( sys_clk                ),
    .sys_rst_n               ( sys_rst_n              ),
    .wr_en                   ( wr_en                  ),
    .rd_en                   ( rd_en                  ),
    .i2c_start               ( i2c_start              ),
    .addr_num                ( addr_num               ),
    .byte_addr               ( byte_addr       [15:0] ),
    .wr_data                 ( wr_data         [7:0]  ),

    .i2c_clk                 ( i2c_clk                ),
    .i2c_end                 ( i2c_end                ),
    .rd_data                 ( rd_data         [7:0]  ),
    .i2c_scl                 ( i2c_scl                ),

    .i2c_sda                 ( i2c_sda                )
);



endmodule

为了不让时钟满天飞,我把i2c_ctrl做了一个改进,主要是把产生的1Mhz分频时钟改成使能时钟,测试结果一致,生成的i2c_scl仍然是250khz。代码如下:

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   i2c_clk_en, // 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时,产生1Mhz 的i2c_clk_en使能时钟信号
    parameter CNT_CLK_MAX ='d50 ;// 输入时钟为50Mhz时,产生1Mhz 的i2c_clk_en使能时钟信号
    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
 
 assign i2c_clk_en=(cnt_clk==CNT_CLK_MAX-1)? 1'b1 :1'b0;

 /*
    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 sys_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n)
            state<=IDLE ;
        else if(i2c_clk_en) 
        begin
        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
    end
    always@(posedge sys_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(i2c_clk_en)
        begin
            if(cnt_i2c_clk_en)
                cnt_i2c_clk<=cnt_i2c_clk+1'b1 ;
        end
    end
//对cnt_i2c_clk_en 限制
    always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n)
            cnt_i2c_clk_en<=1'b0 ;
        else if(i2c_clk_en)
        begin
            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
    end
//规定什么时候对 cnt_bit 进行计数
    always@(posedge sys_clk or negedge sys_rst_n)
    begin 
        if(~sys_rst_n)
            cnt_bit<=4'd0 ;
        else if(i2c_clk_en)
            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
            ACK_1:
                sda_out<=1'b1 ;
            SEND_B_H:
                sda_out<=byte_addr[15-cnt_bit] ;
            ACK_2:
                sda_out<=1'b1 ;
            SEND_B_L:
                sda_out<=byte_addr[7-cnt_bit] ;
            ACK_3:
                sda_out<=1'b1 ;
            WR_DATA:
                sda_out<=wr_data[7-cnt_bit] ;
            ACK_4:
                sda_out<=1'b1 ;
            START_2:
                begin
                    if(cnt_i2c_clk<=2'd1)
                        sda_out<=1'b1 ;
                    else
                        sda_out<=1'b0 ;
 
                end
            SEND_R_A:
                begin
                    if(cnt_bit<=4'd6)
                        sda_out<=DEVICE_ADDR[6-cnt_bit] ;
                    else
                        sda_out<=1'b1;//写控制字
                end
            ACK_5:
                sda_out<=1'b1 ;
            RD_DATA:
                sda_out<=1'b1 ;
            N_ACK:
                sda_out<=1'b1 ;
            STOP:
                begin
                    if((cnt_bit==4'd0) && (cnt_i2c_clk<=2'd2))
                        sda_out<=1'b0 ;
                    else
                        sda_out<=1'b1 ;
                end
            default: sda_out<=1'b1 ;
 
        endcase        
 
    end
 
    //assign sda_en = ((state==RD_DATA) || (state==ACK_1) || (state==ACK_2) || (state==ACK_3) || (state==ACK_4) || (state==ACK_5))? 1'b0 : 1'b1;
 
    always@(posedge sys_clk)
    begin 
        if(i2c_clk_en)
        begin
        case(state)
            ACK_1,ACK_2,ACK_3,ACK_4,ACK_5: 
                begin
                    if(cnt_i2c_clk==2'd0)
                        ack<=sda_in ;
                    else
                        ack<=ack ;
                end
            default : ack<=1'b1 ;
 
 
        endcase
        end 
 
    end
 
    assign sda_en = ((state==RD_DATA) || (state==ACK_1) || (state==ACK_2) || (state==ACK_3) || (state==ACK_4) || (state==ACK_5))? 1'b0 : 1'b1;
    assign sda_in=i2c_sda ;  //sda_in 在三态门中作为 i2c_sda 的输入
    assign i2c_sda=(sda_en)? sda_out: 1'bz ;
    always@(posedge sys_clk)
    begin
        if(i2c_clk_en)
        begin
        case(state)
            IDLE:
                rd_data_reg<=8'd0 ;
            RD_DATA:
            begin
                if(cnt_i2c_clk==2'd1)
                    rd_data_reg[7-cnt_bit]<=sda_in ; //先传输最高位
            end
            default :rd_data_reg<=rd_data_reg ;
        endcase
        end
    end
    always@(posedge sys_clk)
    begin 
        if(i2c_clk_en)
        begin
        case(state)
        IDLE:
            i2c_scl<=1'b1 ;
        START:
            begin
                if(cnt_i2c_clk<=2'd1) //?有问题
                    i2c_scl<=1'b1 ;
                else
                    i2c_scl<=1'b0 ;
            end
        SEND_D_A,ACK_1,SEND_B_H,ACK_2,SEND_B_L,ACK_3,WR_DATA,ACK_4,START_2,SEND_R_A,ACK_5,RD_DATA,N_ACK:
            begin
                if((cnt_i2c_clk==2'd0)  || (cnt_i2c_clk==2'd1))
                    i2c_scl<=1'b1 ;
                else
                    i2c_scl<=1'b0 ;
            end
        STOP:
            begin
                if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd0))
                    i2c_scl<=1'b0 ;
                else
                    i2c_scl<=1'b1 ;
            end
        default : i2c_scl<=1'b1 ;
 
        endcase
        end
 
 
    end
 
 
 
    //assign  i2c_sda=(sda_en)? sda_out: 1'bz ;
    always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n)
            i2c_end<=1'b0 ;
        else if(i2c_clk_en)
            if((state==STOP) && (cnt_bit==4'd3) &&(cnt_i2c_clk==2'd3))
                i2c_end<=1'b1;
            else
                i2c_end<=1'b0 ;
 
    end
 
 
    always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n)
            rd_data<=8'd0 ;
        else if(i2c_clk_en)
            if((state==RD_DATA) && (cnt_bit==4'd7) && (cnt_i2c_clk==2'd3))
                rd_data<=rd_data_reg ;
            else 
                rd_data<=rd_data ;
    end
endmodule

改进之后的波形图:可以看到i2c_scl的一个周期为4us,符合预期。

改进后的测试代码:

`timescale  1ns / 1ps

module tb_i2c_ctrl;



// i2c_ctrl Inputs
reg   sys_clk                              ;
reg   sys_rst_n                             ;
reg   wr_en                                 ;
reg   rd_en                                ;
reg   i2c_start                            ;
reg   addr_num                             = 0;
reg   [15:0]  byte_addr                    ;
reg   [7:0]  wr_data                       ;

// i2c_ctrl Outputs
wire  i2c_clk_en                              ;
wire  i2c_end                              ;
wire  [7:0]  rd_data                       ;
wire  i2c_scl                              ;


// i2c_ctrl Bidirs
wire  i2c_sda                              ;
pullup(i2c_sda);

always #10 sys_clk=~sys_clk;
initial
begin
    sys_clk=1'b0;
    sys_rst_n=1'b0;
    wr_en=1'b0;
    rd_en=1'b0;
    
    #200;
    sys_rst_n=1'b1;
    write_byte(8'hB1,8'h11);

    read_byte(8'hB1);
    
  
    
    
    $stop;
    


end

task write_byte;
    input [7:0]men_addr;
    input [7:0]data;
    begin
        byte_addr=men_addr;
        wr_data=data;
        i2c_start=1'b1;
        wr_en=1'b1;

        @(posedge i2c_end);
        i2c_start=1'b0;
        wr_en=1'b0;
        #50000;
    end

endtask

task read_byte;
    input [7:0]men_addr;
    begin
        byte_addr=men_addr;
        i2c_start=1'b1;
        rd_en=1'b1;

        @(posedge i2c_end);
        i2c_start=1'b0;
        rd_en=1'b0;
        #50000;
    end

endtask
/*
always@(posedge sys_clk or negedge sys_rst_n)
begin 

    if(~sys_rst_n)
        i2c_start<=1'b0;

    else
        i2c_start<=1'b1;

end
*/
/*
always@(posedge sys_clk or negedge sys_rst_n)
begin 
    if(~sys_rst_n)
    begin
        wr_en<=1'b1;
        rd_en<=1'b0;
    end
    else
    begin
        wr_en<=wr_en;
        rd_en<=rd_en;
    end

end
*/
M24LC04B M24LC04B(
    .A0(0),
    .A1(0),
    .A2(0),
    .WP(0),
    .SDA(i2c_sda),
    .SCL(i2c_scl),
    .RESET(sys_rst_n)
);

i2c_ctrl u_i2c_ctrl (
    .sys_clk                 ( sys_clk                ),
    .sys_rst_n               ( sys_rst_n              ),
    .wr_en                   ( wr_en                  ),
    .rd_en                   ( rd_en                  ),
    .i2c_start               ( i2c_start              ),
    .addr_num                ( addr_num               ),
    .byte_addr               ( byte_addr       [15:0] ),
    .wr_data                 ( wr_data         [7:0]  ),

    .i2c_clk_en                 ( i2c_clk_en                ),
    .i2c_end                 ( i2c_end                ),
    .rd_data                 ( rd_data         [7:0]  ),
    .i2c_scl                 ( i2c_scl                ),

    .i2c_sda                 ( i2c_sda                )
);



endmodule

2024.5.28

再改进一个版本,因为实际物理期间两次操作之间需要有一定延时,所以在原来的基础上增加一个延时

i2c_ctrl代码:

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 [19:0]delay_time,//实际器件两次操作之间需要延时
     input addr_num,  //1:两个字节的存储地址  0:一个字节的存储地址
     input [15:0]byte_addr,
     input [7:0]wr_data,
     output   i2c_clk_en, // 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时,产生1Mhz 的i2c_clk_en使能时钟信号
    parameter CNT_CLK_MAX ='d50 ;// 输入时钟为50Mhz时,产生1Mhz 的i2c_clk_en使能时钟信号
    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 ;

    reg [19:0]delay_count;//延时计数
    wire p_i2c_start;//检测i2c_start上升沿
    reg i2c_start_reg1;
    reg delay_en;//延迟使能信号,该信号有效时进行计数延时
    reg i2c_start_delay;//延时过后的开始信号

    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n)
            i2c_start_reg1<=1'b0;
        else 
            i2c_start_reg1<=i2c_start;
    end

    assign p_i2c_start=(i2c_start && !i2c_start_reg1) ? 1'b1: 1'b0;
    
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n)
            delay_en<=1'b0;
        else if(p_i2c_start)
            delay_en<=1'b1;
        else if(delay_count==delay_time)
            delay_en<=1'b0;

    end

    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n)
            delay_count<=20'd0;
        else if(delay_count==delay_time)
            delay_count<=20'd0;
        else if(delay_en)
            delay_count<=delay_count+1'b1;
    end

    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(~sys_rst_n)
            i2c_start_delay<=1'b0;
        else if(delay_count==delay_time)
            i2c_start_delay<=1'b1;
        else if(i2c_end)
            i2c_start_delay<=1'b0;
    end

    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
 
 assign i2c_clk_en=(cnt_clk==CNT_CLK_MAX-1)? 1'b1 :1'b0;
 /*
    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 sys_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n)
            state<=IDLE ;
        else if(i2c_clk_en) 
        begin
        case(state)
            IDLE:
                begin
                    if(i2c_start_delay==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
    end
    always@(posedge sys_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(i2c_clk_en)
        begin
            if(cnt_i2c_clk_en)
                cnt_i2c_clk<=cnt_i2c_clk+1'b1 ;
        end
    end
//对cnt_i2c_clk_en 限制
    always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n)
            cnt_i2c_clk_en<=1'b0 ;
        else if(i2c_clk_en)
        begin
            if((state==STOP) && (cnt_i2c_clk==2'd3) && (cnt_bit==4'd3))
                cnt_i2c_clk_en<=1'b0 ;
            else if(i2c_start_delay==1'b1)
                cnt_i2c_clk_en<=1'b1 ;
        end
    end
//规定什么时候对 cnt_bit 进行计数
    always@(posedge sys_clk or negedge sys_rst_n)
    begin 
        if(~sys_rst_n)
            cnt_bit<=4'd0 ;
        else if(i2c_clk_en)
            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
            ACK_1:
                sda_out<=1'b1 ;
            SEND_B_H:
                sda_out<=byte_addr[15-cnt_bit] ;
            ACK_2:
                sda_out<=1'b1 ;
            SEND_B_L:
                sda_out<=byte_addr[7-cnt_bit] ;
            ACK_3:
                sda_out<=1'b1 ;
            WR_DATA:
                sda_out<=wr_data[7-cnt_bit] ;
            ACK_4:
                sda_out<=1'b1 ;
            START_2:
                begin
                    if(cnt_i2c_clk<=2'd1)
                        sda_out<=1'b1 ;
                    else
                        sda_out<=1'b0 ;
 
                end
            SEND_R_A:
                begin
                    if(cnt_bit<=4'd6)
                        sda_out<=DEVICE_ADDR[6-cnt_bit] ;
                    else
                        sda_out<=1'b1;//写控制字
                end
            ACK_5:
                sda_out<=1'b1 ;
            RD_DATA:
                sda_out<=1'b1 ;
            N_ACK:
                sda_out<=1'b1 ;
            STOP:
                begin
                    if((cnt_bit==4'd0) && (cnt_i2c_clk<=2'd2))
                        sda_out<=1'b0 ;
                    else
                        sda_out<=1'b1 ;
                end
            default: sda_out<=1'b1 ;
 
        endcase        
 
    end
 
    //assign sda_en = ((state==RD_DATA) || (state==ACK_1) || (state==ACK_2) || (state==ACK_3) || (state==ACK_4) || (state==ACK_5))? 1'b0 : 1'b1;
 
    always@(posedge sys_clk)
    begin 
        if(i2c_clk_en)
        begin
        case(state)
            ACK_1,ACK_2,ACK_3,ACK_4,ACK_5: 
                begin
                    if(cnt_i2c_clk==2'd0)
                        ack<=sda_in ;
                    else
                        ack<=ack ;
                end
            default : ack<=1'b1 ;
 
 
        endcase
        end 
 
    end
 
    assign sda_en = ((state==RD_DATA) || (state==ACK_1) || (state==ACK_2) || (state==ACK_3) || (state==ACK_4) || (state==ACK_5))? 1'b0 : 1'b1;
    assign sda_in=i2c_sda ;  //sda_in 在三态门中作为 i2c_sda 的输入
    assign i2c_sda=(sda_en)? sda_out: 1'bz ;
    always@(posedge sys_clk)
    begin
        if(i2c_clk_en)
        begin
        case(state)
            IDLE:
                rd_data_reg<=8'd0 ;
            RD_DATA:
            begin
                if(cnt_i2c_clk==2'd1)
                    rd_data_reg[7-cnt_bit]<=sda_in ; //先传输最高位
            end
            default :rd_data_reg<=rd_data_reg ;
        endcase
        end
    end
    always@(posedge sys_clk)
    begin 
        if(i2c_clk_en)
        begin
        case(state)
        IDLE:
            i2c_scl<=1'b1 ;
        START:
            begin
                if(cnt_i2c_clk<=2'd1) //?有问题
                    i2c_scl<=1'b1 ;
                else
                    i2c_scl<=1'b0 ;
            end
        SEND_D_A,ACK_1,SEND_B_H,ACK_2,SEND_B_L,ACK_3,WR_DATA,ACK_4,START_2,SEND_R_A,ACK_5,RD_DATA,N_ACK:
            begin
                if((cnt_i2c_clk==2'd0)  || (cnt_i2c_clk==2'd1))
                    i2c_scl<=1'b1 ;
                else
                    i2c_scl<=1'b0 ;
            end
        STOP:
            begin
                if((cnt_i2c_clk==2'd3) && (cnt_bit==4'd0))
                    i2c_scl<=1'b0 ;
                else
                    i2c_scl<=1'b1 ;
            end
        default : i2c_scl<=1'b1 ;
 
        endcase
        end
 
 
    end
 
 
 
    //assign  i2c_sda=(sda_en)? sda_out: 1'bz ;
    always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n)
            i2c_end<=1'b0 ;
        else if(i2c_clk_en)
            if((state==STOP) && (cnt_bit==4'd3) &&(cnt_i2c_clk==2'd3))
                i2c_end<=1'b1;
            else
                i2c_end<=1'b0 ;
 
    end
 
 
    always@(posedge sys_clk or negedge sys_rst_n)
    begin
        if(~sys_rst_n)
            rd_data<=8'd0 ;
        else if(i2c_clk_en)
            if((state==RD_DATA) && (cnt_bit==4'd7) && (cnt_i2c_clk==2'd3))
                rd_data<=rd_data_reg ;
            else 
                rd_data<=rd_data ;
    end
endmodule

  • 10
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值