5.10.5. Таймер

До сих пор о таймере нам было известно лишь то, что он вызывает

прерывание приблизительно 18,2 раза в секунду. На самом деле программируемый интервальный таймер - весьма сложная система, состоящая из трех

частей - трех каналов таймера, каждый из которых можно запрограммировать для работы в одном из шести режимов. И более того, на многих современных ма­теринских платах располагаются два таких таймера, следовательно, число кана­лов оказывается равным шести. Для своих нужд программы могут использовать

канал 2 (если им не нужен динамик) и канал 4 (если присутствует второй тай­мер). При необходимости можно перепрограммировать и канал 0, но затем надо будет вернуть его в исходное состояние, чтобы BIOS и DOS могли продолжать

работу.

В пространстве портов ввода-вывода для таймера выделена область от 40h до 5Fh:

□ порт 40h - канал 0 (генерирует IRQ0);

□ порт41п - канал 1 (поддерживает обновление памяти);

□ порт 42h - канал 2 (управляет динамиком);

□ порт 43h - управляющий регистр первого таймера;

□ порты 44h - 47h - второй таймер компьютеров с шиной MicroChannel;

Q порты 48h - 4Bh - второй таймер компьютеров с шиной EISA.

Все управление таймером осуществляется путем вывода одного байта в 43h (для первого таймера). Рассмотрим назначение битов в этом байте.

биты 7-6: если не 11 - это номер канала, который будет программироваться

00, 01, 10 = канал О, 1, 2 бит 5-4: 00 - зафиксировать текущее значение счетчика для чтения (в этом случае биты 3-0 не используются) 01 - чтение/запись только младшего байта

10 - чтение/запись только старшего байта

11 - чтение/запись сначала младшего, а потом старшего байтабиты 3-1: режим работы канала

000 - прерывание ШСЧ) при достижении нуля

001 - ждущий мультивибратор 010 - генератор импульсов

ОН - генератор прямоугольных импульсов (основной режим)

100 - программно запускаемый одновибратор

101 - аппаратно запускаемый одновибратор бит 0: формат счетчика:

0 - двоичное 16-битное число (0000 - ОББЕРІї)

1 - двоично-десятичное число (0000 - 9999)

Если биты 7-6 равны 11, считается, что байт, посылаемый в 43п, - команда чтения счетчиков, формат которой отличается от команды программирования канала:

биты 7-6: 11 (код команды чтения счетчиков) биты 5-4: что читать:

00 - сначала состояние канала, потом значение счетчика

01 - значение счетчика 10 - состояние канала

биты 3-1: команда относится к каналам 3-1 •

Если этой командой запрашивается состояние каналов, новые команды:будут игнорироваться, пока не прочтется состояние из всех каналов, которые были за­казаны битами 3-1.

Состояние и значение счетчика данного канала получают чтением из порта, со­ответствующего требуемому каналу. Формат байта состояния имеет следующий вид: бит 7: состояние входа ОиТх на момент выполнения команды чтения счетчи­ков. Так как в режиме 3 счетчик уменьшается на 2 за каждый цикл, со­стояние этого бита, замороженное командой фиксации текущего значе­ния счетчика, укажет, в каком полуцикле находился таймер бит 6: 1/0: состояние счетчика не загружено/загружено (используется в режи­мах 1 и 5, а также после команды фиксации текущего значения) биты 5-0: совпадают с битами 5-0 последней команды, посланной в 43Ь

Для того чтобы запрограммировать таймер в режиме 3, в котором работают каналы 0 и 2 по умолчанию и который чаще всего применяют в программах, тре­буется выполнить следующие действия:

1. Вывести в регистр 43Ь команду (для канала 0) 001101 1Ь, то есть установить режим 3 для канала 0, и при чтении/записи будет пересылаться сначала младшее слово, а потом старшее.

2. Послать младший байт начального значения счетчика в порт, соответствую­щий выбранному каналу (42Ь для канала 2).

3. Послать старший байт начального значения счетчика в этот же порт.

После этого таймер немедленно начнет уменьшать введенное число от началь­ного значения к нулю со скоростью 1 193 180 раз в секунду (четверть скоростПрограммирование на уровне портов .■ШШЯШШШШШ

процессора 8088). Каждый раз, когда это число достигнет нуля, оно снова вернет­ся к начальному значению. Кроме того, при достижении счетчиком нуля таймер выполняет соответствующую функцию - канал 0 вызывает прерывание IRQO, а канал 2, если включен динамик, посылает ему начало следующей прямоуголь­ной волны, заставляя его работать на установленной частоте. Начальное значе­ние счетчика для канала 0 по умолчанию составляет OFFFFh (65 535), то есть максимально возможное. Поэтому точная частота вызова прерывания IRQO рав­на 1 193 180/65 536 = 18,20648 раза в секунду.

