直接上代码:

`timescale  1ns/1ns

module  i2c_ctrl_adc

#(

    parameter   DEVICE_ADDR     =   7'b1001_000     ,   //i2c设备地址

    parameter   SYS_CLK_FREQ    =   26'd50_000_000  ,   //输入系统时钟频率

    parameter   SCL_FREQ        =   18'd250_000     ,   //i2c设备scl时钟频率

    parameter   UART_BPS        =   26'd1_000_000   ,   //串口波特率

    parameter   CLK_FREQ        =   26'd50_000_000      //时钟频率

)

(

    input   wire            sys_clk     ,   //输入系统时钟,50MHz

    input   wire            sys_rst_n   ,   //输入复位信号,低电平有效

    output  reg             i2c_scl     ,   //输出至i2c设备的串行时钟信号scl

    inout   wire            i2c_sda     ,   //输出至i2c设备的串行数据信号sda

    output  reg             tx              //串转并后的1bit数据

);

//************************************************************************//

//******************** Parameter and Internal Signal *********************//

//************************************************************************//

// parameter define

parameter   CNT_CLK_MAX     =   (SYS_CLK_FREQ/SCL_FREQ) >> 2'd3   ;   //cnt_clk计数器计数最大值

parameter   CNT_START_MAX   =   8'd100; //cnt_start计数器计数最大值

parameter   IDLE            =   5'd00,  //初始状态

            START_WR        =   5'd01,  //开始状态

            SEND_WR_ADDR    =   5'd02,  //设备地址写入状态 + 控制写

            ACK_WADDR       =   5'd03,  //应答状态

            SEND_B_ADDR_H   =   5'd04,  //字节地址高八位写入状态

            ACK_2           =   5'd05,  //应答状态

            SEND_B_ADDR_L   =   5'd06,  //字节地址低八位写入状态

            ACK_3           =   5'd07,  //应答状态

            WR_DATA         =   5'd08,  //写数据状态

            ACK_WD          =   5'd09,  //应答状态

            START_RD        =   5'd10,  //开始状态

            SEND_RD_ADDR    =   5'd11,  //设备地址写入状态 + 控制读

            ACK_RADDR       =   5'd12,  //应答状态

            ACK_MRD         =   5'd13,  //应答状态

            RD_DATA         =   5'd14,  //读数据状态

            N_ACK           =   5'd15,  //非应答状态

            STOP_WR         =   5'd16,  //结束状态

            STOP_RD         =   5'd17;  //结束状态

localparam  BAUD_CNT_MAX    =   CLK_FREQ/UART_BPS;

// wire  define

wire            sda_in          ;   //sda输入数据寄存

wire            sda_en          ;   //sda数据写入使能信号

// reg   define

reg     [7:0]   cnt_clk         ;   //系统时钟计数器,控制生成clk_i2c时钟信号

reg     [4:0]   state           ;   //状态机状态

reg     [1:0]   cnt_i2c_clk     ;   //clk_i2c时钟计数器,控制生成cnt_bit信号

reg     [3:0]   cnt_bit         ;   //sda比特计数器

reg             cnt_finish      ;   //完成一个字节写入或者读取标志

reg             ack             ;   //应答信号

reg             i2c_sda_reg     ;   //sda数据缓存

reg     [1:0]   rd_data_cnt     ;   //对读出的数据进行计数

reg             wr_en           ;   //输入写使能信号

reg             rd_en           ;   //输入读使能信号

reg     [23:0]  rd_data         ;   //输出i2c设备读取数据

reg             i2c_clk         ;   //i2c驱动时钟

reg     [7:0]   wr_data         ;

//reg   define

reg     [12:0]  baud_cnt        ;

reg             bit_flag        ;

reg     [5:0]   bit_cnt         ;

reg             work_en         ;

reg             pi_flag         ;

reg     [15:0]  send_cnt        ;

//********************************************************************//

//***************************** Main Code ****************************//

//********************************************************************//

//work_en:接收数据工作使能信号

always@(posedge sys_clk or negedge sys_rst_n)

        if(sys_rst_n == 1'b0)

            work_en <= 1'b0;

        else    if(pi_flag == 1'b1)

            work_en <= 1'b1;

        else    if((bit_flag == 1'b1) && (bit_cnt == 6'd39))

            work_en <= 1'b0;

//baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1

always@(posedge sys_clk or negedge sys_rst_n)

        if(sys_rst_n == 1'b0)

            baud_cnt <= 13'b0;

        else    if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0))

            baud_cnt <= 13'b0;

        else    if(work_en == 1'b1)

            baud_cnt <= baud_cnt + 1'b1;

//bit_flag:当baud_cnt计数器计数到1时让bit_flag拉高一个时钟的高电平

always@(posedge sys_clk or negedge sys_rst_n)

        if(sys_rst_n == 1'b0)

            bit_flag <= 1'b0;

        else    if(baud_cnt == 13'd1)

            bit_flag <= 1'b1;

        else

            bit_flag <= 1'b0;

//bit_cnt:数据位数个数计数,10个有效数据(含起始位和停止位)到来后计数器清零

always@(posedge sys_clk or negedge sys_rst_n)

    if(sys_rst_n == 1'b0)

        bit_cnt <= 4'b0;

    else    if((bit_flag == 1'b1) && (bit_cnt == 6'd39)) begin

        bit_cnt   <= 4'b0;

    end

    else    if((bit_flag == 1'b1) && (work_en == 1'b1))

        bit_cnt <= bit_cnt + 1'b1;

//tx:输出数据在满足rs232协议(起始位为0,停止位为1)的情况下一位一位输出

always@(posedge sys_clk or negedge sys_rst_n)

    if(sys_rst_n == 1'b0)

        tx <= 1'b1; //空闲状态时为高电平

    else    if(bit_flag == 1'b1)

        case(bit_cnt)

            0       : tx <= 1'b0;

            1       : tx <= 1'b0;

            2       : tx <= 1'b1;

            3       : tx <= 1'b1;

            4       : tx <= 1'b1;

            5       : tx <= 1'b1;

            6       : tx <= 1'b1;

            7       : tx <= 1'b1;

            8       : tx <= 1'b0;

            9       : tx <= 1'b1;

            10      : tx <= 1'b0;

            11      : tx <= rd_data[16];

            12      : tx <= rd_data[17];

            13      : tx <= rd_data[18];

            14      : tx <= rd_data[19];

            15      : tx <= rd_data[20];

            16      : tx <= rd_data[21];

            17      : tx <= rd_data[22];

            18      : tx <= rd_data[23];

            19      : tx <= 1'b1;

            20      : tx <= 1'b0;

            21      : tx <= rd_data[8];

            22      : tx <= rd_data[9];

            23      : tx <= rd_data[10];

            24      : tx <= rd_data[11];

            25      : tx <= rd_data[12];

            26      : tx <= rd_data[13];

            27      : tx <= rd_data[14];

            28      : tx <= rd_data[15];

            29      : tx <= 1'b1;

            30      : tx <= 1'b0;

            31      : tx <= rd_data[0];

            32      : tx <= rd_data[1];

            33      : tx <= rd_data[2];

            34      : tx <= rd_data[3];

            35      : tx <= rd_data[4];

            36      : tx <= rd_data[5];

            37      : tx <= rd_data[6];

            38      : tx <= rd_data[7];

            39      : tx <= 1'b1;

            default : tx <= 1'b1;

        endcase

//************************************************************************//

//******************************* Main Code ******************************//

//************************************************************************//

// cnt_clk:系统时钟计数器,控制生成clk_i2c时钟信号

always@(posedge sys_clk or negedge sys_rst_n) begin

    if(sys_rst_n == 1'b0)

        cnt_clk <=  8'd0;

    else    if(cnt_clk == CNT_CLK_MAX - 1'b1)

        cnt_clk <=  8'd0;

    else

        cnt_clk <=  cnt_clk + 1'b1;

end

// i2c_clk:i2c驱动时钟

always@(posedge sys_clk or negedge sys_rst_n)

    if(sys_rst_n == 1'b0)

        i2c_clk <=  1'b1;

    else    if(cnt_clk == CNT_CLK_MAX - 1'b1)

        i2c_clk <=  ~i2c_clk;

// cnt_i2c_clk:i2c_clk时钟计数器,控制生成cnt_bit信号

always@(posedge i2c_clk or negedge sys_rst_n)

    if(sys_rst_n == 1'b0)

        cnt_i2c_clk = 2'd00;

    else    if(cnt_i2c_clk < 2'b11)

        cnt_i2c_clk = cnt_i2c_clk + 2'b01;

    else

        cnt_i2c_clk = 2'b00;

// sda_in:sda输入数据寄存

assign  sda_in = i2c_sda;

// sda_en:sda数据写入使能信号

assign  sda_en = ((state == RD_DATA)   ||

                  (state == ACK_WADDR) ||

                  (state == ACK_RADDR) ||

                  (state == ACK_WD)) ? 1'b0 : 1'b1;

// i2c_sda:输出至i2c设备的串行数据信号sda

assign  i2c_sda = (sda_en == 1'b1) ? i2c_sda_reg : 1'bz;

always@(posedge i2c_clk or negedge sys_rst_n)

    if(sys_rst_n == 1'b0) begin

            i2c_scl      <= 1'b1;

            i2c_sda_reg  <= 1'b1;

            state        <= START_WR;

            cnt_bit      <= 4'b0000;

            cnt_finish   <= 1'b0;

            rd_data_cnt  <= 1'b0;

            rd_data      = 24'b0;

            rd_en        <= 1'b0;

            wr_en        <= 1'b1;

            wr_data      <= 8'b1000_1100; //8'b10001100; // CONFIGURATION REGISTER

            send_cnt     <= 0;

        end

    else case(state)

        IDLE: begin

            if(cnt_i2c_clk == 2'b00) begin

                if(pi_flag == 1'b1) begin

                    if(work_en == 1'b1) begin

                        pi_flag <= 1'b0;

                        rd_en   <= 1'b0;

                        wr_en   <= 1'b0;

                    end

                end

                else if(pi_flag == 1'b0) begin

                    if(work_en == 1'b0) begin

                        if(send_cnt >= 1000) begin

                            send_cnt <= 0;

                            rd_en   <= 1'b1;

                            wr_en   <= 1'b0;

                        end

                        else

                            send_cnt <= send_cnt + 1;

                    end

                end

               

            end

            if(cnt_i2c_clk == 2'b01) begin

            end

            if(cnt_i2c_clk == 2'b10) begin

            end

            if(cnt_i2c_clk == 2'b11) begin

                i2c_scl     <= 1'b1;

                i2c_sda_reg <= 1'b1;

                if(wr_en == 1'b1) begin

                    wr_en   <= 1'b0;

                    state   <= START_WR;

                    rd_data = 24'b0;

                end

                else if(rd_en == 1'b1) begin

                    rd_en   <= 1'b0;

                    state   <= START_RD;

                    rd_data = 24'b0;

                end

            end

        end

       

        START_WR: begin

            if(cnt_i2c_clk == 2'b00) begin

                i2c_sda_reg <= 1'b1;

                i2c_scl     <= 1'b1;

            end

            else if(cnt_i2c_clk == 2'b01) begin

                i2c_sda_reg <= 1'b0;

            end

            else if(cnt_i2c_clk == 2'b10) begin

                i2c_scl     <= 1'b1;

            end

            else if(cnt_i2c_clk == 2'b11) begin

                i2c_scl     <= 1'b0;

                state       <= SEND_WR_ADDR;

            end

        end

        SEND_WR_ADDR: begin

            if(cnt_i2c_clk == 2'b00) begin

                i2c_scl <= 1'b0;

                if(cnt_bit <= 4'd6) begin

                    i2c_sda_reg <=  DEVICE_ADDR[6 - cnt_bit];

                end

                else begin

                    i2c_sda_reg <=  1'b0;

                end

                if(cnt_bit <= 4'd6) begin

                    cnt_bit <= cnt_bit + 1'b1;

                end

                else begin

                    cnt_bit    <= 4'd0;

                    cnt_finish <= 1'b1;

                end

            end

            else if(cnt_i2c_clk == 2'b01) begin

                i2c_scl <= 1'b1;

            end

            else if(cnt_i2c_clk == 2'b10) begin

                i2c_scl <= 1'b0;

            end

            else if(cnt_i2c_clk == 2'b11) begin

                if(cnt_finish == 1'b1) begin

                    state      <= ACK_WADDR;

                    cnt_finish <= 1'b0;

                end

            end

        end

        ACK_WADDR: begin

            if(cnt_i2c_clk == 2'b00) begin

                i2c_scl <= 1'b0;

            end

            else if(cnt_i2c_clk == 2'b01) begin

                i2c_scl <= 1'b1;

            end

            else if(cnt_i2c_clk == 2'b10) begin

                ack     <= sda_in;

            end

            else if(cnt_i2c_clk == 2'b11) begin

                i2c_scl <= 1'b0;

                if(ack == 1'b1) begin

                    state <= IDLE;

                end

                else begin

                    state <= WR_DATA;

                end

            end

        end

        WR_DATA: begin

            if(cnt_i2c_clk == 2'b00) begin

                if(cnt_bit <= 4'd7) begin

                    i2c_sda_reg <=  wr_data[7 - cnt_bit];

                    cnt_bit <= cnt_bit + 1'b1;

                end

            end

            else if(cnt_i2c_clk == 2'b01) begin

                i2c_scl <= 1'b1;

                if(cnt_bit == 4'd8) begin

                    cnt_bit    <= 4'd0;

                    cnt_finish <= 1'b1;

                end

            end

            else if(cnt_i2c_clk == 2'b10) begin

                i2c_scl <= 1'b0;

            end

            else if(cnt_i2c_clk == 2'b11) begin

                if(cnt_finish == 1'b1) begin

                    state      <= ACK_WD;

                    cnt_finish <= 1'b0;

                end

            end

        end

        ACK_WD: begin

            if(cnt_i2c_clk == 2'b00) begin

                i2c_scl <= 1'b0;

            end

            else if(cnt_i2c_clk == 2'b01) begin

                i2c_scl <= 1'b1;

            end

            else if(cnt_i2c_clk == 2'b10) begin

                ack     <= sda_in;

            end

            else if(cnt_i2c_clk == 2'b11) begin

                i2c_scl <= 1'b0;

                if(ack == 1'b1) begin

                    state <= IDLE;

                end

                else begin

                    state <= STOP_WR;

                end

            end

        end

        STOP_WR: begin

            if(cnt_i2c_clk == 2'b00) begin

                i2c_sda_reg <= 1'b0;

                i2c_scl     <= 1'b0;

            end

            else if(cnt_i2c_clk == 2'b01) begin

                i2c_sda_reg <= 1'b0;

                i2c_scl     <= 1'b1;

            end

            else if(cnt_i2c_clk == 2'b10) begin

                i2c_sda_reg <= 1'b1;

                i2c_scl     <= 1'b1;

            end

            else if(cnt_i2c_clk == 2'b11) begin

                state        <= START_RD;

            end

        end

        START_RD: begin

            if(cnt_i2c_clk == 2'b00) begin

                i2c_sda_reg <= 1'b1;

                i2c_scl     <= 1'b1;

            end

            else if(cnt_i2c_clk == 2'b01) begin

                i2c_sda_reg <= 1'b0;

            end

            else if(cnt_i2c_clk == 2'b10) begin

                i2c_scl     <= 1'b1;

            end

            else if(cnt_i2c_clk == 2'b11) begin

                i2c_scl     <= 1'b0;

                state       <= SEND_RD_ADDR;

            end

        end

        SEND_RD_ADDR: begin

            if(cnt_i2c_clk == 2'b00) begin

                if(cnt_bit <= 4'd6) begin

                    i2c_sda_reg <=  DEVICE_ADDR[6 - cnt_bit];

                end

                else begin

                    i2c_sda_reg <=  1'b1;

                end

                if(cnt_bit <= 4'd6) begin

                    cnt_bit <= cnt_bit + 1'b1;

                end

                else begin

                    cnt_bit    <= 4'd0;

                    cnt_finish <= 1'b1;

                end

            end

            else if(cnt_i2c_clk == 2'b01) begin

                i2c_scl <= 1'b1;

            end

            else if(cnt_i2c_clk == 2'b10) begin

                i2c_scl <= 1'b0;

            end

            else if(cnt_i2c_clk == 2'b11) begin

                if(cnt_finish == 1'b1) begin

                    state      <= ACK_RADDR;

                    cnt_finish <= 1'b0;

                end

            end

        end

        ACK_RADDR: begin

            if(cnt_i2c_clk == 2'b00) begin

                i2c_scl <= 1'b0;

            end

            else if(cnt_i2c_clk == 2'b01) begin

                i2c_scl <= 1'b1;

            end

            else if(cnt_i2c_clk == 2'b10) begin

                ack     <= sda_in;

            end

            else if(cnt_i2c_clk == 2'b11) begin

                i2c_scl <= 1'b0;

                if(ack == 1'b1) begin

                    state <= IDLE;

                end

                else begin

                    state <= RD_DATA;

                end

            end

        end

        RD_DATA: begin

            if(cnt_i2c_clk == 2'b00) begin

                i2c_scl <= 1'b0;

            end

            else if(cnt_i2c_clk == 2'b01) begin

                i2c_scl <= 1'b1;

            end

            else if(cnt_i2c_clk == 2'b10) begin

                rd_data = (rd_data << 1);

                rd_data = (rd_data | sda_in);

                cnt_bit <= (cnt_bit + 1'b1);

                i2c_scl <= 1'b0;

            end

            else if(cnt_i2c_clk == 2'b11) begin

                if(cnt_bit >= 4'd8) begin

                    cnt_bit     <= 4'd0;

                    rd_data_cnt <= rd_data_cnt + 2'd1;

                    state       <= ACK_MRD;

                end

            end

        end

        ACK_MRD: begin

            if(cnt_i2c_clk == 2'b00) begin

                // i2c_scl     <= 1'b0;

                i2c_sda_reg <= 1'b0;

            end

            else if(cnt_i2c_clk == 2'b01) begin

                i2c_scl <= 1'b1;

            end

            else if(cnt_i2c_clk == 2'b10) begin

                i2c_scl <= 1'b0;

            end

            else if(cnt_i2c_clk == 2'b11) begin

                if(rd_data_cnt === 3) begin

                    state <= STOP_RD;

                    rd_data_cnt <= 0;

                end

                else begin

                    state <= RD_DATA;

                end

            end

        end

        STOP_RD: begin

            if(cnt_i2c_clk == 2'b00) begin

                i2c_sda_reg <= 1'b0;

                i2c_scl     <= 1'b0;

            end

            else if(cnt_i2c_clk == 2'b01) begin

                i2c_sda_reg <= 1'b0;

                i2c_scl     <= 1'b1;

            end

            else if(cnt_i2c_clk == 2'b10) begin

                i2c_sda_reg <= 1'b1;

                i2c_scl     <= 1'b1;

            end

            else if(cnt_i2c_clk == 2'b11) begin

                state   <= IDLE;

                pi_flag <= 1'b1;

            end

        end

        default: begin

            i2c_sda_reg <= 1'b1;

            i2c_scl     <= 1'b1;

        end

    endcase

endmodule

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