[현대오토에버SW스쿨4기] 임베디드 MCU 프로그래밍(2)

chaehun·2025년 2월 16일

인터럽트

  • Lecture Overview
    TC275 보드에서 외부 Interrupt를 사용하여 LED/SW를 제어하자
  • 인터럽트를 사용하기 위해 참조해야할 문서
    • Datasheet vs User Manual (Reference Manual)
      • Datasheet: HW적인 정의
        전압이 얼마 필요하고, 회로가 어떻게 구성되어있다. 뭐 이런거
      • UM, RM: 기능(SW)적인 정의
        이걸 많이 참고한다. 범용적으로 '데이터 시트 봐라.' 이렇게 많이 말하기는 함.
  • 폴링 방식과 인터럽트 방식
    • 폴링 방식: 우선순위가 높은 코드를 먼저 처리하는 방식
      • 인터럽트: 정상적인 실행 순서를 바꿀 만큼 비정상적이고 중요한 사건
      • 우선 처리를 위한 사건을 정의하고, 우선 순위에 따라 실행 순서를 변경
      • 하드웨어 vs 소프트웨어 인터럽트
        • HW 인터럽트: 하드웨어에 의해 발생, uC의 인터럽트
          ex) 타이머 모듈 -> 타이머 모듈에서 시그널을 준다.
          이 칩은 운영체제가 없는 상태라서 HW 인터럽트만 발생
        • SW 인터럽트: 운영체제의 커널을 통해 발생
  • 폴링 방식
    하나의 동작에 의해 다른 하나의 동작이 지연될 수 있음
  • 인터럽트 처리 방식
    • 인터럽트 처리 루틴 (Interrupt Service Routine, ISR)
      • 인터럽트 발생 시 HW 도움에 의해 ISR이 자동으로 호출
  • ISR 호출 및 실행
    • ISR 호출을 위한 3가지 조건
      • 전역적인 인터럽트 활성화 비트 세트
      • 인터럽트 별 인터럽트 활성화 비트 세트
      • 인터럽트 발생 조건 충족
        현재 falling edge냐 rising edge냐 이런거
    • 인터럽트 처리 순서
      • 2번 인터럽트 발생
        -> Program Counter(PC)를 인터럽트 벡터 테이블의 2번 위치로 이동
        -> 해당 ISR의 위치로 이동
        -> ISR 실행 (인터럽트 처리)
        -> 인터럽트 발생 전 위치로 복귀
  • 인터럽트 사용 시 주의사항
    • 중첩된 인터럽트
      • ISR 내에서 인터럽트 처리는 권장하지 않음
      • ISR은 가능한 짧게 작성하는 것이 바람직함
        최대한 빨리 MCU가 처리하고 돌아올 수 있게 해줘야함.
    • 인터럽트 우선순위
      • 큰 번호의 인터럽트가 우선순위가 높음 (시스템에 따라 다름)
        만약 한번에 여러개의 인터럽트가 발생했을 때 유리하다. 거의 그런일은 없지만
    • 최적화 방지
      • ISR은 일반 함수와 달리 코드 내에서 명시적으로 호출하지 않음
      • 따라서 ISR 내에서 값이 바뀌는 변수는 최적화에서 제외하기 위해 volatile 키워드를 사용함
  • TC275 외부 인터럽트 사용
    • External Request Unit (ERU)
      • 외부 스위치의 입력은 "External Request"에 해당
      • External Request Unit(ERU) 사용
    • 요소를 하나씩 살펴봅시다.
    • External Request Selection (ERS) Unit -> per 8 input channel
      • Input channel에 연결된 4개의 pin 중 하나를 선택
      • P02.1 스위치를 사용하므로 ERS2 사용함을 확인

        그림이 없는 것들은 내부 인터럽트
        파란색 LED가 꺼질 때 RGB가 토글되도록 할 수도 있다.
        인터럽트의 입력이 반드시 입력모드일 필요는 없다. 출력이 입력으로 들어올 수도 있다.
    • Event Trigger Logic (ETL) Unit
      • EICR에 작성된 내용을 바탕으로 rising/falling edge를 선택하여 Trigger 이벤트를 발생시킴
    • Control 레지스터
      • EICR 레지스터

        일단 EXIS와 FEN, REN 만 기억 -> falling edge enable, rising edge enable
    • Output Gating Unit (OGU) -> per 8 output channel
      • Input Channel 에서 생성된 Trigger Pulser를 조합하여 External Request output 생성
    • Control 레지스터
      • IGCR 레지스터
    • Interrupt Router (IR)
      • ERU의 출력은 IR의 입력으로 연결됨
  • 인터럽트를 위한 레지스터 설정
    • EICR 설정 -> EICR0이 ERS0, ERS1과 ETL0, ETL1을 control한다.
      • ERS2를 사용하기 때문에 EICR1을 사용
      • 두번째 input을 사용하므로 EXIS0은 0b001로 세팅

        아래 라인이 짝수번째 ERS control, 윗 라인이 홀수번째 ERS control
      • 사용하는 회로가 눌렸을 때 1->0으로 변화하므로, falling edge로 설정
      • Interrupt Enable
        • EIEN(11th bit)을 1로 설정
      • Falling edge enable
        • FEN(8th bit)을 1로 설정
      • OGU0로 전달하기 위해
        • INP(12~14th bit)를 0b000으로 설정
    • IGCR 설정
      IOUT로 전달하기 위해
      IGP(14~15th bit)를 0b01로 설정


    • SCUERU0 설정
      • SRPN은 0x10으로, 나머지는 CPU0를 사용하도록 설정
    • 인터럽트 핸들러 설정
      • Interrupt가 발생하면 BLUE LED를 켜는 동작 설정
      • 해당 prio에 사용할 isr(interrupt service routine)을 vector table에 등록
  • 최종 코드
