查看: 568|回复: 1

[经验] FPGA毕设系列 - 传感器

[复制链接]

主题

好友

1457

积分

进士

  • TA的每日心情
    擦汗
    2019-1-15 13:35
  • 签到天数: 13 天

    连续签到: 1 天

    [LV.3]偶尔看看II

    发表于 2019-3-11 17:01:20 |显示全部楼层
    传感器是一种检测装置,能感受到被测量的信息,并能将感受到的信息,按一定规律变换成为电信号或其他所需形式的信息输出。在FPGA中,传感器的应用也是极为常见,这里我们给大家分享下温湿度以及接近式传感器在FPGA中的应用,希望对相关课题的小伙伴有所帮助。

    数字温湿度计
    一、设计任务
    通过FPGA实现传感器的检测,能测出当前环境的温度及湿度。

    二、设计准备
    硬件:小脚丫FPGA核心板、温湿度传感器SHT-20
    软件:Quartus Prime/Lattice Diamond

    三、设计结构
    1.jpg

    四、设计原理
    下图为温湿度传感器SHT-20模块电路,与FPGA硬件接口有I2C总线(SCL、SDA),SHT2x 传感器包含电容式湿度传感器、带隙温度传感器和专用的模拟和数字集成电路-全部放在单 CMOSens®芯片上。
    2.jpg
    五、设计驱动
    通过SHT-20时序参数了解,SHT-20支持I2C通信400KHz快速模式同时兼容100KHz的标准模式,还有两种模式下时序中的各种时间参数,所以通信速度不需要调整,本实验涉及软件复位、温度测量和湿度测量三个操作分别查看其时序流程。

    软件复位操作程序实现如下:
    1. MODE1:begin//单次写操作
    2.         if(cnt_mode1 >=4'd4) cnt_mode1 <=1'b0;    //对START中的子状态执行控制cnt_start
    3.         else cnt_mode1 <= cnt_mode1 +1'b1;
    4.         state_back <= MODE1;
    5.         case(cnt_mode1)
    6.             4'd0:   begin state <= START;end   //I2C通信时序中的START
    7.             4'd1:   begin data_wr <= dev_addr<<1; state <= WRITE;end   //设备地址
    8.             4'd2:   begin data_wr <= reg_addr; state <= WRITE;end  //寄存器地址
    9.             4'd3:   begin state <= STOP;end    //I2C通信时序中的STOP
    10.             4'd4:   begin state <= MAIN;end    //返回MAIN
    11.             default: state <= IDLE;//如果程序失控,进入IDLE自复位状态
    12.         endcase
    13.     end
    复制代码
    温湿度测量分为两部分,写指令部分和读数据部分,写指令部分比复位操作时序流程多了20us的等待,且20us等待不是必须的,可以直接使用MODE1状态完成,读数据部分如果没有测量完成寻址时就会不应答,如果测量完成时序流程程序实现如下:
    1. MODE2:begin//两次读操作
    2.         if(cnt_mode2  >=4'd7) cnt_mode2 <=4'd0;    //对START中的子状态执行控制cnt_start
    3.         else  cnt_mode2 <= cnt_mode2 +1'b1;
    4.         state_back <= MODE2;
    5.         case(cnt_mode2)
    6.             4'd0:   begin state <= START;end   //I2C通信时序中的START
    7.             4'd1:   begin data_wr  <=(dev_addr<<1)|8'h01; state <= WRITE;end//设备地址
    8.             4'd2:   begin ack <= ACK; state <= READ;end    //读寄存器数据
    9.             4'd3:   begin dat_h <= data_r;end
    10.             4'd4:   begin ack <= NACK; state <= READ;end   //读寄存器数据
    11.             4'd5:   begin dat_l <= data_r;end
    12.             4'd6:   begin state <= STOP;end    //I2C通信时序中的STOP
    13.             4'd7:   begin state <= MAIN;end    //返回MAIN
    14.             default: state <= IDLE;//如果程序失控,进入IDLE自复位状态
    15.         endcase
    16.     end
    复制代码
    最后我们编程控制状态机按照驱动例程代码中流程运行,程序实现如下:
    1. MAIN:begin
    2.         if(cnt_main  >=4'd9) cnt_main <=4'd2;      //写完控制指令后循环读数据
    3.         else  cnt_main <= cnt_main +1'b1;  
    4.         case(cnt_main)
    5.             //软件复位
    6. 4'd0:   begin  dev_addr <=7'h40; reg_addr <=8'hfe; state <= MODE1;end
    7.             4'd1:   begin  num_delay <=24'd6000; state <= DELAY;end//复位时间,15ms
    8.             //测量温度
    9. 4'd2:   begin  dev_addr <=7'h40; reg_addr <=8'hf3; state <= MODE1;end
    10.             4'd3:   begin  num_delay <=24'd34000; state <= DELAY;end//温度转换,85ms
    11.             4'd4:   begin  dev_addr <=7'h40; state <= MODE2;end    //读取配置
    12.             4'd5:   begin T_code <={dat_h,dat_l};end  //读取数据
    13.             //测量湿度
    14.             4'd6:   begin  dev_addr <=7'h40; reg_addr <=8'hf5; state <= MODE1;end
    15.             4'd7:   begin  num_delay <=24'd12000; state <= DELAY;end//湿度转换,30ms
    16.             4'd8:   begin  dev_addr <=7'h40; state <= MODE2;end    //读取配置
    17.             4'd9:   begin H_code <={dat_h,dat_l};end  //读取数据
    18.             default: state <= IDLE;//如果程序失控,进入IDLE自复位状态
    19.         endcase
    20.     end
    复制代码
    SHT-20驱动模块得到的是温度和湿度的编码值,想要得到℃和%RH的温度和湿度的数据还需要运算(公式参考数据手册),程序实现如下:
    1. wire[31:0] a = T_code *16'd17572;
    2. wire[31:0] b = a >>16;//除以2^16取商
    3. wire[31:0] c =(b>=32'd4685)?(b -32'd4685):(32'd4685- b);//绝对值
    4. wire[15:0] T_data_bin = c[15:0];
    复制代码
    运算后的数据是二进制数,需经过BCD转码,再将4个BCD码显示在4个数码管上,实现温度的显示,另外还可以增加高位消零的设计,让数码管显示更加符合人的习惯。

    1. //数据显示使能,高位消零
    2. assign dat_en[7]=|T_data[15:12]; //自或
    3. assign dat_en[6]=(b>=32'd4685)?(|T_data[15:8]):(|T_data[11:8]);
    4. assign dat_en[5:4]=2'b11;
    复制代码
    这样就实现了数字温湿度计,能够检测并显示当前环境的温湿度值。


    智能接近
    一、设计任务
    通过FPGA实现接近式的检测,能测出当前距离的远近。

    二、设计准备
    小脚丫FPGA核心板、接近式传感器APDS-9901

    三、设计结构
    3.jpg
    四、设计原理
    下图为接近光传感器APDS-9901模块电路,与FPGA硬件接口有I2C总线(SCL、SDA)和中断信号INT,APDS-9901是博通公司的集成环境光ALS、红外光IR和接近距离传感器,具有体积小、低功耗等优点,被大量应用。
    4.jpg


    五、设计驱动
    APDS-9901支持I2C通信400KHz快速模式同时兼容100KHz的标准模式,还有两种模式下时序中的各种时间参数,本例中我们就采用标准模式完成驱动设计。(此处省略分频模块以及I2C各个单元程序设计的具体过程)

    根据APDS-9901驱动的流程,我们首先有7次向寄存器写入数据的操作,这里我们将1次写操作做成状态机的一个状态,这样7次向寄存器写入数据的操作只需要在这个状态上循环执行7次就好了,单次写操作状态程序实现如下:
    1. MODE1:begin//单次写操作
    2.         if(cnt_mode1 >=4'd5) cnt_mode1 <=1'b0;    //对START中的子状态执行控制cnt_start
    3.         else cnt_mode1 <= cnt_mode1 +1'b1;
    4.         state_back  <= MODE1;
    5.         case(cnt_mode1)
    6.             4'd0:   begin state <= START;end   //I2C通信时序中的START
    7.             4'd1:   begin data_wr <= dev_addr<<1; state <= WRITE;end   //设备地址
    8.             4'd2:   begin data_wr <= reg_addr; state <= WRITE;end  //寄存器地址
    9.             4'd3:   begin data_wr <= reg_data; state <= WRITE;end  //写入数据
    10.             4'd4:   begin state <= STOP;end    //I2C通信时序中的STOP
    11.             4'd5:   begin state <= MAIN;end    //返回MAIN
    12.             default: state <= IDLE;//如果程序失控,进入IDLE自复位状态
    13.         endcase
    14.     end
    复制代码


    同理两字节数据连读的操作也做成一个状态,程序实现如下:
    1. MODE2:begin//两次读操作
    2.         if(cnt_mode2 >=4'd10) cnt_mode2 <=1'b0;   //对START中的子状态执行控制cnt_start
    3.         else cnt_mode2 <= cnt_mode2 +1'b1;
    4.          state_back <= MODE2;
    5.         case(cnt_mode2)
    6.             4'd0:   begin state <= START;end   //I2C通信时序中的START
    7.             4'd1:   begin data_wr <= dev_addr<<1; state <= WRITE;end   //设备地址
    8.             4'd2:   begin data_wr <= reg_addr; state <= WRITE;end  //寄存器地址
    9.             4'd3:   begin state <= START;end   //I2C通信时序中的START
    10.             4'd4:   begin data_wr <=(dev_addr<<1)|8'h01; state <= WRITE;end//设备地址
    11.             4'd5:   begin ack <= ACK; state <= READ;end    //读寄存器数据
    12.             4'd6:   begin dat_l <= data_r;end
    13.             4'd7:   begin ack <= NACK; state <= READ;end   //读寄存器数据
    14.             4'd8:   begin dat_h <= data_r;end
    15.             4'd9:   begin state <= STOP;end    //I2C通信时序中的STOP
    16.             4'd10:  begin state <= MAIN;end    //返回MAIN
    17.             default: state <= IDLE;//如果程序失控,进入IDLE自复位状态
    18.         endcase
    19.     end
    复制代码
    因为用到延时,也设计成一个状态,程序实现如下:
    1. DELAY:begin//延时模块
    2.         if(cnt_delay >= num_delay)begin
    3.             cnt_delay <=1'b0;
    4.             state <= MAIN;
    5.         endelse cnt_delay <= cnt_delay +1'b1;
    6.     end
    复制代码
    最后我们编程控制状态机按照驱动例程代码中流程运行,程序实现如下:
    1. 4'd0:begin dev_addr<=7'h39;reg_addr<=8'h80|8'h00;reg_data<=8'h00;state<=MODE1;end
    2. 4'd1:begin dev_addr<=7'h39;reg_addr<=8'h80|8'h01;reg_data<=8'hff;state<=MODE1;end
    3. 4'd2:begin dev_addr<=7'h39;reg_addr<=8'h80|8'h02;reg_data<=8'hff;state<=MODE1;end
    4. 4'd3:begin dev_addr<=7'h39;reg_addr<=8'h80|8'h03;reg_data<=8'hff;state<=MODE1;end
    5. 4'd4:begin dev_addr<=7'h39;reg_addr<=8'h80|8'h0e;reg_data<=8'h01;state<=MODE1;end
    6. 4'd5:begin dev_addr<=7'h39;reg_addr<=8'h80|8'h0f;reg_data<=8'h20;state<=MODE1;end
    7. 4'd6:begin dev_addr<=7'h39;reg_addr<=8'h80|8'h00;reg_data<=8'h0f;state<=MODE1;end
    8. 4'd7:   begin state <= DELAY; dat_valid <=1'b0;end    //12ms延时
    9. 4'd8:   begin dev_addr <=7'h39; reg_addr <=8'ha0|8'h14;  state <= MODE2;end
    10. 4'd9:   begin ch0_dat <={dat_h,dat_l};end//读取数据
    11. 4'd10:  begin dev_addr <=7'h39; reg_addr <=8'ha0|8'h16;  state <= MODE2;end
    12. 4'd11:  begin ch1_dat <={dat_h,dat_l};end//读取数据
    13. 4'd12:  begin dev_addr <=7'h39; reg_addr <=8'ha0|8'h18;  state <= MODE2;end
    14. 4'd13:  begin prox_dat <={dat_h,dat_l};end    //读取数据
    15. 4'd14:  begin dat_valid <=1'b1;end    //读取数据
    复制代码
    程序中我们做了一个简单的滤波处理,为了保证数据的有效,将瞬间变化太大的采样数据舍弃,程序实现如下:

    1. reg[15:0] prox_dat0,prox_dat1,prox_dat2;
    2. always@(posedge dat_valid)begin
    3.     prox_dat0 <= prox_dat;
    4.     prox_dat1 <= prox_dat0;
    5. if(((prox_dat1-prox_dat0)>=16'h200)||((prox_dat1-prox_dat0)>=16'h200))
    6. prox_dat2 <= prox_dat2;
    7.     else prox_dat2 <= prox_dat0;
    8. end
    复制代码
    从传感器读取的距离信息为16位数据,有效范围为0~1023,对应0到16‘h3ff,本实验要求用能量条的方式显示距离的远近,我们设计一个编码器将0到16‘h3ff的范围控制8个led灯的控制,程序实现如下:
    1. always@(prox_dat2[9:7])
    2.     case(prox_dat2[9:7])
    3.         3'b000: Y_out =8'b11111110;
    4.         3'b001: Y_out =8'b11111100;
    5.         3'b010: Y_out =8'b11111000;
    6.         3'b011: Y_out =8'b11110000;
    7.         3'b100: Y_out =8'b11100000;
    8.         3'b101: Y_out =8'b11000000;
    9.         3'b110: Y_out =8'b10000000;
    10.         3'b111: Y_out =8'b00000000;
    11.         default:Y_out =8'b11111111;
    12.     endcase
    复制代码
    这样就实现了智能接近设计,当靠近或者远离时,距离会反应在LED灯上。

    来源  FPGA入门到精通



    回复

    使用道具 举报

    主题

    好友

    1万

    积分

    翰林

  • TA的每日心情
    郁闷
    昨天 17:17
  • 签到天数: 973 天

    连续签到: 2 天

    [LV.10]以坛为家III

    发表于 2019-4-7 12:08:03 |显示全部楼层
    谢谢分享 522.jpg
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    关闭

    站长推荐上一条 /3 下一条

    手机版|电路城

    GMT+8, 2019-6-16 14:47 , Processed in 0.103447 second(s), 15 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz!

    返回顶部