반응형

외부 인터럽트는 INT7:0핀에 입력되는 트리거 신호에 의해서 발생한다.


트리거 신호는 폴링 에지, 라이징 에지, 로우 레벨 3가지가 될 수 있다. 어떤 신호 입력을 기다릴지 미리 설정해놓고 기다리다가  해당 신호가 입력되는 순간 인터럽트가 발생하는 것이다.



Atmega 128에서 외부 인터럽트용 핀은 아래 그림처럼 8개가 있다.

   


INT7:0 핀을 출력으로 설정해 놓고 인터럽트 핀을 소프트웨어적으로 조작하더라도 인터럽트가 발생한다. 예를들어 falling edge때 인터럽트가 발생하게 해놓고 해당 핀의 출력이 1에서 0으로 떨어지는 것처럼 값을 1과 0 값을 순서대로 출력해도 인터럽트가 발생한다.


어떤 트리거 신호가 입력될 때 인터럽트를 발생시킬지는 외부 인터럽트 제어 레지스터 EICRA (INT3:0)와 EICRB (INT7:4)에서 설정한다.


로우 레벨 트리거 신호 때 외부 인터럽트가 발생하도록 설정했다면, 핀이 LOW상태를 유지하는 동안 인터럽트가 연속해서 계속 발생한다.


INT7:4 핀들에서 폴링 에지 또는 라이징 에지 트리거 신호를 검출하기 위해서는 입출력 클록이 있어야한다. 반면 INT3:0 핀들에선 로우 레벨 또는 폴링/라이징 에지 트리거 신호가 비동기적으로 검출된다.



External Interrupt Control Register A – EICRA


SREG 레지스터의 I플래그가 1로 세팅되어 있고, EIMSK 레지스터에서 해당하는 인터럽트 마스크가 1로 세팅되어 있다면, 외부 인터럽트 3-0은 외부 핀 INT3:0에서 입력되는 트리거 신호에 의해서 동작한다.


검출 가능한 트리거 신호를 설정하는 방법은 표48에 나와있다.


INT3..INT0 핀 상에 폴링 에지와 라이징 에지 트리거는 비동기적으로 인식되며 INT3:0 핀에 입력되는 펄스는 최소 너비(50ns)보다 넓어야 인터럽트가 발생한다. 최소 너비보다 짧은 펄스들은 인터럽트가 발생한다고 보장하지 못한다.


Low level 트리거가 선택되었을 땐, 인터럽트가 발생하기 위해 현재 실행중인 명령어가 완료될 때까지 Low lovel을 유지하고 있어야한다.


Low level 인터럽트가 발생하도록 설정해놓았다면 핀이 로우 레벨을 계속 유지하는 동안 연속적으로 인터럽트가 발생한다


ISCn 비트를 변경하는 중에 인터럽트가 발생할 수 있다. 그러므로, 변경하기 전에 EIMSK 레지스터에서 해당 인터럽트 인에이블 비트를 클리어해서 INTn을 먼저 비활성화하는 것을 권장한다. 그리고 나서 ISCn 비트를 변경하고 인터럽트를 다시 활성화하기 전에 EIFR 레지스터의 Interrupt Flag bit (INTFn)에 1을 기록함으로써 INTn 인터럽트 플래그를 클리어해야 한다.



External Interrupt Control Register B – EICRB


SREG레지스터의 I플래그가 1로 세팅되고 EIMSK 레지스터의 해당되는 인터럽트 마스크가 1로 세팅되면 외부 인터럽트 7-4는 외부 핀 INT7:4에 의해 동작하다.


검출 가능한 트리거 신호를 설정하는 방법은 표50에 나와 있다.



에지 또는 토글 트리거가 선택되었다면, 펄스는 1 클록보다 넓어야 한다. 짧으면 인터럽트가 발생한다는 보장을 하지 못한다.


Low 레벨 트리거가 선택되었다면, low 레벨은 반드시 현재 실행중인 명령어가 완료될 때까지 유지되어야한다. 


Low level 인터럽트가 발생하도록 설정해놓았다면 핀이 로우 레벨을 계속 유지하는 동안 연속적으로 인터럽트가 발생한다



External Interrupt Mask Register – EIMSK

EIMSK레지스터의 INT7-INT0 비트가 1로 세팅되고 Status Register의 I비트가 1로 세팅되면, 해당되는 외부 인터럽트 핀에서의 트리거 신호 입력으로 인터럽트가 발생할 수 있다.



External Interrupt Control Registers( EICRA, EICRB)에서 인터럽트가 발생하는 트리거 신호를 정의한다. 상승 에지, 하강 에지, 로우레벨을 선택할 수 있다. 


핀을 출력으로 해놓고 소프트웨어적으로 조작해도 해당 핀에서 인터럽트가 발생한다.



External Interrupt Flag Register – EIFR

INT7:0 핀에 트리거 신호가 입력되어 인터럽트가 발생하면 EIFR레지스터의 해당되는 비트가 1로 세팅된다.


SREG 레지스터의 I비트가 1로 세팅되어 있고 EIMSK 레지스터에서 해당되는 비트가 1로 세팅되어 있다면, MCU는 현재 실행중인 프로그램 흐름을 중단하고 인터럽트 벡터로 점프한다. 이 흐름은 인터럽트 루틴이 실행되면 EIFR레지스터의 해당되는 비트가 0으로 클리어 된다.   해당 비트에 1을 기록하여 강제로 클리어 시키는 방법도 있다.


Low 레벨 트리거때 인터럽트가 발생하도록 설정되었다면 EIFR 레지스터의 해당 비트는 항상 0으로 클리어 된다. Low 레벨을 유지하는 동안 계속 인터럽트가 발생할 수 있는 것도 이 때문일 것이다.



