最近在做一個DHT11相關(guān)的東西,寫了一個DHT11控制模塊。參考了正點原子和野火電子的文檔資料后總覺得又亂又雜,所以自己跟著數(shù)據(jù)手冊寫了一遍,當(dāng)然了也很感謝正點原子和野火文檔的幫助。
DHT11是一種數(shù)字溫濕度傳感器,有4個引腳但只有三個有效引腳,分別是VDD,DATA和GND,如圖1所示,使用的是典型的單總線通信,即一根數(shù)據(jù)線。這根數(shù)據(jù)線需要通過上拉電阻連接到電源,使得數(shù)據(jù)線在空閑時保持高電平。數(shù)據(jù)手冊建議每2秒種對數(shù)據(jù)進(jìn)行一次讀取,來保證數(shù)據(jù)的穩(wěn)定性。

單總線數(shù)據(jù)傳輸位定義:一次傳輸40位數(shù)據(jù),高位先出。
數(shù)據(jù)格式:8bit濕度整數(shù)+8bit濕度小數(shù)+8bit溫度整數(shù)+8bit溫度小數(shù)+8bit校驗位。
示例數(shù)據(jù):
00110101 ?00000000 ?00011000 ?00000100 ?01010001
濕度高8位 ?濕度低8位??溫度高8位 ?溫度低8位 ?校驗位8bit
計算:
00110101+00000000+00011000+00000100=01010001
濕度:00110101 = 35H = 53
??00000000 = 00H = 0 ?濕度為53.0%
溫度:00011000 = 18H = 24
??00000100 = 04H = 0.4 溫度為24.4℃
并且當(dāng)溫度低于0時溫度數(shù)據(jù)的低8位的最高位置1。由于一般不會出現(xiàn)負(fù)數(shù),所以此次設(shè)計不關(guān)心此處。
時序圖如圖2:
?文章來源地址http://www.zghlxwxcb.cn/news/detail-451430.html

?
外設(shè)讀取步驟:
①設(shè)備上電,等待1s越過不穩(wěn)定狀態(tài),由于2秒讀取一次數(shù)據(jù)較穩(wěn)定,所以我們等待2s。
②主機(jī)發(fā)送開始信號,數(shù)據(jù)線拉低至少18ms。此時總線由FPGA控制。
③主機(jī)將數(shù)據(jù)線拉高后釋放總線等待從機(jī)響應(yīng),時間為13us。
④從機(jī)相應(yīng)低電平,時間為83us。
⑤從機(jī)相應(yīng)高電平,時間為87us。
⑥FPGA讀取DHT11的溫濕度數(shù)據(jù)。
圖3為時間參數(shù)表。

由此我們可以確定使用狀態(tài)機(jī)實現(xiàn)該設(shè)計,很輕松便可以畫出狀態(tài)轉(zhuǎn)換圖,如圖4,圖中狀態(tài)分別對應(yīng)上述外設(shè)讀取的6個步驟。

