AVR 외부 인터럽트를 이용한 LED 제어 - AVR 연습하기

2013.01.10 12:40




현재 군 복무중인 상태입니다.



안녕하세요. 2일 정도 뜸하다 돌아왔네요.^

인터럽트관련 정리글을 올려놓고 실제 실험해본 것을 안올렸는데.. 뒤늦게 올립니다.^

이제 점차 AVR을 다루는 난이도가 올라가는데요. 

AVR은 인터럽트시에는 사용하는 핀이 정해져 있는데 아래 핀아웃을 볼까요.!


AVR 외부인터럽트 핀아웃


ATTINY2313의 핀아웃을 보면 PD1~7 포트가 있고 그 옆에 괄호가 있는데 괄호가 해당핀에서 사용 할 수있는 기능이라고 보면됩니다.

외부인터럽트의 경우에는 앞에 INT가 붙고 뒤에 비트값이 차례대로 붙는데 Tiny2313의 경우에는 외부인터럽트신호를 2선밖에 지원하지않기 때문에 INT0, INT1이 있고, 인터럽트 설정시에도 INT0, INT1 두 인터럽트설정만 하기에 레지스터수가 적습니다.



연습 하드웨어 회로는?




일단 그냥 보기에는 회로는 그냥 스위치입력 연습할때와 다를게 없죠..

하지만 다른점이 3가지 정도있습니다. 먼저 스위치 입력단에 저항이 생겼죠.!

저항이 제 설계에는 GND에 연결되어있는데 이 연결은 조건이 붙습니다.

제가 만약 AVR 외부인터럽트 설정시에 상승에지에 동작한다고 설정을 한다면 위회로로 설계하고

스위치를 누르면 인터럽트가 걸립니다.

하지만 하강에지에서 동작한다고 설정하면 저항과 스위치의 연결을 완전히 바꿔야 합니다.

저항은 GND가 아닌 VCC에 연결되어야하고, 스위치의 1번핀은 GND에 연결되어야 하강에지설정시에 인터럽트가 걸립니다.

그리고 위 핀아웃에서 말한것처럼 INT0, INT1 에만 스위치를 연결했습니다.

갈수록 설명도 어려워지네요.. ㅎ

항상하던 순서로 다음은 소스코드인가요.!!

역시 소스코드는 AVR Studio와 CodeVisionAVR로 나눠서 올립니다.


CodeVisionAVR 용 소스코드


#include <tiny2313.h>    // tiny2313의 입출력 관련 헤더파일을 포함합니다.

#include <delay.h>    // 지연함수관련 헤더파일을 포함합니다.


interrupt [EXT_INT0] void ext_int0_isr(void)    // 핵심 인터럽트가 걸리면 여기로 점프합니다.

{

    PORTB=0b11110000;    // 포트B의 상위 4비트에 1 (5V) 출력합니다.

    delay_ms(500);    // 위 상태를 0.5초 유지합니다.


    EIFR=0x40;    // 인터럽트발생 신호를 초기화해 다시 인터럽트를 받을 수 있는상태로 만듭니다.

}


void main(void)    // main()함수를 호출합니다.

{        

PORTB=0x00;    // 포트B의 초기상태는 모든포트 0 (0V)로 설정합니다.

DDRB=0xFF;    // 포트B의 모든포트를 출력으로 설정합니다.


GIMSK=0x40;    // INT0의 인터럽트를 허용합니다.

MCUCR=0x03;    // MCUCR의 0번,1번 비트를 1로 상승에지에 인터럽트동작으로 설정합니다.


#asm("sei")    // 모든 인터럽트를 허용합니다.


while (1)    // 아래구문을 무한 반복합니다.

      {

      PORTB=0xFF;    // 포트B의 모든포트에 1 (5V)를 출력합니다.

      delay_ms(500);    // 위 상태를 0.5초 유지합니다.

      PORTB=0x00;    // 포트B의 모든포트에 0 (0V)를 출력합니다.

      delay_ms(500);    // 위 상태를 0.5초 유지합니다.

      }

}


이 코드에서 눈여겨 보아야 할것은 interrupt [EXT_INT0] void ext_int0_isr(void) 이 부분과 EIFR 입니다.

