天天看點

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。