본문 바로가기

강좌/아두이노 강좌

아두이노 기초강좌 10. 무작정 on/off LED 만들기와 분석

 지난 7~8강에서 우리는 버튼을 누르는 동안에만 켜지는 LED를 만들었었습니다. 이번에는 버튼을 누르면 LED가 켜지고 버튼을 다시 누르면 LED가 꺼지도록 만들어보고, 코드를 분석해보려고 합니다. 

 

회로도는 지난 강의에서 했던 것과 똑같이 구성해주면 됩니다.

코드는 밑의 코드를 사용하시면 되고, 보드에 업로드 해줍니다.

const int LED = 13;

const int BUTTON = 7;


int val= 0;

int old_val = 0;

int state = 0;



void setup()

{

  pinMode(BUTTON,INPUT);

  pinMode(LED,OUTPUT);

}


void loop()

{

  val = digitalRead(BUTTON);


  if((val==HIGH)&&(old_val==LOW))

  {

    state = 1-state;

    delay(50);

  }


  old_val = val;


  if(state == 1)

  {

    digitalWrite(LED,HIGH);

  }

  else

  {

    digitalWrite(LED,LOW);

  }

}


이제 이 코드를 보드에 업로드 해주면, 버튼을 누르면 LED가 켜지고, 다시 누르면 꺼지는 것을 확인할 수 있을 것입니다.



아마 전의 두 코드에서 보지 못했던 것들이 많이 나와서 당황스러우실수도 있을겁니다. 

이제 코드를 한 줄 씩 분석해볼까요? 


const int LED = 13;

const int BUTTON = 7;


 const 는 상수를 지정해주는 코드입니다. 전의 9강에서 int에 대해 설명을 드릴 때 창고로 설명을 드렸었는데요, const 는 이 창고의 문을 잠궈서 다른 값을 저장하지 못하게 한다고 생각하시면 이해하기 쉬우실 겁니다.

int LED = 13; 를 쓰면 'LED'라는 이름의 창고에 '13'이라는 값을 저장해주게 됩니다. 이렇게 되면 뒤에 다른 코드에서 LED = 12;라는 코드를 써주면 LED라는 창고에 '13' 대신 '12' 값을 저장해주게 됩니다. 

그런데 이 앞에 const 를 붙여서 const int LED = 13;을 쓰면 'LED'라는 이름의 창고에 '13'이라는 값을 저장해준 뒤 창고의 문을 잠궈서 저장된 값이 바뀌지 않게 해줍니다. 이 코드 뒤에 LED = 12;를 써주면 컴파일 중에 오류가 뜨게 됩니다. 사실 이 코드에서는 const를 써 줄 필요가 없긴 합니다만, 나중에 더 복잡한 코드를 작성할 때 쓸 일이 생기게 될 것입니다.


int val= 0;


 'val'이라는 창고에 '0'이라는 값을 저장해줍니다. 여기에는 우리가 버튼을 눌렀을 때의 상태를 저장할 것입니다.


int old_val = 0;


 'old_val'이라는 창고에 '0'이라는 값을 저장해줍니다. 'old_val'에는 'val'의 이전 상태를 저장하기 위해 사용해줄 것입니다. 이 코드를 사용하는 이유는 아래에서 설명해드리겠습니다. 


int state = 0;


 'state'라는 창고에 '0'이라는 값을 저장해줍니다. state에 0이 저장되었을 때는 LED가 꺼지게 될 것이고, 1이 저장되었을 때는 LED가 켜지게 될 것입니다.


void setup()

{

  pinMode(BUTTON,INPUT);

  pinMode(LED,OUTPUT);

}


BUTTON이라는 핀을 INPUT(입력)으로 설정합니다. 우리는 앞에서 BUTTON이라는 창고에 7이라는 값을 저장해준 뒤 창고의 문을 잠궈놨습니다. 따라서 이 코드는 7번 핀을 INPUT(입력)으로 설정한다는 뜻입니다.

마찬가지로 LED라는 핀을 OUTPUT(출력)으로 설정해준다는 뜻인데, 우리는 LED에 13이라는 값을 저장해뒀었습니다. 따라서 이 코드는 13번 핀을 OUTPUT(출력)으로 설정한다는 뜻입니다.


