이 영역을 누르면 첫 페이지로 이동
천천히 꾸준히 조용히 블로그의 첫 페이지로 이동

천천히 꾸준히 조용히

페이지 맨 위로 올라가기

천천히 꾸준히 조용히

천천히 꾸준히 조용히.. i3months 블로그

[Embedded] Interrupt

  • 2026.03.31 11:10
  • Computer Science/Embedded Software
반응형

 

 

 

Cortex-M4에서는 하드웨어 컨트롤러 NVIC가 사건을 통합 관리하고, 각 사건은 우선순위와 PK를 가진다.

사건은 벡터 테이블에 등록된 핸들러로 자동 분기됨.

 

 

 

 

 

예외가 좀 커다란 카테고리고, 인터럽트는 예외의 한 종류.

모든 인터럽트 신호는 NVIC(Nested Vectored Interrupt Controller)를 거쳐서 프로세서 코어로 들어가고, 주변 장치는 코어를 깨울 수 없다. (NMI는 예외. NVIC를 거치지 않고 들어간다)

 

스프링의 Dispatcher Servlet과 유사함. 

우선순위 비교, Masking, Pending 관리를 한 곳에서 처리한다.

 

USART로 시리얼 데이터 한 글자가 도착했다고 하자.

Cortex-M4 칩 안에 USART 시리얼 통신 모듈이 있고, 외부에서 이 모듈로 'A'가 도착함. (main은 실행 중이고 USART 53번 인터럽트)

데이터가 어떻게 처리되는지 하나씩 살펴보자.

 

1. USART 관련 처리 

'A'를 데이터 레지스터에 저장하고, 상태 레지스터 RXNE비트를 1로 바꾼다. 

RXNE 비트가 1이 되면 출력 라인을 high로 올린다. 

비트가 1이 되면 그 선을 따라 NVIC에 도달할 수 있음. 인터럽트는 그냥 출력 라인만 high로 올리는 것..

 

2. NVIC 검토

NVIC는 53번 입력이 high가 된 순간 코어를 깨울지 말지 검토한다.

우선 NVIC 내부에는 IRQ가 켜져있는지 확인하는 비트맵이 있고, 이 비트맵을 활용해 IRQ가 enabled인지 확인한다.

그리고 PRIMASK를 확인해 마스킹 되어 있는지를 확인.

마지막으로 더 높은 우선순위의 작업이 처리중인지 확인하고, 세 단계 모두 통과하면 다음 단계로 이동한다.

 

3. NVIC가 코어에게 요청 

여기서는 NVIC가 코어에게 53번 인터럽트를 처리하라고 신호를 보낸다.

코어는 실행 중인 명령어를 끝낸 후 인터럽트 진입 절차로 들어간다. (진입 절차는 12 cycle)

 

4. 벡터 테이블 lookup

IRQ가 몇 번인지는 알았으니 벡터 테이블에서 이 인터럽트를 처리할 핸들러를 찾는다.

먼저 인덱스를 계산하고 그걸 바탕으로 주소를 찾는다. 그리구 이 주소를 PC에 넣음.

다음 명령어는 USART_IRQHandler의 첫 줄이 되고, 인터럽트를 처리하게 됨.

 

이걸 Auto-Vectored 방식이라고 부른다. 

Vectored 방식은 외부 디바이스가 레지스터에 핸들러 주소를 들고 있는 방식.

 

5. 핸들러 실행 

PC가 핸들러 첫 줄을 가리키니까 코어는 계속 실행된다. 

RXNE가 1이 됐기에 여기까지 올 수 있었음.. 

다만 RXNE 말고 다른 외부 인터럽트들은 ISR 안에서 명시적으로 플래그를 0으로 세팅해줘야 한다. 안하면 무한호출;;

 

6. 리턴 

핸들러 함수가 마무리되면 컴파일러가 BX LR 명령어를 박아둔다.

저 명령어가 실행되면 스택에 저장했던 레지스터들을 복원하고 PC를 끊긴 자리로 복귀시킴.

이후 Handler Mode -> Thread Mode로 전환되고 작업을 이어감. (여기까지가 10 cycle)

 

 

 


 

 

진입에 12 cycle, 리턴에 10 cycle이 걸린다. 이 사이클동안 하드웨어는 레지스터를 조작하고 EXC_RETURN을 수행함.

 

