STM32 #15

홍태준·2026년 3월 25일

STM32

목록 보기
15/15
post-thumbnail

Week 3 Day 5: 프로젝트: 초음파 센서(HC-SR04) 거리 측정

학습 목표

  • HC-SR04 초음파 센서의 동작 원리와 신호 타이밍을 이해한다
  • Trigger 펄스 생성 및 Echo 신호 측정을 위한 타이머 구성 방법을 익힌다
  • 입력 캡처(Input Capture)를 활용한 Echo 펄스 폭 측정 구현을 이해하고 적용한다
  • 측정된 시간 값을 거리로 변환하는 공식을 이해하고 오차 원인을 분석한다
  • 노이즈 및 측정 오류에 대응하는 필터링 및 유효성 검사 기법을 적용한다

HC-SR04는 초음파 펄스의 왕복 시간을 측정하여 거리를 계산하는 센서입니다. STM32에서는 Trigger 신호 생성에 GPIO + 타이머 지연을 사용하고, Echo 신호 측정에 타이머 입력 캡처를 활용합니다. 이번 강의에서는 센서 인터페이스 전체 흐름을 구현하고, 실제 측정값의 정확도를 높이기 위한 필터링 기법까지 다룹니다.


1. HC-SR04 센서 개요

1.1 센서 사양 및 핀 구성

HC-SR04 사양

동작 전압  : 5V DC
동작 전류  : 15mA
측정 주파수: 40kHz 초음파
측정 범위  : 2cm ~ 400cm
측정 각도  : 15도 이내
Trigger 입력 펄스 폭: 최소 10us HIGH
Echo 출력: 펄스 폭이 왕복 시간에 비례 (최대 38ms = 타임아웃)

핀 구성

VCC  → 5V 전원
GND  → GND
TRIG → Trigger 입력 (MCU GPIO 출력)
ECHO → Echo 출력  (MCU GPIO 입력 / 타이머 입력 캡처)

주의: ECHO 핀 출력 전압은 5V
      STM32 GPIO는 3.3V 허용 → 전압 분배 회로 또는 레벨 시프터 필요

전압 분배 예시 (5V → 3.3V):

1.2 동작 원리 및 신호 타이밍

측정 원리

음속: 약 340m/s (20도 기준)
     = 0.034cm/us = 1cm당 29.4us

거리 계산:
Echo 펄스 폭(us) = 초음파 왕복 시간
거리(cm) = Echo 펄스 폭(us) / 58
         = Echo 펄스 폭(us) × 0.017

(왕복이므로 편도 시간 = 펄스 폭 / 2, 거리 = (펄스 폭 / 2) / 29.4)

신호 타이밍 다이어그램

타이밍 규격:
- TRIG HIGH: 최소 10us
- TRIG → ECHO 상승 엣지: 약 450us ~ 600us (센서 내부 처리)
- Echo 펄스 폭: 거리에 비례 (2cm = 116us, 400cm = 23,200us)
- 최소 측정 주기: 60ms 이상 권장 (이전 초음파 반향 소거)

2. 하드웨어 구성 및 타이머 설정

2.1 핀 배치 및 회로 구성

권장 핀 배치 (STM32F411 기준)

HC-SR04 TRIG → PA1  (GPIO 출력, Push-Pull)
HC-SR04 ECHO → PA6  (TIM3_CH1, 입력 캡처 AF)

전압 분배 회로를 ECHO와 PA6 사이에 삽입

TIM2: Trigger 지연 생성용 (10us 펄스 타이밍)
TIM3: Echo 입력 캡처용 (CH1: 상승 엣지, CH2: 하강 엣지)

CubeMX 타이머 설정

TIM3 설정 (입력 캡처):
- Clock Source  : Internal Clock
- Prescaler     : 83 (84MHz / (83+1) = 1MHz → 1us 분해능)
- Counter Period: 65535 (16비트 최대)
- CH1           : Input Capture direct mode, Rising Edge
- CH2           : Input Capture indirect mode (CH1으로부터), Falling Edge
- NVIC          : TIM3 global interrupt 활성화

TIM2 설정 (Trigger 지연):
- Clock Source  : Internal Clock
- Prescaler     : 83
- Counter Period: 9 (10us 카운트 후 UIF 발생)
- One Pulse Mode: 활성화 (1회 카운트 후 자동 정지)
- NVIC          : TIM2 global interrupt 활성화

2.2 입력 캡처 동작 구조

간접 캡처(Indirect Capture) 구조

