FPGA IIC连续读写操作(Verilog)

I 说明

本程式对IIC协议模块:链接,进行连续读写测试。
测试对象为AP3216C传感器,通过对其进行连续的写数据,读数据。从而完成对该传感器的初始化与使用。
学习的重点应为使用多路器+状态值 的方法,借助IIC协议模块“iic_done”信号的反馈,来完成连续读写。

II AP3216C

AP3216C是一款环境光传感器,本程式将通过IIC协议读出此传感器的两个值:
16bit的【als_data】(ALS数据的[7:0]储存在0C地址的[7:0],ALS数据的[15:8]储存在0D地址的[7:0]) 与
10bit的【ps_data】(PS数据的[3:0]储存在0E地址的[3:0],PS数据的[9:4]储存在0F地址的[5:0])
读取这两种数据之前都需要一定的【准备时间】
该传感器需要向 [00H] 地址 写入111启用ALS+PS+IR的单次模式(初始化)

III 程式说明

在这里插入图片描述
【AP3216C模块】控制【IIC协议模块】对AP3216C进行读写控制,读出两组数据"[15:0]als_data" 与 “[9:0]ps_data”
【数码管显示模块】用来驱动数码管的显示,【按键模块】将输入的按键转化为状态值,当按键一次按下,数码管显示als_data数据,当按键再次按下,数码管显示ps_data数据。

IV 模块分解

1顶层模块

module ap3216c_top(
    //global clock
    input                sys_clk    ,       
    input                sys_rst_n  ,       
    
    //按键输出(触摸按键,无去抖过程)  
    input touch_key,
    
    //连接IIC从器件ap3216c
    output               scl     ,       
    inout                sda     ,       

    //连接六位八段数码管
    output        [5:0]  sel     ,      
    output        [7:0]  seg             
);

//...
wire           clk       ;                   // 1M时钟“dri_clk”引出

//...
wire           i2c_exec  ;                   // iic使能
wire   [15:0]  iic_inner_reg_addr  ;         // i2c从器件内部寄存器地址
wire           i2c_done  ;                   // i2c操作结束标志
wire           read1_write0 ;                // i2c读写控制
wire   [ 7:0]  i2c_data_r;                   // i2c读出数据
wire   [ 7:0]  i2c_data_w;                   // i2c写入数据

//...
wire   [15:0]  als_data  ;                   // ALS的数据
wire   [ 9:0]  ps_data   ;                   // PS的数据


