摘要:最近在做FPGA视觉机器人,考虑到晚上机器人视线不好,萌发了给机器人做一个小型灯补光的想法。咱是机器人肯定要整点智能的对不对,思来想去觉得环境光传感器是个不错的选择。
嘿嘿,行动派往往都是说做就做,从不拖拉。
首先查阅资料:
AP3216C是一个能够测量环境光强度和距离的整合型光感测距传感器。因其功耗低、控制简单、封装小而广泛应用于智能手机、电容式触摸屏、数码相机等领域。例如应用于智能手机上面检测环境光强度,用来实现自动背光控制,以及接近开关控制(听筒靠近耳朵,手机自动灭屏功能)。
注:因为它是一种整合型多功能传感器,这种类型的传感器一般都会多种有不同的工作模式(AP3216C内部有一些寄存器,这些寄存器可以控制AP3216C的工作模式、中断方式以及采集数据等。):
这么多模式,我们选择ALS+PS+IR模式,在该模式下AP3216C连续采集环境光照强度和距离值。上表中,地址0X00对应的是一个系统模式控制寄存器,我们在初始化的时候将它配置为011,这样就能开启ALS+PS+IR模式。其他6个寄存器我们用来存储采集到的数据(红外光强度、环境光强度、以及距离值)。AP3216C采用I2C总线协议与控制器(FPGA)进行通信,因此我们通过I2C协议实现对AP3216C
相关寄存器的配置和采集数据的读取。
接下来上某宝淘材料:
L298N驱动模块一个,LED灯若干,洞洞板一块,焊接成3*3的电灯板,建议焊接成并联电路,这样就不需要为电压不足而烦恼啦:
电路连接:将电灯板的正极接到驱动模块的OUT2,负极接到OUT1,驱动模块使能端口以及IN2接到开发板的两个排针(想用哪个排针在程序定义就好),通过改变这两个端口的逻辑,我们可以实现对LED进行亮灭。在这里有个点要提一下,驱动模块只有与开发板共地才能使用,很多初学的朋友会问,怎么共地,一般的驱动模块都是会有一个模块用电输入,和一个开发板供电的(5v的正负极,你只要把这个端口的GND接到开发板的GND就行了)。但是也有特殊情况,就是一些比较小的驱动模块是没有一个独立的开发板供电端口的,像我这个模块,比较小,所以他的用电输入与开发板供电是在一起的(3个口,+5V,GND,+12V),像我图片中圈中这样,这个时候你就要从GND这个口中多分出一条线,接到开发板上,另一条接电源的GND:
AP3216C环境光传感器:因为我的开发板自带了一个环境光传感器,所以无需另接。传感器里面集成了数字环境光传感器(Ambilent Light Sensor,ALS)、距离传感器(Proximity Sensor,PS)和一个红外LED(Infrared Radiation LED,IR LED)。
接下来就是程序设计:
FPGA通过IIC总线读取AP3216C采集的环境光,通过判断环境光光照强度值(als_data)控制LED灯亮灭。从而实现白天夜晚LED灯自动开关的功能。(另外通过ps_data的值可以测试感光器与物体距离远近) 注:(想看Robei程序代码的点这个链接,我已经将整个Robei生成的工程上传!!!)
在上面的工程中,我并没有设计测距效果模块,如果需要想要看看测距效果如何的朋友,new一个模块就好了。代码如下:
//led灯亮灭个数(4个)显示数据大小(距离的远近),这里的led灯不是焊接板的灯,而是开发板上的灯,可自调
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0) begin
led <= 4'd0;
end
else if(data < 10'd16) //这里的data例化的时候连接ap3216c模块的PS_data端口就行
led <= 4'b0001;
else if(data < 10'd128)
led <= 4'b0011;
else if(data < 10'd512)
led <= 4'b0111;
else
led <= 4'b1111;
end
考虑到下载文件不方便,我将核心模块ap3216c模块Verilog代码放在这里:
module ap3216c(
clk, //ap3216c模块主要用来测量环境光照强度和距离值
rst_n,
i2c_data_r,
i2c_done,
i2c_rh_wl,
i2c_exec,
i2c_addr,
i2c_data_w,
als_data,
ps_data);
//---Ports declearation: generated by Robei---
input clk;
input rst_n;
input [7:0] i2c_data_r;
input i2c_done;
output i2c_rh_wl;
output i2c_exec;
output [15:0] i2c_addr;
output [7:0] i2c_data_w;
output [15:0] als_data;
output [9:0] ps_data;
wire clk;
wire rst_n;
wire [7:0] i2c_data_r;
wire i2c_done;
reg i2c_rh_wl;
reg i2c_exec;
reg [15:0] i2c_addr;
reg [7:0] i2c_data_w;
reg [15:0] als_data;
reg [9:0] ps_data;
//----Code starts here: integrated by Robei-----
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靠近)
//配置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 //ap3216c
接下来将程序下载到开发板上来看一下实验效果:
注:因为我是在白天做这个实验,没有黑暗环境做实验条件,所以将开发板放抽屉里面,用手遮住传感器上方,这样模拟一下夜晚环境。
FPGA环境光传感器实验