TIM3_CH1 (PA6): 상승 엣지 캡처 (Echo HIGH 시작)
TIM3_CH2      : 하강 엣지 캡처 (Echo LOW 종료, CH1 핀을 공유)

CubeMX에서 CH2를 "Input Capture indirect mode"로 설정하면
CH1 핀(PA6)의 신호를 반전하여 CH2가 하강 엣지를 캡처함

측정 흐름:
1. Echo 상승 엣지 → TIM3 CH1 캡처 레지스터에 CNT 저장
2. Echo 하강 엣지 → TIM3 CH2 캡처 레지스터에 CNT 저장
3. 펄스 폭 = CH2 캡처값 - CH1 캡처값 (us 단위)

3. 펌웨어 구현

3.1 전체 파일 구조

project/
├── Core/
│   ├── Inc/
│   │   ├── hcsr04.h
│   │   └── main.h
│   └── Src/
│       ├── hcsr04.c
│       └── main.c

3.2 hcsr04.h

#ifndef HCSR04_H
#define HCSR04_H

#include "stm32f4xx_hal.h"

/* 센서 측정 상태 */
typedef enum
{
    HCSR04_IDLE       = 0,
    HCSR04_TRIGGERED,
    HCSR04_CAPTURING,
    HCSR04_DONE,
    HCSR04_TIMEOUT,
    HCSR04_ERROR,
} HCSR04_State_t;

/* 센서 핸들 구조체 */
typedef struct
{
    TIM_HandleTypeDef *htim_capture;   /* 입력 캡처 타이머 (TIM3) */
    TIM_HandleTypeDef *htim_trigger;   /* Trigger 지연 타이머 (TIM2) */
    GPIO_TypeDef      *trig_port;      /* TRIG GPIO 포트 */
    uint16_t           trig_pin;       /* TRIG GPIO 핀 */
    uint32_t           capture_rise;   /* 상승 엣지 캡처값 */
    uint32_t           capture_fall;   /* 하강 엣지 캡처값 */
    volatile HCSR04_State_t state;     /* 현재 측정 상태 */
    float              distance_cm;    /* 최종 거리 결과 */
} HCSR04_Handle_t;

/* 측정 유효 범위 */
#define HCSR04_ECHO_MIN_US    116U     /* 2cm   = 116us  */
#define HCSR04_ECHO_MAX_US  23200U     /* 400cm = 23200us */
#define HCSR04_TIMEOUT_MS      50U     /* 타임아웃 50ms   */

/* API */
void  HCSR04_Init(HCSR04_Handle_t *dev,
                  TIM_HandleTypeDef *htim_cap,
                  TIM_HandleTypeDef *htim_trig,
                  GPIO_TypeDef *trig_port,
                  uint16_t trig_pin);

void  HCSR04_Trigger(HCSR04_Handle_t *dev);
float HCSR04_GetDistance(HCSR04_Handle_t *dev);

/* ISR 콜백 내에서 호출 */
void  HCSR04_CaptureCallback(HCSR04_Handle_t *dev, TIM_HandleTypeDef *htim);
void  HCSR04_TriggerDoneCallback(HCSR04_Handle_t *dev, TIM_HandleTypeDef *htim);

#endif /* HCSR04_H */

3.3 hcsr04.c

#include "hcsr04.h"

/* 음속 기반 변환 상수 (1us당 거리, 단위: cm) */
/* 왕복이므로 편도 = 1/2, 음속 340m/s = 0.034cm/us → 0.034/2 = 0.017 */
#define US_TO_CM   (0.017f)

/* 초기화 */
void HCSR04_Init(HCSR04_Handle_t *dev,
                 TIM_HandleTypeDef *htim_cap,
                 TIM_HandleTypeDef *htim_trig,
                 GPIO_TypeDef *trig_port,
                 uint16_t trig_pin)
{
    dev->htim_capture = htim_cap;
    dev->htim_trigger = htim_trig;
    dev->trig_port    = trig_port;
    dev->trig_pin     = trig_pin;
    dev->capture_rise = 0;
    dev->capture_fall = 0;
    dev->state        = HCSR04_IDLE;
    dev->distance_cm  = -1.0f;

    /* TRIG 핀 초기 LOW */
    HAL_GPIO_WritePin(trig_port, trig_pin, GPIO_PIN_RESET);

    /* 입력 캡처 시작 */
    HAL_TIM_IC_Start_IT(htim_cap, TIM_CHANNEL_1);  /* 상승 엣지 */
    HAL_TIM_IC_Start_IT(htim_cap, TIM_CHANNEL_2);  /* 하강 엣지 */
}

