[Intel AI SW 아카데미] 초음파 센서와 I2CLCD

Jimeaning·2023년 10월 13일
0

Intel AIoT

목록 보기
2/38

2023.10.12 18일차

오늘의 학습 목표

  1. 초음파 센서 code 구현 및 구동
  2. I2C LCD Interface
    • I2C Protocol 이론 & 오실로스코프 분석

숙제
1. I2C protcol 오실로스코프 분석
2. dht11on 명령을 내리면 DHT11 정보 출력
dht11off 명령을 내리면 DHT11 정보 출력 하지 않음
ultra_on 명령을 내리면 거리정보 출력
ultra_off 명령을 내리면 거리정보 출력 하지 않음
3. DHT11 data sheet와 오실로스코프 분석 자료를 보고 DHT11.c를 coding 하기

타이머로 온습도 출력 조절하기

send 버튼을 누름에 따라 1.5초, 2초, 3초 단위로 출력되도록 만드는 프로그램

// main.c

int main(void)
{
	while (1)
  	{
	  DHT11_processing();
      
      pc_command_processing();
	  bt_command_processing();
    }
}
// uart.c

extern void DHT11_processing(void);

extern int dht11time;

#define COMMAND_LENGTH 40
volatile unsigned char rx_buff[COMMAND_LENGTH];	// UART3으로부터 수신된 char를 저장하는 공간 (\n을 만날 때까지)
volatile int rx_index = 0; // rx_buff의 save 위치
volatile int newline_detect_flag = 0; // new line을 만났을 때의 indicator 예) ledallon\n

volatile unsigned char bt_rx_buff[COMMAND_LENGTH]; //UART6 으로부터 수신된 char를 저장하는 공간. (\n을 만날 때까지)
volatile int bt_rx_index = 0; // bt_rx_buff의 save 위치
volatile int bt_newline_detect_flag = 0; // new line을 만났을 때의 indicator 예) ledallon\n

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if (huart == &huart3)	// comport master와 연결된 uart
	{
		if (rx_index < COMMAND_LENGTH)	// 현재까지 들어온 바이트가 40바이트를 넘지 않으면
		{
			if (rx_data == '\n' || rx_data == '\r')
			{
				rx_buff[rx_index] = 0;	// '\0'
				newline_detect_flag = 1;	// new line을 만났다는 flag를 set한다
				rx_index = 0;	// 다음 message 저장을 위해서 rx_index 값을 0으로 한다
			}
			else
			{
				rx_buff[rx_index++] = rx_data;
			}
		}
		else
		{
			rx_index = 0;
			printf("Message Overflow!!!\n");
		}
		// 주의: 반드시 HAL_UART_Recieve_IT를 call 해줘야 다음 INT가 발생된다.
		HAL_UART_Receive_IT(&huart3, &rx_data, 1);
	}
}

void pc_command_processing(void)
{
	if (newline_detect_flag)	// \n을 만나면
	{
		newline_detect_flag = 0;
		printf("%s\n", rx_buff);
		if (!strncmp(rx_buff, "dht11time200", strlen("dht11time200")))	// if (strncmp(rx_buff, "dht11time200", strlen("dht11time200")) == 0)
		{
			dht11time = 200;
			return;
		}
		if (!strncmp(rx_buff, "dht11time300", strlen("dht11time300")))	// if (strncmp(rx_buff, "dht11time300", strlen("dht11time300")) == 0)
		{
			dht11time = 300;
			return;
		}
}
// dht11.c

extern volatile int TIM10_10ms_counter;

void DHT11_main(void);
void DHT11_Init(void);
void DHT11_processing(void);
...

int dht11time = 150;
void DHT11_processing(void)
{
	uint8_t i_RH, d_RH, i_Tmp, d_Tmp;

	if (TIM10_10ms_counter >= dht11time)	// 1500ms
	{
		TIM10_10ms_counter = 0;
		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);
	}
}

초음파 센서


1.1 동작 원리

1) TRIG 핀으로 최소 10us의 펄스를 주면 TRIG소자에 초음파가 발사 되고
2) ECHO소자로 반사파가 들어 온다. (거리에 비례)
3) 거리값 구하기
3.1) 소리 속도 : 340M/S
3.2) us단위 변환 : 0.034cm / us (1us 동안 0.034cm 이동)
3.3) 초음파가 1cm 이동 소요시간
T = 2 * 0.01 / 340
= 58.824 us (왕복 시간)
편도 소요시간: 29us

※ 초음파 속도

  • 초당 340M 이동
  • 29us당 1cm 이동
  • 초음파가 반사된 시간을 거리로 환산
  • 초음파 속도는 340m/s 이므로 1cm를 이동하는데 약 29us.
  • 초 음파 이동거리 = 왕복시간 / 1cm 이동 시간 / 2 이다.
    distance = duration / 29 / 2; <=== 센치미터로 환산

결선

VCC: +5V
Trig: PF12
Echo: PA6
GRD

