본문 바로가기
코스웨어/13년 스마트컨트롤러

[타이머/카운터와 인터럽트로 1초간격 LED 깜빡이기] - by 문남경

by 알 수 없는 사용자 2013. 5. 21.
728x90
반응형

이번 수업 시간에는 

타이머/카운터(TC-Timer Counter)와 AIC(Advanced Interrupt controller)를 사용해 

1초간격으로 LED를 깜빡이게 하였습니다.


 - 먼저 TC 와 AIC에 대해 간략히 알아보고 본문으로 넘어가겠습니다

 AT91SAM7S256의 타이머/카운터(TC)는,

  0~2까지 3개의 채널을 가지며, 이들은 서로 독립적으로 동작합니다.

 ② 타이머 카운터의  레지스터(TC_RC)는 최대 16비트 까지의 이진값(65535)을 가질 수 있습니다. 

 ③ 또한 상태 레지스터(TC_SR)를 호출하면 레지스터가 초기화 됩니다.

 ④ TC는 주파수 측정, 시간 간격 측정, 시간 지연, 펄스 발생, PWM 출력, 이벤트 카운트 등의 기능을 

     수행할 수 있습니다.

  TC의 레지스터 맵핑을 살펴보면 TC0 TC1 TC2 각각의 채널을 제어하는 레지스터가 따로 존재하기

     때문에 그에 해당하는 레지스터를 사용해야 합니다.

 ⑥ 단, TC_BCR과 TC_BMR 레지스터는 3개 채널 공용레지스터입니다.

  채널 모드 레지스터(TC_CMR)가 있어 MCK(마스터클럭)를 분주(2, 8, 32, 128, 1024)한 신호를 

     입력받을 수 있습니다.


 AT91SAM7S256의 AIC(Advanced Interrupt Controller)는, 

 ① 32개의 인터럽트 소스

 ② 0부터 7까지 8레벨의 우선순위 제어

     예) AIC_SMR[TC0] = (0 << PRIOR);   <-- 우선순위 최하

 ③ 개별적인 인터럽트 허용

 ④ 벡터형 인터럽트 처리



자, 이제 소스를 보면서 공부해 봅시다^^

소스의 주석을 통해 설명하고 있으니, 주석을 잘 관찰하시기 바랍니다~*

 #include "timer.h"


static volatile unsigned int uiTic; // Timer_Handler 함수 호출을 카운트, 1m초


void Timer0_Init(void)