i2c_dri(

          //fpga
          .clk(sys_clk)        ,      // 输入时钟(clk_freq)
          .rst_n(sys_rst_n)      ,    // 复位信号
          
          //addr and data
          .slave_address(7'h1e),                   //从机地址
          .iic_inner_reg_addr(iic_inner_reg_addr), //从机内部寄存器地址,若为8bit形式,直接用低八位
          .i2c_w_data(i2c_data_w),                 //写数据端口
          .i2c_r_data(i2c_data_r),                 //读数据端口
          
          //select mode
          .type16_type8(1'b0),               //从机内部寄存器地址是否属于16bit形式(16-1/8-0)
          .read1_write0(read1_write0),       //读模式或写模式
          
          //i2c interface
          .i2c_exec(i2c_exec)   ,  // IIC使能【1】
          .i2c_done(i2c_done)   ,  // 一次操作完成的标志信号
          .scl(scl)        ,       // SCL输出
          .sda(sda)        ,       // SDA输出

          //user interface
          .dri_clk(clk)           // 低频时钟输出
     );
     

ap3216c u_ap3216c(

    .clk         (clk       ),          
    .rst_n       (sys_rst_n ),             

    //iic ctrl
    .read1_write0   (read1_write0 ),           
    .i2c_exec    (i2c_exec  ),           
    .iic_inner_reg_addr    (iic_inner_reg_addr  ),           
    .i2c_data_w  (i2c_data_w),           
    .i2c_data_r  (i2c_data_r),           
    .i2c_done    (i2c_done  ),              

    //data to dig_tube
    .als_data    (als_data  ),              
    .ps_data     (ps_data   )               
);


touch_key f0(
              .in(touch_key),
              .clk(sys_clk),
              .rst_n(sys_rst_n),
              .outstate(shift)
              );

 wire shift;
 wire [15:0] data_out;
 assign  data_out = shift ? ps_data : als_data;
 
 
 dig8_6 f1(
  .clk(sys_clk),
  .rst(sys_rst_n),

  .set_data(data_out),//6xh   h=4

  .dig(sel), //六位独热码表示六个数码管
  .dict(seg)
);


endmodule

2AP3216C模块(控制连续读写)

/*
    数字环境光传感器:ALS
    距离传感器:PS
    红外LED:IR
    
    [内部寄存器地址] (有效位) 含义
    [00H] (2:0) 写入111代表启用ALS+PS+IR的单次模式
    [0AH] (7)   读出0代表IR&PS的数据有效 1无效
    [0AH] (1:0) 读出IR有效数据位[1:0]
    [0BH] (7:0) 读出IR有效数据位[7:0]
    [0CH] (7:0) 读出ALS有效数据位[7:0]
    [0DH] (7:0) 读出ALS有效数据位[15:8]
    [0EH] (7)   读出0代表物体远离 读出1代表物体靠近
    [0EH] (6)   读出0代表PS&IR有效 1代表无效
    [0EH] (3:0) 读出PS有效数据位[3:0]
    [0FH] (7)   读出0代表物体远离 读出1代表物体靠近
    [0FH] (6)   读出0代表PS&IR有效 1代表无效
    [0FH] (5:0) 读出PS有效数据位[5:0]
    。
    本程式将PS数据与ALS数据读出,
    PS为10位数据:PS数据的[3:0]储存在0E地址的[3:0],PS数据的[9:4]储存在0F地址的[5:0]
    ALS为16位数据:ALS数据的[7:0]储存在0C地址的[7:0],ALS数据的[15:8]储存在0D地址的[7:0]
    
    
    操作过程:初始化(0~2) -> 读PS(3~8) -> 读ALS(9~14) -> 读PS(3~8)...
*/
module ap3216c(
    
    input                 clk        ,    
    input                 rst_n      ,    

    
    output   reg          read1_write0  ,           // IIC读写控制
    output   reg          i2c_exec   ,              // IIC使能
    output   reg  [15:0]  iic_inner_reg_addr   ,    // AP3216C内部寄存器地址
    output   reg  [ 7:0]  i2c_data_w ,              // I2C写8bit数据
    input         [ 7:0]  i2c_data_r ,              // I2C读8bit数据
    input                 i2c_done   ,              // IIC一次操作完成标志

    
    output   reg  [15:0]  als_data   ,    // ALS的数据
    output   reg  [ 9:0]  ps_data         // PS的数据
);

//parameter define
parameter      TIME_PS   = 14'd12_500  ;  // PS转换时间为12.5ms(clk = 1MHz)
parameter      TIME_ALS  = 17'd100_000 ;  // ALS转换时间为100ms(clk = 1MHz)
parameter      TIME_REST =  8'd2       ;  // 停止后重新开始的时间间隔控制

//reg define
reg   [ 3:0]   flow_cnt   ;               // 状态流控制
reg   [18:0]   wait_cnt   ;               // 计数等待
reg   [15:0]   als_data_t ;               // ALS的临时数据
reg            als_done   ;               // 环境光照强度值采集完成信号
reg   [ 9:0]   ps_data_t  ;               // PS的临时数据





//配置AP3216C并读取数据
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        i2c_exec   <=  1'b0;
        iic_inner_reg_addr   <=  8'd0;
        read1_write0  <=  1'b0;
        i2c_data_w <=  8'h0;
        
        flow_cnt   <=  4'd0;
        wait_cnt   <= 18'd0;
        
        ps_data    <= 10'd0;
        ps_data_t  <= 10'd0;
        als_data_t <= 16'd0;
        als_done   <=  1'b0;
        
        
        
    end
    else begin
        i2c_exec <= 1'b0;
        case(flow_cnt)
        
            //【0:等待0.01ms】 ->1
            4'd0: begin
                if(wait_cnt == 18'd100) begin 
                    wait_cnt <= 18'd0;
                    flow_cnt <= flow_cnt + 1'b1;
                end
                else
                    wait_cnt <= wait_cnt +1'b1;
            end
            
            //【1、向00H写入03H,启用ALS+PS+IR的单次模式】->2
            4'd1: begin
                i2c_exec   <= 1'b1 ;
                read1_write0  <= 1'b0 ;
                iic_inner_reg_addr   <= 8'h00;               
                i2c_data_w <= 8'h03;               
                flow_cnt   <= flow_cnt + 1'b1;
            end
            
            //【2、等待IIC发送完成】->3
            4'd2: begin
                if(i2c_done)
                    flow_cnt <= flow_cnt + 1'b1;
            end
            
            //【3、等待12.5ms,因即将读取PS,手册要求读取PS前准备12.5ms】->4
            4'd3: begin
                if(wait_cnt  == TIME_PS) begin
                    
                    als_done         <= 1'b0; //清除als_done标志
                    
                    wait_cnt <= 18'd0;
                    flow_cnt <= flow_cnt + 1'd1;
                end
                else
                    wait_cnt <= wait_cnt + 1'b1;
            end
            
            /*【4、对地址0EH做一次读操作】 ->5
                [0EH] (3:0) 读出PS有效数据位[3:0]
            */
            4'd4: begin
                i2c_exec <= 1'b1;
                read1_write0<= 1'b1;
                iic_inner_reg_addr <= 8'h0E;
                flow_cnt <= flow_cnt + 1'b1;
            end
            
            //【5、等待IIC的读操作完成,并处理数据】->6
            4'd5: begin
                if(i2c_done) begin
                    flow_cnt         <= flow_cnt + 1'b1;
                    ps_data_t[3:0]   <= i2c_data_r[3:0];;
                end
            end
            
            //【6、等待一段时间以进行下一次读写】 ->7
            4'd6: begin
                if(wait_cnt == TIME_REST) begin//TIME_REST
                    wait_cnt <= 18'd0;
                    flow_cnt <= flow_cnt + 1'b1;
                end
                else
                    wait_cnt <= wait_cnt +1'b1;
            end
            
            /*【7、对地址0FH做一次读操作】 ->8
                [0FH] (5:0) 读出PS有效数据位[3:0]
            */
            4'd7: begin
                i2c_exec <= 1'b1;
                read1_write0<= 1'b1;
                iic_inner_reg_addr <= 8'h0F;
                flow_cnt <= flow_cnt + 1'b1;
            end
            
            //【8、等待IIC的读操作完成,并处理数据】->9
            4'd8: begin
                if(i2c_done) begin
                    flow_cnt        <= flow_cnt + 1'b1;
                    ps_data_t[9:4] <= i2c_data_r[5:0];
                end
            end
            //【9、等待100ms,因即将读取ALS,手册要求读取ALS前准备100ms,同时将接收到的PS整理】->10
            4'd9: begin
                if(wait_cnt  ==  TIME_ALS) begin
                    wait_cnt <= 18'd0;
                    flow_cnt <= flow_cnt + 1'd1;
                     ps_data  <= ps_data_t;
                end
                else
                    wait_cnt <= wait_cnt + 1'b1;
            end
            
            /*【10、对地址0CH做一次读操作】 ->11
                [0CH] (7:0) 读出ALS有效数据位[7:0]
            */
            4'd10: begin
                i2c_exec <= 1'b1;
                read1_write0<= 1'b1;
                iic_inner_reg_addr <= 8'h0C;
                flow_cnt <= flow_cnt + 1'b1;
            end
            
            //【11、等待IIC的读操作完成,并处理数据】->12
            4'd11: begin
                if(i2c_done) begin
                    als_data_t[7:0] <= i2c_data_r;
                    flow_cnt        <= flow_cnt + 1'b1;
                end
            end
            
            //【12、等待一段时间以进行下一次读写】 ->13
            4'd12: begin
                if(wait_cnt == TIME_REST) begin
                    wait_cnt <= 18'd0;
                    flow_cnt <= flow_cnt + 1'b1;
                end
                else
                    wait_cnt <= wait_cnt +1'b1;
            end
            
            /*【13、对地址0DH做一次读操作】 ->14
                [0DH] (7:0) 读出ALS有效数据位[15:8]
            */
            4'd13: begin
                i2c_exec <= 1'b1;
                read1_write0<= 1'b1;
                iic_inner_reg_addr <= 8'h0D;
                flow_cnt <= flow_cnt + 1'b1;
            end
            
            //【14、等待IIC的读操作完成,并处理数据,输出als_done标志,提供给用于(单位转换)的always块使用】 ->3
            4'd14: begin
                if(i2c_done) begin
                    als_done         <= 1'b1;
                    als_data_t[15:8] <= i2c_data_r;
                    flow_cnt         <= 4'd3;             
                end 
            end
        endcase
    end 
end

//当采集的环境光转换成光照强度(单位:lux)
always @ (*) begin
    if(als_done)
	 als_data = als_data_t * 6'd35 / 7'd100;
end

endmodule

3按键转状态模块

/*
    触摸按键:当按键按下时outstate输出1,再次按下时outstate输出0. 循环切换。 
*/

module touch_key (in,clk,rst_n,outstate);

        input in;            //按键输入(物理)
		input clk;
		input rst_n;

        output reg outstate; //按键按下状态量输出

    //同步
    reg temp1,temp2;
	always@(posedge clk or negedge rst_n)
			if(!rst_n)
			begin
				temp1 <= 1'b0;
				temp2 <= 1'b0;					
			end
			
			else
			begin
				temp1 <= in ;
				temp2 <= temp1;
			end

    //上升沿检测
    reg temp3,temp4;
	always@(posedge clk or negedge rst_n)
			if(!rst_n)
			begin
				temp3 <= 1'b0;
				temp4 <= 1'b0;					
			end
			
			else
			begin
				temp3 <= temp2 ;
				temp4 <= temp3;
			end

    wire up;  
	assign up = temp3 & (!temp4);

    
    //状态切换
    reg switch;
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            outstate <= 1'b0;
        else if(up == 1'b1)
            outstate <= ~outstate;
        else
            outstate <= outstate;
    end

endmodule

4数码管显示模块

链接

5iic协议模块

链接

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搞IC的那些年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值