STM32 #3

홍태준·2026년 1월 15일

STM32

목록 보기
3/15
post-thumbnail

Week 1 Day 3: GPIO 입력 - 버튼 인터럽트

학습 목표

  • GPIO 입력 모드의 동작 원리 이해
  • Pull-up/Pull-down 저항의 필요성과 동작 원리
  • 외부 인터럽트(EXTI)의 개념과 설정 방법
  • 버튼 입력을 인터럽트로 처리하는 방법
  • 채터링(Chattering) 현상과 디바운싱 기법

1. GPIO 입력 기초

1.1 GPIO 입력 모드란?

GPIO 입력 모드는 외부 신호를 읽어들이는 모드입니다. 버튼, 스위치, 센서 등의 디지털 신호를 마이크로컨트롤러로 입력받을 때 사용합니다.

입력 모드의 특징:

  • 핀의 전압 레벨을 읽음 (HIGH: 3.3V, LOW: 0V)
  • 입력 임피던스가 높음 (외부 회로에 영향 최소화)
  • Pull-up/Pull-down 저항 설정 가능

1.2 디지털 입력의 전압 레벨

STM32F4의 디지털 입력 임계값:

VIH (Input HIGH): 2.0V ~ 3.3V
VIL (Input LOW):  0V ~ 0.8V

주의사항:

  • 0.8V ~ 2.0V 사이는 불확정 영역 (Floating)
  • Floating 상태(High에도, Low에도 연결되지 않은 상태, High Impedance)에서는 노이즈에 민감
  • Pull-up/Pull-down 저항으로 해결(전압을 High 또는 Low에 붙여주는 작업)

1.3 버튼 회로의 기본 구조

일반적인 버튼 연결 방법:

Active LOW 방식 (Pull-up 사용)

VCC (3.3V)
    |
   [R] 10kΩ (Pull-up)
    |
    +------- GPIO 핀
    |
  [Button]
    |
   GND
  • 버튼을 누르지 않았을 때: HIGH (3.3V)
  • 버튼을 눌렀을 때: LOW (0V)
  • STM32 내부 Pull-up 저항 사용 가능

Active HIGH 방식 (Pull-down 사용)

VCC (3.3V)
    |
  [Button]
    |
    +------- GPIO 핀
    |
   [R] 10kΩ (Pull-down)
    |
   GND
  • 버튼을 누르지 않았을 때: LOW (0V)
  • 버튼을 눌렀을 때: HIGH (3.3V)
  • STM32 내부 Pull-down 저항 사용 가능

1.4 Pull-up/Pull-down 저항의 역할

Pull-up 저항:

  • 버튼을 누르지 않았을 때 핀을 HIGH로 유지
  • Floating 방지
  • 일반적으로 10kΩ 사용

Pull-down 저항:

  • 버튼을 누르지 않았을 때 핀을 LOW로 유지
  • Floating 방지
  • 일반적으로 10kΩ 사용

STM32 내부 저항:

  • STM32는 내부 Pull-up/Pull-down 저항 내장
  • 약 40kΩ 정도
  • 외부 저항 없이도 사용 가능

2. GPIO 입력 레지스터

2.1 GPIOx_IDR (Input Data Register)

입력 핀의 현재 상태를 읽는 레지스터입니다.

uint32_t input_value = GPIOA->IDR;
  • 읽기 전용 레지스터
  • 각 비트는 해당 핀의 상태 (0 또는 1)
  • 예: GPIOA->IDR & (1 << 0) → PA0 핀 상태 확인

2.2 GPIOx_MODER 설정 (입력용)

입력 모드로 설정하려면 MODER 레지스터를 00으로 설정합니다.

// PA0를 입력 모드로 설정
GPIOA->MODER &= ~(0x3 << (0 * 2));  // 00: Input mode

2.3 GPIOx_PUPDR 설정

Pull-up 또는 Pull-down 저항을 설정합니다.

// PA0에 Pull-up 설정
GPIOA->PUPDR &= ~(0x3 << (0 * 2));
GPIOA->PUPDR |= (0x1 << (0 * 2));   // 01: Pull-up

// PA0에 Pull-down 설정
GPIOA->PUPDR &= ~(0x3 << (0 * 2));
GPIOA->PUPDR |= (0x2 << (0 * 2));   // 10: Pull-down

