目录
1、 FPGA的DDS信号发生器
1.1、DDS简介
DSS全称为“直接数字式频率合成”,通过逻辑高低电平的相加合成数字信号,在将数信信号转换,生成模拟信号。
DSS信号发生器的实现主要分为三个步骤:
- 生成所需波形数据并储存到IP核“ROM”中;
- 读取波形数据并输出数字信号;
- 将数字信号通过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部分:
- 主机拉低电平18ms以上,推荐为20ms,但不要太长;
- 主机拉高20~40us等待辅机(DHT11)应答,推荐为30us;
- 从机拉低80us;
- 从机拉高80us,并进入数据传输阶段;
- 传输数据;
- 拉低50us后结束。
对于数据采集中0或1的判断,通过每个字节高电平的时间来判断。每个字节由恒定的50us低电平开始,若高电平持续时间为20~28us,则为0;若高电平持续时间为70us,则为1。
在此时需要注意两个问题:
- 由于0与1的高电平持续时间相差较大,故可以用一个阈值来区分0与1,如小于50us就为0,大于50us就为1;
- 0与1的判定一定要在高电平结束后,也就是端口的下降沿进行,负责会出现错误数据。
3.2、基本思路
对于整个程序,将其分为三个模块进行。
- 首先DHT11控制模块读取数据,并将数据传输给数据转换模块;
- 数据转换将一组八位二进制数据转换为两个四位二进制数据,分别为各位数据与十位数据,以配合数码管输出十进制数,并将数据传输给数码管模块;
- 数码管模块将二进制数据转换为共阳二极管所对应的格式,并与相应的显示匹配,最终输出。
此外,额外设置一个按钮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
城市参考值:
可以看到实际数据与城市参考值基本上差不多,同时证明在旁边放一桶水确实能够提高空气湿度。