본 글은 MSP430G2553 MCU를 기준으로 작성되었습니다.
세부적인 내용은 제품군마다 조금씩 다를 수 있습니다.
Interrupt란 프로그램 실행 중 외부 입출력 처리나 예외 상황 처리가 필요한 경우 MCU에 이를 알리기 위한 신호를 말한다. 외부 입출력이나 예외 상황에 대해 CPU가 능동적으로 모니터링할 경우 자원 낭비가 크기 때문에, 해당 처리가 필요한 경우에만 CPU에게 신호를 주어 해당 업무를 우선적으로 처리하도록 하기 위해 사용한다.
MSP430에는 아래와 같이 세 가지의 인터럽트가 있다.
- 시스템 리셋
- 논-마스커블 인터럽트(NMI)
- 마스커블 인터럽트
위에서부터 순서대로 우선 순위가 높으며, 자세한 사항은 아래 그림과 같다.
일반적으로 시스템 리셋과 NMI는 비활성화할 수 없으며, 시스템 리셋 발생 시 POR 및 PUC를 수행한다. POR은 Power On Reset의 줄임말로, 프로그램의 시작 주소를 맨 처음으로 돌리는 것을 말한다. PUC는 Power Up Clear의 줄임말로, 프로그램의 설정값을 초기값으로 돌리는 것을 말한다.
비활성화시킬 수 없는 인터럽트를 논-마스커블 인터럽트, 줄여서 NMI라고 한다. NMI 인터럽트는 아래 세 가지 원인에 의해 발생한다.
- /NMI 핀이 NMI 모드로 설정된 후 인터럽트 에지가 감지된 경우
- 오실레이터의 오동작이 감지된 경우
- 플래시 메모리로 잘못된 접근이 발생한 경우
인터럽트 발생 시 해당 인터럽트에 대한 ISR을 수행한다.
비활성화시킬 수 있는 인터럽트를 마스커블 인터럽트라고 한다. 대부분의 사용자 정의 인터럽트는 모두 마스커블 인터럽트에 해당한다.
인터럽트 발생 시 해당 인터럽트에 대한 ISR을 수행한다.
MSP430도 인터럽트 처리 과정이 우리가 컴퓨터 구조 시간에 배우는 것과 비슷하게 동작한다. 상세한 명세는 아래 두 그림을 참고한다.
GPIO를 이용한 마스커블 인터럽트와 관련된 레지스터에 대해 나열하였다. 이전 GPIO 글에서 소개한 PxDIR 레지스터와 더불어 사용된다.
- PxIE - Interrupt Enable Register
- PxIES - Interrupt Edge Select Register
- PxIFG - Interrupt Flag Register
- PxDIR - GPIO Direction Register
해당 핀을 인터럽트 핀으로 사용할지를 설정하는 레지스터이다. 비트를 1로 설정하면 인터럽트 핀으로, 0으로 설정하면 일반 핀으로 사용된다.
Rising Edge와 Falling Edge 중 어느 것을 인터럽트 신호로 간주할 것인지를 설정하는 레지스터이다. 비트를 1로 설정하면 Falling Edge를, 0으로 설정하면 Rising Edge를 인터럽트 신호로 간주한다.
인터럽트가 발생했음을 알려주는 레지스터이다. 인터럽트 핀으로부터 인터럽트 Edge가 감지되면 본 레지스터의 비트가 1로 설정됨과 동시에 ISR이 실행된다. ISR 안에서 원하는 동작을 처리한 후 IFG의 비트를 클리어해줌으로써 다음번 인터럽트 신호를 받을 준비를 할 수 있다. 코드로는 아래와 같다.
#pragma vector=PORT2_VECTOR
__interrupt void Port_2(void) // P2 포트에 대한 ISR
{
if (P2IFG & BIT0) // P2.0에 인터럽트가 발생하면
{
... // 원하는 동작 수행
}
P2IFG &= ~BIT0; // 인터럽트 비트 클리어
}
예제 하나만 다뤄보자.
- 인터럽트를 이용한 LED 제어
두 개의 입력 포트와 두 개의 출력 포트를 이용하여 인터럽트 동작을 확인하려 한다. 아쉽게도 MSP-EXT430G2ET 보드에 버튼 스위치가 하나 밖에 없어서 나머지 하나를 GPIO 헤더와 점퍼선으로 대체한다.
시나리오는 아래와 같다.
- 버튼 스위치(P1.3)와 GPIO 헤더(P1.4)를 인터럽트 입력 핀으로 사용한다.
- 버튼 스위치는 Rising Edge, GPIO 헤더는 Falling Edge로 설정한다.
- LED 1, 2를 출력 핀으로 사용한다. (P1.0, P1.6)
- 버튼 스위치를 눌렀다 떼면 LED 1을, GPIO 헤더에 전압을 넣었다 떼면 LED 2를 토글시킨다.
코드는 아래와 같다.
#include <msp430.h>
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
if (P1IFG & BIT3) // P1.3 인터럽트 감지 시
{
P1OUT ^= BIT0; // P1.0 출력 토글
P1IFG &= ~BIT3; // P1.3 인터럽트 비트 초기화
}
else if (P1IFG & BIT4) // P1.4 인터럽트 감지 시
{
P1OUT ^= BIT6; // P1.6 출력 토글
P1IFG &= ~BIT4; // P1.4 인터럽트 비트 초기화
}
}
int main(void)
{
WDTCTL = WDTPW | WDTHOLD; // 와치독 비활성화
P1DIR |= BIT0 | BIT6; // P1.0, P1.6 핀을 출력으로 설정
P1OUT |= BIT0 | BIT6; // P1.0, P1.6 핀의 출력을 HIGH로 설정
P1IE |= BIT3 | BIT4; // P1.3, P1.4를 인터럽트 핀으로 설정
P1IES &= ~BIT3; // P1.3 인터럽트 에지를 Rising으로 설정
P1IES |= BIT4; // P1.4 인터럽트 에지를 Falling으로 설정
P1IFG &= ~(BIT3 | BIT4); // P1.3, P1.4 인터럽트 비트 초기화
__bis_SR_register(GIE); // 전역 인터럽트 활성화
while (1);
}
LED 1은 버튼 스위치를 눌렀다 떼면 점멸하는 것을 볼 수 있다.
LED 2는 점퍼선 하나를 한 쪽은 3.3V에 넣고, 다른 한 쪽은 P1.4에 넣었다 빼길 반복하면 점멸하는 것을 볼 수 있다.
ISR 안에서 GIE 비트를 1로 설정하는 방식으로 Nested Interrupt 처리를 구현할 수 있다고 한다. 다만 인터럽트의 우선순위가 고려되지 않는다고 한다. 그렇다면 비교적 덜 중요한 인터럽트의 ISR에서만 GIE를 1로 설정함으로써 어느 정도 우선순위를 나눌 수 있을 것 같다.
MSP-EXP430G2ET의 LED, Button 관련 회로도를 첨부하였다.