// PA0에 Pull-up/Pull-down 없음
GPIOA->PUPDR &= ~(0x3 << (0 * 2));  // 00: No pull-up, pull-down

3. HAL을 이용한 GPIO 입력

3.1 GPIO 입력 초기화

GPIO_InitTypeDef GPIO_InitStruct = {0};

// 클럭 활성화
__HAL_RCC_GPIOA_CLK_ENABLE();

// GPIO 설정
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

3.2 GPIO 입력 읽기

GPIO_PinState button_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);

if(button_state == GPIO_PIN_RESET) {
    // 버튼이 눌렸을 때 (Active LOW)
} else {
    // 버튼이 눌리지 않았을 때
}

3.3 Polling 방식 예제

/* USER CODE BEGIN 2 */
GPIO_InitTypeDef GPIO_InitStruct = {0};

// 버튼 핀 초기화 (PA0, Pull-up)
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE END 2 */

/* USER CODE BEGIN WHILE */
while (1)
{
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
  // 버튼 상태 읽기
  if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
    // 버튼이 눌렸을 때 LED 켜기
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
  } else {
    // 버튼이 눌리지 않았을 때 LED 끄기
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET);
  }
  
  HAL_Delay(10);  // 10ms 딜레이
}
/* USER CODE END 3 */

Polling 방식의 문제점:

  • CPU가 지속적으로 버튼 상태를 확인해야 함(처리 속도 저하)
  • 전력 소모 증가
  • 다른 작업 수행 시 버튼 입력을 놓칠 수 있음

4. 외부 인터럽트 (EXTI)

4.1 인터럽트란?

인터럽트는 특정 이벤트가 발생했을 때 현재 실행 중인 프로그램을 중단하고, 미리 정의된 함수(ISR)를 실행하는 메커니즘입니다.

인터럽트의 장점:

  • CPU가 다른 작업을 수행하다가 이벤트 발생 시에만 처리
  • 전력 효율적
  • 빠른 응답 속도
  • 실시간 처리 가능

4.2 EXTI (External Interrupt) 개념

EXTI는 GPIO 핀에서 발생하는 외부 신호 변화를 감지하여 인터럽트를 발생시키는 기능입니다.

EXTI 특징:

  • GPIO 핀의 상태 변화를 감지 (Rising/Falling/Both Edge)
  • 총 16개의 EXTI 라인 (EXTI0 ~ EXTI15)
  • 각 EXTI 라인은 여러 포트의 동일 번호 핀과 연결

EXTI 라인 매핑:

EXTI0: PA0, PB0, PC0, PD0, ... (각 포트의 0번 핀)
EXTI1: PA1, PB1, PC1, PD1, ... (각 포트의 1번 핀)
...
EXTI15: PA15, PB15, PC15, PD15, ...

주의: 하나의 EXTI 라인은 한 번에 하나의 핀만 사용 가능합니다.

4.3 인터럽트 트리거 모드

Rising Edge (상승 에지):

  • LOW → HIGH 변화 시 인터럽트 발생
  • Active HIGH 버튼에 적합

Falling Edge (하강 에지):

  • HIGH → LOW 변화 시 인터럽트 발생
  • Active LOW 버튼에 적합

Both Edges (양 에지):

  • LOW → HIGH 또는 HIGH → LOW 변화 시 모두 인터럽트 발생

4.4 NVIC (Nested Vectored Interrupt Controller)

NVIC는 ARM Cortex-M의 인터럽트 컨트롤러입니다.

NVIC의 역할:

  • 인터럽트 우선순위 관리
  • 인터럽트 활성화/비활성화
  • 펜딩 인터럽트 관리

우선순위:

  • 낮은 숫자 = 높은 우선순위
  • STM32F4는 16단계 우선순위 지원 (0 ~ 15)
  • Preemption Priority: 높은 우선순위 인터럽트가 낮은 우선순위 인터럽트를 중단 가능
  • Sub Priority: 같은 우선순위 내에서의 순서

5. STM32CubeMX에서 EXTI 설정

5.1 GPIO 인터럽트 핀 설정

  1. Pinout & Configuration → System Core → GPIO
  2. PA0 핀 클릭
  3. GPIO_EXTI0 선택

5.2 GPIO 모드 설정

Configuration → GPIO:

GPIO mode: External Interrupt Mode with Falling edge trigger detection
GPIO Pull-up/Pull-down: Pull-up
User Label: USER_BUTTON (선택사항)

