스위치 채터링 (Switch Chattering)

쿨쿨이·2024년 4월 15일

상승/하강 에지 (Rising/Falling Edge)

디지털에서는 데이터를 0과 1로 표현한다. 전자 회로에서 발생하는 전기 신호가 5V 또는 3.3V인 경우를 HIGH(1), 0V 또는 GND(Ground)인 경우를 LOW(0)라고 한다.

Rising Edge와 Falling Edge는 뭘까?

Rising/Falling Edge

디지털 신호가 0에서 1로, 1에서 0으로 변하는 순간이 있다. 이 때 0->1로 값이 증가하는 순간을 상승 에지(Rising Edge), 1->0으로 감소하는 순간을 하강 에지(Falling Edge)라고 한다.

MCU에서는 상승 에지와 하강 에지 중 조건을 선택해 인터럽트를 발생시킬 수 있다.

채터링(Chattering)

푸시(Push) 버튼을 스위치로 사용할 경우, 바운싱(Bouncing) 또는 스위치 채터링(Switch Chattering)이라는 현상이 발생할 수 있다. 푸시 버튼의 구조 상, 누르거나 뗄 때 발생하는 진동 충격으로 인해 값이 튀게 되어 여러 번 누른 것처럼 인식되곤 한다. 이러한 현상을 스위치 채터링이라고 한다.

Chattering

스위치를 눌렀다 떼면 상승/하강 에지가 각각 1번씩만 발생해야 한다. 하지만 채터링 현상이 생기면, 위 그림처럼 여러차례 상승/하강 에지가 반복해서 발생한다. 이로 인해 버튼을 한번만 눌렀다 떼었음에도, 여러 번 누른 것처럼 인식되는 문제점이 생기는 것이다.

Tact Switch
사진처럼 생긴 택타일 스위치를 풀업으로 연결했고, 캐패시터가 없는 상태에서 테스트를 해봤다. 다음 사진은 오실로스코프를 사용해 스위치를 눌렀다 떼며 직접 측정한 파형이다.

Oscilloscope - Chattering

Pull-Up으로 연결한 택트 스위치를 눌렀다 뗄 때 발생한 파형을 오실로스코프로 직접 찍은 모습이다. 그래프 상의 한 칸은 1ms이고, 약 1~4ms 간격 동안 채터링이 발생함을 확인할 수 있다.

채터링은 Push 버튼의 물리적인 구조로 인해 발생하는데, 해결 방법은 1.하드웨어적인 방법과 2.소프트웨어적인 방법 두 가지가 있다.

방법1. H/W Debounce

하드웨어적으로 해결하는 방법은 캐패시터(Capacitor)를 사용해 물리적으로 전기 신호를 늦추는 것이다.

Circuit Diagram(NUCLEO-F103RB) Board Image(NUCLEO-F103RB)

위 그림은 ST사에서 판매하는 개발 보드 NUCLEO-F103RB 회로의 일부분이다. 회로도를 보면, B1(USER Button) 스위치가 Pull-Up 방식으로 달려 있고 100nF의 캐패시터가 달려있는 것을 확인할 수 있다. 이 부분이 스위치 채터링 현상을 잡아주는 부분이다.

결론 = 스위치 버튼 회로에 캐패시터을 적용해서 채터링을 없앨 수 있다.

그렇다면 캐패시터는 무엇이며, 어떤 원리로 채터링을 잡아줄 수 있을까?

캐패시터(Capacitor)

커패시터는 전류 변화에 따라 충전과 방전을 반복하며, 전류 리플을 제거하는 역할을 한다.

위와 같은 회로에서 버튼을 눌렀다 떼는 과정에서 캐패시터가 어떻게 작용하는지 보자.
0) 기본 상태 : 풀업 회로에서 버튼을 누르지 않으면, 캐패시터가 충전되어 평상 시 HIGH 상태
1) 버튼 누르기 : 캐패시터가 서서히 방전되며 일정 시간 지연 후 LOW 상태로 변경
2) 버튼에서 손 떼기 : 캐패시터가 서서히 충전되며 일정 시간 지연 후 HIGH로 복귀
즉, 캐패시터가 충전 또는 방전될 때는 RC 필터로 인해 지연시간이 생기게 되고, 이 지연 시간 동안 바운싱이 제거되어 단일 펄스 신호만 통과되는 원리이다.

캐패시터와 디바운싱

캐패시터를 사용한 디바운싱 회로는 오른쪽 그림과 같은 형태이다. 그림 상에서 저항(R2)과 캐패시터(C1)가 결합한 형태를 저주파 필터(LPF; Low-Pass Filter)라고 한다. 이처럼 상승/하강 에지가 연속적으로 발생할 때, 캐패시터가 있으면 상승/하강 사이의 레벨 높이를 줄여주게 된다. 이로 인해 MCU가 입력으로 인식하지 못하게 되므로 채터링 현상이 사라지게 되는 것이다.

방법2. S/W Debounce

채터링을 소프트웨어적으로 해결하는 방법은 스위치 입력을 확인하는 부분에 적절한 값의 타이머를 적용하는 것이다.
예를 들어, 버튼 입력으로 인한 외부 인터럽트(EXTI)가 발생했을 때 일정 간격(15ms) 이내로 발생한 인터럽트 신호는 무시하도록 구현하는 것이다.
내가 구현한 코드는 다음과 같다.

static bool exti_flag = false;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	static uint32_t prev;
	volatile uint32_t curr;

	// Debouncing
	curr = HAL_GetTick();
	if(curr - prev > 150)
	{
		if(GPIO_Pin == BTN_Pin)
		{
			exti_flag = true;
		}
		prev = curr;
	}
}

이렇게 인터럽트에 대한 Callback 함수가 호출될 때마다 호출된 시점의 값을 HAL_GetTick()으로 확인하고 이전에 호출된 값과 비교해 인터럽트가 발생한 간격을 확인해주는 것이다. 이 때, prev 값은 함수 실행이 끝난 후에도 남아있어야 하기 때문에 static으로 선언했고, curr 값은 컴파일러의 최적화 방지를 위해 volatile 키워드를 붙여줬다.
위 코드 상에서는 15ms 이내 간격으로 발생한 인터럽트는 무의미한 것으로 간주하고, 패싱하도록 구현되어 있다.
또한, 인터럽트 콜백 함수 내에서 바로 원하는 동작을 실행하지 않고 exti_flag 값을 따로 둔 이유는 인터럽트 루틴을 최소화하기 위해서이다. 따로 나와있지는 않지만 콜백 함수에서 exti_flag를 바꿔주면, 이후 super loop에서 플래그 값이 변경되었을 때만 특정 동작을 실행하도록 구현했다.

구현 결과

debouncing

결론

이렇게 디바운싱(Debouncing)을 하드웨어와 소프트웨어적인 방식 두 가지로 구현해보았다.
나는 소프트웨어 개발자라 그런가 소프트웨어적인 구현이 조금 더 쉽게 느껴졌다. 하지만 스위치의 채터링 현상이 모든 스위치에서 동일한 조건으로 발생하는 것이 아니며, 같은 스위치라도 온도, 습도와 같은 환경적 조건의 영향을 받을 수도 있기 때문에 일괄적으로 적용하기에는 어려워 보였다. 또한, 때로는 개발환경에 따라 성능이 낮고 메모리 제약이 있는 프로세서가 있을 수 있기 때문에, 디바운스 루틴을 구현할 수 있는 코드 공간 또는 클럭 주기가 제한적일 수 있다. 찾아보니, 이러한 경우에는 하드웨어적인 구현이 더 나을 수 있다고 한다.

profile
System Software Developer

0개의 댓글