인터럽트는 다양한 source (peripheral)로부터 발생할 수 있다.
인터럽트를 최대한 빨리 처리해주기 위해 HW 에서는 위와같이 다단계 구조를 채택하고 있다.
인터럽트의 종류에 따라 지나가는 HW가 달라질 수 있다. 일반적인 모든 인터럽트는 NVIC에서 관리하나, 일부 sleep mode에서 wake 시켜주는 인터럽트 같은 경우는 Wakeup Interrupt Controller (WIC)라는 곳에서 관리하는 모습을 확인할 수 있다.
발생 가능한 인터럽트의 종류에 따라 처리 가능한 핸들러 (ISR)를 벡터 형태로 정의하고 있는데, 이를 interrupt vector table이라고 부른다. 여기에는 인터럽트의 종류와 ISR의 시작 주소가 저장돼있다.
IRQ
라는 register가 관리하며 IRQ0 ~ IRQ31
까지 있다.ISER
register에서 해당하는 bit를 set/reset 함으로써 IRQn
에 해당하는 source의 interrupt를 enable 할 수 있다.ICER
register에서 해당하는 bit를 set/reset 함으로써 IRQn
에 해당하는 source의 interrupt를 disable 할 수 있다.PRIMASK
register 값을 건들이면, MCU가 인터럽트를 받지 않게끔 설정할 수 있다.IRQ
를 disable하고, 해당 영역을 수행하고 다시 IRQ
를 enable 할 때 사용한다.PCR
)에 대해서 배웠다. PCR
에는 IRQC
라는 4-bit 공간을 통해 인터럽트가 발생할 조건을 설정할 수 있다.IRQC
설정도 앞서 배운 CMSIS
에 정의된 API로 쉽게 설정할 수 있다.PORTA->PCR[~~] = PORT_PCR_IRQC(9) | PORT_PCR_MUX(1)
이런 식으로 말이다.ISFR
)이라고 부르며, 현재 포트에 발견된 인터럽트와 관련된 정보가 저장된다.0, 64, 128, 192
중 하나를 선택할 수 있다.작성한 코드의 일부 동작을 인터럽트를 사용해야겠다고 결정한 뒤 ISR을 만들게 된다.
이때 ISR이 다룰 범위를 설정하는 것을 partitioning이라고 한다. Partitioning 방법에는 여러가지가 있는데, ISR은 긴급한 task를 신속하게 처리하기 위한 목적으로만 활용해야 한다는 점을 잊지말자.
위 그림은 ISR이 다루는 범위를 크게 세 가지로 구분해서 예시를 들고있다. 주어진 상황은 MCU와 연결된 LED에 대한 동작을 ISR로 맡길 때 ISR이 다뤄야 하는 범위를 예로 들고 있다.
g_flash_LED
라는 1-byte flag 변수를 update해서 LED를 점등할지 혹은 소등할지 결정한다.g_RGB_delay
와 g_w_delay
변수를 update해서 LED를 점등 또는 소등을 유지할 시간을 결정한다.ISR이 main code의 variable을 다루기 위해서는 shared variables를 사용해야하므로 static type으로 선언이 돼야한다. Static variable은 코드의 reusability와 readability를 떨어트리는 단점이 있기 때문에 개발자는 performance와 complexity 사이에서 저울질하며 balance가 좋은 partitioning을 하도록 노력해야 한다.
위에서 다룬 인터럽트의 HW 적인 요소와 SW 적인 요소를 합쳐서 정리해보자.
인터럽트를 다루기 위해서는 Peripheral, NVIC, Core 세 가지 부분에서 인터럽트에 대해 설정해야 한다.
IRQ
에 대해 설정해줘야 한다. APRC
에 있는 IRQC
bits를 설정해준다.void PORTD_IRQHandler(void) {
// 스위치 값을 읽는다
if ((PORTD->ISFR & MASK(SW1_POS))) {
if (SWITCH_PRESSED(SW1_POS)) { // flash white
g_flash_LED = 1;
} else {
g_flash_LED = 0;
}
}
if ((PORTD->ISFR & MASK(SW2_POS))) {
if (SWITCH_PRESSED(SW2_POS)) {
g_w_delay = W_DELAY_FAST;
g_RGB_delay = RGB_DELAY_FAST;
} else {
g_w_delay = W_DELAY_SLOW;
g_RGB_delay = RGB_DELAY_SLOW;
}
}
// clear status flags
PORTD->ISFR = 0xffffffff;
}