void loop()

{


이제 반복문을 시작합니다.



val = digitalRead(BUTTON);


val이라는 창고에 BUTTON핀, 즉 7번 핀에서 읽어온 값을 저장해줍니다. 버튼을 누르면 1,HIGH가 저장되고 누르지 않으면 0, LOW가 저장됩니다.


if((val==HIGH)&&(old_val==LOW))

  {


'val' 에 저장된 값이 HIGH, 즉 1이고, old_val에 저장된 값이 0일때' 중괄호 안의 코드를 실행해줍니다.

'A && B' 는 A도 참(TRUE)이고, B도 참(TRUE)일 때라는 뜻입니다. 

우리가 버튼을 누르게 되면 val에는 HIGH, 1이 저장되고 old_val은 우리가 코드 맨 앞에서 0, LOW로 저장해놨기 때문에 if 안의 코드를 실행하게 됩니다. 


state = 1-state;


'state'라는 창고에 1-state한 값을 저장해줍니다. ㅈ

LED가 꺼진 상태였다면 state는 0이었기 때문에, state에는 1-0의 값인 1이 새로 저장됩니다.

반대로 LED가 켜진 상태였다면 state는 1이었기 때문에, state에는 1-1의 값인 0이 새로 저장됩니다.

수학식이라고 생각하면 말이 안되지만, 프로그래밍에서 기호 = 는 '내 뒤에 오는 값'을 앞의 '창고'에 저장해준다는 의미입니다. 따라서, 이 경우 '1 - 이전 state'의 값이 새로 state에 저장되게 됩니다.


delay(50);


이 코드는 '바운싱'이라고 하는 오류를 제거하기 위해 추가한 코드입니다. 0.05초간 아무런 작동을 하지 않고 정지합니다.

9강에서 설명드린 것처럼 푸쉬버튼은 두 금속 조각이 스프링에 떨어진 상태로 버튼을 누르면 전기가 흐르고 누르지 않으면 전기가 흐르지 않는 구조입니다. 굉장히 간단한 구조이고 잘 작동할 것 같지만, 버튼을 완전히 누르지 않았을 때 '바운싱'이라고 하는 가짜 신호가 나타나게 됩니다. 푸시 버튼에 바운싱이 일어나게 되면 아두이노는 버튼이 켜지고 꺼지는 신호가 아주 빠르게 바뀌는 것처럼 인식하게 됩니다. 때문에 처음 신호가 들어오고 0.1초간 다른 가짜 신호가 들어오지 않게 delay(50);을 추가해줍니다.


}


if((val==HIGH)&&(old_val==LOW)) 안의 코드를 종료해줍니다.


 old_val = val;


 old_val에 val의 값을 저장해줍니다. 만약 old_val을 사용하지 않는다면 버튼을 누를 때 LED가 매우 빠르게 깜빡이는 모습을 볼 수 있을 것입니다. 

 아두이노는 매우 빠릅니다. 1초에 16만개의 명령어를 실행할 수 있을 정도입니다. 즉, 우리가 버튼을 1초 동안 누르고 있으면, BUTTON핀에서 'val'이라는 창고로 1이라는 값을 16만번 저장해주게 될 것이고, state값은 1초에 16만번 1과 0을 왔다갔다 하게 될 것입니다. delay(50)을 쓴다 하더라도 1초에 20번 값이 왔다갔다 하게 되죠. 따라서 우리는 버튼이 눌리는 그 순간을 감지하기 위해 새로운 val의 값을 읽기 전에 이전 val의 값을 old_val에 저장해준 뒤 이 값을 val의 값과 비교해주는 겁니다. 

 우리가 버튼을 처음 누르는 순간에는 val에는 1이, old_val에는 0이 저장될 것입니다. 따라서 if((val==HIGH)&&(old_val==LOW))안의 코드가 한 번 실행되게 되죠. 

 그런데 코드가 실행된 이후에도 버튼을 누르고 있는다면 val에는 그대로 1이 저장되어 있겠지만, old_val에는 이 코드 때문에 0이 아닌 1이 저장될 것입니다. 따라서 if((val==HIGH)&&(old_val==LOW))안의 코드가 실행되지 않죠.

 하지만 손을 버튼에서 떼는 순간 val에는 0이 저장되고, old_val에도 역시 0이 저장될 것입니다. if((val==HIGH)&&(old_val==LOW))안의 코드는 당연히 실행되지 않지만, 다시 버튼을 누르게 되면 버튼을 누르는 순간 old_val에는 0이 저장되어 있고, val에는 1이 저장되게 되니 if((val==HIGH)&&(old_val==LOW))안의 코드가 실행되게 됩니다. 


 if(state == 1)

  {

    digitalWrite(LED,HIGH);

  }


위의  if((val==HIGH)&&(old_val==LOW))안의 코드가 처음 실행되게 되면 state에는 1이 저장되게 됩니다. 따라서 위 코드가 실행되고 LED핀, 즉 13번 핀이 켜지게 됩니다. 


 else

  {

    digitalWrite(LED,LOW);

  }


반대로 if((val==HIGH)&&(old_val==LOW))안의 코드가 다시 실행되게 되면 state에는 0이 저장되게 됩니다. 따라서 위 코드가 실행되고 LED핀이 꺼지게 됩니다. 


}


