DHT11 온습도 센서 데이터 전송 오실로스코프 검증

kenGwon·2023년 10월 11일

[STM32] Firmware/RTOS

목록 보기
2/11

목표

브레드보드를 통해 DHT11 온습도 센서를 STM32F429ZI 보드에 연결하고, 해당 센서가 MCU로 보내오는 데이터의 파형을 오실로스코프로 관측하는 것

DHT11의 데이터 전송

DHT11 제조사에서 배부한 데이터시트를 확인해보면, DHT11과 MCU가 연결되는 블럭다이어그램은 다음과 같다.

(출처: DHT11 Data Sheet. 이하 동일)

데이터 핀은 MCU와 직렬연결되어있으며 양방향 통신이 가능하다. 또한 데이터 회로는 풀업저항으로 구성되어 입력전압은 active low이다.

이어지는 데이터시트의 설명을 보면 한번의 데이터 송신은 40비트로 구성되며, 그 구성은 8비트의 습도데이터(정수부), 8비트의 습도데이터(소수부), 8비트의 온도데이터(정수부), 8비트의 온도데이터(소수부), 8비트의 체크섬 패리티 비트로 구성되어 있다고 적혀있다.

DHT11의 0 1 구분법

여기서 DHT11은 0과 1을 체크하는 방식에 대해 별도의 프로토콜을 가지고 있다. 보통은 전압이 Low면 0로, High면 1로 간주하는 것이 일반적이지만, DHT11은 High전압이 인가된 시간의 길이를 기준으로 0과 1을 구분한다. 자세한 내용은 다음 DataSheet의 내용에 나와있다.

High 전압이 26~28us(마이크로 세컨드)동안 유지되면 데이터 0을 보낸 것으로 본다.

High 전압이 70us(마이크로 세컨드)동안 유지되면 데이터 1을 보낸 것으로 본다.

오실로스코프

온습도 센서가 MCU로 보내오고 있는 온도 습도 데이터 값을 ComPort Master 프로그램을 통해 확인하면 1초마다 [Wet], [Tmp] 값을 확인할 수 있다.

회로구성은 다음과 같다.

그러면 이제 DHT11이 MCU로 송신하는 데이터가 정말 데이터 시트에 정의된 형태의 전기적 신호로 MCU에 들어가고 있는지 오실로 스코프를 통해 확인해보자. 아래는 해당 신호를 수기 분석한 그림이다.

정말 한번의 Complete Data Transmission마다 Data Sheet에 적힌 형태 그대로 40비트의 데이터를 표현하는 전기적 신호가 MCU로 흘러 들어간 것을 눈으로 확인할 수 있다.

코드 구현

