[ARM]타이머 카운터와 인터럽트

2013. 5. 20. 17:43스마트콘트롤러/ARM

이번 수업 시간에는 

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

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


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

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

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

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

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

 ④ 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개의 인터럽트 소스

 ② 8레벨의 우선순위 제어

 ③ 개별적인 인터럽트 허용

 ④ 벡터형 인터럽트 처리



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

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

 #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;) 하나를 핸들러 함수 안에 만들어 놓았습니다.


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

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




'스마트콘트롤러 > ARM' 카테고리의 다른 글

text LCD에 사용자정의 한글 띄우기  (0) 2013.05.28
[TEXT LCD 모듈]사용자정의 CG램에 HEX숫자 변환하는 엑셀  (0) 2013.05.27
ARM ADC컨버터  (0) 2013.05.08
1. GNUARM 설치하기。  (0) 2013.04.11
센서 종류  (0) 2013.04.11