PIT 컨트롤러 초기화에 관련된 매크로와 함수
#define PIT_FREQUENCY 1193180
#define MSTOCOUNT( X ) ( PIT_FREQUENCY * ( X ) / 1000 )
#define USTOCOUNT( X ) ( PIT_FREQUENCY * ( X ) / 1000000 )
// I/O 포트
#define PIT_PORT_CONTROL 0x43
#define PIT_PORT_CONTER0 0x40
// 모드
#define PIT_CONTROL_COUNTER0 0x00
#define PIT_CONTROL_LSBMSBRW 0x30
#define PIT_CONTROL_LATCH 0x00
#define PIT_CONTROL_MODE0 0x00
#define PIT_CONTROL_MODE2 0x04
#define PIT_CONTROL_BINARYCOUNTER 0x00
#define PIT_CONTROL_BCDCOUNTER 0x01
#define PIT_COUNTER0_ONCE ( PIT_CONTROL_COUNTER0 | PIT_CONTROL_LSBMSBRW | \\
PIT_CONTROL_MODE0 | PIT_CONTROL_BINARYCOUNTER )
#define PIT_COUNTER0_PERIODIC ( PIT_CONTROL_COUNTER0 | PIT_CONTROL_LSBSBRW | \\
PIT_CONTROL_MODE2 | PIT_CONTROL_BINARYCOUNTER)
// 초기화 함수
void kInitializePIT( WORD wCount, BOOL bPeriodic )
{
// PIT 컨트롤 레지스터(포트 0x43)에 값을 초기화하여 카운트를 먼춘 뒤에
// 모드 0에 바이너리 카운터로 설정
kOutPortByte( PIT_PORT_CONTROL, PIT_COUNTER0_ONCE );
// 만약 일정한 주기로 반복하는 타이머라면, 모드 2로 설정
if( bPeriodic == TRUE )
{
// PIT 컨트롤 레지스터(포트 0x43)에 모드 2에 바이너리 카운터로 설정
kOutPortByte( PIT_PORT_CONTROL, PIT_COUNTER0_PERIODIC );
}
// 카운터 0(포트 0x40)에 LSB -> MSB 순으로 카운터 초기 값을 설정
kOutPortByte( PIT_PORT_CONTER0, wCount );
kOutPortByte( PIT_PORT_CONTER0, wCount >> 8 );
}
PIT 컨트롤러 초기화 예
// 1ms 마다, 주기적으로 인터럽트가 발생하도록 설정하는 예
kInitializePIT( MSTOCOUNT( 1 ), TRUE );
// 100us 후에 인터럽트가 발생하도록 설정하는 예
kInitializePIT( USTOCOUNT( 100 ), FALSE );
PIT 컨트롤러를 통해, 시간을 측정하는 코드
// 카운터 0의 현재 값을 반환
WORD kReadCounteer0 ( void )
{
BYTE bHighByte, bLowByte;
WORD wTemp = 0;
// PIT 컨트롤 레지스터(포트 0x43)에 래치 커맨드를 전송하여, 카운터 0에 있는 현재 값을 읽음
kOutPortByte( PIT_PORT_CONTROL, PIT_COUNTER0_LATCH );
// 카운터 0(포트 0x40)에서 LSB -> MSB 순으로 카운터 값을 읽음
bLowByte = kInPortByte( PIT_PORT_COUNTER0 );
bHighByte = kInPortByte( PIT_PORT_COUNTER0 );
// 읽은 값을 16비트로 합하여 반환
wTemp = bhighByte;
wTemp = ( wTemp << 8 ) | bLowByte;
return wTemp;
}
// 카운터 0을 직접 설정하여, 일정 시간 이상 대기
// 함수를 호출하면, PIT 컨트롤러의 설정이 바뀌므로, 이후에 PIT 컨트롤러를 재설정해야 함
// 정확하게 측정하려면 함수 사용 전에 인터럽트를 비활성화 하는 것이 좋음
// 약 50ms 까지 측정 가능
void kWaitUsingDirectPIT( WORD wCount )
{
WORD wLastCounter0;
WORD wCurrentCounter0;
// PIT 컨트롤러를 0 ~ 0xFFFF까지 반복해서 카운터하도록 설정
kInitializePIT( 0, TRUE );
// 지금부터 wCount 이상 증가할 때까지 대기
wLastCounter0 = kReadCounter0();
while( 1 )
{
// 현재 카운터의 0의 값을 반환
wCurrentCounter0 - kReadCounter0();
if( ( (wLastCounter0 - wCUrrentCounter0) & 0xFFFF ) >= wCount )
{
break;
}
}
}
kWaitUsingDirectPIT( ) 함수의 사용 예
void kWaitms( long lMillisecond )
{
int i;
// 30ms 단위로 나누어서 수행
for( i = 0; i < lMillisencond / 30 ; i++ )
{
kWaitUsingDirectPIT( MSTOCOUNT( 30 ) );
}
kWaitUsingDirectPIT( MSTOCOUNT( lMillisecound % 30 ) );
}
타임 스탬프 카운터를 읽는 어셈블리어 함수와 함수 선언 코드
; 타임 스탬프 카운터를 읽어서 반환
; PARAM: 없음
kReadTSC:
push rdx ; RDX 레지스터를 스택에 저장
rdtsc ; 타임 스탬프 카운터를 읽어서 RDX:RAX에 저장
shl rdx, 32 ; 하위 32비트 TSC 값을 OR하여, RAX 레지스터에 64비트
; TSC 값을 저장
pop rdx
ret
// 함수 선언
QWORD kReadTSC( void );
CMOS 메모리부터 현재 일자와 시간을 읽는 코드
// I/O 포트
#define RTC_CMOSADDRESS 0x70
#define RTC_CMOSDATA 0x71
// CMOS 메모리 어드레스
#define RTC_ADDRESS_SECOND 0X00
#define RTC_ADDRESS_MINUTE 0x02
#define RTC_ADDRESS_HOUR 0x04
#define RTC_ADDRESS_DAYOFWEEK 0x06
#define RTC_ADDRESS_DAYOFMONTH 0x07
#define RTC_ADDRESS_MONTH 0x08
#define RTC_ADDRESS_YEAR 0x09
// BCD 포맷을 Binary로 변환하는 매크로
##define RTC_BCDTOBINARY( X ) (((( X ) >> 4 ) * 10 ) + (( X ) & 0x0F ) )
// CMOS 메모리에서 RTC 컨트롤러가 저장한 현재 시간을 읽음
void kReadRTCTime( BYTE* pbHour, BYTE* pbMinute, BYTE* pbSecond)
{
BYTE bData;
// CMOS 메모리 어드레스 레지스터(포트 0x70)에 시간을 저장하는 레지스터 지정
kOutPortByte( RTC_CMOSADDRESS, RTC_ADDRESS_HOUR );
// CMOS 데이터 레지스터(포트 0x71)에서 시간을 읽음
bDATA = kInPortByte( RTC_CMOSDATA );
*pbHour = RTC_BCDTOBINARY( bData );
// CMOS 메모리 어드레스 레지스터(포트 0x70)에 분을 저장하는 레지스터 지정
kOutPortByte( RTC_CMOSADDRESS, RTC_ADDRESS_MINUTE );
// CMOS 데이터 레지스터(포트 0x71)에서 분을 읽음
bDATA = kInPortByte( RTC_CMOSDATA );
*pbMinute = RTC_BCDTOBINARY( bData );
// CMOS 메모리 어드레스 레지스터(포트 0x70)에 초를 저장하는 레지스터 지정
kOutPortByte( RTC_CMOSADDRESS, RTC_ADDRESS_SECOND );
// CMOS 데이터 레지스터(포트 0x71)에서 시간을 읽음
bDATA = kInPortByte( RTC_CMOSDATA );
*pbHour = RTC_BCDTOBINARY( bData );
}
// CMOS 메모리에서 RTC 컨트롤러가 저장한 현재 일자 읽음
void kReadRTCDate( WORD* pwYear, BYTE* pbMonth, BYTE* pbDAyOfMonth,
BYTE* pbDayOfWeek )
{
BYTE bData;
// CMOS 메모리 어드레스 레지스터(포트 0x70)에 연도를 저장하는 레지스터 지정
kOutPortByte( RTC_CMOSADDESS, RTC_ADDRESS_YEAR );
// CMOS 데이터 레지스터(포트 0x71)에서 연도를 읽음
bData = kInPortByte( RTC_CMOSDATA );
*pwYear = RTC_BCDTOBINARY( bData ) + 2000;
// CMOS 메모리 어드레스 레지스터(포트 0x70)에 월을 저장하는 레지스터 지정
kOutPortByte( RTC_CMOSADDESS, RTC_ADDRESS_MONTH );
// CMOS 데이터 레지스터(포트 0x71)에서 연도를 읽음
bData = kInPortByte( RTC_CMOSDATA );
*pwMonth = RTC_BCDTOBINARY( bData );
// CMOS 메모리 어드레스 레지스터(포트 0x70)에 일를 저장하는 레지스터 지정
kOutPortByte( RTC_CMOSADDESS, RTC_ADDRESS_DAYOFMONTH );
// CMOS 데이터 레지스터(포트 0x71)에서 연도를 읽음
bData = kInPortByte( RTC_CMOSDATA );
*pbDayOfMonth = RTC_BCDTOBINARY( bData );
// CMOS 메모리 어드레스 레지스터(포트 0x70)에 요일을 저장하는 레지스터 지정
kOutPortByte( RTC_CMOSADDESS, RTC_ADDRESS_DAYOFWEEK );
// CMOS 데이터 레지스터(포트 0x71)에서 연도를 읽음
bData = kInPortByte( RTC_CMOSDATA );
*pbDayOfWeek = RTC_BCDTOBINARY( bData );
}
정수로 저장된 RTC의 요일 값을 문자열로 변환하는 코드
char* kConvertDayOfWeekToString( BYTE bDayOfWeek )
{
static char* vpcDayOfWeekString[ 8 ] = { "Error", "Sunday", "Monday",
"Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
// 요일 범위가 넘어가면 에러를 반환
if( bDayOfWeek >= 8 )
{
return vpcDayOfWeekString[ 0 ];
}
// 요일을 반환
return vpcDayOfWeekString[ bDayOfWeek ];
}
RTC 관련 함수의 사용 예
void kShowDateAndTime( const char* pcParmaeterBuffer )
{
BYTE bSecond, bMinute, bHour;
BYTE bDayOfWeek, bDayOfMonth, bMonth;
WORD wYear;
// RTC 컨트롤러에서 시간과 일자를 읽음
kReadRTCTime( &bHour, &bMinute, &bSecond );
kReadRTCDate( &wYear, &bMonth, &bDayOfMonth, &bDayOfWeek );
kPrintf( "Date: %d/%d/%d %s, ", wYear, bMonth, bDayOfMonth,
kConvertDayOfWeekToString( bDayOfWeek ) );
kPrintf( "Time: %d:%d:%d\\n", bHour, bMinute, bSecond );
}
컨트롤러를 제어하는 함수와 관련 매크로를 정의한다.
(2.Kernel64/Source) 디렉터리에 작성한다.
PIT.c
#include "PIT.h"
// PIT를 초기화
void kInitializePIT( WORD wCount, BOOL bPeriodic )
{
// PIT 컨트롤 레지스터(포트 0x43)에 값을 초기화하여, 카운트를 멈춘 뒤에
// 모드 0에 바이너리 카운터로 설정
kOutPortByte( PIT_PORT_CONTROL, PIT_COUNTER0_ONCE );
// 만약 일정한 주기로 반복하는 타이머라면, 모드 2로 설정
if( bPeriodic == TRUE )
{
// PIT 컨트롤 레지스터(포트 0x43)에 모드 2에 바이너리 카운터로 설정
kOutPortByte( PIT_PORT_CONTROL, PIT_COUNTER0_PERIODIC );
}
// 카운터 0(포트 0x40)에 LSB -> MSB 순으로 카운터 초기 값을 설정
kOutPortByte( PIT_PORT_CONTER0, wCount );
kOutPortByte( PIT_PORT_COUNTER0, wCount >> 8 );
}
// 카운터 0의 현재 값을 반환
WORD kReadCounter0( void )
{
BYTE bHighByte, bLowByte;
WORD wTemp = 0;
// PIT 컨트롤 레지스터(포트 0x43)에 래치 커맨드를 전송하여 카운터 0에 있는
// 현재 값을 읽음
kOutPortByte( PIT_PORT_CONTROL, PIT_COUNTER0_LATCH );
// 카운터 0(포트 0x40)에서 LSB -> MSB 순으로 카운터 값을 읽음
bLowByte = kInPortByte( PIT_PORT_COUNTER0 );
bHighByte = kInPortByte( PIT_PORT_COUNTER0 );
// 읽은 값을 16비트로 합하여 반환
wTemp = bHighByte;
wTemp = ( wTemp << 8 ) | bLowByte;
return wTemp;
}
// 카운터 0를 직접 설정하여 일정 시간 이상 대기
// 함수를 호출하면 PIT 컨트롤러의 설정이 바뀌므로, 이후에 PIT 컨트롤러를 재설정해야함
// 정확하게 측정하려면 함수 사용 전에 인터럽트를 비활성화 하는 것이 좋음
// 약 50ms까지 측정 가능
void kWaitUsingDirectPIT( WORD wCount )
{
WORD wLastCounter0;
WORD wCurrentCounter0;
// PIT 컨트롤러를 0~0xFFFF까지 반복해서 카운팅하도록 설정
kInitializePIT( 0, TRUE );
// 지금부터 wCount 이상 증가할 때까지 대기
wLastCounter0 = kReadCounter0();
while( 1 )
{
// 현재 카운터 0의 값을 반환
wCurrentCounter0 = kReadCounter0();
if( ( ( wLastCounter0 - wCurrentCounter0 ) & 0xFFFF ) >= wCount )
{
break;
}
}
}
PIT.h
#ifndef __PIT_H__
#define __PIT_H__
#include "Types.h"
// 매크로
#define PIT_FREQUENCY 1193182
#define MSTOCOUNT( x ) ( PIT_FREQUENCY * ( x ) / 1000 )
#define USTOCOUNT( x ) ( PIT_FREQUENCY * ( x ) / 1000000 )
// I/O 포트
#define PIT_PORT_CONTROL 0x43
#define PIT_PORT_COUNTER0 0x40
#define PIT_PORT_COUNTER1 0x41
#define PIT_PORT_COUNTER2 0x42
// 모드
#define PIT_CONTROL_COUNTER0 0x00
#define PIT_CONTROL_COUNTER1 0x40
#define PIT_CONTROL_COUNTER2 0x80
#define PIT_CONTROL_LSBMSBRW 0x30
#define PIT_CONTROL_LATCH 0x00
#define PIT_CONTROL_MODE0 0x00
#define PIT_CONTROL_MODE2 0x04
// Binary or BCD
#define PIT_CONTROL_BINARYCOUNTER 0x00
#define PIT_CONTROL_BCDCOUNTER 0x01
#define PIT_COUNTER0_ONCE ( PIT_CONTROL_COUNTER0 | PIT_CONTROL_LSBMSBRW | \\
PIT_CONTROL_MODE0 | PIT_CONTROL_BINARYCOUNTER )
#define PIT_COUNTER0_PERIODIC ( PIT_CONTROL_COUNTER0 | PIT_CONTROL_LSBMSBRW | \\
PIT_CONTROL_MODE2 | PIT_CONTROL_BINARYCOUNTER)
#define PIT_COUNTER0_LATCH ( PIT_CONTROL_COUNTER0 | PIT_CONTROL_LATCH )
// 함수
void kInitializePIT( WORD wCount, BOOL bPeriodic );
WORD kReadCounter0( void );
void kWaitUsingDirectPIT( WORD wCount );
#endif /*__PIT_H__*/