5.3 NVIC 설정

Configuration → NVIC:

EXTI line0 interrupt: Enabled
Priority: 0 (기본값)

5.4 코드 생성

Project Manager → Generate Code


6. 인터럽트 서비스 루틴 (ISR) 작성

6.1 HAL 인터럽트 콜백 함수

STM32 HAL에서는 HAL_GPIO_EXTI_Callback() 함수를 사용합니다.

/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin == GPIO_PIN_0) {
    // PA0 인터럽트 발생 시 처리
    HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
  }
}
/* USER CODE END 0 */

6.2 인터럽트 처리 흐름

1. GPIO 핀 상태 변화 감지
2. EXTI 하드웨어가 인터럽트 플래그 설정
3. NVIC가 인터럽트 요청 처리
4. EXTIx_IRQHandler() 호출 (HAL에서 자동 생성)
5. HAL_GPIO_EXTI_IRQHandler() 호출
6. HAL_GPIO_EXTI_Callback() 호출 (사용자 정의)
7. 인터럽트 플래그 클리어
8. 메인 프로그램으로 복귀

6.3 전체 코드 예제

/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin == GPIO_PIN_0) {
    // 버튼이 눌릴 때마다 LED 토글
    HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
  }
}
/* USER CODE END 0 */

int main(void)
{
  /* USER CODE BEGIN 1 */
  /* USER CODE END 1 */

  HAL_Init();
  SystemClock_Config();
  
  MX_GPIO_Init();
  
  /* USER CODE BEGIN 2 */
  /* USER CODE END 2 */

  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    // 메인 루프는 다른 작업 수행 가능
    // 버튼 입력은 인터럽트에서 처리됨
  }
  /* USER CODE END 3 */
}

6.4 생성된 인터럽트 핸들러 확인

stm32f4xx_it.c 파일:

void EXTI0_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI0_IRQn 0 */

  /* USER CODE END EXTI0_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
  /* USER CODE BEGIN EXTI0_IRQn 1 */

  /* USER CODE END EXTI0_IRQn 1 */
}

7. 채터링과 디바운싱

7.1 채터링(Chattering) 현상

버튼을 누르거나 놓을 때 기계적 접점이 여러 번 붙었다 떨어지는 현상입니다.

정의는 위와 같은데 더 깊게 살펴보면 우리가 손으로 버튼을 눌렀을 때 1회만 전원이 연결 되고 끝날 것 같지만 사실은 실제 버튼 내부에서 작은 진동에 의해 버튼이 수차례 눌리는 것 처럼 진동하게 됩니다. 이 현상을 채터링이라고 하며, 이 채터링 현상을 '필터링'해 여러 번 튀는 신호를 단 한번의 입력으로 처리하는 기술을 디바운싱이라고 합니다.

채터링 특징:

  • 한 번 누른 것이 여러 번 눌린 것처럼 인식됨
  • 일반적으로 5~50ms 지속
  • 모든 기계식 스위치에서 발생

채터링 파형:

버튼 누름 시:
    _____     _   _   _________
         |___|_|_|_|_|
         ↑
      여러 번 토글됨 (채터링)

7.2 하드웨어 디바운싱

RC 필터 사용:

Button ---+--- RC 필터 ---+--- GPIO
          |               |
         GND            Pull-up
  • 간단한 RC 회로로 노이즈 제거
  • 회로 복잡도 증가
  • 추가 부품 필요

7.3 소프트웨어 디바운싱

7.3.1 지연 방식

가장 간단한 방법: 일정 시간 대기 후 재확인

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin == GPIO_PIN_0) {
    HAL_Delay(50);  // 50ms 대기 (채터링 안정화)
    
    // 버튼이 여전히 눌려있는지 확인
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
      HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
    }
  }
}

문제점:

  • 인터럽트 내에서 HAL_Delay 사용은 비추천
  • 다른 인터럽트 블로킹 가능

7.3.2 타임스탬프 방식 (권장)

마지막 인터럽트 시간을 저장하여 일정 시간 내 중복 인터럽트 무시

/* USER CODE BEGIN 0 */
uint32_t last_interrupt_time = 0;
const uint32_t debounce_delay = 200;  // 200ms

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin == GPIO_PIN_0) {
    uint32_t current_time = HAL_GetTick();
    
    // 마지막 인터럽트로부터 충분한 시간이 지났는지 확인
    if((current_time - last_interrupt_time) > debounce_delay) {
      HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
      last_interrupt_time = current_time;
    }
  }
}
/* USER CODE END 0 */

