본문 바로가기

배움의 즐거움/하드웨어

ATmega128로 배우는 마이크로컨트롤러 프로그래밍(6) 디지털 데이터 출력과 입력

반응형

이번 글에서는 책의 챕터 7에서 다뤄진 디지털 데이터 출력과 챕터 8에서 다뤄진 디지털 데이터 입력에 대해서 함께 정리할 것이다.

7.1 ATmega128의 데이터 핀

ATmega128은 64개의 핀을 가지고 있고 이 중 53개의 핀을 디지털 데이터 입출력 핀으로 사용할 수 있다. 이 마이크로컨트롤러의 중앙처리장치는 1바이트(8비트) 크기의 워드(word) 단위로 데이터를 처리하므로, 8개의 입출력 핀을 묶어 포트라는 이름으로 관리한다. 포트는 외부 장치와 데이터를 교환하는 기본 단위가 된다.

마이크로컨트롤러는 마이크로프로세서와는 다르게 주변장치와 데이터를 주고받는 기능이 칩 내에 포함돼 있고, 이를 위해 사용하는 임시 기억공간을 입출력 레지스터라고 한다. ATmega128에는 256개의 레지스터를 정의할 수 있으며, 32개의 범용 레지스터(중앙처리장치 내의 연산과정에서 사용됨)와 105개의 입출력 레지스터가 정의되어 있다.

 

7.2 디지털 데이터 출력을 위한 레지스터

포트를 통해 디지털 데이터를 출력하기 위해 사용되는 레지스터는 PORTx(x는 A부터 G까지를 나타낸다) 레지스터 이다. 예를 들어 PE5 핀으로 1 값을 출력하고 싶다면 PORTE 레지스터의 5번 비트를 1로 설정하면 된다. 

포트는 핀 8개를 하나로 묶어서 관리하는 것을 가리키는데, 그 이유는 CPU에서 데이터를 비트 단위로는 처리할 수 없기 때문이다.  ATmega128의 CPU에서 처리할 수 있는 데이터의 최소 크기는 1바이트이기 때문에 바이트 단위로만 데이터를 읽고 쓸 수 있다. 비트 단위를 저장할 수 있는 데이터 타입이 존재하지 않는다. CPU가 바이트 단위로 데이터를 처리하기 때문에 각각의 핀을 처리할 수 없겠다고 생각할 수 있겠지만 비트 연산자를 통해서 비트 단위의 데이터만을 변경할 수 있다. 

ATmega128에서 데이터 핀으로 출력하기 위해서는 PORTx 레지스터를 사용하면 되는데, 주의할 점은 데이터 핀은 입출력이 가능한 핀이지만 핀을 사용하기 전 이를 입력으로 사용할 것인지, 출력으로 사용할 것인지 한 가지로 결정해야 한다. 이를 설정하기 위해서 DDRx(x=A.. G) 레지스터를 통해서 입출력 방향을 결정해야 한다. DDRx를 1로 설정하면 출력, 0으로 설정하면 입력인데 디폴트 값은 0이다.

 

7.3 블링크

포트에 연결된 8개의 LED를 점멸하는 프로그램을 작성해보자. 우선 해당 DDRx 레지스터를 1로 설정하여 해당 핀을 출력으로 설정한 후 LED를 1초 간격으로 점멸시킬 것이다.

#define F_CPU 16000000L   # 마이크로컨트롤러의 동작 주파수 정의, 맨 끝에 L은 long 타입 값을 의미
#include <avr/io.h>                # io.h 헤더 파일에는 레지스터 이름, 비트 이름 등이 정의되어 있다
# include <util/delay.h>         # _delay_ms 함수를 사용하기 위해서 필요함

int main(void)
{
  DDRB = 0xFF;                     # 포트 B를 출력으로 설정

  while(1) 
  {
    PORTB = 0x00;               #포트 B의 모든 LED를 끈다 (0x00 = 0b00000000)
    _delay_ms(1000);            # 1초 지연
    PORTB = 0 xFF;               # 포트 B의 모든 LED를 켠다 (0 xFF = 0b11111111) 
   _delay_ms(1000);
  }
return 0;
}

위의 코드는 모든 8개의 LED에 불이 동시에 들어오고 동시에 꺼지게 하는 코드이다. 이와 달리 순차적으로 켜지고 꺼지는 코드를 적고 싶다면 비트 값의 패턴을 파악하여 해당 비트를 포트에 설정한 뒤 출력하면 된다. 책에는 이 외에도 몇 가지 복잡한 예시들이 나와있다.

 

8.1 풀업 저항과 풀다운 저항

이 책에서는 디지털 데이터의 입력을 위해 푸시버튼을 사용하는데, 버튼을 사용하는 경우 몇 가지 주의점이 있다. 그중 하나는 마이크로컨트롤러의 입력 핀에 개방 회로가 연결되지 않아야 한다(open circuit)는 점이다.

출처: ATmega128로 배우는 마이크로컨트롤러 프로그래밍