#include "Ifx_Types.h"
#include "IfxCpu.h"
#include "IfxScuWdt.h"

#define PCn_2_IDX 19
#define P2_IDX 2
#define PCn_1_IDX 11
#define P1_IDX 1

// ERU related
#define EXIS0_IDX 4
#define FEN0_IDX 8
#define EIEN0_IDX 11
#define INP0_IDX 12
#define IGP0_IDX 14

// SRC related
#define SRE_IDX 10
#define TOS_IDX 11

IfxCpu_syncEvent g_cpuSyncEvent = 0;

void initERU(void);
void initGPIO(void);

IFX_INTERRUPT(ISR0, 0, 0x10);
void ISR0(void){
    P10_OUT.U = 0x1 << P2_IDX;
}

int core0_main(void)
{
    IfxCpu_enableInterrupts();
    
    /* !!WATCHDOG0 AND SAFETY WATCHDOG ARE DISABLED HERE!!
     * Enable the watchdogs and service them periodically if it is required
     */
    IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
    IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
    
    /* Wait for CPU sync event */
    IfxCpu_emitEvent(&g_cpuSyncEvent);
    IfxCpu_waitEvent(&g_cpuSyncEvent, 1);

    
    initGPIO();
    initERU();
        
    while(1)
    {
    }
    return (1);
}

void initGPIO(void){
    P02_IOCR0.U &= ~(0x1F << PCn_1_IDX);
    P02_IOCR0.U |= 0x02 << PCn_1_IDX;

    P10_IOCR0.U &= ~(0x1F << PCn_1_IDX);
    P10_IOCR0.U |= 0x10 << PCn_1_IDX;
}