아래처럼 회로를 구성하고 두가지 프로그램을 테스트 해본다.  5V에 풀업저항을 달아서 평소에 PD0핀이 5V를 유지하도록 했다. 버튼을 누르게 되면 그라운드와 연결이 되어 PD0핀의 전압은 0V로 떨어지게 된다. 



1.  Low 레벨 인터럽트 태스트.. 버튼을 누르고 있는 동안 계속 인터럽트가 발생하는 것을 볼 수 있다. 

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/*
 * interrupt_example1.c
 * Low Level을 유지하는 동안 인터럽트가 계속 발생한다. 
 *
 * Created: 2016-06-16 
 * Author : webnautes
 */ 
 
#define F_CPU 16000000UL
 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
 
 
 
//1. baud rate를 선택
#define USART_BAUDRATE 9600
 
//2.시스템 클록과 원하는 baud rate를 이용하여 UBRR 값을 계산한다.
#define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
 
 
void usartInit()
{
    //3. UBRR0은 16비트 레지스터이기 때문에 8비트씩 나누어서 넣어야 한다.
    UBRR0H = (uint8_t)(UBRR_VALUE>>8);
    UBRR0L = (uint8_t) UBRR_VALUE;
    
    //4. USART 설정
    UCSR0C |= (1<<UCSZ00)|(1<<UCSZ01); //Charecter size : 8비트
    UCSR0C &= ~(1<<USBS0); //stop  bit : 1비트
    UCSR0C &= ~((1<<UPM01)|(1<<UPM00)); // no parity mode
    
    //5. 송수신을 가능하게 한다.
    UCSR0B=(1<<RXEN0)|(1<<TXEN0);
}
 
void transmitByte(uint8_t data) {
    
    //이전 전송이 끝나기를 기다림
    while(!(UCSR0A&(1<<UDRE0))){};
    
    UDR0 = data;                                            /* send data */
}
 
void printString(const char str[]) {
    uint8_t i = 0;
    while (str[i]) {
        transmitByte(str[i]);
        i++;
    }
}
 
//Interrupt Service Routine for INT0
ISR(INT0_vect)
{
 
    printString("인터럽트발생\n\r");
 
    PORTB |= 1<<PB3;
    _delay_ms(500);
 
}
 
 
 
int main(void)
{
    usartInit();
    DDRB |= 1<<PB3;         //PORTB3을 출력으로 설정, LED용
    
    //Low level에서 인터러브 발생
    EICRA  &= ~(1<<ISC00); 
    EICRA  &= ~(1<<ISC01);
     
    EIMSK |= 1<<INT0;    //External Interrupt Request 0 Enable
                         //PD0에 스위치 연결, 풀업 저항을 추가한 회로여야 한다. 
    
    sei();      //Grobal Interrupt Enable
    
    while(1)
    {
        PORTB &= ~(1<<PB3);
        _delay_ms(500);
    }
}
 
 
cs


2. falling edge 인터럽트 태스트 - 버튼을 누르는 순간 인터럽트가 발생한다.

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/*
 * interrupt_example2.c
 *
 * Created: 2016-06-16 
 * Author : webnautes
 */ 
 
#define F_CPU 16000000UL
 
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
 
 
 
//1. baud rate를 선택
#define USART_BAUDRATE 9600
 
//2.시스템 클록과 원하는 baud rate를 이용하여 UBRR 값을 계산한다.
#define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
 
 
void usartInit()
{
    //3. UBRR0은 16비트 레지스터이기 때문에 8비트씩 나누어서 넣어야 한다.
    UBRR0H = (uint8_t)(UBRR_VALUE>>8);
    UBRR0L = (uint8_t) UBRR_VALUE;
    
    //4. USART 설정
    UCSR0C |= (1<<UCSZ00)|(1<<UCSZ01); //Charecter size : 8비트
    UCSR0C &= ~(1<<USBS0); //stop  bit : 1비트
    UCSR0C &= ~((1<<UPM01)|(1<<UPM00)); // no parity mode
    
    //5. 송수신을 가능하게 한다.
    UCSR0B=(1<<RXEN0)|(1<<TXEN0);
}
 
void transmitByte(uint8_t data) {
    
    //이전 전송이 끝나기를 기다림
    while(!(UCSR0A&(1<<UDRE0))){};
    
    UDR0 = data;                                            /* send data */
}
 
void printString(const char str[]) {
    uint8_t i = 0;
    while (str[i]) {
        transmitByte(str[i]);
        i++;
    }
}
 
//Interrupt Service Routine for INT0
ISR(INT0_vect)
{
 
    printString("인터럽트발생\n\r");
 
    PORTB |= 1<<PB3;
    _delay_ms(500);
 
}
 
 
 
int main(void)
{
    usartInit();
    DDRB |= 1<<PB3;         //PORTB3을 출력으로 설정, LED용
 
    
    //falling edge에서 인터러브 발생
    EICRA  &= ~(1<<ISC00);
    EICRA  |= 1<<ISC01;
    
    EIMSK |= 1<<INT0;    //External Interrupt Request 0 Enable
                        //PD0에 스위치 연결, 풀업 저항을 추가한 회로여야 한다.
    
    sei();      //Grobal Interrupt Enable
    
    while(1)
    {
        PORTB &= ~(1<<PB3);
        _delay_ms(500);
    }
}
 
cs


반응형

문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.

도움이 되셨다면 토스아이디로 후원해주세요.
https://toss.me/momo2024


제가 쓴 책도 한번 검토해보세요 ^^

+ Recent posts