Twinkle twinkle little star
시작하는 이야기
"반짝 반짝 작은 별~~~" 우리가 즐겨 부르던 동요입니다.
마이크로컨트롤러에 프로그래밍 할 때도 첫 번째로 만들어 봐야 하는 프로그램으로 일정 시간주기를 정해주고 주기마다 LED를 반짝이게 하는 것입니다. 단 불빛만 반짝인다고 만족해서는 안 됩니다. 이 프로그램으로 우리는 다음의 사항들을 확실하게 할 수 있게 됩니다.
- 디지털 출력을 내보낼 수 있고,
- 시스템타이머를 사용할 줄 알게 되고,
- 인터럽트를 처리할 수 있게 된다.
이 프로그램은 앞으로 진행할 다른 모든 프로그램의 기초가 됩니다. 심지어 실제로 최종 프로그램을 동작시킬 때에도, background로 실행되는 이 루틴은 살았는지 죽었는지 알 수 없는 마이크로컨트롤러의 심장박동으로 사용될 것입니다. 깜깜하기만 한 마이크로컨트롤러 보드에 작은 불빛을 밝혀 봅시다.
Objectives
- 시스템타이머를 이해하고 활용할 수 있다.
- 인터럽트를 활용할 수 있다.
References
- TC23x TC27x Family User's Manual v1.1 - 17 STM
- iLLD_1_0_1_8_0_TC2xx_Drivers_And_Demos_Release - Modules/iLLD/STM
[Example Code]
- MyIlldModule_TC23A - Stm
- MyIlldModule_TC27D - Stm
Example Description
- 시스템 타이머를 사용해서 1초 마다 인터럽트를 발생시킨다.
- 인터럽트를 이용해서 2초 주기로 LED_TICK을 점멸시킨다.
Backgounds
-
타이머란?
- 세상에는 대개 주기적으로 처리하는 일이 있으며 프로그램 역시 마찬가지.
- 그런 주기적 시간처리를 위한 clock 모듈을 타이머라고 한다.
-
시스템 타이머
- 프로그램 내부에는 여러가지 타이머가 있을 수 있고,
- 전체 시스템의 시간을 동기화 해야할 때가 있을 것이며,
- 그 때 시스템의 기준이 될 타이머가 필요하다.
- 그것이 시스템 타이머이며 Tick 이라고 불리는 매우 작은 단위의 시간이 누적된다.
AURIX - related
STM (System timer)
-
64-bit timer of 32-bit microcontroller
- TC2XX이 한 번의 명령어로 다룰 수 있는 데이터량은 32 bit 이며,
-
하나의 레지스터에는 최대 2^32의 Tick 을 누적시킬 수 있다.
-
STM은 복수의 32-bit 레지스터를 사용하여 Tick 의 누적허용량을 늘리면서,
- 더욱 유연하게 동작할 수 있는 시스템 타이머이다.
- Tick 은 일정한 주기(f_stm)에 따라 자동으로 값이 더해져 증가한다.(Free-running)
- 이 때 여러 레지스터에서 하나의 Tick 값을 읽어들이기 위해 capture라는 기능이 있다.
-
Timer 값의 정확한 시간 알기 (Timer register와 capture register)
- 64-bit의 Tick 값을 동기화해 읽어 옴으로써 시스템의 정확한 동작 시간을 알 수 있다.
- STM의 내부에는 별개의 range를 가지는 32bit timer register(STM_TIMx)들이 있고,
- 동기화를 위한 capture register(STM_CAP)가 존재한다.
- 만약 사용자가 하위단의 시간정보를 요청한다면(TIM0~TIM5),
- 그 요청와 동시에 capture register에서 상위단(32~63bit,TIM6)의 시간을 capture한다.

- 정해진 시간에 동작하기 (Timer의 누적시간을 이용하는 Compare register)
- Timer를 이용하는 가장 큰 이유는 정확한 시간 혹은 주기로 정해진 동작을 수행하기 위해서이다. (ex 정해진 주기로 LED를 껐다 킴)
- 이를 위해서 timer를 인터럽트를 발생시키기 위한 trigger 역할로 이용 할 수 있다.
- STM에서는 이를 위한 compare register라는 것이 존재하며 구성하여 이용할 수 있다.
- 구성된 register는 tick이 쌓일 때마다 비교를 하여 일치할 시 flag를 발생시킨다.
- TC2XX은 두 개의 compare 레지스터를 제공하며 선택적으로 사용할 수 있다.