void initERU(void){
    // ERU(External Request Unit) setting
    SCU_EICR1.U &= ~(0x7 << EXIS0_IDX);
    SCU_EICR1.U |= 0x1 << EXIS0_IDX;
    
    SCU_EICR1.U |= 1 << FEN0_IDX;
    SCU_EICR1.U |= 1 << EIEN0_IDX;
    
    SCU_EICR1.U &= ~(0x7 << INP0_IDX);
    
    SCU_IGCR0.U &= ~(0x3 << IGP0_IDX);
    SCU_IGCR0.U |= 0x1 << IGP0_IDX;
    
    // SRC(Service Request Control) setting -> ERU에서 들어온 것을 처리
    SRC_SCU_SCU_ERU0.U &= ~0xFF;
    SRC_SCU_SCU_ERU0.U |= 0x10;
    
    SRC_SCU_SCU_ERU0.U |= 1 << SRE_IDX;
    SRC_SCU_SCU_ERU0.U &= ~(0x3 << TOS_IDX);
}
   
  • 실습 2) LED Blinking 멈추기
    • 문제 구현 방법론 -> Finite State Machine 기법 -> 교수님이 매우 중시
      1) State Transition
      조건 검사를 해서 ST 일어나게
      2) State Action
      state에 따른 동작
      switch 문으로 보통 작성한다.
      switch 문 쓸 때 default는 꼭 넣어줘라.
  • 실습 2 코드
#include "Ifx_Types.h"
#include "IfxCpu.h"
#include "IfxScuWdt.h"

#define PCn_2_IDX 19
#define P2_IDX 2
#define PCn_1_IDX 11
#define P1_IDX 1

// ERU related
#define EXIS0_IDX 4
#define EXIS1_IDX 20
#define FEN0_IDX 8
#define FEN1_IDX 24
#define EIEN0_IDX 11
#define EIEN1_IDX 27
#define INP0_IDX 12
#define INP1_IDX 28
#define IGP0_IDX 14
#define IGP1_IDX 30

// SRC related
#define SRE_IDX 10
#define TOS_IDX 11

IfxCpu_syncEvent g_cpuSyncEvent = 0;

volatile unsigned toggle = 1;
enum state{
    BLINK,
    STOP
}state;

void initERU(void);

void initGPIO(void);

IFX_INTERRUPT(ISR0, 0, 0x10);

void ISR0(void){
    switch(state){
        case BLINK:
            state = STOP;
            break;
        case STOP:
            state = BLINK;
            break;
        default:
            break;
    }
}


int core0_main(void)
{
    IfxCpu_enableInterrupts();
    
    /* !!WATCHDOG0 AND SAFETY WATCHDOG ARE DISABLED HERE!!
     * Enable the watchdogs and service them periodically if it is required
     */
    IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
    IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
    
    /* Wait for CPU sync event */
    IfxCpu_emitEvent(&g_cpuSyncEvent);
    IfxCpu_waitEvent(&g_cpuSyncEvent, 1);
        
    initGPIO();
    initERU();
    P10_OMR.U = 0x20004;
    P10_OMR.U = 0x40002;

    while(1)
    {
        switch(state){
            case BLINK:
                P10_OMR.U = 0x60006;
                for(int i = 0; i < 10000000; i++) ;
                break;
            case STOP:
            default:
                break;
        }
    }
    return (1);
}

void initGPIO(){
    P02_IOCR0.U &= ~(0x1F << PCn_1_IDX);
    P02_IOCR0.U |= 0x02 << PCn_1_IDX;

    P10_IOCR0.U &= ~(0x1F << PCn_1_IDX);
    P10_IOCR0.U |= 0x10 << PCn_1_IDX;
    P10_IOCR0.U &= ~(0x1F << PCn_2_IDX);
    P10_IOCR0.U |= 0x10 << PCn_2_IDX;
}

void initERU(void){
    //set EICR
    SCU_EICR1.U &= ~(0x7 << EXIS0_IDX);
    SCU_EICR1.U |= 0x1 << EXIS0_IDX;

    SCU_EICR1.U |= 1 << FEN0_IDX;
    SCU_EICR1.U |= 1 << EIEN0_IDX;

    SCU_EICR1.U &= ~(0x7 << INP0_IDX);

    SCU_IGCR0.U &= ~(0X3 << IGP0_IDX);
    SCU_IGCR0.U |= 0x1 << IGP0_IDX;

    SRC_SCU_SCU_ERU0.U &= ~0xFF;
    SRC_SCU_SCU_ERU0.U |= 0x10;

    SRC_SCU_SCU_ERU0.U |= 1 << SRE_IDX;
    SRC_SCU_SCU_ERU0.U &= ~(0x3 << TOS_IDX);
}

0개의 댓글