| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |
- 자바
- JavaScript
- 누적합
- kotlin
- 구현
- 코틀린
- 다이나믹프로그래밍
- dp
- c++
- Stack
- 다이나믹 프로그래밍
- 그리디
- 임베디드
- 컴퓨터 비전
- lv2
- dfs
- 2018 KAKAO BLIND RECRUITMENT
- level3
- 이분탐색
- 백준
- cpp
- 프로그래머스
- 동적계획법
- C
- 컴퓨터비전
- BFS
- 우선순위큐
- 통신 인터페이스
- level2
- java
- Today
- Total
코드를 느껴바라
[임베디드] TC275 STM 신호등 : 외부 인터럽트(ERU)와 Flag 기반 상태 머신 구현 본문
[임베디드] TC275 STM 신호등 : 외부 인터럽트(ERU)와 Flag 기반 상태 머신 구현
feelTheCode 2026. 2. 23. 14:43개요
이번 실습에서는 Infineon AURIX TC275의 STM(System Timer Module)과 ERU(External Request Unit)를 결합하여, 단순한 자동 신호등을 넘어 사용자가 제어 가능한 신호등 시스템을 구현해 보았습니다.
1. 핵심 설계 전략: "Flag & Main Loop" 구조
이 코드의 가장 큰 특징은 인터럽트 핸들러에서는 '깃발(Flag)'만 들고, 실제 무거운 로직은 메인 루프에서 처리한다는 점입니다. 이는 시스템 안정성을 높이는 매우 중요한 설계 방식입니다.
그런데 만약 무거운 로직을 핸들러에서 처리하다 보면 어떤 문제가 생길까요?
+)
- 인터럽트 지연(Latency)과 응답성 저하
임베디드 시스템은 보통 여러 개의 인터럽트를 관리합니다. 만약 낮은 우선순위의 핸들러가 CPU를 붙잡고 긴 로직을 수행하면, 더 중요한(높은 우선순위) 인터럽트가 제때 처리되지 못하고 밀리게 됩니다.
문제: 긴급한 센서 데이터 수집이나 통신 패킷 수신을 놓칠 수 있습니다.
- 데드락(Deadlock) 및 시스템 중단 가능성
특정 인터럽트 핸들러가 실행 중일 때는 같은 우선순위나 낮은 우선순위의 인터럽트가 차단되는 경우가 많습니다.
문제: 무거운 로직 중에 다른 인터럽트의 결과(예: 특정 플래그가 바뀌길 기다림)를 기다리는 코드가 있다면, 시스템은 영원히 대기 상태에 빠지는 데드락 현상이 발생할 수 있습니다.
- 지터(Jitter) 발생
주기적인 작업(예: 0.5초마다 LED 토글)을 수행할 때, 핸들러가 무거우면 실행 시간이 일정하지 않게 됩니다.
문제: 타이머는 정확히 0.5초마다 신호를 주지만, 핸들러가 끝나는 시간이 매번 달라지면 전체적인 제어 주기(Sampling Rate)가 흔들리는 지터 현상이 발생하여 정밀한 제어가 불가능해집니다.
- 메인 루프(Main Loop)의 기아 상태(Starvation)
메인 함수(while(1))는 시스템의 전반적인 스케줄링을 담당합니다. 핸들러가 CPU 점유율을 너무 많이 가져가면 메인 루프가 실행될 시간이 부족해집니다.
문제: 버튼 입력 감지, 상태 모니터링, 로그 출력 등 메인에서 돌아가야 할 작업들이 버벅거리거나 멈춘 것처럼 보일 수 있습니다.
그렇기 때문에 메인 문에서 작성을 해주었습니다.
2. 주요 함수 및 코드 분석
① STM_Int0Handler() : 0.5초 주기의 알람 설정
이 함수는 타이머가 설정된 시간에 도달할 때마다 실행됩니다.
void STM_Int0Handler(void){
IfxStm_clearCompareFlag(g_Stm.stmSfr, g_Stm.stmConfig.comparator);
IfxStm_increaseCompare(g_Stm.stmSfr, g_Stm.stmConfig.comparator, 50000000);
timer_tick = 1; // "0.5초 지났다!" 깃발(Flag)만 들고 끝!
IfxCpu_enableInterrupts();
}
- 분석: 직접 로직을 수행하지 않고
timer_tick변수만 1로 바꿉니다. 이는 메인 루프에게 "이제 일을 할 시간이다"라고 알려주는 신호탄 역할을 합니다.
② ISR0() : 외부 버튼을 통한 일시정지 제어
ERU 외부 인터럽트를 통해 버튼(SW2) 입력 시 즉각적으로 동작 상태를 변경합니다.
void ISR0 (void) {
is_paused = !is_paused; // 일시정지 상태 토글 (0: 실행, 1: 정지)
}
- 분석:
is_paused변수 하나만 제어하여 메인 루프의 카운트가 멈추게 만듭니다. 하드웨어 버튼이 소프트웨어의 흐름을 직접 제어하는 핵심 연결 고리입니다.
③ initERU() : 외부 인터럽트 설정 (Register Level)
iLLD 라이브러리를 쓰지 않고 레지스터에 직접 접근하여 하드웨어를 설정하는 부분입니다.
void initERU(void) {
// ERU 설정 중 하강 엣지(Falling Edge) 감지 및 인터럽트 허용 설정 발췌
SCU_EICR1.U |= (1 << FEN0_IDX); // Falling Edge Detection
SCU_EICR1.U |= 1 << EIEN0_IDX; // External Interrupt Enable
// SRC(Service Request Control) 설정 : 우선순위 부여
SRC_SCU_SCU_ERU0.U |= 0x10; // Priority 0x10
SRC_SCU_SCU_ERU0.U |= 1 << SRE_IDX;
}
- 분석: 비트 연산을 통해
SCU_EICR,SRC_SCU레지스터를 직접 건드립니다. 이를 통해 AURIX의 인터럽트 시스템이 버튼의 물리적 신호를 어떻게 받아들이는지 알 수 있습니다.
3. 상태별 LED 제어 로직 (Main Loop)
메인 루프에서는 0.5초마다 갱신되는 cnt와 flag를 바탕으로 LED를 제어합니다.
flag = (cnt/10) % 3; // 0, 1, 2 상태 결정 (각 5초씩)
if(flag == 0){ // State 0: Red 전용 점등
IfxPort_setPinLow(IfxPort_P10_2.port, IfxPort_P10_2.pinIndex);
IfxPort_setPinHigh(IfxPort_P10_1.port, IfxPort_P10_1.pinIndex);
}
else if(flag == 1){ // State 1: Blue 전용 점등
IfxPort_setPinLow(IfxPort_P10_1.port, IfxPort_P10_1.pinIndex);
IfxPort_setPinHigh(IfxPort_P10_2.port, IfxPort_P10_2.pinIndex);
}
else{ // State 2: Blue 점멸 (Blink)
IfxPort_setPinLow(IfxPort_P10_1.port, IfxPort_P10_1.pinIndex);
if(cnt % 2 == 0){ // 0.5초 주기로 ON/OFF 반복
IfxPort_setPinHigh(IfxPort_P10_2.port, IfxPort_P10_2.pinIndex);
} else {
IfxPort_setPinLow(IfxPort_P10_2.port, IfxPort_P10_2.pinIndex);
}
}
- 동작 원리:
flag가 0, 1일 때는 일반적인 신호등처럼 동작합니다.flag가 2일 때는cnt % 2 == 0조건을 활용해 0.5초 간격으로 파란색 LED가 깜빡이도록 설계했습니다.- 만약 버튼을 눌러
is_paused가 1이 되면,cnt가 증가하지 않아 현재의 LED 상태(켜짐, 꺼짐, 혹은 깜빡이는 중의 한 지점)에서 그대로 멈추게 됩니다.

