温湿度测量模块DHT11使用方法(FPGA)

这篇具有很好参考价值的文章主要介绍了温湿度测量模块DHT11使用方法(FPGA)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.DHT11工作流程

        DHT11采用了简化的单总线通讯。当DHT11在上电一秒后收到来自控制器(FPGA)发出的起始信号后,会向控制器发送一个响应信号,随后便会发送40位的数据。

        起始信号:一个时长大于18ms小于30ms的低电平

温湿度测量模块DHT11使用方法(FPGA),Verilog代码分享,fpga开发

        响应信号:

温湿度测量模块DHT11使用方法(FPGA),Verilog代码分享,fpga开发

        数据格式:

温湿度测量模块DHT11使用方法(FPGA),Verilog代码分享,fpga开发

        工作时序图:

温湿度测量模块DHT11使用方法(FPGA),Verilog代码分享,fpga开发

        数据格式:

温湿度测量模块DHT11使用方法(FPGA),Verilog代码分享,fpga开发

        校验位=湿度高8位+湿度低8位+温度高8位+温度低8位;

        湿度高8位对应湿度的整数部分,湿度低8位对应湿度的小数部分;

        温度高8位对应温度的整数部分,温度低8位对应温度的小数部分(当温度的低8位的最高位为1时表示此时测量到的温度为0下);

      !!!  主机从DHT11读取的温湿度数据总是前一次的测量值,如两次测间隔时间很长,请连 续读两次以第二次获得的值为实时温湿度值。

2.代码详解

2.1起始信号的发送

1.对使能信号en进行两次缓存,data_reg在输入时钟的上升沿对来自DHT11的数据进行采样;

	always @(posedge clk or negedge rstn )
		begin
		if (!rstn)
		begin
		en_reg0<=1'b0;
		en_reg1<=1'b0;
		end
		else
		begin
		en_reg0<=en;
		en_reg1<=en_reg0;
		end
		end 
	always @(posedge clk )
		begin
		data_reg<=data;
		end

2.当起始信号发送标志位send_flag有效时,对输入的50Mhz时钟clk进行分频产生一个周期为1ms的时钟clk_1ms,在clk时钟的上升沿对clk_1ms进行采样,采样结果放在寄存器clk_reg0和clk_reg1中(clk_reg1滞后于clk_reg0一个clk周期)。当~clk_reg1&clk_reg0(clk_1ms出现一个上升沿)时cnt_19ms+1;

        为什么不直接用产生的周期为1ms的时钟clk_1ms直接进行计数,而是用对clk_reg0和clk_reg1来判断clk_1ms是否出现上升沿从而进行计数?

        这是因为send_flag是在一个以clk为时钟的状态机中产生的,如果用clk_1ms直接进行计数,当cnt_19ms计到特定时,状态机在clk的时钟控制下跳转到下一个状态使send_flag无效,此时clk_1ms为低电平且不会在发生向高电平的跳变,此时就会导致寄存器cnt_19ms无法被正确的置0,导致下次测量使能en信号来临时模块无法正常工作;

always @(posedge clk or negedge rstn)
			begin
				if(!rstn)
					begin
					clk_1ms<=1'b0;
					cnt_1ms<=15'd0;
					end
				else if (send_flag)
					begin
					cnt_1ms<=cnt_1ms==num_1ms ? 15'd0 : cnt_1ms+15'd1;
					clk_1ms<=cnt_1ms==num_1ms ? ~clk_1ms : clk_1ms;
					end
				else 
					begin
					clk_1ms<=1'b0;
					cnt_1ms<=15'd0;
					end
			end
			reg clk_reg0,clk_reg1;
			always @(posedge clk or negedge rstn)
				begin
					if (!rstn)
						begin
						clk_reg0<=0;
						clk_reg1<=0;
						end
					else if (send_flag)
						begin
						clk_reg0<=clk_1ms;
						clk_reg1<=clk_reg0;
						end
					else 
						begin
					   clk_reg0<=0;
						clk_reg1<=0;
						end
				end
			wire cnt_en=~clk_reg1&clk_reg0;
			
			always@(posedge clk or negedge rstn)
				begin
				if (!rstn)
					begin
					cnt_19ms<=5'd0;
					end
				else if (cnt_en)
					begin
					cnt_19ms<=cnt_19ms+5'd1; 
					end
				  else if (!send_flag)
				begin
				   cnt_19ms<=5'd0;
				end
					else
					begin
					cnt_19ms<=cnt_19ms;
					end
				end