/* Trigger 펄스 시작 */
void HCSR04_Trigger(HCSR04_Handle_t *dev)
{
    if (dev->state != HCSR04_IDLE)  return;  /* 이전 측정 미완료 시 무시 */

    dev->state = HCSR04_TRIGGERED;

    /* TRIG HIGH */
    HAL_GPIO_WritePin(dev->trig_port, dev->trig_pin, GPIO_PIN_SET);

    /* TIM2 시작: 10us 후 UIF 발생 → ISR에서 TRIG LOW */
    __HAL_TIM_SET_COUNTER(dev->htim_trigger, 0);
    HAL_TIM_Base_Start_IT(dev->htim_trigger);
}

/* TIM2 UIF ISR에서 호출: TRIG LOW 처리 */
void HCSR04_TriggerDoneCallback(HCSR04_Handle_t *dev, TIM_HandleTypeDef *htim)
{
    if (htim->Instance != dev->htim_trigger->Instance)  return;

    HAL_TIM_Base_Stop_IT(dev->htim_trigger);
    HAL_GPIO_WritePin(dev->trig_port, dev->trig_pin, GPIO_PIN_RESET);

    dev->state = HCSR04_CAPTURING;

    /* TIM3 카운터 초기화 (캡처 기준점 설정) */
    __HAL_TIM_SET_COUNTER(dev->htim_capture, 0);
}

/* TIM3 입력 캡처 ISR에서 호출 */
void HCSR04_CaptureCallback(HCSR04_Handle_t *dev, TIM_HandleTypeDef *htim)
{
    if (htim->Instance != dev->htim_capture->Instance)  return;
    if (dev->state != HCSR04_CAPTURING)                 return;

    if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
    {
        /* 상승 엣지: Echo 시작 */
        dev->capture_rise = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
    }
    else if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
    {
        /* 하강 엣지: Echo 종료 */
        dev->capture_fall = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);
        dev->state        = HCSR04_DONE;
    }
}

/* 거리 계산 및 반환 */
float HCSR04_GetDistance(HCSR04_Handle_t *dev)
{
    if (dev->state != HCSR04_DONE)  return -1.0f;

    uint32_t echo_us;

    /* 오버플로우 처리 */
    if (dev->capture_fall >= dev->capture_rise)
    {
        echo_us = dev->capture_fall - dev->capture_rise;
    }
    else
    {
        /* 16비트 카운터 오버플로우 발생 시 */
        echo_us = (0xFFFF - dev->capture_rise) + dev->capture_fall + 1U;
    }

    /* 유효 범위 검사 */
    if (echo_us < HCSR04_ECHO_MIN_US || echo_us > HCSR04_ECHO_MAX_US)
    {
        dev->state       = HCSR04_ERROR;
        dev->distance_cm = -1.0f;
        return -1.0f;
    }

    dev->distance_cm = (float)echo_us * US_TO_CM;
    dev->state       = HCSR04_IDLE;

    return dev->distance_cm;
}

3.4 main.c (콜백 연결 및 측정 루프)

#include "main.h"
#include "hcsr04.h"
#include <stdio.h>

/* HAL 타이머 핸들 (CubeMX 생성) */
extern TIM_HandleTypeDef htim2;
extern TIM_HandleTypeDef htim3;

/* 센서 핸들 */
HCSR04_Handle_t hcsr04;

/* 입력 캡처 콜백 → HCSR04 드라이버로 위임 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    HCSR04_CaptureCallback(&hcsr04, htim);
}

/* 타이머 주기 만료 콜백 (TIM2: Trigger 10us 완료) */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    HCSR04_TriggerDoneCallback(&hcsr04, htim);
}

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_TIM2_Init();
    MX_TIM3_Init();
    MX_USART2_UART_Init();

    /* 센서 초기화: TIM3(캡처), TIM2(트리거), PA1(TRIG 핀) */
    HCSR04_Init(&hcsr04, &htim3, &htim2, GPIOA, GPIO_PIN_1);

    uint32_t last_trigger = 0;

    while (1)
    {
        /* 60ms 간격으로 측정 (이전 초음파 반향 소거) */
        if (HAL_GetTick() - last_trigger >= 60U)
        {
            last_trigger = HAL_GetTick();
            HCSR04_Trigger(&hcsr04);
        }

        /* 측정 완료 시 결과 출력 */
        if (hcsr04.state == HCSR04_DONE)
        {
            float dist = HCSR04_GetDistance(&hcsr04);

            if (dist >= 0.0f)
            {
                printf("Distance: %.1f cm\r\n", dist);
            }
            else
            {
                printf("Distance: out of range\r\n");
            }
        }
    }
}

