아두이노 버튼 누를때마다 - adu-ino beoteun nuleulttaemada

아두이노

[ 아두이노 ] 버튼 눌러서 LED 패턴 변경하기 (p.115 문제6.3)

mminky 2021. 2. 14. 00:51

[ 문제 ]

디지털 2번 핀에서 5번 핀까지 5개의 LED를 연결하고 디지털 14번 핀에 풀다운 저항을 통해 버튼을 연결한다.

LED에는 다음의 패턴이 0.5초 간격으로 반복되도록 한다.

단, 버튼이 눌러진 경우에 패턴의 진행방향이 바뀌도록 스케치를 작성해보자.

즉, 버튼이 한 번 눌러지면 패턴은 '1>2>3>4' 순서의 순방향으로 진행되고,

버튼이 다시 눌러지면 패턴은 '4>3>2>1' 순서의 역방향으로 진행되도록 한다.

시작 시에는 순방향으로 패턴이 바뀌는 것으로 한다.

아두이노 버튼 누를때마다 - adu-ino beoteun nuleulttaemada


[ 코드 ]

결과영상

int pins_LED[] = {2,3,4,5};

int pins_button = 14;

int LED_pattern[] = {0b0001,0b0010,0b0100,0b1000};

boolean state_previous = false;

int i=0;

int state = 0;



void setup() {

  // put your setup code here, to run once:

  Serial.begin(9600);

  

  pinMode(pins_button,INPUT);

  for(int i=0;i<4;i++){

    pinMode(pins_LED[i],OUTPUT);

  }

}



void loop() {

  // put your main code here, to run repeatedly:

    boolean state_current = digitalRead(pins_button);



  if(state ==0){// 2>3>4>5

       if((state_current==1) && (state_previous==0)){

          i-=2;

          state_previous = 1;

          state = 1;

      }

      else{

        if((state_current==0)&&(state_previous==1)){

        state_previous = 0;  

        }

        if(i==4) i=0;

        if(i<4){

          for(int j=0;j<4;j++){

            digitalWrite(pins_LED[j],bitRead(LED_pattern[i],j)); //BITREAD read right->left

            }

            i++;

            delay(500);

           }

         }

       }



    if(state == 1){ //2<3<4<5

      if((state_current==1) && (state_previous==0)){

          i+=2;

          state_previous = 1;

          state = 0;         

      }

      else{

        if((state_current==0)&&(state_previous==1)){

        state_previous = 0;  

        }

        

        if(i==-1) i=3;

        if(i>=0){

          for(int j=0;j<4;j++){

            digitalWrite(pins_LED[j],bitRead(LED_pattern[i],j));

          }         

          i--;

         delay(500);

         }

      }

  }

  

}

※ 제 글이 도움이 되었다면 공감 부탁드려요 :)

Arduino

아두이노 - 입력 버튼 설정 방법, debouncing

NextPop 2019. 9. 21. 18:44

일상생활에서 버튼을 이용한 장치 들을 많이 볼 수 있다. 스마트 폰에도 물리적인 버튼이 있고 시계에도 버튼이 있다. 아두이노에 단순히 버튼을 연결하여 사용할 수 있을 것 같으나 아두이노와 같은 MCU에서는 버튼을 사용하는 게 장치에 전원을 넣고 끄는 것처럼 단순하지만은 않게 된다.

아두이노 버튼 누를때마다 - adu-ino beoteun nuleulttaemada

* 택트 스위치의 경우 연결 방향을 잘 확인해야 한다. 다리가 삐져나온 쪽에서 연결해줘야 정상 연결이 된다. 

아두이노에서 버튼을 설정하기 위해서는 우선 pinMode를 통해 버튼이 연결될 핀을 입력으로 설정을 해주어야 한다. 

int buttonPin = 7; // 버튼 핀 번호 

pinMode(buttonPin, INPUT); 

하지만 이코드를 적용하게 되면 버튼과 핀의 연결 과정에서 PULL-UP 저항 회로 또는 PULL-DOWN 저항 회로를 버튼과 함께 구성해 주어야만 아두이노는 입력 핀에서 0과 1을 구분할 수 있다. 자세한 사항은 인터넷에서 "아두이노 버튼 PULL-UP저항 회로"를 검색하면 살펴볼 수 있다.

