10 타이머(Timer) 제어

원문 : http://kkamagui.springnote.com/pages/428819

 

들어가기 전에...

 

0.시작하면서...

 타이머는 일정 시간이 지나면 그것을 알려주는 역할을 하는 아주 간단한 장치이다. 아주 간단하지만 OS에서, 특히 시분할 스케줄링을 하는 OS에서는 스케줄링의 시작점이자 프로세스 퍼포먼스 분석의 기초자료가 되는 아주 중요한 역할을 한다. 또한 주기적으로 해야하는 작업의 경우 타이머를 사용해서 하면 편리하게 할 수 있다.

NDS의 타이머에 대해서 자세히 알아보자.

 

1.타이머 레지스터(Timer Register)

 NDS의 타이머에 대한 내용은 http://nocash.emubase.de/gbatek.htm#dstimers에서 자세히 볼 수 있다. NDS에서는 ARM9 4개, ARM7 4개 총 8개의 타이머를 가지고 있으며 타이머의 클럭은 F = 33.514 MHz 이다.

 GBA와 클럭만 다르고 나머지는 동일하므로 GBA의 타이머에 대한 설명을 보도록 하자.

The GBA includes four incrementing 16bit timers.
Timer 0 and 1 can be used to supply the sample rate for DMA sound channel A and/or B.


4000100h - TM0CNT_L - Timer 0 Counter/Reload (R/W)
4000104h - TM1CNT_L - Timer 1 Counter/Reload (R/W)
4000108h - TM2CNT_L - Timer 2 Counter/Reload (R/W)
400010Ch - TM3CNT_L - Timer 3 Counter/Reload (R/W)
Writing to these registers initializes the <reload> value (but does not directly affect the current counter value). Reading returns the current <counter> value (or the recent/frozen counter value if the timer has been stopped).
The reload value is copied into the counter only upon following two situations: Automatically upon timer overflows, or when the timer start bit becomes changed from 0 to 1.
Note: When simultaneously changing the start bit from 0 to 1, and setting the reload value at the same time (by a single 32bit I/O operation), then the newly written reload value is recognized as new counter value.

 Counter/Reload 레지스터는 16bit 크기를 가지는 레지스터로써, 읽을 때는 현재 카운터의 값을 리턴하고 쓸 때는 타이머 만료 카운트를 설정하는데 사용된다. 주의할 점은 쓸 때(Reload) 만료 카운트의 값이 바로 적용되는 것이 아니라 아래의 타이머 컨트롤(Timer Control) 레지스터의 7Bit의 값이 0 -> 1로 바뀌거나 타이머가 만료되었을 때 내부적으로 복사되어서 사용되는 점이다. 만료 카운트를 즉시 적용시키려면 값을 쓴 다음 타이머를 중지했다가 다시 시작하면 된다.

 만료 카운터를 설정할때 값을 양수로 넣으면 안되고 음수로 넣어야 한다. 즉 타이머 카운팅이 100회되면 만료된다는 것을 설정하고 싶다면 -100을 넣어야 한다. 이 점을 특히 주의해야 한다.

 

 만약 직접 값을 설정하고 싶다면 매크로를 사용하지 않고 직접 쓸 수 있다. 만약 타이머가 10회 카운트 되면 인터럽트가 발생하도록 설정하려면, 타이머의 값이 16bit이므로 Max 값은 0xFFFF가 된다. 이 값이 0x10000으로 되는 순간 overflow가 발생해서 인터럽트가 발생하므로 따라서 0x10000 - 10의 값을 설정하면 차례로 카운팅 되어 10회 카운트 된 다음 인터럽트가 발생하게 된다. 

 

4000102h - TM0CNT_H - Timer 0 Control (R/W)
4000106h - TM1CNT_H - Timer 1 Control (R/W)
400010Ah - TM2CNT_H - Timer 2 Control (R/W)
400010Eh - TM3CNT_H - Timer 3 Control (R/W)

Bit   Expl.
  0-1   Prescaler Selection (0=F/1, 1=F/64, 2=F/256, 3=F/1024)  2     Count-up Timing   (0=Normal, 1=See below)
  3-5   Not used
  6     Timer IRQ Enable  (0=Disable, 1=IRQ on Timer overflow)
  7     Timer Start/Stop  (0=Stop, 1=Operate)
  8-15  Not used