iLLD - related
Module Configuration
- 시스템 타이머에 누적되는 Tick을 이용하여 주기적으로 LED를 제어하고자 한다.
- 이를 위해 제어하고자 하는 LED와 연결된 IO port를 사용할수 있도록 해주고,
-
Compare register를 이용해 특정 주기마다 인터럽트를 발생시킬 것이다.
-
LED port 정의 및 초기화
-
SB_TC27D 와 AK_TC23A 가 사용하는 IO port 가 다르기 때문에 각각 정의해 준 후 LED 제어를 위해 port를 output으로 사용할 수 있도록 초기화 시켜준다
// in StmDemo.c
static void IfxBlinkLed_Init(void)
{
#if BOARD == APPLICATION_KIT_TC237
IfxPort_setPinMode(&MODULE_P13, 0, IfxPort_Mode_outputPushPullGeneral);
#elif BOARD == SHIELD_BUDDY
IfxPort_setPinMode(&MODULE_P10, 2, IfxPort_Mode_outputPushPullGeneral);
#endif
}
- STM 사용을 위한 변수 정의
- STM 관련 설정 값들을 관리할 수 있는 variable을 정의한다
- STM configuration 변수는 각 target iLLD에 정의되어 있다
- 아래 정의된 STM configuration 변수에 설정된 값들을 STM register에 iLLD interface를 통해 할당한다
// in StmDemo.h
typedef struct
{
Ifx_STM *stmSfr; // STM register 변수
IfxStm_CompareConfig stmConfig; // Register configuration 변수
volatile uint8 LedBlink; /**< \brief LED state variable */
volatile uint32 counter; /**< \brief interrupt counter */
} App_Stm;
// in BaseSw\iLLD\TC23A(TC27D)\Tricore\Stm\Std\IfxStm.h
typedef struct
{
IfxStm_Comparator comparator; // 사용할 compare register의 번호
IfxStm_ComparatorInterrupt comparatorInterrupt; // 인터럽트 flag를 어디로 내보낼지
IfxStm_ComparatorOffset compareOffset; // compare를 시작할 bit (MSTARTx)
IfxStm_ComparatorSize compareSize; // compare register의 사이즈 (MSIZEx)
uint32 ticks; // compare할 누적 tick 값
Ifx_Priority triggerPriority; // 인터럽트 우선순위
IfxSrc_Tos typeOfService; // 사용할 CPU 번호
} IfxStm_CompareConfig;
- Compare interrupt 초기화
- STM configuration 변수 값들을 결정해 주고
- STM을 통해서 발생하는 interrupt 관련 초기화를 해준다
- 이 중 ticks를 수정하여 인터럽트 flag를 발생시킬 주기를 결정한다. (아래 BSP에 정의된 변수 사용)
// in StmDemo.c
void IfxStmDemo_init(void)
{
printf("IfxStmDemo_init() called\n");
/* disable interrupts */
boolean interruptState = IfxCpu_disableInterrupts();
g_Stm.LedBlink = 0;
g_Stm.counter = 0;
initTime();
// Stm register 변수에 module0 register 할당
g_Stm.stmSfr = &MODULE_STM0;
// Register configuration 변수 설정
IfxStm_initCompareConfig(&g_Stm.stmConfig);
g_Stm.stmConfig.triggerPriority = ISR_PRIORITY_STM_INT0;
g_Stm.stmConfig.typeOfService = IfxSrc_Tos_cpu0;
#ifdef SIMULATION
g_SrcSwInt.stmConfig.ticks = 1000;
#else
g_Stm.stmConfig.ticks = TimeConst_1s;
#endif
IfxStm_initCompare(g_Stm.stmSfr, &g_Stm.stmConfig);
IfxBlinkLed_Init();
/* enable interrupts again */
IfxCpu_restoreInterrupts(interruptState);
}
Module Behavior
- Timer register에 누적된 Tick 값이 compare register의 값과 같아진다면 인터럽트가 한 번 발생한다.
- 인터럽트가 발생하면 정의된 인터럽트 handler function을 구동한다.
// in StmDemo.c
IFX_INTERRUPT(STM_Int0Handler, 0, ISR_PRIORITY_STM_INT0); // STM_Int0Handler 실행
- 인터럽트를 일정 주기로 실행시키기 위해서는 compare를 반복시기 위해서 인터럽트 발생시마다 compare register의 값을 주기만큼 증가
- Interrupt flag를 reset하고,
IfxStm_clearCompareFlag - Compare register의 값을 주기만큼 더해주며,
IfxStm_increaseCompare - Interrupt를 다시 활성화한다.
IfxCpu_enableInterrupts - 그 후 LED blink function 실행하고
IfxBlinkLed_Task, - 각각 다른주기마다 task를 실행 하는 Static Cycle Scheduler을 위해
g_Stm.counter를 관리한다.
// in StmDemo.c
// Interrupt handler function
void STM_Int0Handler(void)
{
IfxStm_clearCompareFlag(g_Stm.stmSfr, g_Stm.stmConfig.comparator);
#ifdef SIMULATION
IfxStm_increaseCompare(g_Stm.stmSfr, g_Stm.stmConfig.comparator, 1000);
#else
IfxStm_increaseCompare(g_Stm.stmSfr, g_Stm.stmConfig.comparator, TimeConst_1s);
#endif
IfxCpu_enableInterrupts();
IfxBlinkLed_Task();
}
// LED blink function
static void IfxBlinkLed_Task(void)
{
g_Stm.LedBlink ^= 1;
#if BOARD == APPLICATION_KIT_TC237
setOutputPin(&MODULE_P13, 0, g_Stm.LedBlink);
#elif BOARD == SHIELD_BUDDY
setOutputPin(&MODULE_P10, 2, g_Stm.LedBlink);
#endif
g_Stm.counter++;
}
BSP (Board support package)
-
실제 시간단위를 사용하는 사용자 친화적 타이머 보조 모듈.
-
initTime()함수를 통해 시스템 타이머의 tick과 사람의 시간단위를 매칭시킨 상수를 생성한다.
// in Bsp.c
void initTime(void)
{
sint32 Fsys = IfxStm_getFrequency(BSP_DEFAULT_TIMER);
TimeConst[TIMER_INDEX_10NS] = Fsys / (1000000000 / 10);
TimeConst[TIMER_INDEX_100NS] = Fsys / (1000000000 / 100);
TimeConst[TIMER_INDEX_1US] = Fsys / (1000000 / 1);
TimeConst[TIMER_INDEX_10US] = Fsys / (1000000 / 10);
TimeConst[TIMER_INDEX_100US] = Fsys / (1000000 / 100);
TimeConst[TIMER_INDEX_1MS] = Fsys / (1000 / 1);
TimeConst[TIMER_INDEX_10MS] = Fsys / (1000 / 10);
TimeConst[TIMER_INDEX_100MS] = Fsys / (1000 / 100);
TimeConst[TIMER_INDEX_1S] = Fsys * (1);
TimeConst[TIMER_INDEX_10S] = Fsys * (10);
TimeConst[TIMER_INDEX_100S] = Fsys * (100);
}
- 위에서 생성한 constants들을 이용하면 좀 더 쉽고 직관적인 시간 제어가 가능해진다.
// in Bsp.h
#define TimeConst_0s ((Ifx_TickTime)0)
#define TimeConst_10ns (TimeConst[TIMER_INDEX_10NS])
#define TimeConst_100ns (TimeConst[TIMER_INDEX_100NS])
#define TimeConst_1us (TimeConst[TIMER_INDEX_1US])
#define TimeConst_10us (TimeConst[TIMER_INDEX_10US])
#define TimeConst_100us (TimeConst[TIMER_INDEX_100US])
#define TimeConst_1ms (TimeConst[TIMER_INDEX_1MS])
#define TimeConst_10ms (TimeConst[TIMER_INDEX_10MS])
#define TimeConst_100ms (TimeConst[TIMER_INDEX_100MS])
#define TimeConst_1s (TimeConst[TIMER_INDEX_1S])
#define TimeConst_10s (TimeConst[TIMER_INDEX_10S])
#define TimeConst_100s (TimeConst[TIMER_INDEX_100S])
추가적인 설명
- 이번 장에서 추가로 설명할 내용은 없습니다.
- 대신 간단한 exercise를 해결해보며 이해도를 높여봅시다.
[Exercise 1] 바ㅡㅡ안 짜ㅡㅡ악 을 빤짝빤짝 으로
- 위의 예제는 인터럽트의 주기가 1초이기 때문에 LED_TICK의 점멸 주기는 2초가 됩니다.
- 주기를 100msec로 바꿔서 200msec 주기로, 즉 LED를 5Hz의 주기로 점멸시켜 봅시다.
- 위의 코드에서 2군데만 수정하면 됩니다.
[Exercise 2] 인터럽트 발생 주기를 1msec로
- 인터럽트 발생 주기를 1msec 로 변경해 봅시다.
- 프로그래밍 가이드
- ISR(
STM_Int0Handler(void))에서 직접 LED를 점멸하는 대신g_Stm.counter를 1씩 증가 시킵니다. IfxStmDemo_run(void)함수에서g_Stm.counter값을 살펴보면서 100 이 될 때마다 LED blink 함수를 호출합니다.- [중요]
- 위와 같은 방식으로 프로그래밍 하는 것이 스케쥴러의 기본 아이디어 입니다.
- 여기서 가장 기본이 되는 주기적 증가 카운터, 이 예에서는
g_Stm.counter를 Tick 이라 부르며, 시계의 초침과 같은 역할을 합니다.