3.由于DHT11采用的是单总线控制,只有一条数据线,来自控制器(FPGA)和DHT11的数据都在这条数据线上传输。所以在FPGA中需要设置一个三态门来应对这种情况,在verlog中三态门对于的端口类型为inout。

 当dir_reg为1时三态门为输出模式,此时FPGA可以通过该端口向外发送数据,当dir_reg为0时三态门为输入模式,此时FPGA可以通过该端口接收来自DHT11的数据。 

状态机:当检测到en_reg1滞后en_reg0一个周期,当检测到测量控制en出现下降沿后send_flag为1,三态门设置为输出模式并向外输出电平直至cnt_19ms为20(定时19.5ms)。当cnt_19ms为20时三态门三态门配置为输入模式,flag_rce置1准备开始接收来自DHT11的40位数据。当完成数据的接收后状态机回到初始状态准备接收下一次的测量开始信号(en出现下降沿)

        为什么要在接收完数据后才跳转到初始状态?

为了避免在数据接收的过程中,en有效从而打断一次正常的数据接收。

always @(posedge clk or negedge rstn)
					begin
						if (!rstn)
							begin
							st0<=s0;
							send_flag<=1'b0;
							dir_reg<=1'b0;
							out_2_dht<=1'b1;
							flag_rce<=1'b0;
							end
					else 
					begin
						case(st0)
						s0:begin
							st0<=(en_reg1&~en_reg0) ? s1 : s0;
							end
						s1:begin
							send_flag<=1'b1;
							st0<= cnt_19ms==20?s2:s1;						
							dir_reg<=1'b1;
							out_2_dht<=1'b0;
							end
						s2:
							begin
							dir_reg<=1'b0;
							out_2_dht<=1'b1;
							send_flag<=1'b0;
							flag_rce<=1'b1;
							st0<=s3;
							end
						s3:
							begin
							flag_rce<=1'b0;
							st0<= done_reg==1'b1 ? s0 : s3;
							end
						endcase
					end
					end
			assign io_dir=dir_reg;
			assign data_2_dht11=out_2_dht;

2.2数据的接收

当检测到数据接收有效信号flag_rce为1时跳转到下一个状态等待数据总线被释放,当数据总线被释放后开始接收来自DHT11的响应信号(一个83us的低电平和一个87us的高电平),若接收到的响应信号不满足用户手册中给出的最小值则认为数据传输有误跳转到s_erro,结束数据的接收并输出一个clk时钟高电平的error信号。若接收到的响应信号符合要求则进行来自DHT11的40位数据的接收。通过观察DHT11工作流程中的数据格式我们可以发现,数据“0”和数据“1”只有高电平的时候是有差异的,所以我采用了一个比较偷懒的办法,只对数据的高电平进行计数,如果计数结果小于用户手册数据“0”的最大时间,则认为该数据为0反之为1。

 在接收完40个数据后,根据DHT11工作流程中检验位的产生方法,生成校验位与接收到的数据的低8位进行比对,若相等则此次数据有效,进行数据的输出并结束,若不相等则跳转到s_erro.

