天天看点

FPGA——驱动DHT11温湿度模块

DHT11模块

DHT11是比较简单的一个模块,一共三个引脚:VCC、GND和双向数据线DATA,所以DHT11有主从之分。我们用FPGA当作主机,控制DATA总线向从机DHT11模块发送起始信号去采集温度湿度数据,然后DATA总线控制权交给DHT11用来传输数据给FPGA,就完成一次温湿度检测。

具体的控制过程还要参考DHT11模块的时序图:

FPGA——驱动DHT11温湿度模块

首先在给模块上电以后要等待至少1S的时间,越过不稳定期,然后就可以发送开始信号,开始信号是一个不低于18ms的低电平信号,其实发送完这个开始信号以后,总线控制权就可以交给DHT11了,时序图中说要主机拉高20-40us,其实经过实测并没有必要,直接去检测DATA的下降沿即可,然后就是DHT的响应信号,DHT11会先拉低总线80us,然后再拉高80us,为了方便,这边也是直接检测上升沿和下降沿即可。在DHT响应过后就是数据的传输。

FPGA——驱动DHT11温湿度模块

这里数据传输有点特殊,每一个有效bit位的传输过程是:DHT11先拉低总线50us,然后拉高总线,如果拉高的时间在26us-28us之间,则表示这个bit位为0

FPGA——驱动DHT11温湿度模块

而如果拉高的时间为70us,则这个bit位为1。

看着有点麻烦,其实FPGA处理起来非常方便,我们只要检测每一次上升沿,在这个上升沿过后就开启一个计数器,计到30us停止,这时再去看现在总线上的值是0还是1,如果是0则表明总线已经被拉低,这个bit位为0,而如果是1则这个bit位为1。

FPGA——驱动DHT11温湿度模块

在发送完最后一位数据后,DHT11会最后一次拉低总线50us,接着把总线控制权交还给FPGA,这时我们可以设置一个时间间隔,在这个时间之后,再次发出开始检测信号,开启下一次的检测。

我们还需要注意数据部分约定的格式,如图所示:

FPGA——驱动DHT11温湿度模块

要注意这40bit的数据都是高位先出,也就是最先输入给FPGA的是湿度整数的最高位,最后输入的是校验和的最低位。

这里的校验和计算方法是将前四个字节的温湿度数据依次相加,然后取这个和的低八位。如果计算出来的校验和和收到的校验和一致,即为有效数据,否则为无效,将其丢弃。

verilog代码

遵循上面所述,结合状态机、计数器等手段就可以很容易写出驱动代码:

module	temperature_capture#(		
	parameter				TIME_1S			=		50_000_000	,
	parameter				TIME_20MS		=		1_000_000	,
	parameter				TIME_30US		=		1500		,
	parameter				TIME_0P5S		=		25_000_000	,
	parameter				BIT_NUM			=		40				
)
(
	input					clk			,
	input					reset		,
	inout	wire			dht11		,
	output	logic	[31:0]	dout		,
	output	logic			dout_vld	
);

	parameter				INIT			=		7'b000_0001	;
	parameter				HOST_LOW		=		7'b000_0010	;
	parameter				HOST_HIGH		=		7'b000_0100	;
	parameter				DHT_ACK_LOW		=		7'b000_1000	;
	parameter				DHT_ACK_HIGH	=		7'b001_0000	;
	parameter				DHT_DATA		=		7'b010_0000	;
	parameter				WAIT_0P5S		=		7'b100_0000	;
					
	logic		[ 6:0]		state_c			;
	logic		[ 6:0]		state_n			;
	logic		[31:0]		cnt				;
	logic					init_end		;
	logic					host_low_end	;
    logic					host_high_end	;
	logic					dht_low_end		;
	logic					dht_ack_end		;
	logic					dht_data_end	;
    logic					wait_0p5s_end	;

	logic					dht_ctrl_flag	;
	logic					dht11_temp1		;
	logic					dht11_temp2		;
	logic					dht_pos			;
	logic					dht_neg			;
	logic					dht11_buffer	;
	
	logic		[10:0]		cnt_dht			;
	logic					add_cnt_dht		;
	logic					end_add_cnt_dht	;
	logic		[ 5:0]		dht_bit_cnt		;
	logic					add_dht_bit_cnt	;
	logic					end_cnt_bit		;
	logic					end_cnt_bit_temp;
	logic					dht_bit_end_flag;

	logic					dht_data		;
	logic					dout_vld_temp	;
	logic		[39:0]		dht_data_temp	;
	
	always @(posedge clk or negedge reset)begin
		if(reset==1'b0)
			dht_ctrl_flag	<=	0	;
		else if(dht_data_end)
			dht_ctrl_flag	<=	0	;
		else if(host_low_end)
			dht_ctrl_flag	<=	1	;
	end
	
	always	@(posedge clk or negedge reset)begin
		if(reset==1'b0)begin
			dht11_temp1	<=	0	;
			dht11_temp2	<=	0	;
		end
		else if(dht_ctrl_flag)begin
			dht11_temp1	<=	dht11	;
			dht11_temp2	<=	dht11_temp1	;
		end
	end
	
	assign	dht_pos		=	dht11_temp1==1	&&	dht11_temp2==0	;
	assign	dht_neg		=	dht11_temp1==0	&&	dht11_temp2==1	;

	always @(posedge clk or negedge reset)begin
		if(reset==1'b0)
			state_c	<=	INIT	;
		else
			state_c	<=	state_n	;
	end
	
	always @(*)begin
		case(state_c)
			INIT:begin	
				if(init_end)
					state_n	=	HOST_LOW	;	
				else
					state_n	=	state_c		;
			end
			HOST_LOW:begin
				if(host_low_end)
					state_n	=	HOST_HIGH	;
				else
					state_n	=	state_c		;
			end
			HOST_HIGH:begin
				if(host_high_end)
					state_n	=	DHT_ACK_LOW	;
				else
					state_n	=	state_c		;
			end
			DHT_ACK_LOW:begin
				if(dht_low_end)
					state_n	=	DHT_ACK_HIGH	;
				else
					state_n	=	state_c		;
			end
			DHT_ACK_HIGH:begin
				if(dht_ack_end)
					state_n	=	DHT_DATA	;
				else
					state_n	=	state_c		;
			end
			DHT_DATA:begin
				if(dht_data_end)
					state_n	=	WAIT_0P5S		;
				else
					state_n	=	state_c		;
			end
			WAIT_0P5S:begin
				if(wait_0p5s_end)
					state_n	=	HOST_LOW	;
				else
					state_n	=	state_c		;
			end
			default:begin	
				state_n		=	INIT		;
			end
		endcase
	end
	
	assign	init_end		=		state_c==INIT			&&	cnt==0		;
	assign	host_low_end	=		state_c==HOST_LOW		&&	cnt==0		;
	assign	host_high_end	=		state_c==HOST_HIGH		&&	dht_neg		;
	assign	dht_low_end		=		state_c==DHT_ACK_LOW	&&	dht_pos		;
	assign	dht_ack_end		=		state_c==DHT_ACK_HIGH	&&	dht_neg		;
	assign	dht_data_end	=		state_c==DHT_DATA		&&	dht_bit_end_flag&&	dht_pos	;
	assign	wait_0p5s_end	=		state_c==WAIT_0P5S		&&	cnt==0		;
	
	assign	dht11_buffer	=		(state_c==INIT	||	state_c==HOST_LOW	||	state_c==WAIT_0P5S)	?	(state_c==HOST_LOW	?	0	:	1)	:	1'bz	;
	assign	dht11			=		dht11_buffer	;
	
	always @(posedge clk or negedge reset)begin
		if(reset==1'b0)
			cnt	<=	TIME_1S-1	;
		else if(init_end || wait_0p5s_end)
			cnt	<=	TIME_20MS-1	;
		else if(dht_data_end)
			cnt	<=	TIME_0P5S-1	;
		else if(cnt!=0)
			cnt	<=	cnt-1	;
	end
	
	always	@(posedge clk or negedge reset)begin
		if(reset==1'b0)
			cnt_dht	<=	0	;
		else if(add_cnt_dht)begin
			if(end_add_cnt_dht)
				cnt_dht	<=	0	;
			else
				cnt_dht	<=	cnt_dht+1	;
		end
	end
	assign	end_add_cnt_dht	=	add_cnt_dht	&&	cnt_dht==TIME_30US-1	;
	
	always 	@(posedge clk or negedge reset)begin
		if(reset==1'b0)
			add_cnt_dht	<=	0	;
		else if(end_add_cnt_dht)
			add_cnt_dht	<=	0	;
		else if(state_n==DHT_DATA && dht_pos)
			add_cnt_dht	<=	1	;
	end
	
	always	@(posedge clk or negedge reset)begin	
		if(reset==1'b0)
			dht_bit_cnt	<=	0	;
		else if(add_dht_bit_cnt)begin
			if(end_cnt_bit)
				dht_bit_cnt	<=	0	;
			else	
				dht_bit_cnt	<=	dht_bit_cnt+1	;
		end
	end
	assign	add_dht_bit_cnt	=	end_add_cnt_dht;
	assign	end_cnt_bit		=	add_dht_bit_cnt	&&	dht_bit_cnt==BIT_NUM-1	;
	
	always	@(posedge clk or negedge reset)begin
		if(reset==1'b0)
			end_cnt_bit_temp	<=	0	;
		else 
			end_cnt_bit_temp	<=	end_cnt_bit	;
	end
	
	always	@(posedge clk or negedge reset)begin
		if(reset==1'b0)
			dht_bit_end_flag	<=	0	;
		else if(dht_data_end)
			dht_bit_end_flag	<=	0	;
		else if(end_cnt_bit)
			dht_bit_end_flag	<=	1	;
	end
	
	assign	dht_data	=	(add_dht_bit_cnt	&&	dht11_temp2==1)	?	1	:	0	;
	
	always	@(posedge clk or negedge reset)begin
		if(reset==1'b0)
			dht_data_temp	<=	0	;
		else if(add_dht_bit_cnt)
			dht_data_temp	<=	{dht_data_temp[38:0],dht_data}	;
	end
			
	always @(posedge clk or negedge reset)begin
		if(reset==0)
			dout_vld_temp	<=	0	;
		else if(end_cnt_bit_temp && (dht_data_temp[7:0]==dht_data_temp[39:32]+dht_data_temp[31:24]+dht_data_temp[23:16]+dht_data_temp[15:8]))
			dout_vld_temp	<=	1	;
		else
			dout_vld_temp	<=	0	;
	end
	
	always  @(posedge clk or negedge reset)begin
		if(reset==1'b0)
			dout_vld	<=	0	;
		else 
			dout_vld	<=	dout_vld_temp	;
	end
	
	always @(posedge clk or negedge reset)begin
		if(reset==1'b0)
			dout	<=	0	;
		else if(dout_vld_temp)
			dout	<=	dht_data_temp[39:8]	;
		else 
			dout	<=	0	;
	end

endmodule

           

仿真验证

FPGA——驱动DHT11温湿度模块

箭头标出的两个地方表示模块的最终输出:32位的dout数据和数据有效标志dout_vld,传给下游模块去处理。

RTL图

FPGA——驱动DHT11温湿度模块

这是整个工程的RTL图,后面两个模块就是提取温湿度数据,并把它们显示到数码管上。

上板验证

FPGA——驱动DHT11温湿度模块

左边是当前湿度,右边是当前温度,后面的00是小数部分,因为DHT11的灵敏度不高,小数部分都是输出的0。