**这是本文档旧的修订版!**

=====数字温湿度计设计=====

实验任务

  • 任务:基于 STEP-MAX10M08核心板 和 STEP BaseBoard V3.0底板 完成数字温湿度计设计并观察调试结果
  • 要求:驱动底板上的温湿度传感器SHT-20测量空气中的温度和湿度,将温湿度信息显示在8位扫描式数码管上。
  • 解析:通过FPGA编程驱动I2C接口温湿度传感器SHT-20,获取温湿度码值信息,将两种码值信息经过运算转换成物理温度湿度数据,然后经过BCD转码处理并显示到扫描式数码管上。

实验目的

前面的章节中我们学习了扫描式数码管模块和BCD转码模块的工作原理及驱动方法,也对I2C总线协议及相关知识,本实验主要对I2C总线驱动方法加以练习,同时熟悉FPGA设计中常用运算方法,最终完成数字温湿度计总体设计。

  • 复习I2C总线工作原理及通信协议
  • 练习I2C接口驱动设计方法,完成温湿度传感器SHT-20驱动设计
  • 完成数字温湿度计总体设计

设计框图

根据前面的实验解析我们可以得知,该设计可以拆分成两个功能模块实现,

  • SHT20Driver:温湿度传感器SHT-20芯片I2C总线通信驱动模块。 * Calculate:完成温湿度码值到数码管显示之间的运算、转码和显示控制。 * bintobcd:将二进制数据转换成BCD码的方法。。 * Segmentscan:通过驱动扫描式数码管将温湿度数据显示出来。

Top-Down层次设计 模块结构设计

实验原理

SHT-20模块介绍

SHT-20是一款集成温度和湿度感测于一体的传感器芯片,采用3mm x 3mm贴片DFN封装,数字I2C总线接口,管脚功能描述如下:

管脚功能描述

SHT-20芯片典型电路连接如下:

典型电路连接

SHT-20芯片可以配置不同的分辨率模式,User Register中的bit0和bit7控制分辨率模式选择,默认状态温度T和湿度RH分别采用14bit和12bit模式

分辨率模式配置

不同的分辨率模式下,温度和湿度分辨率不同,默认状态温度和湿度分辨率分别为0.01℃和0.04%RH。

温度和湿度分辨率

不同的分辨率模式下,温度和湿度的转换时间也是不同的,默认状态温度和湿度最大转换时间分别为85ms和29ms。

温度和湿度转换时间

温度和湿度测量范围如下:

温度和湿度测量范围

SHT-20模块连接

STEP BaseBoard V3.0底板上的温湿度传感器SHT-20模块电路图如下(上拉电阻未显示):

SHT-20模块电路

上图为温湿度传感器SHT-20模块电路,与FPGA硬件接口有I2C总线(SCL、SDA),SHT2x 传感器包含电容式湿度传感器、带隙温度传感器和专用的模拟和数字集成电路-全部放在单 CMOSens®芯片上。这在精度和稳定性方面, 以及功耗最小的情况下, 都能产生无与伦比的传感器性能, SHT20的分辨率可以通过命令 (RH/T 的8/12 位到12/14 位) 进行更改, 并且校验和有助于提高通信的可靠性。

SHT-20模块驱动设计

智能接近系统设计实验中我们已经讲述学习过I2C总线驱动的设计,本实验可以上原来的基础上调整,首先来了解SHT-20时序中的参数要点。

SHT-20时序图

SHT-20时序参数

通过SHT-20时序参数了解,SHT-20支持I2C通信400KHz快速模式同时兼容100KHz的标准模式,还有两种模式下时序中的各种时间参数,所以通信速度不需要调整。

  • 普通列表项目分频得到400KHz的时钟,程序实现同智能接近系统设计实验。