환경설정

길이를 잴 때 인터럽트가 필요하므로 NVIC Settings에서 Interrupt를 Enabled 시켜준다.

Parameter는 84로 분주해주고, Polarity Selection은 Rising과 Falling을 포함하는 Both Edges로 바꿔준다.

PA6은 자동으로 활성화되고, PF12만 따로 활성화 시킨다
PF는 GPIO_Output로 설정한다

다 됐으면 Ctrl + s를 눌러 generate 시켜준다.

코드

// main.h

...
#define ULTRASONIC_TIM_CH1_Pin GPIO_PIN_6
#define ULTRASONIC_TIM_CH1_GPIO_Port GPIOA
...
#define ULTRASONIC_TRIGGER_Pin GPIO_PIN_12
#define ULTRASONIC_TRIGGER_GPIO_Port GPIOF
...
// main.c

volatile int TIM10_10ms_ultrasonic = 0;

/* USER CODE BEGIN PFP */
extern void ultrasonic_process(void);
/* USER CODE END PFP */

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if (htim->Instance == TIM10)
	{
		TIM10_10ms_counter++;
		TIM10_10ms_ultrasonic++;	// 초음파 센서(ultrasonic trigger) timer
	}
}

int main(void)
{
	HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);	// for pulse count (rising edge & falling edge)
    
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  DHT11_processing();
	  pc_command_processing();
	  bt_command_processing();
      ultrasonic_processing();
   }
}

Drivers > STM32F4xx_HAL_Driver > Src > stm32f4xx_hal_tim.c 에서 HAL_TIM_IC_CaptureCallback을 잘라내기 한 후 가져 온다.

// ultrasonic.h

#include "main.h"	// GPIO HAL
// ultrasonic.c

#include "ultrasonic.h"

extern volatile int TIM10_10ms_ultrasonic;

void ultrasonic_process(void);

volatile int distance;	// 거리를 측정 펄스 개수를 저장하는 변수
volatile int ic_cpt_finish_flag = 0;	// 초음파 거리 측정 완료 indicator 변수

volatile uint8_t is_first_capture = 0;	// 0 : 상승 에지, 1 : 하강 에지

// 초음파 센서의 Echo핀의 상승 에지와 하강 에지 발생 시 이 곳으로 들어 온다
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if (htim -> Instance == TIM3)
	{
		if (is_first_capture == 0)	// 상승 에지
		{
			__HAL_TIM_SET_COUNTER(htim, 0);		// clear H/W counter
			is_first_capture = 1;		// 상승 에지를 만났다는 flag
		}
		else if (is_first_capture == 1)	// 하강 에지를 만나면
		{
			is_first_capture = 0;		// 다음 echo 펄스를 count 하기 위해 변수 초기화
			distance = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);	// 현재까지 count한 펄스 수를 읽어 온다
			ic_cpt_finish_flag = 1;		// 초음파 측정 완료
		}
	}
}

void ultrasonic_process(void)
{
	int dis;	// 거리를 측정 펄스 개수를 저장하는 변수
	
	if (TIM10_10ms_ultrasonic >= 100)	// 1초
	{
		TIM10_10ms_ultrasonic = 0;
		make_trigger();
		if (ic_cpt_finish_flag == 1)	// 초음파 측정이 완료되었으면
		{
			ic_cpt_finish_flag = 0;
			dis = distance;
			dis *= 0.034 / 2;	// 1us가 0.034cm를 이동. 왕복 거리를 리턴하기 때문에 /2를 해주는 것.
			printf("dis: %dcm\n", dis);
		}
	}
}

void make_trigger()
{
	HAL_GPIO_WritePin(ULTRASONIC_TRIGGER_GPIO_Port, ULTRASONIC_TRIGGER_Pin, 0);
	HAL_Delay(2);
	HAL_GPIO_WritePin(ULTRASONIC_TRIGGER_GPIO_Port, ULTRASONIC_TRIGGER_Pin, 1);
	HAL_Delay(10);
	HAL_GPIO_WritePin(ULTRASONIC_TRIGGER_GPIO_Port, ULTRASONIC_TRIGGER_Pin, 0);
}

결과


손을 움직이면서 센서에 갖다 대면 해당 거리만큼 출력된다.

I2C

I2C 통신은 두 개의 신호선(SDA, SCL)으로 다수의 디바이스와 송/수신하는 통신 방식이다.
마스터에서 기준 클럭(SCL)을 생성
클럭에 맞춰 데이터(SDA) 전송, 수신
I2C는 동기식 방식(클럭 신호에 맞춰 수신하는 것)

환경설정

I2C를 activate하면 PB9가 generate된다.

결선

SDA: PB9
SCL: PB8

코드

// main.c

extern void i2c_lcd_main(void);

int main(void)
{
	i2c_lcd_main();
}
// i2c_lcd.c

#include "main.h"
#include "i2c_lcd.h"  // < >

