티스토리 뷰

입문

푸쉬 버튼 감지 1

Just4Fun 2016. 3. 6. 10:07

AVR의 포트핀 출력을 제어하여 두개의 LED를 점멸시키는 방법을 여러가지 예제를 통하여 알아 보았다.

이번 글에서는 포트로 입력되는 신호를 읽어내는 방법에 대해서 알아본다.

이전글에서 사용하였던 회로에서 초록색 LED회로를 들어내고 푸쉬버튼을 넣어보도록 하겠다.  회로도로 표현하면 아래 그림과 같이 된다.

 

위의 회로를 브레드보드에 꾸미면 아래 그림처럼 된다.

회로도 그림에서 보이는 푸쉬버튼 스위치 'S1'의 한쪽은 PB0에 연결되어 있고, 나머지 한쪽은 GND에 연결되어 있다.  버튼을 누르지 않은 상태에서 PB0에 입력되는 값을 high로 만들고 버튼을 눌렀을때 PB0의 값이 low로 되면 AVR CPU입장에서 1혹은 0의 입력으로 판단할 수 있다.  그렇게 하기 위해서는 PB0에 pull-up 저항을 연결시켜 줘야 한다.  AVR 데이터쉬트의 포트관련 부분을 보면 다음과 같은 그림을 볼 수 있다.

이미 AVR 내부 회로에 모든 핀에 대해서 pull-up 저항이 들어가 있다.  PORT블럭의 레지스터를 조절하면 pull-up저항을 활성화 시킬수 있고 반대로 비활성화 시킬수도 있다.  Pull-up 저항을 활성화 하기 위해서는 DDR레지스터를 이용하여 해당 핀을 입력으로 설정하고 PORT레지스터의 해당 핀을 high로 만들면 pull-up 저항을 핀에 연결시키는 동작을 수행한다.  그 상태에서 PIN레지스터의 값을 읽어 보면 1의 값이 읽히게 된다.

문서 14.2.1 Configuring the Pin 절에 다음과 같은 내용이 나온다.

If PORTxn is written logic one when the pin is configured as an input pin, the pull-up resistor is activated.

위의 내용을 바탕으로 버튼이 눌린 상태에서 LED의 불이 켜지고 버튼을 누르지 않을때 LED가 꺼지는 코드를 만들어 보겠다.

#include <avr/io.h>
 
#define PUSH_BTN    0x01
#define RED_LED     0x02
 
int main(void)
{
    DDRB = RED_LED;
    PORTB= (RED_LED | PUSH_BTN);
 
    while (1) 
    {
        if (PINB & PUSH_BTN)
            PORTB |= RED_LED;
        else
            PORTB &= ~RED_LED;
    }
}

위의 코드대로 동작시키면 스위치를 누를때 불이 켜지고 스위치를 누르지 않으면 불이 꺼질것이다.

임베디드 시스템은 일반 컴퓨터에서 실행되는 프로그램과 달리 프로그램이 무한히 반복되어 끝이 나지 않는다.  외부의 어떤 신호를 감지하여 그에 맞는 적절한 동작을 수행하도록 하는것이 임베디드 시스템의 목적이다.  외부의 신호가 딱 한번만 들어오고 더이상의 신호가 입력되지 않는다면 프로그램을 종료시켜도 문제가 없을 것이다. 하지만 그런 목적으로 임베디드 시스템을 만들지는 않는다.  외부에서 신호가 언제 어느때 반복해서 입력되더라도 항상 그 상황에 맞는 동작을 수행하여야 하므로 무한히 반복되는 프로그램이 되어야 한다.

따라서 main() 함수는 항상 while(1)을 사용하여 무한히 반복되는 동작을 수행하도록 되어있다.  main() 함수를 추상화 하면 다음과 같이 될것이다.

int main(void) 
{    
    초기화    
    
    while (1)   
    {       
        반복 수행    
    } 
}

어떤 임베디드 시스템도 위와 같은 구조로 추상화 할 수 있다.  따라서 위의 코드를 다음과 같이 실체화 할 수 있겠다.

void setup(void) 
{
}

void loop(void) 
{ 
}  

int main(void) 
{     
    setup();      
    
    while (1)      
    {         
        loop();     
    }
 }

이제 main()함수 부분을 제외하고 나머지 코드만 보면 어디서 많이 본듯한 코드가 될것이다.  바로 아두이노 프로그램이 이러한 모습을 하고있다.

아두이노 프로그램은 항상 setup()함수와 loop()함수로 프로젝트를 시작하도록 되어 있다.

우리도 아두이노처럼 setup()과 loop()를 이용하여 프로그램을 수정하면 다음과 같은 코드를 만들 수 있다.

#include <avr/io.h>
 
#define PUSH_BTN    0x01
#define RED_LED     0x02
 
void setup(void)
{
    DDRB = RED_LED;
    PORTB= (RED_LED | PUSH_BTN);
}
 