4. 측정 정확도 향상

4.1 이동 평균 필터 (Moving Average Filter)

이동 평균 필터 원리

단일 측정값은 반사면 각도, 노이즈, 다중 반사 등으로 튐 현상 발생
N회 측정값의 평균을 사용하면 노이즈 감소

N이 클수록 안정적이나 응답 지연 증가
실용 범위: N = 4 ~ 16

이동 평균 필터 구현

#define FILTER_SIZE  8U

typedef struct
{
    float    buf[FILTER_SIZE];
    uint8_t  idx;
    uint8_t  count;
    float    sum;
} MovAvgFilter_t;

void filter_init(MovAvgFilter_t *f)
{
    f->idx   = 0;
    f->count = 0;
    f->sum   = 0.0f;
    for (uint8_t i = 0; i < FILTER_SIZE; i++)  f->buf[i] = 0.0f;
}

float filter_update(MovAvgFilter_t *f, float new_val)
{
    /* 유효하지 않은 값은 필터에 반영하지 않음 */
    if (new_val < 0.0f)  return (f->count > 0) ? (f->sum / f->count) : -1.0f;

    f->sum -= f->buf[f->idx];
    f->buf[f->idx] = new_val;
    f->sum        += new_val;

    f->idx = (f->idx + 1) % FILTER_SIZE;
    if (f->count < FILTER_SIZE)  f->count++;

    return f->sum / f->count;
}

필터 적용 예시

MovAvgFilter_t dist_filter;

int main(void)
{
    // ...초기화...
    filter_init(&dist_filter);

    while (1)
    {
        if (HAL_GetTick() - last_trigger >= 60U)
        {
            last_trigger = HAL_GetTick();
            HCSR04_Trigger(&hcsr04);
        }

        if (hcsr04.state == HCSR04_DONE)
        {
            float raw  = HCSR04_GetDistance(&hcsr04);
            float filt = filter_update(&dist_filter, raw);

            if (filt >= 0.0f)
            {
                printf("Raw: %.1f cm | Filtered: %.1f cm\r\n", raw, filt);
            }
        }
    }
}

4.2 온도 보정

음속의 온도 의존성

음속(m/s) = 331.5 + 0.6 × T(도)

온도별 음속 및 오차:
T = 0도  → 331.5m/s
T = 20도 → 343.5m/s (기본값으로 340 사용 시 약 1% 오차)
T = 40도 → 355.5m/s

100cm 측정 기준:
T = 0도 기준 340m/s로 계산 시: 오차 약 ±8mm
T = 40도 기준 340m/s로 계산 시: 오차 약 ±15mm

온도 보정이 필요한 경우: 측정 정밀도 5mm 이하 요구 시
온도 보정이 불필요한 경우: 일반적인 장애물 감지(수 cm 허용 오차)

온도 보정 적용 코드

/* 온도 센서(LM35 등)로부터 온도 읽기 후 적용 */
float HCSR04_GetDistanceWithTempComp(uint32_t echo_us, float temp_c)
{
    float sound_speed_cm_us = (331.5f + 0.6f * temp_c) * 0.0001f;
    /* m/s → cm/us 변환: × 100 / 1,000,000 = × 0.0001 */

    return ((float)echo_us * sound_speed_cm_us) / 2.0f;
}

4.3 중간값 필터 (Median Filter)

중간값 필터 원리

N회 측정 후 중앙값을 선택
이상치(Outlier) 제거에 효과적
반사 노이즈, 전기적 노이즈에 의한 단발성 오류 제거

N = 3 (최소 구성), N = 5 권장

중간값 필터 구현 (N=5)

#define MEDIAN_SIZE  5U

float median_filter(float *buf, uint8_t size)
{
    /* 버블 정렬 후 중앙값 반환 */
    float sorted[MEDIAN_SIZE];
    for (uint8_t i = 0; i < size; i++)  sorted[i] = buf[i];

    for (uint8_t i = 0; i < size - 1; i++)
    {
        for (uint8_t j = 0; j < size - 1 - i; j++)
        {
            if (sorted[j] > sorted[j + 1])
            {
                float tmp     = sorted[j];
                sorted[j]     = sorted[j + 1];
                sorted[j + 1] = tmp;
            }
        }
    }

    return sorted[size / 2];  /* 중앙값 */
}

