2023.10.12 18일차
숙제
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
※ 초음파 속도
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 통신은 두 개의 신호선(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_ */
clock 신호에 맞춰서 데이터를 송수신하는 동기식 방식이기 때문에 기준 clock(SCL)을 생성하고 있다.
0x27이 들어 오고 Ack bit(데이터)가 0x39, 즉 9가 들어 오는 것을 확인
코드
// 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하면 꺼진다.