ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
` `DHT11数字温[湿度传感器](http://www.hqchip.com/app/42)是一款含有已校准数字信号输出的温湿度复合[传感器](http://www.hqchip.com/app/835)。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。传感器包括一个[电阻](http://www.hqchip.com/app/dianzudianrongdiangan)式感湿元件和一个NTC测温元件,并与一个高性能8位[单片机](http://www.elecfans.com/tags/%E5%8D%95%E7%89%87%E6%9C%BA/)相连接。因此该产品具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点。每个DHT11传感器都在极为精确的湿度校验室中进行校准。校准系数以程序的形式储存在OTP内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。单线制串行[接口](http://www.hqchip.com/app/1039),使系统集成变得简易快捷。超小的体积、极低的功耗,信号传输距离可达20米以上,使其成为各类应用甚至最为苛刻的应用场合的最佳选则。产品为4针单排引脚封装。连接方便,特殊封装形式可根据用户需求而提供。 ![](https://img.kancloud.cn/3f/b2/3fb2f3fe98c7f253accbc96946f445bf_470x358.png) ![](https://img.kancloud.cn/56/1a/561ad1053c94ffc2350fafb11cdbda15_1053x536.png) ` `DHT11的供电电压为 3-5.5V。传感器上电后,要等待 1s 以越过不稳定状态在此 期间无需发送任何指令。电源引脚(VDD,GND)之间可增加一个100nF 的电容,用以去 耦滤波。 ` `DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次 通讯时间4ms左右,数据分小数部分和整数部分,具体格式在下面说明,当前小数 ` `部分用于以后扩展,现读出为零.操作流程如下: 一次完整的数据传输为40bit,高位先出。 数据格式:8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据 +8bit校验和 ![](https://img.kancloud.cn/c6/5b/c65b10934a590ed1655c5aeab9f20280_1250x543.png) ![](https://img.kancloud.cn/20/e3/20e337884645187e95b8de5a3288fa9c_1117x667.png) ![](https://img.kancloud.cn/48/bb/48bb77b41efc5e72dff6046c0f55f2fa_1132x618.png) ![](https://img.kancloud.cn/ef/c3/efc396eeedefd80d62e366fd21d3e684_1094x518.png) ## verilog驱动 ``` module DHT11( input wire clk , //1MHz时钟 input wire start ,//上升沿触发采集 input wire rst_n , inout dat_io , output reg [39:0] data , output error ,//数据度错误时为1 output done//完成一次转换后数据更新 ); wire din;//读取的数据 reg read_flag; reg dout; reg[3:0] state; localparam s1 = 0; localparam s2 = 1; localparam s3 = 2; localparam s4 = 3; localparam s5 = 4; localparam s6 = 5; localparam s7 = 6; localparam s8 = 7; localparam s9 = 8; localparam s10 = 9; assign dat_io = read_flag ? 1'bz : dout; assign din = dat_io; assign done = (state == s10)?1'b1:1'b0; assign error = (data[7:0] == data[15:8] + data[23:16] + data[31:24] + data[39:32])?1'b0:1'b1; reg [5:0]data_cnt; reg start_f1,start_f2,start_rising; always@(posedge clk) begin if(!rst_n)begin start_f1 <=1'b0; start_f2 <= 1'b0; start_rising<= 1'b0; end else begin start_f1 <= start; start_f2 <= start_f1; start_rising <= start_f1 & (~start_f2); end end reg [39:0] data_buf; reg [15:0]cnt ; always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0)begin read_flag <= 1'b1; state <= s1; dout <= 1'b1; data_buf <= 40'd0; cnt <= 16'd0; data_cnt <= 6'd0; data<=40'd0; end else begin case(state) s1:begin//当数据总线空闲时,收到数据采集时开启采集 if(start_rising && din==1'b1)begin state <= s2; read_flag <= 1'b0;//主机获取总线 dout <= 1'b0;//拉低 cnt <= 16'd0; data_cnt <= 6'd0; end else begin read_flag <= 1'b1; dout<=1'b1; cnt<=16'd0; end end s2:begin//主机输出低电平延时19ms,结束后主机发出高电平 if(cnt >= 16'd19000)begin state <= s3; dout <= 1'b1; cnt <= 16'd0; end else begin cnt<= cnt + 1'b1; end end s3:begin//主机延时20-40us,结束后释放数据总线,准备读取数据 if(cnt>=16'd20)begin cnt<=16'd0; read_flag <= 1'b1; state <= s4; end else begin cnt <= cnt + 1'b1; end end s4:begin//等待从机响应 if(din == 1'b0)begin//从机响应 state<= s5; cnt <= 16'd0; end else begin cnt <= cnt + 1'b1; if(cnt >= 16'd65500)begin//超时自恢复 state <= s1; cnt<=16'd0; read_flag <= 1'b1; end end end s5:begin//检查从机是否回应 if(din==1'b1)begin state <= s6; cnt<=16'd0; data_cnt <= 6'd0; end else begin cnt <= cnt + 1'b1; if(cnt >= 16'd65500)begin//超时自恢复 state <= s1; cnt<=16'd0; read_flag <= 1'b1; end end end s6:begin//等待第一个数据的起始信号点 if(din == 1'b0)begin//数据bit开始接收 state <= s7; cnt <= cnt + 1'b1; end else begin cnt <= cnt + 1'b1; if(cnt >= 16'd65500)begin//超时自恢复 state <= s1; cnt<=16'd0; read_flag <= 1'b1; end end end s7:begin// if(din == 1'b1)begin//决定数据的高电平起始点 state <= s8; cnt <= 16'd0; end else begin cnt <= cnt + 1'b1; if(cnt >= 16'd65500)begin//超时自恢复 state <= s1; cnt<=16'd0; read_flag <= 1'b1; end end end s8:begin//检测高电平的时间,并判断数据的 0 1 if(din == 1'b0)begin data_cnt <= data_cnt + 1'b1; state <= (data_cnt >= 6'd39)?s9:s7;//40bit数据接收完进入s9,否则进入s7继续接收下一bit cnt<=16'd0; if(cnt >= 16'd60)begin data_buf<={data_buf[39:0],1'b1}; end else begin data_buf<={data_buf[39:0],1'b0}; end end else begin cnt <= cnt + 1'b1; if(cnt >= 16'd65500)begin//超时自恢复 state <= s1; cnt<=16'd0; read_flag <= 1'b1; end end end s9:begin//锁存数据,并等待从机释放总线 //data <= (data_buf[7:0] == (data_buf[15:8] + data_buf[23:16] + data_buf[31:24] + data_buf[39:32]))?data_buf : data; data <= data_buf; if(din == 1'b1)begin state <= s10; cnt<=16'd0; end else begin cnt <= cnt + 1'b1; if(cnt >= 16'd65500)begin//超时自恢复 state <= s1; cnt<=16'd0; read_flag <= 1'b1; end end end s10:begin//空一拍,产生完成一次读数据的信号 state <= s1; cnt <= 16'd0; end default:begin state <= s1; cnt <= 16'd0; end endcase end end endmodule ``` 测试代码 ``` module top2( input wire clk,//50MHz时钟 //rst,// output reg led, //用于指示 input wire rxd, output wire txd, inout dht_io ); localparam DATA_NUM = 32; //*********************************PROCESS************************************** // 复位模块 //****************************************************************************** wire clk_1mhz; //assign dht_io = (1'b1)?clk_1mhz:1'bz; reg rst_n ; reg [15:0]delay_cnt; always@(posedge clk) begin if(delay_cnt>=16'd35530)begin delay_cnt <= delay_cnt; rst_n <= 1'b1; end else begin rst_n <= 1'b0; delay_cnt <= delay_cnt + 1'b1; end end //指示灯 //assign txd = led; reg [31:0]cnt; reg start; reg led_f1,led_f2,tx_flag; always@(posedge clk) begin led_f1 <= led; //tx_flag <= led &(~led_f1); led_f2 <= led &(~led_f1); if(cnt >= 32'd25000000 - 1) begin cnt <= 0; led <=~led; end else begin cnt <= cnt + 1'b1 ; end if(cnt>=32'd12500000 - 1)start <=1'b1; else start <= 1'b0; end //-------------------------------------------- localparam s_s1=0; localparam s_s2=1; localparam s_s3=2; localparam s_s4=3; reg [DATA_NUM*8-1:0]my_data;//待发送的数据 reg [DATA_NUM*8-1:0]send_data_cache; reg [7:0]my_data_num;//发送的数据量 reg [7:0]send_data; reg to_uart_valid , to_uart_ready; reg [2:0]send_st; reg [7:0]data_cnt; always@(posedge clk) begin if(!rst_n)begin to_uart_ready <= 1'b0; to_uart_valid <= 1'b0; send_data <= 8'd0; send_st<= s_s1; data_cnt <= 8'd0; end else begin case(send_st) s_s1:begin//待机 if(tx_flag)begin send_st <= s_s2; to_uart_valid <= 1'b0; to_uart_ready<= 1'b0; data_cnt <= 8'd0; send_data_cache <= my_data<<((DATA_NUM - my_data_num)<<3); end else begin to_uart_valid <= 1'b0; to_uart_ready<= 1'b0; end end s_s2:begin if(data_cnt <= my_data_num-1'b1)begin to_uart_valid <= 1'b1; to_uart_ready <= (data_cnt >= my_data_num-1)?1'b0:1'b1; send_data <= send_data_cache[DATA_NUM*8-1:DATA_NUM*8 - 8]; send_data_cache<= send_data_cache << 8; data_cnt <= data_cnt + 1'b1; send_st <= (data_cnt >= my_data_num-1)?s_s3:s_s2; end end s_s3:begin to_uart_valid <= 1'b0; //to_uart_ready <= 1'b1; send_st <= s_s1; data_cnt<=8'd0; end default :send_st <= s_s1; endcase end end //----------------------测试模块------------------------- myclock mclk_u1( .areset(!rst_n), .inclk0(clk), .c0(clk_1mhz), .locked() ); wire [39:0]dht_data; /* temp_dht11 u1( .clk(clk_1mhz), .nRST(~rst_n), .Data(dht_io), .data1(dht_data) );*/ wire done; DHT11 dht_inst1( .clk(clk_1mhz) , //1MHz时钟 .start(start) ,//上升沿触发采集 .rst_n(rst_n) , .dat_io(dht_io) , .data(dht_data) , .done(done) //.error ,//数据度错误时为1 //.done//完成一次转换后数据更新 ); reg done_f1,done_f2,done_rising; always@(posedge clk) begin done_f1<=done; done_f2<=done_f1; done_rising <= done_f1 &(~done_f2); end //DHT11获取数据 localparam s1 = 0; localparam s2 = 1; localparam s3 = 2; localparam s4 = 3; localparam s5 = 4; localparam s6 = 5; reg[4:0]st; reg [39:0]temp_data; always@(posedge clk) begin if(rst_n == 1'b0)begin my_data <= 128'd0; my_data_num <= 8'd0; tx_flag <= 1'b0; st <= s1; end else begin case(st) s1:begin//待机等待 if(done_rising)begin st<=s2; temp_data <= dht_data; end else begin st<=s1; tx_flag<=1'b0; end end s2:begin//数据校验 if(temp_data[7:0] == temp_data[15:8]+temp_data[23:16]+temp_data[31:24]+temp_data[39:32])begin st<=s3; end else st<=s5; end s3:begin my_data[47:32] <= temp_data[39:24]; my_data[31:16] <= temp_data[23:8]; my_data[15:0] <="\r\n"; my_data_num <= 8'd6; tx_flag <= 1'b1; st <= s4; end s4:begin tx_flag<=1'b0; st<=s1; end s5:begin//错误 my_data <="数据错误\r\n"; my_data_num <= 8'd10; tx_flag <= 1'b1; st<=s4; end default:st<=s1; endcase end end //-----------------------end测试模块--------------------- //获取数据 //always@(posedge clk) //begin // if(rst_n == 1'b0)begin // my_data <= 128'd0; // my_data_num <= 8'd0; // tx_flag <= 1'b0; // end // else begin // if(led_f2)begin // tx_flag <= 1'b1; // my_data_num <= 8'd12; // my_data <= "我是袁洪平\r\n"; // end // else tx_flag<=1'b0; // end //end //串口模块实例化 IP_UART u0 ( //.rs232_0_from_uart_ready (<connected-to-rs232_0_from_uart_ready>), // rs232_0_avalon_data_receive_source.ready //.rs232_0_from_uart_data (<connected-to-rs232_0_from_uart_data>), // .data //.rs232_0_from_uart_error (<connected-to-rs232_0_from_uart_error>), // .error //.rs232_0_from_uart_valid (<connected-to-rs232_0_from_uart_valid>), // .valid .rs232_0_to_uart_data (send_data), // rs232_0_avalon_data_transmit_sink.data .rs232_0_to_uart_error (), // .error .rs232_0_to_uart_valid (to_uart_valid), // .valid .rs232_0_to_uart_ready (to_uart_ready), // .ready .rs232_0_UART_RXD (rxd), // rs232_0_external_interface.RXD .rs232_0_UART_TXD (txd), // .TXD .clk_clk (clk), // clk.clk .reset_reset_n (rst_n) // reset.reset_n ); endmodule ```