На главную Назад
Добро пожаловать, уважаемый посетитель!

Канал 0 таймера 8253 имеет специальное назначение в IBM PC. Выход

этого канала таймера подключен к уровню прерывания 0 микросхемы

8259. Это означает, что всякий раз, когда выход канала 0 имеет

активный уровень сигнала, возникает прерывание (при условии, что

все остальное установлено корректно). Процедура самопроверки при

включении питания инициализирует канал 0 таймера, загружая в него

число 0. Это дает наибольшее (не наименьшее) значение счетчика,

которое может записать в него программа. Имея на входе частоту 1.19

МГц, счетчик считает обратно к нулю чуть быстрее, чем за 55

миллисекунд. Программа инициализации устанавливает таймер таким

образом, что он считает непрерывно. Это означает, что прерывание 0

возникает 18.2 раза в секунду.

 

Как мы увидим в следующей главе, встроенная система программ

BIOS использует это постоянное прерывание от таймера, чтобы следить

за текущим временем. BIOS продвигает часы текущего времени вперед

всякий раз, когда возникает прерывание. Затем, с помощью

соответствующих вычислений, Вы можете преобразовать число циклов

таймера в часы, минуты и секунды.

 

Почему же выбрано значение 18.2? Почему счетчик не

программируется так, чтобы давать прерывание 20 раз в секунду, или

другое "хорошее" число раз? Это объясняет следующий пример.

 

Системный таймер может выполнять функцию измерения времени

отличного от времени дня. Время дня прекрасно подходит для

определения интервалов времени, измеряемых в секундах или минутах.

Но в некоторых ситуациях, возникающих при управлении

вводом-выводом, нужно определять интервалы времени порядка одной -

двух миллисекунд. Обычно программы отсчитывают такие интервалы с

помощью временного цикла. Программа для такого цикла выглядит

примерно так:

 

MOVCX, LOOP_VALUE

HERE:LOOPHERE

 

Вы выбираете константу LOOP_VALUE так, что цикл выполняется в

точности нужное число раз. Это очень хороший метод, если вам нужна

задержка на определенное время. В выше приведенном примере

начальное значение константы LOOP_VALUE, равное 0FFFFH, дает время

выполнения около 250-миллисекунд.

 

Но предположим, что вы хотите понаблюдать за внешним событием,

и определить, сколко времени займет его наступление. Можно использовать

вариант временного цикла такого, например, вида:

 

MOVCX, 0

HERE:

; --- проверка возникновения события

INAL, DX

TESTAL, MASK_BIT

LOOPNEHERE

DONE:

; --- CX содержит число итераций цикла

 

Таким способом вы считаете число итераций цикла, чтобы

вычислить затраченное на него время. Этот метод предполагает, что

событие возникнет до того, как содержимое регистра CX второй раз

достигнет 0. Но если вам нужно измерить что-то с точностью до

микросекунд, этот метод не удобен, так как каждая итерация цикла

требует от 10 до 20 микросекунд. Системный таймер дает лучшее

решение. Поскольку он изменяет свое значение каждые 840 наносекунд,

вы сможете определить длительность события с точностью до

микросекунды.

 

На Фиг. 8.5 показан пример программы, вычисляющей время события

с помощью системного таймера. В этом примере в качестве

регистрируемого события используется канал 2 таймера. В первой

Microsoft (R) Macro Assembler Version 5.001/1/80 04:05:19

Фиг. 8.5 Управление системным таймеромPage1-1

 

PAGE,132

TITLEФиг. 8.5 Управление системным таймером

 

0000STACKSEGMENT STACK

00000040[DW64 DUP (?)

????

]

0080STACKENDS

 

0000CODESEGMENT

ASSUMECS:CODE

0000TIMERPROCFAR

00001E PUSHDS; Занесение адреса возврата

0001B8 0000MOVAX, 0

000450 PUSHAX

 

0005B0 B6MOVAL, 10110110B; Выборка таймера 2

0007E6 43OUT43H, AL

0009B8 0500MOVAX, 500H

000CE6 42OUT42H, AL; Таймер 2 установлен на 500 отсчетов

000E8A C4MOVAL, AH

0010E6 42OUT42H, AL

 0012E8 001D RCALLLOW_TO_HIGH; Выборка времени первого перехода с 0 на 1