always @(posedge clk or negedge rstn)
				   begin
						if (!rstn)
							begin
							bit_cnt<=6'd39;
							st<=s0;
							cnt<=13'd0;
							error_reg<=1'b0;
							d_reg<=39'd0;
							done_reg<=1'b0;
							parity_bit<=8'd0;
							end
							else
								begin
								case(st)
								s0: begin
									st<= flag_rce ? s1: s0;
								    end
								s1: begin
									st<= data_reg==1'b1 ? s2 :s1;
									end
								s2:begin
									st<= data_reg==1'b0 ? s3: s2;
									cnt<=13'd1;
									end
								s3:begin
									st<= data_reg==1'b1 ? s4 :s3;
									cnt<=cnt+13'd1;
									end
								s4:begin
									cnt<=13'd2;
									st<= cnt>13'd4000 ? s5 : s_erro;
									end
								s5:begin
									st<= data_reg==1'b0 ? s6 :s5;
									cnt<=cnt+13'd1;
									end
								s6:begin
									cnt<=13'd1;
									st<= cnt>13'd4200 ? s7 : s_erro;
									end
									s7:begin
										st<= data_reg==1'b1 ? s8 :s7;
										cnt<=13'd1;
										end
									s8:begin
										cnt<=cnt+13'd1;
										st<= data_reg==1'b0 ? s9 : s8;
										end
									s9:begin	
									   d_reg[bit_cnt]<=cnt<13'd1400 ? 1'b0:1'b1;
										bit_cnt<= bit_cnt==6'd0 ? 6'd0 :bit_cnt-6'd1;
										st<= bit_cnt==6'd0 ? s10 : s7;
										end
										s10:begin
										parity_bit=d_reg[39:32]+d_reg[31:24]+d_reg[23:16]+d_reg[15:8];
										st<=s11;
											 end
										s11:begin
											done_reg<=parity_bit==d_reg[7:0] ? 1'b1: 1'b0;
											d0<=parity_bit==d_reg[7:0] ? d_reg: 40'd0;
											st<=parity_bit==d_reg[7:0] ? s12:s_erro;
											end
										s12:begin											 
						            	bit_cnt<=6'd39;
											st<=s0;
											cnt<=13'd0;
											error_reg<=1'b0;
											d_reg<=39'd0;
											done_reg<=1'b0;
											end
											s_erro:begin
											      done_reg<=1'b1;
													error_reg<=1'b1;
													d0<=40'd0;
													st<=s12;
													 end
													 endcase
													 
								end
								end

3.整体代码

3.1DHT11完整驱动代码