I2C时序基本单元(启动、停止、发送、接收、发应答、读应答)协议里统一的,所以所以基本单元状态的设计也是不需要调整的。

  • 启动时序状态设计程序实现同智能接近系统设计实验。
  • 发送单元和读应答单元合并,时序状态设计程序实现同智能接近系统设计实验。
  • 接收单元和写应答单元合并,时序状态设计程序实现同智能接近系统设计实验。
  • 停止时序状态设计程序实现同智能接近系统设计实验。

SHT-20驱动的流程,手册上看到SHT-20芯片有很多指令,指令列表如下:

SHT-20基础指令

本实验涉及软件复位、温度测量和湿度测量三个操作分别查看其时序流程。

软件复位

软件复位操作时序流程如下:

软件复位操作

我们将这种操作设计成一个一个状态,程序实现如下:

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

温湿度测量

温度测量分为两种模式:hold master和no hold master,两种模式都可用但时序不同,本实验我们使用no hold master,湿度测量流程同温度测量流程,只是指令不一样。其操作时序流程如下:

温湿度采样操作

根据问湿度测量的时序流程,我们分为两部分,写指令部分和读数据部分,写指令部分比复位操作时序流程多了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的温度和湿度的数据还需要运算,运算后的数据是二进制数,想要显示在数码管上还需要BCD转码。先考虑运算:

温度运算 湿度运算

这里我们以温度的运算为例,FPGA不擅长小数的运算,我们可以将小数运算转换成整数运算处理,如下:

T = -46.85 + 175.72 * Tcode / 2^16 = (-4685 + 17572 * Tcode / 2^16) / 100

程序实现如下:

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];

上面程序中没有除以100的运算,没有集成专用除法器的FPGA实现除法运算非常麻烦,需要大量的逻辑资源且性能不佳,通常我们不在FPGA中直接做除法运算,上面程序中两个除法。

⑴除以2^16可以通过右移16位方式解决。

⑵除以100在二进制数中不好解决,而在BCD码的十进制数据很好处理,相当于小数点左移两位(十进制位),所以等完成BCD码后再来处理。

BCD转码在前面电压器实验中介绍过,这里直接例化,程序实现如下:

//进行BCD转码处理
//小数点在BCD码基础上左移2位,完成除以100的操作
//移位后T_data_bcd[19:16]百位,[15:12]十位,[11:8]个位,[7:0]两个小数位
wire [19:0] T_data_bcd;
bin_to_bcd u1
(
.rst_n              (rst_n      ),  //系统复位,低有效
.bin_code           (T_data_bin ),  //需要进行BCD转码的二进制数据
.bcd_code           (T_data_bcd )   //转码后的BCD码型数据输出
);
 
//4位数码管用于温度显示,保留1位小数
//若温度为负,将T_data_bcd[19:16]百位数据用数字A替换,同时把数码管A的字库显示负号
assign T_data = (b>=32'd4685)? T_data_bcd[19:4]:{4'ha,T_data_bcd[15:4]};
assign dot_en[7:4] = 4'b0010;  //小数点显示使能

设计到这里,将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;

综合后的设计框图如下:

RTL设计框图

实验步骤

  1. 双击打开Quartus Prime工具软件;
  2. 新建工程:File → New Project Wizard(工程命名,工程目录选择,设备型号选择,EDA工具选择);
  3. 新建文件:File → New → Verilog HDL File,键入设计代码并保存;
  4. 设计综合:双击Tasks窗口页面下的Analysis & Synthesis对代码进行综合;
  5. 管脚约束:Assignments → Assignment Editor,根据项目需求分配管脚;
  6. 设计编译:双击Tasks窗口页面下的Compile Design对设计进行整体编译并生成配置文件;
  7. 程序烧录:点击Tools → Programmer打开配置工具,Program进行下载;
  8. 观察设计运行结果。

实验现象

将程序加载到FPGA,观察数码管显示,左边4位数码管显示温度,右边4位数码管显示湿度,用手接触温湿度传感器,观察显示变化。

实验现象