00158B D8MOVBX, AX; Сохранение значения в регистре BX

0017E8 001D RCALLLOW_TO_HIGH; Выборка времени второго перехода с 0 на 1

001A2B D8SUBBX, AX; Вычитая получаем длину цикла

001CCB RET

001DTIMERENDP

 

Фиг. 8.5 Управление системным таймером (начало)

;--------------------------------------------

; Эта подпрограмма ждет перехода с нижнего уровня

;сигнала на верхний (с 0 на 1) в таймере 2

;и возвращает в регистре AX значение счетчика таймера 0

;--------------------------------------------

001DLOW_TO_HIGHPROCNEAR

001DE4 62INAL, 62H; Проверка разряда таймера 2

001FA8 20TESTAL, 20H

002175 FAJNZLOW_TO_HIGH; Цикл: сигнал таймера 2 на

;нижнем уровне

0023WAIT_HIGH:

0023E4 62INAL, 62H; Проверка разряда таймера 2

0025A8 20TESTAL, 20H

002774 FAJZWAIT_HIGH; Цикл: сигнал таймера 2 на

;верхнем уровне

0029B0 00MOVAL, 0; Послать команду в регистр управления тай-

002BE6 43 OUT43H, AL;мером 2, которая "замораживает" таймер 2

002D90 NOP

002E90 NOP; Задержка, необходимая для 8253

002FE4 40INAL, 40H; Чтение младшего байта счетчика

00318A E0MOVAH, AL

003390 NOP

0034E4 40INAL, 40H; Чтение старшего байта счетчика

003686 E0XCHGAH, AL

0038C3 RET; Возвращение значения в AX

0039LOW_TO_HIGHENDP

0039CODEENDS

END

 

Фиг. 8.5 Системный таймер (продолжение)

 

части программы этот канал таймера загружается известным значением.

Здесь мы произвольно выбрали число 500H. Обратите внимание, что эта

часть программы идентична способу генерации звуков с помощью втого

канала таймера.

 

Наша программа вызывает подпрограмму LOW_TO_HIGH, которая

возвращает значение таймера в тот момент, когда на выходе канала 2

таймера отмечается переход от низкого к высокому уровню. Программа

рследит именно за переходом; если бы она регистрировала только

высокий уровень, было бы неизвестно, стал ли сигнал высоким только

что или уже готов стать низким. Подпрограмма посылает нуль в

управляющий регистр таймера (порт 43H), чтобы "заморозить" текущее

значение канала 0. Это позволяет ей прочитать текущее значение

таймера, продолжающего счет. Если бы программа временно не

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

16-битовое значение.

 

Обратим внимание на то, что подпрограмма на Фиг. 8.5 содержит

несколько команд NOP. Эти команды записаны в программе для выдержки

временных соотношений. Если очень внимательно прочитать инструкции

по микросхеме 8253, мы заметим, что между командами IN и OUT,

выполняемыми этой микросхемой, проходит не меньше 1 микросекунды.

Команда NOP занимает как раз достаточно времени, чтобы исключить

нарушение требований микросхемы по времени.

После возврата из подпрограммы программа сохраняет в регистре

BX значение счетчика таймера во время первого перехода с низкого

уровня на высокий. Затем программа снова вызывает подпрограмму,

чтобы зарегистрировать следующий переход с низкого уровня на

высокий на выходе канала 2 таймера. Потом она вычитает одно число

из другого, чтобы определить время цикла канала 2.

 

Мы уже говорили о том, что загрузка регистра 0 таймера

значением счета 0 - очень полезна. Данная программа подтверждает

это, так как она вычитает два значения таймера, не обращая внимания

на то, какое из них больше, а какое меньше. Так как канал 0 таймера

работает асинхронно по отношению к этой программе, нет никакой

гарантии, что первое читаемое из него число больше второго.

Например, предположим, что первый переход с низкого на высокий

уровень происходит, когда таймер 0 имеет значение 100H. После 500H

циклов значение числа в таймере будет 0FC00H. Счетчик таймера 0

автоматически "проскочил" от значения 0 к значению 0FFFFH, и

значение прочитанное вторым оказалось численно больше первого. Но

из-за того, что регистр таймера снова начинает счет со значения

0FFFFH, мы всегда можем вычитать эти два числа. При этом иногда

