传感器是一种检测装置,能感受到被测量的信息,并能将感受到的信息,按一定规律变换成为电信号或其他所需形式的信息输出。在FPGA中,传感器的应用也是极为常见,这里我们给大家分享下温湿度以及接近式传感器在FPGA中的应用,希望对相关课题的小伙伴有所帮助。
数字温湿度计
一、设计任务 通过FPGA实现传感器的检测,能测出当前环境的温度及湿度。
二、设计准备 硬件:小脚丫FPGA核心板、温湿度传感器SHT-20 软件:Quartus Prime/Lattice Diamond
三、设计结构
四、设计原理 下图为温湿度传感器SHT-20模块电路,与FPGA硬件接口有I2C总线(SCL、SDA),SHT2x 传感器包含电容式湿度传感器、带隙温度传感器和专用的模拟和数字集成电路-全部放在单 CMOSens®芯片上。 五、设计驱动 通过SHT-20时序参数了解,SHT-20支持I2C通信400KHz快速模式同时兼容100KHz的标准模式,还有两种模式下时序中的各种时间参数,所以通信速度不需要调整,本实验涉及软件复位、温度测量和湿度测量三个操作分别查看其时序流程。
软件复位操作程序实现如下: - MODE1:begin//单次写操作
- if(cnt_mode1 >=4'd4) cnt_mode1 <=1'b0; //对START中的子状态执行控制cnt_start
- else cnt_mode1 <= cnt_mode1 +1'b1;
- state_back <= MODE1;
- case(cnt_mode1)
- 4'd0: begin state <= START;end //I2C通信时序中的START
- 4'd1: begin data_wr <= dev_addr<<1; state <= WRITE;end //设备地址
- 4'd2: begin data_wr <= reg_addr; state <= WRITE;end //寄存器地址
- 4'd3: begin state <= STOP;end //I2C通信时序中的STOP
- 4'd4: begin state <= MAIN;end //返回MAIN
- default: state <= IDLE;//如果程序失控,进入IDLE自复位状态
- endcase
- end
复制代码温湿度测量分为两部分,写指令部分和读数据部分,写指令部分比复位操作时序流程多了20us的等待,且20us等待不是必须的,可以直接使用MODE1状态完成,读数据部分如果没有测量完成寻址时就会不应答,如果测量完成时序流程程序实现如下: - MODE2:begin//两次读操作
- if(cnt_mode2 >=4'd7) cnt_mode2 <=4'd0; //对START中的子状态执行控制cnt_start
- else cnt_mode2 <= cnt_mode2 +1'b1;
- state_back <= MODE2;
- case(cnt_mode2)
- 4'd0: begin state <= START;end //I2C通信时序中的START
- 4'd1: begin data_wr <=(dev_addr<<1)|8'h01; state <= WRITE;end//设备地址
- 4'd2: begin ack <= ACK; state <= READ;end //读寄存器数据
- 4'd3: begin dat_h <= data_r;end
- 4'd4: begin ack <= NACK; state <= READ;end //读寄存器数据
- 4'd5: begin dat_l <= data_r;end
- 4'd6: begin state <= STOP;end //I2C通信时序中的STOP
- 4'd7: begin state <= MAIN;end //返回MAIN
- default: state <= IDLE;//如果程序失控,进入IDLE自复位状态
- endcase
- end
复制代码最后我们编程控制状态机按照驱动例程代码中流程运行,程序实现如下: - MAIN:begin
- if(cnt_main >=4'd9) cnt_main <=4'd2; //写完控制指令后循环读数据
- else cnt_main <= cnt_main +1'b1;
- case(cnt_main)
- //软件复位
- 4'd0: begin dev_addr <=7'h40; reg_addr <=8'hfe; state <= MODE1;end
- 4'd1: begin num_delay <=24'd6000; state <= DELAY;end//复位时间,15ms
- //测量温度
- 4'd2: begin dev_addr <=7'h40; reg_addr <=8'hf3; state <= MODE1;end
- 4'd3: begin num_delay <=24'd34000; state <= DELAY;end//温度转换,85ms
- 4'd4: begin dev_addr <=7'h40; state <= MODE2;end //读取配置
- 4'd5: begin T_code <={dat_h,dat_l};end //读取数据
- //测量湿度
- 4'd6: begin dev_addr <=7'h40; reg_addr <=8'hf5; state <= MODE1;end
- 4'd7: begin num_delay <=24'd12000; state <= DELAY;end//湿度转换,30ms
- 4'd8: begin dev_addr <=7'h40; state <= MODE2;end //读取配置
- 4'd9: begin H_code <={dat_h,dat_l};end //读取数据
- default: state <= IDLE;//如果程序失控,进入IDLE自复位状态
- endcase
- end
复制代码 SHT-20驱动模块得到的是温度和湿度的编码值,想要得到℃和%RH的温度和湿度的数据还需要运算(公式参考数据手册),程序实现如下: - wire[31:0] a = T_code *16'd17572;
- wire[31:0] b = a >>16;//除以2^16取商
- wire[31:0] c =(b>=32'd4685)?(b -32'd4685):(32'd4685- b);//绝对值
- wire[15:0] T_data_bin = c[15:0];
复制代码运算后的数据是二进制数,需经过BCD转码,再将4个BCD码显示在4个数码管上,实现温度的显示,另外还可以增加高位消零的设计,让数码管显示更加符合人的习惯。
- //数据显示使能,高位消零
- assign dat_en[7]=|T_data[15:12]; //自或
- assign dat_en[6]=(b>=32'd4685)?(|T_data[15:8]):(|T_data[11:8]);
- assign dat_en[5:4]=2'b11;
复制代码这样就实现了数字温湿度计,能够检测并显示当前环境的温湿度值。
智能接近 一、设计任务 通过FPGA实现接近式的检测,能测出当前距离的远近。
二、设计准备 小脚丫FPGA核心板、接近式传感器APDS-9901
三、设计结构 四、设计原理 下图为接近光传感器APDS-9901模块电路,与FPGA硬件接口有I2C总线(SCL、SDA)和中断信号INT,APDS-9901是博通公司的集成环境光ALS、红外光IR和接近距离传感器,具有体积小、低功耗等优点,被大量应用。
五、设计驱动 APDS-9901支持I2C通信400KHz快速模式同时兼容100KHz的标准模式,还有两种模式下时序中的各种时间参数,本例中我们就采用标准模式完成驱动设计。(此处省略分频模块以及I2C各个单元程序设计的具体过程)
根据APDS-9901驱动的流程,我们首先有7次向寄存器写入数据的操作,这里我们将1次写操作做成状态机的一个状态,这样7次向寄存器写入数据的操作只需要在这个状态上循环执行7次就好了,单次写操作状态程序实现如下: - MODE1:begin//单次写操作
- if(cnt_mode1 >=4'd5) cnt_mode1 <=1'b0; //对START中的子状态执行控制cnt_start
- else cnt_mode1 <= cnt_mode1 +1'b1;
- state_back <= MODE1;
- case(cnt_mode1)
- 4'd0: begin state <= START;end //I2C通信时序中的START
- 4'd1: begin data_wr <= dev_addr<<1; state <= WRITE;end //设备地址
- 4'd2: begin data_wr <= reg_addr; state <= WRITE;end //寄存器地址
- 4'd3: begin data_wr <= reg_data; state <= WRITE;end //写入数据
- 4'd4: begin state <= STOP;end //I2C通信时序中的STOP
- 4'd5: begin state <= MAIN;end //返回MAIN
- default: state <= IDLE;//如果程序失控,进入IDLE自复位状态
- endcase
- end
复制代码
同理两字节数据连读的操作也做成一个状态,程序实现如下: - MODE2:begin//两次读操作
- if(cnt_mode2 >=4'd10) cnt_mode2 <=1'b0; //对START中的子状态执行控制cnt_start
- else cnt_mode2 <= cnt_mode2 +1'b1;
- state_back <= MODE2;
- case(cnt_mode2)
- 4'd0: begin state <= START;end //I2C通信时序中的START
- 4'd1: begin data_wr <= dev_addr<<1; state <= WRITE;end //设备地址
- 4'd2: begin data_wr <= reg_addr; state <= WRITE;end //寄存器地址
- 4'd3: begin state <= START;end //I2C通信时序中的START
- 4'd4: begin data_wr <=(dev_addr<<1)|8'h01; state <= WRITE;end//设备地址
- 4'd5: begin ack <= ACK; state <= READ;end //读寄存器数据
- 4'd6: begin dat_l <= data_r;end
- 4'd7: begin ack <= NACK; state <= READ;end //读寄存器数据
- 4'd8: begin dat_h <= data_r;end
- 4'd9: begin state <= STOP;end //I2C通信时序中的STOP
- 4'd10: begin state <= MAIN;end //返回MAIN
- default: state <= IDLE;//如果程序失控,进入IDLE自复位状态
- endcase
- end
复制代码 因为用到延时,也设计成一个状态,程序实现如下: - DELAY:begin//延时模块
- if(cnt_delay >= num_delay)begin
- cnt_delay <=1'b0;
- state <= MAIN;
- endelse cnt_delay <= cnt_delay +1'b1;
- end
复制代码最后我们编程控制状态机按照驱动例程代码中流程运行,程序实现如下: - 4'd0:begin dev_addr<=7'h39;reg_addr<=8'h80|8'h00;reg_data<=8'h00;state<=MODE1;end
- 4'd1:begin dev_addr<=7'h39;reg_addr<=8'h80|8'h01;reg_data<=8'hff;state<=MODE1;end
- 4'd2:begin dev_addr<=7'h39;reg_addr<=8'h80|8'h02;reg_data<=8'hff;state<=MODE1;end
- 4'd3:begin dev_addr<=7'h39;reg_addr<=8'h80|8'h03;reg_data<=8'hff;state<=MODE1;end
- 4'd4:begin dev_addr<=7'h39;reg_addr<=8'h80|8'h0e;reg_data<=8'h01;state<=MODE1;end
- 4'd5:begin dev_addr<=7'h39;reg_addr<=8'h80|8'h0f;reg_data<=8'h20;state<=MODE1;end
- 4'd6:begin dev_addr<=7'h39;reg_addr<=8'h80|8'h00;reg_data<=8'h0f;state<=MODE1;end
- 4'd7: begin state <= DELAY; dat_valid <=1'b0;end //12ms延时
- 4'd8: begin dev_addr <=7'h39; reg_addr <=8'ha0|8'h14; state <= MODE2;end
- 4'd9: begin ch0_dat <={dat_h,dat_l};end//读取数据
- 4'd10: begin dev_addr <=7'h39; reg_addr <=8'ha0|8'h16; state <= MODE2;end
- 4'd11: begin ch1_dat <={dat_h,dat_l};end//读取数据
- 4'd12: begin dev_addr <=7'h39; reg_addr <=8'ha0|8'h18; state <= MODE2;end
- 4'd13: begin prox_dat <={dat_h,dat_l};end //读取数据
- 4'd14: begin dat_valid <=1'b1;end //读取数据
复制代码程序中我们做了一个简单的滤波处理,为了保证数据的有效,将瞬间变化太大的采样数据舍弃,程序实现如下:
- reg[15:0] prox_dat0,prox_dat1,prox_dat2;
- always@(posedge dat_valid)begin
- prox_dat0 <= prox_dat;
- prox_dat1 <= prox_dat0;
- if(((prox_dat1-prox_dat0)>=16'h200)||((prox_dat1-prox_dat0)>=16'h200))
- prox_dat2 <= prox_dat2;
- else prox_dat2 <= prox_dat0;
- end
复制代码从传感器读取的距离信息为16位数据,有效范围为0~1023,对应0到16‘h3ff,本实验要求用能量条的方式显示距离的远近,我们设计一个编码器将0到16‘h3ff的范围控制8个led灯的控制,程序实现如下: - always@(prox_dat2[9:7])
- case(prox_dat2[9:7])
- 3'b000: Y_out =8'b11111110;
- 3'b001: Y_out =8'b11111100;
- 3'b010: Y_out =8'b11111000;
- 3'b011: Y_out =8'b11110000;
- 3'b100: Y_out =8'b11100000;
- 3'b101: Y_out =8'b11000000;
- 3'b110: Y_out =8'b10000000;
- 3'b111: Y_out =8'b00000000;
- default:Y_out =8'b11111111;
- endcase
复制代码这样就实现了智能接近设计,当靠近或者远离时,距离会反应在LED灯上。
来源 FPGA入门到精通
|