{

PMC_PCER = 1 << TC0;  // PMC_PCER(Peripheral Clock Enable Register)에 타이머카운터 장치 활성화, P204, P33

 //  TC0(타이머카운터 0)에 클럭이 공급된다.


   

// 1. 시작 : 타이머 클럭 비활성화 ------------------

TC0_CCR = 1 << CLKDIS; //카운터 클럭 비활성화 명령(CLKDIS)을 Enable


// 2. 시작 : 타이머 인터럽트 비활성화 -------------

TC0_IDR = (1 << COVFS)|(1 << LOVRS)|(1 << CPAS)|(1 << CPBS)

|(1 << CPCS)|(1 << LDRAS)|(1 << LDRBS)|(1 << ETRGS); // 타이머 인터럽트 비활성화(TC_IDR 설정)

TC0_SR;// 타이머카운터 상태 레지스터 초기화(TC_SR 읽기)


TC0_CMR = (TIMER_CLOCK4 << TCCLKS) | (1 << CPCTRG); /*TC0_CMR(채널 모드 레지스터)에서 

TCCLKS(Clock Selection)에 3을 넣어 분주비 128(TIMER_CLOCK4) 설정 | RC 비교방식 트리거 활성화*/


TC0_RC = 375; // MCK divided by 128 => 375000Hz, 1CK = (about)2.67us [TC_RC(Register C)설정]

     // 한마디로 1ms에 인터럽터를 발생시키기 위해 375로 설정 


// 3. 시작 : 타이머 카운터 0 인터럽트 비활성화 ------

AIC_IDCR = 1 << TC0; // 인터럽트 비활성화(AIC_IDCR) = Enable(1) << 타이머 카운터 0(TC0) 


AIC_SVR[TC0] = (volatile unsigned int)Timer_Handler;

/*AIC_Source Vector Register = 타이머 카운터 0 인터럽트 핸들러 등록

여기서 Vector 레지스터는 인터럽트가 발생하면 점프하게 될 함수(ISR:인터럽트 서비스 루틴) 주소만을 저장할 수 있다.

함수 전체를 저장할 수 있으면 좋겠지만, 그러기엔 ARM 레지스터의 용량이 너무 작다.


 Vector 가 뭔지 궁금해 하시는 분들을 위해

 - 우리의 ARM은 폴링형 인터럽트와 vector형 인터럽트 등 인터럽트 처리방법 중에 vector형 인터럽트 처리 방식을 사용한다.

1. Polling 형 인터럽트

- 폴링은 투표, 개표 라는 뜻이다. 즉 CPU가 소프트웨어적으로 보드에 인터럽트를 낼수있는 하드웨어들을 차례대로

  검사하는 방식이다. 하드웨어가 간단하고 저렴하지만, 인터럽트를 낼수있는 하드웨어 갯수 증가에 따라 속도가 느려진다

  이후 인터럽트가 검출된 장치를 처리한다. 여기서 "처리한다"   ISR(Interrupt Service Rutine)이라고 한다.


2. vector 형 인터럽트

- cpu가 소프트웨어적으로 하드웨어들을 검사하는것이 아니라, 

   인터럽트를 발생시킨 장치가 cpu에 ISR(우리가 만든 핸들러 함수)의 

   시작 번지를 제공하면 cpu가 이것을 먼저 처리하는 방식. 

   이 ISR들은 기본적으로 interrupt vector에 저장되어 있고 더 나아가 

   사용자가 ISR을 설정해주고 싶으면 Interrupt Handler를 작성/등록(AIC_SVR[TC0])해주면 된다.

   하드웨어가 복잡하고 비싸지만, 빠르고, 장치 갯수에 상관없이  언제든지! 서비스를 제공한다. 

   AT91SAM7S256, atmega2560 등 에서는 이방식을 사용한다.


AIC_SMR[TC0] = (0 << PRIOR)|(3 << SRCTYPE);

/* TC0 인터럽트 소스 모드 레지 = 우선순위레벨(Priority Level)을 0  | 

     여기서 만약, 인터럽트 두개가 동시에 발생하면 ?

      polling 방식은 순차적으로 장치를 검사하기 때문에 그 순서대로 우선순위를 정하지만

      vector 방식은 인터럽트 우선순위를 제어할 수 있는 레지스터(AIC_SMR)가 존재하여, 

      레지스터에 값을 집어넣어 우선순위를 정한다.

[출처] [AVR] Interrupt|작성자 태희로그

[출처] [AVR] Interrupt|작성자 태희로그


  Interrupt Source Type(SRCTYPE) High level Sensitive로 설정*/



AIC_ICCR = 1 << TC0;  // 타이머 카운터 0 인터럽트 클리어(TC0)


TC0_IER = 1 << CPCS;

/* 타이머 카운터 인터럽트 활성화 레지스터 = RC 비교 인터럽트만 활성화*/


// 2. 끝 : 타이머 인터럽트 비활성화 ---------------


AIC_IECR = 1 << TC0;  // 타이머 카운터 0 인터럽트 활성화(AIC_IECR, TC0)


// 3. 끝 : 타이머 카운터 0 인터럽트 비활성화 -------


TC0_CCR = 1 << CLKEN;  // 타이머 클럭 활성화(TC_CCR, CLKEN)


// 1. 끝 : 타이머 클럭 비활성화 ------------------


TC0_CCR = 1 << SWTRG;  // 타이머 시작(TC_CCR, SWTRG)


}


void Timer_Handler(void)

{

TC0_SR; //핸들러가 호출되면 현재 인터럽트가 발생된 상태이기 때문에

//다음 인터럽트가 발생할 수 있도록 상태 레지스터 초기화

++uiTic;

return;

}


void ms_Delay(unsigned int uims)

{

uiTic = 0;

while(uims > uiTic);  //이 함수를 종료하려면 uims를 기다려야 한다

  // uiTic은 1ms마다 증가하므로 uims에 1000을 넣으면 

  //약 1초를 기다려야 반복문이 종료된다.

}



헤더파일과 메인소스는 첨부하지 않아도 이해할 수 있으므로 생략하도록 하겠습니다.


위의 소스를 보면서 이해하셨다면, 좋겠지마~는 아닐 경우를 대비해

여기서 끝내지 않고 조금더 이해하기 쉽도록 그림으로 설명해 볼까요?^^



 





위의 그림에서 인터럽트가 발생하는 시점은 : 우리가 소스에서 TC0_RC = 375; 를 함으로써 0.001초마다 한번씩 인터럽트가 발생합니다.

그러므로 main함수에서 Timer_Init()을 호출하면 약 0.001초 마다 인터럽트가 발생하게 되는 것입니다.

그 인터럽트 발생 횟수를 저장하기 위해서 전역변수(static volatile unsigned int uiTic;) 하나를 핸들러 함수 안에 만들어 놓았습니다.


제 글을 읽고 조금이나마 이해하시는데 도움이 됐으면~ 하는 바램입니다~

모두들! 즐공^^ 개선해야 할 점 댓글로 달아주세용~



728x90