12 cycle동안 하드웨어는 5단계로 나눠서 작업을 수행한다.

 

1. 명령어 마무리 

실행 중인 명령어는 끝까지 마무리한다.

다만 LDM, STM 처럼 여러 워드를 한 번에 옮기는 명령어는 중간에 끊기도 함 

 

 

2. 8개 레지스터를 스택에 push

 

 

하드웨어가 현재 사용 중이 스택에 8개 레지스터를 push한다.

 

ARM Architecture Procedure Call Standard은 함수 호출 시 어떤 레지스터는 호출자로 보관하고 어떤 레지스터는 호출되는 함수가 보관한다는 규칙을 정해둔다.

 

인터럽트가 자동으로 push하는 8개는 모두 caller-saved 레지스터이고, 하드웨어가 caller 입장에서 보존이 필요한 것들을 자동으로 백업하는 것.

 

이러면 ISR 내부 함수는 그냥 C언어처럼 R4~R11을 사용할 수 있다.

어차피 AAPCS때문에 ISR이 callee로 R4~R11을 다루는데 이건 C 컴파일러가 자동으로 해준다.

 

결론은 ISR을 평범한 C 함수로 다룰 수 있다는 것. 

 

 

3. ISR 주소 fetch

레지스터에 push하는 것과 동시에 코어는 벡터 테이블에서 핸들러 주소를 읽어온다.

 

4. LR에 EXC_RETURN 적재 

LR에는 리턴 주소가 들어가지만 인터럽트에서는 0xFFFFFFFx 형태의 특수값이 들어간다. 그리고 이게 EXC_RETURN

 

5. 모드 전환 및 핸들러 첫 명령어 실행 

Thread Mode -> Handler Mode 로 모드를 전환하고 활성 스택을 MSP로 변경한다.

PC에는 핸들러의 주소가 들어가게 됨.

 

 

설정된 LR 주소의 특정한 패턴 덕분에 하드웨어는 인터럽트 리턴을 인식할 수 있음.

BX LR을 수행할 때 LR이 인터럽트 리턴을 인식하면 자동 복원 모드로 진입한다.

 

자동 복원 시에는 설정된 비트의 하위 4비트만 보면 된다. 

4비트에 어떻게 복원할지에 대한 정보가 모두 담겨있음.

 

 

 

 

 

일단 Nested는 기본적으로 활성화된 상태. 

같은 우선순위는 Preemption이 불가능하고, 스택은 자연스럽게 쌓이게 된다.

핸들러는 항상 MSP를 사용하니 nested가 깊어지면 MSP가 터질 수 있음. 오버플로우에 주의

 

 

 


 

 

 

인터럽트가 내부적으로 어떻게 작동하는지 알았으니.. 이제 소스코드 작성에 초점을 맞추자.

인터럽트 사용 순서를 잘 맞춰야 함.

 

1. 핸들러 함수 작성 및 벡터 테이블 등록

2. 우선순위 설정 

3. 주변장치의 인터럽트 생성 활성화

4. NVIC에서 IRQ 활성화 

 

NVIC를 먼저 켜놓고, 핸들러가 등록되지 않은 상태에서 인터럽트가 터지면 벡터 테이블의 그 슬롯에는 쓰레기 값만 등러가고 코어는 어딘지 모르는 곳으로 점프하게 됨. 

 

특정 구간에 인터럽트가 절대 끼어들지 않아야 하는 경우 PRIMASK로 모두 막아서 원자성을 유지할 수 있다.

중요한 인터럽트는 받고 나머지는 무시하고 싶으면 BASEPRI로 일부만 막는 것도 가능하다.

 

 

// Polling
while (1) {
    if (peripheral_A_ready()) process_A();
    if (peripheral_B_ready()) process_B();
    if (peripheral_C_ready()) process_C();
}

// Interrupt
int main(void) {
    init_all();
    while (1) {
        __WFI();   // Wait For Interrupt — sleep 모드 진입
    }
}

void USART_IRQHandler(void) { /* 깨어나서 처리 */ }
void Timer_IRQHandler(void) { /* 깨어나서 처리 */ }

 

 

 

Polling과 Interrupt 방식을 비교해보면, 운영체제의 Busy Wait과 웹의 SSE 느낌이 강하다.

실전에서는 두 방식을 섞어서 사용함. 

 

 