5. 타임아웃 처리

5.1 Echo 신호 미수신 처리

타임아웃이 발생하는 상황

1. 측정 범위 초과 (400cm 이상, Echo 펄스 폭 > 23ms)
2. 반사면 없음 (개방 공간, 흡음 재질 등)
3. 센서 연결 불량

Echo 미수신 시 상태 머신이 CAPTURING에 고착
→ 다음 HCSR04_Trigger() 호출이 무시됨
→ 주기적인 타임아웃 해제 처리 필요

타임아웃 처리 구현

/* HCSR04_Handle_t 에 타임아웃 필드 추가 */
typedef struct
{
    // ...기존 필드...
    uint32_t trigger_tick;   /* Trigger 시점의 HAL_GetTick() 값 */
} HCSR04_Handle_t;

/* Trigger 시 타임아웃 시작 시각 기록 */
void HCSR04_Trigger(HCSR04_Handle_t *dev)
{
    if (dev->state != HCSR04_IDLE)  return;

    dev->trigger_tick = HAL_GetTick();
    dev->state        = HCSR04_TRIGGERED;

    HAL_GPIO_WritePin(dev->trig_port, dev->trig_pin, GPIO_PIN_SET);
    __HAL_TIM_SET_COUNTER(dev->htim_trigger, 0);
    HAL_TIM_Base_Start_IT(dev->htim_trigger);
}

/* 메인 루프에서 주기적으로 호출 */
void HCSR04_Poll(HCSR04_Handle_t *dev)
{
    if (dev->state == HCSR04_CAPTURING || dev->state == HCSR04_TRIGGERED)
    {
        if (HAL_GetTick() - dev->trigger_tick > HCSR04_TIMEOUT_MS)
        {
            dev->state       = HCSR04_TIMEOUT;
            dev->distance_cm = -1.0f;
            /* 다음 측정을 위해 IDLE로 복귀 */
            dev->state       = HCSR04_IDLE;
        }
    }
}

/* main 루프 */
while (1)
{
    HCSR04_Poll(&hcsr04);   /* 타임아웃 감시 */

    if (HAL_GetTick() - last_trigger >= 60U)
    {
        last_trigger = HAL_GetTick();
        HCSR04_Trigger(&hcsr04);
    }

    if (hcsr04.state == HCSR04_DONE)
    {
        float dist = HCSR04_GetDistance(&hcsr04);
        printf("Distance: %.1f cm\r\n", (dist >= 0.0f) ? dist : -1.0f);
    }
}

6. 디버깅

6.1 측정값 오류 유형 및 원인 분석

증상별 원인 분석

증상 1: 거리가 항상 -1.0f (측정 실패)
원인 1-1: ECHO 신호가 MCU에 도달하지 않음
         → 전압 분배 회로 및 배선 확인
         → 오실로스코프로 PA6 핀 파형 확인
원인 1-2: 입력 캡처 초기화 누락
         → HAL_TIM_IC_Start_IT() 호출 여부 확인
원인 1-3: TRIG 핀이 HIGH가 되지 않음
         → CubeMX GPIO 출력 설정 확인

증상 2: 측정값이 심하게 요동침 (±수 cm)
원인: 다중 반사, 센서 주변 장애물, 전원 노이즈
해결: 이동 평균 필터 또는 중간값 필터 적용
     센서 전용 디커플링 커패시터 추가 (100nF + 10uF)

증상 3: 측정값이 실제보다 항상 크거나 작음
원인: 음속 상수 오류 또는 타이머 분해능 불일치
확인: Prescaler 계산 재검토 (1MHz = 1us 분해능 여부)
     US_TO_CM 상수 (0.017) 재확인

증상 4: 첫 번째 측정만 성공하고 이후 실패
원인: state가 DONE 또는 ERROR에서 IDLE로 복귀하지 않음
해결: HCSR04_GetDistance() 호출 후 state = IDLE 확인
     타임아웃 처리 로직 점검

6.2 DWT를 이용한 타이밍 검증

Trigger 및 Echo 타이밍 검증

/* TRIG 펄스 폭 검증 */
uint32_t t_start, t_end;

