1)实验平台:正点原子新起点V2开发板 2)平台采购地址:https://detail.tmall.com/item.htm?id=609758951113 2)全套实验源码 手册 视频下载地址:http://www.openedv.com/thread-300792-1-1.html 3)对正点原子FPGA感兴趣的同学可以加群讨论:99424016 4)关注正点原子微信官方账号,获取最新信息更新
第三十三章环境光传感器实验
AP3216C是一种能测量环境光强度和距离的集成光传感器。智能手机、电容式触摸屏、数码相机等领域因其功耗低、控制简单、包装小而得到广泛应用。例如,用于检测智能手机上的环境光强度,实现自动背光控制和接近开关控制(听筒靠近耳朵,手机自动屏幕故障功能)。我们将使用本章FPGA开发板上的AP3216C测量环境光强度和距离。 本章包括以下几个部分: 3232.1简介 32.2实验任务 32.3硬件设计 32.4程序设计 32.5下载验证
33.1简介 AP3216C是敦南科技推出的集成传感器,集成了数字环境光传感器(Ambilent Light Sensor,ALS)、距离传感器(Proximity Sensor,PS)和一个红外LED(Infrared Radiation LED,IR LED)。其中距离传感器分辨率10位,环境光传感器分辨率16位。AP3216C我们可以支持多种工作模式ALS PS IR该模式下的模式AP3216C连续收集环境光强度和距离值。 AP3216C 内部功能模块的框图如图所示 33.1.1所示:
图 33.1.1 AP3216C功能框图 当物体接近时,图 33.1.红外发光二极管1中(IR_LED)红外线与物体碰撞后反射到红外光电二极管(PS)光电二极管通过模数转换器将光信号转换为电流信号(ADC)将其转换成数字信号并存储在寄存器中。 物体越近,反射就越近PS红外光强度越高,模数转换后获得的数据越大,从而实现感应物体距离的功能。类似地,光电二极管(ALS)感应环境光强度,并将其转化为数字信号,以实现环境光强度的检测。 AP3216C有一些可以控制的寄存器AP3216C工作模式、中断模式和数据采集。在这里,我们只介绍本章中需要使用的一些寄存器和其他寄存器的描述和说明。AP3216C数据手册。 本章用到的AP3216C寄存器如表 33.1.1所示: 表 33.1.1 AP3216C 相关寄存器及其说明
地址0在上表中X00对应于系统模式控制寄存器,我们在初始化时将其配置为011ALS PS IR检测功能。剩下的6个寄存器是数据寄存器,分别存储AP3216C采集到的红外光强度、环境光强度、以及距离值。 AP3216C采用I2C总线协议和控制器(FPGA)所以我们通过I2C协议实现对AP3216C相关寄存器的配置和数据收集的读取。 AP3216C写寄存器时序图如图所示 33.1.2所示:
图 33.1.2 AP3216C 编写寄存器时序 图 33.1.2中,先发送AP3216C设备地址(0X1E)和读写控制位,最低位W=0表示写数据;然后发送8个寄存器地址,最后发送写入寄存器的配置指令。其中:S,表示IIC起始信号;W,表示读写标志位(W=0 表示写,W=1 表示读);A,表示响应信号;P,表示IIC停止信号。有关I2C详细介绍总线协议,请参考EEPROM读写实验。 AP3216C读寄存器时序如图 33.1.3所示:
图 33.1.3 AP3216C 读取寄存器时序 图 33.1.3.7位装置的地址也首先发送 编写操作标志,然后发送寄存器地址;然后重新发送起始信号(Sr),再次发送7个地址 读取操作标志,最后读取寄存器值。Sr,表示重新发送IIC起始信号;N,表示不对AP3216C进行应答。 33.2实验任务 本节的实验任务是使用新的起点FPGA开发板上的AP3216C测量环境光强度和物体距离和物体距离,并在数字管上显示环境光强度led灯的亮灭来指示物体的距离。 33.3硬件设计 开发板的新起点AP3216C接口部分的原理图如图所示 33.3.1所示。
图 33.3.1 AP3216C接口原理图 AP3216C作为I2C接口从器件和EEPROM等模块统一挂在新起点开发板上IIC总线上。LEDA装置内红外发光二极管(IR_LED)的阳极,LEDC一般连接到阴极LED的驱动输出脚LDR。 在本实验中,各端口信号的管脚分: 表 33.3.1 AP3216C环境光-距离传感器实验管脚分配
对应的TCL约束语句如下: set_location_assignment PIN_M2 -to sys_clk set_location_assignment PIN_M1 -to sys_rst_n set_location_assignment PIN_C8 -to ap_sda set_location_assignment PIN_D8 -to ap_scl set_location_assignment PIN_D11 -to led[0] set_location_assignment PIN_C11 -to led[1] set_location_assignment PIN_E10 -to led[2] set_location_assignment PIN_F9 -to led[3] set_location_assignment PIN_N16 -to sel[0] set_location_assignment PIN_N15 -to sel[1] set_location_assignment PIN_P16 -to sel[2] set_location_assignment PIN_P15 -to sel[3] set_location_assignment PIN_R16 -to sel[4] set_location_assignment PIN_T15 -to sel[5] set_location_assignment PIN_M11 -to seg_led[0] set_location_assignment PIN_N12 -to seg_led[1] set_location_assignment PIN_C9 -to seg_led[2] set_location_assignment PIN_N13 -to seg_led[3] set_location_assignment PIN_M10 -to seg_led[4] set_location_assignment PIN_P11 -to seg_led[6] set_location_assignment PIN_D9 -to seg_led[7] 33.4程序设计 根据实验任务,我们可以大致规划出系统的控制流程:FPGA首先通过I2C总线读取AP3216C采集的环境光及距离数据,然后将读到的距离值用于控制4个led灯的亮灭,以指示物体的远近;并将环境光光照强度用数码管显示出来。由此画出系统的功能框图如下所示:
图 33.4.1 AP3216C环境光—距离测量实验系统框图 程序中各模块端口及信号连接如图 33.4.2所示: 由系统框图可知,FPGA部分包括五个模块,顶层模块(ap3216c_top)、IIC驱动模块(i2c_dri)、AP3216C数据采集模块(ap3216c)、LED显示模块(led_disp)以及数码管显示模块(seg_led)。各模块功能如下:
图 33.4.2 顶层模块原理图 顶层模块(ap3216c_top):顶层模块例化了IIC驱动模块(i2c_dri)、AP3216C数据采集模块(ap3216c)、LED显示模块(led_disp)以及数码管显示模块(seg_led),完成各模块之间的数据交互。AP3216C数据采集模块通过IIC驱动模块与AP3216C器件进行通信,并将采集到的环境光强度送入数码管显示模块显示,采集到的距离值送入LED显示模块显示。 IIC驱动模块(i2c_dri):由于AP3216C采用I2C协议与FPGA进行通信,所以需用IIC驱动模块实现FPGA与AP3216C信号的交互。 AP3216C数据采集模块(ap3216c)通过调用IIC驱动模块(i2c_dri)来实现对AP3216C采集数据的读取。将读到的环境光照强度数值als_data传递给数码管模块(seg_led)显示,将读到的距离值ps_data传递给led显示模块(led_disp),用于控制4个led灯的亮灭以指示物体的远近。 LED显示模块(led_disp):根据距离值的远近点亮LED灯的个数,距离越近,LED亮的个数越多,距离越远,LED亮的个数越少。 数码管显示模块(seg_led):数码管显示模块显示采集到的环境光强度值。 顶层模块的代码如下:
1 module ap3216c_top(
2 //global clock
3 input sys_clk , // 系统时钟
4 input sys_rst_n , // 系统复位
5
6 //ap3216c interface
7 output ap_scl , // i2c时钟线
8 inout ap_sda , // i2c数据线
9
10 //user interface
11 output [3:0] led , // led灯接口
12 output [5:0] sel , // 数码管位选
13 output [7:0] seg_led // 数码管段选
14 );
15
16 //parameter define
17 parameter SLAVE_ADDR = 7'h1e ; // 器件地址
18 parameter BIT_CTRL = 1'b0 ; // 字地址位控制参数(16b/8b)
19 parameter CLK_FREQ = 26'd50_000_000; // i2c_dri模块的驱动时钟频率(CLK_FREQ)
20 parameter I2C_FREQ = 18'd250_000 ; // I2C的SCL时钟频率
21
22 //wire define
23 wire clk ; // I2C操作时钟
24 wire i2c_exec ; // i2c触发控制
25 wire [15:0] i2c_addr ; // i2c操作地址
26 wire [ 7:0] i2c_data_w; // i2c写入的数据
27 wire i2c_done ; // i2c操作结束标志
28 wire i2c_rh_wl ; // i2c读写控制
29 wire [ 7:0] i2c_data_r; // i2c读出的数据
30 wire [15:0] als_data ; // ALS的数据
31 wire [ 9:0] ps_data ; // PS的数据
32
33 //*****************************************************
34 //** main code
35 //*****************************************************
36
37 //例化i2c_dri,调用IIC协议
38 i2c_dri #(
39 .SLAVE_ADDR (SLAVE_ADDR), // slave address从机地址,放此处方便参数传递
40 .CLK_FREQ (CLK_FREQ ), // i2c_dri模块的驱动时钟频率(CLK_FREQ)
41 .I2C_FREQ (I2C_FREQ ) // I2C的SCL时钟频率
42 ) u_i2c_dri(
43 //global clock
44 .clk (sys_clk ), // i2c_dri模块的驱动时钟(CLK_FREQ)
45 .rst_n (sys_rst_n ), // 复位信号
46 //i2c interface
47 .i2c_exec (i2c_exec ), // I2C触发执行信号
48 .bit_ctrl (BIT_CTRL ), // 器件地址位控制(16b/8b)
49 .i2c_rh_wl (i2c_rh_wl ), // I2C读写控制信号
50 .i2c_addr (i2c_addr ), // I2C器件内地址
51 .i2c_data_w (i2c_data_w), // I2C要写的数据
52 .i2c_data_r (i2c_data_r), // I2C读出的数据
53 .i2c_done (i2c_done ), // I 2C一次操作完成
54 .i2c_ack (), // I2C应答标志 0:应答 1:未应答
55 .scl (ap_scl ), // I2C的SCL时钟信号
56 .sda (ap_sda ), // I2C的SDA信号
57 //user interface
58 .dri_clk (clk ) // I2C操作时钟
59 );
60
61 //例化AP3216C测量模块
62 ap3216c u_ap3216c(
63 //system clock
64 .clk (clk ), // 时钟信号
65 .rst_n (sys_rst_n ), // 复位信号
66 //i2c interface
67 .i2c_rh_wl (i2c_rh_wl ), // I2C读写控制信号
68 .i2c_exec (i2c_exec ), // I2C触发执行信号
69 .i2c_addr (i2c_addr ), // I2C器件内地址
70 .i2c_data_w (i2c_data_w), // I2C要写的数据
71 .i2c_data_r (i2c_data_r), // I2C读出的数据
72 .i2c_done (i2c_done ), // I2C一次操作完成
73 //user interface
74 .als_data (als_data ), // ALS的数据
75 .ps_data (ps_data ) // PS的数据
76 );
77
78 //例化动态数码管显示模块
79 seg_led u_seg_led(
80 //module clock
81 .clk (sys_clk ), // 时钟信号
82 .rst_n (sys_rst_n), // 复位信号
83 //seg_led interface
84 .seg_sel (sel ), // 位选
85 .seg_led (seg_led ), // 段选
86 //user interface
87 .data (als_data ), // 显示的数值
88 .point (6'd0 ), // 小数点具体显示的位置,从高到低,高电平有效
89 .en (1'd1 ), // 数码管使能信号
90 .sign (1'b0 ) // 符号位(高电平显示“-”号)
91 );
92
93 //例化LED模块
94 led_disp u_led_disp(
95 //system clock
96 .clk (clk ), // 时钟信号
97 .rst_n (sys_rst_n), // 复位信号
98 //led interface
99 .led (led ), // led灯接口
100 //user interface
101 .data (ps_data ) // PS的数据
102 );
103
104 endmodule
顶层模块中主要完成对其余模块的例化,其中I2C驱动模块(i2c_dri)程序与“EEPROM读写实验”章节中的IIC驱动模块(i2c_dri)程序完全相同。有关IIC驱动模块的详细介绍请大家参考“EEPROM读写实验”。 为了可以同时采集到环境光照强度值和距离值,我们需要配置系统寄存器(地址0x00)为011,使AP3216C工作在PS和ALS模式下,此时AP3216C交替采集距离值PS和环境光照强度ALS。
图 33.4.3 采集时序图 由图 33.4.3可以看到,I2C配置完系统寄存器后采集距离值PS需要的时间为12.5ms,采集环境光照强度ALS需要的时间为100ms。 AP3216C数据采集模块的代码如下所示:
1 module ap3216c(
2 //system clock
3 input clk , // 时钟信号
4 input rst_n , // 复位信号
5
6 //i2c interface
7 output reg i2c_rh_wl , // I2C读写控制信号
8 output reg i2c_exec , // I2C触发执行信号
9 output reg [15:0] i2c_addr , // I2C器件内地址
10 output reg [ 7:0] i2c_data_w , // I2C要写的数据
11 input [ 7:0] i2c_data_r , // I2C读出的数据
12 input i2c_done , // I2C一次操作完成
13
14 //user interface
15 output reg [15:0] als_data , // ALS的数据
16 output reg [ 9:0] ps_data // PS的数据
17 );
18
19 //parameter define
20 parameter TIME_PS = 14'd12_500 ; // PS转换时间为12.5ms(clk = 1MHz)
21 parameter TIME_ALS = 17'd100_000 ; // ALS转换时间为100ms(clk = 1MHz)
22 parameter TIME_REST = 8'd2 ; // 停止后重新开始的时间间隔控制
23
24 //reg define
25 reg [ 3:0] flow_cnt ; // 状态流控制
26 reg [18:0] wait_cnt ; // 计数等待
27 reg [15:0] als_data_t ; // ALS的临时数据
28 reg als_done ; // 环境光照强度值采集完成信号
29 reg [ 9:0] ps_data_t ; // PS的临时数据
30 reg ir_of ; // 溢出标志(判断ps_data是否有效)
31 reg obj ; // 物体状态标志(0远离1靠近)
32
33 //*****************************************************
34 //** main code
35 //*****************************************************
36
37 //配置AP3216C并读取数据
38 always @(posedge clk or negedge rst_n) begin
39 if(!rst_n) begin
40 i2c_exec <= 1'b0;
41 i2c_addr <= 8'd0;
42 i2c_rh_wl <= 1'b0;
43 i2c_data_w <= 8'h0;
44 flow_cnt <= 4'd0;
45 wait_cnt <= 18'd0;
46 ps_data <= 10'd0;
47 ps_data_t <= 10'd0;
48 ir_of <= 1'b0;
49 obj <= 1'b0;
50 als_done <= 1'b0;
51 als_data_t <= 16'd0;
52 end
53 else begin
54 i2c_exec <= 1'b0;
55 case(flow_cnt)
56 //初始化AP3216C
57 4'd0: begin
58 if(wait_cnt == 18'd100) begin
59 wait_cnt <= 18'd0;
60 flow_cnt <= flow_cnt + 1'b1;
61 end
62 else
63 wait_cnt <= wait_cnt +1'b1;
64 end
65 //配置AP3216C的功能模式
66 4'd1: begin
67 i2c_exec <= 1'b1 ;
68 i2c_rh_wl <= 1'b0 ;
69 i2c_addr <= 8'h00; // 配置系统寄存器
70 i2c_data_w <= 8'h03; // 激活ALS+PS+IR 功能
71 flow_cnt <= flow_cnt + 1'b1;
72 end
73 //配置完成
74 4'd2: begin
75 if(i2c_done)
76 flow_cnt <= flow_cnt + 1'b1;
77 end
78 //等待PS转换完成(12.5ms)
79 4'd3: begin
80 if(wait_cnt == TIME_PS) begin
81 wait_cnt <= 18'd0;
82 flow_cnt <= flow_cnt + 1'd1;
83 end
84 else
85 wait_cnt <= wait_cnt + 1'b1;
86 end
87 //预读PS Data Register(0x0E)
88 4'd4: begin
89 i2c_exec <= 1'b1;
90 i2c_rh_wl<= 1'b1;
91 i2c_addr <= 8'h0E;
92 flow_cnt <= flow_cnt + 1'b1;
93 end
94 //读PS Data Register(0x0E)
95 4'd5: begin
96 if(i2c_done) begin
97 flow_cnt <= flow_cnt + 1'b1;
98 ps_data_t[3:0] <= i2c_data_r[3:0];
99 ir_of <= i2c_data_r[6] ;
100 obj <= i2c_data_r[7] ;
101 end
102 end
103 //等待一段时间以进行下一次读写
104 4'd6: begin
105 if(wait_cnt == TIME_REST) begin//TIME_REST
106 wait_cnt <= 18'd0;
107 flow_cnt <= flow_cnt + 1'b1;
108 end
109 else
110 wait_cnt <= wait_cnt +1'b1;
111 end
112 //预读PS Data Register(0x0F)
113 4'd7: begin
114 i2c_exec <= 1'b1;
115 i2c_rh_wl<= 1'b1;
116 i2c_addr <= 8'h0F;
117 flow_cnt <= flow_cnt + 1'b1;
118 end
119 //读PS Data Register(0x0F)
120 4'd8: begin
121 if(i2c_done) begin
122 flow_cnt <= flow_cnt + 1'b1;
123 ps_data_t[9:4] <= i2c_data_r[5:0];
124 ir_of <= i2c_data_r[6] ;
125 obj <= i2c_data_r[7] ;
126 end
127 end
128 //等待ALS转换完成(100ms)
129 4'd9: begin
130 if(wait_cnt == TIME_ALS) begin
131 wait_cnt <= 18'd0;
132 flow_cnt <= flow_cnt + 1'd1;
133 ps_data <= ps_data_t;
134 end
135 else
136 wait_cnt <= wait_cnt + 1'b1;
137 end
138 //预读ALS Data Register(0x0C)
139 4'd10: begin
140 i2c_exec <= 1'b1;
141 i2c_rh_wl<= 1'b1;
142 i2c_addr <= 8'h0C;
143 flow_cnt <= flow_cnt + 1'b1;
144 end
145 //读ALS Data Register(0x0C)
146 4'd11: begin
147 if(i2c_done) begin
148 als_done <= 1'b0;
149 als_data_t[7:0] <= i2c_data_r;
150 flow_cnt <= flow_cnt + 1'b1;
151 end
152 end
153 //等待一段时间以进行下一次读写
154 4'd12: begin
155 if(wait_cnt == TIME_REST) begin
156 wait_cnt <= 18'd0;
157 flow_cnt <= flow_cnt + 1'b1;
158 end
159 else
160 wait_cnt <= wait_cnt +1'b1;
161 end
162 //预读ALS Data Register(0x0D)
163 4'd13: begin
164 i2c_exec <= 1'b1;
165 i2c_rh_wl<= 1'b1;
166 i2c_addr <= 8'h0D;
167 flow_cnt <= flow_cnt + 1'b1;
168 end
169 //读ALS Data Register(0x0D)
170 4'd14: begin
171 if(i2c_done) begin
172 als_done <= 1'b1;
173 als_data_t[15:8] <= i2c_data_r;
174 flow_cnt <= 4'd3; //跳转到状态3重新读取数据
175 end
176 end
177 endcase
178 end
179 end
180
181 //当采集的环境光转换成光照强度(单位:lux)
182 always @(posedge clk or negedge rst_n) begin
183 if(!rst_n)
184 als_data <= 16'd0;
185 else if(als_done)
186 als_data = als_data_t * 6'd35 / 7'd100;
187 end
188
189 endmodule
程序中第98行我们只取了读到的数据的低4位,第123取了读到的数据的低6位。这是因为PS数据的低4位放在地址为0x0e处的寄存器的低4位,PS数据的高6位放在地址0x0f处的寄存器的低6位。 程序中第182行开始的always语句是对采集到的als_data转化为环境光照强度值。由AP3216C器件datasheet可知环境光照强度值Ambient Light (lux) 为: Ambient Light (lux)= 16 bit ALS ADC data * Resolution 这里的16 bit ALS ADC data即程序中的als_data_t,Resolution 为0.35 lux/count(由AP3216C的datasheet可知),所以环境光照强度值Ambient Light (lux)= als_data_t * 0.35。由于0.35为小数,而Verilog HDL不能直接表示小数,所以我们需要进行转化。而0.35=35/100,所以我们可以将读取到的als_data_t值乘以35然后再除以100得到环境光照强度值als_data。并把最终得到的环境光照强度值als_data传递给数码管显示,数码管显示模块可参考动态数码管显示实验。 另外需要注意的是,在程序的第30和第31行我们定义了两个寄存器变量ir_of和obj。ir_of是溢出标志用来判断ps_data是否有效,obj是物体状态标志:0表示物体远离传感器,1表示物体靠近传感器。用户可根据这两个数据来对AP3216C传感器应用进行拓展。 图 33.4.4为采集过程中SignalTap抓取的波形图。从图中可以看到I2C写的数值为03h,也即配置AP3216C工作在ALS+PS+IR模式。当前读到的环境光强度als_data为2164lux,距离值ps_data为029h。 图 33.4.4 SignalTap波形图 led显示模块的代码如下:
1 module led_disp(
2 //system clock
3 input clk , // 时钟信号
4 input rst_n, // 复位信号
5
标签: 16b6荷重传感器m11传感器n12f接近传感器2p3210光电传感器开关ps30系列光电传感器wl的二极管