#include "stm32f4xx_hal.h"
#include <string.h>
#include <stdio.h>

extern I2C_HandleTypeDef hi2c1;
extern UART_HandleTypeDef huart3;

void i2c_lcd_main(void);

void i2c_lcd_main(void)
{

	uint8_t value=0;
	unsigned char i2c_test[] = {'9', 0};
#if 1	// I2C Test
	while(1)
	{
		while(HAL_I2C_Master_Transmit(&hi2c1, I2C_LCD_ADDRESS,
				i2c_test, 1, 100)!=HAL_OK){
		}
		HAL_Delay(200);
	}

#endif

 	i2c_lcd_init();


	while(1)
	{
		move_cursor(0,0);
		lcd_string("Hello World!!!");
		move_cursor(1,0);
		lcd_data(value + '0');
		value++;
		if(value>9)value=0;
		HAL_Delay(500);
	}
}

// i2c_lcd.h

#ifndef INC_I2C_LCD_H_
#define INC_I2C_LCD_H_

#define I2C_LCD_ADDRESS (0x27<<1)
#define BACKLIGHT_ON 0x08

/* LCD command   */
#define DISPLAY_ON 0x0C
#define DISPLAY_OFF 0x08
#define CLEAR_DISPLAY 0x01  //Delay 2msec
#define RETURN_HOME 0x02

void lcd_command(uint8_t command);
void lcd_data(uint8_t data);
void i2c_lcd_init(void);
void lcd_string(uint8_t *str);
void move_cursor(uint8_t row, uint8_t column);

#endif /* INC_I2C_LCD_H_ */

숙제

  1. I2C protcol 오실로스코프 분석

clock 신호에 맞춰서 데이터를 송수신하는 동기식 방식이기 때문에 기준 clock(SCL)을 생성하고 있다.
0x27이 들어 오고 Ack bit(데이터)가 0x39, 즉 9가 들어 오는 것을 확인

  1. dht11on 명령을 내리면 DHT11 정보 출력
    dht11off 명령을 내리면 DHT11 정보 출력 하지 않음
    ultra_on 명령을 내리면 거리정보 출력
    ultra_off 명령을 내리면 거리정보 출력 하지 않음

코드

// uart.c

extern volatile int DHT11_flag;
extern volatile int ultrasonic_flag;

void pc_command_processing(void)
{
	if (newline_detect_flag)	// \n을 만나면
	{
		newline_detect_flag = 0;
		printf("%s\n", rx_buff);
		if (!strncmp(rx_buff, "dht11_on", strlen("dht11_on")))	// if (strncmp(rx_buff, "ledallon", strlen("ledallon")) == 0)
		{
			DHT11_flag = 1;
			return;
		}
		if (!strncmp(rx_buff, "dht11_off", strlen("dht11_off")))	// if (strncmp(rx_buff, "ledallon", strlen("ledallon")) == 0)
		{
			DHT11_flag = 0;
			return;
		}
		if (!strncmp(rx_buff, "ultra_on", strlen("ultra_on")))	// if (strncmp(rx_buff, "ledallon", strlen("ledallon")) == 0)
		{
			ultrasonic_flag = 1;
			return;
		}
		if (!strncmp(rx_buff, "ultra_off", strlen("ultra_off")))	// if (strncmp(rx_buff, "ledallon", strlen("ledallon")) == 0)
		{
			ultrasonic_flag = 0;
			return;
		}
	}
}
// DHT11.c

volatile int DHT11_flag;

int dht11time = 150;
void DHT11_processing(void)
{
	uint8_t i_RH, d_RH, i_Tmp, d_Tmp;

	if (TIM10_10ms_counter >= dht11time)	// 1500ms
	{
		TIM10_10ms_counter = 0;
		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);

		if (DHT11_flag)
		{
			printf("[Tmp]%d\n",(int)i_Tmp);
			printf("[Wet]%d\n",(int)i_RH);
		}

	}
}
// ultrasonic.c

volatile int ultrasonic_flag;

void ultrasonic_processing(void)
{
	int dis;	// 거리를 측정 펄스 개수를 저장하는 변수

	if (TIM10_10ms_ultrasonic >= 100)	// 1초
	{
		TIM10_10ms_ultrasonic = 0;
		make_trigger();
		if (ic_cpt_finish_flag == 1)	// 초음파 측정이 완료되었으면
		{
			ic_cpt_finish_flag = 0;
			dis = distance;
			dis *= 0.034 / 2;	// 1us가 0.034cm를 이동. 왕복 거리를 리턴하기 때문에 /2를 해주는 것.
			if (ultrasonic_flag)
			{
				printf("dis: %dcm\n", dis);
			}
		}
	}
}

결과 화면

dht11_on을 누르면 온습도 정보가 출력되고, off하면 꺼진다.
ultra_on을 누르면 거리 정보가 출력되고, off하면 꺼진다.

profile
I mean

0개의 댓글