module DHT11(
				input clk,rstn,
						data,en,
				output reg [39:0] d0, 
				output done0,erro,
				output io_dir,data_2_dht11
				);
			parameter num_1ms=15'd24_999;
			localparam s0=4'b0000,s1=4'b0001,s2=4'b0011,s3=4'b0010,s4=4'b0110,s5=4'b0111,s6=4'b0101,s7=4'b0100,s8=4'b1100,s9=4'b1101,s10=4'b1111,s11=4'b1110,s12=4'b1010, s_erro=4'b1000;
		reg en_reg0,en_reg1;
		reg send_flag,flag_rce,done_reg,data_reg;
		reg [3:0]st0;
		reg [4:0]cnt_19ms;
		reg dir_reg,out_2_dht;
	always @(posedge clk or negedge rstn )
		begin
		if (!rstn)
		begin
		en_reg0<=1'b0;
		en_reg1<=1'b0;
		end
		else
		begin
		en_reg0<=en;
		en_reg1<=en_reg0;
		end
		end 
	always @(posedge clk )
		begin
		data_reg<=data;
		end
				always @(posedge clk or negedge rstn)
					begin
						if (!rstn)
							begin
							st0<=s0;
							send_flag<=1'b0;
							dir_reg<=1'b0;
							out_2_dht<=1'b1;
							flag_rce<=1'b0;
							end
					else 
					begin
						case(st0)
						s0:begin
							st0<=(en_reg1&~en_reg0) ? s1 : s0;
							end
						s1:begin
							send_flag<=1'b1;
							st0<= cnt_19ms==20?s2:s1;						
							dir_reg<=1'b1;
							out_2_dht<=1'b0;
							end
						s2:
							begin
							dir_reg<=1'b0;
							out_2_dht<=1'b1;
							send_flag<=1'b0;
							flag_rce<=1'b1;
							st0<=s3;
							end
						s3:
							begin
							flag_rce<=1'b0;
							st0<= done_reg==1'b1 ? s0 : s3;
							end
						endcase
					end
					end
			assign io_dir=dir_reg;
			assign data_2_dht11=out_2_dht;
				

			reg  [14:0]cnt_1ms;
			reg  clk_1ms;
		always @(posedge clk or negedge rstn)
			begin
				if(!rstn)
					begin
					clk_1ms<=1'b0;
					cnt_1ms<=15'd0;
					end
				else if (send_flag)
					begin
					cnt_1ms<=cnt_1ms==num_1ms ? 15'd0 : cnt_1ms+15'd1;
					clk_1ms<=cnt_1ms==num_1ms ? ~clk_1ms : clk_1ms;
					end
				else 
					begin
					clk_1ms<=1'b0;
					cnt_1ms<=15'd0;
					end
			end
			reg clk_reg0,clk_reg1;
			always @(posedge clk or negedge rstn)
				begin
					if (!rstn)
						begin
						clk_reg0<=0;
						clk_reg1<=0;
						end
					else if (send_flag)
						begin
						clk_reg0<=clk_1ms;
						clk_reg1<=clk_reg0;
						end
					else 
						begin
					   clk_reg0<=0;
						clk_reg1<=0;
						end
				end
			wire cnt_en=~clk_reg1&clk_reg0;
			
			always@(posedge clk or negedge rstn)
				begin
				if (!rstn)
					begin
					cnt_19ms<=5'd0;
					end
				else if (cnt_en)
					begin
					cnt_19ms<=cnt_19ms+5'd1; 
					end
				  else if (!send_flag)
				begin
				   cnt_19ms<=5'd0;
				end
					else
					begin
					cnt_19ms<=cnt_19ms;
					end
				end
				
					reg [39:0]d_reg;
					reg [12:0]cnt;
					reg [3:0]st;
					reg error_reg;
					reg [5:0] bit_cnt; 
					reg [7:0] parity_bit;
					always @(posedge clk or negedge rstn)
				   begin
						if (!rstn)
							begin
							bit_cnt<=6'd39;
							st<=s0;
							cnt<=13'd0;
							error_reg<=1'b0;
							d_reg<=39'd0;
							done_reg<=1'b0;
							parity_bit<=8'd0;
							end
							else
								begin
								case(st)
								s0: begin
									st<= flag_rce ? s1: s0;
								    end
								s1: begin
									st<= data_reg==1'b1 ? s2 :s1;
									end
								s2:begin
									st<= data_reg==1'b0 ? s3: s2;
									cnt<=13'd1;
									end
								s3:begin
									st<= data_reg==1'b1 ? s4 :s3;
									cnt<=cnt+13'd1;
									end
								s4:begin
									cnt<=13'd2;
									st<= cnt>13'd4000 ? s5 : s_erro;
									end
								s5:begin
									st<= data_reg==1'b0 ? s6 :s5;
									cnt<=cnt+13'd1;
									end
								s6:begin
									cnt<=13'd1;
									st<= cnt>13'd4200 ? s7 : s_erro;
									end
									s7:begin
										st<= data_reg==1'b1 ? s8 :s7;
										cnt<=13'd1;
										end
									s8:begin
										cnt<=cnt+13'd1;
										st<= data_reg==1'b0 ? s9 : s8;
										end
									s9:begin	
									   d_reg[bit_cnt]<=cnt<13'd1400 ? 1'b0:1'b1;
										bit_cnt<= bit_cnt==6'd0 ? 6'd0 :bit_cnt-6'd1;
										st<= bit_cnt==6'd0 ? s10 : s7;
										end
										s10:begin
										parity_bit=d_reg[39:32]+d_reg[31:24]+d_reg[23:16]+d_reg[15:8];
										st<=s11;
											 end
										s11:begin
											done_reg<=parity_bit==d_reg[7:0] ? 1'b1: 1'b0;
											d0<=parity_bit==d_reg[7:0] ? d_reg: 40'd0;
											st<=parity_bit==d_reg[7:0] ? s12:s_erro;
											end
										s12:begin											 
						            	bit_cnt<=6'd39;
											st<=s0;
											cnt<=13'd0;
											error_reg<=1'b0;
											d_reg<=39'd0;
											done_reg<=1'b0;
											end
											s_erro:begin
											      done_reg<=1'b1;
													error_reg<=1'b1;
													d0<=40'd0;
													st<=s12;
													 end
													 endcase
													 
								end
								end
								assign done0=done_reg;
								assign erro=error_reg;