void loop(void)
{
    if (PINB & PUSH_BTN)
        PORTB |= RED_LED;
    else
        PORTB &= ~RED_LED;
}
 
int main(void)
{
    setup();
 
    while (1) 
    {
        loop();
    }
}

이제 위의 예제에서 나온 몇개의 코드에 대한 설명을 하겠다.

14번 줄에 다음과 같은 코드가 있다.

if (PINB & PUSH_BTN)

if문안에서 사용된 '&' 연산자는 특정 비트값이 1의 값을 가지는지 알아보기 위하여 사용한다.  조건문안의 내용이 '0'이면 거짓이 된다.  그리고 참은 '0'이 아닌 모든것은 참이된다.  즉 '0'인지 아닌지가 판단기준이 되는 것이다.

위의 예제에서는 푸쉬버튼이 연결되어 있는 PB0 하나의 조건을 판단하기 위해서 한개의 비트만 비교하였는데, 꼭 한개의 비트만 비교할 필요는 없다.  만약 0번 핀과 7번핀 둘중에 하나라도 1의 값을 가지는 핀이 있는지 검사하고 싶다면 다음과 같은 코드를 사용하면 된다.

if (PINB & (0x01|0x80))

15번 줄에 있는 PORTB |= RED_LED;는 다음과 같은 코드이다.

PORTB = PORTB | RED_LED;

이 코드의 목적은 PORTB의 현재값에 RED_LED에 해당되는 비트값을 1로 만드는 것이다.  그런데 실수로 PORTB = RED_LED라고 하면 PORTB에 있던 기존 값들이 모두 지워지고 RED_LED에 해당되는 비트값만 1이된다.  따라서 이 코드로 인해 그 뒤에 동작되는 코드들이 비 정상적으로 동작되는 원인이 되는 것이다.  실제로 임베디드 시스템의 많은 오류들이 이와 같은 이유로 발생한다.  즉, 특정 비트만 변경한다는 것이 레지스터 안에 있는 나머지 값들까지 변경시켜 잘 동작되던 시스템이 갑자기 먹통이 되기도 한다.

17번 줄에 있는 PORTB &= ~RED_LED;는 다음과 같은 코드이다.

PORTB = PORTB & ~RED_LED;

이 코드는 현재 PORTB 레지스터에 설정되어 있는 값들 중에 RED_LED에 해당되는 비트값만 0으로 만드는 코드이다.  예제에서 RED_LED는 0x02의 값을 가지고 있다.  이 값에 '~' 연산자를 적용하면 0은 1이 되고 1은 0이 되므로 0xFD가 된다.  이 값을 PORTB레지스터와 '&' 연산을 하게되면 기존에 있던 값들은 그대로 유지되고 두번째 비트값만 무조건 0이 되게된다.  이 코드에서도 프로그래머가 실수로 PORTB = ~RED_LED;로 하게되면 PORTB의 값이 0xFD로 바뀌게 된다.  만약 PORT B블럭의 핀들이 모두 출력으로 설정되어 있다면 RED_LED를 제외한 모든 핀들의 값이 high 상태로 되는 것이다.  이로 인해 PORT B블럭에 연결되어 있던 주위 부품들에 high 시그널이 인가된다.  물론 아무일도 안 일어날 수도 있겠지만 경우에 따라 심각한 이상 동작을 일으킬 수도 있게 된다.  예를 들어 인공위성을 쏘아 올리는 로켓에는 메인엔진 외에 자세를 유지하기 위하여 자그마한 수십개의 엔진이 설치되어있다.  그 중에서 특정 엔진만 작동하도록 한다는 것이 프로그램 오류에 의하여 모든 엔진이 동작 된다면 로켓은 제 궤도로 들어가지 못하고 엉뚱한 곳으로 날아갈수도 있는것이다.

임베디드 시스템 개발에서는 단 한비트의 값이 잘못 설정되더라도 전체 시스템의 동작에 영향을 미칠수 있으므로 항상 주의하여야 한다.

앞에서 구현한 프로그램은 버튼이 눌리면 LED가 켜지고, 버튼을 누르지 않으면 LED가 꺼지도록 만든것인데, 반대로 버튼을 누르면 LED를 끄고 버튼을 누르지 않으면 LED가 켜지는 코드를 만들어 보도록 하겠다.  이것은 앞에서 만든 loop함수에서 if조건문에 해당되는 코드만 서로 맞바꾸면 해결이 될것 같다.

아래 코드처럼 수정하여 예상했던대로 보드가 동작되는지 확인해 본다.

void loop(void) 
{     
    if (PINB & PUSH_BTN)         
        PORTB &= ~RED_LED;     
    else         
        PORTB |= RED_LED; 
}

 

입문 과정 목차

'입문' 카테고리의 다른 글

LED 제어 - 부록  (3) 2016.03.07
푸쉬 버튼 감지 2  (3) 2016.03.06
LED 제어 7  (13) 2016.03.05
LED 제어 6  (1) 2016.03.05
LED 제어 5  (4) 2016.03.05
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함