FPGA29(2) I2C驱动

FPGA29(2) I2C驱动_第1张图片

module i2c_dri
    #(// slave address(器件地址),放此处方便参数传递
      parameter   SLAVE_ADDR =  7'b1010000   ,
      parameter   CLK_FREQ   = 26'd50_000_000,   // i2c_dri模块的驱动时钟频率(CLK_FREQ)
      parameter   I2C_FREQ   = 18'd250_000       // I2C的SCL时钟频率
     )(
          //global clock
          input                clk        ,      // i2c_dri模块的驱动时钟(CLK_FREQ)
          input                rst_n      ,      // 复位信号

          //i2c interface
          input                i2c_exec   ,      // 我想启动IIC吗
          input                bit_ctrl   ,      // IIC从设备内部地址是8位还是16位呢
          input                i2c_rh_wl  ,      // 我想从IIC从器件中读数据还是向里写数据
          input        [15:0]  i2c_addr   ,      // I2C从器件内部地址
          input        [ 7:0]  i2c_data_w ,      // 如果我要向IIC从器件内部写数据的话我要写什么数据呢
          output  reg  [ 7:0]  i2c_data_r ,      // 如果我要从IIC从器件内部读出数据的话我要把它输出出来
          output  reg          i2c_done   ,      // I2C一次操作完成的标志信号
          output  reg          scl        ,      // SCL线
          inout                sda        ,      // SDA线

          //user interface
          output  reg          dri_clk           // 驱动I2C操作的驱动时钟,也可供外部设备只用
     );




//#############################  SDA控制  ##################################
reg            sda_dir     ;                     // (SDA)方向控制读出还是写入
reg            sda_out     ;                     // SDA输出信号
wire           sda_in       ;                     // SDA输入信号

assign  sda     = sda_dir ?  sda_out : 1'bz;     // 如果是输出,引脚sda输出数据就跟随sda_out寄存器的数据,如果是输入就拉为高阻
assign  sda_in  = sda ;                          // sda_in寄存器就要从sda引脚上取得数据
//#########################################################################





//####################  产生驱动时钟250k × 4 = 1M   ######################
wire   [8:0]  clk_divide   ;                     // 模块驱动时钟的分频系数
reg    [ 9:0]  clk_cnt     ;                     // 分频时钟计数

assign  clk_divide = (CLK_FREQ/I2C_FREQ) >> 2'd3;// 模块驱动时钟的分频系数