endmodule

3.2调用示例

当复位后1s,led灯亮起此时按下按键en开始进行一次温湿度数据采集,若采集到的数据有误则会重新进行一次采集直至采集到正确数据。采集到的数据通过sender模块由通过串口发送到上位机。

module DHT11_code (
						input clk ,rstn,en,
						inout data_dht11,
						output tx,
						output led,test
						);	
reg[26:0] cnt;
reg ready;
always @ (posedge clk or negedge rstn)
begin
if (!rstn)
begin
cnt<=27'd0;
ready<=1'b0;
end
	else 
	begin
	cnt<=cnt==27'd50_000_000 ? 27'd50_000_000:cnt+27'd1;
	ready<= cnt==27'd50_000_000 ? 1'b1:1'b0;
	end
end

assign led=~ready;

  reg  clk_us;
  reg [4:0] cnt_num;
  always @(posedge clk  or negedge rstn)
	begin
	if (!rstn)
		begin
		clk_us<=1'b0;
		cnt_num<=5'd0;
		end
	else
		begin
		cnt_num<=cnt_num==5'd25 ? 5'd0:cnt_num+5'd1;
		clk_us<=cnt_num==5'd25 ? ~clk_us:clk_us;
		end
	end

	reg key_buff0,key_buff1;
	always @(posedge clk_us or negedge rstn)
		begin
			if(!rstn)
			begin
			key_buff0<=1'b1;
			key_buff1<=1'b1;
			end
			else 
			begin
			key_buff0<=en;
			key_buff1<=key_buff0;
			end
		end
	wire en_flag,erro;
	
	assign en_flag=key_buff1&~key_buff0&ready|erro;
	wire out_2_dht11;
	wire  io_ctrl;
	assign data_dht11 = io_ctrl==1'b1 ? out_2_dht11 : 1'bz;

	wire  [39:0]dht11_data;
	wire  done_2_uart;
  DHT11 U0(
				.clk(clk),.rstn(rstn),
				.data(data_dht11),.en(en_flag),   
				.d0(dht11_data),
				.done0(done_2_uart),.erro(erro),
				.io_dir(io_ctrl),.data_2_dht11(out_2_dht11)
				);
	wire send_done;
	sender U1(.clk(clk),.rstn(rstn),.data_flag(done_2_uart),
				 .data(data_dht11),
				 .done(send_done),.tx_d(tx)
					);
	
	

	
	endmodule
	

用signaltap II抓到的DHT11 模块的输出结果。

温湿度测量模块DHT11使用方法(FPGA),Verilog代码分享,fpga开发文章来源地址https://www.toymoban.com/news/detail-786338.html

