FPGA之DHT11温湿度传感器学习笔记
一.DHT
DHT11作为一款低价、入门级的温湿度传感器,常用于单片机设计实例中;它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。DHT11为 4 针单排引脚封装,如下图,采用单线制串行接口,只需加适当的上拉电阻,信号传输距离可达20米以上,使其成为各类应用甚至最为苛刻的应用场合的最佳选则。
二、工作总结
1.RTL图:
其中消抖模块和数码管模块之前已完成编写,直接例化就可。
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 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开发教程视频写得,暂未上版验证,下周开始上板验证!
初学入门,分享学习笔记和心得,如有指教,感激不尽!