4. 전체 소스 코드 (Cpu0_Main.c)
/**********************************************************************************************************************
* \file Cpu0_Main.c
* \copyright Copyright (C) Infineon Technologies AG 2019
*
* Use of this file is subject to the terms of use agreed between (i) you or the company in which ordinary course of
* business you are acting and (ii) Infineon Technologies AG or its licensees. If and as long as no such terms of use
* are agreed, use of this file is subject to following:
*
* Boost Software License - Version 1.0 - August 17th, 2003
*
* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and
* accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute,
* and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the
* Software is furnished to do so, all subject to the following:
*
* The copyright notices in the Software and this entire statement, including the above license grant, this restriction
* and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all
* derivative works of the Software, unless such copies or derivative works are solely in the form of
* machine-executable object code generated by a source language processor.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*********************************************************************************************************************/
#include "Ifx_Types.h"
#include "IfxCpu.h"
#include "IfxScuWdt.h"
#include "IfxPort.h"
#include "IfxPort_PinMap.h"
#include "IfxStm.h"
#include "IfxCpu_Irq.h"
#define PCn_2_IDX 19
#define P2_IDX 2
#define PCn_1_IDX 11
#define P1_IDX 1
#define PCn_0_IDX 3
#define BLUE_PIN 2
#define RED_PIN 1
#define EXIS0_IDX 4
#define FEN0_IDX 8
#define REN0_IDX 9
#define EIEN0_IDX 11
#define INP0_IDX 12
#define IGP0_IDX 14
#define EXIS1_IDX 20
#define FEN1_IDX 24
#define REN1_IDX 25
#define EIEN1_IDX 27
#define INP1_IDX 28
#define IGP1_IDX 30
#define SRE_IDX 10
#define TOS_IDX 11
typedef struct{
Ifx_STM *stmSfr;
IfxStm_CompareConfig stmConfig;
volatile uint8 LedBlink;
volatile uint32 counter;
}App_Stm;
App_Stm g_Stm;
void initERU(void);
void initGPIO(void);
void IfxStmDemo_init(void);
IFX_INTERRUPT(ISR0, 0, 0x10);
IFX_INTERRUPT(ISR1, 0, 0x20);
IFX_INTERRUPT(STM_Int0Handler, 0, 100);
volatile int timer_tick = 0; // 타이머 깃발 (0: 대기, 1: 0.5초 경과)
volatile int is_paused = 0; // 일시정지 상태 (0: 실행, 1: 정지)
void ISR0 (void) {
is_paused = !is_paused; // 일시정지 상태만 뒤집고 끝!
}
void ISR1 (void) {
static int red_state = 0;
if (red_state){
P10_OMR.U = (1u << (RED_PIN + 16u)); // RESET
}
else{
P10_OMR.U = (1u << RED_PIN); // SET
}
red_state = 1 - red_state;
}
void STM_Int0Handler(void){
IfxStm_clearCompareFlag(g_Stm.stmSfr, g_Stm.stmConfig.comparator);
IfxStm_increaseCompare(g_Stm.stmSfr, g_Stm.stmConfig.comparator, 50000000);
timer_tick = 1; // "0.5초 지났다!" 깃발만 들고 끝!
IfxCpu_enableInterrupts();
}
IfxCpu_syncEvent cpuSyncEvent = 0;
void core0_main(void)
{
IfxCpu_enableInterrupts();
IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
IfxCpu_emitEvent(&cpuSyncEvent);
IfxCpu_waitEvent(&cpuSyncEvent, 1);
initGPIO();
initERU();
IfxStmDemo_init();
// 메인 함수용 지역 변수
static int flag = 0;
static int cnt = 0;
while(1)
{
// 깃발이 올라왔는지 계속 감시
if (timer_tick == 1) {
timer_tick = 0; // 확인했으니 깃발을 다시 내림
// 여기서부터 기존에 짜셨던 로직 그대로 실행
if (is_paused == 0) {
cnt++;
if (cnt >= 30) cnt = 0; // 오버플로우 방지
}
flag = (cnt/10) % 3;
if(flag == 0){
IfxPort_setPinLow(IfxPort_P10_2.port, IfxPort_P10_2.pinIndex);
IfxPort_setPinHigh(IfxPort_P10_1.port, IfxPort_P10_1.pinIndex);
}
else if(flag == 1){
IfxPort_setPinLow(IfxPort_P10_1.port, IfxPort_P10_1.pinIndex);
IfxPort_setPinHigh(IfxPort_P10_2.port, IfxPort_P10_2.pinIndex);
}
else{
IfxPort_setPinLow(IfxPort_P10_1.port, IfxPort_P10_1.pinIndex);
if(cnt%2==0){
IfxPort_setPinHigh(IfxPort_P10_2.port, IfxPort_P10_2.pinIndex);
}
else{
IfxPort_setPinLow(IfxPort_P10_2.port, IfxPort_P10_2.pinIndex);
}
}
}
}
}
void initGPIO(void) {
// PC2.1 button
P02_IOCR0.U &= ~(0x1F << PCn_1_IDX);
P02_IOCR0.U |= 0x02 << PCn_1_IDX;
P02_IOCR0.U &= ~(0x1F << PCn_0_IDX);
P02_IOCR0.U |= 0x02 << PCn_0_IDX;
// PC10.2 blue led
P10_IOCR0.U &= ~(0x1F << PCn_2_IDX);
P10_IOCR0.U |= 0x10 << PCn_2_IDX;
// PC10.1 red led
P10_IOCR0.U &= ~(0x1F << PCn_1_IDX);
P10_IOCR0.U |= 0x10 << PCn_1_IDX;
}
void initERU(void) {
// ERU
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;
// SW1
SCU_EICR1.U &= ~(0x7 << EXIS1_IDX);
SCU_EICR1.U |= 0x02 << EXIS1_IDX;
SCU_EICR1.U |= (1 << FEN1_IDX);
SCU_EICR1.U |= 1 << EIEN1_IDX;
SCU_EICR1.U &= ~(0x7 << INP1_IDX);
SCU_EICR1.U |= 0x1 << INP1_IDX;
SCU_IGCR0.U &= ~(0x3 << IGP1_IDX);
SCU_IGCR0.U |= 0x1 << IGP1_IDX;
// SRC (Service Request Control) Setting
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);
//SW1
SRC_SCU_SCU_ERU1.U &= ~0xFF;
SRC_SCU_SCU_ERU1.U |= 0x20;
SRC_SCU_SCU_ERU1.U |= 1 << SRE_IDX;
SRC_SCU_SCU_ERU1.U &= ~(0x3 << TOS_IDX);
}
void IfxStmDemo_init(void){
boolean interruptState = IfxCpu_disableInterrupts();
g_Stm.stmSfr = &MODULE_STM0;
IfxStm_initCompareConfig(&g_Stm.stmConfig);
g_Stm.stmConfig.triggerPriority = 100u;
g_Stm.stmConfig.typeOfService = IfxSrc_Tos_cpu0;
g_Stm.stmConfig.ticks = 100000000;
IfxStm_initCompare(g_Stm.stmSfr, &g_Stm.stmConfig);
IfxCpu_restoreInterrupts(interruptState);
}'개발 > 임베디드(Embedded)' 카테고리의 다른 글
| [임베디드] TC275로 스톱워치 구현 : 74HC595 제어와 Flag 기반 인터럽트 설계 (2) | 2026.02.23 |
|---|---|
| [임베디드] 리눅스 디바이스 드라이버로 GPIO LED 제어하기 (Linux Kernel) (0) | 2026.02.13 |
| [임베디드] 라즈베리파이로 7-Segment 제어해서 스톱워치 만들기 (2) | 2026.02.11 |
| [네트워크] I2C 통신 인터페이스 개요 (2) | 2026.02.06 |
| [네트워크] U(S)ART 통신 인터페이스 (0) | 2026.02.06 |
