FPGA实验笔记_Vivado:DDS信号发生器;数码管;基于DHT11的温湿度传感器

  • Post author:
  • Post category:其他



目录


1、 FPGA的DDS信号发生器


1.1、DDS简介


1.2、ROM IP核的生成


1.3、波形数据的生成


1.4、 ROM的调用


1.5、 完整代码(包括拓展部分)


2、数码管显示


2.1、数码管简要说明


2.2、SM410564


3、基于DHT11的温湿度传感器


3.1、DHT11


3.2、基本思路


3.3、数据分离模块(BTD)


3.4、数据转换模块(SMG)


3.5、DHT11控制模块


3.5.1、上升、下降沿的判定


3.5.2、端口IO状态控制


3.5.3、状态判断


3.5.4、数据读入


3.5.5、完整代码


3.6、TOP


3.7、结果展示


1、 FPGA的DDS信号发生器

1.1、DDS简介

DSS全称为“直接数字式频率合成”,通过逻辑高低电平的相加合成数字信号,在将数信信号转换,生成模拟信号。

DSS信号发生器的实现主要分为三个步骤:

  1. 生成所需波形数据并储存到IP核“ROM”中;
  2. 读取波形数据并输出数字信号;
  3. 将数字信号通过DA转换模块转换成模拟信号。

1.2、ROM IP核的生成

调用ROM,首先需要生成一个ROM。步骤如下:

1.         PROJECT MANAGER — IP Catalog;

2.        在IP Catalog界面搜索ROM,选择RAMs & ROMs & BRAM — Block Memory Generator;

3.        选择所需要的Memory Type ,这里我们选择Single Port ROM;

4.      设定Memory Size,根据所需要的Width 与 Depth,这里我们由于所用的波形数据为256个采样点,故设定Width = 8;Depth = 256;

5.        设定波形文件,在Memory Initialization中勾选 Load Init File,然后在Coe File一栏选择对于的文件,随后点击OK,这样ROM就生成完毕了。

调用端口如下:

rom_name rom_name(
.clka( ),
//input ck
.addra( ),
//input address
.douta( )
//output data
);

需要注意,rom的地址从0开始,依次向上加1。如0地址的下一位地址为1。

1.3、波形数据的生成

波形数据生成较为简单,MATLAB代码如下:

clc;
clear all;
N=2^8;
A=2^8;%振幅
f=1;9
F1=0;%相位
F2=256;
n=[0:N-1];
s1=round((A/2)*sin(2*pi*n/N+pi*F1)+F2/2);%256/2为0    
fild=fopen('wave_dds_sin.coe','wt');
fprintf(fild,'memory_initialization_radix=10;\n');
fprintf(fild,'memory_initialization_vector=\n');
for i=1:N
    if(s1(i)==256)
            s1(i)=255;
    end
    fprintf(fild,'%d,\n',s1(i));
end
fclose(fild);
plot(s1)

通过程序生成单周期有256个数据点的正弦波,A为峰峰值。

需要注意最小值为0,而最大值并非256,而是255,因为ROM所设置的Width为8,仅能储存八位二进制数,而256为“ ‘b100000000 ” 。若最大值为256,则会在ROM选择信号源文件时报错。

1.4、 ROM的调用

假设系统时钟频率为50MHz,一个周期输出一个数据点,则输出波形频率为

Fout = 50M/256 Hz = 195312.5Hz

若想要改变输出波形的频率,可以生成一个相位累加器,俗称加法器,同时设置一个频率控制字Fword来控制加法器累加的速度。

虽然在固定的时钟频率下加法器每次累加的速度是一样的,但是可以调整每次累加的步长来控制其达到最大值的速度。

频率控制字、加法器长度(位数)N、时钟频率以及输出波形频率关系如下:

Fword * clk = Fout * 2 ^ N

加法器控制程序如下:

    always@(posedge dac_clk or negedge rst )//计数器,越界归零, frequency = 50 MHz
        begin
            if(rst)   begin//当 rst = 1
                Fcnt <= 32'd0;
            end
            else//当rst等于0时     
                Fcnt <= Fcnt + Fword;
        end

需要注意的是,由于程序中使用的频率控制字并不一定为整数,故在计算时会产生一定误差,但此误差基本可忽略不计。

由于所用的ROM地址为8位二进制数,在一个周期内需要从0变化到255 即 11111111 一次,而累加器同样需要变到 1111 1111……一次,故可以使用累加器的前八位作为调用ROM的地址。

        assign rom_addr = Fcnt[31:24] + Pword;

1.5、 完整代码(包括拓展部分)

module DDS_CHEN(
    input clk, //fpga clock
    input rst,//rst is 0 if no act
    input swi,
    input [3:0]bot,//按下时为高电平,逻辑1,控制频率以及幅度
    output dac_clk,
    output [7:0]sin_out,  
    output  [4:0] led
 ); 
    reg [31:0] Fword;
    wire [31:0] Pword;
    assign Pword = 8'b0;    
    wire clk50;//标准时钟
    wire clk10;//没啥用
    
    wire [7:0] wave_data_sin;//读取数据
    wire [7:0] wave_data_sq;
    
    reg [7:0] wave_data_r;//赋值数据;
    reg [31:0] Fcnt;//计数器
    wire [7:0] rom_addr;//地址    
    
    reg [4:0]led_r;//测试用
    
//实际时钟
    assign dac_clk = clk50;   //输出时钟
 //仿真时钟
    //assign dac_clk = clk;

//控频
    //50M clk 858993-10k 8589933-100k 429497-5k 85899-1k 4295-50 相位累加器步长误差最大为7.6E-6
    //Fword = fout*(2^n)/fclk   n = 32 fclk = 50MHz
    always@( posedge clk50)
    begin
        casex(bot)
            4'b11xx: Fword = 4295;//50
            4'b10xx: Fword = 85899;//1k
            4'b01xx: Fword = 429497;//5k
            4'b00xx: Fword = 858993;//10k
            default :Fword = 858993;
        endcase
    end
        
//地址控制 
    always@(posedge dac_clk or negedge rst )//计数器,越界归零, frequency = 50 MHz
        begin
            if(rst)   begin//当 rst = 1
                Fcnt <= 32'd0;
            end
            else//当rst等于0时     
                Fcnt <= Fcnt + Fword;
        end
        assign rom_addr = Fcnt[31:24] + Pword;//the first 8 bit as the address

//指示灯        
    always@(posedge dac_clk)
        begin
            if(!rst&&!bot)
                led_r <= 5'b1;
            else if(!rst&&bot[0])
                led_r <= 5'b00010;
            else if(!rst&&bot[1])
                led_r <= 5'b00100;
            else if(!rst&&bot[2])
                led_r <= 5'b01000;
            else if(!rst&&bot[3])
                led_r <= 5'b10000;
            else
                led_r <=0;
        end    
    