장점:

  • 블로킹 없음
  • 빠른 처리
  • 다중 버튼 처리 가능

7.3.3 상태 머신 방식

/* USER CODE BEGIN 0 */
typedef enum {
  BUTTON_IDLE,
  BUTTON_PRESSED,
  BUTTON_DEBOUNCING
} ButtonState_t;

ButtonState_t button_state = BUTTON_IDLE;
uint32_t debounce_start_time = 0;
const uint32_t debounce_time = 50;

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin == GPIO_PIN_0) {
    if(button_state == BUTTON_IDLE) {
      button_state = BUTTON_DEBOUNCING;
      debounce_start_time = HAL_GetTick();
    }
  }
}
/* USER CODE END 0 */

/* USER CODE BEGIN WHILE */
while (1)
{
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
  if(button_state == BUTTON_DEBOUNCING) {
    if((HAL_GetTick() - debounce_start_time) > debounce_time) {
      // 디바운싱 시간 경과 후 버튼 상태 확인
      if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
        // 실제 버튼이 눌린 것으로 확인
        HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
        button_state = BUTTON_PRESSED;
      } else {
        button_state = BUTTON_IDLE;
      }
    }
  } else if(button_state == BUTTON_PRESSED) {
    // 버튼이 놓이길 기다림
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET) {
      button_state = BUTTON_IDLE;
    }
  }
}
/* USER CODE END 3 */

7.4 디바운싱 파라미터 선택

일반적인 디바운싱 시간:

  • 저품질 버튼: 50~100ms
  • 일반 버튼: 20~50ms
  • 고품질 버튼: 10~20ms

실험으로 최적값 찾기:
1. 50ms로 시작
2. 채터링 발생 시 증가
3. 반응이 느리면 감소


8. 심화 예제

8.1 버튼으로 LED 밝기 조절

/* USER CODE BEGIN 0 */
uint8_t brightness = 0;  // 0 ~ 10
uint32_t last_interrupt_time = 0;

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin == GPIO_PIN_0) {
    uint32_t current_time = HAL_GetTick();
    
    if((current_time - last_interrupt_time) > 200) {
      brightness = (brightness + 1) % 11;  // 0 ~ 10 순환
      last_interrupt_time = current_time;
    }
  }
}
/* USER CODE END 0 */

/* USER CODE BEGIN WHILE */
while (1)
{
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
  // 소프트웨어 PWM으로 밝기 조절
  for(int i = 0; i < 100; i++) {
    if(i < brightness * 10) {
      HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
    } else {
      HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET);
    }
    HAL_Delay(1);
  }
}
/* USER CODE END 3 */

8.2 두 개의 버튼 사용하기

/* USER CODE BEGIN 0 */
uint32_t last_button1_time = 0;
uint32_t last_button2_time = 0;

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  uint32_t current_time = HAL_GetTick();
  
  if(GPIO_Pin == GPIO_PIN_0) {  // Button 1
    if((current_time - last_button1_time) > 200) {
      HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);  // LED 1 토글
      last_button1_time = current_time;
    }
  }
  
  if(GPIO_Pin == GPIO_PIN_1) {  // Button 2
    if((current_time - last_button2_time) > 200) {
      HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_13);  // LED 2 토글
      last_button2_time = current_time;
    }
  }
}
/* USER CODE END 0 */

8.3 버튼 길게 누르기 감지

/* USER CODE BEGIN 0 */
uint32_t button_press_time = 0;
uint8_t button_long_press = 0;

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin == GPIO_PIN_0) {
    if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
      // 버튼이 눌림
      button_press_time = HAL_GetTick();
      button_long_press = 0;
    } else {
      // 버튼이 놓임
      uint32_t press_duration = HAL_GetTick() - button_press_time;
      
      if(press_duration > 1000 && !button_long_press) {
        // 1초 이상 눌렀을 때
        HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, GPIO_PIN_SET);
      } else if(press_duration < 1000) {
        // 짧게 눌렀을 때
        HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
      }
    }
  }
}
/* USER CODE END 0 */

