Chapter 7: Timer

1231·2024년 6월 30일

임베디드 OS 개발

목록 보기
5/8

SP804 Registers 구조체화

Realview PB 에는 SP804 라는 타이머 하드웨어를 가지고 있다.

https://developer.arm.com/documentation/ddi0271/latest/

SP804 의 경우 측정 카운터가 감소하여 카운터가 0이 되면 인터럽트를 발생시키는 방식이다.
베이스 메모리는 0x10011000

또한 총 7개의 레지스터 존재

TimerXLoad: 32-bit register that contains the value from which the counter is to decrement. 카운터의 목표 값을 지정하는 레지스터

TimerXValue: Read-only register that gives the current value of the decrementing counter. 감소하는 렞스터, 0이 되면 인터럽트가 발생한다.

TimerXControl:
- Oneshot[0]: One-shot 모드이면 인터럽트가 한번 발생하고 타이머가 바로 꺼진다.
- TimerSize[1]: 16 bit 와 32 bit timexvalue 의 크기 설정
- TimerPre[3:2]: Prescale 설정, 1, 16, 256 설정 가능(몇 클럭 마다 카운트를 줄이겠는가)
- IntEnable[5]: Interrupt enable
- TimerMode[6]: Free-running/Periodic mode, Free-running 은 최대값(0xFFFFFFFF if 32bit) 부터 0까지 decrement, Periodic 은 TimerXLoad 에 설정한 값부터 0까지 decrement
- TimerEn[7]: Timer Enable

TimerXIntClr: Any write to this register, clears the interrupt output from the counter.

... 등등이 존재

이를 구조체화 할 것임.
hal/rvpb/Timer.h

  1 #ifndef HAL_RVPB_TIMER_H_
  2 #define HAL_RVPB_TIMER_H_
  3
  4 typedef union TimerXControl_t {
  5   uint32_t all;
  6   struct {
  7     uint32_t OneShot:1; //0
  8     uint32_t TimerSize:1; //1
  9     uint32_t TimerPre:2; //2:3
 10     uint32_t Reserved0:1; //4
 11     uint32_t IntEnable:1; //5
 12     uint32_t TimerMode:1; //6
 13     uint32_t TimerEn:1; //7
 14     uint32_t Reserved1:24; //8:31
 15   } bits;
 16 } TimerXControl_t;
 17
 18 typedef union TimerXRIS_t {
 19   uint32_t all;
 20   struct {
 21     uint32_t TimerXRIS:1; //0
 22     uint32_t Reserved0:31; //31:1
 23   } bits;
 24 } TimerXRIS_t;
 25
 26 typedef union TimerXMIS_t {
 27   uint32_t all;
 28   struct {
 29     uint32_t TimerXMIS:1; //0
 30     uint32_t Reserved0:31; //31:1
 31   } bits;
 32 } TimerXMIS_t;
 33
 34
 35 typedef struct Timer_t
 36 {
 37         uint32_t        timerxload; //0x00
 38         uint32_t        timerxvalue; //0x04
 39         TimerXControl_t timerxctrl; //0x08
 40         uint32_t        timerxintclr; //0x0c
 41         TimerXRIS_t     timerxris; //0x10
 42         TimerXMIS_t     timerxmis; //0x14
 43         uint32_t        timerxbgload; //0x18
 44 }Timer_t;
 45
 46 #define TIMER_CPU_BASE 0x10011000
 47 #define TIMER_INTERRUPT 36
 48
 49 #define TIMER_FREERUNNING 0
 50 #define TIMER_PERIODIC 1
 51
 52 #define TIMER_16BIT_COUNTER 0
 53 #define TIMER_32BIT_COUNTER 1
 54
 55 #define TIMER_1MZ_INTERVAL (1024*1024)

HAL 공용 API 작성

hal/HalTimer.h

  1 #ifndef HAL_HALTIMER_H_
  2 #define HAL_HALTIMER_H_
  3
  4 void    Hal_timer_init(void);
  5 uint32_t Hal_timer_get_1ms_counter(void);
  6 #endif

