【FPGA】Verilog代码实现温湿度传感器DHT11

#今天也是咸鱼的一天~

因为参加Robei 杯 是要做一个机器人,先不说这个机器人具体怎么机器法,但是和外界互通的传感器肯定少不了,通过获取外界环境数据,进行处理,然后做出各种各样的功能。

先来讲个简单的模块,温湿度传感器DHT11。

DHT11介绍

DHT11是一款价格便宜,易于使用的温度湿度测量二合一传感器。它具有超小体积、极低功耗的特点。它使用单根总线与单片机进行双向的串行数据传输,信号传输距离可达20米以上。非常适用于对精度和实时性要求不高的温湿度测量场合。
本文将以DFRobot开源硬件平台的DHT11模块和DFRduino开发板来演示,讲解DHT11的驱动和使用。
DHT11电气参数
电源电压:3~5.5V(典型值:5V);
温度量程:0~50℃,误差 ±2℃;
湿度量程:20~90%RH,误差 ±5%RH;
采样周期:大于等于1秒/次。

在这里插入图片描述
这个传感器有三个脚,分别是VCC、GND、DATA。这是一个单数据口的传感器,数据口做双向传输使用,这个管脚在FPGA上要设置为inout

工作原理

DHT11使用单一总线通信,即DATA引脚和单片机连接的线。总线总是处于空闲状态通信状态这个2个状态之间。
当单片机没有与DHT11交互时,总线处于空闲状态,在上拉电阻的作用下,处于高电平状态。
当单片机和DHT11正在通信时,总线处于通信状态,一次完整的通信过程如下:
①单片机将驱动总线的IO配置为输出模式。准备向DHT11发送数据。
②单片机将总线拉低至少18ms,以此来发送起始信号。再将总线拉高并延时20~40us,以此来代表起始信号结束。
③单片机将驱动总线的IO配置为输入模式,准备接收DHT11回传的数据。
④当DHT11检测倒单片机发送的起始信号后,就开始应答,回传采集到的传感器数据。DHT11先将总线拉低80us作为对单片机的应答(ACK),然后接着将总线拉高80us,准备回传采集到的温湿度数据。温湿度数据以固定的帧格式发送。
一帧为40个bit,而每一个bit的传输时序逻辑为:每一个bit都以50us的低电平(DHT11将总线拉低)为先导,然后紧接着DHT11拉高总线,如果这个高电平持续时间为26~28us,则代表逻辑0,如果持续70us则代表逻辑1。

在这里插入图片描述
看这张信号图,就可以知道模块和主控之间的通信方式。

  1. 上电后等待1s,以越过不稳定状态。
  2. IO为信号输出模式,低电平持续18ms以上。(建议20ms)
  3. IO电平拉高,持续20~40us。(建议30us)
  4. IO为信号输入模式,进入DHT响应时间。(就是模块受到了主控的信息,返回一个数据预发送信号)
  5. 低电平延时80us,电平拉高80us,完成DHT响应。
  6. 进入数据逻辑0/1发送,持续40位。
  7. 逻辑0,电平拉低持续50us,电平拉高持续25us。
  8. 逻辑1,电平拉低持续50us,电平拉高持续80us。
  9. 数据发送结束后,总线拉高。(此处可以至少延时1s,然后再进行下一次数据采集)

Verilog驱动代码:

// dht11
// made by 00
//time 2020.4.28
module dht11(
    input               clk,   
    input               rst_n,                                   
    inout               dht11,   
    output  reg  [31:0] data_valid     
); 
/**************parameter********************/              
parameter  POWER_ON_NUM     = 1000_000;              
parameter  S_POWER_ON      = 3'd0;       
parameter  S_LOW_20MS      = 3'd1;     
parameter  S_HIGH_13US     = 3'd2;    
parameter  S_LOW_83US      = 3'd3;      
parameter  S_HIGH_87US     = 3'd4;      
parameter  S_SEND_DATA     = 3'd5;      
parameter  S_DEALY         = 3'd6; 
//reg define
reg[2:0]   cur_state;        
reg[2:0]   next_state;        
reg[20:0]  count_1us;       
reg[5:0]   data_count;                                       
reg[39:0]  data_temp;        
reg[4:0]   clk_cnt;

reg        clk_1M;       
reg        us_clear;        
reg        state;        
reg        dht_buffer;        
reg        dht_d0;        
reg        dht_d1;        
               
wire       dht_podge;        //data posedge
wire       dht_nedge;        //data negedge
/*********************main codes*********************/
assign dht11     = dht_buffer;
assign dht_podge   = ~dht_d1 & dht_d0; // catch posedge
assign dht_nedge   = dht_d1  & (~dht_d0); // catch negedge