아두이노 버튼 누를때마다 - adu-ino beoteun nuleulttaemada
풀업 저항 회로 및 풀다운 저항 회로 예

회로를 구성하기 위해서는 저항도 필요하고 5V 전원 핀을 점유하게 되어 복잡해진다.

아두이노의 메인칩(MCU)에는 20K 옴의 PULL-UP 저항이 내장되어 있다. 이 PULL-UP 저항을 이용하면 위의 PULL-UP 저항 회로 또는 PULL-DOWN 저항 회로를 외부에 구성하지 않고 버튼만 연결해도 입력 핀에서 0과 1을 구분할 수 있게 된다. 즉 내부 저항을 이용하여 PULL-UP 저항 회로를 구현하는 것이다. 주의할 점은 HIGH가 OFF가 되고 LOW가 ON이 된다는 것이다. 내장 PULL-UP 저항을 이용하는 방법은 매우 간단하다. pinMode에서 INPUT대신 INPUT_PULLUP 옵션만 적용해주면 된다.   

pinMode(buttonPin, INPUT_PULLUP);    

보통 자주 사용하는 버튼은 버튼을 누르면 ON이되고 버튼을 떼면 OFF가 되는 푸시버튼이 아니라 버튼을 한 번 누르면 ON 상태를 유지하고 또 한번 누르면 OFF 상태를 유지하는 토글 버튼이다. 이를 코드상으로 구현해야 한다. 

푸시버튼 코드는 매우 단순하다. loop() 함수의 디지털 출력 코드상에서 입력 핀의 현재 값을 읽어오기만 하면 된다. 

digitalWrite(13, !digitalRead(buttonPin));

반면에 토글 버튼 코드는 버튼이 눌렸을 때만(digitalRead(buttonPin) == 0) 어떤 코드를 실행시키고 또한, 한번 눌렸을 때는 ON 또 한 번 눌렀을 때 OFF가 되는, 상태가 반대로 변하는 플래그 코드를 구현하고, 그 플래그에 따라 LED가 켜지거나 어떤 작동을 하도록 코드를 짜주어야 한다.

여기서 버튼의 설정이 끝나는 건 아니다. 모든 물리 버튼에는 미세한 떨림, 채터링 또는 바운싱 현상이 있다. 접점이 붙는 순간 그 점접이 자석처럼 딱 붙어있는 게 아니라 떨리게 되고 그 떨림을 아두이노는 입력으로 모두 인식하게 된다. 이러한 채터링 현상을 제어하지 않게 되면 버튼을 누를 때 코드가 연속해서 실행 되고 원하는 대로 제어가 되지 않게 된다.

button_toggle.ino

0.00MB

int ledPin = 13;
int buttonPin = 7;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
}

bool Led_state = LOW;

void loop() {
  if (digitalRead(buttonPin) == 0) {
    Led_state = !Led_state;
    digitalWrite(ledPin, Led_state);
  }
}

상기 코드를 아두이노에 업로드하고 버튼을 클릭해보면 아두이노의 기본 LED가 수없이 깜박이다 켜지거나 꺼지는 것을 볼 수 있다. 접점이 떨릴 때마다 0과 1이 입력되고 0이 입력되었을 때 LED가 켜지고 다시 0이 입력되었을 때 LED가 꺼지는 동작을 무수히 반복하는 것이다. 떨림의 마지막 입력이 Led_state == true로 끝나면 LED가 켜지고 Led_state == false로 끝나면 LED는 꺼진 상태가 되는 것이다. 

이런 채터링 또는 바운싱을 해결하기 위해서 캐퍼시터를 이용하여 캐퍼시터가 충전 또는 방전되는 시간 동안의 입력을 지연시키는 회로를 구성하여 해결하는 물리적인 방법과 코드로 해결하는 소프트웨어 적인 방식이 있는데, 코드로 해결해 보자. 

아두이노 버튼 누를때마다 - adu-ino beoteun nuleulttaemada
캐퍼시터를 이용한 채터링 및 바운싱 해결 방법 예

