DHT11子產品
DHT11是比較簡單的一個子產品,一共三個引腳:VCC、GND和雙向資料線DATA,是以DHT11有主從之分。我們用FPGA當作主機,控制DATA總線向從機DHT11子產品發送起始信号去采集溫度濕度資料,然後DATA總線控制權交給DHT11用來傳輸資料給FPGA,就完成一次溫濕度檢測。
具體的控制過程還要參考DHT11子產品的時序圖:

首先在給子產品上電以後要等待至少1S的時間,越過不穩定期,然後就可以發送開始信号,開始信号是一個不低于18ms的低電平信号,其實發送完這個開始信号以後,總線控制權就可以交給DHT11了,時序圖中說要主機拉高20-40us,其實經過實測并沒有必要,直接去檢測DATA的下降沿即可,然後就是DHT的響應信号,DHT11會先拉低總線80us,然後再拉高80us,為了友善,這邊也是直接檢測上升沿和下降沿即可。在DHT響應過後就是資料的傳輸。
這裡資料傳輸有點特殊,每一個有效bit位的傳輸過程是:DHT11先拉低總線50us,然後拉高總線,如果拉高的時間在26us-28us之間,則表示這個bit位為0
而如果拉高的時間為70us,則這個bit位為1。
看着有點麻煩,其實FPGA處理起來非常友善,我們隻要檢測每一次上升沿,在這個上升沿過後就開啟一個計數器,計到30us停止,這時再去看現在總線上的值是0還是1,如果是0則表明總線已經被拉低,這個bit位為0,而如果是1則這個bit位為1。
在發送完最後一位資料後,DHT11會最後一次拉低總線50us,接着把總線控制權交還給FPGA,這時我們可以設定一個時間間隔,在這個時間之後,再次發出開始檢測信号,開啟下一次的檢測。
我們還需要注意資料部分約定的格式,如圖所示:
要注意這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
仿真驗證
箭頭标出的兩個地方表示子產品的最終輸出:32位的dout資料和資料有效标志dout_vld,傳給下遊子產品去處理。
RTL圖
這是整個工程的RTL圖,後面兩個子產品就是提取溫濕度資料,并把它們顯示到數位管上。
上闆驗證
左邊是目前濕度,右邊是目前溫度,後面的00是小數部分,因為DHT11的靈敏度不高,小數部分都是輸出的0。