[Intel AI SW 아카데미] 분주와 DHT11 온습도 센서

Jimeaning·2023년 10월 11일
0

Intel AIoT

목록 보기
1/38

2023.10.11 17일차

오늘의 학습 목표

  1. UART - Circular Queue code review
  2. 분주의 목적 및 필요성
    • Timer 10: 활성화
    • Timer 11: 1ms 활성화
    • us_delay 함수 작성하기
  3. DHT11 구동

  1. 초음파센서 코드 구현 및 구동
  2. I2C LCD 부착
    • I2C Protocol 설명

학습한 용어

💡 unsigned란?

자료형에 unsigned가 붙게 되면 부호가 없다는 의미가 되는데, 이는 곧 음의 부호(-)를 붙이지 않고 0 이상의 값만을 표현하겠다는 의미이다.

정수 기반 자료형은 signed와 unsigned가 있다. 보통 사용할 데이터가 양수와 음수 값 모두라면 signed 자료형을 선택하고, 양수 값만 사용한다면 unsigned 자료형을 선택한다.

음수 처리를 하려면 부호 비트가 필요하기 때문에 양수의 저장 범위가 반으로 줄어들어 signed가 무조건 좋다고 이야기할 순 없다.

예를 들어 signed char 자료형은 데이터의 표현 범위가 -128 ~ 127까지 총 256개의 값을 저장할 수 있고, unsigned char 자료형은 데이터 표현 범위가 0 ~ 255까지 총 256개의 값을 저장할 수 있어 실제로 ‘저장 가능한 숫자(표현 가능한 숫자)’의 개수는 동일하다.
따라서 signed와 unsigned는 상황에 맞춰서 사용하는 것이다. 사용하는 값이 양수만 존재하는 경우에는 unsigned 자료형을 사용해 양수의 범위를 2배로 확대시킬 수 있다. 반면 사용하는 값에 음수가 존재한다면 signed를 선택할 수밖에 없다.

💡 UART란?

UART는 UART(Universal Asynchronous Receiver/Transmitter, 범용 비동기 송수신기)의 약자이다.
병렬 데이터의 형태를 직렬 방식으로 전환하여 데이터를 전송하는 컴퓨터 하드웨어의 일종이다.

UART를 이용한 통신 방식의 종류

  • Simplex(단방향 통신): 데이터가 한 방향으로만 전송됨
  • Half-duplex(반이중): 한 번에 한 쪽만 전송 가능
  • Full-duplex(전이중): 양쪽이 동시에 전송 가능

UART에서 데이터는 프레임 형태로 전송된다.

(UART에 대한 내용은 새로운 포스트에 자세히 정리해야겠다..)

💡 volatile이란?

변수를 선언할 때 앞에 volatile을 붙이면 컴파일러는 해당 변수를 최적화에서 제외하여 항상 메모리에 접근하도록 한다. 즉, volatile 변수를 참조할 경우 레지스터에 로드된 값을 사용하지 않고 매번 메모리를 참조한다.

volatile [type] [variable_name];

필요성
반복문을 돌면서 1부터 10까지 더하는 프로그램이 있다고 가정하자.
이 경우 컴파일러는 최적화를 위해 반복문이 실행되지 않고 바로 1부터 10까지의 합을 출력하는데, 일반적인 코드라면 이런 최적화를 통해 수행 속도 면에서 이득을 보게 된다.

하지만 이 코드가 메모리 주소에 연결된 하드웨어 레지스터에 값을 쓰는 프로그램이라면 이야기가 달라진다. 각각의 쓰기가 하드웨어에 특정 명령을 전달하는 것이므로, 주소가 같다는 이유만으로 중복되는 쓰기 명령을 없애 버리면 하드웨어가 오동작하게 될 것이다. 이런 경우 유용하게 사용할 수 있는 키워드가 volatile이다. 변수를 volatile 타입으로 지정하면 앞서 설명한 최적화를 수행하지 않고 모든 메모리 쓰기를 지정한 대로 수행한다.

사용하는 곳
1. MIMO(Memory-mapped I/O)
2. 인터럽트 서비스 루틴(Interrupt Service Routine)의 사용
3. 멀티 쓰레드 환경

세 가지 모드 공통점은 현재 프로그램의 수행 흐름과 상관없이 외부 요인이 변수 값을 변경할 수 있다는 점이다. 인터럽트 서비스 루틴이나 멀티 쓰레드 프로그램의 경우 일반적으로 스택에 할당하는 지역 변수는 공유하지 않으므로, 서로 공유되는 전역 변수의 경우에만 필요에 따라 volatile을 사용하면 된다.

분주

개념과 필요성

체배: 원래의 신호를 크게 하는 것
분주: 원래의 신호를 나눈다. 원래의 신호보다 작게 만드는 것 (divider, prescale)