이 중괄호는 반복문의 종료를 알립니다. 



조금은 어려웠을지도 모르겠네요.


코드 순서를 정리해보자면


1. LED라는 창고에 13이라는 값을 저장해 준 뒤 창고의 문을 잠궈줍니다.

2. BUTTON이라는 창고에 7이라는 값을 저장해 준 뒤 창고의 문을 잠궈줍니다.

3. val, old_val, state에 0을 저장해줍니다.

4. LED핀을 출력으로, BUTTON핀을 입력으로 설정해줍니다.

5. val에 BUTTON핀의 입력값을 저장해줍니다.

6. 버튼을 한 번 누르게 되면

6-1. val에는 1이, old_val에는 0이 저장되어 있기 때문에 

6-2. state에 '1-이전 state의 값인 0'인 1을 저장해줍니다.

6-3. 바운싱을 제거하기 위해 0.05초간 대기합니다.

6-4. old_val에 val의 값인 1을 저장해줍니다.

6-5. state에 1이 새로 저장되었기 때문에 LED핀을 켜줍니다.

7. 버튼에서 손을 떼면 val에는 0이 저장되고, old_val에도 0이 저장되게 됩니다.

8. 버튼을 다시 누르면

8-1. 6-1을 실행해준 뒤 state에는 '1-이전 state의 값인 1'인 0을 저장해줍니다.

8-2. 6-3부터 6-4까지를 실행해준 뒤 state에 0이 새로 저장되었기 때문에 LED를 꺼줍니다.


다음 강좌에서는 LED의 밝기를 조정하는 법을 알아보겠습니다!


이전 강좌 보기

2018/12/24 - [강좌/아두이노 강좌] - 아두이노 기초강좌 01. 아두이노란?

2018/12/24 - [강좌/아두이노 강좌] - 아두이노 기초강좌 02. 아두이노 설치하기

2018/12/24 - [강좌/아두이노 강좌] - 아두이노 기초강좌 03. 전기 이해하기, 전압, 전류, 저항

2018/12/24 - [강좌/아두이노 강좌] - 아두이노 기초강좌 04. 무작정 LED 켜보기

2018/12/24 - [강좌/아두이노 강좌] - 아두이노 기초강좌 05. 무작정 LED 켜보기 코드 분석

2018/12/29 - [강좌/아두이노 강좌] - 아두이노 기초강좌 06. 브레드보드, 빵판에 대하여

2018/12/29 - [강좌/아두이노 강좌] - 아두이노 기초강좌 07. 무작정 푸시 버튼으로 LED 제어하기

2018/12/29 - [강좌/아두이노 강좌] - 아두이노 기초강좌 08. 무작정 푸시 버튼으로 LED 제어하기 분석

2018/12/29 - [강좌/아두이노 강좌] - 아두이노 기초강좌 09. 푸쉬버튼과 저항을 써야하는 이유