//赋值与控幅,控幅还可以通过外部电路改变电压来实现        
    always@(posedge dac_clk)
        begin
          case(swi)
          1'b0:
            casex(bot)
            4'bxx00 : wave_data_r <= wave_data_sin / 4;
            4'bxx01 : wave_data_r <= wave_data_sin / 2;
            4'bxx10 : wave_data_r <= (wave_data_sin / 4)*3 ;
            4'bxx11 : wave_data_r <= wave_data_sin ;
            endcase
          1'b1:
             casex(bot)
             4'bxx00 : wave_data_r <= wave_data_sq / 4;
             4'bxx01 : wave_data_r <= wave_data_sq / 2;
             4'bxx10 : wave_data_r <= (wave_data_sq / 4)*3 ;
             4'bxx11 : wave_data_r <= wave_data_sq;
             default: wave_data_r <= 114;
             endcase
           endcase
         end                
    assign sin_out = wave_data_r;
    assign led = led_r;
    
blk_mem_gen_0 blk_mem_gen_0 (
 //实际
 .clka(clk50), // input clka
 //仿真
 //.clka(clk),
 .addra(rom_addr), // input [8 : 0] addra
 .douta(wave_data_sin) // output [7 : 0] douta
);

blk_mem_gen_1 sq(
//仿真
//.clka(clk),
//实际
.clka(clk50),
.addra(rom_addr),
.douta(wave_data_sq)
);
clk_wiz_0 clk_wiz_0 (
    .clk_in1(clk),
    .resetn(1'b1),
    .clk_out50M(clk50),
    .clk_out10M(clk10),
    .locked()
    );
endmodule

2、数码管显示

2.1、数码管简要说明

一般而言,数码管可以分为共阴、共阳两种。

共阴为数码管的八个二极管共用一个阴极,此时控制不同二极管阳极的电平就可以控制其显示状态,当阳极与阴极形成一定电压差时,二极管导通发光,即高电平发光。一般而言,共极低电平表示数码管工作。

共阳为数码管的八个二极管共用一个阳极。一般当共阳极为高电平时二极管工作,相应二极管阴极为低电平时发光。即低电平发光。

共阳、共阴极的数码管真值表正好完全相反。

2.2、SM410564

本次实验所使用的是四位共阳数码管SM410564。当正视显示面时,其管脚约束如下:


s1~s4为四个共阳极,控制四个显示数位,可以同时为高电平,由于共极管脚分别独立,故电平高低与否与其他管脚无关。

a~g为显示数字的七个发光二极管的阴极管脚,dp为小数点位置发光二极管的阴极管脚。

整个模块共由4*8共32个发光二极管组成,显然控制发光二极管的8个阴极管脚不足以同时显示四个不同的符号,但可以同时显示相同的符号。故采用交替显示的方式进行输出。

交替显示通过对于每一时钟周期,仅让一个共极为高电平,并将相应真值赋值,在下一个周期相邻共极变为高电平,当前共极变为低电平,并重新进行赋值,如此循环。假设时钟周期为50M赫兹,则整个显示模块的刷新率即为50Mhz, 而对于单一的显示位,刷新率为50M/4 = 12.5MHz远大于人眼25Hz或是80Hz的基本要求。

完整代码如下:(刷新了为1lHz,输出仅为6.6.6.6.)

`timescale 1ns / 1ps

module SMG(
    input clk,
    input rst,
    output [3:0] numo,
    output [6:0] datao,
    output pointo
    );
    wire clk50;
    reg [3:0]numr;
    wire [3:0]numw;
    reg [6:0]datar;
    wire[6:0]dataw;
    reg 	clk_s;		
    reg       [32:0]        cnt;//微妙时钟计数器
    reg pointr;
    
    assign pointo = pointr;
    assign numo = numr;
    assign datao = datar;
    
    always@(posedge clk_s)//0.001s
    begin
        datar = 7'b0000_010;//6
        pointr = 1'b0;
        if(rst)
            numr = 4'b0001;
        else if(numr ==4'b1000)
            numr =  4'b0001;//从s4向s1刷新;
        else if(numr !=4'b1000)
                numr = numr << 1;
    end
 //时钟   
    always@(posedge clk50 or negedge rst)
    begin//一个周期为25时钟周期,0.02*25*10^6=0.5us,一个周期0.5us
        if(rst)
            cnt <= 32'd0;
        else if(cnt == 32'd24_999)//毫秒计数器
            cnt <= 32'd0;
        else 
            cnt <= cnt + 1'b1;
    end
    //微秒时钟2,由cnt控制
    always@(posedge clk50 or negedge rst)
    begin
        if(rst)
            clk_s <= 1'b0;
        else if(cnt== 32'd24_999)//将cnt-us换为cnt,此时0.5us翻转一次,clk-us一个周期为0.001s
            clk_s <= ~ clk_s;
        else 
            clk_s <= clk_s;
    end
 clk_wiz_0 clk_wiz_0(
    .reset(1'b0),
    .clk_in1(clk),

3、基于DHT11的温湿度传感器

3.1、DHT11

详情参考技术手册。

采用单线双向串口通信,一次性传输四十个字节的二进制数据,高位先输出。数据每八位分为一组,第一组为温度整数;第二组为温度小数;第三组为湿度整数;第四组为湿度小数;第五组为校验码,在正常情况下第五组应为前四组数据之和。

但对于简单的温湿度测量来说,实际上有用的只有前24位,即前三组。因为在不拓展的情况下,湿度小数的部分输出数据一直为0,即8’b 0000_0000。

DHT11的通信串口需要并联一个5K欧姆左右的上拉电阻使其在非工作状态保持高电平,其控制主要分为6部分:

  1. 主机拉低电平18ms以上,推荐为20ms,但不要太长;
  2. 主机拉高20~40us等待辅机(DHT11)应答,推荐为30us;
  3. 从机拉低80us;
  4. 从机拉高80us,并进入数据传输阶段;
  5. 传输数据;
  6. 拉低50us后结束。

对于数据采集中0或1的判断,通过每个字节高电平的时间来判断。每个字节由恒定的50us低电平开始,若高电平持续时间为20~28us,则为0;若高电平持续时间为70us,则为1。

在此时需要注意两个问题:

  1. 由于0与1的高电平持续时间相差较大,故可以用一个阈值来区分0与1,如小于50us就为0,大于50us就为1;
  2. 0与1的判定一定要在高电平结束后,也就是端口的下降沿进行,负责会出现错误数据。

3.2、基本思路

对于整个程序,将其分为三个模块进行。

  1. 首先DHT11控制模块读取数据,并将数据传输给数据转换模块;
  2. 数据转换将一组八位二进制数据转换为两个四位二进制数据,分别为各位数据与十位数据,以配合数码管输出十进制数,并将数据传输给数码管模块;
  3. 数码管模块将二进制数据转换为共阳二极管所对应的格式,并与相应的显示匹配,最终输出。

此外,额外设置一个按钮flag_key来控制温湿度的切换显示。

3.3、数据分离模块(BTD)

此模块较为简单,有四个输入:时钟clk、复位rst、整数部分输入int_in、小数部分输入dec_in;四个输出:int_out1,2和dec_out1,2,分别对应整数、小数的十位与个位。

十位分离过程直接通过对10(’b1010)整除即可以实现;个位分离过程通过原数据减去十倍的十位部分即可得到。

代码如下:

`timescale 1ns / 1ps
module BTD(
    input clk50,
    input [7:0] int_in,
    input [7:0] dec_in,
    output [3:0] int_out1,
    output [3:0] int_out2,
    output [3:0] dec_out1,
    output [3:0] dec_out2,    
    input rst
    );
//定义       
    reg [7:0]intbr;
    reg [3:0]in1;
    reg [3:0]in2;
    
    reg [3:0]de1;
    reg [3:0]de2;
    reg [7:0]decbr;  
 //整数位   
    always@(posedge clk50)begin
    intbr = int_in;
    in1 = intbr/4'b1010;
    in2 = intbr-in1*4'b1010;
    end
//小数位    
    always@(posedge clk50)begin
    decbr = dec_in;
    de1 = decbr/4'b1010;
    de2 = decbr-de1*4'b1010;
    end
 //赋值   
    assign int_out1 = in1;
    assign int_out2 = in2;
    assign dec_out1 = de1;
    assign dec_out2 = de2;
endmodule

3.4、数据转换模块(SMG)

数据转换模块结构简单,主体部分即为四个数据选择器。

需要注意的仅有共阳数码管为低电平发光以及数据赋值的顺序为从右往左为从低到高。

代码如下:

`timescale 1ns / 1ps
module SMG(
        input   clk50,
        input   rst,
        input   data_flag,//0-hu    1-te//没有用
        input   [3:0] int_in1,//D
        input   [3:0] dec_in1,//D
        input   [3:0] int_in2,//D
        input   [3:0] dec_in2,//D                    
        output  [6:0] data_out1,
        output  [6:0] data_out2,
        output  [6:0] data_out3,
        output  [6:0] data_out4
        );
//定义        
        reg    [3:0]numr;
        reg    [6:0]datar1;
        reg    [6:0]datar2;
        reg    [6:0]datar3;
        reg    [6:0]datar4;
        reg    [31:0]cnt;
        reg    [3:0]temp_data_1;
        reg    [3:0]temp_data_2;
        reg    [3:0]temp_data_3;
        reg    [3:0]temp_data_4;
        
        reg     int1;
        reg     int2;
        reg     dec1;
        reg     dec2;
//输出赋值       
        assign data_out1 = datar1;
        assign data_out2 = datar2;
        assign data_out3 = datar3;
        assign data_out4 = datar4;
//数据选择器     
//小数点显示位数选择     转换数据选择
    always@(posedge clk50)//0.02us 一次
        begin
                    temp_data_1 = int_in1;   
                    temp_data_2 = int_in2;
                    temp_data_3 = dec_in1;
                    temp_data_4 = dec_in2;        
        end
//数据选择器     数据转换
    always@(posedge clk50)
        begin
            case(temp_data_1)//gfedcba
            4'b0000:datar1 = 7'b1000000;//0
            4'b0001:datar1 = 7'b1111001;
            4'b0010:datar1 = 7'b0100100;
            4'b0011:datar1 = 7'b0110000;
            4'b0100:datar1 = 7'b0011001;
            4'b0101:datar1 = 7'b0010010;
            4'b0110:datar1 = 7'b0000010;
            4'b0111:datar1 = 7'b1111000;
            4'b1000:datar1 = 7'b0000000;
            4'b1001:datar1 = 7'b0010000;
            endcase
        end
    always@(posedge clk50)
        begin
            case(temp_data_2)//gfedcba
            4'b0000:datar2 = 7'b1000000;//0
            4'b0001:datar2 = 7'b1111001;
            4'b0010:datar2 = 7'b0100100;
            4'b0011:datar2 = 7'b0110000;
            4'b0100:datar2 = 7'b0011001;
            4'b0101:datar2 = 7'b0010010;
            4'b0110:datar2 = 7'b0000010;
            4'b0111:datar2 = 7'b1111000;
            4'b1000:datar2 = 7'b0000000;
            4'b1001:datar2 = 7'b0011000;
            endcase
        end
    always@(posedge clk50)
        begin
            case(temp_data_3)//gfedcba
            4'b0000:datar3 = 7'b1000000;//0
            4'b0001:datar3 = 7'b1111001;
            4'b0010:datar3 = 7'b0100100;
            4'b0011:datar3 = 7'b0110000;
            4'b0100:datar3 = 7'b0011001;
            4'b0101:datar3 = 7'b0010010;
            4'b0110:datar3 = 7'b0000010;
            4'b0111:datar3 = 7'b1111000;
            4'b1000:datar3 = 7'b0000000;
            4'b1001:datar3 = 7'b0011000;
            endcase
        end
    always@(posedge clk50)
       begin
           case(temp_data_4)//gfedcba
           4'b0000:datar4 = 7'b1000000;//0
           4'b0001:datar4 = 7'b1111001;
           4'b0010:datar4 = 7'b0100100;
           4'b0011:datar4 = 7'b0110000;
           4'b0100:datar4 = 7'b0011001;
           4'b0101:datar4 = 7'b0010010;
           4'b0110:datar4 = 7'b0000010;
           4'b0111:datar4 = 7'b1111000;
           4'b1000:datar4 = 7'b0000000;
           4'b1001:datar4 = 7'b0011000;
           endcase
       end       
endmodule

3.5、DHT11控制模块

DHT11的控制主要有四个重点:

3.5.1、上升、下降沿的判定

通过两个指示信号来实现,代码如下:

//通过dht11的上升下降沿来控制步进次数,确保在输入一个字节是bitcnt等累加器只会加一次,同时控制判别器在电平下降时才进行判断
always@(posedge clk_us or negedge sys_rst)
begin
	if(sys_rst)//复位是两个都是1
		begin
			dht11_reg1 <= 1'b1;
			dht11_reg2 <= 1'b1;
		end
	else 
		begin
			dht11_reg1 <= dht11;//读入数据,非上升下降沿时,二者相等,pos、neg均为0,
			//上升沿:reg1 = 1,reg2 = 0,pos = 1,neg = 0;
			//下降沿:reg1 = 0,reg2 = 1,pos = 0,neg = 1;
			dht11_reg2 <= dht11_reg1;//reg2 = reg1
		end
end
assign dht11_pos = (dht11_reg1) & (~dht11_reg2);//posedge,时钟上升沿
assign dht11_neg = (~dht11_reg1) & (dht11_reg2);//negedge,时钟下降沿

3.5.2、端口IO状态控制

主机仅在前两个阶段生成开始指示信号是控制端口,其他阶段仅作为接收端。

IO状态的控制可以通过对dht11进行赋值来实现,当主机输出时dht11为具体值,当辅机输出时dht11为未知量

1’bz

代码如下:

//数值判定 IO状态 与 输出值
always@(posedge clk_us or negedge sys_rst)
begin
	if(sys_rst)begin
		dht11_en <= 1'b0;
		dht11_out <= 1'b1;
		end
    else if(state == WAIT_1S)//等待系统稳定
    begin
        dht11_en <= 1'b1;
        dht11_out <= 1'b1;
    end
	else if(state == START)//wait 1s or start 
	begin
		dht11_en <= 1'b1;
		dht11_out <= 1'b0;
		if(cnt_us ==  LOW_18MS_MAX)
		  dht11_out <= 1'b1;
	end	  
	else //其他状态
	begin
		dht11_en <= 1'b0;	
		dht11_out <=1'b0;
    end
end
//赋值
assign dht11 = dht11_en ? dht11_out : 1'bz;

3.5.3、状态判断

状态判断主要通过两个计数器进行,一个记录高电平以及非工作时间、一个记录低电平时间。状态转换的判定通过上升、下降沿,计数器取值来进行,每当状态进行转换,计数器清零一次。

计数器控制代码:

//DHT11状态变量控制	
always@(posedge clk_us or negedge sys_rst)
begin
	if(sys_rst)//复位
		begin
			cnt_low = 20'd0;
			cnt_us  = 20'd0;
		end
	else
	begin
		case(state)
			WAIT_1S:begin//初始状态,
				if(cnt_us == WAIT_1S_MAX)//若满足时间1s
					cnt_us = 20'd0;
				else //not 到1s时,每1us cnt us+1,要加1E6次
					cnt_us = cnt_us + 1'b1;
			end
            START  :begin//主机拉低频率18ms
				if(cnt_us == LOW_18MS_MAX)//在清零后cnt us 重新开始计数,直到到达18ms
					cnt_us = 20'd0;
				else 
					cnt_us = cnt_us + 1'b1;
			end
            DLY_1  :begin//等待从机发出信号,延时30us后 cntus清零
				if(cnt_us == 20'd29)
					cnt_us = 20'd0;
				else 
					cnt_us = cnt_us + 1'b1;
			end
            REPLY  :begin//从机发射响应信号
				if(dht11_pos == 1'b1 && (cnt_low > 80))//从机相应80us后,清零、进入下一状态
					begin
						cnt_low = 20'd0;
						cnt_us  = 20'd0;
					end
				else if(dht11 == 1'b0)//DATA为低电平,但为满足状态,持续计数
					begin
						cnt_low = cnt_low + 1'b1;//低电平时间计数
						cnt_us  = cnt_us + 1'b1;//时间计数
					end
				else if(cnt_us > 1000)//若超过1ms,归零,重新开始
					begin
						cnt_low <= 20'd0;
						cnt_us  <= 20'd0;
					end
				else //系统不稳定,计时但是不记录低电平时间
					begin
						cnt_low = cnt_low;
						cnt_us  = cnt_us + 1'b1;
					end
			end
            DLY_2  :begin//等待
				if(dht11_neg == 1'b1 && (cnt_us > 80))//拉高80us响应后,进入读取阶段
					cnt_us = 20'd0;
				else 
					cnt_us  = cnt_us + 1'b1;
			end
            RD_DATA:begin//传输信号数据
				if(dht11_neg == 1'b1 || dht11_pos == 1'b1)//df与dr均为1时归零(电平变化时)
					begin 
					cnt_us  = 1'b0;
					end
				else 
					cnt_us = cnt_us + 1'b1;//计时
			end
			default://其他情况归零
				begin
						cnt_low = 20'd0;
						cnt_us  = 20'd0;
				end
		endcase
	end
end

状态控制代码:

//状态控制,控制变量在后续模块
always@(posedge clk_us or negedge sys_rst)//微妙时钟或复位
begin
	if(sys_rst)//rst = 0
		state <= WAIT_1S; //等待1s,初始状态为state = wait 1s
	else 
	begin
		case(state)
		 WAIT_1S:begin
		 	if(cnt_us == WAIT_1S_MAX)//1s后,state = start,同时cnt us清零
		 		state = START;
		 	else 
		 		state = WAIT_1S;
		 end
		 START	:begin//主机发出开始信号
		 	if(cnt_us ==  LOW_18MS_MAX)//到达18ms后cnt us清零,进入等待1状态
		 		state = DLY_1; 
		 	else 
		 		state =  START;
		 end
		 DLY_1  :begin//拉高30us表示结束
		 	if(cnt_us == 20'd30)//10us后进入从机相应状态
		 		state = REPLY;
		 	else
		 		state = DLY_1;
		 end
		 REPLY  :begin//从机回应
		 	if(dht11_pos == 1'b1 && (cnt_low > 80))//低电平计数80us以上,进入等待2状态,等待传感器数据
		 		state = DLY_2;
		 	else if(cnt_us > 1000)//表示等待了1MS,重新开始
		 		state = START;
		 	else 
		 		state = REPLY;
		 end
		 DLY_2  :begin//拉高80us后进入数据读取
		 	if(dht11_neg == 1'b1 && cnt_us >80)
		 		state = RD_DATA;
		 	else 
		 		state = DLY_2;
		 end
		 RD_DATA:begin
		 	if(bit_cnt == 40 && dht11_pos == 1'b1)//读取完40bit数据后重新开始
		 		state = START;//读完后立刻返回开始
		    else//否则持续进行 	
		 		state = RD_DATA;
		 end
		default:state = WAIT_1S;
		endcase
	end
end

3.5.4、数据读入

代码如下:

//依次对data——temp写入信号数据,输出data-temp
always@(posedge clk_us or negedge sys_rst)
begin
	if(sys_rst)
		data_temp <= 40'd0;
	else if(state == RD_DATA && dht11_neg == 1'b1 && cnt_us< 50 ) //下降沿是出发,确保高点平占时完全被记录
		data_temp[39 - bit_cnt] <= 1'b0; //DHT11先输出高位,因此从高位向低位依次赋值
	else if(state == RD_DATA && dht11_neg == 1'b1 && cnt_us > 50)
		data_temp[39 - bit_cnt] <= 1'b1;//小于50us当成0,大于50us当成1
	else 
		data_temp <= data_temp;
end

//数据校验和赋值,输出data
always@(posedge clk_us or negedge sys_rst)
begin
	if(sys_rst)
		data <= 32'd0;
	else if(data_temp[7:0] == data_temp[39:32] + data_temp[31:24] + data_temp[23:16] + data_temp[15:8])
		data <= data_temp[39:8];//校验位无误时将数据赋值给data
	else 
		data <= data;
end

3.5.5、完整代码

第一组:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/11 15:49:57
// Design Name: 
// Module Name: dht11_ctrl
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module dht11_ctrl(sys_clk,sys_rst,data_flag,dht11,data_out, test_leds);
	
	input  				sys_clk;//clk-50Mhz
	input 				sys_rst;//rst,触发为1
	input 				data_flag;//控制输出温度还是湿度
	inout 				dht11;//既可输入又可输出,即为DATA线	
	output 	   [15:0]	data_out;//输出16位信号
	output     [3:0] test_leds;
	
//状态定义
	parameter          WAIT_1S	 =  6'b000_001,//上电等待1s状态
						START    =  6'b000_010,//主机拉低20ms,发送开始信号状态
						DLY_1    =  6'b000_100,//等待从机答应
						REPLY    =  6'b001_000,//从机对主机发出发送信号
						DLY_2    =  6'b010_000,//等待主机回应
						RD_DATA  =  6'b100_000;//开始传输数据
						
	parameter			WAIT_1S_MAX	= 32'd999_999,
						LOW_18MS_MAX = 20'd19_999;
		
	wire 				dht11_pos;
	wire				dht11_neg;
		
    reg 				clk_us;		
	reg 	  [4:0]		cnt;//微妙时钟计数器
	reg 	  [5:0]		state;
	reg 	  [19:0]	cnt_us;//微秒计时器
	reg 	  [19:0]	cnt_low;//低电平计时器
	reg 				dht11_reg1;//时钟沿计数器
	reg 				dht11_reg2;//时钟沿计数器
	reg 	  [5:0]		bit_cnt;//数据写入计数器
	reg  	  [39:0]	data_temp;//全部数据
	reg 	  [31:0]	data;//校验后数据
	reg 				dht11_en;
	reg 				dht11_out;
	reg    [3:0] led;
//微秒时钟1,一个周期0.5us
always@(posedge sys_clk or negedge sys_rst)
begin//一个周期为25时钟周期,0.02*25=0.5us,一个周期0.5us
	if(sys_rst)
		cnt <= 5'd0;
	else if(cnt == 5'd24)//微秒计数器
		cnt <= 5'd0;
	else 
		cnt <= cnt + 1'b1;
end
//微秒时钟2,由cnt控制
always@(posedge sys_clk or negedge sys_rst)
begin
	if(sys_rst)
		clk_us <= 1'b0;
	else if(cnt== 5'd24)//将cnt-us换为cnt,此时一个没0.5us翻转一次,clk-us一个周期为1us
		clk_us <= ~ clk_us;
	else 
		clk_us <= clk_us;
end

//状态控制,控制变量在后续模块
always@(posedge clk_us or negedge sys_rst)//微妙时钟或复位
begin
	if(sys_rst)//rst = 0
		state <= WAIT_1S; //等待1s,初始状态为state = wait 1s
	else 
	begin
		case(state)
		 WAIT_1S:begin
		 	if(cnt_us == WAIT_1S_MAX)//1s后,state = start,同时cnt us清零
		 		state = START;
		 	else 
		 		state = WAIT_1S;
		 end
		 START	:begin//主机发出开始信号
		 	if(cnt_us ==  LOW_18MS_MAX)//到达18ms后cnt us清零,进入等待1状态
		 		state = DLY_1; 
		 	else 
		 		state =  START;
		 end
		 DLY_1  :begin//拉高30us表示结束
		 	if(cnt_us == 20'd30)//10us后进入从机相应状态
		 		state = REPLY;
		 	else
		 		state = DLY_1;
		 end
		 REPLY  :begin//从机回应
		 	if(dht11_pos == 1'b1 && (cnt_low > 80))//低电平计数80us以上,进入等待2状态,等待传感器数据
		 		state = DLY_2;
		 	else if(cnt_us > 1000)//表示等待了1MS,重新开始
		 		state = START;
		 	else 
		 		state = REPLY;
		 end
		 DLY_2  :begin//拉高80us后进入数据读取
		 	if(dht11_neg == 1'b1 && cnt_us >80)
		 		state = RD_DATA;
		 	else 
		 		state = DLY_2;
		 end
		 RD_DATA:begin
		 	if(bit_cnt == 40 && dht11_pos == 1'b1)//读取完40bit数据后重新开始
		 		state = START;//读完后立刻返回开始
		    else//否则持续进行 	
		 		state = RD_DATA;
		 end
		default:state = WAIT_1S;
		endcase
	end
end

//DHT11状态变量控制	
always@(posedge clk_us or negedge sys_rst)
begin
	if(sys_rst)//复位
		begin
			cnt_low = 20'd0;
			cnt_us  = 20'd0;
		end
	else
	begin
		case(state)
			WAIT_1S:begin//初始状态,
				if(cnt_us == WAIT_1S_MAX)//若满足时间1s
					cnt_us = 20'd0;
				else //not 到1s时,每1us cnt us+1,要加1E6次
					cnt_us = cnt_us + 1'b1;
			end
            START  :begin//主机拉低频率18ms
				if(cnt_us == LOW_18MS_MAX)//在清零后cnt us 重新开始计数,直到到达18ms
					cnt_us = 20'd0;
				else 
					cnt_us = cnt_us + 1'b1;
			end
            DLY_1  :begin//等待从机发出信号,延时30us后 cntus清零
				if(cnt_us == 20'd29)
					cnt_us = 20'd0;
				else 
					cnt_us = cnt_us + 1'b1;
			end
            REPLY  :begin//从机发射响应信号
				if(dht11_pos == 1'b1 && (cnt_low > 80))//从机相应80us后,清零、进入下一状态
					begin
						cnt_low = 20'd0;
						cnt_us  = 20'd0;
					end
				else if(dht11 == 1'b0)//DATA为低电平,但为满足状态,持续计数
					begin
						cnt_low = cnt_low + 1'b1;//低电平时间计数
						cnt_us  = cnt_us + 1'b1;//时间计数
					end
				else if(cnt_us > 1000)//若超过1ms,归零,重新开始
					begin
						cnt_low <= 20'd0;
						cnt_us  <= 20'd0;
					end
				else //系统不稳定,计时但是不记录低电平时间
					begin
						cnt_low = cnt_low;
						cnt_us  = cnt_us + 1'b1;
					end
			end
            DLY_2  :begin//等待
				if(dht11_neg == 1'b1 && (cnt_us > 80))//拉高80us响应后,进入读取阶段
					cnt_us = 20'd0;
				else 
					cnt_us  = cnt_us + 1'b1;
			end
            RD_DATA:begin//传输信号数据
				if(dht11_neg == 1'b1 || dht11_pos == 1'b1)//df与dr均为1时归零(电平变化时)
					begin 
					cnt_us  = 1'b0;
					end
				else 
					cnt_us = cnt_us + 1'b1;//计时
			end
			default://其他情况归零
				begin
						cnt_low = 20'd0;
						cnt_us  = 20'd0;
				end
		endcase
	end
end

reg ct;
always@(posedge clk_us)
    begin
    if(cnt_us == 20'b1000)
        ct = 1;
    if(ct == 1)
        led[0] = 1;
    else if(ct == 0)
        led[0] = 0;
    end
//通过dht11的上升下降沿来控制步进次数,确保在输入一个字节是bitcnt等累加器只会加一次,同时控制判别器在电平下降时才进行判断
always@(posedge clk_us or negedge sys_rst)
begin
	if(sys_rst)//复位是两个都是1
		begin
			dht11_reg1 <= 1'b1;
			dht11_reg2 <= 1'b1;
		end
	else 
		begin
			dht11_reg1 <= dht11;//读入数据,非上升下降沿时,二者相等,pos、neg均为0,
			//上升沿:reg1 = 1,reg2 = 0,pos = 1,neg = 0;
			//下降沿:reg1 = 0,reg2 = 1,pos = 0,neg = 1;
			dht11_reg2 <= dht11_reg1;//reg2 = reg1
		end
end
assign dht11_pos = (dht11_reg1) & (~dht11_reg2);//posedge,时钟上升沿
assign dht11_neg = (~dht11_reg1) & (dht11_reg2);//negedge,时钟下降沿

//数据位数控制,输出bit-cnt
always@(posedge clk_us or negedge sys_rst)
begin
	if(sys_rst)
		bit_cnt <= 6'd0;
	else if(bit_cnt == 40 && dht11_pos == 1'b1)
		bit_cnt <= 6'd0;
	else if((state == RD_DATA) && (dht11_neg == 1'b1))
		bit_cnt <= bit_cnt + 1'b1;//当状态为写入且dht11-neg = 1时,bit—cnt依次变化
	else
		bit_cnt <= bit_cnt;
end

//依次对data——temp写入信号数据,输出data-temp
always@(posedge clk_us or negedge sys_rst)
begin
	if(sys_rst)
		data_temp <= 40'd0;
	else if(state == RD_DATA && dht11_neg == 1'b1 && cnt_us< 50 ) //下降沿是出发,确保高点平占时完全被记录
		data_temp[39 - bit_cnt] <= 1'b0; //DHT11先输出高位,因此从高位向低位依次赋值
	else if(state == RD_DATA && dht11_neg == 1'b1 && cnt_us > 50)
		data_temp[39 - bit_cnt] <= 1'b1;//小于50us当成0,大于50us当成1
	else 
		data_temp <= data_temp;
end
always@(posedge clk_us)
    begin
        if(data_temp == 0)
            led[1] = 1;
    end
//数据校验和赋值,输出data
always@(posedge clk_us or negedge sys_rst)
begin
	if(sys_rst)
		data <= 32'd0;
	else if(data_temp[7:0] == data_temp[39:32] + data_temp[31:24] + data_temp[23:16] + data_temp[15:8])
		data <= data_temp[39:8];//校验位无误时将数据赋值给data
	else 
		data <= data;
end

//IO控制
always@(posedge clk_us or negedge sys_rst)
begin
	if(sys_rst)begin
		dht11_en <= 1'b0;
		dht11_out <= 1'b1;
		end
    else if(state == WAIT_1S)//等待系统稳定
    begin
        dht11_en <= 1'b1;
        dht11_out <= 1'b1;
    end
	else if(state == START)//wait 1s or start 
	begin
		dht11_en <= 1'b1;
		dht11_out <= 1'b0;
		if(cnt_us ==  LOW_18MS_MAX)
		  dht11_out <= 1'b1;
	end	  
	else //其他状态
	begin
		dht11_en <= 1'b0;	
		dht11_out <=1'b0;
    end
end

assign dht11 = dht11_en ? dht11_out : 1'bz;
//当dht11en = 1时,dht11 = dht11 out,当en = 0 时,dht11 = 未知
//start时dht11 受主机控制,等于0--低电平
//其他时候,dht11为未知量,由辅机控制
reg [15:0] datar;
always@(posedge clk_us or negedge sys_rst)//温湿度选择器,输出data-out
begin
	if(sys_rst)
		datar <= 16'd0;//十六位
	else if(data_flag == 1'b0)//data为32位 整温-小温-整湿-小湿
		datar <= data[31:16];//湿度数据
	else if(data_flag == 1'b1)
		datar <= data[15:0];//湿度数据
	else 
		datar <= data_out;
end
assign data_out = datar;
always@(posedge clk_us)
    begin
        if(data == 0 /*&& data_temp != 0*/)
            led[2] = 1;
        else if(datar == 0)
            led[3] = 1;
    end

endmodule

第二组:

`timescale 1ns / 1ps

module dht11_2(
	input	sys_clk		,	//system clock
	input	sys_rst_n	,	//system reset negedge	
	inout	dht11_data	,	//dht11 inout port
	output	reg	[39:0]	t_h_data
);
//tate code
parameter	WAIT			=	6'b000_001,//wait state 2s
			START			=	6'b000_010,//make bus low 20ms
			WAIT_RES		=	6'b000_100,//wait respond
			RES_LOW			=	6'b001_000,//respond low
			RES_HIGH		=	6'b010_000,//respong high
			REC_DATA		=	6'b100_000;//receive datas
//time parameter
parameter	CNT_2S_MAX		=	100_000_000	,
			CNT_20MS_MAX	=	1_000_000	,
			CNT_1US_MAX		=	50			;
//state define
reg	[5:0] 	state_cur;//current state
reg [5:0] 	state_nex;//next state
//lag define
wire		end_2s		;	//wait 2s end
wire		end_20ms    ;	//wait 20ms end
wire		res_ok      ;	//respond ok
wire		res_no      ;	//no respond
wire		end_res_low ;	//wait respond low end 83us
wire		end_res_high;	//wait respond high end 87us
wire		end_rec     ;	//data receive end 40bits
//dht11
reg			dht11_data_r1;
reg			dht11_data_r2;
wire		dht11_posedge;
wire		dht11_negedge;
reg			data;
reg			output_en;
wire		check;//校验			
reg	[39:0]	t_h_data_temp;//温湿度数据
//计数器
reg	[26:0]	cnt_2s;
reg [19:0]	cnt_20ms;
reg	[6:0]	cnt_nus;
reg	[5:0]	cnt_1us;
reg			cnt_us_rst;
reg	[5:0]	cnt_bit;

//条件判断
assign	end_2s 			= (state_cur == WAIT && cnt_2s == CNT_2S_MAX - 1'b1) ? 1'b1 : 1'b0;
assign	end_20ms 		= (state_cur == START && cnt_20ms == CNT_20MS_MAX - 1'b1) ? 1'b1 : 1'b0;
assign	res_ok 			= (state_cur == WAIT_RES && cnt_nus < 20 && dht11_negedge) ? 1'b1 : 1'b0;
assign 	res_no 			= (state_cur == WAIT_RES && cnt_nus > 20) ? 1'b1 : 1'b0;
assign 	end_res_low 	= (state_cur == RES_LOW && cnt_nus > 70 && dht11_posedge) ? 1'b1 : 1'b0;
assign 	end_res_high 	= (state_cur == RES_HIGH && cnt_nus > 70 && dht11_negedge) ? 1'b1 : 1'b0;
assign	end_rec 		= (state_cur == REC_DATA && cnt_bit >= 40) ? 1'b1 : 1'b0;

//dht11传输上升、下降延判断
assign dht11_posedge = dht11_data_r1 & ~dht11_data_r2;
assign dht11_negedge = ~dht11_data_r1 & dht11_data_r2;
//生成
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(~sys_rst_n)begin
		dht11_data_r1 <= 1'b0;
		dht11_data_r2 <= 1'b0;
	end
	else begin
		dht11_data_r1 <= dht11_data;
		dht11_data_r2 <= dht11_data_r1;
	end
end
//信息校验
assign check = (t_h_data_temp[39:32]+t_h_data_temp[31:24]+
					t_h_data_temp[23:16]+t_h_data_temp[15:8] == t_h_data_temp[7:0])
					? 1'b1 : 1'b0;
//计数器群
always@(*)begin
	case(state_cur)
		WAIT		:	cnt_us_rst = 1'b1;
	    START		:	cnt_us_rst = 1'b1;
	    WAIT_RES	:	begin
			if(res_ok)
				cnt_us_rst = 1'b1;
			else
				cnt_us_rst = 1'b0;
		end
	    RES_LOW		:	begin
			if(end_res_low)
				cnt_us_rst = 1'b1;
			else
				cnt_us_rst = 1'b0;
		end
	    RES_HIGH	:	begin
			if(end_res_high)
				cnt_us_rst = 1'b1;
			else
				cnt_us_rst = 1'b0;
		end
	    REC_DATA	:	begin
			if(dht11_posedge || dht11_negedge)
				cnt_us_rst = 1'b1;
			else
				cnt_us_rst = 1'b0;
		end
		default		:cnt_us_rst = 1'b1;
	endcase
end
//cnt_2s
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(~sys_rst_n)begin
		cnt_2s <= 27'd0;
	end
	else begin
		if(state_cur == WAIT)begin
			if(cnt_2s <= CNT_2S_MAX - 1'b1)
				cnt_2s <= cnt_2s + 1'b1;
			else
				cnt_2s <= cnt_2s;
		end
		else if(state_cur == REC_DATA)begin
			cnt_2s <= 27'd0;
		end
		else begin
			cnt_2s <= cnt_2s;
		end
	end
end
//cnt_20ms
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(~sys_rst_n)begin
		cnt_20ms <= 20'd0;
	end
	else begin
		if(state_cur == START)begin
			if(cnt_20ms <= CNT_20MS_MAX - 1'b1)
				cnt_20ms <= cnt_20ms + 1'b1;
			else
				cnt_20ms <= cnt_20ms;
		end
		else if(state_cur == REC_DATA)begin
			cnt_20ms <= 20'd0;
		end
		else begin
			cnt_20ms <= cnt_20ms;
		end
	end
end
//cnt_1us
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(~sys_rst_n)begin
		cnt_1us <= 6'd0;
	end
	else begin
		if(cnt_1us == CNT_1US_MAX - 1'b1)
			cnt_1us <= 6'd0;
		else if(cnt_us_rst)
			cnt_1us <= 6'd0;
		else
			cnt_1us <= cnt_1us + 1'b1;
	end
end
//cnt_nus
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(~sys_rst_n)begin
		cnt_nus <= 7'd0;
	end
	else begin
		if(cnt_us_rst)
			cnt_nus <= 7'd0;
		else if(cnt_1us == CNT_1US_MAX - 1'b1)
			cnt_nus <= cnt_nus + 1'b1;
		else
			cnt_nus <= cnt_nus;
	end
end
//信号位数控制
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(~sys_rst_n)begin
		cnt_bit <= 6'd0;
	end
	else begin
		if(state_cur == REC_DATA)begin
			if(dht11_negedge)
				cnt_bit <= cnt_bit + 1'b1;
			else
				cnt_bit <= cnt_bit;
		end
		else begin
			cnt_bit <= 6'd0;
		end
	end
end

//状态控制1
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(~sys_rst_n)
		state_cur <= WAIT;
	else
		state_cur <= state_nex;
end
//状态控制2
always@(*)begin
	case(state_cur)
		WAIT	:begin
			if(end_2s)
				state_nex = START;	//count 2s finish 
			else
				state_nex = WAIT;
		end		
		START	:begin
			if(end_20ms)
				state_nex = WAIT_RES;//count 20ms finish 
			else
				state_nex = START;
		end			
		WAIT_RES:begin	
			if(res_ok)				//respond 
				state_nex = RES_LOW;	
			else if(res_no)			//no respond 
				state_nex = WAIT;
			else
				state_nex = WAIT_RES;
		end			
		RES_LOW	:begin
			if(end_res_low)
				state_nex = RES_HIGH;
			else
				state_nex = RES_LOW;
		end			
		RES_HIGH:begin
			if(end_res_high)
				state_nex = REC_DATA;
			else
				state_nex = RES_HIGH;
		end			
		REC_DATA:begin
			if(end_rec)
				state_nex = WAIT;
			else
				state_nex = REC_DATA;
		end		
		default	:begin
			state_nex = WAIT;
		end			
	endcase
end
//IO控制
assign dht11_data = output_en ? data : 1'bz;
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(~sys_rst_n)begin
		output_en <= 1'b0;
		data <= 1'b0;
	end
	else begin
		case(state_cur)
			WAIT	 :begin
				output_en <= 1'b1;//output
				data <= 1'b1;
			end
		    START	 :begin
				output_en <= 1'b1;//output
				data <= 1'b0;
				if(end_20ms)
					data <= 1'b1;
			end
		    WAIT_RES :begin
				output_en <= 1'b0;//input
				data <= 1'b0;
			end
		    RES_LOW	 :begin
				output_en <= 1'b0;//input
				data <= 1'b0;
			end
		    RES_HIGH :begin
				output_en <= 1'b0;//input
				data <= 1'b0;
			end
		    REC_DATA :begin
				output_en <= 1'b0;//input
				data <= 1'b0;
			end
			default  :begin
				output_en <= 1'b0;//input
				data <= 1'b0;
			end
		endcase
	end
end
//写入1
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(~sys_rst_n)begin
		t_h_data_temp <= 40'd0;
	end
	else begin
		if(state_cur == REC_DATA)begin
			if(cnt_nus > 50 && dht11_negedge)
				t_h_data_temp[39 - cnt_bit] <= 1'b1;
			else if(cnt_nus < 50 && dht11_negedge)
				t_h_data_temp[39 - cnt_bit] <= 1'b0;
			else 
				t_h_data_temp <= t_h_data_temp;
		end
		else begin
			t_h_data_temp <= t_h_data_temp;
		end
	end
end
//写入2
always@(posedge sys_clk or negedge sys_rst_n)begin
	if(~sys_rst_n)begin
		t_h_data <= 40'd0;
	end
	else begin
		if(state_cur == REC_DATA)begin
			if(end_rec && check)
				t_h_data <= t_h_data_temp;
			else
				t_h_data <= t_h_data;
		end
		else begin
			t_h_data <= t_h_data;
		end
	end
end
endmodule

3.6、TOP

顶层模块需要注意两点:

1、若数据只是“经过”顶层模块或是作为赋值对象而不对其改变,则需要用wire型变量,否则将会报错;

2、最好保持输出位置与输出数据的控制时序相同,千万不要让输出数据的改变快于输出位置的改变,否则数码管最终会显示非理想的输出。而非理想输出的原因可能在于:数据转换模块有问题;DHT11控制模块有问题;数码管模块有问题。本人就因此将所有代码从头到尾检查了一遍。

代码如下:

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/06/11 14:45:14
// Design Name: 
// Module Name: DHT_TOP
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module DHT_TOP(
    input sys_clk,
    input sys_rst,//触发为1    D20
    input tap8421, 
    input key,  //D19
    inout dht11,    //V17       AR8
    output point,   //AR7
    output [3:0]num,    //A0-5
    output [6:0]abcdefg,     //AR0-6
    output [3:0]leds
    );
				
	reg        data_flag;
//显示切换  1--温度   0--湿度
	wire   [15:0]  data_ctrl;
	wire   [39:0]  thdata;
//不同模块之间传输而不在顶层改变的变量用wire型	
//应用时钟  自用    输出给dht11——ctrl  输出给——SMG
	wire     clk50; 
//数据选择器所用变量
	reg    [7:0]   data_hu_int;//十位湿度整数
	reg    [7:0]   data_te_int;//十位温度整数
 	reg    [7:0]   data_hu_dec;//十位湿度小数
    reg    [7:0]   data_te_dec;//十位温度小数         
//SMG 4个数据信号
//BTD 小数点内设,不需要输入  
    reg    [7:0]   data_smg_int;   
    reg    [7:0]   data_smg_dec;
//SMG的输入    BTD的输出     
    wire    [3:0]    int_d1;//四位十进制
    wire    [3:0]    int_d2;//四位十进制
    wire    [3:0]    dec_d1;//四位十进制
    wire    [3:0]    dec_d2;//四位十进制    
    reg [3:0] numr;
    reg pointr;
//test
    wire [6:0] a1;
    wire [6:0] a2;
    wire [6:0] a3;
    wire [6:0] a4;
    reg [6:0] ar;
    
    assign abcdefg = ar;
    assign num = numr;
    assign point = pointr;
//dht11控制程序	
/*
dht11_ctrl dht11_ctrl
(
    .sys_clk(clk50), //input
    .sys_rst(sys_rst),//input 
    .data_flag(data_flag), //input 温湿度选择
    .dht11(dht11), //DHT11数据 input
    .data_out(data_ctrl),//输出数据 output
    .test_leds(leds)
);
*/
//输出控制  8421BCD TO ABCDEFG
dht11_2 dht11_2
(
    .sys_clk(clk50),
    .sys_rst_n(!sys_rst),
    .dht11_data(dht11),
    .t_h_data(thdata)
);
assign leds = int_d1;
//assign leds = dec_d1;
SMG SMG 
(
    .clk50(clk50),//in
    .rst(sys_rst),//in normal = 0
    .data_flag(data_flag),//in  0h  1t
    .int_in1(int_d1),//in   d
    .dec_in1(dec_d1),//in   d
    .int_in2(int_d2),//in   d
    .dec_in2(dec_d2),//in   d
    .data_out1(a1),
    .data_out2(a2),
    .data_out3(a3),
    .data_out4(a4)    
//out 8421bcd to abcdefg
);
//BTD   SMG TO 4 8421 BCD
BTD BTD
(
    .clk50(clk50),
   .int_in(data_smg_int[7:0]),
   .dec_in(data_smg_dec[7:0]),
    //.int_in('b01010100),//
    //.dec_in('b00010101),//
    //DHT11有效测试范围不过100,因此仅前7位信号有效
    .int_out1(int_d1),
    .dec_out1(dec_d1),
    .int_out2(int_d2),
    .dec_out2(dec_d2),
    .rst(sys_rst)
);
//温湿度切换
//通过按键的key信号,转换data_out输出的是湿度还是温度,没按一次按钮,转换一次
always@(posedge clk50 or negedge sys_rst)
begin
	if(sys_rst)
		data_flag <= 1'b0;
	else if(key == 1'b1)
		data_flag <= ~data_flag;
	else 
		data_flag <= data_flag;
end
//温湿度选择器,输出data-out
always@(posedge clk50 or negedge sys_rst)
begin
	if(sys_rst)
	   begin
	   end
	else if(data_flag == 1'b0)//data为32位 整温-小温-整湿-小湿
        //data_flag为0时data_crtl为湿度数据
        begin
		    data_hu_int <= thdata[39:32];//湿度数据
	  	    data_hu_dec <= thdata[31:24];//湿度数据
	    end
	else if(data_flag == 1'b1)//te
	    begin
		    data_te_int <= thdata[23:16];//温度数据
            data_te_dec <= thdata[15:8];//温度数据
 
        end
end
//数据选择器1
always@(posedge clk50)
    begin
        if(sys_rst)begin
            numr = 4'b1111;
            end
         else if(numr == 4'b1000)
            numr = 4'b0001;
         else
            numr = numr <<1;
     end
always@(posedge clk50)
    begin 
        case(numr)
        4'b0001:begin ar = a1;
            end
        4'b0010:begin ar = a2;
            end
        4'b0100:begin ar = a3;
            end
        4'b1000:begin ar = a4;
            end
        4'b1111:begin ar = 7'b0111111;
            end
        endcase
    end
always@(posedge clk50)
    begin
        if(numr == 4'b0010)
            pointr = 1'b0;
        else
            pointr = 1'b1;
    end
 
//smg赋值
always@(posedge clk50 or negedge sys_rst)
    begin
        if(sys_rst)
            begin
                data_smg_int <= 8'b0000_0000; 
                data_smg_dec <= 8'b0000_0000;                
            end
        else if (tap8421)
            begin 
                 data_smg_int <= 8'b0101_0100;//84 
                 data_smg_dec <= 8'b0001_0101;//21          
            end         
        else if(data_flag == 1'b1 && !tap8421)//温度
            begin
                data_smg_int <= data_te_int;
                data_smg_dec <= data_te_dec; 
            end
         else if(data_flag == 1'b0 && !tap8421)//湿度
            begin
                data_smg_int <= data_hu_int; 
                data_smg_dec <= data_hu_dec;                
            end   
     end
        
//时钟
clk_wiz_0 clk0(
.reset(1'b0),
.clk_in1(sys_clk),
.clk50(clk50)
);

endmodule

3.7、结果展示

湿度:37%

温度:28.09

城市参考值:

可以看到实际数据与城市参考值基本上差不多,同时证明在旁边放一桶水确实能够提高空气湿度。