/* USER CODE BEGIN WHILE */
while (1)
{
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
  // 버튼을 1초 이상 누르고 있는지 확인
  if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) {
    uint32_t press_duration = HAL_GetTick() - button_press_time;
    
    if(press_duration > 1000 && !button_long_press) {
      // 길게 누름 처리 (한 번만)
      HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, GPIO_PIN_SET);
      button_long_press = 1;
    }
  }
}
/* USER CODE END 3 */

9. 디버깅 팁

9.1 인터럽트가 발생하지 않을 때

확인 사항:
1. GPIO 클럭이 활성화되었는가?
2. EXTI 라인이 올바르게 설정되었는가?
3. NVIC에서 인터럽트가 활성화되었는가?
4. Pull-up/Pull-down 설정이 올바른가?
5. 트리거 모드가 적절한가? (Falling/Rising/Both)

디버깅 코드:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin == GPIO_PIN_0) {
    // 인터럽트가 호출되는지 확인
    static uint32_t counter = 0;
    counter++;
    // 디버거로 counter 값 확인
  }
}

9.2 인터럽트가 너무 자주 발생할 때

원인:

  • 채터링
  • 잘못된 Pull-up/Pull-down 설정
  • 노이즈

해결:

  • 디바운싱 시간 증가
  • 하드웨어 필터 추가
  • 케이블 길이 줄이기

9.3 여러 인터럽트 동시 처리 문제

우선순위 설정:

// EXTI0 우선순위: 1
HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 0);

// EXTI1 우선순위: 2 (낮음)
HAL_NVIC_SetPriority(EXTI1_IRQn, 2, 0);

9.4 인터럽트 내에서 피해야 할 것들

절대 하지 말 것:

  • 긴 처리 시간 (수 ms 이상)
  • HAL_Delay() 사용
  • printf() 등 느린 함수 호출
  • 복잡한 계산

권장 방법:

  • 플래그만 설정하고 메인 루프에서 처리
  • 데이터만 버퍼에 저장
/* USER CODE BEGIN 0 */
volatile uint8_t button_pressed = 0;

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin == GPIO_PIN_0) {
    button_pressed = 1;  // 플래그만 설정
  }
}
/* USER CODE END 0 */

/* USER CODE BEGIN WHILE */
while (1)
{
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
  if(button_pressed) {
    button_pressed = 0;
    
    // 여기서 실제 처리 수행
    // 복잡한 로직, HAL_Delay 등 사용 가능
    HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
  }
}
/* USER CODE END 3 */

10. 실습 과제

10.1 과제 1: 토글 스위치 구현

버튼을 누를 때마다 LED 상태를 토글하는 프로그램을 작성하세요.

요구사항:

  • 버튼을 누르면 LED ON
  • 다시 누르면 LED OFF
  • 디바운싱 적용

10.2 과제 2: 카운터 구현

버튼을 누를 때마다 카운터를 증가시키고, 4개의 LED로 2진수 표시

요구사항:

  • 버튼 누를 때마다 카운터 증가 (0 ~ 15)
  • LED로 2진수 표시
  • 15에서 다시 0으로 순환

힌트:

uint8_t counter = 0;

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if(GPIO_Pin == GPIO_PIN_0) {
    counter = (counter + 1) % 16;
    
    // LED 업데이트
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, (counter & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_13, (counter & 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, (counter & 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOD, GPIO_PIN_15, (counter & 0x08) ? GPIO_PIN_SET : GPIO_PIN_RESET);
  }
}

10.3 과제 3: 더블 클릭 감지

버튼을 빠르게 두 번 누르는 것을 감지하는 프로그램 작성

요구사항:

  • 300ms 이내에 두 번 누르면 더블 클릭으로 인식
  • 싱글 클릭: LED 1 토글
  • 더블 클릭: LED 2 토글

11. 정리

11.1 오늘 배운 내용

  1. GPIO 입력 모드의 동작 원리
  2. Pull-up/Pull-down 저항의 필요성
  3. 외부 인터럽트(EXTI)의 개념과 설정
  4. 인터럽트 서비스 루틴 작성 방법
  5. 채터링 현상과 디바운싱 기법
  6. Polling vs Interrupt 비교

11.2 Polling vs Interrupt 비교

구분PollingInterrupt
CPU 사용지속적으로 확인 필요이벤트 발생 시만 동작
전력 소모높음낮음
응답 속도폴링 주기에 의존빠름
구현 복잡도낮음중간
놓칠 가능성있음없음

11.3 참고 자료

profile
당신의 코딩 메이트

0개의 댓글