[FPGA] 온습도 센서 DHT11 구현하는 Verilog 코드

# 오늘도 젓갈의 날 ~

로베이 컵에 참여하는 것은 로봇이되는 것이기 때문에 로봇의 구체적인 기계 방식에 대해 이야기하지 말고 외부 세계와 통신하는 센서는 필수 불가결합니다. 외부 환경 데이터를 수집하고 처리하여 수행 할 수 있습니다. 다양한 기능.

간단한 모듈, 온도 및 습도 센서 DHT11에 대해 이야기하겠습니다.

DHT11 소개

DHT11은 온도 및 습도 측정을위한 저렴하고 사용하기 쉬운 투 인원 센서입니다. 그것은 초소형 크기와 극히 낮은 전력 소비의 특성을 가지고 있습니다. 단일 버스를 사용하여 단일 칩 컴퓨터로 양방향 직렬 데이터 전송을 수행하며 신호 전송 거리는 20m 이상에 달할 수 있습니다. 높은 정확도와 실시간이 필요하지 않은 온도 및 습도 측정 상황에 매우 적합합니다.
이 기사에서는 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 핀과 마이크로 컨트롤러를 연결하는 라인입니다. 버스는 항상 유휴 상태통신 상태 사이에 있습니다.
단일 칩 마이크로 컴퓨터가 DHT11과 상호 작용하지 않을 때 버스는 유휴 상태에 있고 풀업 저항의 작동하에 높은 수준의 상태에 있습니다.
MCU와 DHT11이 통신 중일 때 버스는 통신 상태이며 완전한 통신 과정은 다음과 같습니다.
① MCU는 버스를 구동하는 IO를 출력 모드로 구성합니다. DHT11로 데이터를 보낼 준비가되었습니다.
② 마이크로 컨트롤러는 시작 신호를 보내기 위해 최소 18ms 동안 버스를 아래로 당깁니다. 그런 다음 버스를 하이로 당기고 20 ~ 40us를 지연시켜 시작 신호의 끝을 나타냅니다.
③ 단일 칩 마이크로 컴퓨터는 DHT11에서 반환 된 데이터를 수신 할 준비가 된 입력 모드로 드라이브 버스의 IO를 구성합니다.
④ DHT11은 마이크로 컨트롤러에서 보낸 초기 신호를 감지하면 응답을 시작하고 수집 된 센서 데이터를 반환합니다. DHT11은 먼저 마이크로 컨트롤러에 대한 승인 (ACK)으로 버스를 80us 낮게 가져온 다음 수집 된 온도 및 습도 데이터를 반환 할 준비가 된 버스를 80us만큼 높입니다. 온도 및 습도 데이터는 고정 프레임 형식으로 전송됩니다.
프레임은 40 비트이고 각 비트의 전송 타이밍 로직은 다음과 같습니다. 각 비트는 50us의 로우 레벨 (DHT11이 버스를 로우로 끌어 옴)에 의해 주도되고 DHT11은 버스를 하이로 끌어옵니다. 26 ~ 28us는 로직 0을 나타내고, 70us 지속되면 로직 1을 나타냅니다.

여기에 사진 설명 삽입
이 신호 다이어그램을 보면 모듈과 마스터 간의 통신 모드를 알 수 있습니다.

  1. 전원을 켠 후 1 초 동안 불안정한 상태를 지나갈 때까지 기다리십시오.
  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. 데이터 전송이 끝나면 버스가 하이로 당겨집니다. (여기에서 1 초 이상 지연된 후 다음 데이터 수집을 수행 할 수 있습니다.)

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                                     

Pro-test 가능합니다. 댓글이 모두 영어로되어있는 이유는 코드를 작성할 때 영문 키보드를 열고 중국어를 쓸 수 없어 중국어 기호의 실수를 피할 수 있기 때문입니다.

물리적 테스트

여기에 사진 설명 삽입

이것은 Robei Cup 대회에서 사용될 예정이지만, RISC-V 아키텍처를 사용하려면 Xilinx를 사용하여 하드 코어를 조정해야하기 때문에 Xilinx 또는 Altera의 보드를 사용할지 여전히 얽혀 있습니다. 아직 할 수없는 것 같아서 먼저 구조를 구축 한 다음 RISC-V로 구현할 수 있는지 확인하려고합니다.

추천

출처blog.csdn.net/Ninquelote/article/details/105824323