본문으로 바로가기

통상적인 절차인지는 몰르지만, 저는 하드웨어 엔지니어는 아니지만, 아~주 예전에 80196이라는 MCU를 가지고 역진자 시스템(inverted pendulum)을 제어[바로가기]했었죠. 그 후에는 AVR를 살짝 하다가 그 다음에는 Cortex M3도 살짝 하다가.. 뭐 지금은 하드웨어를 제공받을 때 왠만한 지원은 받으니까 속칭 때와 장소를 가리지 않고 그냥 제어기를 구현했던 것 같습니다.ㅠㅠ. 

그러다가 최근에는 출시된진 좀 되었지만 TI의 TMS320F28335를 가지고 놀고 있는 중이지요... 속칭 28335~~ 얼마전에 GPIO의 몹시 기초적 부분을 건드렸구요~~~ 이번에는 타이머 인터럽트라는 걸 이야기할까 합니다. 뭐 언제나 그렇듯.. 아~~~주 기초적인 이야기지요~~^^ 사실 어차피 제조사인 TI가 배포하는 공식 예제[바로가기]를 들여다 보면 다 있습니다.^^ 여전히 사용하는 보드는 SyncWorks의 28335 초소형 모듈[바로가기]이구요. 어차피 예제가 꽤 자세히 잘 나와 있지만... 그 예제 마저도 좀 분량을 줄여서 이야기할려구요.. 왜냐구요?? 기초니까요~~^^

// Step 1. Initialize System Control:
// PLL, WatchDog, enable Peripheral Clocks
// This example function is found in the DSP2833x_SysCtrl.c file.
   InitSysCtrl();

// Step 3. Clear all interrupts and initialize PIE vector table:
// Disable CPU interrupts
   DINT;

// Initialize the PIE control registers to their default state.
// The default state is all PIE interrupts disabled and flags
// are cleared.
// This function is found in the DSP2833x_PieCtrl.c file.
   InitPieCtrl();

// Disable CPU interrupts and clear all CPU interrupt flags:
   IER = 0x0000;
   IFR = 0x0000;

// Initialize the PIE vector table with pointers to the shell Interrupt
// Service Routines (ISR).
// This will populate the entire table, even if the interrupt
// is not used in this example.  This is useful for debug purposes.
// The shell ISR routines are found in DSP2833x_DefaultIsr.c.
// This function is found in DSP2833x_PieVect.c.
   InitPieVectTable();

이번에는 코드부터 들이밀어야죠... 보드 제조사에서 강좌를 잘 진행해주기도 하지만, 아주 복잡하게 흘러가는 경우가 아니라면 통상 요런 설정들은 파워서플리이 전원을 넣고, 스위치를 켜고 .. 등등 처럼 절차일때가 많아서 말이죠~~^^ TI가 제공하는 예제의 첫 부분은 위와 같네요. 특이할 만한 부분은 없습니다. 단지.. Step대로 따라가면 되겠어요. 단.. InitPieCtrl()이라는 함수를 실행하는데.. 그 이름에 있는 PIE가

출처 : TI가 배포하는 28335문서

위 그림에 보면 인터럽트를 배분하는 역할을 하는 군요. 문서를 읽어보면 정말 많은 수의 인터럽트를 28335가 가지고 있어서 저렇게 확장해주는 역할이 필요한 모양입니다.

// Interrupts that are used in this example are re-mapped to
// ISR functions found within this file.
   EALLOW;  // This is needed to write to EALLOW protected registers
   PieVectTable.TINT0 = &cpu_timer0_isr;

   GpioCtrlRegs.GPBMUX1.bit.GPIO36 = 0;
   GpioCtrlRegs.GPBDIR.bit.GPIO36 = 1;

   EDIS;    // This is needed to disable write to EALLOW protected registers

그리고 저는 이번 예제에서 LED를 키고 끌거라 위와 같이 살짝 Gpio를  초기화하는 코드도 넣어 두었습니다.. 저 부분은 첫 예제[바로가기]에서도 이야기했고, 그다음 GPIO를 다룰 때[바로가기]도 이야기를 했습니다. 제가 사용하는 보드에 있는 LED를 일반 GPIO로 설정(GpioCtrlRegs.GPBMUX1.bit.GPIO36 = 0)하고 해당 핀을 출력으로 설정(GpioGtrlRegs.GPBDIR.bit.GPIO36 = 1)한다는 내용입니다.^^

