求大神给一份PCF8591的基于FPGA的verilog的IIc控制程序

求大神给一份PCF8591的基于FPGA的verilog的IIc控制程序

module DAC_I2C
(
input clk_in, // system clock
input rst_n_in, // system reset, active low

input           [7:0]   dac_data,   // dac data input
output  reg             dac_done,   // dac transfer done

output                  scl_out,    // I2C CLK
inout                   sda_out     // I2C SDA

);

parameter   CNT_NUM =   30;

localparam  IDLE    =   3'd0;
localparam  MAIN    =   3'd1;
localparam  START   =   3'd2;
localparam  WRITE   =   3'd3;
localparam  ACK     =   3'd4;
localparam  STOP    =   3'd5;

//generate clk_200khz clock
reg                 clk_200khz;
reg     [5:0]       cnt_200khz;
always@(posedge clk_in or negedge rst_n_in) begin
    if(!rst_n_in) begin
        cnt_200khz <= 5'd0;
        clk_200khz <= 1'b0;
    end else if(cnt_200khz >= CNT_NUM-1) begin
        cnt_200khz <= 5'd0;
        clk_200khz <= ~clk_200khz;
    end else begin
        cnt_200khz <= cnt_200khz + 1'b1;
    end
end

//reg       [7:0]       cnt_wave;

reg                 scl_out_r;
reg                 sda_out_r;
reg     [3:0]       cnt_main;
reg     [7:0]       data_wr;
reg     [1:0]       cnt_start;
reg     [4:0]       cnt_write;
reg     [1:0]       cnt_ack;
reg     [1:0]       cnt_stop;
reg     [2:0]       state = IDLE;
reg     [2:0]       state_back = IDLE;
always@(posedge clk_200khz or negedge rst_n_in) begin
    if(!rst_n_in) begin
        //cnt_wave <= 8'd0;
        scl_out_r <= 1'd1;
        sda_out_r <= 1'd1;
        cnt_main <= 4'd0;
        cnt_start <= 2'd0;
        cnt_write <= 5'd0;
        cnt_ack <= 2'd0;
        cnt_stop <= 2'd0;
        state <= IDLE;
        state_back <= IDLE;
    end else begin
        case(state)
            IDLE:begin
                    scl_out_r <= 1'd1;
                    sda_out_r <= 1'd1;
                    cnt_main <= 4'd0;
                    cnt_start <= 2'd0;
                    cnt_write <= 5'd0;
                    cnt_ack <= 2'd0;
                    cnt_stop <= 2'd0;
                    state <= MAIN;
                    state_back <= MAIN;
                end
            MAIN:begin
                    //if(cnt_main >= 4'd8) cnt_main <= 4'd0;        //write in byte mode
                    if(cnt_main >= 4'd6) cnt_main <= 4'd5;      //write in chain mode
                    else cnt_main <= cnt_main + 1'b1;
                    case(cnt_main)
                        4'd0: begin state <= START; end
                        4'd1: begin data_wr <= 8'h90; state <= WRITE; end
                        4'd2: begin state <= ACK; end
                        4'd3: begin data_wr <= 8'h40; state <= WRITE; end
                        4'd4: begin state <= ACK; end
                        //4'd5: begin data_wr <= cnt_wave; cnt_wave <= cnt_wave+8'd8; state <= WRITE; end
                        4'd5: begin data_wr <= dac_data; state <= WRITE; dac_done <= 1'b0;end
                        4'd6: begin state <= ACK; dac_done <= 1'b1; end
                        4'd7: begin state <= STOP; end
                        4'd8: begin state <= IDLE; end
                        default: state <= IDLE;
                    endcase
                end
            START:begin
                    if(cnt_start >= 2'd2) cnt_start <= 1'b0;
                    else cnt_start <= cnt_start + 1'b1;
                    case(cnt_start)
                        2'd0:   begin sda_out_r <= 1'b1; scl_out_r <= 1'b1; end
                        2'd1:   begin sda_out_r <= 1'b0; end
                        2'd2:   begin scl_out_r <= 1'b0; state <= MAIN; end
                        default: state <= IDLE;
                    endcase
                end
            WRITE:begin
                    if(cnt_write >= 5'd16) cnt_write <= 1'b0;
                    else cnt_write <= cnt_write + 1'b1;
                    case(cnt_write)
                        //transfer data with i2c
                        5'd0:   begin sda_out_r <= data_wr[7]; scl_out_r <= 1'b0; end
                        5'd1:   begin scl_out_r <= 1'b1; end
                        5'd2:   begin sda_out_r <= data_wr[6]; scl_out_r <= 1'b0; end
                        5'd3:   begin scl_out_r <= 1'b1; end
                        5'd4:   begin sda_out_r <= data_wr[5]; scl_out_r <= 1'b0; end
                        5'd5:   begin scl_out_r <= 1'b1; end
                        5'd6:   begin sda_out_r <= data_wr[4]; scl_out_r <= 1'b0; end
                        5'd7:   begin scl_out_r <= 1'b1; end
                        5'd8:   begin sda_out_r <= data_wr[3]; scl_out_r <= 1'b0; end
                        5'd9:   begin scl_out_r <= 1'b1; end
                        5'd10:  begin sda_out_r <= data_wr[2]; scl_out_r <= 1'b0; end
                        5'd11:  begin scl_out_r <= 1'b1; end
                        5'd12:  begin sda_out_r <= data_wr[1]; scl_out_r <= 1'b0; end
                        5'd13:  begin scl_out_r <= 1'b1; end
                        5'd14:  begin sda_out_r <= data_wr[0]; scl_out_r <= 1'b0; end
                        5'd15:  begin scl_out_r <= 1'b1; end
                        5'd16:  begin scl_out_r <= 1'b0; state <= MAIN; end
                        default: state <= IDLE;
                    endcase
                end
            ACK:begin
                    if(cnt_ack >= 2'd3) cnt_ack <= 1'b0;
                    else cnt_ack <= cnt_ack + 1'b1;
                    case(cnt_ack)
                        //read bit 0
                        2'd0:   begin sda_out_r <= 1'bz; end
                        2'd1:   begin scl_out_r <= 1'b1; end
                        2'd2:   begin if(sda_out) state <= IDLE; else state <= state; end
                        2'd3:   begin scl_out_r <= 1'b0; state <= MAIN; end
                        default: state <= IDLE;
                    endcase
                end
            STOP:begin
                    if(cnt_stop >= 2'd2) cnt_stop <= 1'b0;
                    else cnt_stop <= cnt_stop + 1'b1;
                    case(cnt_stop)
                        2'd0:   begin sda_out_r <= 1'b0; end
                        2'd1:   begin scl_out_r <= 1'b1; end
                        2'd2:   begin sda_out_r <= 1'b1; state <= MAIN; end
                        default: state <= IDLE;
                    endcase
                end
        endcase
    end
end

assign  scl_out = scl_out_r;
assign  sda_out = sda_out_r;

endmodule