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);
}
}
위 코드로 빌드하면 잘 동작한다.
현재 소스 트리
'컴퓨터과학 > os' 카테고리의 다른 글
navilos - 13. 라운드로빈 스캐줄러 (0) | 2022.08.22 |
---|---|
navilos - 12. 태스크 (0) | 2022.08.22 |
navilos - 10. UART 키보드 입력 인터럽트 (0) | 2022.08.21 |
나빌로스 하다가 찾은 문제 (0) | 2022.08.20 |
navilos - 9. UART 3 printf 구현하기 (0) | 2022.08.20 |