외부 인터럽트를 실험하기 위해서 INT1핀에 택스위치를 연결한 회로도를 [그림 1]에 도시하였다. 풀업 저항이 AVR내부에 내장되어 있기 때문에 외부에 별도로 풀업 저항을 달아주지 않아서 회로가 굉장히 간단해짐을 알 수 있다. 대신 내부에서 이 두 핀에 풀업 저항을 연결하려면 DDRD3 = '0', PORTD3 = '1'로 설정하여야 한다.


[그림 1] 외부 스위치 회로도


전기적인 접점을 갖는 스위치를 연결하여 사용할 때 꼭 고려해야 될 사항이 있는데 그것은 진동(bouncing) 현상이다. 이것은 전기적인 접점이 닫히거나 열릴 때 그 양단의 전기적인 신호가 깔끔하게 ‘1’에서 ‘0’으로 또는 ‘0’에서 ‘1’로 변하는 것이 아니라 아주 짧은 동안에 접점이 열렸다 닫히기를 반복하는 현상이다. [그림 2]는 접점이 떨어지는 순간의 진동 현상을 보여준다.


[그림 2] 기계적인 접점을 갖는 스위치의 바운싱 현상


이러한 바운싱을 적절히 처리하지 못하면 매우 빠른 속도로 처리되는 uC에서는 스위치가 여러 번 눌려진 것으로 인식하며 이로 인해서 ‘rising edge'나 ’falling edge'에서 인터럽트가 의도하지 않게 여러 번 발생하게 된다. 이러한 기계적인 접점의 진동을 제거하는 것을 디바운싱(debouncing)이라고 하며 크게 하드웨어적인 회로를 이용하는 법과 소프트웨어적인 방법 두 가지로 나뉜다. 많이 사용되는 하드웨어 디바운싱회로를 [그림 3]에 도시하였다.

[그림 3] 디바운싱 회로의 예들 (a) 좌측, (b) 우측


[그림 3]의 (a)회로도를 보면 스위치 양단에 커패시터를 병렬로 달아서 전압 리플을 억제해주는 가장 간단한 회로이다. (b)회로도는 여기에 슈미트 트리거를 추가하여 작은 리플도 제거해주는 회로이다.


 소프트웨어 디바운싱은 하드웨어의 추가 없이 프로그램으로 진동을 제거하는 방식으로 진동이 보통 10ms(길어야 20ms)정도 지속된다는 점에서 착안하여 처음 감지된 이후 일정 시간이 지난 후에도 그 값이 유지가 되는지를 확인하는 알고리즘을 프로그램으로 작성하는 것이다.


 여기에서는 바운싱을 제거하기 위해서 푸시 스위치와 병렬로 1uF의 커패시터를 각각 연결한 가장 간단한 회로를 택하였으며 이 커페시터가 양단의 전압이 급격하게 변하는 것을 어느 정도 억제해 준다.


 내부에 풀업 저항을 연결하였다면 스위치가 안 눌려졌을 때는 INT0/INT1핀으로 ‘1’신호가 입력이 될 것이다. 만약 스위치가 눌려졌다면 이 핀들은 GND에 연결이 되기 때문에 ‘0’신호가 읽려진다. 따라서 스위치를 누르는 순간 1→0으로 변하게 되고 이렇게 변하는 순간을 하강 엣지라고 한다. 반대로 스위치를 누르고 있다가 떼는 순간 0→1로 신호가 바뀌게 되며 이러한 순간을 상승 엣지라고 한다.


스위치 실험 1

 첫 번째 스위치 실험으로 SW1을 누를 때마다 짧게 부저가 울리면서 LED의 상위 니블과 하위니블의 위치가 바뀌는 프로그램을 작성해 보자. 인터럽트는 하강 엣지, 즉 스위치를 누르는 순간에 걸리도록 설정했다.


#define F_CPU 16000000
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "Am8USBasp.h"
volatile uchar ucLed = 0x0F;
ISR(INT1_vect) {
   BuzzOn;
   _delay_ms(10);
   BuzzOff;
   LED( ucLed=~ucLed );
}
ISR(BADISR_vect) {}
int main(void) {
   InitAM8();
   GICR|=0xC0; // External Interrupt(s) initialization
   MCUCR=0x0A; // INT0: Falling Edge, INT1: Falling Edge
   GIFR=0xC0;
   sei();
   LED(ucLed);
   while(1);
}

 이 예제를 보면 먼저 main()함수 내부의 while(1);에서 무한루프에 빠진다는 것을 알 수 있다. 즉, main()함수 내부의 동작은 여기에서 멈추지만 SW0을 누를 때마다 하드웨어적으로 인터럽트가 걸리게 되고 ISR(INT0_vect)함수가 호출이 된다. 이 함수 내부에서 부저를 짧게 울리고 LED출력(PORTB)를 반전시킨 후 다시 main()함수로 돌아와 무한루프를 계속 돌게 된다. 이것이 인터럽트가 운용되는 기본적인 구조이다. 여기에서는 단순히 무한루프에 빠져 있지만 중요한 점은 main()함수 내에서 스위치의 동작을 검출하려는 어떠한 코드도 없다는 것이다.

스위치 실험 2

 두 번째 스위치 실험으로 SW1을 누르면 LED가 위로 한 칸씩 움직이고 끝까지 가면 다시 처음 위치로 되돌아가는 프로그램을 작성해 보자. 이전 실험과 마찬가지로 스위치를 누를 때마다 부저가 짧게 울리게끔 해보자.


#define F_CPU 16000000
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "Am8USBasp.h"
volatile uchar ucLed = 0x01;
ISR(INT1_vect) {
   ucLed = (ucLed == 0x80) 0x01:(ucLed<<1);
   LED(ucLed);
   Beep;
}
ISR(BADISR_vect) {}
int main(void) {
   InitAM8();
   GICR|=0xC0; // External Interrupt(s) initialization
   MCUCR=0x0A; // INT1: Falling Edge
   GIFR=0xC0;
   sei();
   LED(ucLed);
   while(1);
}



Posted by 살레시오

댓글을 달아 주세요