RealViewPB에는 SP804 타이머 내장하며, 카운터가 내려가는 식

 

 

먼저 SP804 하드웨어 레지스터 구조체로 추상화

 

hal/rvpb/Timer.h

 

Timer_t의

timerxload는 카운터 목표값

timerxvalue는 감소 레지스터

타이머 on 시 timerxvalue에 timerxload 값을 복사 후 timerxvalue가 감소 0이 되면 인터럽트 발생

timermode는 timerxload사용할지말지 결정

timerpre는 클럭마다 얼마나 줄일지 결정

timerxcontrol은 타이머 hw 속성 설정

 

timerxintclr인터럽트 처리 완료를 hw에 알리는 레지스터

 

프리러닝모드 timerxvalue 최댓값에서 0까지 내려가면 인터럽트 발생하는 모드

피리오딕모드 timerxload에 지정한값부터 0까지 내려가면 인터럽트 발생

 

 

#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 간단하게 정의하고

hal/HalTimer.h

#ifndef HAL_HALTIMER_H_
#define HaL_HALTIMER_H_

void	Hal_timer_init(void);

#endif

 

hal/rvpb/Timer.c

타이머 hal 구현하면

 

timeren=0 타이머 끄고

프리러닝 모드 설정 timermode=0, oneshot=0

16비트 카운터 모드 설정 timersize==0

분주율 1 timerpre=0

인터럽트 켜기 intenable = 1

로드 레지스터 키기

카운터레지스터는 0xffffffff

#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.OneShot=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;
}

 

realviewpb는 클럭 소스로 1mhz나 32.768khz 사용

SYSCTRL0 레지스터로 타이머클럭소스 확인 가능. 주소는 0x10001000

클럭 확인하도록 main.c 수정하면..

 

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

#include "HalInterrupt.h"
#include "HalUart.h"
#include "stdio.h"

static void Hw_init(void);
static void	Printf_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();

	while(true);
}

static void Hw_init(void)
{
	Hal_interrupt_init();
	Hal_uart_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);
}

 

시간지연함수 delay를 쓰기위해 타이머 카운터를 이용

 

stdlib.h에 delay 함수 작성

 

lib/stdlib.h

#ifndef LIB_STDLIB_H_
#define LIB_STDLIB_h_

void delay(uint32_t ms);

#endif

 

hal/HalTimer.h

타이머 카운터 변수값을 반환하는

hal_timer_get_1ms_counter 선언

#ifndef HAL_HALTIMER_H_
#define HaL_HALTIMER_H_

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

#endif

 

 

hal/rvpb/Timer.c에 위 함수 구현

#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;
}

 

lib/stdlib.c 에서

delay 함수 구현

#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());
}

 

 

boot/Main.c에 테스트 코드 작성하는데

책에는 좀 빠진 부분이 있다.

타이머 초기화나 타이머 테스트를 메인함수에 넣는 내용

 

#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);
	}
}

 

 

위 코드로 빌드하면 잘 동작한다.

 

 

 

현재 소스 트리

 

+ Recent posts