I2C .2

77 阅读3分钟

I2C驱动模块代码编写

module i2c_ctrl
#(
parameter SYS_CLK_FREQ = 'd50_000_000,
parameter SCL_FREQ = 'd250_000,
parameter DEVICE_ADDR = 7'd1010_011

)
//系统时钟 50mhz和 i2c_scl 250khz
(
input wire sys_clk,
input wire sys_rst_n,
input wire i2c_start,
input wire wr_en,
input wire [15:0] byte_addr,
input wire [7:0] wr_data,
input wire rd_en,
input wire addr_num,


output wire i2c_scl,
output wire i2c_sda,
output wire [7:0] rd_data,
output wire i2c_end,
output reg i2c_clk

);
parameter CNT_CLK_MAX = (SYS_CLK_FREQ/SCL_FREQ) >>3//将这个数值缩写四倍 获取 i2c_clk,右移三位,得到 i2c_clk的计数值,/6

parameter IDLE     =  4'd00
          START    =  4'd01
          SEND_D_A =  4'd02
          ACK_1  = 4'd03
          SEND_B_H = 4'd04
          ACK_2 =  4'd05
          SEND_B_L  = 4'd06
          ACK_3      =  4'd07
          WR_DATA =  4'd08
          ACK_4 =  4'd09
          START_2 =  4'd10
          SEND_R_A   =  4'd11              //发送读地址
          ACK_5 =  4'd12
          RD_DATA = 4'd13
          N_ACK = 4'd14
          STOP =  4'd15





reg [7:0] cnt_clk;
reg [3:0] state;
reg [1:0] cnt_i2c_clk;
reg     cnt_i2c_clk_en;
reg [3:0]cnt_bit;
reg sda_out;
wire sda_en;
reg ack;
wire sda_in;


