8254 프로그래머블 인터벌 타이머(programmable interval timer) 사용법
1999년 7월 24일
이신(tactlee@realtime.soongsil.ac.kr, tactlee@hitel.net)
머리말
이 문서는 인텔사의 8254 프로그래머블 인터벌 타이머의 사용법을 PC 프로그래머의 관점에서 기술하였다.
때문에 자세한 하드웨어 구조에 대해서는 언급하지 않았다.
이 문서에 포함되어있지 않은 자세한 사항은 문서 끝부분의 참고도서목록을 참고하기 바란다.
차례
1 개요
2 카운터의 입출력 핀 구성
3 카운터의 동작 모드?4 카운트 값과 동작 모드의 설정
5 예제 프로그램
6 참고도서목록
1 개요
8254 타이머는 독립적으로 동작하는 세 개의 카운터를 갖고 있다.
각 카운터에는 카운트 값과 동작 모드를 지정할 수 있다.
카운터에 카운트 값을 저장하면, 카운터는 매 클럭 신호마다 카운트 값을 1 씩 감소시킨다.
카운트 값이 0이 되기 전까지는 8254의 출력 핀에서 0이 출력된다.
카운트 값이 0이 되면, 카운터의 출력 핀에서 1이 출력된다.
따라서 카운트 값을 변경하면 원하는 시간 간격으로 신호를 발생시킬 수 있다.
또한 출력 핀을 인터럽트 발생기에 연결하면 원하는 시간 간격으로 인터럽트를 발생시킬 수도 있다.
PC에서는 8254 타이머의 각 카운터를 특정 용도로 사용하고 있다[3].
예를 들어 카운터 0은 시스템에 시간을 기록하는 일을 하고 있고, 카운터 1은 메모리 재충전(refresh)을 위해 사용하고 있다.
재충전 시간 간격은 사용되는 메모리의 종류에 따라 달라지는데,
최초의 IBM AT에서 사용된 메모리는 약 50 nsec(nano second; 10만분의 일초)마다 한 번씩 재충전 해야 했다.
따라서 카운터 1은 50 nsec마다 한 번씩 메모리 재충전 신호를 발생하도록 프로그래밍되어있다.
그리고 카운터 2는 스피커와 연결되어 소리를 만드는데 이용된다.
2 카운터의 입출력 핀 구성
8254에는 세 개의 독립적인 카운터가 있고, 각 카운터는 다음과 같은 입출력 핀들로 이루어져있다.
(1) 클럭 신호 입력 핀
클럭 신호가 입력되는 핀으로 PC의 경우 1193180 Hz의 클럭 신호가 입력된다.
카운터는 매 클럭 신호마타 카운트 값을 1 씩 감소시킨다.
(2) 게이트 입력 핀
카운터의 정지 및 시작 기능으로 사용한다.
보통 게이트 입력 핀으로 0을 입력하면 카운터가 정지하고, 1이 입력하면 다시 시작한다.
(3) 출력 핀
카운트 값이 0이 되면, 카운터는 출력 핀으로 1을 출력한다.
출력 핀은 인터럽터 요청 신호로 쓰이거나,
더 큰 시간 간격을 얻기 위해 다른 카운터의 클럭 신호 입력 핀으로 연결하기도 한다.
PC에서는 카운터 2의 출력 핀을 스피커에 연결하여 소리를 발생시키는데 이용하고 있다.
3 카운터의 동작 모드?
8254 타이머는 다음과 같이 6 가지 동작 모드를 갖고 있다.
모드 0 - interrupt on terminal count
모드 1 - hardware retrigerable one-shot
모드 2 - rate generator
모드 3 - square wave mode
모드 4 - software triggered strobe
모드 5 - hardware triggered strobe (retriggerable)
3.1 모드 0
카운트 값 설정 후, 카운트 값은 매 클럭 신호마다 1 씩 감소한다.
이동안 출력 핀으로는 0이 출력된다.
카운트 값이 0이 되면, 출력 핀으로 1이 출력된다.
출력 핀의 값은 모드를 변경하거나 카운트 값을 다시 설정할 때까지 그대로 유지된다.
보통 출력 핀은 인터럽트 요청 핀에 연결되어있어 인터럽트가 발생하게 된다.
게이트 입력 핀의 값이 0이되면 카운트를 잠시 멈추었다가, 게이트 입력 핀의 값이 1이되면 계속해서 카운트 한다.
예를 들어 카운트 값이 4에서 0으로 감소하고 있다고 하자.
현재 카운트 값이 3일 때, 게이트 입력 핀으로 0이 입력되면 카운트는 잠시 중지되어 카운트 값은 계속 3으로 남아 있게 된다.
게이트 입력 핀으로 1이 들어오면 카운트는 3부터 다시 감소하기 시작한다.
3.2 모드 1
모드 0과 비슷한 동작을 한다.
모드 1은 모드 0과 단지 게이트 입력 핀에 대한 쓰임새가 다르다.
모드 1에서 게이트의 입력 핀은 방아쇠와 같은 역할을 한다.
게이트 입력 핀의 값이 1이 되면, 초기값부터 다시 카운트 한다.
예를 들어 카운트 값이 4에서 0으로 감소하는 도중에 게이트 입력 핀으로 0이 들어오면,
카운트 값이 0이 되더라도 출력 핀으로는 1이 출력되지 않는다.
게이트 입력 핀으로 들어오는 값이 0에서 1로 바뀌면, 카운터는 카운트 값 다시 4로 설정하고 카운트 한다.
3.3 모드 2
카운트 값을 N으로 설정하면, 출력 핀으로 1이 N-1 번 출력되고 0이 한 번 출력된다.
예를 들어 카운트 값을 4로 설정하면, 출력 핀으로 1이 세 번 출력되고 0이 한 번 출력된다.
카운트 값이 0이 된 후에는 자동적으로 초기 카운트 값으로 다시 설정된다.
게이트 입력 핀은 리셋 기능을 갖고 있다.
게이트 입력 핀으로 0이 들어오면, 출력 핀으로는 1이 출력되고 카운트는 정지한다.
게이트 입력 핀으로 들어오는 값이 0에서 1로 바뀌면, 초기값부터 다시 카운트 한다.
3.4 모드 3
설정된 카운트 값에 따라 출력 핀으로 사각파(square wave)를 만든다.
예를 들어 카운드 값을 4로 설정하면, 출력 핀으로 두 번은 1이 출력되고 두 번은 0이 출력된다.
또한 카운트 값을 5로 설정하면, 출력 핀으로 세 번은 1이 출력되고 두 번은 0이 출력된다.
카운트 값이 0이 된 후에는 자동적으로 초기 카운트 값으로 다시 설정된다.
그러므로 한 번 설정한 후에는 영원히 출력 핀으로 사각파를 출력한다.
게이트 입력 핀의 기능은 모드 2와 같다.
3.5 모드 4
모드 지정후 출력 핀으로는 1이 출력된다.
카운트 값을 설정한 후부터 카운트가 시작된다.
카운트 값이 0이되는 시점에 출력 핀으로 0이 출력되고 다음 클럭 신호부터는 1이 출력된다.
즉 카운트 하는 동안 출력 핀으로 1을 출력하다가 카운트 값이 0이 되는 한 클럭 주기 동안만 0을 출력한다.
게이트 입력 핀의 기능은 모드 2와 같다.
3.6 모드 5
모드 지정후 출력 핀으로는 1이 출력된다.
카운트 값 설정 후, 게이트 입력 핀의 값이 0에서 1로 바뀌면 카운트를 시작한다.
카운트 값이 0이 되면, 출력 핀으로 한 클럭 주기 동안 0을 출력한다.
게이트 입력 핀의 기능은 모드 1과 같다.
4 8254 타이머의 제어
8254 타이머를 제어하기 위해서는 8254 타이머와 연결되어있는 입출력 포트에 어떤 값을 써 넣거나 읽어와야 한다.
PC에서 8254 타이머에 대한 입출력 포트 주소는 다음과 같다.
40h - 카운터 0의 카운트 값을 읽거나 쓰는 곳
41h - 카운터 1의 카운트 값을 읽거나 쓰는 곳
42h - 카운터 2의 카운트 값을 읽거나 쓰는 곳
43h - 제어 명령어 레지스터, 즉 8254 타이머에게 전달할 제어 명령어를 기록하는 곳
4.1 제어 명령어 전달하기
제어 명령어의 크기는 한 바이트로 다음과 같은 형식을 갖고?있다.
비트 위치: b7 b6 b5 b4 b3 b2 b1 b0
비트 내용: CS1 CS0 RW1 RW0 MD2 MD1 MD0 BCD
제어 명령어의 각 비트는 다음과 같은 의미를 갖고 있다.
(1) CS (counter select)
CS1, CS0 두 비트로 읽기 또는 쓰기가 이루어지는 카운터를 선택한다.
CS1, CS0 두 비트의 조합이 나타내는 값의 의미는 다음과 같다.
00 - 카운터 0의 카운트 값에 대한 읽기 또는 쓰기를 선택
01 - 카운터 1의 카운트 값에 대한 읽기 또는 쓰기를 선택
10 - 카운터 2의 카운트 값에 대한 읽기 또는 쓰기를 선택
11 - 리드백(read-back) 명령어, 4.3.2 참조
(2) RW (read / write)
RW1, RW0 두 비트로 카운터에 읽기 또는 쓰기를 하는 값의 형태를 지정한다.
RW1, RW0 두 비트의 조합이 나타내는 값의 의미는 다음과 같다.
00 - 카운터 래치(counter latch), 4.3.1 참조
01 - 상위 바이트만 읽기 또는 쓰기
10 - 하위 바이트만 읽기 또는 쓰기
11 - 하위 바이트, 상위 바이트 순서로 두 바이트 읽기 또는 쓰기
(3) MD (mode)
MD2, MD1, MD0 세 비트로 카운터의 동작 모드를 선택한다.
MD2, MD1, MD0 세 비트의 조합이 나타내는 값의 의미는 다음과 같다.
000 - 모드 0
001 - 모드 1
010 - 모드 2
011 - 모드 3
100 - 모드 4
101 - 모드 5
(4) BCD (binary count / binary-coded decimal count)
카운트 값의 저장 형식을 이진수로 할 것인지 이진화 십진수로 할 것인지를 지정한다.
BCD 비트가 나타내는 값의 의미는 다음과 같다.
0 - 이진수
1 - 이진화 십진수
제어 명령어 레지스터에 제어 명령어를 써 넣는 것으로 8254 타이머에 제어 명령어를 전달할 수 있다.
예를 들어 제어 명령어가 0B6h 라고 하자.
제어 명령어 레지스터에 제어 명령어를 써 넣는 방법은 다음과 같다.
mov al, 0B6h
out 43h, al
같은 내용을 터보 C에서는 다음과 같은 C 함수를 사용할 수 있다.
outportb(0x43, 0xB6);
4.2 카운트 값 쓰기
카운트 값을 쓰기 위해서는 먼저 제어 명령어로 카운트 값을 써 넣을 카운터를 지정하여 제어 명령어 레지스터에 저장한다.
그리고 카운트 값의 하위 바이트, 상위 바이트 순서로 원하는 카운터의 입출력 포트에 써 넣는다.
예를 들어 제어 명령어 레지스터로 보내는 제어 명령어를 0B6h라 하자.
이 제어 명령어는 카운터 2의 동작 모드를 모드 3으로 설정하고, 카운트 값의 저장 형식을 이진수로 설정하며, 카운트 값은 두 바이트로 저장한다.
다음은 이런 가정하에 카운트 값을 8000h로 초기화하는 예이다.
mov al, 0B6h
out 43h, al
mov ax, 8000h
out 42h, al
out 42h, ah
같은 내용을 터보 C에서는 다음과 같은 C 함수를 사용할 수 있다.
#define COUNT 0x8000
outportb(0x43, 0xB6);
outportb(0x42, COUNT & 0xFF);
outportb(0x42, (COUNT >> 8) && 0xFF);
카운트 값으로 초기화할 수 있는 최대값은 0이다.
이 값은 카운트 값이 이진수인 경우는 65536이 되고, 이진화 십진수의 경우 10000이 된다.
카운트 값으로 초기화할 수 있는 최소값은 1이다.
단 모드 2와 모든 3에서 카운트 값으로 초기화할 수 있는 최소값은 2이다.
한편 모드 0, 1, 4, 5의 경우 카운트 값이 감소하여 0이 되더라도 카운터는 동작을 멈추지 않는다.
즉 카운트 값이 0이 되어 출력 핀으로 1을 출력한 후에도 카운트 값은 매 클럭 신호마다 1 씩 감소한다.
다시 말해서 카운트 값이 0으로 감소한 후 다음 클럭 신호가 들어오면 카운트 값이 1 감소하여,
이진수라면 0FFFFh가 되고 이진화 십진수라면 9999가 된다.
모드 2, 3의 경우 카운터는 카운트 값이 감소하여 0이 되면, 스스로 카운트 값을 초기값으로 설정된다.
4.3 카운트 값 읽기
PC에서 카운터의 현재 카운트 값을 읽어오는 방법에는 카운터 래치 명령어를 사용하는 방법과 리드백 명령어를 사용하는 방법이 있다.
4.3.1 카운터 래치 명령어
제어 명령어의 RW 비트에서 카운터 래치를 선택하여 제어 명령어 레지스터에 쓰면 카운터 래치 명령어가 8254 타이머로 전달된다.
물론 이때 카운트 값을 읽어올 카운터를 CS 비트로 선택해 주어야 한다.
그런 다음 지정한 카운터의 입출력 포트로부터 카운트 값을 읽어오면 된다.
카운터 래치 명령어를 8254 타이머에 전달한 후 카운트 값을 읽어오지 않고 계속 카운터 래치 명령어를 전달하면, 두번째 이후 명령은 무시된다.
카운터 래치 명령어의 형식은 제어 명령어에서 RW 비트를 0으로 만들고 CS 비트로 읽어올 카운터를 선택하면 된다.
나머지 비트들은 어떤 값을 가져도 상관없지만, 0으로 채울 것을 권한다.
다음은 카운터 래치 명령어를 이용해서 카운터 0의 카운트 값을 읽어오는 예이다.
mov al, 0h
out 43h, al
in al, 40h
in ah, 40h
같은 내용을 터보 C에서는 다음과 같은 C 함수를 사용할 수 있다.
union { unsigned int w; unsigned char b[2]; } count;
outportb(0x43, 0);
count.b[0] = (unsigned char) inportb(0x40);
count.b[1] = (unsigned char) inportb(0x40);
4.3.2 리드백 명령어
8254 타이머는 8253 타이머의 기능을 개선하여 만들어졌다.
리드백 명령어는 8254 타이머에 추가된 기능 중 하나다.
때문에 리드백 명령어는 8253 타이머에서 사용할 수 없다.
카운터 래치 명령어는 한 번에 하나의 카운트 값만 읽을 수 있다.
또한 카운터의 상태 정보는 전혀 읽을 수 없다.
리드백 명령어는 이러한 카운터 래치 명령어의 단점을 개선한 명령어이다.
리드백 명령어는 여러 카운터의 카운트 값과 상태 정보를 동시에 읽을 수 있도록 해 준다.
리드백 명령어는 제어 명령어 형식과 많이 다르다.
다음은 리드백 명령어의 형식이다.
비트 위치: b7 b6 b5 b4 b3 b2 b1 b0
비트 내용: 1 1 COUNT STATUS CNT2 CNT1 CNT0 0
제어 명령어의 CS 비트에 해당하는 b7과 b6 비트는 1로 만들고, BCD 비트에 해당하는 b0 비트는 0으로 만든다.
COUNT 비트는 카운트 값을 읽고자 할 경우에는 0으로 만들고, 그렇지 않다면 1로 만든다.
STATUS 비트는 카운터의 상태 정보를 읽고자 할 경우에는 0으로 만들고, 그렇지 않다면 1로 만든다.
CNT2 비트는 카운터 2의 정보(카운트 값 또는 상태 정보)를 읽고자 할 경우에는 1로 만들고, 그렇지 않다면 0으로 만든다.
CNT1 비트는 카운터 1의 정보(카운트 값 또는 상태 정보)를 읽고자 할 경우에는 1로 만들고, 그렇지 않다면 0으로 만든다.
CNT0 비트는 카운터 0의 정보(카운트 값 또는 상태 정보)를 읽고자 할 경우에는 1로 만들고, 그렇지 않다면 0으로 만든다.
한편 리드백 명령어로 읽은 카운터의 상태 정보는 한 바이트로 구성되어 있다.
상태 정보의 b7 비트는 출력 핀의 값이 들어있다.
상태 정보의 b6 비트는 카운트 값을 읽을 수 있는 지 여부를 나타낸다.
b6 비트의 값이 1이라면 카운트 값을 읽을 수 있다는 뜻이다.
상태 정보의 나머지 비트들은 제어 명령어 레지스터에 들어있는 내용이다.
COUNT 비트와 STATUS 비트를 모두 0으로 만들면 카운트 값과 상태 정보를 동시에 읽을 수 있다.
이 경우 첫번째 읽은 바이트는 상태 정보이고, 두번째와 세번째 읽은 바이트는 카운트 값이다.
다음은 리드백 명령어로 카운터 0과 카운터 2의 카운트 값을 읽는 예이다.
mov al, 0DAh
out 43h, al
in al, 40h
in ah, 40h
in bl, 40h
in bh, 40h
같은 내용을 터보 C에서는 다음과 같은 C 함수를 사용할 수 있다.
union { unsigned int w; unsigned char b[2]; } count0, count2;
outportb(0x43, 0xDA);
count0.b[0] = (unsigned char) inportb(0x40);
count0.b[1] = (unsigned char) inportb(0x40);
count2.b[0] = (unsigned char) inportb(0x42);
count2.b[1] = (unsigned char) inportb(0x42);
5 응용 예
PC에서는 8254 타이머의 각 카운터를 이미 특정 용도로 사용하고 있다.
사용자가 쓸 수 있는 카운터는 0번과 2번이다.
카운터 0은 주기적으로 인터럽트를 발생시키도록 프로그래밍되어있다.
카운터 0의 카운트 값은 65536로 초기화되어 있고, 매 클럭 신호마다 1 씩 감소한다.
카운트 값이 0이 되면, 인터럽트(인터럽트 08h, 인터럽트 벡터 주소 020h-023h, IRQ0)를 발생시킨다.
PC는 1193180 Hz의 클럭 신호를 발생시킨다.
즉 초당 1193180 번의 신호가 발생한다.
따라서 초당 약 18.2 (1193180/65536) 번의 인터럽트가 발생한다.
카운터 0의 카운트 값을 65536이 아닌 다른 값으로 초기화한다면, 발생하는 인터럽트의 주기를 바꿀 수 있다.
예를 들어 [2]에서는 카운트 값의 초기값을 11932로 바꾸어 10 msec(milli second, 천분의 일초) 간격으로 인터럽트가 발생하도록 하고 있다.
이러한 기능을 어디에 쓸 수 있을까?
첫번째로 멀티 태스킹 기능을 지원하기 위해 사용할 수 있다.
간단히 생각해서 태스크마다 10 msec 씩 실행하도록 한다면 초당 100 개의 태스크를 실행시킬 수 있다.
즉 인터럽트 서비스 루틴은 10 msec마다 깨어나서 가장 우선순위가 높은 태스크를 찾아 태스크 문맥교환을 한다.
이것이 바로 멀티 태스킹을 지원하는 운영체제의 스케줄러의 기본적인 기능이다.
카운터 2는 스피커와 연결되어있어 소리를 만드는데 사용된다.
사람이 귀로 들을 수 있는 가청 주파수는 20Hz에서 2만Hz 사이이다.
PC의 클럭 신호가 1193180Hz이므로 이론적으로 카운터 2를 통해 만들수 있는 주파수는 0Hz부터 1193180Hz까지이다.
그러므로 카운터 2를 이용하면 사람이 들을 수 있는 소리는 물론 사람이 들을 수 없는 소리도 만들어낼 수 있다.
카운터 2를 이용해서 PC 스피커로 소리를 만들어내는 예는 [3]에 나와있다.
6 참고도서목록
[1] Intel, 8254 Programmable Interval Timer Datasheet, http://support.intel.com/support/controllers/peripheral/231164.htm
[2] Richard A. Burgess, Developing Your Own 32-Bit Operating System, Sams Publishing, 1995.
[3] 유정하, PC 시스템 프로그래밍, 정보문화사, 1991.
[4] 차영배, IBM PC 인터페이스 응용, 동일출판사, 1999.
1999년 7월 24일
이신(tactlee@realtime.soongsil.ac.kr, tactlee@hitel.net)
머리말
이 문서는 인텔사의 8254 프로그래머블 인터벌 타이머의 사용법을 PC 프로그래머의 관점에서 기술하였다.
때문에 자세한 하드웨어 구조에 대해서는 언급하지 않았다.
이 문서에 포함되어있지 않은 자세한 사항은 문서 끝부분의 참고도서목록을 참고하기 바란다.
차례
1 개요
2 카운터의 입출력 핀 구성
3 카운터의 동작 모드?4 카운트 값과 동작 모드의 설정
5 예제 프로그램
6 참고도서목록
1 개요
8254 타이머는 독립적으로 동작하는 세 개의 카운터를 갖고 있다.
각 카운터에는 카운트 값과 동작 모드를 지정할 수 있다.
카운터에 카운트 값을 저장하면, 카운터는 매 클럭 신호마다 카운트 값을 1 씩 감소시킨다.
카운트 값이 0이 되기 전까지는 8254의 출력 핀에서 0이 출력된다.
카운트 값이 0이 되면, 카운터의 출력 핀에서 1이 출력된다.
따라서 카운트 값을 변경하면 원하는 시간 간격으로 신호를 발생시킬 수 있다.
또한 출력 핀을 인터럽트 발생기에 연결하면 원하는 시간 간격으로 인터럽트를 발생시킬 수도 있다.
PC에서는 8254 타이머의 각 카운터를 특정 용도로 사용하고 있다[3].
예를 들어 카운터 0은 시스템에 시간을 기록하는 일을 하고 있고, 카운터 1은 메모리 재충전(refresh)을 위해 사용하고 있다.
재충전 시간 간격은 사용되는 메모리의 종류에 따라 달라지는데,
최초의 IBM AT에서 사용된 메모리는 약 50 nsec(nano second; 10만분의 일초)마다 한 번씩 재충전 해야 했다.
따라서 카운터 1은 50 nsec마다 한 번씩 메모리 재충전 신호를 발생하도록 프로그래밍되어있다.
그리고 카운터 2는 스피커와 연결되어 소리를 만드는데 이용된다.
2 카운터의 입출력 핀 구성
8254에는 세 개의 독립적인 카운터가 있고, 각 카운터는 다음과 같은 입출력 핀들로 이루어져있다.
(1) 클럭 신호 입력 핀
클럭 신호가 입력되는 핀으로 PC의 경우 1193180 Hz의 클럭 신호가 입력된다.
카운터는 매 클럭 신호마타 카운트 값을 1 씩 감소시킨다.
(2) 게이트 입력 핀
카운터의 정지 및 시작 기능으로 사용한다.
보통 게이트 입력 핀으로 0을 입력하면 카운터가 정지하고, 1이 입력하면 다시 시작한다.
(3) 출력 핀
카운트 값이 0이 되면, 카운터는 출력 핀으로 1을 출력한다.
출력 핀은 인터럽터 요청 신호로 쓰이거나,
더 큰 시간 간격을 얻기 위해 다른 카운터의 클럭 신호 입력 핀으로 연결하기도 한다.
PC에서는 카운터 2의 출력 핀을 스피커에 연결하여 소리를 발생시키는데 이용하고 있다.
3 카운터의 동작 모드?
8254 타이머는 다음과 같이 6 가지 동작 모드를 갖고 있다.
모드 0 - interrupt on terminal count
모드 1 - hardware retrigerable one-shot
모드 2 - rate generator
모드 3 - square wave mode
모드 4 - software triggered strobe
모드 5 - hardware triggered strobe (retriggerable)
3.1 모드 0
카운트 값 설정 후, 카운트 값은 매 클럭 신호마다 1 씩 감소한다.
이동안 출력 핀으로는 0이 출력된다.
카운트 값이 0이 되면, 출력 핀으로 1이 출력된다.
출력 핀의 값은 모드를 변경하거나 카운트 값을 다시 설정할 때까지 그대로 유지된다.
보통 출력 핀은 인터럽트 요청 핀에 연결되어있어 인터럽트가 발생하게 된다.
게이트 입력 핀의 값이 0이되면 카운트를 잠시 멈추었다가, 게이트 입력 핀의 값이 1이되면 계속해서 카운트 한다.
예를 들어 카운트 값이 4에서 0으로 감소하고 있다고 하자.
현재 카운트 값이 3일 때, 게이트 입력 핀으로 0이 입력되면 카운트는 잠시 중지되어 카운트 값은 계속 3으로 남아 있게 된다.
게이트 입력 핀으로 1이 들어오면 카운트는 3부터 다시 감소하기 시작한다.
3.2 모드 1
모드 0과 비슷한 동작을 한다.
모드 1은 모드 0과 단지 게이트 입력 핀에 대한 쓰임새가 다르다.
모드 1에서 게이트의 입력 핀은 방아쇠와 같은 역할을 한다.
게이트 입력 핀의 값이 1이 되면, 초기값부터 다시 카운트 한다.
예를 들어 카운트 값이 4에서 0으로 감소하는 도중에 게이트 입력 핀으로 0이 들어오면,
카운트 값이 0이 되더라도 출력 핀으로는 1이 출력되지 않는다.
게이트 입력 핀으로 들어오는 값이 0에서 1로 바뀌면, 카운터는 카운트 값 다시 4로 설정하고 카운트 한다.
3.3 모드 2
카운트 값을 N으로 설정하면, 출력 핀으로 1이 N-1 번 출력되고 0이 한 번 출력된다.
예를 들어 카운트 값을 4로 설정하면, 출력 핀으로 1이 세 번 출력되고 0이 한 번 출력된다.
카운트 값이 0이 된 후에는 자동적으로 초기 카운트 값으로 다시 설정된다.
게이트 입력 핀은 리셋 기능을 갖고 있다.
게이트 입력 핀으로 0이 들어오면, 출력 핀으로는 1이 출력되고 카운트는 정지한다.
게이트 입력 핀으로 들어오는 값이 0에서 1로 바뀌면, 초기값부터 다시 카운트 한다.
3.4 모드 3
설정된 카운트 값에 따라 출력 핀으로 사각파(square wave)를 만든다.
예를 들어 카운드 값을 4로 설정하면, 출력 핀으로 두 번은 1이 출력되고 두 번은 0이 출력된다.
또한 카운트 값을 5로 설정하면, 출력 핀으로 세 번은 1이 출력되고 두 번은 0이 출력된다.
카운트 값이 0이 된 후에는 자동적으로 초기 카운트 값으로 다시 설정된다.
그러므로 한 번 설정한 후에는 영원히 출력 핀으로 사각파를 출력한다.
게이트 입력 핀의 기능은 모드 2와 같다.
3.5 모드 4
모드 지정후 출력 핀으로는 1이 출력된다.
카운트 값을 설정한 후부터 카운트가 시작된다.
카운트 값이 0이되는 시점에 출력 핀으로 0이 출력되고 다음 클럭 신호부터는 1이 출력된다.
즉 카운트 하는 동안 출력 핀으로 1을 출력하다가 카운트 값이 0이 되는 한 클럭 주기 동안만 0을 출력한다.
게이트 입력 핀의 기능은 모드 2와 같다.
3.6 모드 5
모드 지정후 출력 핀으로는 1이 출력된다.
카운트 값 설정 후, 게이트 입력 핀의 값이 0에서 1로 바뀌면 카운트를 시작한다.
카운트 값이 0이 되면, 출력 핀으로 한 클럭 주기 동안 0을 출력한다.
게이트 입력 핀의 기능은 모드 1과 같다.
4 8254 타이머의 제어
8254 타이머를 제어하기 위해서는 8254 타이머와 연결되어있는 입출력 포트에 어떤 값을 써 넣거나 읽어와야 한다.
PC에서 8254 타이머에 대한 입출력 포트 주소는 다음과 같다.
40h - 카운터 0의 카운트 값을 읽거나 쓰는 곳
41h - 카운터 1의 카운트 값을 읽거나 쓰는 곳
42h - 카운터 2의 카운트 값을 읽거나 쓰는 곳
43h - 제어 명령어 레지스터, 즉 8254 타이머에게 전달할 제어 명령어를 기록하는 곳
4.1 제어 명령어 전달하기
제어 명령어의 크기는 한 바이트로 다음과 같은 형식을 갖고?있다.
비트 위치: b7 b6 b5 b4 b3 b2 b1 b0
비트 내용: CS1 CS0 RW1 RW0 MD2 MD1 MD0 BCD
제어 명령어의 각 비트는 다음과 같은 의미를 갖고 있다.
(1) CS (counter select)
CS1, CS0 두 비트로 읽기 또는 쓰기가 이루어지는 카운터를 선택한다.
CS1, CS0 두 비트의 조합이 나타내는 값의 의미는 다음과 같다.
00 - 카운터 0의 카운트 값에 대한 읽기 또는 쓰기를 선택
01 - 카운터 1의 카운트 값에 대한 읽기 또는 쓰기를 선택
10 - 카운터 2의 카운트 값에 대한 읽기 또는 쓰기를 선택
11 - 리드백(read-back) 명령어, 4.3.2 참조
(2) RW (read / write)
RW1, RW0 두 비트로 카운터에 읽기 또는 쓰기를 하는 값의 형태를 지정한다.
RW1, RW0 두 비트의 조합이 나타내는 값의 의미는 다음과 같다.
00 - 카운터 래치(counter latch), 4.3.1 참조
01 - 상위 바이트만 읽기 또는 쓰기
10 - 하위 바이트만 읽기 또는 쓰기
11 - 하위 바이트, 상위 바이트 순서로 두 바이트 읽기 또는 쓰기
(3) MD (mode)
MD2, MD1, MD0 세 비트로 카운터의 동작 모드를 선택한다.
MD2, MD1, MD0 세 비트의 조합이 나타내는 값의 의미는 다음과 같다.
000 - 모드 0
001 - 모드 1
010 - 모드 2
011 - 모드 3
100 - 모드 4
101 - 모드 5
(4) BCD (binary count / binary-coded decimal count)
카운트 값의 저장 형식을 이진수로 할 것인지 이진화 십진수로 할 것인지를 지정한다.
BCD 비트가 나타내는 값의 의미는 다음과 같다.
0 - 이진수
1 - 이진화 십진수
제어 명령어 레지스터에 제어 명령어를 써 넣는 것으로 8254 타이머에 제어 명령어를 전달할 수 있다.
예를 들어 제어 명령어가 0B6h 라고 하자.
제어 명령어 레지스터에 제어 명령어를 써 넣는 방법은 다음과 같다.
mov al, 0B6h
out 43h, al
같은 내용을 터보 C에서는 다음과 같은 C 함수를 사용할 수 있다.
outportb(0x43, 0xB6);
4.2 카운트 값 쓰기
카운트 값을 쓰기 위해서는 먼저 제어 명령어로 카운트 값을 써 넣을 카운터를 지정하여 제어 명령어 레지스터에 저장한다.
그리고 카운트 값의 하위 바이트, 상위 바이트 순서로 원하는 카운터의 입출력 포트에 써 넣는다.
예를 들어 제어 명령어 레지스터로 보내는 제어 명령어를 0B6h라 하자.
이 제어 명령어는 카운터 2의 동작 모드를 모드 3으로 설정하고, 카운트 값의 저장 형식을 이진수로 설정하며, 카운트 값은 두 바이트로 저장한다.
다음은 이런 가정하에 카운트 값을 8000h로 초기화하는 예이다.
mov al, 0B6h
out 43h, al
mov ax, 8000h
out 42h, al
out 42h, ah
같은 내용을 터보 C에서는 다음과 같은 C 함수를 사용할 수 있다.
#define COUNT 0x8000
outportb(0x43, 0xB6);
outportb(0x42, COUNT & 0xFF);
outportb(0x42, (COUNT >> 8) && 0xFF);
카운트 값으로 초기화할 수 있는 최대값은 0이다.
이 값은 카운트 값이 이진수인 경우는 65536이 되고, 이진화 십진수의 경우 10000이 된다.
카운트 값으로 초기화할 수 있는 최소값은 1이다.
단 모드 2와 모든 3에서 카운트 값으로 초기화할 수 있는 최소값은 2이다.
한편 모드 0, 1, 4, 5의 경우 카운트 값이 감소하여 0이 되더라도 카운터는 동작을 멈추지 않는다.
즉 카운트 값이 0이 되어 출력 핀으로 1을 출력한 후에도 카운트 값은 매 클럭 신호마다 1 씩 감소한다.
다시 말해서 카운트 값이 0으로 감소한 후 다음 클럭 신호가 들어오면 카운트 값이 1 감소하여,
이진수라면 0FFFFh가 되고 이진화 십진수라면 9999가 된다.
모드 2, 3의 경우 카운터는 카운트 값이 감소하여 0이 되면, 스스로 카운트 값을 초기값으로 설정된다.
4.3 카운트 값 읽기
PC에서 카운터의 현재 카운트 값을 읽어오는 방법에는 카운터 래치 명령어를 사용하는 방법과 리드백 명령어를 사용하는 방법이 있다.
4.3.1 카운터 래치 명령어
제어 명령어의 RW 비트에서 카운터 래치를 선택하여 제어 명령어 레지스터에 쓰면 카운터 래치 명령어가 8254 타이머로 전달된다.
물론 이때 카운트 값을 읽어올 카운터를 CS 비트로 선택해 주어야 한다.
그런 다음 지정한 카운터의 입출력 포트로부터 카운트 값을 읽어오면 된다.
카운터 래치 명령어를 8254 타이머에 전달한 후 카운트 값을 읽어오지 않고 계속 카운터 래치 명령어를 전달하면, 두번째 이후 명령은 무시된다.
카운터 래치 명령어의 형식은 제어 명령어에서 RW 비트를 0으로 만들고 CS 비트로 읽어올 카운터를 선택하면 된다.
나머지 비트들은 어떤 값을 가져도 상관없지만, 0으로 채울 것을 권한다.
다음은 카운터 래치 명령어를 이용해서 카운터 0의 카운트 값을 읽어오는 예이다.
mov al, 0h
out 43h, al
in al, 40h
in ah, 40h
같은 내용을 터보 C에서는 다음과 같은 C 함수를 사용할 수 있다.
union { unsigned int w; unsigned char b[2]; } count;
outportb(0x43, 0);
count.b[0] = (unsigned char) inportb(0x40);
count.b[1] = (unsigned char) inportb(0x40);
4.3.2 리드백 명령어
8254 타이머는 8253 타이머의 기능을 개선하여 만들어졌다.
리드백 명령어는 8254 타이머에 추가된 기능 중 하나다.
때문에 리드백 명령어는 8253 타이머에서 사용할 수 없다.
카운터 래치 명령어는 한 번에 하나의 카운트 값만 읽을 수 있다.
또한 카운터의 상태 정보는 전혀 읽을 수 없다.
리드백 명령어는 이러한 카운터 래치 명령어의 단점을 개선한 명령어이다.
리드백 명령어는 여러 카운터의 카운트 값과 상태 정보를 동시에 읽을 수 있도록 해 준다.
리드백 명령어는 제어 명령어 형식과 많이 다르다.
다음은 리드백 명령어의 형식이다.
비트 위치: b7 b6 b5 b4 b3 b2 b1 b0
비트 내용: 1 1 COUNT STATUS CNT2 CNT1 CNT0 0
제어 명령어의 CS 비트에 해당하는 b7과 b6 비트는 1로 만들고, BCD 비트에 해당하는 b0 비트는 0으로 만든다.
COUNT 비트는 카운트 값을 읽고자 할 경우에는 0으로 만들고, 그렇지 않다면 1로 만든다.
STATUS 비트는 카운터의 상태 정보를 읽고자 할 경우에는 0으로 만들고, 그렇지 않다면 1로 만든다.
CNT2 비트는 카운터 2의 정보(카운트 값 또는 상태 정보)를 읽고자 할 경우에는 1로 만들고, 그렇지 않다면 0으로 만든다.
CNT1 비트는 카운터 1의 정보(카운트 값 또는 상태 정보)를 읽고자 할 경우에는 1로 만들고, 그렇지 않다면 0으로 만든다.
CNT0 비트는 카운터 0의 정보(카운트 값 또는 상태 정보)를 읽고자 할 경우에는 1로 만들고, 그렇지 않다면 0으로 만든다.
한편 리드백 명령어로 읽은 카운터의 상태 정보는 한 바이트로 구성되어 있다.
상태 정보의 b7 비트는 출력 핀의 값이 들어있다.
상태 정보의 b6 비트는 카운트 값을 읽을 수 있는 지 여부를 나타낸다.
b6 비트의 값이 1이라면 카운트 값을 읽을 수 있다는 뜻이다.
상태 정보의 나머지 비트들은 제어 명령어 레지스터에 들어있는 내용이다.
COUNT 비트와 STATUS 비트를 모두 0으로 만들면 카운트 값과 상태 정보를 동시에 읽을 수 있다.
이 경우 첫번째 읽은 바이트는 상태 정보이고, 두번째와 세번째 읽은 바이트는 카운트 값이다.
다음은 리드백 명령어로 카운터 0과 카운터 2의 카운트 값을 읽는 예이다.
mov al, 0DAh
out 43h, al
in al, 40h
in ah, 40h
in bl, 40h
in bh, 40h
같은 내용을 터보 C에서는 다음과 같은 C 함수를 사용할 수 있다.
union { unsigned int w; unsigned char b[2]; } count0, count2;
outportb(0x43, 0xDA);
count0.b[0] = (unsigned char) inportb(0x40);
count0.b[1] = (unsigned char) inportb(0x40);
count2.b[0] = (unsigned char) inportb(0x42);
count2.b[1] = (unsigned char) inportb(0x42);
5 응용 예
PC에서는 8254 타이머의 각 카운터를 이미 특정 용도로 사용하고 있다.
사용자가 쓸 수 있는 카운터는 0번과 2번이다.
카운터 0은 주기적으로 인터럽트를 발생시키도록 프로그래밍되어있다.
카운터 0의 카운트 값은 65536로 초기화되어 있고, 매 클럭 신호마다 1 씩 감소한다.
카운트 값이 0이 되면, 인터럽트(인터럽트 08h, 인터럽트 벡터 주소 020h-023h, IRQ0)를 발생시킨다.
PC는 1193180 Hz의 클럭 신호를 발생시킨다.
즉 초당 1193180 번의 신호가 발생한다.
따라서 초당 약 18.2 (1193180/65536) 번의 인터럽트가 발생한다.
카운터 0의 카운트 값을 65536이 아닌 다른 값으로 초기화한다면, 발생하는 인터럽트의 주기를 바꿀 수 있다.
예를 들어 [2]에서는 카운트 값의 초기값을 11932로 바꾸어 10 msec(milli second, 천분의 일초) 간격으로 인터럽트가 발생하도록 하고 있다.
이러한 기능을 어디에 쓸 수 있을까?
첫번째로 멀티 태스킹 기능을 지원하기 위해 사용할 수 있다.
간단히 생각해서 태스크마다 10 msec 씩 실행하도록 한다면 초당 100 개의 태스크를 실행시킬 수 있다.
즉 인터럽트 서비스 루틴은 10 msec마다 깨어나서 가장 우선순위가 높은 태스크를 찾아 태스크 문맥교환을 한다.
이것이 바로 멀티 태스킹을 지원하는 운영체제의 스케줄러의 기본적인 기능이다.
카운터 2는 스피커와 연결되어있어 소리를 만드는데 사용된다.
사람이 귀로 들을 수 있는 가청 주파수는 20Hz에서 2만Hz 사이이다.
PC의 클럭 신호가 1193180Hz이므로 이론적으로 카운터 2를 통해 만들수 있는 주파수는 0Hz부터 1193180Hz까지이다.
그러므로 카운터 2를 이용하면 사람이 들을 수 있는 소리는 물론 사람이 들을 수 없는 소리도 만들어낼 수 있다.
카운터 2를 이용해서 PC 스피커로 소리를 만들어내는 예는 [3]에 나와있다.
6 참고도서목록
[1] Intel, 8254 Programmable Interval Timer Datasheet, http://support.intel.com/support/controllers/peripheral/231164.htm
[2] Richard A. Burgess, Developing Your Own 32-Bit Operating System, Sams Publishing, 1995.
[3] 유정하, PC 시스템 프로그래밍, 정보문화사, 1991.
[4] 차영배, IBM PC 인터페이스 응용, 동일출판사, 1999.




댓글을 달아 주세요