always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0) begin
        dri_clk <=  1'b1;
        clk_cnt <= 10'd0;
    end
    else if(clk_cnt == clk_divide - 1'd1) begin
        clk_cnt <= 10'd0;
        dri_clk <= ~dri_clk;
    end
    else
        clk_cnt <= clk_cnt + 1'b1;
end
//#########################################################################





//##########################  三段状态机  #################################
localparam  st_idle     = 8'b0000_0001;          // 空闲状态        并判断IIC是否触发
localparam  st_sladdr   = 8'b0000_0010;          // 发送器件地址    并判断要向从器件内部发送的地址是16位还是8位
localparam  st_addr16   = 8'b0000_0100;          // 发送高8位字地址 并进入发送低八位状态,如果st_sladdr状态判断的是8位地址 那么就只发送低八位
localparam  st_addr8    = 8'b0000_1000;          // 发送低8位字地址 并判断我接受到的指令是向从器件写入还是读出

localparam  st_data_wr  = 8'b0001_0000;          // 写数据(8 bit)   写完之后就进入st_stop
localparam  st_addr_rd  = 8'b0010_0000;          // 发送器件地址读  根据协议,读之前要再次发送IIC从设备器件地址
localparam  st_data_rd  = 8'b0100_0000;          // 读数据(8 bit)   读完之后就进入stop
localparam  st_stop     = 8'b1000_0000;          // 结束I2C操作

reg    [ 7:0]  cur_state   ;                     // 状态机当前状态
reg    [ 7:0]  next_state  ;                     // 状态机下一状态
reg            st_done     ;                     // 状态结束
reg            wr_flag     ;                     // 读写标志
reg    [15:0]  addr_t      ;                     // 地址
reg    [ 7:0]  data_r      ;                     // 读取的数据
reg    [ 7:0]  data_wr_t   ;                     // I2C需写的数据的临时寄存
reg    [ 6:0]  cnt         ;                     // 计数

//(三段式状态机)同步时序描述状态转移
always @(posedge dri_clk or negedge rst_n) begin
    if(rst_n == 1'b0)
        cur_state <= st_idle;
    else
        cur_state <= next_state;
end

//组合逻辑判断状态转移条件
always @( * ) begin
//    next_state = st_idle;
    case(cur_state)

        st_idle: begin                        
           if(i2c_exec) begin         
               next_state = st_sladdr;
           end
           else
               next_state = st_idle;
        end
	
        st_sladdr: begin
            if(st_done) begin         
                if(bit_ctrl)                     
                   next_state = st_addr16;
                else
                   next_state = st_addr8 ;
            end
            else
                next_state = st_sladdr;
        end
		

        st_addr16: begin                          
            if(st_done) begin
                next_state = st_addr8;
            end
            else begin
                next_state = st_addr16;
            end
        end
		

        st_addr8: begin                           
            if(st_done) begin
                if(wr_flag==1'b0)                 
                    next_state = st_data_wr;
                else
                    next_state = st_addr_rd;
            end
            else begin
                next_state = st_addr8;
            end
        end
		
        st_data_wr: begin                        
            if(st_done)
                next_state = st_stop;
            else
                next_state = st_data_wr;
        end
        st_addr_rd: begin                         
            if(st_done) begin
                next_state = st_data_rd;
            end
            else begin
                next_state = st_addr_rd;
            end
        end
        st_data_rd: begin                        
            if(st_done)
                next_state = st_stop;
            else
                next_state = st_data_rd;
        end
        st_stop: begin                          
            if(st_done)
                next_state = st_idle;
            else
                next_state = st_stop ;
        end
        default: next_state= st_idle;
    endcase
end

//时序电路描述状态输出

always @(posedge dri_clk or negedge rst_n) begin
    //复位初始化
    if(rst_n == 1'b0) begin
        scl        <= 1'b1;
        sda_out    <= 1'b1;    //根据协议,IIC空闲状态两条线均为高
        sda_dir    <= 1'b1;    //初始化SDA接口方向为写
        i2c_done   <= 1'b0;
        cnt        <= 1'b0;
        st_done    <= 1'b0;
        data_r     <= 1'b0;
        i2c_data_r <= 1'b0;
        wr_flag    <= 1'b0;
        addr_t     <= 1'b0;
        data_wr_t  <= 1'b0;  //其余信号均0
    end
    else begin
        st_done <= 1'b0 ;
        cnt     <= cnt +1'b1 ;
        case(cur_state)
             st_idle: begin
				// 空闲状态			 
                scl     <= 1'b1;
                sda_out <= 1'b1;       //根据协议,IIC空闲状态两条线均为高
                sda_dir <= 1'b1;       //空闲时SDA接口方向为写
                i2c_done<= 1'b0;
                cnt     <= 7'b0;
                if(i2c_exec) begin
                    wr_flag   <= i2c_rh_wl ;  //外部输入读写信号  经过寄存  赋值给wr_flag
                    addr_t    <= i2c_addr  ;  //IIC器件内部地址 由外部输入设定  经过寄存  赋值给addr_t
                    data_wr_t <= i2c_data_w;  //外部输入向器件写的数据 经过寄存  赋值给data_wr_t
                end
            end
            st_sladdr: begin 
                //在判断从器件内部的地址是16位还是8位之前,先把从器件地址发送过去		
                case(cnt) 
                    7'd1 : sda_out <= 1'b0;             //在scl高电平的情况下,把原本高电平的sda拉低产生一个下降沿  作为起始信号
					                                    //SCL:高(闲)--- 拉低->发数据->拉高 *8  拉低—>接收应答—>拉高  ---完成
					//scl高电平时候数据稳定
                    7'd3 : scl <= 1'b0;
                    7'd4 : sda_out <= SLAVE_ADDR[6];    
                    7'd5 : scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd7 : scl <= 1'b0;
                    7'd8 : sda_out <= SLAVE_ADDR[5];
                    7'd9 : scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd11: scl <= 1'b0;
                    7'd12: sda_out <= SLAVE_ADDR[4];
                    7'd13: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd15: scl <= 1'b0;
                    7'd16: sda_out <= SLAVE_ADDR[3];
                    7'd17: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd19: scl <= 1'b0;
                    7'd20: sda_out <= SLAVE_ADDR[2];
                    7'd21: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd23: scl <= 1'b0;
                    7'd24: sda_out <= SLAVE_ADDR[1];
                    7'd25: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd27: scl <= 1'b0;
                    7'd28: sda_out <= SLAVE_ADDR[0];
                    7'd29: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd31: scl <= 1'b0;
                    7'd32: sda_out <= 1'b0;      // 0:写
                    7'd33: scl <= 1'b1;
					//scl高电平时候数据稳定
                    
					
					//
					7'd35: scl <= 1'b0;
                    7'd36: begin
                        sda_dir <= 1'b0;         // sda线路设为高阻态准备接收从机应答
                        sda_out <= 1'b1;
                    end
                    7'd37: scl     <= 1'b1;
                    7'd38: st_done <= 1'b1;      //操作完成 给个标志
                    //
					
					
					7'd39: begin
                        scl <= 1'b0;             //最后把scl拉低为以后的收发数据做准备
                        cnt <= 1'b0;
                    end
                    default :  ;
                endcase
            end
            st_addr16: begin
                case(cnt)
                    7'd0 : begin
                        sda_dir <= 1'b1 ;                // sda线路设为向外输出状态
                        sda_out <= addr_t[15];           
                    end
                    7'd1 : scl <= 1'b1;
					//scl高电平时候数据稳定
					
                    7'd3 : scl <= 1'b0;
                    7'd4 : sda_out <= addr_t[14];
                    7'd5 : scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd7 : scl <= 1'b0;
                    7'd8 : sda_out <= addr_t[13];
                    7'd9 : scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd11: scl <= 1'b0;
                    7'd12: sda_out <= addr_t[12];
                    7'd13: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd15: scl <= 1'b0;
                    7'd16: sda_out <= addr_t[11];
                    7'd17: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd19: scl <= 1'b0;
                    7'd20: sda_out <= addr_t[10];
                    7'd21: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd23: scl <= 1'b0;
                    7'd24: sda_out <= addr_t[9];
                    7'd25: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd27: scl <= 1'b0;
                    7'd28: sda_out <= addr_t[8];
                    7'd29: scl <= 1'b1;
					//scl高电平时候数据稳定
                   
				   
				    //
				    7'd31: scl <= 1'b0;
                    7'd32: begin
                        sda_dir <= 1'b0;               // sda线路设为高阻态准备接收从机应答
                        sda_out <= 1'b1;
                    end
                    7'd33: scl     <= 1'b1;
					//
					
					
                    7'd34: st_done <= 1'b1;
                    7'd35: begin
                        scl <= 1'b0;                   //最后把scl拉低为以后的收发数据做准备
                        cnt <= 1'b0;
                    end
                    default :  ;
                endcase
            end
            st_addr8: begin
                case(cnt)
                    7'd0: begin
                       sda_dir <= 1'b1 ;                // sda线路设为向外输出状态
                       sda_out <= addr_t[7];            
                    end
                    7'd1 : scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd3 : scl <= 1'b0;
                    7'd4 : sda_out <= addr_t[6];
                    7'd5 : scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd7 : scl <= 1'b0;
                    7'd8 : sda_out <= addr_t[5];
                    7'd9 : scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd11: scl <= 1'b0;
                    7'd12: sda_out <= addr_t[4];
                    7'd13: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd15: scl <= 1'b0;
                    7'd16: sda_out <= addr_t[3];
                    7'd17: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd19: scl <= 1'b0;
                    7'd20: sda_out <= addr_t[2];
                    7'd21: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd23: scl <= 1'b0;
                    7'd24: sda_out <= addr_t[1];
                    7'd25: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd27: scl <= 1'b0;
                    7'd28: sda_out <= addr_t[0];
                    7'd29: scl <= 1'b1;
					//scl高电平时候数据稳定
                   
				    
					//
					7'd31: scl <= 1'b0;
                    7'd32: begin
                        sda_dir <= 1'b0;                // sda线路设为高阻态准备接收从机应答
                        sda_out <= 1'b1;
                    end
                    7'd33: scl <= 1'b1;
					// 
					
					
                    7'd34: st_done <= 1'b1;             
                    7'd35: begin
                        scl <= 1'b0;                    //最后把scl拉低为以后的收发数据做准备
                        cnt <= 1'b0;
                    end
                    default :  ;
                endcase
            end
            st_data_wr: begin                         
                case(cnt)
                    7'd0: begin
                        sda_dir <= 1'b1;                // sda线路设为向外输出状态
						sda_out <= data_wr_t[7];        
                    end
                    7'd1 : scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd3 : scl <= 1'b0;
                    7'd4 : sda_out <= data_wr_t[6];
                    7'd5 : scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd7 : scl <= 1'b0;
                    7'd8 : sda_out <= data_wr_t[5];
                    7'd9 : scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd11: scl <= 1'b0;
                    7'd12: sda_out <= data_wr_t[4];
                    7'd13: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd15: scl <= 1'b0;
                    7'd16: sda_out <= data_wr_t[3];
                    7'd17: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd19: scl <= 1'b0;
                    7'd20: sda_out <= data_wr_t[2];
                    7'd21: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd23: scl <= 1'b0;
                    7'd24: sda_out <= data_wr_t[1];
                    7'd25: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd27: scl <= 1'b0;
                    7'd28: sda_out <= data_wr_t[0];
                    7'd29: scl <= 1'b1;
					//scl高电平时候数据稳定
					
					
					//
                    7'd31: scl <= 1'b0;
                    7'd32: begin
                        sda_dir <= 1'b0;               //sda线路设为高阻态准备接收从机应答
                        sda_out <= 1'b1;
                    end
                    7'd33: scl <= 1'b1;
					//
					
					
                    7'd34: st_done <= 1'b1;
                    7'd35: begin
                        scl  <= 1'b0;                   //最后把scl拉低为以后的收发数据做准备
                        cnt  <= 1'b0;
                    end
                    default  :  ;
                endcase
            end
            st_addr_rd: begin                          
                case(cnt)
                    7'd0 : begin
                        sda_dir <= 1'b1;                // sda线路设为向外输出状态
                        sda_out <= 1'b1;
                    end
                    7'd1 : scl <= 1'b1;
                    7'd2 : sda_out <= 1'b0;             //在scl高电平的情况下,把原本高电平的sda拉低产生一个下降沿  作为起始信号
					                                    //SCL:高(闲)--- 拉低->发数据->拉高 *8  拉低—>接收应答—>拉高  ---完成
                    7'd3 : scl <= 1'b0;
                    7'd4 : sda_out <= SLAVE_ADDR[6];    //传送器件地址
                    7'd5 : scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd7 : scl <= 1'b0;
                    7'd8 : sda_out <= SLAVE_ADDR[5];
                    7'd9 : scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd11: scl <= 1'b0;
                    7'd12: sda_out <= SLAVE_ADDR[4];
                    7'd13: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd15: scl <= 1'b0;
                    7'd16: sda_out <= SLAVE_ADDR[3];
                    7'd17: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd19: scl <= 1'b0;
                    7'd20: sda_out <= SLAVE_ADDR[2];
                    7'd21: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd23: scl <= 1'b0;
                    7'd24: sda_out <= SLAVE_ADDR[1];
                    7'd25: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd27: scl <= 1'b0;
                    7'd28: sda_out <= SLAVE_ADDR[0];
                    7'd29: scl <= 1'b1;
					//scl高电平时候数据稳定
                    7'd31: scl <= 1'b0;
                    7'd32: sda_out <= 1'b1;             // 1:读
                    7'd33: scl <= 1'b1;
					//scl高电平时候数据稳定
                    
					
					//
					7'd35: scl <= 1'b0;
					7'd36: begin
                        sda_dir <= 1'b0;                //sda线路设为高阻态准备接收从机应答
                        sda_out <= 1'b1;
                    end
                    7'd37: scl     <= 1'b1;
					//
					
					
                    7'd38: st_done <= 1'b1;
                    7'd39: begin
                        scl <= 1'b0;                    //最后把scl拉低为以后的收发数据做准备
                        cnt <= 1'b0;
                    end
                    default : ;
                endcase
            end
            st_data_rd: begin                          
                case(cnt)
                    7'd0: sda_dir <= 1'b0;             //sda线路设为向内读入状态
                    7'd1: begin
                        data_r[7] <= sda_in;
                        scl       <= 1'b1;
						//scl高电平时候数据稳定
                    end
                    7'd3: scl  <= 1'b0;
                    7'd5: begin
                        data_r[6] <= sda_in ;
                        scl       <= 1'b1   ;
						//scl高电平时候数据稳定
                    end
                    7'd7: scl  <= 1'b0;
                    7'd9: begin
                        data_r[5] <= sda_in;
                        scl       <= 1'b1  ;
						//scl高电平时候数据稳定
                    end
                    7'd11: scl  <= 1'b0;
                    7'd13: begin
                        data_r[4] <= sda_in;
                        scl       <= 1'b1  ;
						//scl高电平时候数据稳定
                    end
                    7'd15: scl  <= 1'b0;
                    7'd17: begin
                        data_r[3] <= sda_in;
                        scl       <= 1'b1  ;
						//scl高电平时候数据稳定
                    end
                    7'd19: scl  <= 1'b0;
                    7'd21: begin
                        data_r[2] <= sda_in;
                        scl       <= 1'b1  ;
						//scl高电平时候数据稳定
                    end
                    7'd23: scl  <= 1'b0;
                    7'd25: begin
                        data_r[1] <= sda_in;
                        scl       <= 1'b1  ;
						//scl高电平时候数据稳定
                    end
                    7'd27: scl  <= 1'b0;
                    7'd29: begin
                        data_r[0] <= sda_in;
                        scl       <= 1'b1  ;
						//scl高电平时候数据稳定
                    end
					
					
					//
                    7'd31: scl  <= 1'b0;
                    7'd32: begin
                        sda_dir <= 1'b1;              //sda线路设为输出 准备让主机发送非应答
                        sda_out <= 1'b1;
                    end
                    7'd33: scl     <= 1'b1;
					//
					
					
                    7'd34: st_done <= 1'b1;
                    7'd35: begin
                        scl <= 1'b0;                  //最后把scl拉低为以后的收发数据做准备
                        cnt <= 1'b0;
                        i2c_data_r <= data_r;         //把读完放到寄存器data_r中的数据,传出来。
                    end
                    default  :  ;
                endcase
            end
            st_stop: begin                          
                case(cnt)
                    7'd0: begin
                        sda_dir <= 1'b1;              // sda线路设为向外输出状态
                        sda_out <= 1'b0;              //把sda上的数据写低
                    end
                    7'd1 : scl     <= 1'b1;           //把scl抬高
                    7'd3 : sda_out <= 1'b1;           //在scl为高电平的状态的时候,把sda抬高,从而产生上升沿作为结束位。
                    7'd15: st_done <= 1'b1;
                    7'd16: begin
                        cnt      <= 1'b0;
                        i2c_done <= 1'b1;             //向上层模块传递I2C结束信号!一次IIC读写结束。。。
                    end
                    default  : ;
                endcase
            end
        endcase
    end
end
//###################################################################################
endmodule

FPGA29(2) I2C驱动_第2张图片
FPGA29(2) I2C驱动_第3张图片

你可能感兴趣的:(FPGA从硬件描述到删核跑路)