будет появляться перенос, иногда нет, но разность этих двух чисел

всегда будет равна числу отсчетов.

 

Чтобы убедить вас в правильности этого положения, рассмотрим

случай, когда счетчик загружается числом 8000H. Если первый переход

возникает при значении 6000H, второй появится при значении 5B00H, и

разница между ними составляет 500H. Но если первый переход

возникает при значении 100H, второй возникает при значении 7C00H, и

разница станет равна 8500H. Чтобы правильно отреагировать на эту

ситуацию, программа должна была бы проверить, не произошло ли

переполнение счетчика за время отсчета.

 

При выполнении этой программы вы обнаружите, что значение в

регистре BX составляет около 0A00H, и не равно ожидаемому значению

500H. Так происходит потому, что таймер работает в режиме

уменьшения содержимого счетчика на два по каждому временному

импульсу. Чтобы разобраться в работе микросхемы 8253, нужно

ознакомиться с инструкцией по ее программированию.

 

Фиг. 8.6 дает сводку для управляющего слова микросхемы 8253.

Для настройки одного из каналов на конкретный режим работы вы

выводите это управляющее слово в порт 43H. Мы уже встречались с

выводом некотрых значений в порт 43H. Чтобы "заморозить" счетчик,

мы послали в этот порт нуль, а для настройки генератора тональности

- код 0B6H. Посмотрим, откуда берутся эти значения.

 

Два старших бита управляющего слова определяют канал таймера.

Следующие два бита - выполняемую операцию. Когда мы выводим

значение 0, выбирается таймер 0 и запирание данных в счетчике.

Следующие 3 бита задают режим работы выбранного таймера. Эти биты

не играют роли, когда счетчик заперт, но нужны при инициализации

таймера. Оставшийся бит определяет, будет ли счетчик работать как

16-битовое двоичное число или как четырехзначное десятичное в

двоичном представлении.

A

Формат управляющего слова

D7D6D5 D4D3D2D1D0

ЪДДДДВДДДДВДДДДВДДДДВДДДДВДДДДВДДДДВДДДДї

і SC1і SC0і RL1і RL0і M2 і M1 і M0 і BCDі

АДДДДБДДДДБДДДДБДДДДБДДДДБДДДДБДДДДБДДДДЩ

 

Определение управления

SC - выбор счетчика:

SC1SC0

ЪДДДДДДДДДВДДДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДДї

і0і0ізадать счетчик 0і

ГДДДДДДДДДЕДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДґ

і0і1ізадать счетчик 1і

ГДДДДДДДДДЕДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДґ

і1і0ізадать счетчик 2і

ГДДДДДДДДДЕДДДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДґ

і1і1інеопределеноі

АДДДДДДДДДБДДДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДДДЩ

 

RL - чтение/загрузка:

RL1RL0

ЪДДДДВДДДДВДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї

і0 і0 і Операция запирания счетчика (см.і

ііі раздел процедуры READ/WRITE)і

ГДДДДЕДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ

і1 і0 і Считать/загрузить старший байті

ГДДДДЕДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ

і0 і1 і Считать/загрузить младший байті

ГДДДДЕДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ

і1 і1 і Считать/загрузить младший байт,і

ііі затем - старшийі

АДДДДБДДДДБДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

 

M - режим:

M2M1M0

ЪДДДВДДДВДДДВДДДДДДДДДДДї

і 0 і 0 і 0 і Режим 0і

ГДДДЕДДДЕДДДЕДДДДДДДДДДДґ

і 0 і 0 і 0 і Режим 1і

ГДДДЕДДДЕДДДЕДДДДДДДДДДДґ

і X і 1 і 0 і Режим 2і

ГДДДЕДДДЕДДДЕДДДДДДДДДДДґ

і X і 1 і 1 і Режим 3і

ГДДДЕДДДЕДДДЕДДДДДДДДДДДґ

і 1 і 0 і 0 і Режим 4і

ГДДДЕДДДЕДДДЕДДДДДДДДДДДґ

і 1 і 0 і 1 і Режим 5і

АДДДБДДДБДДДБДДДДДДДДДДДЩ

 

BCD:

ЪДДДДДДДВДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДї

і0і 16-битовый двоичный счетчикі

ГДДДДДДДЕДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДґ

