본문 바로가기

SW 개발 공부/OS 개발 프로젝트

[나빌로스] 7.타이머

모든 작업 순서

  1. 하드웨어 레지스터 구조체 선언 & 주소 define // periperal.h
  2. 레지스터 접근할 인스턴스 선언 // Regs.c
  3. 인스턴스가 구동할 API 선언 // Halperiperal.h
  4. API 정의 // periperal.c + 응용 API 정의 ( stdlib.h/c or stdio.h/c)
  5. Main 에서 동작 추가 정의

 

RealViewPB에는 SP804 타이머 내장됨.

 

SP804 Timer 개요 (RealViewPB)

RealView Platform Baseboard (RealViewPB)에는 SP804 Timer가 포함되어 있으며, 이는 Dual Timer Module로 동작하는 ARM의 범용 타이머 컨트롤러이다.
SP804는 32-bit 다운 카운트 타이머이며, 인터럽트를 발생시킬 수도 있다.

SP804는 **타이머 2개(Timer1, Timer2)**로 구성되어 있으며, 각각 독립적으로 동작하거나 연동할 수 있다.


SP804 Timer 레지스터 설명

SP804에는 여러 개의 제어 및 상태 레지스터가 존재하며, 각각의 역할은 다음과 같다.

Offset 레지스터 이름  설명
0x00 Timer1Load Timer1에 초기 카운트 값을 설정
0x04 Timer1Value Timer1의 현재 카운트 값 읽기
0x08 Timer1Control Timer1의 설정 (모드, 인터럽트, Enable 등)
0x0C Timer1IntClr Timer1 인터럽트 클리어
0x10 Timer1RIS Timer1 Raw Interrupt Status
0x14 Timer1MIS Timer1 Masked Interrupt Status
0x18 Timer1BGLoad Timer1 Background Load 값 설정
0x20 Timer2Load Timer2에 초기 카운트 값을 설정
0x24 Timer2Value Timer2의 현재 카운트 값 읽기
0x28 Timer2Control Timer2의 설정 (모드, 인터럽트, Enable 등)
0x2C Timer2IntClr Timer2 인터럽트 클리어
0x30 Timer2RIS Timer2 Raw Interrupt Status
0x34 Timer2MIS Timer2 Masked Interrupt Status
0x38 Timer2BGLoad Timer2 Background Load 값 설정

💡 주요 레지스터 기능

  • Load Register: 타이머 초기값을 설정
  • Value Register: 현재 타이머 값 읽기
  • Control Register: 타이머 설정 (Enable, Mode, Prescaler 등)
  • IntClr Register: 인터럽트 클리어
  • RIS/MIS Register: 인터럽트 상태 확인
  • BGLoad Register: Reload 값 설정 (Auto-reload mode 사용 시)

 

그렇다면 이제 설정을 위한 timerxcontrol register에 대해 알아보자

 

SP804 타이머의 TimerxControl 레지스터 설명

SP804 타이머에서 TimerxControl 레지스터는 타이머의 동작을 설정하는 제어 레지스터다.
각 비트가 타이머의 동작 모드, 인터럽트 설정, 클럭 소스 등을 제어하는 역할을 한다.


TimerxControl 레지스터 비트 구성

TimerxControl은 32비트 레지스터이지만, 실제로 사용되는 비트는 일부다.

비트 이름  설명
0 OneShot 1: 원샷 모드 (한 번 실행 후 정지)
0: 반복 모드 (주기적으로 실행)
1 TimerSize 1: 32비트 타이머
0: 16비트 타이머
2 TimerPrescale 00: 프리스케일러 없음
01: 클럭을 16으로 나눔
10: 클럭을 256으로 나눔
3 Reserved 예약됨 (사용 안 함)
4 InterruptEnable 1: 인터럽트 활성화
0: 인터럽트 비활성화
5 TimerMode 1: 32비트 프리러닝 모드 (Free-running)
0: 랩 모드 (Wrap-around)
6 TimerEnable 1: 타이머 시작
0: 타이머 정지
7 TimerDebugMode 1: 디버그 모드에서 동작 유지
0: 디버그 모드에서 정지
8~31 Reserved 예약됨 (사용 안 함)

 

RealViewPB에서는 클럭 소스로 1mhz나 32.768khz를 사용한다.

현재 예제에서는 1mhz를 클럭소스로 사용한다.

 

위의 정의와 같이 우선 레지스터 구조체를 정의한다.

#ifndef HAL_RVPB_TIMER_H_
#define HAL_RVPB_TIMER_H_

typedef union TimerXControl_t
{
    uint32_t all;
    struct {
        uint32_t OneShot:1;     //0
        uint32_t TimerSize:1;   //1
        uint32_t TimerPre:2;    //3:2
        uint32_t Reserved0:1;   //4
        uint32_t IntEnable:1;   //5
        uint32_t TimerMode:1;   //6
        uint32_t TimerEn:1;     //7
        uint32_t Reserved1:24;  //31:8
    } bits;
} TimerXControl_t;

typedef union TimerXRIS_t
{
    uint32_t all;
    struct {
        uint32_t TimerXRIS:1;   //0
        uint32_t Reserved:31;   //31:1
    } bits;
} TimerXRIS_t;

typedef union TimerXMIS_t
{
    uint32_t all;
    struct {
        uint32_t TimerXMIS:1;   //0
        uint32_t Reserved:31;   //31:1
    } bits;
} TimerXMIS_t;