STM32F429ZI가 168 MHz로 동작을 하고 있다. (100 MHz로 가정)
100 MHz를 1ms로 count한다 했을 때,
1) T(주기) = 1/f(주파수) = 1/100,000,000 = 0.00000001 sec = 10 nsec (nano second)
2) 10ns clock count를 몇 번 세어야 하는가?
3) 100,000번을 세어야 1ms가 나온다
결론 : CPU 부하(load)가 많이 걸린다
즉, 분주를 해야 부하가 적게 걸린다.

168 MHz -> 1 MHz로 분주 (dividor)
T(주기) = 1/f(주파수) = 1/1,000,000 Hz = 0.000001sec = 1 MSec

결론적으로 100 MHz상에서 1ms를 count 하기 위해서는 100,000번의 펄스를 count해야 하므로 그만큼 CPU 부하가 많이 걸린다.
1 MHz에서 1ms를 count 하기 위해서는 1000번만 count하므로 CPU 부하가 적게 걸림을 알 수 있다.
분주를 하는 이유는 CPU 부하를 적게 걸리게 함이 가장 큰 목적이다.

Timer 10

0도 카운트를 하기 때문에 반드시 -1을 붙여줘야 함. 0도 유효 숫자


enabled 시켜주기

// main.c	
static void MX_TIM10_Init(void);

TIM_HandleTypeDef htim10;
...
/* Initialize all configured peripherals */
  ...	
  MX_TIM10_Init();
  
  ...
  /* USER CODE BEGIN 2 */
  HAL_TIM_Base_Start_IT(&htim10);	// IT = Interrupt	ADD_JIMIN_1011
  /* USER CODE END 2 */
  
// move from Dirver/STM32F4xx_HAL_Driver/stm32f4xx_hal_tim.c to here
// enter here when every timer INT occurs
volatile int TIM10_10ms_counter = 0;

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if (htim->Instance == TIM10)
	{
		TIM10_10ms_counter++;
	}
}

  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart3, &rx_data, 1); // assing to RX INT
  HAL_UART_Receive_IT(&huart6, &bt_rx_data, 1); //for BT assign to RX INT
  HAL_TIM_Base_Start_IT(&htim10);	// IT = Interrupt	ADD_JIMIN_1011
  
  TIM10_10ms_counter = 0;
//  led_main();
  /* USER CODE END 2 */
  
   /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  if (TIM10_10ms_counter >= 5)	// 50ms
	  {
		  HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);	// LED1
		  HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);	// LED2
		  HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_14);	// LED3
	  }
   }

LED 1, 2, 3이 번갈아 가면서 나오는 코드 (LED123_toggle)

while (1)
  {
	  if (TIM10_10ms_counter % 5 == 0)	// 50ms
	  {
//		  TIM10_10ms_counter = 0;
		  HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);	// LED1

		  if (TIM10_10ms_counter % 10 == 0)
		  {
			  HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);	// LED2
		  }
		  if (TIM10_10ms_counter % 15 == 0)
		  {
			  HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_14);	// LED3
		  }
	  }
}

혹은..

while (1)
  {
	  if (TIM10_10ms_counter >= 5)	// 50ms
	  {
		  HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);	// LED1

		  if (TIM10_10ms_counter >= 10)
		  {
			  HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);	// LED2
			  if (TIM10_10ms_counter >= 15)
			  {
				  HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_14);	// LED3
				  TIM10_10ms_counter = 0;
			  }
		  }
	  }
}

Timer 11

결선
VCC: 빨간색 (+)
GRN: 검정색 (-)

온습도 센서 연결

분주로 168 MHz를 1 MHz로 만들어 MS_delay에 사용할 것

  • main.c 코드
//main.c

/* USER CODE BEGIN PV */
UART_HandleTypeDef huart3;
UART_HandleTypeDef huart6;
/* USER CODE END PV */

void delay_us(unsigned long us);

static void MX_TIM11_Init(void);
/* USER CODE BEGIN PFP */
extern void DHT11_main(void);

void delay_us(unsigned long us);
/* USER CODE END PFP */

int main(void)
{
  /* Initialize all configured peripherals */
  MX_TIM11_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart3, &rx_data, 1); // assing to RX INT
  HAL_UART_Receive_IT(&huart6, &bt_rx_data, 1); //for BT assign to RX INT
  HAL_TIM_Base_Start_IT(&htim10);	// IT = Interrupt	ADD_JIMIN_1011
  HAL_TIM_Base_Start_IT(&htim11);	// IT = Interrupt	ADD_JIMIN_1011
  TIM10_10ms_counter = 0;
//  led_main();
	DHT11_main();
  /* USER CODE END 2 */
 }
  • DHT11.c 코드
// DHT11.c

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

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

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

void DHT11_main(void);


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_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);
}
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;
}

  • DHT11.h 코드
// DHT11.h

/*
 * DHT11.h
 *
 *  Created on: 2019. 9. 2.
 *      Author: kccistc
 */

#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 /* SRC_DHT11_H_ */

온습도 센서 오실로스코프로 분석


습도 2AH = 42%
온도 1AH = 26도

profile
I mean

2개의 댓글

comment-user-thumbnail
2023년 10월 12일

멋져요 :)

1개의 답글