Биты
|
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
Значение
|
SC1
|
SC0
|
RW1
|
RW0
|
М2
|
M1
|
M0
|
BCD
|
Старшие два разряда SC1 и SC0 позволяют выбрать счетчик, либо получить информацию по состоянию канала (режим SC1=1 и SC0=1 определен только для микросхемы 8254 и последующих версий; для 8253 этот режим является запрещенным) (табл. 81).
Табл. 81. Номер счетчика.
SС1
|
SС0
|
Значение
|
0
|
0
|
Счетчик 0.
|
0
|
1
|
Счетчик 1.
|
1
|
0
|
Счетчик 2.
|
1
|
1
|
Состояние канала.
|
Разряды RW1RW0 определяют способ загрузки делителя через однобайтовый порт (табл. 82). Управляющее слово RW1=0 и RW0=0 используется для фиксации текущего содержимого регистров счетчика в буферном регистре. Поля M и BCD при этом игнорируются.
Табл. 82. Тип операции чтения-записи.
RW1
|
RW0
|
Значение
|
0
|
0
|
Фиксировать текущее значение счетчика.
|
0
|
1
|
Чтение/запись только LSB.
|
1
|
0
|
Чтение/запись только MSB.
|
1
|
1
|
Чтение/запись LSB сначала, затем MSB.
|
Режим работы счетчика задается разрядами M2M1M0 (табл. 83).
Табл. 83. Выбор режима работы.
Режим
|
М2
|
М1
|
М0
|
Значение
|
0
|
0
|
0
|
0
|
Прерывание от таймера.
|
1
|
0
|
0
|
1
|
Программируемый ждущий мультивибратор.
|
2
|
x
|
1
|
0
|
Программируемый генератор импульсов.
|
3
|
x
|
1
|
1
|
Генератор меандра.
|
4
|
1
|
0
|
0
|
Программно-запускаемый одновибратор.
|
5
|
1
|
0
|
1
|
Аппаратно-запускаемый одновибратор.
|
Разряд BCD служит для задания системы счисления (двоичной или двоично-десятичной) (табл. 84). Диапазон значений в двоичной системе 0…65535, в двоично-десятичной – 0…9999.
Табл. 84. Выбор системы счисления.
S0
|
Значение
|
0
|
Двоичная.
|
1
|
Двоично-десятичная.
|
В режиме SC1=1 и SC0=1 управляющий регистр изменяет свою структуру (табл. 85). Такой режим носит название «Read back command» и позволяет выполнить операцию чтения состояния каналов либо получить содержимое регистра счетчика для указанного канала. Можно выполнить эти операции для отдельных каналов, так и для всех одновременно, если установить соответствующие биты в 1 (табл. 86).
Табл. 85. Формат управляющего регистра.
Биты
|
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
Значение
|
1
|
1
|
CNT
|
STA
|
C2
|
C1
|
C0
|
x
|
Табл. 86. Коды выбора счетчика.
Счетчик
|
C2
|
C1
|
C0
|
0
|
0
|
0
|
1
|
1
|
0
|
1
|
0
|
2
|
1
|
0
|
0
|
Все
|
1
|
1
|
1
|
При CNT=0 и STA=1 доступно текущее значение счетчика. Для этого необходимо просто прочитать его содержимое. При CNT=1 и STA=0 доступен режим работы счетчика. В этом случае структура регистра счетчика изменится (табл. 87).
Табл. 87. Формат регистра счетчика.
Биты
|
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
Значение
|
OUT
|
NC
|
RW1
|
RW0
|
M2
|
M1
|
M0
|
BCD
|
Формат состояния канала напоминает формат регистра управляющего слова, за исключением двух старших разрядов. Назначения разрядов M2M1M0 и BCD останется прежними. Разряд OUT определяет текущий уровень на выходе счетчика в момент выполнения команды. Разряд NC используется, в основном, в режимах 1 и 5 для определения, произошла ли загрузка делителя из регистра RD в регистр счетчика RCNT.
10.4. Упражнения
Пример 1. Используя встроенный в среду Borland® Delphi ассемблер, разработать две подпрограммы для определения текущего состояния счетчика и его значения. Обе подпрограммы должны быть функциями. Первая подпрограмма должна использовать регистр задвижки и возвращать текущее значение счетчика. Вторая подпрограмма должна использовать режим «Read back command» и в зависимости от переданного параметра возвращать режим работы счетчика либо его текущее значение.
Решение. Функция LatchCNT() возвращает текущее значение счетчика с помощью задвижки и поэтому может работать с обеими микросхемами таймера 8253(4). Единственным параметром функции является порядковый номер канала CNT, который может принимать значения [0,1,2]. Формат управляющего регистра показан в табл. 88, а алгоритм работы функции – на рис. 38.
Табл. 88. Формат управляющего регистра.
Биты
|
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
Значение
|
SC1
|
SC0
|
0
|
0
|
x
|
x
|
x
|
x
|
Исходный текст функции приведен ниже.
function LatchCNT(CNT: Byte): Word;
begin
asm
{ Сохранение в стеке используемых регистров }
PUSH AX
PUSH DX
{ Фиксировать текущее значение счетчика }
MOV AL, CNT
SHL AL, 6
MOV DX, 43h
OUT DX, AL
{ Код выбора сначала LSB, а затем MSB }
OR AL, 00110000b
MOV DX, 43h
OUT DX, AL
{ Адрес регистра счетчика }
MOV DX, 40h
ADD DL, CNT
{ Получение младшего байта регистра счетчика }
IN AL, DX
{ Сохранение младшего байта в AH }
MOV AH, AL
{ Получение старшего байта регистра счетчика }
IN AL, DX
{ Обмен младшего и старшего байтов аккумулятора }
XCHG AL, AH
{ Функция возвращает результат }
MOV @Result, AX
{ Восстановление из стека содержимого регистров }
POP DX
POP AX
end
end;
Рис. 38. Алгоритм работы функции LatchCNT().
Функция GetCNT() предназначена для работы только с микросхемой 8254, т.к. использует режим «Read back command». Первый параметр CNT по-прежнему задает номер канала, а параметр Mode – запрашиваемое действие. При Mode = 0 функция возвращает текущее значение, при Mode = 1 – режим работы указанного канала (табл. 89).
Табл. 89. Формат управляющего регистра.
Значение счетчика
|
Биты
|
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
Значение
|
1
|
1
|
0
|
1
|
C2
|
C1
|
C0
|
X
|
Статус канала
|
Биты
|
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
|
Значение
|
1
|
1
|
1
|
0
|
C2
|
C1
|
C0
|
X
|
Коды выбора счетчика перечислены в табл. 90. Для упрощения не используется комбинация (C2=1, C1=1, C0=1).
Табл. 90. Коды выбора счетчика.
Счетчик
|
C2
|
C1
|
C0
|
0
|
0
|
0
|
1
|
1
|
0
|
1
|
0
|
2
|
1
|
0
|
0
|
Алгоритм работы функции GetCNT() может быть следующим (рис. 39). Вначале код канала CNT сдвигается влево на CNT+1 разрядов. Затем код необходимо сложить по маске 0E0h (получить статус канала) или по маске 0D0h (получить значение счетчика). Полученный управляющий код должен быть выведен в порт 43h. После этого может быть прочитано содержимое регистра счетчика, вначале младший значимый байт, а затем старший.
Исходный текст функции приведен ниже.
function GetCNT(CNT,Mode: Byte): Word;
begin
asm
{ Сохранение в стеке используемых регистров }
PUSH AX
PUSH BX
PUSH DX
{ Сохранение номера счетчика в AL }
MOV AL,CNT
{ Сдвиг на CNT+1 влево содержимого AL для размещения
номера канала в разрядах D3D2D1 управляющего кода }
MOV CL, CNT
INC CL
SHL AL,CL
{ Определение запрашиваемого действия }
CMP Mode, 0
JNE @@1
{ Сложение по маске $0E0 }
OR AL, 0E0h
JMP @@2
{ Сложение по маске $0D0 }
@@1: OR AL, 0D0h
@@2: MOV DX, 43h
{ Передача кода в управляющий регистр }
OUT DX, AL
{ Получить адрес счетчика }
MOV DH, 0
MOV DL, 40h
ADD DL, CNT
{ Получение младшего байта регистра счетчика }
IN AL, DX
MOV AH, 0
{ Вновь определение запрашиваемого действия }
CMP Mode, 0
JNE @@3
{ Сохранение младшего байта в AH }
MOV AH, AL
{ Получение старшего байта регистра счетчика }
IN AL, DX
{ Обмен младшего и старшего байтов аккумулятора }
XCHG AL, AH
{ Функция возвращает результат }
@@3: MOV @Result, AX
{ Восстановление из стека содержимого регистров }
POP DX
POP BX
POP AX
end;
end;
Рис. 39. Алгоритм работы функции GetCNT().
Пример 2. Используя ассемблер TASM, разработать программу определения внутренней тактовой частоты микропроцессора в МГц. Использовать режим работы ассемблера IDEAL и упрощенные директивы сегментации. Для подсчета количества тактов процессора применить команду RDTSC (код операции 0F31h).
Решение. Команда RDTSC предназначена для загрузки в регистровую пару EDX:EAX содержимого 64-разрядного регистра MSR (Model Specific Register). Он используется для хранения значения счетчика тактовых импульсов TSC (Time Stamp Counter). Значение счетчика увеличивается на единицу с приходом очередного такта микропроцессора, а обнуление происходит при системном сбросе. Заметим, что команда RDTSC работает на микропроцессорах Intel Pentium и выше.
Внутренняя тактовая частота микропроцессора может быть рассчитана исходя из количества тактов, прошедших за временной интервал, равный периоду следования импульсов. Число тактов определяется с помощью команды RDTSC по содержимому регистра MSR в моменты изменения значения таймера. Эти изменения можно отследить с помощью переменной BIOS, расположенной по адресу 0040:006Ch. На рис. 40 показан алгоритм вычисления тактовый частоты.
Установить начальные значения параметров для интервалов
Нет
Получить текущее время, выраженное в 1/18.2 долях секунды
Да
Расчет длительности интервалов в тактах
Задержка на время 1/18.2 доли секунды
Расчет среднего
значения длительности
интервала в тактах
Выполнить
команду RDTSC
Расчет тактовой
частоты в МГц
Сохранить значение счетчика TSC
Выход в
MS-DOS
Рис. 40. Алгоритм расчета внутренней тактовой частоты процессора.
Для повышения точности вычислений необходима выборка, по которой будут рассчитаны длительности каждого интервала в тактах, а также среднее значение длительности интервалов. Учитывая частоту генератора и максимальное значение коэффициента делителя для нулевого канала, расчетная формула внутренней частоты процессора будет иметь вид:
или
Количество замеров для вычисления среднего значения длительности интервалов может быть произвольным, исходя из принципа «чем больше, тем лучше». Однако в случае реализации на ассемблере оптимальным является выбор значения, на единицу большего числа, кратного степени 2. Связано это с тем, что операция деления на 2n в двоичной арифметике реализуется простым сдвигом на n разрядов вправо. Число итераций равно 33, а количество интервалов 32.
Возможный вариант программы, реализующей алгоритм вычислений, представлен ниже.
IDEAL
.386
MODEL MEDIUM
STACK 100h
DATASEG
; Массив для хранения значений отсчетов счетчика
; тактов в моменты срабатывания системного таймера
TimeCounter DD 33 DUP(?)
; Разность между соседними замерами
DeltaT DD 32 DUP(?)
; Среднее значение длительности интервала
AverageTime DD ?
; Предыдущее значение системного таймера
Time DD ?
; Результат вычислений
FreqResult DD ?
ENDS
CODESEG
PROC ProcFrequency
; Настроить регистр DS на глобальный сегмент данных
MOV AX, DGROUP
MOV DS, AX
; Настроить сегментный регистр ES на область данных BIOS
MOV AX, 0
MOV ES, AX
; Инициализация начальных параметров
MOV BX, 33
MOV DI, OFFSET TimeCounter
; Текущее время, выраженное в 1/18.2 долях секунды
MOV EAX, [ES:046Ch]
MOV [Time], EAX
; Сохранение значений счетчика TSC каждые 1/18.2 сек
@@t0:CALL WaitTimerStateChange
; Выполнить команду RDTSC
DB 0Fh, 31h
; Сохранить значение в массиве
MOV [DI], EAX
; Переход к следующему элементу массива
ADD DI, 4
DEC BX
; Число замеров равно 33
JNZ @@t0
; Вычислить длительность каждого интервала в тактах
MOV BX, 32
MOV DI, OFFSET TimeCounter
MOV SI, OFFSET DeltaT
MOV EDX, 0
; Из значения счетчика в момент t+1 вычесть значение в момент t
@@t1:MOV EAX, [DI+4]
SUB EAX, [DI]
MOV [SI], EAX
; Накопление суммы значений
ADD EDX, EAX
; Индексирование массивов
ADD DI, 4
ADD SI, 4
DEC BX
JNZ @@t1
; Вычислить среднюю длительность интервала в тактах
SHR EDX, 5
MOV [AverageTime], EDX
; Умножить среднее значение на частоту генератора
MOV EAX, [AverageTime]
MOV EDX, 1193180
MUL EDX
; Разделить результат на делитель таймера (65536)
SHRD EAX, EDX, 16
; Вычислить частоту микропроцессора в МГц
XOR EDX, EDX
; Разделить результат на 106
MOV EBX, 1000000
DIV EBX
; Сохранить результат вычислений
MOV [FreqResult],EAX
; Выход в MS DOS
MOV AX, 4C00h
INT 21h
ENDP ProcFrequency
; Процедура ожидания очередного изменения значения таймера
PROC WaitTimerStateChange NEAR
MOV EAX, [Time]
; Цикл до тех пор, пока текущее значение времени
; не будет отличаться от ранее сохраненного значения
@@T: CMP EAX, [ES:046Ch]
JE @@T
; Сохранить новое значение времени
MOV EAX,[ES:046Ch]
MOV [Time], EAX
RET
ENDP WaitTimerStateChange
ENDS
END
Пример 3. Разработать подпрограмму, организующую задержку на заданный временной интервал. Минимальный временной интервал может быть меньше периода следования импульсов от системных часов.
Решение. При разработке измерительных приложений часто требуется организовать временные задержки для выполнения различных программных операций. Такая необходимость может быть связана, например, с ожиданием окончания переходных процессов, получением данных, синхронизацией критичных по времени операций.
Самый простой способ организовать задержку – определить пустой цикл. В этом случае может потребоваться достаточно много времени для того, чтобы добиться нужного времени задержки. Однако даже если и удастся определить требуемую длительность, нельзя быть уверенным, что программа будет давать нужное время задержки при всех условиях. Длительность цикла может меняться в зависимости от используемого компилятора, скорости процессора и других факторов.
Даже цикл на языке ассемблера может приводить к различным временам задержки. Поэтому разумно определять время программной задержки непосредственно по часам. Частота отсчета в 18.2 Гц, используемая в модификации счетчика времени суток, должна вполне удовлетворять большинство потребностей.
Чтобы обеспечить задержку заданной продолжительности, программа должна подсчитать требуемое число импульсов счетчика времени суток. Значение добавляется к считанному текущему значению счетчика. Затем программа постоянно считывает текущее значение счетчика и сравнивает его с запомненным значением. Когда достигается равенство, то считается, что требуемая задержка прошла. Значение счетчика времени суток хранится по адресу 0040:006Ch.
Более высокую дискретность отсчетов, требуемую по условиям задачи, можно достичь, воспользовавшись счетчиком тактовых импульсов TSC. Однако в этом случае требуется предварительная калибровка таймера. Первым шагом алгоритма будет вычисление среднего числа тактов за период системного таймера и расчет продолжительности одного такта (см. задачу 2).
Дело в том, что использование паспортных данных процессора приведет к неточности, т.к. всегда имеется некоторый технологический разброс значений параметра. Лучший выход в данной ситуации – определение значения экспериментальным путем. Далее применим адаптированный для счетчика TSC приведенный выше алгоритм. Описанные алгоритмы организации временных задержек реализуйте самостоятельно на языке ассемблера, используя TASM.
Do'stlaringiz bilan baham: |