버튼을 사용할 때 가장 간단한 회로는 위와 같이 버튼의 양 쪽을 VCC와 범용 입출력(GPIO) 핀에 연결해서 버튼이 눌려지면 1, 안 눌려지면 0이 가해지도록 하는 것이다. 그러나 위의 경우, 버튼을 누를 경우는 1이 되지만 누르지 않을 때는 그 값은 0이 아니라 알 수 없다이다. 즉, 버튼이 눌려지지 않은 경우에는 오픈된 회로가 되는데 이 경우 전압이나 정전기 등의 영향을 받아서 무작위의 값이 가해질 수 있게 되고, 이렇게 개방된 GPIO을 플로팅(floating) 되었다고 한다. 플로팅 상태를 제거하기 위해서는 풀업 또는 풀다운 저항을 사용해야 한다.



먼저 왼쪽은 풀업 저항을 사용한 것이다. 버튼을 누르지 않을 때에는 GPIO 핀에 저항으로 통해 VCC가 가해지게 되고, 버튼을 누르게 될 경우 GND 값이 가해지게 된다. 즉 버튼을 누르면 0, 누르지 않으면 1이 되므로 우리가 예상했던 버튼의 일반적인 동작과는 반대의 상황이 된다.

반대로 오른쪽 풀다운 저항을 보면, 버튼을 누르지 않은 상태면 GPIO 핀에 GND가 가해져 0의 값을 갖게 되고, 버튼을 누르게 되면 VCC가 가해져서 1의 값을 갖게 된다. 

위처럼 GPIO가 플로팅 되는 것을 막기 위해 풀업, 풀다운 저항 사용을 추천하지만 매번 버튼과 함께 저항을 연결하는 것은 성가신 일이다. 그러므로 ATmega128의 범용 입출력 핀은 프로그램으로 제어 가능한 풀업 저항을 포함하고 있으므로 사용 여부를 설정만 하면 된다. 풀다운 저항이 좀 더 우리에게는 직관적이지만 풀업 저항이 구현하기가 쉽고 GND보다는 VCC가 안정된 전압을 유지하는 장점 덕분에 마이크로컨트롤러에서는 풀업 저항을 주로 사용한다.

 

8.2 버튼 입력

앞에서 디지털 데이터를 출력하기 위해서는 DDRx 레지스터와 PORTx 레지스터를 사용하였다. 데이터를 입력하기 위해서는 DDRx 레지스터와 PINx 레지스터를 사용하면 된다. 사실 DDRx 레지스터의 디폴트 설정이 0인 입력으로 설정되어 있기 때문에 입력을 할 때는 굳이 지정하지 않아도 되지만 혼란을 방지하기 위해서 입력할 하는 경우에도 DDRx 레지스터 설정을 추천한다.

출처: ATmega128로 배우는 마이크로컨트롤러 프로그래밍

DDRx 값을 0으로(입력으로) 설정한 경우, PORTx 레지스터는 마이크로 컨트롤러 내부에 포함되어 있는 풀업 저항의 사용 여부를 나타내기 위해 사용된다. 즉, 풀업 저항을 사용하기 위해서는 DDRx 값을 0으로, PORTx 값을 1로 출력하면 된다. PORTx의 디폴트 값은 0으로 풀업 저항을 사용하지 않는다. 

 

8.4 디바운스

버튼을 누르면 버튼 내부의 접점은 완전히 연결되기 전까지 내부 스프링에 의해 연결되고 떨어지기를 밀리 초동 안 반복한다. 이로 인해 버튼을 한 번만 눌렀음에도 여러 번 누른 것처럼 보이는데, 이를 바운스 현상(bounce effect) 또는 채터링(chattering)이라고 한다.

바운스 현상 또는 채터링

이를 없애는 것을 디바운스라고 하며 이는 소프트웨어와 하드웨어 모두를 통해 가능하다. 먼저 소프트웨어를 통해 하는 방법은 두 가지가 있다. 첫 번째 방법은 버튼을 누르기 시작한 시점에서 1의 값을 가지게 되는데, 그 시점에서 일정 시간 동안의 입력을 무시함으로써 채터링 현상을 줄이는 것이다. 두 번째 방법은 버튼 입력을 짧은 시간 내에 두 번 검사하여 두번 모두 1의 값이 입력되는 경우를 찾아내는 것이다. 첫번째 방법은 버튼의 종류에 따라 지연 시간이 다르기에 각각 설정해야 된다는 단점이 있고, 두번째 방법은 두번 검사해야 하므로 프로그램이 복잡해진다는 단점이 있다.

커패시터를 이용하여 디바운스 하는 방법

하드웨어를 통해 디바운스 하는 방법도 있는데 커패시터를 추가하는 것이다. 버튼이 눌러지지 않은 경우 커패시터는 서서히 천천히 충전되며 버튼이 눌려진 경우 충전된 커패시터는 서서히 방전되어 버튼 진동에 의한 전압 변동을 일부 흡수함으로써 채터링 현상을 줄여줄 수 있다. 줄기는 하지만 완전히 없애주지는 못한다.

이 외에도 다이오드 저항 그리고 슈미트 트리거를 이용하는 방법이 있지만 회로가 복잡하므로 커패시터를 이용하는 방법을 많이 사용한다.

 

반응형