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/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 가 흘렀는지 확인할 수 있게 한다.
일단 여기서 먼저 알아야 하는 점은 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 문 종료.