STM32F429ZI 실습 환경은 CubeIDE에서 진행하였다.

  1. Comport Master를 사용하기 위해 InputOutput Configuration 파일(project.ioc)에서 UART3 포트를 열어주었다.

  2. DHT11의 송신 데이터를 수신하기 위해 InputOutput Configuration 파일(project.ioc)에서 GPIO_Output 포트를 열어주었다.(MCU가 온습도 센서의 데이터 값이 필요할 경우에만 DHT11에게 값을 먼저 요청하여, DHT11이 그에 대해 응답하는 구조로 작동하기 때문에, MCU입장에서는 GPIO_Output포트로 DHT11에 연결하는 것이 타당하다.

  3. DHT11.c의 delay_us() 함수는 InputOutput Configuration 파일(project.ioc)에서 마이크로 세컨드를 체크할 수 있는 범용타이머 하나 활성화 시켜서 그것을 바탕으로 직접 구현하면 된다.

DHT11.h

#ifndef SRC_DHT11_H_
#define SRC_DHT11_H_

#define DHT11_PORT			GPIOA
#define DHT11_DATA_RIN		GPIO_PIN_0
void DHT11_Init(void);
void DHT11_trriger(void);
void DHT11_DataLine_Input(void);
void DHT11_DataLine_Output(void);
uint8_t DHT11_rx_Data(void);
void DHT11_dumi_read(void);

#endif 

DHT11.c

#include "main.h"
#include "DHT11.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

//extern TIM_HandleTypeDef htim2;
//extern void FND_update(uint16_t value);
extern void delay_us (unsigned long us);
extern volatile int TDHT11_timer_counter;

void DHT11_main(void)
{
	uint8_t i_RH, d_RH, i_Tmp, d_Tmp;
	
	// HAL_TIM_Base_Start_IT(&htim2);
	DHT11_Init();
	while(1)
	{
		DHT11_trriger();
		DHT11_DataLine_Input();
		DHT11_dumi_read();

		i_RH = DHT11_rx_Data();
		d_RH = DHT11_rx_Data();
		i_Tmp = DHT11_rx_Data();
		d_Tmp = DHT11_rx_Data();
		
		DHT11_DataLine_Output();
		HAL_GPIO_WritePin(DHT11_PORT, DHT11_DATA_RIN, GPIO_PIN_SET);
		printf("[Tmp]%d\n",(int)i_Tmp);
		printf("[Wet]%d\n",(int)i_RH);
		// FND_Update(i_Tmp*100 + i_RH);
		HAL_Delay(1500);
	}
	
}

void DHT11_processing(void)
{
	uint8_t i_RH, d_RH, i_Tmp, d_Tmp;

	// 아래 세줄은 DHT11과 MCU의 Hand-shaking과정이다.
	DHT11_trriger();
	DHT11_DataLine_Input();
	DHT11_dumi_read();

	// 여기부터 DHT11가 수집한 데이터를 읽어오는 것이다.
	i_RH = DHT11_rx_Data();
	d_RH = DHT11_rx_Data();
	i_Tmp = DHT11_rx_Data();
	d_Tmp = DHT11_rx_Data();

	DHT11_DataLine_Output();
	HAL_GPIO_WritePin(DHT11_PORT, DHT11_DATA_RIN, GPIO_PIN_SET);
	printf("[Tmp]%d\n",(int)i_Tmp);
	printf("[Wet]%d\n",(int)i_RH);
//	FND_update(i_Tmp*100 + i_RH);
}

void DHT11_Init(void)
{
	HAL_GPIO_WritePin(DHT11_PORT, DHT11_DATA_RIN, GPIO_PIN_SET);
	HAL_Delay(3000);
	return;
}


void DHT11_trriger(void)
{
	HAL_GPIO_WritePin(DHT11_PORT, DHT11_DATA_RIN, GPIO_PIN_RESET);
	HAL_Delay(20);
	
	HAL_GPIO_WritePin(DHT11_PORT, DHT11_DATA_RIN, GPIO_PIN_SET);
	delay_us(7);
	return;
}


void DHT11_DataLine_Input(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	
	/*Configure GPIO pin : PH0 */
  GPIO_InitStruct.Pin = DHT11_DATA_RIN;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;			//Change Output to Input
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(DHT11_PORT, &GPIO_InitStruct);
	
	return;
}


void DHT11_DataLine_Output(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	
	/*Configure GPIO pin : PH0 */
  GPIO_InitStruct.Pin = DHT11_DATA_RIN;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;			//Change Input to Output 
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(DHT11_PORT, &GPIO_InitStruct);
	
	return;
}


uint8_t DHT11_rx_Data(void)
{
	uint8_t rx_data = 0;
	
	for(int i = 0; i < 8; i++)
	{
		//when Input Data == 0
		while( 0 == HAL_GPIO_ReadPin(DHT11_PORT, DHT11_DATA_RIN) );
#if 1
		delay_us(40);
#else  // org
		delay_us(16);
#endif
		rx_data<<=1;
		
		//when Input Data == 1
		if(HAL_GPIO_ReadPin(DHT11_PORT, DHT11_DATA_RIN))
		{
			rx_data |= 1;
		}
		while( 1 == HAL_GPIO_ReadPin(DHT11_PORT, DHT11_DATA_RIN) );
	}
	return rx_data;
}


void DHT11_dumi_read(void)
{
	while( 1 == HAL_GPIO_ReadPin(DHT11_PORT, DHT11_DATA_RIN) );
	while( 0 == HAL_GPIO_ReadPin(DHT11_PORT, DHT11_DATA_RIN) );
	while( 1 == HAL_GPIO_ReadPin(DHT11_PORT, DHT11_DATA_RIN) );
	return;
}
profile
스펀지맨

0개의 댓글