// cnt_clk的生成
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk <= 8'b0;
    else if(cnt_clk == CNT_CLK_MAX - 1)
        cnt_clk <= 8'd0;
    else
        cnt_clk <= cnt_clk + 1;
     
 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)
        i2c_clk <= ~i2c_clk;
        
  
  //  i2c_clk为工作时钟    状态转移 
   always@(posedge  i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state <= IDLE;
    else case(state)
        
        IDLE:
            if(i2c_start == 1'b1)
                state<= START;
             else 
                 state <= state;
        START:
             if(cnt_i2c_clk == 2'd3)
                  state <= SEND_D_A;
              else
                  state <= state;
        SEND_D_A:
            if(cnt_bit == 3'd7 && cnt_i2c_clk == 2'd3)
            state <= ACK_1;
             else
                  state <= state;
       
        ACK_1:
            if((cnt_i2c_clk == 2'd3) &&(ack == 1'b0))
            begin
                if(addr_num == 1'b1)
                    state <= SEND_B_H;
                 else
                     state <= SEND_B_L;
            end
                state <= state;
            SEND_B_H:
                if(cnt_bit == 3'd7 && cnt_i2c_clk == 2'd3)
            state <= ACK_2;
             else
                  state <= state;
                    
            ACK_2:
                if((cnt_i2c_clk == 2'd3) &&(ack == 1'b0))
                state <= SEND_B_L;
                else
                  state <= state;
            SEND_B_L:
                  if(cnt_bit == 3'd7 && cnt_i2c_clk == 2'd3)
            state <= ACK_3;
             else
                  state <= state;
            ACK_3:
                if((cnt_i2c_clk == 2'd3) &&(ack == 1'b0))
               begin
                   if(wr_en == 1'b1)
                       state <= WR_DATA;
                    else if(rd_en == 1'b1)
                        state <= START_2;
                    else
                        state <= state;
                end
                 else
                     state<=state;
             WR_DATA:
                  if(cnt_bit == 3'd7 && cnt_i2c_clk == 2'd3)
            state <= ACK_4
             else
                  state <= state;

            ACK_4:
                  if((cnt_i2c_clk == 2'd3) &&(ack == 1'b0))
                state <= STOP;
                else
                  state <= state;
            START2:
                 if(cnt_i2c_clk == 2'd3)
                  state <= SEND_R_A;
              else
                  state <= state;
            SEND_R_A:
                  if(cnt_bit == 3'd7 && cnt_i2c_clk == 2'd3)
            state <= ACK_5
             else
                  state <= state;
             ACK_5:
                  if((cnt_i2c_clk == 2'd3) &&(ack == 1'b0))
                state <= RD_DATA;
                else
                  state <= state;
            RD_DATA:
                if(cnt_bit == 3'd7 && cnt_i2c_clk == 2'd3)
            state <= N_ACK;
             else
                  state <= state;
            N_ACK:
                 if((cnt_i2c_clk == 2'd3) &&(ack == 1'b0))
                state <= STOP;
                else
                  state <= state;
            STOP:
                  if(cnt_bit == 3'd3 && cnt_i2c_clk == 2'd3)
            state <= IDLE;
             else
                  state <= state;
            
            default:
                state <= IDLE;
     endcase
     
   // cnt_bit  的赋值
     
          
 always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_i2c_clk <= 2'd0;
    else if(cnt_i2c_clk_en == 1'b1)
        cnt_i2c_clk <= cnt_i2c_clk+ 1'b1;
  always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_i2c_clk_en <= 1'b0;
    else if(cnt_bit == 3'd3 && cnt_i2c_clk == 2'd3&& state ==STOP)
        cnt_i2c_clk_en <= 1'b0;
    else if(i2c_start == 1'b1)
       cnt_i2c_clk_en <= 1'b1;
  always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)  
       cnt_bit <= 3'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 <= 3'd0;
     else if(cnt_bit == 3'd7 && cnt_i2c_clk == 2'd3)
            cnt_bit <= 3'd0;
    else if(cnt_i2c_clk ==2'd3 && state != IDLE)
        cnt_bit <= cnt_bit + 1'b1;
            
            
  // 完成组合逻辑赋值  , 状态输出
    always@(*)
        case(state)
            IDLE:
                sda_out <= 1’b1;
            START:
                if(cnt_i2c_clk == 2'd0)
                    sda_out <=1'b1;
                else
                    sda_out <= 1'b0;
                SEND_D_A:
                    if(cnt_bit <= 3'd6)
                        sda_out <= DEVICE_ADDR[6-cnt_bit)
                       
            
            .....
            
            ......
  //    sda_en 的赋值
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;


// 组合逻辑  ACK的赋值

always@(*)
    case(state)
        ACK_1,ACK_2,ACK_3,ACK_4,ACK_5;
            if(cnt_i2c_clk == 2'd0)
                ack <= sda_in;
             else 
                 ack <= ack;
            default:
                ack <= 1'b1;
     endcase
     
   assign sda_en  = i2c_sda;
   
       
     always@(*)
         case(state)
             IDLE:
                 rd_data_reg <= 8'd0;
             RD_DATA:
                 rd_data_reg[7-cnt_bit] <= sda_in;
              default:
                  rd_data_reg <= rd_data_reg;
          endcase
          
     ........
     
     //赋值
     
  
                    
endmodule

image.png

i2c_rw_data:

跨时钟域处理:

sys_clk  i2c_clk:

工作时钟为 i2c_clk为1MHZ,此处我们采用低频信号去采集高频信号,出现采集不正确的情况,做跨时钟域处理。需要将读写使能信号 在系统时钟下  同步到 工作时钟i2c_clk信号下。     经过处理后可以正确采集到read和write信号。

跨时钟域处理:

多bit数据:用fifo,数据缓存,写入和读出采用不同时钟。

同频不同相数据: 采用打拍的方法。

我们这里时高频到低频的跨时钟域处理:

经过按键消抖write和read只有一个时钟周期的有效信号,我们可以延长数据有效周期,write_vaild,高电平持续时间大于2个时钟周期的 工作时钟i2c_clk。 也就是说我们这里高电平有效时间 大于100个时钟周期sys_clk, 我们这里取200个时钟周期, 所以我们只需要去采集到 write_vaild信号即可, 而读取信号同理,延长有效时钟周期即可。

i2c_rw_data模块的编写不做叙述了。