더 중요한 부분은 바로 PieVectTable.TINT0 = &cpu_timer0_isr이라는 구문입니다. TINT0를 cpu_timer0_isr이라는 함수에 연결해둔 겁니다. 즉, 타이머 인터럽트가 발생하면 실행할 함수를 알려주는 거죠~

// Step 4. Initialize the Device Peripheral. This function can be
//         found in DSP2833x_CpuTimers.c
   InitCpuTimers();   // For this example, only initialize the Cpu Timers


// Configure CPU-Timer 0, 1, and 2 to interrupt every second:
// 150MHz CPU Freq, 1 second Period (in uSeconds)
   ConfigCpuTimer(&CpuTimer0, 150, 1000000);

// To ensure precise timing, use write-only instructions to write to the entire register. Therefore, if any
// of the configuration bits are changed in ConfigCpuTimer and InitCpuTimers (in DSP2833x_CpuTimers.h), the
// below settings must also be updated.
   CpuTimer0Regs.TCR.all = 0x4000; // Use write-only instruction to set TSS bit = 0

// Step 5. User specific code, enable interrupts:

// Enable CPU int1 which is connected to CPU-Timer 0
   IER |= M_INT1;

// Enable TINT0 in the PIE: Group 1 interrupt 7
   PieCtrlRegs.PIEIER1.bit.INTx7 = 1;

// Enable global Interrupts and higher priority real-time debug events:
   EINT;   // Enable Global interrupt INTM
   ERTM;   // Enable Global realtime interrupt DBGM

이제 위에 있는 설정 중에 ConfigCpuTimer는 현재 사용하는 클럭 주파수 (저는 150MHz)를 설정하고, us단위의 타이머 인터럽트 간격(1,000,000us = 1,000ms = 1sec)을 설정하는 겁니다. 그리고 나서 CpuTimerRegs.TCR.all = 0x4000이라고 설정합니다. 이 것의 의미는 

출처 : TI 28335 문서

각 비트별로 의미가 있습니다. 0x4000은 14번 비트만 1이 되는데... 바로 타이머를 enable한다는 거죠. 또 중요한건 4번 비티를 0으로 둔 것인데요. 그건 타이머를 런닝한다는 것입니다.

출처 : SyncWorks의 인터럽트 설명 문서

뭐 그러나 영어가 짜증난다면 저렇게 한글로도 배포를 해주고 계시네요~~^^. 그리고 PieCtrlRegs.PIEIER1.bit.INTx7 = 1라는 구문이 보이네요.. 이것은

출처 : TI의 28335 문서

위 PIE 벡터 테이블에 보면 INTx.7의 INT1이 TINT0라는 것을 알 수 있죠... 그걸 1로 셋해서 타이머를 인에이블 시키는 것입니다.^^

그리고.. 저런 보드에 위 코드를 실행해 보는거죠.. ㅋㅋ. 뭐 다시 한 번 말씀드리지만... TI가 배포하는 예제를 그냥 실행한 것 뿐입니다. 아~주 살짝 편집만 하구요.. 아참.. 그전에 하나 더있네요... 타이머 인터럽트가 실행되면 처리할 루틴 말이죠...

__interrupt void cpu_timer0_isr(void)
{
   CpuTimer0.InterruptCount++;

   GpioDataRegs.GPBTOGGLE.bit.GPIO36 = 1;

   // Acknowledge this interrupt to receive more interrupts from group 1
   PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
}

위 코드입니다. GPIO36번을 토글하는 거죠... 원래 의도는 1초에 한 번 깜빡거리는 건데.. 멍청하게 아무 생각없이 예제를 편집했더니 깜빡이는 주기가 2초가 되어 버렸네요. ㅎㅎ^^