소프트 웨어 방식으로 채터링 또는 바운싱을 효과적으로 제어하기 하기 위해서는 두 가지 방식을 동시에 사용해야 원하는 결과를 얻을 수 있다. 

1. 버튼 핀을 통해 들어온 입력값이 이전 입력값과 다를 경우에만 코드 실행

2. 코드가 실행되면 버튼의 입력 지연을 구현하여 일정 시간 내의 입력은 무시 

1번을 위해 아래 변수들을 선언해 주고 코드를 적용해 준다. 

bool pre_button_state = 1; // 이전 상태 저장 변수, PULL_UP button 접점 붙은 상태: 0, 떨어진 상태: 1 -> 초기값 1
bool button_state = 1;      // 버튼 핀을 통해 들어온 현재 상태 저장 변수, 초기값 1 

button_state = digitalRead(buttonPin); // 버튼 핀의 입력값을 저장하고 

if (button_state != pre_button_state) {  // 저장된 값이 앞선 값과 다르면 코드 실행

  실행 코드;

  pre_button_state = button_state;      // 다음 실행 시 비교할 수 있도록 현재 핀 입력값을 앞선 값에 저장 

}

2번을 위해 millis() 함수를 이용하여 사용자 함수를 만들고 지연 값을 설정해 준다. 접점의 떨림 현상이 발생하는 시간은 버튼의 접점 형상에 따라 다르겠지만 보통 100 밀리초 이내라고 볼 수 있다. 즉, 아두이노가 버튼의 입력을 인식한 이후부터 100 밀리초 내에 들어오는 입력을 무시하도록 코드를 짜주게 되면 물리적인 입력 지연과 같은 효과를 내게 된다.

사용자 함수 제어용 플래그와 밀리 초 저장용 변수를 설정해 주고 아래 사용자 함수를 만들어준다. 

bool button_hold = false;                  // 입력 지연 트리거 플래그

unsigned int long button_delay = 0;    // 밀리 초 저장 변수

void button() {                                // 입력 지연 사용자 함수
  if (button_hold == true) {                // 입력지연 시작이면
    if (millis() - button_delay >= 100) {  // 기준 시간에서 100 밀치 초가 지나면
      button_hold = false;                   // 입력지연 정지
    } 
  } 
}

1번의 실행코드에 진입했을 때 트리거 될 수 있도록 실행코드 영역에 아래 코드를 삽입시켜준다. 

button_hold = true;      // 입력 지연 함수 시작
button_delay = millis();  // 기준시간은 현재 밀리초부터

아래는 상기 코드를 모두 적용한 스케치이다.

button_toggle_debjounce.ino

0.00MB

int ledPin = 13;
int buttonPin = 7;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
}

unsigned int long button_delay = 0;
bool button_hold = false;
bool pre_button_state = 1; // PULL_UP button 접점 붙은 상태: 0, 떨어진 상태: 1 -> 초기값 1
bool button_state = 1;  
bool Led_state = LOW;

void loop() {
  button();
  button_state = digitalRead(buttonPin);
  if (button_hold == false) {
    if (button_state != pre_button_state) {
      button_hold = true;
      button_delay = millis();
      if (button_state == 0) {
        Led_state = !Led_state;
        digitalWrite(ledPin, Led_state);
      }
      pre_button_state = button_state;
    }
  }
}

void button() {
  if (button_hold == true) {
    if (millis() - button_delay >= 100) {
      button_hold = false;
    }
  }
}

버튼을 누를 때마다 정확하게 LED가 켜지고 꺼지는 것을 확인할 수 있다.

delay() 함수를 대체하기 위해 millis() 함수를 이용한 사용자 함수를 만들어 주었다. delay() 함수 대체 방법에 대해서는 이전 글 "아두이노 - 디지털 도어락 예제, delay() 함수 대체 방법"을 참조하기 바란다. 

관련 글

[arduino] - 아두이노 - 입력 버튼 설정 방법, debouncing

[arduino] - 아두이노 - ADC 핀을 이용한 터치 센서의 구현

[arduino] - 아두이노 - 무드등 예제, RGB LED 제어

[arduino] - 아두이노 - 무드등, 블루투스 연결 스마트폰 원격제어