到了这里,关于温湿度测量模块DHT11使用方法(FPGA)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【STM32】DHT11温湿度模块传感器详解&代码

    DHT11是数字温湿度传感器,测量范围:湿度20%-95%,温度0-50℃,广泛应用于加湿器、温湿度计、空调、汽车等领域。 如上图DATA引脚用于MCU与DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右(超时时间的判断)。一次完整的数据传输为40bit,高位先出(MSB最高

    2024年02月12日
    浏览(54)
  • STM32系列(HAL库)——使用ESP8266-01S物联网模块连接Onenet云平台上报DHT11温湿度

    本篇主要讲解如何使用ESP8266-01S物联网模块连接Onenet云平台,并上报DHT11模块的温湿度数据。本文单片机主控采用STM32F405RGT6,使用其他主控的话基本要求有2个串口,一个串口用于调试使用,另一个用于ESP模块通讯。 1.软件 CubeMX Keil5 串口调试助手 Onenet云平台账户 2.硬件 STM32开

    2024年02月02日
    浏览(80)
  • DHT11温湿度传感器(配合树莓派使用)

    DHT11是一种数字温湿度传感器,可以测量周围环境的温度和相对湿度。该传感器使用单个数字信号线与微控制器通信,具有较高的可靠性和稳定性。它适用于许多应用领域,如气象观测、室内环境监测、工业控制等。 DHT11传感器使用的基本原理是通过感应元件测量周围环境的

    2024年02月06日
    浏览(51)
  • STM32使用温湿度DHT11基于HAL库开发

    DHT11 是一款湿温度一体化的数字传感器。该传感器包括一个电阻式测湿元件和一个 NTC 测温元件,并与一个高性能 8 位单片机相连接。通过单片机等微处理器简单的电路连接就能够 实时的采集本地湿度和温度。DHT11 与单片机之间能采用简单的单总线进行通信,仅仅需要一 个

    2024年02月03日
    浏览(67)
  • [教程]一文搞懂STM32使用DHT11采集温湿度

          DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高 的可靠性与卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测 温元件,并与一个高性能8位单片机相连接。

    2024年02月01日
    浏览(39)
  • ESP32单片机使用DHT11温湿度传感器的实践与代码解析

    摘要:本文将介绍如何使用ESP32单片机与温湿度传感器进行连接和数据读取。通过实例代码的解析,帮助读者了解实现温湿度检测的原理和方法。 一、引言 ESP32是一款功能强大的微控制器,广泛应用于物联网、智能家居等领域。在许多应用场景中,温湿度传感器是不可或缺的

    2024年02月21日
    浏览(59)
  • 温湿度传感器DHT11介绍

    温湿度传感器DHT11简介       DHT11数字温湿度传感器是一种出厂时经过校准的数字信号输出的温湿度数字温湿度传感器 。DHT11 数字温湿度传感器应用温湿度传感技术和数字采集技术,确保其具有极高的可靠性和卓越的长期稳定性。       DHT11 数字温湿度传感器内置一个电阻式

    2023年04月22日
    浏览(58)
  • DHT11温湿度传感器学习

     DHT11温湿度传感器共有四个引脚 1个VCC高电平,1个GND接地低电平,1个数据输出引脚,一个空引脚 工作时候,通过out引脚可以向传感器传递应答信号并返回40位的温湿度数据,也就是5个字节 前2个字节表示温度的整数位和小数位,后面两个字节是湿度的整数位和小数位,最后

    2024年02月07日
    浏览(69)
  • 【mcuclub】温湿度传感器DHT11

    为什么接上拉电阻: 因为DHT11的数据口是漏极开路,如果不接上拉电阻,则只能输出低电平和高阻态,不能输出高电平,因此需要外接上拉电阻,否则无法输出1。DHT11的工作电流约为1mA,VCC一般为5V,则电阻R=5V/1mA=5KΩ。一般3.3k~10k都可以。 DHT11 数字温湿度传感器是一款含有已

    2024年02月06日
    浏览(60)
  • 十一、DHT11 温湿度检测(OLED显示)

    见博客:stm32f103c8t6新建固件库模板(可自取) 固件库模板 MDK5开发环境 stm32参考手册 利用固件库模板点灯工程(下面第三行,手动狗头) OLED模块资料 DHT11数据手册 利用固件库模板点灯工程(下面第三行,手动狗头) CH340 USB→TTL模块 实验程序已经发布到百度网盘,本文末有

    2024年02月01日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包