//###########################################################################
// Description
//! \addtogroup f2833x_example_list
//! <h1>Cpu Timer (cpu_timer)</h1>
//!
//! This example configures CPU Timer0, 1, and 2 and increments
//! a counter each time the timers asserts an interrupt.
//!
//! \b Watch \b Variables \n
//! - CputTimer0.InterruptCount
//! - CpuTimer1.InterruptCount
//! - CpuTimer2.InterruptCount
//
//###########################################################################
// $TI Release: F2833x/F2823x Header Files and Peripheral Examples V141 $
// $Release Date: November  6, 2015 $
// $Copyright: Copyright (C) 2007-2015 Texas Instruments Incorporated -
//             http://www.ti.com/ ALL RIGHTS RESERVED $
//###########################################################################

#include "DSP28x_Project.h"     // Device Headerfile and Examples Include File

// Prototype statements for functions found within this file.
__interrupt void cpu_timer0_isr(void);

void main(void)
{
// Step 1. Initialize System Control:
// PLL, WatchDog, enable Peripheral Clocks
// This example function is found in the DSP2833x_SysCtrl.c file.
   InitSysCtrl();

// Step 3. Clear all interrupts and initialize PIE vector table:
// Disable CPU interrupts
   DINT;

// Initialize the PIE control registers to their default state.
// The default state is all PIE interrupts disabled and flags
// are cleared.
// This function is found in the DSP2833x_PieCtrl.c file.
   InitPieCtrl();

// Disable CPU interrupts and clear all CPU interrupt flags:
   IER = 0x0000;
   IFR = 0x0000;

// Initialize the PIE vector table with pointers to the shell Interrupt
// Service Routines (ISR).
// This will populate the entire table, even if the interrupt
// is not used in this example.  This is useful for debug purposes.
// The shell ISR routines are found in DSP2833x_DefaultIsr.c.
// This function is found in DSP2833x_PieVect.c.
   InitPieVectTable();

// Interrupts that are used in this example are re-mapped to
// ISR functions found within this file.
   EALLOW;  // This is needed to write to EALLOW protected registers
   PieVectTable.TINT0 = &cpu_timer0_isr;

   GpioCtrlRegs.GPBMUX1.bit.GPIO36 = 0;
   GpioCtrlRegs.GPBDIR.bit.GPIO36 = 1;

   EDIS;    // This is needed to disable write to EALLOW protected registers

// Step 4. Initialize the Device Peripheral. This function can be
//         found in DSP2833x_CpuTimers.c
   InitCpuTimers();   // For this example, only initialize the Cpu Timers


// Configure CPU-Timer 0, 1, and 2 to interrupt every second:
// 150MHz CPU Freq, 1 second Period (in uSeconds)
   ConfigCpuTimer(&CpuTimer0, 150, 1000000);

// To ensure precise timing, use write-only instructions to write to the entire register. Therefore, if any
// of the configuration bits are changed in ConfigCpuTimer and InitCpuTimers (in DSP2833x_CpuTimers.h), the
// below settings must also be updated.
   CpuTimer0Regs.TCR.all = 0x4000; // Use write-only instruction to set TSS bit = 0

// Step 5. User specific code, enable interrupts:

// Enable CPU int1 which is connected to CPU-Timer 0
   IER |= M_INT1;

// Enable TINT0 in the PIE: Group 1 interrupt 7
   PieCtrlRegs.PIEIER1.bit.INTx7 = 1;

// Enable global Interrupts and higher priority real-time debug events:
   EINT;   // Enable Global interrupt INTM
   ERTM;   // Enable Global realtime interrupt DBGM

// Step 6. IDLE loop. Just sit and loop forever (optional):
   for(;;);
}

__interrupt void cpu_timer0_isr(void)
{
   CpuTimer0.InterruptCount++;

   GpioDataRegs.GPBTOGGLE.bit.GPIO36 = 1;

   // Acknowledge this interrupt to receive more interrupts from group 1
   PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
}

//===========================================================================
// No more.
//===========================================================================

위 코드가 TI가 배포하는 전체 예제를 제 상황에 맞게 살~~~짝 바꾼겁니다. 뭐 허접하지만.. 동영상으로도 보시죠 ㅋㅋ.

이제 뭔가 283335로 재미있는 예제를 하나 다뤄봐야 할텐데 말이죠. 뭐 아직 ADC도 해야하고.. 간단하게 나마 시리얼 통신이라도 해야죠~^^

신고