环境光传感器

1. AP3216C简介

AP3216C是一款三合一环境传感器,它内部集成了:数字环境光传感器(Ambilent Light Aensors,ALS)、距离传感器(Proximity Sensor,PS)和一个红外LED(Infrared Radiation LED,IR LED),该芯片通过IIC接口和FOGA连接。

1.1 AP3216C特点

IIC接口,支持高达400KHz通信速率;
支持多种工作模式(ALS、PS+IR、ALS+PS+IR等);
内置温度补偿电路;
工作温度支持-30~80℃;
环境光传感器具有16位分辨率;
接近传感器具有10位分辨率;
红外传感器具有10位分辨率;
超小封装(4.12.41.35mm)。

1.2 AP3216C框图

在这里插入图片描述

1.3 AP3216C寄存器描述

AP3216C有一系列寄存器,由这些寄存器来控制AP3216C工作模式,以及中断配置和数据传输等。
在这里插入图片描述

1.4 AP3216C写寄存器

在这里插入图片描述
图中,先发送AP3216C的地址(7’h1E),最低位W=0表示写数据,随后发送8位寄存器地址,最后发送8位寄存器值。其中:S,表示IIC起始信号;W,表示读/写标志位(W=0表示写,W=1表示读);A,表示应答信号;P,表示IIC停止信号。

1.5 AP3216C读寄存器

在这里插入图片描述
图中,同样是先发送7位地址+写操作,然后再发送寄存器地址,随后,重新发送起始信号(Sr),再次发送7位地址+读操作,然后读取寄存器值。其中:Sr,表示重新发送IIC起始信号;N,表示不对AP3216C进行应答;其他简写同上。

2. 程序设计

使用FOGA开发板上AP3216C器件测量环境光强度和物体距离,并在数码管上显示环境光强度,用4个led灯的亮灭来只是物体距离的远近。
系统框图
在这里插入图片描述
IIC驱动参考:EEPROM读写–IIC协议
其中LED显示距离远近,距离越近显示的led灯数目越多。
在这里插入图片描述

代码

module ap3216c_top(
    //global clock
    input                sys_clk    ,        // 系统时钟
    input                sys_rst_n  ,        // 系统复位

    //ap3216c interface
	output               i2c_ack    ,        // I2C应答标志 0:应答 1:未应答
    output               ap_scl     ,        // i2c时钟线
    inout                ap_sda     ,        // i2c数据线

    //user interface
    output        [3:0]  led        ,        // led灯接口
    output        [5:0]  sel        ,        // 数码管位选
    output        [7:0]  seg_led             // 数码管段选
);

//parameter define
parameter      SLAVE_ADDR =  7'h1e        ;  // 器件地址
parameter      BIT_CTRL   =  1'b0         ;  // 字地址位控制参数(16b/8b)
parameter      CLK_FREQ   = 26'd50_000_000;  // i2c_dri模块的驱动时钟频率(CLK_FREQ)
parameter      I2C_FREQ   = 18'd250_000   ;  // I2C的SCL时钟频率

//wire define
wire           clk       ;                   // I2C操作时钟
wire           i2c_exec  ;                   // i2c触发控制
wire   [15:0]  i2c_addr  ;                   // i2c操作地址
wire   [ 7:0]  i2c_data_w;                   // i2c写入的数据
wire           i2c_done  ;                   // i2c操作结束标志
wire           i2c_rh_wl ;                   // i2c读写控制
wire   [ 7:0]  i2c_data_r;                   // i2c读出的数据
wire   [15:0]  als_data  ;                   // ALS的数据
wire   [ 9:0]  ps_data   ;                   // PS的数据

//*****************************************************
//**                    main code
//*****************************************************

