02. Interrupt#
GPIO Interrupt Example (GPIO Interrupt를 사용하여 Switch를 눌렀을 때 LED Toggle)#
- Swtich를 눌렀을 때 발생한 Interrupt가 LED를 Toggle시키는 예제이다.
- 새로운 예제를 위한 프로젝트를 생성한다.
- 원하는 동작을 위해 레지스터와 메모리에 직접 접근해 값을 써야한다.
- GPIO Interrupt를 사용하기 위해 Datasheet를 분석한다.
- 분석 결과를 활용해 임베디드 프로그래밍을 한다.
1. Datasheet 분석#
Easy Module Shield V1의 SW1을 사용하기 위해, PTD2를 사용한다.
1-1. MSCR#
PTDm을 제어하기 위한 MSCRn 번호는 \(\rm n=(m+96)\)로 구할 수 있다.
MSCRn의 주소는 \(\rm SIUL2\ base + MSCR\ offset+ (n \times 4)\)로 구할 수 있다.
MSCR98 주소: 4029_03C8h (4029_0000h + 240h + 188h)
1-2. IMCR#
S32K312는 외부 인터럽트를 사용할 때 IMCRn의 SSS 비트를 EIRQ로 설정할 필요가 있다. PTD2의 경우 IMCR538의 SSS 비트를 0011b로 설정해야 한다.
하지만 IMCR538이라는 레지스터는 없다. 512의 offset이 적용된 상태이므로 실제로는 \(538-512=26\)이므로 IMCR26에 접근하면 된다.
IMCRn의 주소는 \(\rm SIUL2\ base + IMCR\ offset+ (n \times 4)\)로 구할 수 있다.
IMCR26 주소: 4029_0AA8h (4029_0000h + A40h + 68h)
IMCR26의 SSS 비트에 앞서 확인한 값(0011b)을 쓰면 된다.
1-3. 인터럽트 리퀘스트 번호#
위에서 IMCR 비트를 확인할 때 EIRQ[10]이라는 번호도 확인할 수 있는데, 이는 REQ[10]에 해당하므로 1번 채널에 해당한다.
1-4. 인터럽트 관련 SIUL2 레지스터#
- IFER (Interrupt Filter Enable Register) 를 set해 글리치(노이즈) 필터를 켠다.
- DIRER (DMA/Interrupt Request Enable Register) 를 clear해 인터럽트를 차단한다.
- IREER (Interrupt Rising-edge Event Enable Reigster) 또는 IFEER (Interrupt Falling-edge Event Enable Register) 을 set해 인터럽트 발생 조건을 설정한다. 실습 보드에서 스위치를 누르면 falling-edge가 발생하므로 본 예제에서는 IFEER0을 설정한다.
- MSCR의 OBE를 clear하고 IBE를 set해 입력으로 설정한다.
- DIRSR (DMA/Interrupt Request Select Register) 을 clear해 interrupt 모드로 설정한다. (set시 DMA 모드)
- 필터 관련 설정은 생략한다.
- DISR (DMA/Interrupt Status flag Register) 을 set해 인터럽트 플래그를 clear한다. (w1c 속성이므로 set해서 clear)
- DIRER을 set해 인터럽트를 활성화한다.
설정에 필요한 레지스터 주소 오프셋은 위와 같다.
1-5. NVIC 레지스터#
- NVIC 모듈에서 사용하고자 하는 interrupt의 우선순위를 정하고 enable해주어야 interrupt를 사용할 수 있다.
- 각 모듈의 interrupt에 대한 설정 위치(메모리 주소)가 정해져 있기 때문에 사용하고자 하는 interrupt를 선택적으로 설정할 수 있다.
- NVIC 레지스터의 종류
이름 | 설명 |
---|---|
ISER(Interrupt Set-Enable Register) | 레지스터의 각 비트는 32개의 인터럽트 중 하나에 해당하고, 특정 비트를 set하면 해당 인터럽트가 활성화된다. |
ISER(Interrupt Clear-Enable Register) | 레지스터의 각 비트는 32개의 인터럽트 중 하나에 해당하고, 특정 비트를 set하면 해당 인터럽트가 비활성화된다. |
ISER(Interrupt Set-Pending Register) | 레지스터의 각 비트는 32개의 인터럽트 중 하나에 해당하고, 특정 비트를 set하면 해당 인터럽트를 pending state로 만든다. |
ISER(Interrupt Clear-Pending Register) | 레지스터의 각 비트는 32개의 인터럽트 중 하나에 해당하고, 특정 비트를 set하면 해당 인터럽트를 pending state에서 벗어나게 한다. |
IABR(Interrupt Active Bit Registers) | 어떤 인터럽트가 활성화 상태인지 판별하기 위해 읽는 비트다. 인터럽트가 활성화되어 있으면 1, 아니면 0이다. |
IPR(Interrupt Priority Registers) | 레지스터의 각 바이트는 해당 인터럽트의 우선순위를 결정한다. |
- NVIC 레지스터 주소
위의 표에서 각 레지스터의 시작 주소를 알 수 있고, 아래의 방법으로 m번째 인터럽트를 제어하는 레지스터 번호를 구할 수 있다.
IPR을 제외한 레지스터 번호 구하기
IxxRn: \(\rm n=floor(m\div32)\)
해당 레지스터에서 각 인터럽트를 제어하는 비트 번호 구하기
\(\rm bit=n\ mod\ 32\)
IPR 레지스터 번호 구하기
IPRn: \(\rm n=m\)
인터럽트 번호를 구하기 위해서는 프로젝트 폴더 내 Startup_Code 폴더에 있는 startup_ARMCM7.c 파일을 확인할 필요가 있다.
[1.1.3에서 설명한 내용]에서 1번 채널 인터럽트를 사용해야 하므로, SIUL_1_Handler를 사용해야 한다.
0번 인터럽트가 267행이고, 사용하고자 하는 인터럽트 핸들러 SIUL_1_Handler가 321행이므로 \(\rm 321-267 = 54\)번 인터럽트임을 알 수 있다. 따라서
NVIC_ISER1 주소: E000_E104h (E000_E100h + 4h)
NVIC_ICPR1 주소: E000_E284h (E000_E280h + 4h)
NVIC_IPR54 주소: E000_E436h (E000_E400h + 36h) (54=36h)
2. 프로그래밍#
2-1. 매크로 정의#
// SIUL2 essential Registers
#define SIUL2_BASE (0x40290000UL)
#define SIUL2_MSCR_BASE (SIUL2_BASE + 0x240)
#define SIUL2_MSCR74 (*(volatile unsigned *)(SIUL2_MSCR_BASE + 0x128))
#define SIUL2_MSCR98 (*(volatile unsigned *)(SIUL2_MSCR_BASE + 0x188))
#define SIUL2_IMCR_BASE (SIUL2_BASE + 0xA40)
#define SIUL2_IMCR26 (*(volatile unsigned *)(SIUL2_IMCR_BASE + 0x68))
#define SIUL2_GPDO_BASE (SIUL2_BASE + 0x1300)
#define SIUL2_GPDO74 (*(volatile unsigned char *)(SIUL2_GPDO_BASE + 0x49))
// MSCR & IMCR Bits
#define OBE_BIT 21 // Output Buffer Enable
#define IBE_BIT 19 // Input Buffer Enable
#define SSS_BITS 0 // Source Signal Select
// SIUL2 Interrupt Registers
#define SIUL2_DISR0 (*(volatile unsigned *)(SIUL2_BASE + 0x10))
#define SIUL2_DIRER0 (*(volatile unsigned *)(SIUL2_BASE + 0x18))
#define SIUL2_DIRSR0 (*(volatile unsigned *)(SIUL2_BASE + 0x20))
#define SIUL2_IREER0 (*(volatile unsigned *)(SIUL2_BASE + 0x28))
#define SIUL2_IFEER0 (*(volatile unsigned *)(SIUL2_BASE + 0x30))
#define SIUL2_IFER0 (*(volatile unsigned *)(SIUL2_BASE + 0x38))
// SIUL2 Interrupt Register Bits
#define EIRQ10 10
#define IFE10 10
#define EIRE10 10
#define IREE10 10
#define IFEE10 10
#define DIRS10 10
#define EIF10 10
// Arm Cortex-M7 NVIC Registers
#define NVIC_ISER1 (*(volatile unsigned *)(0xE000E100 + 0x4))
#define NVIC_ICPR1 (*(volatile unsigned *)(0xE000E280 + 0x4))
#define NVIC_IPR54 (*(volatile unsigned char *)(0xE000E400 + 0x36))
2-2. PORT_init()#
- Switch, LED 포트 설정을 위해 PORT_init() 함수를 구현한다.
PTC10(MSCR74)는 출력으로, PTD2(MSCR98 & IMCR26)은 입력 및 EIRQ로 설정한다.
void PORT_init(void) { SIUL2_MSCR74 &= ~(0b1111 << SSS_BITS); // GPIO SIUL2_MSCR74 |= (1 << OBE_BIT); // output SIUL2_MSCR98 &= ~(0b1111 << SSS_BITS); // GPIO SIUL2_MSCR98 |= (1 << IBE_BIT); // input SIUL2_IMCR26 &= ~(0b1111 << SSS_BITS); SIUL2_IMCR26 |= (0b0011 << SSS_BITS); // EIRQ10 }
2-3. SIUL2_init_exint()#
- 인터럽트를 위한 SIUL2 레지스터 설정 함수를 구현한다.
void SIUL2_init_exint(void) { // 1. Set IFER to enable glitch filter SIUL2_IFER0 &= ~(1 << IFE10); SIUL2_IFER0 |= (1 << IFE10); // 2. clear DIRER to disable interrupt SIUL2_DIRER0 &= ~(1 << EIRE10); // 3. write 1 to IFEER0 to set pin polarity SIUL2_IFEER0 |= (1 << IFEE10); // 5. write DIRSR0 to select DMA or interrupt SIUL2_DIRSR0 &= ~(1 << DIRS10); // 0: Interrupt // 7. write 1 to DISR0 to clear any flag SIUL2_DISR0 |= (1 << EIF10); // w1c // 8. set DIRER0 to enable interrupt SIUL2_DIRER0 |= (1 << EIRE10); }
2-4. NVIC_init_IRQs()#
- 54번째 인터럽트에 대한 NVIC 설정 함수를 구현한다.
void NVIC_init_IRQs(void) { NVIC_ICPR1 |= (1 << (54 % 32)); NVIC_ISER1 |= (1 << (54 % 32)); NVIC_IPR54 |= (10 << 4); }
2-5. main#
- 위에서 작성한 함수들을 호출한다.
int main(void) { SIUL2_init_exint(); NVIC_init_IRQs(); while (1); return 0; }
2-6. ISR#
- 인터럽트 발생 시 호출될 인터럽트 핸들러를 작성한다.
인터럽트가 발생할 때마다 PTC10을 토글하는 코드이다. ISR이 끝나기 전에 DISR(플래그 레지스터)에 1을 써 flag를 clear해주지 않으면 인터럽트를 다시 발생시킬 수 없으므로, 반드시 마지막에 flag를 클리어해 주어야 한다.
void SIUL_1_Handler(void) { SIUL2_GPDO74 = SIUL2_GPDO74 ? 0 : 1; SIUL2_DISR0 |= (1 << EIF10); // w1c }
3. 동작 모습#
빌드, 디버그 후 동작시켰을 때 SW1을 누를 떄마다 Red LED가 토글됨을 확인할 수 있다.