t_start = DWT->CYCCNT;
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
/* TIM2 10us 대기 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
t_end = DWT->CYCCNT;

uint32_t trig_us = (t_end - t_start) / 84U;
printf("TRIG pulse width: %lu us\r\n", trig_us);
/* 기대값: 10us ± 1us */

6.3 일반적인 실수와 해결

문제 1: ECHO 5V → 3.3V 변환 누락

STM32 GPIO 최대 허용 입력: VDD + 0.3V ≈ 3.6V
5V 직결 시 ESD 다이오드를 통해 전류 유입 → GPIO 손상 또는 오동작

반드시 전압 분배 회로 또는 레벨 시프터 삽입
비용 절감 시: 1kΩ + 2kΩ 분압 (5V → 3.33V)
정밀 보호 필요 시: 74LVC1T45 또는 BSS138 레벨 시프터

문제 2: 측정 주기가 너무 짧아 이전 초음파 간섭

이전 측정의 초음파가 먼 거리에서 반사되어 돌아오는 동안
다음 TRIG 펄스가 발사되면 잘못된 Echo 감지 발생

최소 측정 주기: 60ms 이상 (데이터시트 권장)
400cm 왕복 시간: 약 23ms + 안전 여유 = 60ms

문제 3: TIM3 Prescaler 계산 오류

목표: 1us 분해능 (1MHz 카운트 클럭)
84MHz 기준: Prescaler = 84 - 1 = 83

잘못된 설정:
Prescaler = 84 → 클럭 = 84MHz / 85 ≈ 988kHz (1us보다 느림)
Prescaler = 83 → 클럭 = 84MHz / 84 = 1MHz (정확)

CubeMX Prescaler 입력값 = 실제값 - 1 임에 주의

7. 학습 정리

오늘 배운 내용

HC-SR04 센서 인터페이스

  • TRIG: 10us HIGH 펄스 생성 (GPIO + TIM2 One Pulse Mode)
  • ECHO: 입력 캡처로 펄스 폭 측정 (TIM3 간접 캡처 CH1/CH2)
  • 거리 계산: echo_us × 0.017 = 거리(cm)
  • ECHO 핀 5V → 전압 분배 회로로 3.3V 변환 필수

상태 머신 기반 드라이버 구조

  • IDLE → TRIGGERED → CAPTURING → DONE / ERROR / TIMEOUT
  • 비동기 측정: Trigger 호출 후 콜백으로 캡처, 메인 루프에서 결과 조회
  • 타임아웃 처리: HCSR04_Poll()로 50ms 초과 시 IDLE 복귀

측정 정확도 향상

  • 이동 평균 필터: 노이즈 감소, N = 8 권장
  • 중간값 필터: 이상치 제거에 효과적, N = 5 권장
  • 온도 보정: 정밀 측정 시 음속 보정 적용

핵심 개념 요약

1. 거리 계산 공식

거리(cm) = Echo 펄스 폭(us) × 0.017
         = Echo 펄스 폭(us) / 58

음속 340m/s 기준, 왕복 편도 계산
1cm 왕복 = 약 58us

2. 타이머 설정 핵심

TIM3 Prescaler: 84 - 1 = 83  (1MHz, 1us 분해능)
TIM3 CH1: 상승 엣지 캡처 (direct)
TIM3 CH2: 하강 엣지 캡처 (indirect, CH1 핀 공유)
TIM2 ARR: 10 - 1 = 9         (10us 후 UIF → TRIG LOW)

3. 입력 캡처 오버플로우 처리

if (fall >= rise)
    echo_us = fall - rise;
else
    echo_us = (0xFFFF - rise) + fall + 1U;

4. 측정 주기 및 유효 범위

최소 측정 주기 : 60ms
유효 Echo 범위 : 116us (2cm) ~ 23200us (400cm)
타임아웃 기준  : 50ms 내 DONE 미달성 시 IDLE 복귀

HC-SR04 드라이버 체크리스트

항목확인 사항상태
전압 분배ECHO 5V → 3.3V 변환 회로 적용 여부
PrescalerTIM3 1us 분해능 설정 확인 (83)
입력 캡처CH1 상승/CH2 하강 엣지 설정 여부
TRIG 펄스10us 이상 HIGH 유지 확인
측정 주기60ms 이상 간격 유지 여부
타임아웃50ms 이내 미완료 시 IDLE 복귀 처리
유효 범위116us ~ 23200us 외 값 필터링 여부
필터 적용이동 평균 또는 중간값 필터 적용 여부
profile
당신의 코딩 메이트

0개의 댓글