//例化i2c_dri,调用IIC协议
i2c_dri #(
    .SLAVE_ADDR  (SLAVE_ADDR),               // slave address从机地址,放此处方便参数传递
    .CLK_FREQ    (CLK_FREQ  ),               // i2c_dri模块的驱动时钟频率(CLK_FREQ)
    .I2C_FREQ    (I2C_FREQ  )                // I2C的SCL时钟频率
) u_i2c_dri(
    //global clock
    .clk         (sys_clk   ),               // i2c_dri模块的驱动时钟(CLK_FREQ)
    .rst_n       (sys_rst_n ),               // 复位信号
    //i2c interface
    .i2c_exec    (i2c_exec  ),               // I2C触发执行信号
    .bit_ctrl    (BIT_CTRL  ),               // 器件地址位控制(16b/8b)
    .i2c_rh_wl   (i2c_rh_wl ),               // I2C读写控制信号
    .i2c_addr    (i2c_addr  ),               // I2C器件内地址
    .i2c_data_w  (i2c_data_w),               // I2C要写的数据
    .i2c_data_r  (i2c_data_r),               // I2C读出的数据
    .i2c_done    (i2c_done  ),               // I 2C一次操作完成
	 .i2c_ack     (i2c_ack   ),               // I2C应答标志 0:应答 1:未应答
    .scl         (ap_scl    ),               // I2C的SCL时钟信号
    .sda         (ap_sda    ),               // I2C的SDA信号
    //user interface
    .dri_clk     (clk       )                // I2C操作时钟
);

//例化AP3216C测量模块
ap3216c u_ap3216c(
    //system clock
    .clk         (clk       ),               // 时钟信号
    .rst_n       (sys_rst_n ),               // 复位信号
    //i2c interface
    .i2c_rh_wl   (i2c_rh_wl ),               // I2C读写控制信号
    .i2c_exec    (i2c_exec  ),               // I2C触发执行信号
    .i2c_addr    (i2c_addr  ),               // I2C器件内地址
    .i2c_data_w  (i2c_data_w),               // I2C要写的数据
    .i2c_data_r  (i2c_data_r),               // I2C读出的数据
    .i2c_done    (i2c_done  ),               // I2C一次操作完成
    //user interface
    .als_data    (als_data  ),               // ALS的数据
    .ps_data     (ps_data   )                // PS的数据
);

