[아두이노] Maker UNO 사용기 : 푸쉬 버튼을 토글 스위치로 사용하기.

    이전 포스팅에서 Cytron 사의 코딩 교육용 아두이노 호환 보드인 Maker UNO를 살펴봤습니다.

    가격도 저렴하며 디자인도 예쁘고 코딩을 처음 접하는 청소년들이나 어린이들이 쉽게 접근할 수 있게끔 설계 되었습니다.

    각 디지털 핀에 LED가 내장되어 있어 신호의 입/출력을 마치 RX/TX LED가 깜빡이는 것과 비슷하게 육안으로 확인 가능하며 푸쉬 버튼과 피에조 부저가 내장되어 있어 여러가지 재미있는 것들을 해볼 수 있습니다.

    디지털 핀의 LED는 확인했고, 리뷰가 길어질 것 같아 해당 포스팅에서 푸쉬 버튼과 피에조 부저의 사용법에 대해서만 간단히 다뤄볼 예정입니다.


    Push Button -> Toggle Button

    앞에 "programmable"이라는 단어가 붙어있는데, 말 그대로 프로그램 가능한 푸쉬 버튼이라는 말로 그냥 '사용 가능하다' 정도로 생각하면 됩니다.

    Cytron사가 제공한 제품 메뉴얼을 살펴보면, 이 버튼은digital 2 pin으로 사용 가능하며 보드에 내장된풀업 저항을 사용하여 입력을 받아야 합니다.

     

    푸쉬 버튼의 동작 특성부터 살펴 보겠습니다.

    푸쉬 버튼을 눌렀을 때를 LOW라고 생각할 때, 평상시에는 계속 HIGH값을 읽어 들이다가 사용자가 버튼을 눌렀을 때만 LOW값을 읽습니다.

    쉽게 말해 푸쉬 버튼은 "안 누름", "누르고 있음"이렇게 두 가지 상태가 있습니다.

     

    하지만 저는 눌렀을 때마다 상태가 바뀌는 토글 버튼으로 사용하고 싶습니다.

    토글 버튼은"활성화(set)", "비 활성화(reset)"이렇게 두 가지 상태가 있습니다.

    여기서 저는 토글 버튼의 현재 상태를 'toggleStatus'라 부르겠습니다.

    사용자가 버튼을 누르는 행위는 현재 toggleStatus를 변경하고자 하는 의지로 표출하는 행위라고 말할 수 있습니다.

    버튼의 값이 HIGH에서 LOW로 바뀌는 순간 사용자의 의지가 보였다고 생각하면 될 것 같네요.

     

    즉, 버튼의 값을 계속 읽고 있다가 LOW가 들어오는 순간부터 다시 HIGH가 들어오는 순간 까지를 잘 정의하면 될 것 같습니다.

    아래는 제가 짜본 코드입니다.

    #define pushButton 2
    
    bool globalFlag = false;
    bool toggleStatus = false;
    void detectButton() {
      int readPin = digitalRead(pushButton);
      if(readPin == LOW) {
        if(globalFlag == false)
          globalFlag = true;
      }
    
      else {
        if(globalFlag == true) {
          if(toggleStatus == true)
            toggleStatus = false;
          else
            toggleStatus = true;
          globalFlag = false;
        }
      }
    }
    
    void setup()
    {
      Serial.begin(115200);
      pinMode(pushButton, INPUT_PULLUP);
    }
    
    void loop()
    {
      detectButton();
      Serial.println(toggleStatus);
      delay(100);
    }

    대학생때 '디지털 논리회로'와 '디지털 시스템설계'라는 과목에서 버튼플립 플롭에 대해 설명을 들어서 개념은 대충 알고 있었습니다.

    하지만 막상 저 혼자 자력으로 구현하려니까 머리가 터질 것 같네요.

    (그땐 이미 함수가 있어 사용하기만 하면 됐었는데...)

     

    이거 하나 짜는데 혼자 손가락 접었다 폈다 하면서 거의 3시간이 걸린 것 같네요

    그나마 상태가 SET/RESET 두 가지라 손가락을 한 개만 써도 되서 다행이었습니다.

     

    개념은 다음과 같습니다.

    버튼을 동작하는데에 있어서 버튼의 상태를 크게 4가지로 구분해 보겠습니다.

    1. 누르지 않은 상태(idle)
    2. 누르는 순간(High to Low / Low to High)
    3. 누르는 중
    4. 떼는 순간(Low to High / High to Low)

    이 4가지 상태를 정확히 구분할 수 있는 함수를 만들어야 합니다.

    이 기능의 Key Point인 변수 설정을 먼저 보겠습니다.

    // global variable
    bool globalFlag = false;
    bool toggleStatus = false;
    
    // local variable
    int readPin = digitalRead(pushButton);

    어느 정도 프로그래밍에 대해 알고 계시겠지만 한번 짚고 넘어가겠습니다.

     

    전역 변수(global variable)는 가장 바깥, 즉 void setup() 함수 및 void main() 함수와 같은 위치에 선언 됩니다.

    그러니 모든 함수들이 이 전역 변수의 존재를 알고 있습니다.

    따라서 어느 함수에서든지 전역 변수를 호출 가능하고, 값을 변경 가능하며 함수가 끝나더라도 값이 유지됩니다.

     

    지역 변수(local variable)는 특정 함수 내부(중괄호 {} 내부)에서 선언된 변수로, 그 함수 내부에서만 의미가 있는 녀석입니다.

    함수가 실행되고 지역 변수가 선언되는 순간에 실질적으로 메모리가 할당되고 함수가 끝나면 할당되었던 메모리가 소멸되는 하루살이 같은 유통기한을 가지고 있습니다.

    만약 반복적/주기적으로 실행되는 함수일지라도, 반복되는 매 순간마다 지역 변수들의 메모리가 할당되는 것입니다.

     

    (저의 저만의 생각대로 작성한 함수이기 때문에, 설명을 드려도 이해가 안될 수 있습니다)

     

    아래 함수만 살펴 보겠습니다.

    globalFlag는 전역 변수로 정의되었습니다.

    이 변수를 만든 의도는"너는 detectButton() 이 종료되어도 계속 그 값을 기억하고 있어라"입니다.

    toggleStatus는 실질적으로 우리가 궁금한 토글 스위치의 상태를 나타내는 변수입니다.

    그래서 당연히 전역 변수로 설정되어야 합니다.

    readPin은 함수 내부에 지역 변수로 함수가 실행될 때, 현재 버튼이 HIGH(안 누름)인지 LOW(누름)인지"현재 버튼의 눌림 상태를 함수가 살아있는 동안만 저장"하는 변수입니다.

    bool globalFlag = false;    // 프로그램이 실행되는 순간에는 RESET 상태로 초기화
    bool toggleStatus = false;    // 프로그램이 실행되는 순간에는 RESET 상태로 초기화
    void detectButton() {
      int readPin = digitalRead(pushButton);    // 현재 버튼의 눌림 상태를 확인함
      if(readPin == LOW) {        // 버튼이 눌렸을 때
        if(globalFlag == false)    // 전역 플래그(globalFlag)가 RESET 상태라면
          globalFlag = true;    // SET으로 변경
      }
    
      // 버튼을 누르고 전역 플래그가 SET 되었는데도,
      // 아직 사용자가 버튼을 누르고 있는 상태라면 아무일도 일어나지 않음.
      // 이 지점에서 함수가 끝나기 때문.
      // 따라서 버튼을 누르고 있는 상태라면 전역 플래그가 RESET 되지 않고, SET 상태를 유지함.
    
      else {                        // 버튼이 안 눌렸을 때 = 누르던중에 떨어졌을 때
        if(globalFlag == true) {    // 전역 플래그가 SET 상태라면,
          if(toggleStatus == true)        // 현재 토글 스위치 상태가 SET 상태라면,
            toggleStatus = false;            // 토글 스위치 상태를 SET에서 RESET으로 변경.
          else                            // 현재 토글 스위치 상태가 RESET 상태라면,
            toggleStatus = true;            // 토글 스위치 상태를 RESET에서 SET으로 변경.
          globalFlag = false;        // 토글 스위치 상태에 변경이 이루어 졌으므로 전역 플래그를 다시 RESET.
        }
      }
    }

    토글 스위치의 상태가 변경되는 조건은 방금 전까지 LOW(누름)였던 버튼 값이 HIGH(안 누름)로 바뀌는 순간입니다.

    그 순간을 정의하기 위해 위와 같이 조건문을 남발했습니다.

     

    댓글

    Designed by JB FACTORY