[아두이노] Debounce에 대해 살펴보자

IOT/아두이노|2019. 3. 18. 09:00

[아두이노] Debounce에 대해 살펴보자



오늘은 Debounce에 대해서 이야기를 하도록 하겠습니다. 채터링 or 바운딩이라는 단어가 있는데 이 의미는 실제 스위치 버턴과 같은 인간이 터치하는 센서가 있을때 이 센서를 누르고 때는 순간에 센서의 접전 부분에서 On/Off 가 여러번 반복되는 현상을 말합니다. 스플링 있는 버턴을 누를고 때는 순간에 그 버턴이 미세하지만 흔들리는 모습을 보시거나 손끝에서 느끼신적이 있을거에요. 키보드 자판을 한번 눌렀다 때 보세요. 느끼실지 모르겠지만 손끝에서 스플링이 누를때랑 땔려는 순간 약하게 미세하게 흔들림을 느끼실꺼에요. 그때가 아주 짧은 시간이지만 스위치 버턴 접전 부위에서 On/Off 가 여러번 반복되는 상황이라고 생각하시면 됩니다. 이런걸 하드웨어인 방법과 소프트웨어적인 방법으로 해결하는데 하드웨어적인 경우는 회로도에서 스위치 버턴이 흐르는 전류를 안정적 제어하기 위한 부품을 달아야 하는데 저항과 캐피시터인가 달아서 해결한다고 하는데 저도 정확히는 하드웨어적인 제어는 해보지 못해서 설명드리기 애매하네요.

아두이노 공식홈페이지에서 소개된 소프트웨어적인 방법을 소개할 하도록 하겠습니다.

1. 회로도 구성


  • 준비물 : 스위치 버턴 1개, Red LED 1개, 저항 220옴 1개, 아두이노우노
  • 내용 : 스위치 버턴을 눌렸을때 Red LED가 켜지게 한다.


3. 코딩



[ 스위치 버턴 기본 소스 ]

void setup()
{
  pinMode(13, OUTPUT);
  pinMode(7, INPUT_PULLUP);
}

void loop()
{
  digitalWrite(13, digitalRead(7));  //버턴을 누르면 불이 꺼지고 안누르면 불이 들어온다.
}

내부풀업모드를 사용할 시에 해당 핀에 초기 상태는 1이고 버턴을 누르면 0이 된다. 그래서 스위치 버턴 기본소스에서는 위 회로도에서는 누르면 불이 들어오고 때면 불이 꺼진다.



[ Dedboune 기본 소스 ] 출처는 아두이노공식홈페지의 튜토리얼 소스 내용으로 링크 걸어 놨네요. 가셔서 한번 읽어보세요.

int debounceDelay = 50; //바운드에 대한 기준 시간값
unsigned long lastDebounceTime = 0; // 현재시간값을 가져와서 저장할 변수 자료형은 같은형으로 맞춤
int buttonstate=1; //내부풀업모드 버턴이 초기값이 1이여서 초기값을 1로 잡음.
int lastButtonState=1; //마지막 버턴상태를 기록변수

void loop(){

    int reading = digitalRead(BUTTONPIN); //스위치 버턴상태값을 읽는다.

    if (reading != lastButtonState) { //현재 읽은 버턴의 상태값과 이전상태가 다른지 체크
        lastDebounceTime = millis(); // 다르면 변화(On/Off)가 일어난거니 그 시간을 변수에 저장시킨다.
    }

    if ((millis() - lastDebounceTime) > debounceDelay) {  //버턴변화가 50시간값 안에 발생했으면 바운스(채터링)으로 무시
                
                if(reading!=buttonState){ // 버턴이 On/Off 됐는지 확인(버턴의 변화가 일어났는가)       
                    buttonSate=reading; //버턴의 변화가 일어났으면 그 변화를 기록했다가 다음 버턴 변화의 비교대상이 됨
                    
                    스위치 On/Off로 인정한 동작 수행;                  
                }
        }
    
    lastButtonState = reading; //현재 읽은 버턴의 상태를 다음 버턴의 상태와 비교하기 위해 변수에 저장한다.
}

쉽게 말해서, 현재의 버턴이 누른다면 이전상태변수(lastButtonState)은 누르지 않는 상태이겠죠. 누르니깐 현재상태변수(reading)와는 다른 상태 변화가 일어나니깐 그 변화가 일어난 시간을 기록해서 그 변화가 일어나는 간격이 50이라는 시간값과 비교해서 50이하의 시간에 바운스(On/Off) 상태가 일어나면 그냥 무시한다는 의미이지요.

번역하자면

  1. 버턴의 상태값 읽기
  2. 버턴의 상태가 이전버턴의 상태와 다른가 체크하여 변화가 일어나면 그 시간을 저장한다.
  3. 현재의 시간과 이전시간과 차이가 50이하면 바운스(채터링)으로 간주하고 50이상일때는 버턴이 눌러진걸로 확정한다.

결론은 50이라는 시간값 안에 일어난 변화는 바운스(채터링)이다. 이런뜻이죠.

두 코딩을 합치면

int debounceDelay = 50; //바운드에 대한 기준 시간값
unsigned long lastDebounceTime = 0; // 현재시간값을 가져올꺼니간 자료형은 같은형으로 맞춘다.
int buttonstate=1; //왜! 1이냐면 내부풀업모드 버턴이 초기값이 1이여서 초기값을 1로 잡음.
int lastButtonState=1; //마지막 버턴상태를 기록한다. 왜냐면 다음 버턴상태와 비교하기 위해서이다.
boolean ledState = false; // LED 반전 상태값을 만들려고 변수를 선언했군요.