//例化动态数码管显示模块
seg_led u_seg_led(
    //module clock
    .clk           (sys_clk  ),              // 时钟信号
    .rst_n         (sys_rst_n),              // 复位信号
    //seg_led interface
    .seg_sel       (sel      ),              // 位选
    .seg_led       (seg_led  ),              // 段选
    //user interface
    .data          (als_data ),              // 显示的数值
    .point         (6'd0     ),              // 小数点具体显示的位置,从高到低,高电平有效
    .en            (1'd1     ),              // 数码管使能信号
    .sign          (1'b0     )               // 符号位(高电平显示“-”号)
);

//例化LED模块
led_disp u_led_disp(
   //system clock
   .clk          (clk      ),                // 时钟信号
   .rst_n        (sys_rst_n),                // 复位信号
   //led interface
   .led          (led      ),                // led灯接口
   //user interface
   .data         (ps_data  )                 // PS的数据
);

endmodule
module ap3216c(
    //system clock
    input                 clk        ,    // 时钟信号
    input                 rst_n      ,    // 复位信号

    //i2c interface
    output   reg          i2c_rh_wl  ,    // I2C读写控制信号
    output   reg          i2c_exec   ,    // I2C触发执行信号
    output   reg  [15:0]  i2c_addr   ,    // I2C器件内地址
    output   reg  [ 7:0]  i2c_data_w ,    // I2C要写的数据
    input         [ 7:0]  i2c_data_r ,    // I2C读出的数据
    input                 i2c_done   ,    // I2C一次操作完成

    //user interface
    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的临时数据
reg            ir_of      ;               // 溢出标志(判断ps_data是否有效)
reg            obj        ;               // 物体状态标志(0远离1靠近)

//*****************************************************
//**                    main code
//*****************************************************

//配置AP3216C并读取数据
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        i2c_exec   <=  1'b0;
        i2c_addr   <=  8'd0;
        i2c_rh_wl  <=  1'b0;
        i2c_data_w <=  8'h0;
        flow_cnt   <=  4'd0;
        wait_cnt   <= 18'd0;
        ps_data    <= 10'd0;
        ps_data_t  <= 10'd0;
        ir_of      <=  1'b0;
        obj        <=  1'b0;
        als_done   <=  1'b0;
        als_data_t <= 16'd0;
    end
    else begin
        i2c_exec <= 1'b0;
        case(flow_cnt)
            //初始化AP3216C
            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
            //配置AP3216C的功能模式
            4'd1: begin
                i2c_exec   <= 1'b1 ;
                i2c_rh_wl  <= 1'b0 ;
                i2c_addr   <= 8'h00;               // 配置系统寄存器
                i2c_data_w <= 8'h03;               // 激活ALS+PS+IR 功能
                flow_cnt   <= flow_cnt + 1'b1;
            end
            //配置完成
            4'd2: begin
                if(i2c_done)
                    flow_cnt <= flow_cnt + 1'b1;
            end
            //等待PS转换完成(12.5ms)
            4'd3: begin
                if(wait_cnt  == TIME_PS) begin
                    wait_cnt <= 18'd0;
                    flow_cnt <= flow_cnt + 1'd1;
                end
                else
                    wait_cnt <= wait_cnt + 1'b1;
            end
            //预读PS Data Register(0x0E)
            4'd4: begin
                i2c_exec <= 1'b1;
                i2c_rh_wl<= 1'b1;
                i2c_addr <= 8'h0E;
                flow_cnt <= flow_cnt + 1'b1;
            end
            //读PS Data Register(0x0E)
            4'd5: begin
                if(i2c_done) begin
                    flow_cnt     <= flow_cnt + 1'b1;
                    ps_data_t[3:0] <= i2c_data_r[3:0];
                    ir_of        <= i2c_data_r[6]  ;
                    obj          <= i2c_data_r[7]  ;
                end
            end
            //等待一段时间以进行下一次读写
            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
            //预读PS Data Register(0x0F)
            4'd7: begin
                i2c_exec <= 1'b1;
                i2c_rh_wl<= 1'b1;
                i2c_addr <= 8'h0F;
                flow_cnt <= flow_cnt + 1'b1;
            end
            //读PS Data Register(0x0F)
            4'd8: begin
                if(i2c_done) begin
                    flow_cnt     <= flow_cnt + 1'b1;
                    ps_data_t[9:4] <= i2c_data_r[5:0];
                    ir_of        <= i2c_data_r[6]  ;
                    obj          <= i2c_data_r[7]  ;
                end
            end
            //等待ALS转换完成(100ms)
            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
            //预读ALS Data Register(0x0C)
            4'd10: begin
                i2c_exec <= 1'b1;
                i2c_rh_wl<= 1'b1;
                i2c_addr <= 8'h0C;
                flow_cnt <= flow_cnt + 1'b1;
            end
            //读ALS Data Register(0x0C)
            4'd11: begin
                if(i2c_done) begin
                    als_done        <= 1'b0;
                    als_data_t[7:0] <= i2c_data_r;
                    flow_cnt        <= flow_cnt + 1'b1;
                end
            end
            //等待一段时间以进行下一次读写
            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
            //预读ALS Data Register(0x0D)
            4'd13: begin
                i2c_exec <= 1'b1;
                i2c_rh_wl<= 1'b1;
                i2c_addr <= 8'h0D;
                flow_cnt <= flow_cnt + 1'b1;
            end
            //读ALS Data Register(0x0D)
            4'd14: begin
                if(i2c_done) begin
                    als_done         <= 1'b1;
                    als_data_t[15:8] <= i2c_data_r;
                    flow_cnt         <= 4'd3;             //跳转到状态3重新读取数据
                end
            end
        endcase
    end
end

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

endmodule
module led_disp(
    //system clock
    input                          clk  ,     // 时钟信号
    input                          rst_n,     // 复位信号

    //led interface
    output   reg   [3:0]           led  ,     // led灯接口

    //user interface
    input          [9:0]           data       // 数据
);

//*****************************************************
//**                    main code
//*****************************************************

//led灯亮灭个数显示数据大小
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0) begin
        led <=  4'd0;
    end
    else if(data < 10'd16)
        led <= 4'b0001;
    else if(data < 10'd128)
        led <= 4'b0011;
    else if(data < 10'd512)
        led <= 4'b0111;
    else
        led <= 4'b1111;
end

endmodule

猜你喜欢

转载自blog.csdn.net/gemengxia/article/details/115431968