Чтобы прочитать текущее значение счетчика, надо:

1. Послать в порт 43h команду фиксации значения счетчика для выбранного канала (биты 5-4 равны ООЬ).

2. Послать в порт 43h команду перепрограммирования канала без смены ре­жима работы, если нужно воспользоваться другим способом чтения/записи (обычно не требуется).

3. Прочитать из порта, соответствующего выбранному каналу, младший байт

зафиксированного значения счетчика.

4. Прочитать из того же порта старший байт.

Для таймера найдется много применений, единственное ограничение здесь

следующее: таймер - это глобальный ресурс, и перепрограммировать его в много­задачных системах можно только с ведома операционной системы, если она вооб­ще это позволяет.

Рассмотрим в качестве примера, как при помощи таймера измерить, сколько времени проходит между реальным аппаратным прерыванием и моментом, когда обработчик этого прерывания получает управление (почему это важно, см. при­меры программ вывода звука из разделов 5.10.8 и 5.10.9). Так как IRQO происхо­дит при нулевом значении счетчика, нам достаточно прочитать его значение во время старта обработчика и изменить его знак (потому что счетчик таймера по­стоянно уменьшается).

; latency, asm

Измеряет среднее время, проходящее между аппаратным прерыванием и запуском соответствующего обработчика.   Выводит среднее  время  в микросекундах после нажатия любой клавиши (на самом деле в 1/1 193 180).

Программа использует 16-битный сумматор для простоты,   так что может давать неверные результаты,  если подождать больше нескольких минут.

.model tiny .code

start:

.386 ; Для команды shld.

org 1001т ; СОМ-программа. '' •

mov ax,3508h ; АН = 35h,  AL = номер прерывания.

int - Получить адрес обработчика

mov word ptr old_int08h, bx     : и записать его в old_int08h.

mov word ptr old_int08h+2,es

mov ax,2508h ; AH = 25h,  AL = номер прерывания.

mov       dx,offset int08h_handler  ;  DS:DX - адрес обработчика.

int        21h ;  Установить обработчик,

этого момента в переменной latency накапливается сумма.

mov

ah,0

 

int

16h

Пауза до нажатия любой клавиши.

mov

ax,word ptr latency

; Сумма в АХ.

cmp

word ptr counter,0

;  Если клавишу нажали немедленно,

jz

dont.divide

; избежать деления на ноль.

xor

dx, dx

; ОХ = 0.

div

word ptr counter

; Разделить сумму на число накоплений

dont_divide:

 

 

call

print_ax

: и вывести на экран.

mov

ax,2508h

; АН = 251л, АЕ = номер прерывания.

Ids

dx.dword ptr old_int08h

;     :ОХ = адрес обработчика.

int

21h

; Восстановить старый обработчик.

ret

 

;  Конец программы.

latency

dw 0

Сумма задержек.

counter

dw 0

; Число вызовов прерывания.

; Обработчик

прерывания 08h (IRQO).

 

; Определяет

время,  прошедшее с момента

срабатывания ЩО.

int08h handler

proc far'

 

push      ах ;   Сохранить используемый регистр,

mov       al,0 ;  Фиксация значения счетчика в канале 0.

out       43h,al ;  Порт 43h: управляющий регистр таймера.

; Так как этот канал инициализируется BIOS для 16-битного чтения/записи, другие

;   команды не

требуются.

 

in

al,40h

Младший байт счетчика -

mov

ah,al

в АН.

in

al,40h

; Старший байт счетчика в AL.

xchg

ah, al

Поменять их местами.

neg

ax

Изменить его знак,  так как счетчик уменьшается.

add

word ptr

cs:latency,ах     ; Добавитв к сумме.                    ' .

inc

word ptr

cs:counter         ; Увеличитв счетчик накоплений.

pop

ax

і

db

OEAh

; Команда far.

old int08h

dd

0       ; Адрес старого обработчика.

int08h handler

endp

 

; Процедура print_ax.

 

Выводит АХ на экран в

шестнадцатеричном формате.

print_ax

proc near

xchg

dx, ax

; БХ = АХ.

mov

cx,4

; Число цифр для вывода.

shift ax:

 

 

shld

ax,dx,4

; Получить в АП очередную цифру.

rol

dx,4

: Удалить ее из БХ.

and

al.OFh

; Оставитв в А1 толвко эту цифру.

С

cmp al.OAh

sbb al,69h

das

int 29h

loop shift.ax

ret

print_ax endp

end start

Таймер можно использовать для управления динамиком, точных измерений отрезков времени, создания задержек, управления переключением процессов и даже для выбора случайного числа с целью запуска генератора случайных чи­сел - текущее значение счетчика канала 0 представляет собой идеальный вариант

такого начального числа для большинства приложений.