void setup()
{
  pinMode(13, OUTPUT); //13(RedLED)핀을 출력모드로 쓸꺼야!
  pinMode(7, INPUT_PULLUP); //스위치버턴을 내부풀업모드로 쓸꺼야.
}

void loop()
{
    int reading = digitalRead(7); //버턴값 읽는다.
    
    if (reading != lastButtonState) { //버턴의 변화가 일어났니!
        lastDebounceTime = millis(); // 그러면 현재 시간을 저장한다.
    }
    if ((millis() - lastDebounceTime) > debounceDelay) {  //버턴변화가 50시간값 안에 발생했으면 바운스(채터링)으로 무시
                                
        //진입! 성공! 50시간값이상 벌어졌으니 버턴으로 눌러진 걸로 인정 받는 거야!
            if(reading!=buttonState){ // 버턴이 On/Off 됐는지 확인(버턴의 변화가 일어났는가)       
                buttonSate=reading; //버턴의 변화가 일어났으면 그 변화를 기록했다가 다음 버턴 변화의 비교대상이 됨
                ledState=!ledState; 
        }

    }
    digitalWrite(13, ledState);  //버턴을 On/Off를 인정받은 ledState값으로 Red LED을 깜박인다.
  lastButtonState = reading; //현재 읽은 버턴의 상태를 다음 버턴의 상태와 비교하기 위해 변수에 저장한다.
}

주석 처리한 내용만 읽어보시면 대충 이해가 되실꺼에요.

이해가 안된다면 다시 한글로 로직을 설명


void loop(){
reading 변수에 현재 스위치버턴값을 읽는다.

만약, 현재스위치버턴(reading)의 상태가 이전스위치버턴상태1(lastButtonState) 다르면 변화시간(lastDebounceTime)을 저장 한다.

만약, 현재시간(millis())-변화시간(lastDebounceTime) 차 값이 50보다 작으면 채터링으로 무시하고 넘어가고 50보다 크면 스위치 버턴의 변화를 인정한다.

만약, 현재스위치버턴(reading)이 이전버턴상태2(buttonState)가 다르면 스위치 On/Off로 간주하고 다음 동작을 수행한다. 여기서 이전버턴상태2(buttonState)은 변화로 인정받은 스위치버턴상태값이다.

인정받았으니 현재스위치버턴(reading)값은 이전버턴상태(buttonState)에 저장한다. 다음에 비교 대상이 되기 때문이다.

마지막으로 이전스위치버턴상태1(lastButtonState)은 현재스위치버턴(reading)값을 저장한다. 왜냐면 다음에 변화가 일어나는 시간을 기록하기 위해서 다음에 비교 대상이 되기 때문이다.


대충 이렇게 글로써 표현할 수 있습니다. 여기서 왜 이전스위치상태변수를 lastButtonState, buttonState로 비슷한 변수로 둘을 사용했냐면 lastButtonState은 우선 버턴의 변화가 일어나는 시간을 찾기 위해서 reading가 비교하기 위해서 입니다. 버턴이 On/Off 인정을 받던 안받던 무조건 버턴의 변화가 일어나면 그 시간을 기록해서 그 시간값이 50 이하면 채터링으로 간주하기 위해서 체크용 변수입니다. buttonState은 실제 버턴이 On/Off 된것로 인정하고 그 변화를 상태를 비교하기 위해 체크용으로 사용하는 변수입니다. 둘 차이점을 정확히 이해하시고 보면 어렵지 않을거에요.

4. 결과


스위치를 누르니깐 정상적으로 불이 들어 왔네요.


마무리


오늘은 프로그래언어쪽으로 좀 더 깊은 내용이였네요. 어려우시다면 그냥 잊으셔도 됩니다. 원리는 정리하자면 50이라는 시간값을 주고 버턴이 On/Off가 발생하면 그 버턴의 On/Off를 무시한다는 내용입니다. 50이상일때 버턴의 On/Off를 인정받고 스위치가 눌러진 다음 동작인 Red LED에 불이 들어오게 한다는게 오늘 주제의 핵심입니다.

가상시뮬레이터에서 실험하시면 컴퓨터상이니간 바운스(채터링)은 발생하지 않습니다. 실제 스플링이 붙어 있는 버턴을 누르면 원치 않는 결과를 얻을 수 있습니다. 어디까지 버턴의 터치를 인정받아야 하는지를 개발자거 설정해야 합니다. 오늘 주제는 소프트웨어적인 방식으로 제어 했지만 이 방법이 정석이라고 할 수 없습니다. 하드웨어적으로 저항+캐피시터를 부착해서 전류를 안정적으로 제어해서 채터링이 사전에 발생하지 않게 하드웨어적으로 아예 만들어 버리면 쉽게 해결 되겠죠.

하지만 우리가 실험을 하면 하드웨어적 부분은 약할 수 밖에 없습니다. 어떤 부품을 부착해야하고 어느 위치에 그 부품을 위치해야할지 막막합니다. 최대한 기초적 지식으로 아두이노를 다루면서 나머지 부분은 소프트웨어적으로 접근해서 문제를 해결하는게 더 빠릅니다. 프로그램적으로는 구글검색만 조금만 하면 왠만한 표현이 오픈소스로 거의 대부분 제공되고 있으니깐 어렵지 않게 아두이노를 공부하실 수 있을거에요.

댓글()