?
?接下來就根據(jù)狀態(tài)轉(zhuǎn)換圖來進(jìn)行verilog代碼的編寫,代碼如下。
/*
description : dht11 control
*/
module dht11(
input sys_clk , //system clock
input sys_rst_n , //system reset negedge
inout dht11_data , //dht11 inout port
output reg [39:0] t_h_data
);
//---------state code
parameter WAIT = 6'b000_001,//wait state 2s
START = 6'b000_010,//make bus low 20ms
WAIT_RES = 6'b000_100,//wait respond
RES_LOW = 6'b001_000,//respond low
RES_HIGH = 6'b010_000,//respong high
REC_DATA = 6'b100_000;//receive datas
//---------time parameter
//parameter CNT_2S_MAX = 100 ,
// CNT_20MS_MAX = 1_0 ,
// CNT_1US_MAX = 50 ;
parameter CNT_2S_MAX = 100_000_000 ,
CNT_20MS_MAX = 1_000_000 ,
CNT_1US_MAX = 50 ;
//---------state define
reg [5:0] state_cur;//current state
reg [5:0] state_nex;//next state
//---------flag define
wire end_2s ; //wait 2s end
wire end_20ms ; //wait 20ms end
wire res_ok ; //respond ok
wire res_no ; //no respond
wire end_res_low ; //wait respond low end 83us
wire end_res_high; //wait respond high end 87us
wire end_rec ; //data receive end 40bits
//---------dht11_data regist
reg dht11_data_r1;
reg dht11_data_r2;
wire dht11_posedge;
wire dht11_negedge;
reg data;
reg output_en;
wire check; //the datas is correct or wrong ?
reg [39:0] t_h_data_temp;//temperature and huminity data
//---------counter define
reg [26:0] cnt_2s;
reg [19:0] cnt_20ms;
reg [6:0] cnt_nus;
reg [5:0] cnt_1us;
reg cnt_us_rst;
reg [5:0] cnt_bit;
//---------flag assignments
assign end_2s = (state_cur == WAIT && cnt_2s == CNT_2S_MAX - 1'b1) ? 1'b1 : 1'b0;
assign end_20ms = (state_cur == START && cnt_20ms == CNT_20MS_MAX - 1'b1) ? 1'b1 : 1'b0;
assign res_ok = (state_cur == WAIT_RES && cnt_nus < 20 && dht11_negedge) ? 1'b1 : 1'b0;
assign res_no = (state_cur == WAIT_RES && cnt_nus > 20) ? 1'b1 : 1'b0;
assign end_res_low = (state_cur == RES_LOW && cnt_nus > 70 && dht11_posedge) ? 1'b1 : 1'b0;
assign end_res_high = (state_cur == RES_HIGH && cnt_nus > 70 && dht11_negedge) ? 1'b1 : 1'b0;
assign end_rec = (state_cur == REC_DATA && cnt_bit >= 40) ? 1'b1 : 1'b0;
//---------dht11 assignments
assign dht11_posedge = dht11_data_r1 & ~dht11_data_r2;
assign dht11_negedge = ~dht11_data_r1 & dht11_data_r2;
assign dht11_data = output_en ? data : 1'bz;
assign check = (t_h_data_temp[39:32]+t_h_data_temp[31:24]+
t_h_data_temp[23:16]+t_h_data_temp[15:8] == t_h_data_temp[7:0])
? 1'b1 : 1'b0;//the datas is correct or wrong ?
//*********posedge and negedge detect
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
dht11_data_r1 <= 1'b0;
dht11_data_r2 <= 1'b0;
end
else begin
dht11_data_r1 <= dht11_data;
dht11_data_r2 <= dht11_data_r1;
end
end
//*********counter
always@(*)begin
case(state_cur)
WAIT : cnt_us_rst = 1'b1;
START : cnt_us_rst = 1'b1;
WAIT_RES : begin
if(res_ok)
cnt_us_rst = 1'b1;
else
cnt_us_rst = 1'b0;
end
RES_LOW : begin
if(end_res_low)
cnt_us_rst = 1'b1;
else
cnt_us_rst = 1'b0;
end
RES_HIGH : begin
if(end_res_high)
cnt_us_rst = 1'b1;
else
cnt_us_rst = 1'b0;
end
REC_DATA : begin
if(dht11_posedge || dht11_negedge)
cnt_us_rst = 1'b1;
else
cnt_us_rst = 1'b0;
end
default :cnt_us_rst = 1'b1;
endcase
end
//---------cnt_2s
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
cnt_2s <= 27'd0;
end
else begin
if(state_cur == WAIT)begin
if(cnt_2s <= CNT_2S_MAX - 1'b1)
cnt_2s <= cnt_2s + 1'b1;
else
cnt_2s <= cnt_2s;
end
else if(state_cur == REC_DATA)begin
cnt_2s <= 27'd0;
end
else begin
cnt_2s <= cnt_2s;
end
end
end
//---------cnt_20ms
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
cnt_20ms <= 20'd0;
end
else begin
if(state_cur == START)begin
if(cnt_20ms <= CNT_20MS_MAX - 1'b1)
cnt_20ms <= cnt_20ms + 1'b1;
else
cnt_20ms <= cnt_20ms;
end
else if(state_cur == REC_DATA)begin
cnt_20ms <= 20'd0;
end
else begin
cnt_20ms <= cnt_20ms;
end
end
end
//---------cnt_1us
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
cnt_1us <= 6'd0;
end
else begin
if(cnt_1us == CNT_1US_MAX - 1'b1)
cnt_1us <= 6'd0;
else if(cnt_us_rst)
cnt_1us <= 6'd0;
else
cnt_1us <= cnt_1us + 1'b1;
end
end
//---------cnt_nus
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
cnt_nus <= 7'd0;
end
else begin
if(cnt_us_rst)
cnt_nus <= 7'd0;
else if(cnt_1us == CNT_1US_MAX - 1'b1)
cnt_nus <= cnt_nus + 1'b1;
else
cnt_nus <= cnt_nus;
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
cnt_bit <= 6'd0;
end
else begin
if(state_cur == REC_DATA)begin
if(dht11_negedge)
cnt_bit <= cnt_bit + 1'b1;
else
cnt_bit <= cnt_bit;
end
else begin
cnt_bit <= 6'd0;
end
end
end
//*********three stages state machine
//---------the first stage : state transmission
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)
state_cur <= WAIT;
else
state_cur <= state_nex;
end
//---------the second stage : conditions
always@(*)begin
case(state_cur)
WAIT :begin
if(end_2s)
state_nex = START; //count 2s finish
else
state_nex = WAIT;
end
START :begin
if(end_20ms)
state_nex = WAIT_RES;//count 20ms finish
else
state_nex = START;
end
WAIT_RES:begin
if(res_ok) //respond
state_nex = RES_LOW;
else if(res_no) //no respond
state_nex = WAIT;
else
state_nex = WAIT_RES;
end
RES_LOW :begin
if(end_res_low)
state_nex = RES_HIGH;
else
state_nex = RES_LOW;
end
RES_HIGH:begin
if(end_res_high)
state_nex = REC_DATA;
else
state_nex = RES_HIGH;
end
REC_DATA:begin
if(end_rec)
state_nex = WAIT;
else
state_nex = REC_DATA;
end
default :begin
state_nex = WAIT;
end
endcase
end
//---------the third stage : outputs
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
output_en <= 1'b0;
data <= 1'b0;
end
else begin
case(state_cur)
WAIT :begin
output_en <= 1'b1;//output
data <= 1'b1;
end
START :begin
output_en <= 1'b1;//output
data <= 1'b0;
if(end_20ms)
data <= 1'b1;
end
WAIT_RES :begin
output_en <= 1'b0;//input
data <= 1'b0;
end
RES_LOW :begin
output_en <= 1'b0;//input
data <= 1'b0;
end
RES_HIGH :begin
output_en <= 1'b0;//input
data <= 1'b0;
end
REC_DATA :begin
output_en <= 1'b0;//input
data <= 1'b0;
end
default :begin
output_en <= 1'b0;//input
data <= 1'b0;
end
endcase
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
t_h_data_temp <= 40'd0;
end
else begin
if(state_cur == REC_DATA)begin
if(cnt_nus > 50 && dht11_negedge)
t_h_data_temp[39 - cnt_bit] <= 1'b1;
else if(cnt_nus < 50 && dht11_negedge)
t_h_data_temp[39 - cnt_bit] <= 1'b0;
else
t_h_data_temp <= t_h_data_temp;
end
else begin
t_h_data_temp <= t_h_data_temp;
end
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(~sys_rst_n)begin
t_h_data <= 40'd0;
end
else begin
if(state_cur == REC_DATA)begin
if(end_rec && check)
t_h_data <= t_h_data_temp;
else
t_h_data <= t_h_data;
end
else begin
t_h_data <= t_h_data;
end
end
end
endmodule
?完成代碼后仿真觀察前三個狀態(tài),modelsim仿真結(jié)果如圖5,可以清晰地觀察到狀態(tài)的轉(zhuǎn)換以及轉(zhuǎn)換條件。

由于沒有dht11仿真模型,所以只好使用signaltap直接對實際波形進(jìn)行抓取,在抓取了幾次之后并且更改代碼后得到了最終的正確結(jié)果,如圖6,讀取波形數(shù)據(jù)后可得濕度為18,溫度為25,并且校驗通過,數(shù)據(jù)讀取正確。

?
隨后通過數(shù)碼管進(jìn)行顯示,數(shù)碼管模塊的代碼非常簡單,這里就不再貼出,直接查看最后的上板實驗效果,只顯示了溫濕度的整數(shù)位,如圖7,左側(cè)為濕度,右側(cè)為溫度。

參考資料:
1.dht11數(shù)據(jù)手冊
2.征途Mini《FPGA Verilog開發(fā)實戰(zhàn)指南——基于Altera EP4CE10》
3.新起點FPGA開發(fā)指南文章來源:http://www.zghlxwxcb.cn/news/detail-451430.html
?
到了這里,關(guān)于FPGA讀取DHT11數(shù)字溫濕度傳感器的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!