FPGA之DHT11温湿度传感器

FPGA之DHT11温湿度传感器学习笔记

一.DHT

DHT11作为一款低价、入门级的温湿度传感器,常用于单片机设计实例中;它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。DHT11为 4 针单排引脚封装,如下图,采用单线制串行接口,只需加适当的上拉电阻,信号传输距离可达20米以上,使其成为各类应用甚至最为苛刻的应用场合的最佳选则。

二、工作总结

1.RTL图:
基于ISE

其中消抖模块和数码管模块之前已完成编写,直接例化就可。

2.如上图所示,DHT11共有四个接线,1(接3-5.5V电源),2(40位data数据位),3(无效线,不接或接地),4(接地线)
3.串行接口
(单线双向)
DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次
通讯时间4ms左右,数据分小数部分和整数部分,具体格式在下面说明,当前小数
部分用于以后扩展,现读出为零.操作流程如下:
一次完整的数据传输为40bit,高位先出。
数据格式:8bit湿度整数数据+8bit湿度小数数据(湿度小数一般为0)
+8bi温度整数数据+8bit温度小数数据
+8bit校验和
数据传送正确时校验和数据等于“8bit湿度整数数据+8bit湿度小数数据
+8bi温度整数数据+8bit温度小数数据”所得结果的末8位。

用户MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主
机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集,
用户可选择读取部分数据.从模式下,DHT11接收到开始信号触发一次温湿度采集,
如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集.采集数据后
转换到低速模式。
1.通讯过程如图1所示
在这里插入图片描述

总线空闲状态为高电平,主机把总线拉低等待DHT11响应,主机把总线拉低必
须大于18毫秒,保证DHT11能检测到起始信号。DHT11接收到主机的开始信号后,
等待主机开始信号结束,然后发送80us低电平响应信号.主机发送开始信号结束
后,延时等待20-40us后, 读取DHT11的响应信号,主机发送开始信号后,可以切换
到输入模式,或者输出高电平均可,
总线由上拉电阻拉高

总线为低电平,说明DHT11发送响应信号,DHT11发送响应信号后,再把总线拉
高80us,准备发送数据,每一bit数据都以50us低电平时隙开始,高电平的长短定
了数据位是0还是1.格式见下面图示.如果读取响应信号为高电平,则DHT11没有
响应,请检查线路是否连接正常.当最后一bit数据传送完毕后,DHT11拉低总线
50us,随后总线由上拉电阻拉高进入空闲状态。

4.数据0,1表示方法:
(1)0的表示方法:在这里插入图片描述

当状态到了读取数据时,数据线拉高26us-28us即为0
(2)1的表示方法:在这里插入图片描述

当状态到了读取数据时,数据线拉高70us即为1
在实际代码中,为了好判断结果为0还是1,可以取一个中间数50进行判断。
例如:cnt_us <= 50表示为0,反之则为1
5.工作频率为2S一个周期,动态刷新的时间为2S。

三.代码

顶层模块

module dht11_top(sys_clk,sys_rst_n,key,dht11,stcp,shcp,ds,oe);

	input  		wire		sys_clk;
	input 		wire		sys_rst_n;
	input 		wire		key;
	inout 		wire		dht11;
				
	output  	wire        stcp        ; 
	output 		wire        shcp        ; 
	output  	wire        ds          ; 
	output  	wire        oe          ; 
			
	wire  				key_flag;
	wire	[19:0]		data_out;
	wire 				sign;
	

		
/*key_filter
#(
	.CNT_MAX(20'd999_999)
)*/		
key_filter key_filter 
(
    .sys_clk(sys_clk), 
    .sys_rst_n(sys_rst_n), 
    .key_in(key), 
    .key_flag(key_flag)
);

dth11_ctrl dth11_ctrl 
(
    .sys_clk(sys_clk), 
    .sys_rsy_n(sys_rsy_n), 
    .key_flag(key_flag), 
    .dht11(dht11), 
    .data_out(data_out), 
    .sign(sign)
);