interrupt [EXT_INT0] void ext_int0_isr(void) 이 부분은 인터럽트가 걸리면 점프해서 무슨처리를 한뒤에 다시 main()으로 돌아갈지 프로그램 하는곳인데 저 같은 경우에는 인터럽트 처리 구문에 PORTB=0b11110000 로 입력했습니다. 그러면 main()에서 while(1)에서 LED를 무한으로 깜박깜박 반복하다가 제가 스위치입력으로  인터럽트신호를 주게 되면 LED중 앞에 4개만 불이들어온상태로 0.5초 있다가 다시 Main()의 While(1)무한 반복으로 돌아와 깜박깜박 반복하는 것이지요.! 그리고 인터럽트가 걸리면 EIFR레지스터에 인터럽트가 걸렸다고 1이 입력되 다시 인터럽트를 받을수 없는 상태가 됩니다. 따라서  EIFR 해당비트에 다시 1을 써주어 0으로 초기화시켜 다시 인터럽트를 받을 수있는 상태로 만듭니다.



AVR Studio 용 소스코드


#include <avr/io.h>    // AVR 기본 입출력 관련 헤더파일을 포함합니다.

#define F_CPU 8000000UL    // 사용하는 크리스탈속도 8Mhz를 상수로 설정합니다.

#include <util/delay.h>    // 지연함수 관련 헤더파일을 포함합니다.

#include <avr/interrupt.h>    // 인터럽트 관련 헤더파일을 포함합니다.


ISR(INT0_vect)    // 핵심 인터럽트가 걸리면 여기로 점프합니다.

{

    PORTB=0xF0;    // 포트B의 상위 4비트에 1 (5V)를 출력합니다.

    _delay_ms(500);    // 위 상태를 0.5초 유지합니다.


    EIFR=0x40;    // 인터럽트발생 신호를 초기화해 다시 인터럽트를 받을 수 있는상태로 만듭니다.

}


int main(void)    // main() 함수를 호출합니다.

{        

PORTB=0x00;    // 포트B의 모든포트 초기상태를 0 (0V)로 설정합니다.

DDRB=0xFF;    // 포트B의 모든포트를 출력으로 설정합니다.


GIMSK=0x40;    // INT0의 인터럽트를 허용합니다.

MCUCR=0x03;    // MCUCR의 0번,1번 비트를 1로 상승에지에 인터럽트동작으로 설정합니다.


sei();    // 모든 인터럽트를 허용합니다.


while (1)    // 아래구문을 무한 반복합니다.

      {

      PORTB=0xFF;    // 포트B의 모든포트에 1 (5V)를 출력합니다.

      _delay_ms(500);    // 위 상태를 0.5초 유지합니다.

      PORTB=0x00;    // 포트B의 모든포트에 0 (0V)를 출력합니다.

      _delay_ms(500);    // 위 상태를 0.5초 유지합니다.

      }


return 0;    // main함수에 0을 리턴합니다.

}


코드가 비슷하지요. 하지만 나중에 결정적으로 CodeVision과 AVR Studio가 호환이 안되는 부분은 ISR(INT0_vect) 인터럽트 처리부분입니다.

이 부분만 변경하고 Delay함수처리만 변경하면 다른 컴파일러에서도 사용할 수가 있습니다.


프로그래밍 후 동작은?




 버튼까지 누르려니까 손이떨려요.!!!


위 처럼 원래 PINx레지스터로 제어했을 때는 AVR이 다른 작업중일 때 스위치를 눌러도 반응하지 않았지만 인터럽트로 처리하면 중간에 다른작업이 가능해집니다.^