typedef struct Timer_t
{
    uint32_t        timerxload;     // 0x00
    uint32_t        timerxvalue;    // 0x04
    TimerXControl_t timerxcontrol;  // 0x08
    uint32_t        timerxintclr;   // 0x0C
    TimerXRIS_t     timerxris;      // 0x10
    TimerXMIS_t     timerxmis;      // 0x14
    uint32_t        timerxbgload;   // 0x18
} Timer_t;

#define TIMER_CPU_BASE  0x10011000
#define TIMER_INTERRUPT 36

#define TIMER_FREERUNNING   0
#define TIMER_PERIOIC       1

#define TIMER_16BIT_COUNTER 0
#define TIMER_32BIT_COUNTER 1

#define TIMER_1MZ_INTERVAL       (1024 * 1024)

#endif /* HAL_RVPB_TIMER_H_ */

 

 

이후 Regs.c 에서 타이머 인스턴스를 선언한다.

#include <stdint.h>
#include "Uart.h"
#include "Interrupt.h"
#include "Timer.h"

volatile PL011_t*	Uart	= (PL011_t*)UART_BASE_ADDRESS0;
volatile GicCput_t*	GicCpu	= (GicCput_t*)GIC_CPU_BASE;
volatile GicDist_t*	GicDist	= (GicDist_t*)GIC_DIST_BASE;
volatile Timer_t*	Timer	= (Timer_t*)TIMER_CPU_BASE;

 

 

해당 인스턴스가 활용 될 수 있는 API를 선언하고 정의한다.

#ifndef HAL_HALTIMER_H_
#define HaL_HALTIMER_H_

void		Hal_timer_init(void);
uint32_t	Hal_timer_get_1ms_counter(void);

#endif

 

#include <stdint.h>
#include "Timer.h"
#include "HalTimer.h"
#include "HalInterrupt.h"

extern volatile Timer_t* Timer;

static void interrupt_handler(void);

static uint32_t internal_1ms_counter;

void Hal_timer_init(void)
{
	//interface reset
	Timer->timerxcontrol.bits.TimerEn=0;
	Timer->timerxcontrol.bits.TimerMode=0;
	Timer->timerxcontrol.bits.OneShot=0;
	Timer->timerxcontrol.bits.TimerSize=0;
	Timer->timerxcontrol.bits.TimerPre=0;
	Timer->timerxcontrol.bits.IntEnable=1;
	Timer->timerxload=0;
	Timer->timerxvalue=0xFFFFFFFF;

	//set periodic mode
	Timer->timerxcontrol.bits.TimerMode=TIMER_PERIOIC;
	Timer->timerxcontrol.bits.TimerSize=TIMER_32BIT_COUNTER;
	Timer->timerxcontrol.bits.OneShow=0;
	Timer->timerxcontrol.bits.TimerPre=0;
	Timer->timerxcontrol.bits.IntEnable=1;

	uint32_t interval_1ms = TIMER_1MZ_INTERVAL / 1000;

	Timer->timerxload = interval_1ms;
	Timer->timerxcontrol.bits.TimerEn=1;

	internal_1ms_counter=0;

	//Register Timer interrupt handler
	Hal_interrupt_enable(TIMER_INTERRUPT);
	Hal_interrupt_register_handler(interrupt_handler, TIMER_INTERRUPT);
}

static void interrupt_handler(void)
{
	internal_1ms_counter++;

	Timer->timerxintclr=1;
}

uint32_t Hal_timer_get_1ms_counter(void)
{
	return internal_1ms_counter;
}

 

또한 해당 API 를 활용한 응용 API를 선언/정의한다.

#ifndef LIB_STDLIB_H_
#define LIB_STDLIB_h_

void delay(uint32_t ms);

#endif
#include <stdint.h>
#include <stdbool.h>
#include "HalTimer.h"

void delay(uint32_t ms)
{
	uint32_t goal = Hal_timer_get_1ms_counter() + ms;

	while(goal != Hal_timer_get_1ms_counter());
}

 

이후 Main 함수에서 응용 API인 delay를 사용 할 수 있다.

#include <stdint.h>
#include <stdbool.h>

#include "HalInterrupt.h"
#include "HalUart.h"
#include "HalTimer.h"

#include "stdio.h"
#include "stdlib.h"

static void Hw_init(void);
static void	Printf_test(void);
static void Timer_test(void);

void main(void)
{
	Hw_init();

	uint32_t i = 100;
	while(i--)
	{
		Hal_uart_put_char('N');
	}
	Hal_uart_put_char('\n');
	putstr("Hello World!\n");

	Printf_test();
	Timer_test();
}

static void Hw_init(void)
{
	Hal_interrupt_init();
	Hal_uart_init();
	Hal_timer_init();
}

static void Printf_test(void)
{
	char* str = "printf pointer test";
	char* nullptr = 0;
	uint32_t i = 5;
	uint32_t* sysctrl0 = (uint32_t*)0x10001000;

	debug_printf("%s\b", "Hello printf");
	debug_printf("output string pointer: %s\n", str);
	debug_printf("%s is null pointer, %u number\n", nullptr, 10);
	debug_printf("%u=5\n", i);
	debug_printf("dec=%u hex=%x\n", 0xff, 0xff);
	debug_printf("print zero %u\n", 0);
	debug_printf("SYSCTRL0 %x\n", *sysctrl0);

}

static void Timer_test(void)
{
	while(true)
	{
		debug_printf("current count : %u\n", Hal_timer_get_1ms_counter());
		delay(1000);
	}
}

 

결과는 아래와 같이 출력된다.