/*********************counters*****************************/
//clock with 1MHz
always @ (posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        clk_cnt <= 5'd0;
        clk_1M  <= 1'b0;
    end 
    else if (clk_cnt < 5'd24) 
        clk_cnt <= clk_cnt + 1'b1;       
    else begin
        clk_cnt <= 5'd0;
        clk_1M  <= ~ clk_1M;
    end 
end
//counter 1 us
always @ (posedge clk_1M or negedge rst_n) begin
    if (!rst_n)
        count_1us <= 21'd0;
    else if (us_clear)
        count_1us <= 21'd0;
    else 
        count_1us <= count_1us + 1'b1;
end 
//change state
always @ (posedge clk_1M or negedge rst_n) begin
    if (!rst_n)
        cur_state <= S_POWER_ON;
    else 
        cur_state <= next_state;
end 
// state machine
always @ (posedge clk_1M or negedge rst_n) begin
    if(!rst_n) 
	 begin
        next_state <= S_POWER_ON;
        dht_buffer <= 1'bz;   
        state      <= 1'b0; 
        us_clear   <= 1'b0;
		  data_temp  <= 40'd0;
        data_count <= 6'd0;

    end 
    else 
	 begin
        case (cur_state)     
            S_POWER_ON :    //wait
				begin                
             if(count_1us < POWER_ON_NUM)
				 begin
					dht_buffer <= 1'bz; 
               us_clear   <= 1'b0;
				 end
             else 
				 begin            
               next_state <= S_LOW_20MS;
					us_clear   <= 1'b1;
				 end
            end
                
            S_LOW_20MS:  // send 20 ms
				begin
             if(count_1us < 20000)
				 begin
              dht_buffer <= 1'b0; 
              us_clear   <= 1'b0;
             end
				 else
				 begin
				  next_state   <= S_HIGH_13US;
              dht_buffer <= 1'bz; 
              us_clear   <= 1'b1;
                end    
            end 
               
            S_HIGH_13US:  // Hign 13 us
				begin                      
             if (count_1us < 20)
				 begin
              us_clear    <= 1'b0;
              if(dht_nedge)
				  begin   
					next_state <= S_LOW_83US;
               us_clear   <= 1'b1; 
              end
            end
              else                      
                next_state <= st_delay;
            end 
                
            S_LOW_83US:   
				begin                  
             if(dht_podge)                   
               next_state <= S_HIGH_87US;  
            end 
                
            S_HIGH_87US:               // ready to receive data signal
				begin
             if(dht_nedge)
				 begin          
              next_state <= S_SEND_DATA; 
              us_clear    <= 1'b1;
             end
             else
				 begin                
               data_count <= 6'd0;
               data_temp  <= 40'd0;
               state      <= 1'b0;
             end
            end 
                  
            S_SEND_DATA:    // have 40 bit
				begin                                
              case(state)
                0: begin               
                   if(dht_podge)
						 begin 
                     state    <= 1'b1;
                     us_clear <= 1'b1;
                   end            
                   else               
                    us_clear  <= 1'b0;
                   end
						 
                1: begin               
                   if(dht_nedge)
						 begin 
                     data_count <= data_count + 1'b1;
                     state    <= 1'b0;
							us_clear <= 1'b1;              
                     if(count_1us < 60)
                       data_temp <= {
    
    data_temp[38:0],1'b0}; //0
                     else                
							  data_temp <= {
    
    data_temp[38:0],1'b1}; //1
                    end 
                      else                                            //wait for high end
                       us_clear <= 1'b0;
                    end
                endcase
                
                if(data_cnt == 40)                                      //check data bit
					 begin  
                 next_state <= st_delay;
                 if(data_temp[7:0] == data_temp[39:32] + data_temp[31:24] + data_temp[23:16] + data_temp[15:8])
                   data_valid <= data_temp[39:8];  
                end
            end 
                
            S_DELAY:                                      // after data received delay 2s
				begin
             if(count_1us < 2000_000)
              us_cnt_clr <= 1'b0;
             else
				 begin                 
              next_state <= S_LOW_20MS;              // send signal again
              us_cnt_clr <= 1'b1;
             end
           end
            default :
					cur_state <= cur_state;
        endcase
    end 
end

//edge
always @ (posedge clk_1M or negedge rst_n) begin
    if (!rst_n) begin
        dht_d0 <= 1'b1;
        dht_d1 <= 1'b1;
    end 
    else begin
        dht_d0 <= dht11;
        dht_d1 <= dht_d0;
    end 
end 
endmodule                                     

亲测可用,里头注释都是英文的原因是,我写代码的时候开的英文键盘,写不了中文,这样可以避免中文符号的错误。

实物测试

在这里插入图片描述

这个是准备用在Robei 杯的比赛上的,但是现在还在纠结用Xilinx还是 Altera的板子,因为如果要用RISC-V架构的话,就一定得用Xilinx,调它的硬核。我感觉我还真的做不出来,所以打算先把结构搭出来,然后再试试看能不能用RISC-V 来实现。

猜你喜欢

转载自blog.csdn.net/Ninquelote/article/details/105824323