하드웨어 코드 작성
hal/rvpb/Timer.c

  1 #include "stdint.h"
  2 #include "Timer.h"
  3 #include "HalTimer.h"
  4 #include "HalInterrupt.h"
  5
  6 extern volatile Timer_t* Timer;
  7
  8 static uint32_t internal_1ms_counter;
  9
 10 static void interrupt_handler(void);
 11
 12 void Hal_timer_init(void) {
 13
 14   //interface reset
 15   Timer->timerxctrl.bits.OneShot = 0;
 16   Timer->timerxctrl.bits.TimerSize = 0;
 17   Timer->timerxctrl.bits.TimerPre = 0;
 18   Timer->timerxctrl.bits.IntEnable = 1;
 19   Timer->timerxctrl.bits.TimerMode = 0;
 20   Timer->timerxctrl.bits.TimerEn = 0;
 21
 22   Timer->timerxload = 0;
 23   Timer->timerxvalue = 0xFFFFFFFF;
 24
 25   //periodic setting
 26   Timer->timerxctrl.bits.TimerMode = TIMER_PERIODIC;            //Timer mode is Periodic
 27   Timer->timerxctrl.bits.TimerSize = TIMER_32BIT_COUNTER;       //Timer size 32 bit
 28   Timer->timerxctrl.bits.OneShot = 0;   //wrapping mode, not oneshot mode
 29   Timer->timerxctrl.bits.TimerPre = 0;  //Timer Prescale is 1
 30   Timer->timerxctrl.bits.IntEnable = 1; //Interrupt Enable
 31
 32   uint32_t interval_1ms = TIMER_1MZ_INTERVAL / 1000;
 33
 34   Timer->timerxload = interval_1ms;
 35   Timer->timerxctrl.bits.TimerEn = 1;
 36
 37   internal_1ms_counter = 0;
 38
 39   //Register timer interrupt handler
 40   Hal_interrupt_enable(TIMER_INTERRUPT);
 41   Hal_interrupt_register_handler(interrupt_handler, TIMER_INTERRUPT);
 42 }
 43
 44 static void interrupt_handler(void) {
 45   internal_1ms_counter++;
 46   Timer->timerxintclr = 1;
 47 }
 48
 49 uint32_t Hal_timer_get_1ms_counter(void) {
 50   return internal_1ms_counter;
 51 }

1 ms 마다 인터럽트가 발생되게 하기 위해서(interval = 1ms) TimerXLoad 값을 (10241024)/1000 으로 설정했다.
현재 Prescale = 1, TIMCLKENX = 1이므로, Interval = 1 ms 가 되기 위해서
(1 ms
1 MHZ) / (1 * 1) = 1000 이다. 따라서 1000 번의 클럭마다 인터럽트가 발생할 수 있도록 해야 1 ms 의 간격의 가지는 인터럽트가 발생한다.

또한, Interrupt Handler 를 등록시켜, Interrupt 발생때마다 internal_1ms_counter 를 1씩 증가시키면서 하드웨어를 시작한 후 몇 ms 가 흘렀는지 확인할 수 있게 한다.

Delay Function

일단 여기서 먼저 알아야 하는 점은 internal_1ms_counter 가 uint32_t 라는것이다. 즉, 최대값이 0xFFFFFFFF 이고, 카운터가 이를 넘어서면 overflow 되어 다시 0부터 시작하게 된다는것이다. 그러므로 Delay 함수나 이 카운터를 사용하는 함수를 작성할 때 유의하여야 한다.

lib/stdlib.h

  1 #ifndef LIB_STDLIB_H_
  2 #define LIB_STDLIB_H_
  3
  4 void delay(uint32_t ms);
  5
  6 #endif

lib/stdlib.c

  1 #include "stdint.h"
  2 #include "HalTimer.h"
  3
  4 void delay(uint32_t ms) {
  5   uint32_t goal = Hal_timer_get_1ms_counter() + ms;
  6
  7   while(goal != Hal_timer_get_1ms_counter());
  8 }

위에서 작성한 공용 API Hal_timer_get_1ms_counter() 함수를 사용한다.

whlie 문 에서 goal > counter 조건을 사용하면 카운터가 overflow 되었을 경우 정상적인 동작을 수행할 수 없다.
99가 최대치라면, goal = 80 + 20 = 1, 1 > 81 (FALSE), while 문 종료.

0개의 댓글