volatile bool flag_A = false;

void Peripheral_A_IRQHandler(void) {  // 1단계: ISR
    flag_A = true;                    // 플래그만 세팅 (몇 cycle)
    clear_irq_flag();                 // 인터럽트 원인 제거
}

int main(void) {
    while (1) {
        if (flag_A) {                 // 2단계: 메인 루프에서 천천히
            flag_A = false;
            process_A_heavy();        // 무거운 작업 (수ms 걸려도 OK)
        }
        __WFI();
    }
}

 

 

ISR은 플래그만 세팅하고, 무거운 처리는 메인 루프에서 수행한다.

다른 인터럽트가 끼어들 수 있지만 우선순위가 다른 인터럽트들끼리의 처리시간은 비슷하다.

 

volatile 을 빼먹으면 컴파일러가 main 루프 안에서 flag_A 안바뀐다고 판단해 레지스터에 캐싱할수도 있으니 주의

 

 

반응형
저작자표시 (새창열림)

'Computer Science > Embedded Software' 카테고리의 다른 글

[Embedded] ARM Assembly  (0) 2026.05.10
[Embedded] General Purpose Input Output  (0) 2026.04.30
[Embedded] Cortex-M4 Processor  (0) 2026.03.18

댓글

이 글 공유하기

  • 구독하기

    구독하기

  • 카카오톡

    카카오톡

  • 라인

    라인

  • 트위터

    트위터

  • Facebook

    Facebook

  • 카카오스토리

    카카오스토리

  • 밴드

    밴드

  • 네이버 블로그

    네이버 블로그

  • Pocket

    Pocket

  • Evernote

    Evernote

다른 글

  • [Embedded] ARM Assembly

    [Embedded] ARM Assembly

    2026.05.10
  • [Embedded] General Purpose Input Output

    [Embedded] General Purpose Input Output

    2026.04.30
  • [Embedded] Cortex-M4 Processor

    [Embedded] Cortex-M4 Processor

    2026.03.18
다른 글 더 둘러보기

정보

천천히 꾸준히 조용히 블로그의 첫 페이지로 이동

천천히 꾸준히 조용히

  • 천천히 꾸준히 조용히의 첫 페이지로 이동

검색

방문자

  • 전체 방문자
  • 오늘
  • 어제

카테고리

  • 분류 전체보기 (695) N
    • Algorithm (205)
      • Data Structure (5)
      • Theory && Tip (33)
      • Baekjoon (166)
      • ALGOSPOT (1)
    • Spring (123)
      • Spring (28)
      • Spring Web MVC (20)
      • Spring Database (14)
      • Spring Boot (6)
      • Spring 3.1 (11)
      • Spring Batch (6)
      • Spring Security (16)
      • JPA (12)
      • Spring Data JPA (5)
      • QueryDSL (4)
      • eGovFramework (1)
    • Programming Language (74)
      • C (25)
      • C++ (12)
      • Java (19)
      • JavaScript (15)
      • Python (1)
      • PHP (2)
    • Computer Science (154) N
      • Machine Learning (38)
      • Operating System (18)
      • Computer Network (28)
      • System Programming (22)
      • Universial Programming Lang.. (8)
      • Data Science (8) N
      • Embedded Software (4) N
      • Computer Architecture (4)
      • Compiler Design (11)
      • Computer Security (13)
      • BlockChain (0)
    • Database (21)
      • Database (7)
      • MySQL (3)
      • Oracle (3)
      • Redis (5)
      • Elasticsearch (3)
    • DevOps (20)
      • Docker && Kubernetes (8)
      • Jenkins (4)
      • Amazon Web Service (8)
    • Mobile (28)
      • Android (21)
      • Flutter (7)
    • 💡 솔루션 (17)
    • 👥 모각코 (12)
    • 💬 기록 (9)
    • 📚 공부 (7)
    • -------------- (25)

최근 글

나의 외부 링크

메뉴

  • 홈
반응형

정보

i3months의 천천히 꾸준히 조용히

천천히 꾸준히 조용히

i3months

블로그 구독하기

  • 구독하기
  • RSS 피드

티스토리

  • 티스토리 홈
  • 이 블로그 관리하기
  • 글쓰기
Powered by Tistory / AXZ. Copyright © i3months.

티스토리툴바