When Count-up Timing is enabled, the prescaler value is ignored, instead the time is incremented each time when the previous counter overflows. This function cannot be used for Timer 0 (as it is the first timer).
F = System Clock (16.78MHz). <== GBA의 클럭

 위의 타이머 컨트롤러의 플래그를 보면 만료가 되었을 때 인터럽트를 발생하게 하여 알려주는 옵션이 있다. 이것을 이용하여 인터럽트 발생 시 즉시 어떤 일을 수행하게 하면 타이밍에 민감한 프로그램도 작성할 수 있다.

 프리스케일러(Prescaler)의 경우 타이머 클럭을 특정 값으로 나누어 타이머의 분해능을 선택하는 역할을 한다. 0을 선택한 경우는 1초에 33.514M 번 타이머가 증가하지만 1을 선택한 경우는 1초에 33.514/64번 발생하는 것이다. 자신의 용도에 맞게 잘 사용하면 된다.

 Count-up Timing 옵션이 있는데 이 옵션을 사용하면 프리스케일러 값은 무시되고 바로 이전의 카운터(타이머 1의 경우는 타이머 0, 타이머 2의 경우는 타이머 1...)의 overflow가 발생할 때마다 카운트가 증가하게 된다. 단 타이머 0의 경우는 이전 타이머가 없기 때문에 해당되지 않는다.

 

2.구현

 타이머에 대한 구현은 devkitPro\libnds\source\include\nds 폴더에 timers.h 파일에서 찾을 수 있다.

  1. // Max frequency is: 33554432Hz\n
    // Min frequency is: 512Hz\n
  2. // 0x2000000는 33554432와 같다.
  3. #define TIMER_FREQ(n)    (-0x2000000/(n)) 
  4. #define TIMER_FREQ_64(n)  (-(0x2000000>>6)/(n))
  5. #define TIMER_FREQ_256(n) (-(0x2000000>>8)/(n))
  6. #define TIMER_FREQ_1024(n) (-(0x2000000>>10)/(n))

  7. //! Same as %TIMER_DATA(0).
    #define TIMER0_DATA    (*(vuint16*)0x04000100)
    #define TIMER1_DATA    (*(vuint16*)0x04000104)
    #define TIMER2_DATA    (*(vuint16*)0x04000108)
    #define TIMER3_DATA    (*(vuint16*)0x0400010C)
  8. #define TIMER_DATA(n)  (*(vuint16*)(0x04000100+((n)<<2)))
  9. // Timer control registers
    //! Same as %TIMER_CR(0).
    #define TIMER0_CR   (*(vuint16*)0x04000102)
    #define TIMER1_CR   (*(vuint16*)0x04000106)
    #define TIMER2_CR   (*(vuint16*)0x0400010A)
    #define TIMER3_CR   (*(vuint16*)0x0400010E)
  10. #define TIMER_CR(n) (*(vuint16*)(0x04000102+((n)<<2)))
  11. #define TIMER_ENABLE    (1<<7)
  12. //! Causes the timer to request an Interupt on overflow.
    #define TIMER_IRQ_REQ   (1<<6)
  13. //! When set will cause the timer to count when the timer below overflows (unavailable for timer 0).
    #define TIMER_CASCADE   (1<<2)
  14. //! Causes the timer to count at 33.514Mhz.
    #define TIMER_DIV_1     (0)
    //! Causes the timer to count at (33.514 / 64) Mhz.
    #define TIMER_DIV_64    (1)
    //! Causes the timer to count at (33.514 / 256) Mhz.
    #define TIMER_DIV_256   (2)
    //! Causes the timer to count at (33.514 / 1024)Mhz.
    #define TIMER_DIV_1024  (3)

 타이머의 주소와 비트 값에 대한 간단한 매크로로 되어있는 것을 알 수 있다.

 

 그럼 이제 실제로 사용하는 예제를 보자. 아주 간단한데 타이머를 1/1000초 즉 ms 단위로 튀게 하려면 아래와 같이 하면된다. ㅡ,.ㅡ;;;

  1. TIMER0_DATA = TIMER_FREQ( 1000 );
  2. // 타이머의 시작
  3. TIMER0_CRTIMER_ENABLE | TIMER_IRQ_REQ | TIMER_DIV_1;
  4. ...... 생략 ......
  5. // 타이머의 종료
  6. TIMER0_CR &= ~TIMER_ENABLE;

 

 

3.마치면서...

 이상으로 타이머에 대해 알아보았다. 워낙 간단해서 따로 설명할 것도 없었는데... 사용법 정도만 알아놓자.

 

 

 

이 글은 스프링노트에서 작성되었습니다.

+ Recent posts