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 이라고 불리는 매우 작은 단위의 시간이 누적된다.

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한다.

TwinkleTwinkleLittleStar_GeneralBlockDiagram

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

TwinkleTwinkleLittleStar_CompareMode

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 이라 부르며, 시계의 초침과 같은 역할을 합니다.

Analytics