seg_595_dynamic seg_595_dynamic 
(
    .sys_clk(sys_clk), 
    .sys_rst_n(sys_rst_n), 
    .data(data_out), 
    .point(6'b000_010), 
    .seg_en(1'b1), 
    .sign(sign), 
	
    .stcp(stcp), 
    .shcp(shcp), 
    .ds(ds), 
    .oe(oe)
);


endmodule

DHT11驱动模块

module dth11_ctrl(sys_clk,sys_rsy_n,key_flag,dht11,data_out,sign);
	
	input  				sys_clk;
	input 				sys_rsy_n;
	input 				key_flag;
	inout 				dht11;
	
	output 	reg	 [19:0]	data_out;
	output 	reg			sign;
	
//状态定义
	parameter           WAIT_1S	 =  6'b000_001,上电等待1s状态
						START    =  6'b000_010,//主机拉低18ms,发送开始信号状态
						DLY_1    =  6'b000_100,//等待从机答应
						REPLY    =  6'b001_000,//从机对主机发出发送信号
						DLY_2    =  6'b010_000,//等待主机回应
						RD_DATA  =  6'b100_000;//开始传输数据
						
	parameter			WAIT_1S_MAX	= 20'd999_999,
						LOW_18MS_MAX = 20'd17_999;
		
	wire 				dht11_rise;
	wire				dht11_fall;
	
	
	
    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 	  [39:0]	data;
	reg 				data_flag;
	reg 				dht11_en;
	reg 				dht11_out;
	

always@(posedge sys_clk or negedge sys_rsy_n)
begin
	if(!sys_rsy_n)
		cnt <= 5'd0;
	else if(cnt == 5'd24)
		cnt <= 5'd0;
	else 
		cnt <= cnt + 1'b1;

end
//微妙时钟
always@(posedge sys_clk or negedge sys_rsy_n)
begin
	if(!sys_rsy_n)
		clk_us <= 1'b0;
	else if(cnt_us == 5'd24)
		clk_us <= ~ clk_us;
	else 
		clk_us <= clk_us;
end

//状态机
always@(posedge clk_us or negedge sys_rsy_n)
begin
	if(!sys_rsy_n)
		state <= WAIT_1S;
	else 
		case(state)
		 WAIT_1S:
		 	if(cnt_us == WAIT_1S_MAX)
		 		state <= START;
		 	else 
		 		state <= WAIT_1S;
		 START	:
		 	if(cnt_us ==  LOW_18MS_MAX)
		 		state <= DLY_1; 
		 	else 
		 		state <=  START;
		 DLY_1  :
		 	if(cnt_us == 20'd10)
		 		state <= REPLY;
		 	else
		 		state <= DLY_1;
		 REPLY  :
		 	if(dht11_rise == 1'b1 && (cnt_low > 80))
		 		state <= DLY_2;
		 	else if(cnt_us > 1000)//表示等待了1MS
		 		state <= START;
		 	else 
		 		state <= REPLY;
		 DLY_2  :
		 	if(dht11_fall == 1'b1 && cnt_us >80)
		 		state <= RD_DATA;
		 	else 
		 		state <= DLY_2;
		 RD_DATA:
		 	if(bit_cnt == 40 && dht11_rise == 1'b1)
		 		state <= START;
		    else 	
		 		state <= RD_DATA;
		default:state <= WAIT_1S;
		endcase
end
	
always@(posedge clk_us or negedge sys_rsy_n)
begin
	if(!sys_rsy_n)
		begin
			cnt_low <= 20'd0;
			cnt_us  <= 20'd0;
		end
	else
		case(state)
			WAIT_1S:
				if(cnt_us == WAIT_1S_MAX)
					cnt_us <= 20'd0;
				else 
					cnt_us <= cnt_us + 1'b1;
            START  :
				if(cnt_us == LOW_18MS_MAX)
					cnt_us <= 20'd0;
				else 
					cnt_us <= cnt_us + 1'b1;
            DLY_1  :
				if(cnt_us == 10)
					cnt_us <= 20'd0;
				else 
					cnt_us <= cnt_us + 1'b1;
            REPLY  :
				if(dht11_rise == 1'b1 && (cnt_low > 80))
					begin
						cnt_low <= 20'd0;
						cnt_us  <= 20'd0;
					end
				else if(dht11 == 1'b0)
					begin
						cnt_low <= cnt_low + 1'b1;
						cnt_us  <= cnt_us + 1'b1;
					end
				else if(cnt_us > 1000)
					begin
						cnt_low <= 20'd0;
						cnt_us  <= 20'd0;
					end
				else 
					begin
						cnt_low <= cnt_low;
						cnt_us  <= cnt_us + 1'b1;
					end
            DLY_2  :
				if(dht11_fall == 1'b1 && (cnt_us > 80))
					cnt_us <= 20'd0;
				else 
					cnt_us  <= cnt_us + 1'b1;
            RD_DATA:
				if(dht11_fall == 1'b1 || dht11_rise == 1'b1)
					cnt_us  <= 1'b0;
				else 
					cnt_us <= cnt_us + 1'b1;
			default:
				begin
						cnt_low <= 20'd0;
						cnt_us  <= 20'd0;
				end
		endcase
end

always@(posedge clk_us or negedge sys_rsy_n)
begin
	if(!sys_rsy_n)
		begin
			dht11_reg1 <= 1'b1;
			dht11_reg2 <= 1'b1;
		end
	else 
		begin
			dht11_reg1 <= dht11;
			dht11_reg2 <= dht11_reg1;
		end
end


assign dht11_rise = (dht11_reg1) && (~dht11_reg2);
assign dht11_fall = (~dht11_reg1) && (dht11_reg2);


always@(posedge clk_us or negedge sys_rsy_n)
begin
	if(!sys_rsy_n)
		bit_cnt <= 6'd0;
	else if(bit_cnt == 40 && dht11_rise == 1'b1)
		bit_cnt <= 6'd0;
	else if((state == RD_DATA) && (dht11_fall == 1'b1))
		bit_cnt <= bit_cnt + 1'b1;
	else
		bit_cnt <= bit_cnt;
end

always@(posedge clk_us or negedge sys_rsy_n)
begin
	if(!sys_rsy_n)
		data_temp <= 40'd0;
	else if(state == RD_DATA && dht11_fall == 1'b1 && )
		data_temp[39 - bit_cnt] <= 1'b0; //从高位向低位赋值
	else if(state == RD_DATA && dht11_fall == 1'b1 && cnt_us > 50)
		data_temp[39 - bit_cnt] <= 1'b1;
	else 
		data_temp <= data_temp;
end
//数据校验和赋值
always@(posedge clk_us or negedge sys_rsy_n)
begin
	if(!sys_rsy_n)
		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];
	else 
		data <= data;
end

always@(posedge clk_us or negedge sys_rsy_n)
begin
	if(!sys_rsy_n)
		dht11_en <= 1'b0;
	else if(state <= START)
		dht11_en <= 1'b1;
	else 
		dht11_en <= 1'b0;	
end

always@(posedge clk_us or negedge sys_rsy_n)
begin
	if(!sys_rsy_n)
		dht11_out <= 1'b0;
	else 
		dht11_out <= 1'b0;
end
//通过按键的key_flag信号,转换data_out输出的是湿度还是温度
always@(posedge sys_clk or negedge sys_rsy_n)
begin
	if(!sys_rsy_n)
		data_flag <= 1'b0;
	else if(key_flag == 1'b1)
		data_flag <= ~data_flag;
	else 
		data_flag <= data_flag;
end

always@(posedge clk_us or negedge sys_rsy_n)
begin
	if(!sys_rsy_n)
		data_out <= 20'd0;
	else if(data_flag == 1'b0)
		data_out <= data[31:24] * 10;//湿度的整数值
	else if(data_flag == 1'b1)
		data_out <= (data[15:8]*10) + data[3:0];
	else 
		data_out <= data_out;
end

always@(posedge clk_us or negedge sys_rsy_n)
begin
	if(!sys_rsy_n)
		sign <= 1'b0;
	else if(data_flag == 1'b1 && data[7] == 1'b1)//data[7]信号表示温度的正负,data[7]为1是负
		sign <= 1'b1;
	else 
		sign <= 1'b0;
end

assign dht11 = (dht11_en == 1'b1) ? dht11_out : 1'bz;


endmodule

数码管模块和按键消抖模块就不贴了,这两个模块网上会有很多资料。

PS:该代码由野火FPGA开发教程视频写得,暂未上版验证,下周开始上板验证!

初学入门,分享学习笔记和心得,如有指教,感激不尽!

猜你喜欢

转载自blog.csdn.net/weixin_46628093/article/details/115055623