BinGoon AVR/AVR 연습, Tutorial , , , , , ,

  1. Blog Icon
    AVRAVR

    항상 좋은 자료 감사합니다

  2. 감사합니다.^ 계속 좋은 자료로 찾아뵙겠습니다.^

  3. Blog Icon
    Mr도리

    ATmega128로도 레지스터값보고 해봐야겠네요 ㅎ
    잘보고 갑니다

  4. 앞으로 AVR자료정리는 AVR기능을 모두 사용할때까지 계속됩니다^

  5. Blog Icon
    윤윤

    GIMSK를 정의하지 못하는데 제 AVR Studio4 에서는 헤더파일이 부족한것 같은데 어떻게 처리하죠?

  6. 저와 동일한 TINY2313을 사용하신 건가요??, 혹시 MEGA128 등 다른 AVR을 사용하셨다면 EIMSK (External Interrupt Mask Register) 로 변경되어있는 칩도 있으므로 주의하시기 바랍니다.
    TINY2313과 동일한 AVR을 사용하셨다면, 헤더파일의 문제가 맞을 것 같네요. iom2313.h 파일에서 GIMSK관련된 코드를 수정해야 될것 같습니다.
    질문 감사드립니다.^

  7. Blog Icon
    y

    만약 포트를 위에서부터 5개만 사용하면 DDRB는 어떻게 지정해줘야되나요?

    DDRB = 0xff 라는 것은 8비트가 256이라서 0xff 해준건가요?
    7번째만 불이들어오는경우
    6번째만 불이들어오는경우 등등일때는 어떻게 값을 정해줘야되나요?

  8. AVR의 디지털 포트는 PB7 = 128 PB6 = 64 PB5 = 32 PB4 = 16 PB3 = 8 PB2 = 4 PB1 = 2 PB0 = 1 로 구성됩니다.^
    따라서 입출력포트 중 5개를 출력으로 PB4~PB0까지를 출력으로 설정하고자 한다면, 1+2+4+8+16을 2진수로 변경한 값을 입력하시면 됩니다.^
    그럼 10진수로 31이 나오고 이 값은 16진수로 1F가 나오므로, DDRB = 0x1F로 입력하시면 하위 5비트를 출력으로 사용하실수 있습니다.^
    좀 더 자세한 사항을 원하시면 메일로 질문 보내주세요^

  9. Blog Icon
    leedoosol

    정말 도움 많이됫네요..ㅠㅠ
    1학년이라 혼자공부하기 힘든데 감사해요

  10. 도움이 되셨다니 기분이 좋습니다.ㅎ 앞으로도 자주 방문해 주세요.^

  11. Blog Icon
    leu

    혼자 공부하기 어려웠는데 좋은 자료 남겨주셔서 감사합니다.
    그런데 질문드리고 싶은게 있는데요,

    제가 알기로는 다른 MCU(ATmega128의 경우)interrupt의 서비스 루틴이 끝나고 난 뒤에는 인터럽트가 걸리기 직전의 main()부분으로 이동하는 것으로 알고 있습니다.

    그런데 만약 주인장님께서 쓰신 MCU에서도 같은 방식으로 복귀한다면, While()문 밖에 있는 곳에서 재 인터럽트 발생이 가능토록 리지스터 값을 저장해주는 것은 의미가 없지 않나요?

    아니면, 여기서 사용된 MCU역시 Atmega128과마찬가지로 자동으로 인터럽트가 가능하도록 해당 레지스터값들이 초기화되는 것인가요?

    ㅎㅎ 좋은 자료 다시 한 번 감사드립니다ㅏ.

  12. 안녕하세요^ 블로그에 방문해 주셔서 먼저 감사드립니다.^

    지금 생각해 보니 EIFR 외부 인터럽트 플래그 레지스터를 인터럽트가 끝나는 지점에 넣어주어야 될 것 같네요. 프로그램 카운터에 작업하던 main()구문의 위치를 저장해 두고 인터럽트 후에 다시 프로그램 카운터와 연산해서 원래 처리하던 구문으로 돌아온다고 알고 있습니다. 따라서 지적해 주신 부분이 맞을 것 같습니다, 방학중 대대적으로 수정하도록 할테니 다른 분들이 댓글을 봐주셨으면 하네요.ㅎㅎ

    Attiny2313은 외부인터럽트의 경우 플래그 레지스터가 자동으로 초기화 되지 않아서 수동으로 해주어야 되는 걸로 알고 있습니다.
    AVR 칩 위에 생산 날짜에 따라서 약간의 변동사항은 있을 것으로 보여 집니다.^ Atmega128은 자동으로 초기화되는 건가 보군요 ㅎㅎ 좋은 정보 얻었습니다.
    앞으로도 좋은 포스팅 하겠습니다. 감사합니다.^^

  13. Blog Icon
    zeff1991

    궁금한점이생겨서요 ㅎ 왜저항은 4.7k 를다셧나요?

  14. 안녕하세요.^ 블로그 방문해 주신것을 환영합니다.

    그럼 바로 답변드리겠습니다. 먼저 AVR 시리즈 중 하나인 Attiny2313은 TTL레벨 즉 5V의 전압과 0V의 전압으로 논리1과 논리0을 구분지어내는 프로세서 IC입니다.

    그렇다 보니 어떤 입력을 받을 때 그냥 스위치만 연결된 상태로 있게되면 스위치를 누르지 않았을 때 AVR의 입력상태는 Floating (Not Connect) 논리1과 논리0을 구분짓지 못함, 상태가 됩니다.

    이 것을 방지하기 위해 Pull Up 혹은 Pull Down 저항이 필요하게 되는데요. 위에서 사용한것은 입력핀에 저항이 연결되고 한쪽에 GND 되었으므로 Pull Down 저항이 되게 됩니다. 여기에서 4.7K옴의 저항을 달게 되는 이유는 먼저 TTL IC의 소비 전류인 1mA정도의 입력을 전류를 줄수 있고, 입력시 가장 좋은 특성을 보이기 때문에 TTL IC 디지털 입력에서는 4.7K ~ 5K 옴 정도의 풀업, 풀다운 저항을 연결해 줍니다.
    CMOS 구조의 IC에서는 변경될수 있습니다. 도움이 되셨기를 바랍니다.^

  15. Blog Icon
    Hyun

    ATmega128 로 공부하고있는데... MCUCR이란 레지스터가 적확히 이해가안됩니다..
    이전글에 나와있는 MCUCR의 포트도 정확히 뭘말하는건지 이해가 안되네요...

  16. 안녕하세요.^
    MCUCR은 포트를 직접적으로 제어하는 레지스터가 아니라, 내부 기능을 설정하는 레지스터인데요, http://binworld.kr/10 이 글에서 MCUCR레지스터의 기능을 보면 많은 기능이 있지만, 0번 비트부터 3번 비트까지만 외부 인터럽트 제어에 사용된답니다. 이 때 상승에지와 하강에지가 존재하는데요, 상승에지는 입력핀에 입력이 0V에서 5V로 상승할 때 인터럽트가 걸리고, 하강에지는 5V에서 0V로 내려갈 때 인터럽트가 발생하도록 설정하는 역할을 합니다. 그럼 INT0기능을 가진핀에서 상승에지로 외부인터럽트를 입력받을 때 ISC01과 ISC00을 11로 설정해 주어야 하는데, MCUCR레지스터의 2비트부터 7비트는 사용하지 않으므로, 00000011(2진수)로 10진수로는 3, 16진수로 변경하면 0x03이 되게 됩니다. 따라서 INT0번 핀에 스위치와 풀다운 저항을 달아서 스위치가 눌리면 5V로 상승하게 되므로 인터럽트가 걸려 EXT_INT0으로 점프, B포트에 11110000(2진수)로 출력하게 된답니다.^ 풀어쓴다고 썼는데 도움이 되셨기를 바랍니다.^

  17. Blog Icon
    jboo123@naver.com

    메일로 문의햇는데
    작동이 어찌어찌 되네요
    ㅋㅋ
    제가 2번3번인지 모르고 0번 1 번 이랑 햇갈려서,,,,그랫던 문제고,,
    근데 일단 제가 인터룹트걸어논 함수에들어갓다가 바로 나오나봐요/?
    근데 모터가 정방향 역방향잇는데
    알아서 되긴되는데 코드상으로 더 고칠건 없는건지 엽쭙고십네여

  18. 안녕하세요.^ 메일을 확인하긴 했는데, 이번달 스마트폰 데이터통화료 고갈로 인터넷접속 자체를 할 수가 없게 되었습니다. :<

    그래서 확인 후에도 조금 늦게 답변되었음을 알려드리구요, 소스코드는 AVR과 상관없는 코드이고, 아두이노로 구성된 코드인데요, 코드를 보니 attachInterrupt라는 구문은 2번핀 혹은 3번핀에 폴링에지 인터럽트(입력이 5V > 0V 내려갈때) 인터럽트가 발생하면 사용자가 구성한 bt1()과 bt2()라는 함수를 꺼내는 호출하게 되는데 이 때 함수 bt1()과 bt2()가 Delay함수로 만들어진 PWM신호로 출력되는듯 합니다. 하지만 딜레이로 만들어진 PWM은 부정확 하기 때문에 아두이노에서 analogWrite로 PWM신호를 출력해서 DC모터를 제어하면 어떨까 생각해 봅니다.^

    그런데 용도하고 front(); back(); 등의 함수의 코드가 없고 일부는 잘려있어서 정확한 용도및 완전한 해석은 불가능해 말남겨놓습니다.^ 그리고 무엇인지는 모르겠지만 좋은 작품 만드시기 바랍니다.^

  19. Blog Icon
    avr

    궁금한 점이 있는데요 지금 인터럽트가 INT0만 사용되는거 맞나요?

    INT0하고 INT1 을 둘다 사용 하려면 GIMSK=(0x40,0x80) 이렇게 하고

    EIFR=(0x40,0x80) 이렇게 하면 인터럽트 INT0,INT1을 사용할수 있는 건가요?

    그리고 항상 좋은 자료 너무 감사합니다. 좋은 하루 되세요^^*

  20. 네^ 스위치는 두개 연결했는데 코드를 INT0만 사용했네요 :-)
    INT0과 INT1을 둘다 사용하려면 GIMSK = 0xC0로 입력해 주셔야 합니다. INT1을 허용시키는 값은 16진수로 0x80 이고, INT0을 허용시키는 값은 16진수로 0x40이게 되는데 이값을 두개 더하면 0x는 16진수를 의미하고 뒤에 숫자 40과 80을 더하면 40+80 = 120이 되게 됩니다. 하지만, 16진수에서는 10을 A로 11을 B로 12를 C로 13을 D로 14를 E로 15를 F로 표현하기 때문에 앞 12는 C로 표현해서 0xC0로 값을 넣어주시면 되고, 인터럽트 루틴 구문을 추가해서 원하는 동작을 넣어주시면 됩니다. ^^

  21. Blog Icon
    초짜

    그렇게 해석이 되는거라면 0xFF는 어떻게 되는거에요;; INT0~7까지 사용하겠다고 선언한건가요;;? 그리고INT1,INT2,INT3 을 사용하려면 어떻게 되는건가요 저의 질문의 요지는 최대로 설정할수 있는값이 0xFF로 알고있는데 이값을 설정하다가 초과하면 어떻게 되는건가요;; 설명보면 40+80이라고 되어있는데 쉽게 이해가 되질않네요;;

  22. 안녕하세요.^
    질문 주신 0xFF는 먼저 Attiny2313에서는 INT값과 상관이 없는 값입니다. Attiny2313에서는 GIMSK와 MCUCR 또 EIFR에 의해서 INT 외부인터럽트가 설정이 되며 PORTB 레지스터는 B포트의 일반적인 입출력에 대해서 설정을 하는 레지스터입니다. 따라서 B포트에서는 LED 제어 목적으로 포트PB0~PB7까지 전체 OUT으로 설정했으므로, DDRB=0xFF이게 되는 것이고, PORTB=0xFF하시면 LED 전체가 ON되게 됩니다.

    외부인터럽트는 Attiny2313의 경우 INT0과 INT1까지밖에 사용할 수 없도록 구성되어있으며, GIMSK에서 INT0만 허용해 둔것이 0x40이고 GIMSK값에 0x40과 0x80 값을 더한값을 입력했다면 INT0과 INT1을 동시에 허용하게 되는 것입니다. 자세한 인터럽트 허용에 대한 설명은 http://binworld.kr/10 포스팅에서 확인 하실수 있으며, GIMSK (Genaral Interrupt Mask Register)에서 부터 확인이 가능합니다. Atmega128 또는 다른 AVR을 사용하고 계시다면 GIMSK가 EIMSK로 변경되어 있을 수도 있습니다. 만약 Atmega128에서 INT0~INT7까지 허용하고 싶다면 EIMSK = 0xFF 입력하여 주시면 되게 되는 것입니다.^^

    부족하지만 도움되는 답글이 되었다면 좋겠습니다.

  23. Blog Icon
    avr

    안녕하세요 글 잘봤습니다.
    만약 atmega128로 avr studio코드 이용해서 하려면
    int0 int1 연결하는 부분은 portd0과 portd1이 맞는건가요??
    정말초짜라서 인터럽트가 필요해서 예제를 따라해보는 중인데 책은 다 코드비젼이고 어렵네요

  24. 안녕하세요.
    정신없는 일상을 보내고 있어서, 답글이 늦었습니다.
    질문주신 외부 인터럽트 int0과 int1을 연결하는 부분은 데이터시트에 외부인터럽트 int로 정의(이미 지정되어 있는) 핀에서만 사용가능합니다. 그래서 PORTD2번과 PORTD3번에서 이용가능합니다.^
    그럼 도움 되셨기를 바랍니다.^

  25. Blog Icon
    ..

    친절한 설명 많은도움 되었습니다 감사합니다
    앞으로 종종 찾아들르겠습니다 ㅎ

  26. 부족한 글이 도움이 되셔서 다행입니다.^^
    앞으로도 가끔 찾아주신다고 따로 말씀도 주셨네요.ㅎㅎ

  27. Blog Icon
    감사합니다.

    안녕하세요 위의 내용 정말 누구보다 쉽게 설명 잘해주셔서 감사합니다.
    저같은 무뇌한?을 이해시켜주심을 ^^ 제가제일 해깔리는 부분은 소스코드짤때
    코드비젼: interrupt[EXT_INT0]void ext_int0_isr(void)이부분 입니다.

    보고 따라하면 그렇게 써놓았으니 그런가부다 할텐데.. 혼자 하려면 저 부분을 어떻게 쓴건지??
    정해진 틀에 맞춰서 써준것인가요 임의적으로 써준것인가요??? 다른소스를 보면 저렇게 선언을 했을텐데.. 정해진 틀이 있는건가요? 왜저렇게씌여졌는지요??

  28. 안녕하세요.^
    모바일 다음에서 댓글 알림이 안떠서 늦게 봤습니다. 먼저 AVR Studio의 경우 인터럽트 처리시에 ISR 이라는 인터럽트 루틴처리문을 불러서 io.h에 정의된 TIMER_OVF_vect, INT0_vect 같은 해당 인터럽트를 괄호에 입력하여 사용합니다.

    CodevisionAVR도 이와 동일한 방식으로 운용되는데요, interrupt[EXT_INT0]void ext_int0_isr(void) 일 경우 interrupt[사용된 인터럽트]void 사용된 인터럽트_isr(void)로 프로그램상 그렇게 사용하도록 되어있습니다. 다만 대괄호 안에 들어갈 내용은 tiny2313.h에 정의 되어있습니다.

    혹시 답글에 오류가 있을 수도 있으니, 문제가 있으면 피드백 해주시면 감사하겠습니다. 또한 답글이 도움이 되셨기를 바랍니다.^^

  29. Blog Icon
    감사합니다.

    감사합니다 아직도 잘 모르겠지만 정해진 명령어틀을 사용
    MUC에 따라 [ ]사이에 변동이 있다는 것인걸로 이해가 됩니다.
    더 노력해보겠습니다~

  30. 네, 인터럽트로 처리되는 것은 컴파일러 종류마다 이미 컴파일러 내부에 정의되어 있는 명령어가 있습니다. 그것을 사용해야 한답니다.^^
    이해 되시는데 도움이 되셨으면 좋겠습니다.^^

  31. Blog Icon

    비밀댓글입니다

  32. 안녕하세요.^^
    cli(); 와 sei();는 전체 인터럽트를 금지시키거나 허용시키는 함수인데요, EIFR은 별개의 외부인터럽트 동작시 비트1이 입력되고 자동으로 클리어가 안되는 것으로 알고있습니다. 따라서 다시 0을 써주어야 동작 가능한 상태로 되기 때문에 썼구요, cli();와 sei();를 인터럽트 처리 블록마다 넣는게 원래 맞기는 합니다. 하지만 그런식으로 작성하는것은 인터럽트를 하나만 사용하는 경우 말고, 외부인터럽트도 사용하면서 타이머카운터 인터럽트도 사용하고, 등등 일때 인터럽트 처리가 충돌하지 않도록 구성하기 위해서 많이 사용하는 방식이니 참고하시기 바랍니다.^

    그럼 2% 부족한 답글이라도 도움 되셨기를 바랍니다. ㅎㅎ

  33. Blog Icon
    Yeon

    스위치쪽에 저항이 없으면 어떻게되나요??
    저항을 다는 이유가 뭔가요??

  34. Blog Icon

    비밀댓글입니다

티스토리 툴바