і1і десятичный в двоичном представленииі

іі (BCD) счетчик (4-х разрядный)і

АДДДДДДДБДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ

 

Фиг. 8.6 Программирование таймера/счетчика

(с разрешения фирмы Intel; приоритет Intel 1981г.)A

Управляющий код 0B6H, который использовался для генерации

тональности, можно расписать таким образом:

 

0B6H = 10110110B = 01 11 011 0

 

Это управляющее слово выбирает канал 2. Счетчик этого канала

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

всчетчик первым. Последный бит показывает, что счет будет

двоичным. Можно видеть также, что выбирается третий режим работы.

 

Микросхема 8253 может работать в шести разных режимах. Но для

наших целей, в конфигурации системы IBM, удобны только два из них.

Режим 3 устанавливается по умолчанию для всех трех каналов таймера.

Это режим работы генератора прямоугольных импульсов: половину

периода выход канала дает нижний уровень, а другую половину периода

- верхний. Счетчик работает в этом режиме, вычитая из своего

значения по два, а не по одному. Во время первого обратного счета

выход низкий, а затем, во время второго счета - высокий. Так как

счетчик считает по два, выход имеет высокий уровень точно половину

заданного времени, и низкий тоже точно половину. Из-за того, что

канал 0 таймера обычно работает в режиме 3, пример на Фиг. 8.5

заканчивает счет со значением счетчика 0A00H, а не 500H, как

ожидалось. Каждый отсчет таймера уменьшает содержимое счетчика на

два.

 

Так как счетчик считает двойками, он переполняется каждые 27

микросекунд. Если вы хотите измерять события более длительные, то

вам придется воспользоваться другим способом измерения времени.

 

Другой режим таймера, который используется в IBM PC - это режим

0. Этот режим называют прерыванием по завершению счета. В этом

режиме таймер не работает непрерывно. После его установки таймер до

тех пор не начинает считать (единицами), пока в него полностью не

будет загружено число. Затем счетчик считает в сторону уменьшения с

частотой синхроимпульсов, пока не достигнет нуля. В этот момент его

выход становится высоким. Поскольку выход канала 0 таймера

подключен к прерыванию 0 контроллера 8259, в системе возникает

прерывание.

 Режим прерывания по завершению счета полезен, если вы хотите в

определенный момент подать программе сигнал с помощью прерывания.

Так как счетчик ограничен шестнадцатью битами, максимальный

отсчитываемый интервал времени составляет 55 миллисекунд. Если этот

интервал слишком мал, нужен другой метод измерения времени.

 

Если вы хотите измерять интервал времени в секундах, нужно

оставить таймер в его обычном режиме работы. Система BIOS позволяет

захватывать управление системой каждые 55 миллисекунд, и в каждый

такой момент вы можете решить, не исчерпался ли нужный промежуток

времени.

Если время нужной вам задержки находится между 55

миллисекундами и 5 секундами, можно использовать метод без

использования программ BIOS. Например, вам хочется сделать задержку

на 150 миллисекунд. Используя режим прерывания по завершению счета,

вы настраиваете таймер на прерывание через 50 миллисекунд (этому

соответствует значенние счетчика около 59500). Обработчик

прерывания программируется так, чтобы, получая управление первые

два раза, он заново устанавливал таймер на 50 миллисекунд. По

третьему прерыванию от таймера, когда 150 миллисекунд исчерпаны,

можно предпринять нужные действия.

 

При организации задержек через таймер всегда нужна некоторая

осторожность. Как упоминалось выше, канал 1 таймера выполняет одну

важную аппаратную функцию. Если вы модифицируете число в канале 1,

ваша программа может немедленно разрушиться. Использование канала 2

таймера безопасно. Этот канал подключен только к динамику и выходу

кассетного магнитофона. Отсюда, очевидно, следует, что нельзя

использовать канал 2 таймера для отсчета промежутков времени в одно

время с попытками воспроизводить мелодии через динамик. И наконец,

BIOS пользуется услугами таймера 0 для различных системных функций.

При обсуждении BIOS будет видно, что прерывание по времени суток

управляет не только текущим временем, но также обслуживает и

двигатель накопителя на дискетах. Перед тем, как изменять настройку

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

функции вы можете при этом изменить.


 

Mail.ru