Вы находитесь на странице: 1из 27

6/6/3.

Трудности, связанные с расширением диапазона измерения интервалов


времени между двумя событиями захватов и способы их преодоления.
Расширение диапазона измерения периода в 2 раза.

С измерением длительности периода я "вожусь" не просто так, а с "дальним


прицелом" как на измерение длительности импульса, так и на измерение интервалов
времени между двумя случайными/псевдослучайными событиями.
С "регистрацией" этих событий (любого вида) проблем нет, так как, по факту каждого
из них, происходит малоинерционный захват с "уходом" в прерывание.
Главная проблема "дислоцирована" между ними, и она заключается в эффективном
преодолении трудностей, связанных с расширением диапазона измерения периода.
Таким образом, классность того или иного устройства измерения интервала времени
между двумя событиями, напрямую зависит от ширины его диапазона измерения.
Номинал верхней его границы определяется интервалом времени шага измерения и
"выше его не прыгнешь".
Используя внешний источник калиброванного такта, можно довести шаг измерения до
0,1 мкс.
Обычно этим и ограничиваются.
Номинал нижней его границы определяется "вычислительной мощью" и аппаратными
возможностями м/контроллера.
С первого, поверхностного взгляда, отодвинуть, в "заоблачные дали", нижнюю границу
диапазона измерения, не составляет большого труда.
Для этого нужно всего – навсего организовать счетчик и, в интервале времени между
двумя захватами, посчитать количество переполнений TMR1.
Далее - дело техники.
Если такой счетчик сделать многобайтным (организовать переносы из младших
разрядов в старшие), то можно измерять "огроменные" интервалы времени.
Чем не "счастье"?
Я тоже так, до поры, до времени, наивно думал, пока не столкнулся "лоб в лоб" с
конкретикой.
Давайте разберемся.
Прежде чем посчитать количество переполнений TMR1, их нужно "засечь" (обнаружить).
Следовательно, нужно организовать проверку состояния флага переполнения TMR1
(название флага TMR1IF).
Да, это сделать можно, причем, с неимоверной легкостью, но нужно определиться, в
какую именно процедуру "врезать" эту проверку?
Это очень интересный вопрос.
Он из категории "твердых орешков", и ответ на него подобен "предательскому
выстрелу в спину" (или "кирпичу, свалившемуся на голову").
Если Вы еще имеете смутные представления о таком "явлении природы", как "голый
зад проблемы", то сейчас я попытаюсь объяснить что это такое (естественно, речь
идет о частном случае), и как с этим бороться.
Имеется два "места", в которые, теоретически, можно "врезать" группу команд проверки
состояния флага переполнения TMR1: ПП прерывания и "плавающая" задержка.
В ПП прерывания "врезаться" нельзя, так как рабочая точка программы "проносится
через нее с реактивным свистом", и поэтому ничего толкового в ней не насчитаешь.
Остается "плавающая" задержка.
Она вполне подходит для реализации желаемого, так как рабочая точка программы
долго в ней "пасется", и формально (без учета специфики), "врезавшись" в нее, можно
посчитать количество переполнений TMR1, происходящих в интервале времени между
прерываниями.
Но все это "великолепие разбивается о железобетонный факт": "уход" в прерывание
может произойти с любой команды, входящей в "зону" разрешения прерываний.
Соответственно, возврат из прерывания также может произойти на любую команду,
входящую в "зону" разрешения прерываний.
Кроме бит – ориентированной команды проверки состояния флага TMR1IF, в состав
"врезки", как минимум (и обязательно), должны входить команда инкремента
содержимого регистра, назначенного счетчиком количества переполнений, и команда
сброса флага TMR1IF (чтобы был возможен следующий инкремент).
1
А теперь давайте предположим, что переполнения TMR1 не было, но возврат
произошел на команду инкремента.
Что будет?
А будет то, что команда инкремента исполнится, и будет подсчитано переполнение,
которого, по факту, не было ("паразитное" переполнение).
Если же возврат произойдет на команду сброса флага TMR1IF, то, при фактическом
наличии переполнения TMR1, оно подсчитано не будет.
Получается числовое "супербардальеро с тяжкими последствиями" (шаг "гуляния" - аж
4096 мкс.).
Я уж не говорю об увеличении времени исполнения цикла "плавающей" задержки и
суммарного времени исполнения обеих сценариев ПП прерывания ("подрезание"
верхней границы диапазона измерения), которые "автоматически прилагаются" к этому
"супербардальеро" (за что "кровь пролита"?!) …
Ради "спортивного интереса", я попробовал проигнорировать сказанное выше (составил
соответствующую программу, "прогнал" ее через "железо") и неумолимо заполучил
указанное выше "супербардальеро", в виде хаотических смен показаний с кратностью в
4096 мкс.
И ничего с этим не поделаешь, так как нельзя запретить возврат на "неугодные"
команды.
Вот Вам и "голый зад проблемы" в виде однозначно тупиковой ситуации.
Объективности ради, нужно признать, что, в части касающейся расширения диапазона
измерения периода, пропущен классный "удар в челюсть".
Как тут не "озвереть"?
Полундра!!! Всеобщая мобилизация! Летающие коровы и весь арсенал стрелкового
оружия - к бою.
Задача - "завалить голый зад проблемы".

???
Вопрос: "Как быть" ("Быть иль не быть")?
Ответ: думать, думать и еще раз думать.
Далее следует: ??????!ёклмн(ёпрст и еще много кое-чего)?!!!...?!убью!??ах, ты так…!?
больно??!!держи сдачу!??! и так далее, до "вставай, что разлегся"? (до "выноса тела")
Как это не прискорбно, но нужно поставить "большой и жирный крест" на указанной
выше, "тупиковой ветви развития" (эх … а счастье было так близко) и, с учетом
требования обеспечения автоматического переключения между поддиапазонами, искать
иные способы расширения диапазона измерения периода.
Сие означает то, что нужно "уносить ноги" из "плавающей" задержки и "оставить ее в
покое".
И это уже не "хухры-мухры", а "пахнет революцией" ("сменой общественного строя").
А раз это так, то изначально, высоко "задирать планку" требований не стоит.
Для начала (по принципу "от простого к сложному"), достаточно и двухкратного
расширения диапазона измерения периода, причем, это должно быть сделано так,
чтобы не "подрезать" верхнюю границу "материнского" диапазона.
Вопрос: "Если плавающая задержка "оставлена в покое" и в нее нельзя "врезать"
группу команд подсчета количества переполнений TMR1, то в каком "месте" можно
"засечь" переполнение TMR1 и каково должно быть максимальное количество этих
переполнений"?
Следует сразу же уточнить, что, в процедуре вычитания по кольцу, факт переполнения
TMR1 "засекается" (по состоянию бита №0 регистра BitC), но это переполнение имеет
место быть (а может и не быть) в диапазоне измерения периода от 1 до 4095 мкс., и
оно не есть то переполнение TMR1, о котором идет речь в вопросе.
А в нем идет речь о переполнении TMR1, которое возникает не в диапазоне
измерения от 1 до 4095 мкс., а в диапазоне измерения от 4096 до 8191 мкс.
Получается то, что, по логике "материнской" программы (Hz_Mks_1.asm), в диапазоне
измерения от 1 до 4095 мкс., за один цикл измерения периода, флаг TMR1IF может
либо вообще не подняться, либо подняться 1 раз, а в диапазоне измерения от 4096
до 8191 мкс., флаг TMR1IF может подняться либо 1 раз, либо 2 раза.
В условиях отсутствия подсчета количества переполнений TMR1 (а это и имеет место
быть), и одно, и два поднятие флага TMR1IF (а также и большее количество
2
поднятий) будет "восприниматься" программой как одно поднятие, что свидетельствует
об отсутствии четкого критерия разделения поддиапазонов и "режет на корню всю
затею".
Следовательно, при работе в расширенном диапазоне (от 1 до 8191 мкс.), нужно
"привязаться" к количеству переполнений TMR1, не требующему подсчета, то есть, к
одному переполнению TMR1, которое должно произойти при работе на поддиапазоне
от 4096 до 8191 мкс., и которое не должно произойти при работе на поддиапазоне от
1 до 4095 мкс.
Для обнаружения одного переполнения TMR1, достаточно только проверить состояние
флага переполнения TMR1, и подсчитывать ничего не нужно.
Вывод: нужно "убить" то переполнение TMR1, которое возникает при работе на
поддиапазоне от 1 до 4095 мкс. (при счете по кольцу).
То есть, при работе на поддиапазоне от 1 до 4095 мкс., переполнения TMR1 быть не
должно.
Следовательно, процедура вычитания по кольцу оказывается "не при деле", так как
она построена по принципу "засекания" переполнения TMR1, происходящего при работе
на поддиапазоне от 1 до 4095 мкс. (поднятие флага С свидетельствует о факте
переполнения TMR1).
Это не в коем разе не "умаляет достоинств" процедуры вычитания по кольцу.
Просто, в данном случае, она оказывается ненужной, но в других случаях, она может
оказаться нужной (эта "деталька" обязательно должна быть в Вашем "конструкторе").
Теперь возникает вопрос: "Что нужно сделать для того, чтобы, при работе на
поддиапазоне от 1 до 4095 мкс., переполнения TMR1 не было"?
Разумный ответ только один: после возникновения события 1-го захвата, TMR1H/TMR1L
должен считать от нуля.
То есть, требуется предустановка нуля.
В идеале, TMR1H/TMR1L должен быть сброшен в ноль событием 1-го захвата, но это
технически не предусмотрено, следовательно, предустановку нуля в TMR1H/TMR1L
нужно произвести программно, в ходе отработки 1-го сценария ПП прерывания.
Но ведь, с момента возникновения события 1-го захвата и до момента окончания
записи (предустановки) в TMR1H/TMR1L, пройдет несколько машинных циклов счета, и
если предустановить в TMR1H/TMR1L ноль, то будут проблемы при измерениях
длительности периода в конце 1-го (от 1 до 4095 мкс.) поддиапазона, связанные с
нарушением "привязки" момента поднятия флага TMR1IF к переходу от состояния .
65535 к состоянию .0.
Поднятие флага будет происходить, например, при переходе от .65529 к .65530 ("до
того"), что тут же создаст "бардак" в показаниях.
Следовательно, в регистр старшего разряда TMR1H, нужно записать 0, а в регистр
младшего разряда TMR1L, нужно записать корректирующую константу (поправку).
Не трудно догадаться, что числовое значение этой корректирующей константы будет
строго фиксированным и равным количеству машинных циклов, отработанных, с
момента "ухода" в 1-е прерывание (начиная с "виртуальной" команды call) и до
момента окончания предустановки TMR1H/TMR1L.
Предположим, что предустановка (с четкой "привязкой" момента поднятия флага
TMR1IF к моменту смены .65535 на .0) произошла, и TMR1H/TMR1L начал считать
внутренний такт ПИКа от предустановленного значения.
Ну и пусть себе считает на здоровье (за то и боролся).
Далее, для того чтобы обеспечить условия дальнейшей, объективной проверки
состояния флага TMR1IF, нужно его опустить (это флаг 2-й группы: сбрасывается
программно).
Итак, в 1-м прерывании, все необходимые дела сделаны, но суммарное количество
машинных циклов исполнения обеих сценариев ПП прерывания "вылезло" из
обозначенных ранее "рамок", что соответствует "подрезанию" верхней границы
диапазона измерения.
Как быть?
А очень просто - "убить" группу команд копирования содержимого TMR1 в CCPR1, а
это целых 4 машинных цикла.
И в самом деле, при наличии предустановки TMR1H/TMR1L, эта группа команд просто
не нужна.
3
По этой же причине, ненужными оказываются регистры Temp1_H и Temp1_L.
А раз это так, то можно, "со спокойной совестью", "убить" ПП вычитания по кольцу.
Остается только регистр Temp2_H/Temp2_L, который я переименовал в
Temp_H/Temp_L и в котором "напрямую" (без использования процедуры вычитания по
кольцу) "оседает" (в двоичной форме) результат замера периода, умноженный на 16.
Вот сюрприз, так сюрприз!
Недаром говорят, что нет худа без добра.
Даже "вырисовывается" нечто лучшее, чем "мать".
Уже значительно веселее и жизнерадостнее.
И "зад проблемы" как-то "съежился".
"Раскручиваю" дальше.
Итак, рабочая точка программы вернулась из 1-го прерывания в "плавающую"
задержку, и TMR1 считает от предустановленного значения.
Считает… Считает…
Возникло событие 2-го захвата (подсчет закончен), и рабочая точка программы
"метнулась" во 2-й сценарий ПП прерывания.
Теперь нужно выяснить, на каком из поддиапазонов, на момент возникновения события
2-го захвата, происходил счет?
На это указывает состояние флага TMR1IF.
Следовательно, нужно произвести проверку состояния флага TMR1IF, и, в зависимости
от ее результата, сделать вывод о том, в границах какого из поддиапазонов находится
результат текущего замера.
Если флаг TMR1IF не поднялся (напоминаю, что в 1-м прерывании он "принудительно"
опускается), то результат текущего замера находится в границах 1-го поддиапазона (от
1 до 4095 мкс.), а если флаг поднялся (произошло переполнение TMR1), то результат
текущего замера находится в границах 2-го поддиапазона (от 4096 до 8191 мкс.).
Но выводом "сыт не будешь".
Его нужно "задокументировать" для того, чтобы позднее (после выхода из "зоны"
разрешения прерываний от модуля CCP) произвести суммирование обработанного
результата (того, который получится после деления на 16) текущего замера с числом
4096 (если флаг TMR1IF поднялся) или не производить его (если флаг TMR1 не
поднялся).
Пояснение: если не учитывать этой "поддиапазонной поправки" (.4096) то счет будет
происходить по кольцу (… 4094, 4095, 0, 1, 2 …).
Указанное выше "документирование" состояния флага TMR1IF, происходит в бите №0
регистра Bit.
Если флаг TMR1IF поднялся, то в Bit,0 записывается 1.
Если флаг TMR1IF не поднялся, то в Bit,0 записывается 0.
Желательно произвести проверку состояния флага TMR1IF и "документирование" этого
состояния в самом начале 2-го сценария ПП прерывания, то есть, на минимальном
удалении (по количеству машинных циклов) от момента возникновения 2-го события
захвата.
Но так или иначе, из-за наличия этого, хотя и минимального, но удаления, существует
вероятность поднятия флага TMR1IF при измерении длительности периода величиной
4095 мкс. ("пограничное" состояние между диапазонами).
В этом случае, на индикацию будет выведено не число 4095, а число 4095 + 4096 =
8191.
И это не "голые слова". Я эту "бяку" наблюдал лично.
Каков выход из положения?
А он очень простой: после окончания отработки процедуры деления на 16, нужно
обнаружить число .4095, и в случае его обнаружения, необходимо опустить флаг
TMR1IF, причем, это нужно сделать до проверки типа "плюсовать к результату замера
число 4096 или нет"?
Ниже Вы сами убедитесь, как это просто делается.
Вот, собственно говоря, и вся премудрость, в результате реализации которой, "на гора"
выдается программа, объем которой увеличился всего на 4 команды (по сравнению
с "материнской").
Это раз.
При этом, диапазон измерения длительности расширился в 2 раза.
4
Это два.
Верхняя граница диапазона измерения не "подрезалась" и осталась прежней
(1 мкс.).
Это три.
Что касается последнего, то я все-таки "втиснулся" в ранее обозначенные "рамки"
19-ти машинных циклов (19 м.ц. - максимально допустимое, суммарное количество
машинных циклов исполнения обеих сценариев ПП прерывания, без учета команд
retfie).
Правда, суммарное количество машинных циклов исполнения обеих сценариев моей
ПП прерывания составляет именно те самые 19 м.ц., но оно вполне приемлемо и
находится в тех "рамках", в которых верхняя граница диапазона измерения не
"режется".
Проверил. На частоте 1 Мгц., на индикацию выводится устойчивая единичка, плюс, в
части касающейся остального, сформулированная выше задумка, оказалась вполне
"жизнеспособной".
Предлагаю Вашему вниманию программу Hz_Mks_2.asm, в которой эта задумка
полномасштабно реализована:

;********************************************************************************
; Hz_Mks_2.asm ПРОГРАММА 2-ДИАПАЗОННОГО 8-РАЗРЯДНОГО ЧАСТОТОМЕРА-
; ИЗМЕРИТЕЛЯ ПЕРИОДА С ИСПОЛЬЗОВАНИЕМ ЖКИ МОДУЛЯ НА ОСНОВЕ HD44780.
; Для измерения периода, задействован модуль CCP, работающий в режиме захвата.
;********************************************************************************
; "Практикум по конструированию устройств на PIC контроллерах"
; (http://ikarab.narod.ru)
; Корабельников Евгений Александрович karabea@Lipetsk.ru
;********************************************************************************
; Длительности интервалов времени измерения частоты: 1сек., 10сек.
; Диапазон измерения периода от 1 до 8191 мкс., шаг измерения 1 мкс.
; "Помощник": ЖКИ модуль на основе м/контроллера HD44780: 2 строки по 16 символов
; 4-разрядный, двунаправленный интерфейс: задействованы выводы порта В RB4...7.
; Выводы порта В RB0 и RB1 задействованы под двухкнопочную клавиатуру
; (Кн1 и Кн2 соответственно).
; Вывод порта В RB3 - счетный вход модуля CCP, работающего в режиме захвата.
; Вывод порта В RB2 не задействован.
; Функции выводов порта А:
; RA0 - RW,
; RA1 - RS,
; RA2 - E,
; RA3 - блокировочный вывод,
; RA4 - счетный вход TMR0,
; Выводы RA3, RA4 и RB3 между собой электрически соединены.
; Кварц 4000 Кгц (1 м.ц.= 1 мкс.).
; Используется PIC16F628A.
;----------------------------------------------
; Объем программы: 491 слово в памяти программ.
;********************************************************************************
LIST p=16F628A ; "Привязка" MPLAB к микроконтроллеру
; PIC16F628A.
__CONFIG 03F25H ; Определение значений битов конфигурации:
; бит защиты выключен, WDT включен,
; стандартный генератор XT.
;================================================================================
; "Прописка" регистров специального назначения.
;================================================================================
IndF equ 00h ; Доступ к памяти через FSR.
Tmr0 equ 01h ; Таймер TMR0.
OptionR equ 01h ; Регистр Option-банк1.
PC equ 02h ; Счетчик команд.
Status equ 03h ; Регистр Status.
FSR equ 04h ; Регистр косвенной адресации.
PortA equ 05h ; Регистр Port A.
TrisA equ 05h ; Регистр Tris A-банк1.

5
PortB equ 06h ; Регистр Port B.
TrisB equ 06h ; Регистр Tris B-банк1.
IntCon equ 0Bh ; Регистр IntCon.
EEData equ 1Ah ; Регистр EEData-банк1.
EECon1 equ 1Ch ; Регистр EECon1-банк1.
EEAdr equ 1Bh ; Регистр EEAdr-банк1.
EECon2 equ 1Dh ; Регистр EECon2-банк1.
CMCON equ 1Fh ; Регистр управления модулем компараторов.
PIE1 equ 0Ch ; Регистр разрешения прерываний
; от периферийных модулей.
PIR1 equ 0Ch ; Регистр флагов прерываний
; от периферийных модулей.
T1CON equ 10h ; Регистр управления модулем таймера TMR1.
CCP1CON equ 17h ; Регистр управления модулем CCP.
CCPR1L equ 15h ; Регистр младшего разряда CCP.
CCPR1H equ 16h ; Регистр старшего разряда CCP.
TMR1L equ 0Eh ; Регистр младшего разряда TMR1.
TMR1H equ 0Fh ; Регистр старшего разряда TMR1.
;================================================================================
; "Прописка" регистров общего назначения.
;================================================================================
; Регистры оперативной памяти частотомера.
;---------------------------------------------------
LED0 equ 70h ; Регистр младшего разряда LED0.
LED1 equ 71h ; ------------"----------- LED1.
LED2 equ 72h ; ------------"----------- LED2.
LED3 equ 73h ; ------------"----------- LED3.
LED4 equ 74h ; ------------"----------- LED4.
LED5 equ 75h ; ------------"----------- LED5.
LED6 equ 76h ; ------------"----------- LED6.
LED7 equ 77h ; Регистр старшего разряда LED7.
;---------------------------------------------------
; Регистры оперативной памяти измерителя периода.
;---------------------------------------------------
LED00 equ 30h ; Регистр младшего разряда LED00.
LED11 equ 31h ; ------------"----------- LED11.
LED22 equ 32h ; ------------"----------- LED22.
LED33 equ 33h ; ------------"----------- LED33.
LED44 equ 34h ; ------------"----------- LED44.
LED55 equ 35h ; Регистр старшего разряда LED55.
;---------------------------------------------------
; Регистры счетчика частоты.
;---------------------------------------------------
TimerL equ 78h ; Младший.
TimerM equ 79h ; Средний.
TimerH equ 7Ah ; Старший.
TimerHH equ 7Bh ; Самый старший.
;---------------------------------------------------
; Регистры временного хранения "захваченных" чисел.
;---------------------------------------------------
Temp_L equ 2Ch ; Младший.
Temp_H equ 2Dh ; Старший.
;---------------------------------------------------
; Регистры счетчика "грубой" доводки.
;---------------------------------------------------
SecL equ 20h ; Младший.
SecM equ 21h ; Средний.
SecH equ 22h ; Старший.
;---------------------------------------------------
; Регистры счетчика "точной" доводки.
;---------------------------------------------------
Kalib equ 7Ch ; Регистр счетчика "точной" доводки 4 м.ц.
Kalib_1 equ 7Dh ; Регистр счетчика "точной" доводки 3 м.ц.
;---------------------------------------------------
; Прочие.
;---------------------------------------------------
6
Mem equ 7Eh ; Регистр буферной памяти 2/10 преобразования.
Mem_1 equ 7Fh ; Регистр оперативной памяти клавиатуры.
Total equ 23h ; Указатель разрядов.
Count1 equ 24h ; Многофункциональный регистр.
Count equ 25h ; Регистр числа выводимых в строку символов.
Flag equ 26h ; Регистр указателя конца замера периода.
Bit equ 27h ; Регистр указателя переполнения TMR1.
Temp_0 equ 28h ; "Обманный" регистр.
;================================================================================
; Присвоение названий местам размещения результатов операций.
;================================================================================
F equ 1 ; Результат направить в регистр.
W equ 0 ; Результат направить в аккумулятор.
;================================================================================
; Присвоение названий битам регистров специального назначения.
;================================================================================
RP0 equ 5 ; Бит переключения банков.
RW equ 0 ; Бит №0 регистра PortA (вывод RA0 - линия RW)
RS equ 1 ; Бит №1 регистра PortA (вывод RA1 - линия RS)
E equ 2 ; Бит №2 регистра PortA (вывод RA2 - линия E)
BF equ 7 ; Бит №7 регистра PortB.
Z equ 2 ; Флаг нулевого результата.
C equ 0 ; Флаг переноса-заема.
CCP1IF equ 2 ; Флаг прерывания от модуля CCP.
TMR1IF equ 0 ; Флаг прерывания по переполнению TMR1.
;================================================================================
org 2100h ; Разрешение записи в EEPROM надписи.
DE " http://www. " ; Надпись в ячейках с адресами
; .00h ... 15h.
DE " ikarab.narod.ru" ; Надпись в ячейках с адресами
; .16h ... 31h.
;================================================================================
; Подготовка к началу исполнения программы.
;================================================================================
org 0 ; Начальная точка отсчета в PC – нулевой адрес
goto START ; Переход на начало программы.
org 4 ; Начать исполнение ПП прерывания c команды,
; которая, в PC, имеет адрес 04h.
;********************************************************************************

;================================================================================
; Подпрограмма прерывания.
;================================================================================
; Анализ указателя конца замера периода.
;---------------------------------------
decfsz Flag,F ; Flag,F - 1 = ... Результат - в Flag.
goto WRITE_1 ; Если результат не=0, то производится
; проход №1.
; Если результат =0, то производится
; проход №2.
;--------------------------------------------------------------------------------
; Проход №2.
;--------------------------------------------------------------------------------
btfsc PIR1,TMR1IF ; Флаг прерывания по переполнению TMR1
; поднят или опущен?
bsf Bit,0 ; Если поднят, то Bit,0 устанавливается в 1.
; Если опущен, то в Bit,0 остается "лежать"
; ранее предустановленный 0 и программа
; исполняется далее.
movf CCPR1L,W ; Содержимое регистра CCPR1L
movwf Temp_L ; копируется в регистр Temp_L.
movf CCPR1H,W ; Содержимое регистра CCPR1H
movwf Temp_H ; копируется в регистр Temp_H.
bsf Status,Z ; Установка признака конца замера периода.
;-----------------------------------
7
bcf PIR1,CCP1IF ; Сброс флага прерывания от модуля CCP.
retfie ; Возврат из прерывания.
;--------------------------------------------------------------------------------
; Проход №1.
;--------------------------------------------------------------------------------
WRITE_1 movlw .9 ; Числовая
movwf TMR1L ; коррекция
clrf TMR1H ; TMR1H/TMR1L.

bcf Status,Z ; Установка признака продолжения замера


; периода.
;-----------------------------------
bcf PIR1,TMR1IF ; Сброс флага прерывания по переполнению TMR1.
bcf PIR1,CCP1IF ; Сброс флага прерывания от модуля CCP.
retfie ; Возврат из прерывания.
;********************************************************************************

;********************************************************************************
; ----------------------- "РАБОЧАЯ" ЧАСТЬ ПРОГРАММЫ ----------------------
;********************************************************************************
; "Рабочая" инициализация ЖКИ модуля.
;================================================================================
LCD_INIT movlw b'00101000' ; Установка: 4-разрядный интерфейс, 2 строки,
; 5х7 точек.
call ENTER_BF ; "Плавающая" задержка со стробом
; под команду 00101000.
;----> Возврат по стеку из ПП ENTER_BF.
LCD_INIT_1 movlw b'00101000' ; Установка: 4-разрядный интерфейс, 2 строки,
; 5х7 точек.
call ENTER_BF ; "Плавающая" задержка со стробом
; под команду 00101000.
;----> Возврат по стеку из ПП ENTER_BF.
movlw b'00001100' ; Установка: дисплей включен, видимое
; отображение курсора выключено.
call ENTER_BF ; "Плавающая" задержка со стробом
; под команду 00001100.
;----> Возврат по стеку из ПП ENTER_BF.
movlw b'00000001' ; Установка: очистка дисплея со сбросом
; данных, установка курсора в начало
; 1-й строки.
call ENTER_BF ; "Плавающая" задержка со стробом
; под команду 00000001.
;----> Возврат по стеку из ПП ENTER_BF.

return ; Возврат по стеку.


;================================================================================
; ПП "плавающей" задержки на основе анализа состояния флага занятости BF
; (вариант для 4-разрядного интерфейса).
;================================================================================
ENTER_BF movwf Mem ; Переправка старшего п/байта регистра W на
movwf PortB ; линии RB4...7 (состояния линий RB0...3
; не важны).
;------------------------------------------
; Запуск в работу старшего п/байта (строб).
;------------------------------------------
nop ; Задержка в 1 м.ц.
bsf PortA,E ; Установка на линии Е "1".
nop ; Задержка в 1 м.ц.
bcf PortA,E ; Установка на линии Е "0".
;------------------------------------------
swapf Mem,W ; Смена п/байтов с сохранением результата
; операции в W.
movwf PortB ; Переправка младшего п/байта регистра W на
; линии RB4...7 (состояния линий RB0...3
; не важны).
8
;------------------------------------------
; Запуск в работу младшего п/байта (строб).
;------------------------------------------
nop ; Задержка в 1 м.ц.
bsf PortA,E ; Установка на линии Е "1".
nop ; Задержка в 1 м.ц.
bcf PortA,E ; Установка на линии Е "0".
;-------------------------------------------------
; Проверка состояния флага занятости BF.
;-------------------------------------------------
; Подготовка к проверке.
;------------------------
bsf Status,RP0 ; Переход в 1-й банк.
movlw b'11110000' ; Запись в W "11110000"
movwf TrisB ; RB4...7 работают на вход, а RB0...3 работают
; на выход.
bcf Status,RP0 ; Переход в 0-й банк.

bcf PortA,RS ; Установка на линии RS "0" (режим команд).


bsf PortA,RW ; Линия RW в "1" (режим чтения данных).
nop ; Задержка в 1 м.ц.
bsf PortA,E ; Установка на лини Е "1".
POVTOR nop ; Задержка в 1 м.ц.
;-----------------------
; Сама проверка.
;-----------------------
btfsc PortB,BF ; Проверка состояния флага занятости BF.
goto POVTOR ; Если BF=1, то продолжение задержки до тех
; пор, пока BF не установится в "0"
; (программа исполняется далее).
;-----------------------
; Завершение процедуры.
;-----------------------
clrf PortA ; Сброс в "0" всех защелок порта А
; (RW=0, RS=0, E=0).
bsf Status,RP0 ; Переход в 1-й банк.
clrf TrisB ; Все выводы порта В работают на выход.
bcf Status,RP0 ; Переход в 0-й банк.

return ; Возврат по стеку.


;================================================================================
; Чтение данных из текущей ячейки EEPROM памяти.
;================================================================================
EEPR bsf Status,RP0 ; Переход в 1-й банк.
movwf EEAdr ; Выбор адреса ячейки.
bcf EECon1,2 ; Запрет записи.
bsf EECon1,0 ; Разрешение чтения.
movf EEData,W ; Считывание байта в регистр W.
bcf Status,RP0 ; Переход в 0-й банк.
return ; Возврат по стеку.
;********************************************************************************
; Вывод на индикацию результатов измерений и сопровождающих их надписей.
;********************************************************************************
DISPLAY call LCD_INIT_1 ; Условный переход в процедуру "рабочей"
; инициализации.
;----> Возврат по стеку из ПП LCD_INIT_1.
;-----------------------------------------
; Задание положения курсора в 1-й строке.
;-----------------------------------------
movlw b'10000011' ; Копирование в W адреса 4-го слева
; знакоместа.
call ENTER_BF ; "Плавающая" задержка со стробом под команды
; выбора ячеек DD RAM.
;----> Возврат по стеку из ПП ENTER_BF.
;================================================================================
; Вывод результатов измерения периода в 1-ю строку.
9
;================================================================================
movlw .6 ; Задание количества знакомест (десятичных
movwf Total ; разрядов дисплея), в которые будут
; выводиться результаты измерений.
;----------------------------------------------------------
; Косвенная адресация к содержимому текущих LED.
;----------------------------------------------------------
movlw LED55 ; Стандартная процедура косвенной адресации с
movwf FSR ; "переправкой" содержимого регистра, сначала,
SNOVA_1 movf IndF,W ; LED55, а затем и всех остальных LED
; (44,33,22,11,00) в регистр W.
;----------------------------------------------------------
; Вывод символов результатов измерения периода в 1-ю строку
; ("врезка" в процедуру косвенной адресации).
;----------------------------------------------------------
bsf PortA,RS ; Установка на линии RS "1"
; (режим записи данных).
call ENTER_BF ; "Плавающая" задержка со стробом под вывод
; данных на индикацию.
;----> Возврат по стеку из ПП ENTER_BF.
;----------------------------------------------------------
; Переход на следующий LED, который далее станет текущим.
;----------------------------------------------------------
decf FSR,F ; Уменьшение адреса текущего LED на 1.
;----------------------------------------------------
decf Total,F ; Уменьшение содержимого указателя разрядов
; на 1.
btfss Status,Z ; Анализ состояния флага нулевого результата Z
goto SNOVA_1 ; Если результат декремента Total не=0, то
; переход в ПП SNOVA_1.
; Если он =0, то программа исполняется далее.
;----------------------------------------------------------
; Вывод на индикацию надписи "мкс".
;----------------------------------------------------------
movlw 0BCh ; Запись в W символа "м" (0BCh).
bsf PortA,RS ; Установка на линии RS "1"
; (режим записи данных).
call ENTER_BF ; "Плавающая" задержка со стробом под вывод
; данных на индикацию.
;----> Возврат по стеку из ПП ENTER_BF.
movlw 0BAh ; Запись в W символа "к" (0BAh).
bsf PortA,RS ; Остальное - то же самое.
call ENTER_BF ; -----------"------------
;----> Возврат по стеку из ПП ENTER_BF.
movlw 63h ; Запись в W символа "с" (63h).
bsf PortA,RS ; ; Остальное - то же самое.
call ENTER_BF ; -----------"------------
;----> Возврат по стеку из ПП ENTER_BF.
;================================================================================
; Вывод результатов измерения частоты во 2-ю строку.
;================================================================================
; Вычисляемый переход в зависимости от установленного
; предела измерения (1/10 сек.)
;----------------------------------------------------
movfw Mem_1 ; Копирование содержимого регистра Mem_1
; в регистр W.
addwf PC,F ; Сам вычисляемый переход.
nop ; Для 1 сек.
goto $+3 ; Для 1 сек.
movlw b'11000001' ; Для 10 сек. (копирование в W адреса 2-го
; слева знакоместа).
;----------------------------------------------------
goto $+2 ; Переход на команду call ENTER_BF.
movlw b'11000010' ; Копирование в W адреса 3-го слева
; знакоместа.
call ENTER_BF ; "Плавающая" задержка со стробом под команды
10
; выбора ячеек DD RAM.
;----> Возврат по стеку из ПП ENTER_BF.
;----------------------------------------------------------
; Определение позиции результатов измерения в строке.
;----------------------------------------------------------
movlw .8 ; Задание количества знакомест (десятичных
movwf Total ; разрядов дисплея), в которые будут
; выводиться результаты измерений.
;----------------------------------------------------------
; Косвенная адресация к содержимому текущих LED.
;----------------------------------------------------------
movlw LED7 ; Стандартная процедура косвенной адресации с
movwf FSR ; "переправкой" содержимого регистра, сначала,
SNOVA movf IndF,W ; LED7, а затем и всех остальных LED
; (6,5,4,3,2,1,0) в регистр W.
;----------------------------------------------------------
; Вывод символов результатов измерения во 2-ю строку
; ("врезка" в процедуру косвенной адресации).
;----------------------------------------------------------
bsf PortA,RS ; Установка на линии RS "1"
; (режим записи данных).
call ENTER_BF ; "Плавающая" задержка со стробом под вывод
; данных на индикацию.
;----> Возврат по стеку из ПП ENTER_BF.
;----------------------------------------------------------
; Переход на следующий LED, который далее станет текущим.
;----------------------------------------------------------
decf FSR,F ; Уменьшение адреса текущего LED на 1.
;----------------------------------------------------
; Вычисляемый переход в зависимости от установленного
; предела измерения (1/10 сек.)
;----------------------------------------------------
movfw Mem_1 ; Копирование содержимого регистра Mem_1
; в регистр W.
addwf PC,F ; Сам вычисляемый переход.
nop ; Для 1 сек. (запятая не выставляется).
goto NO_PIN ; Для 1 сек. (запятая не выставляется).
; Для 10 сек. - программа исполняется далее
; (запятая выставляется).
;----------------------------------------------------
; Установка запятой между предпоследним и последним
; десятичными разрядами, выводимыми на индикацию.
;----------------------------------------------------
movfw Total ; Копирование содержимого указателя разрядов
; в регистр W.
sublw .2 ; .2 - W(Total)=?
btfss Status,Z ; Анализ состояния флага нулевого результата Z
goto NO_PIN ; Если результат вычитания не=0, то запятая
; не выставляется.
movlw 2Ch ; Если результат вычитания =0, то запятая
; выставляется {запись в W символа
; "запятая" (2Сh)}.
bsf PortA,RS ; Установка на линии RS "1"
; (режим записи данных).
call ENTER_BF ; "Плавающая" задержка со стробом под вывод
; данных на индикацию.
;----> Возврат по стеку из ПП ENTER_BF.
;----------------------------------------------------
; Здесь запятая не выставляется.
;----------------------------------------------------
NO_PIN decf Total,F ; Уменьшение содержимого указателя разрядов
; на 1.
btfss Status,Z ; Анализ состояния флага нулевого результата Z
goto SNOVA ; Если результат декремента Total не=0,
; то переход в ПП SNOVA.
; Если он =0, то программа исполняется далее.
11
;--------------------------------------------------------------------------------
; Формирование и вывод на индикацию символов, расположенных правее младшего
; разряда результата измерения.
;--------------------------------------------------------------------------------
movlw 0B4h ; Запись в W символа "г" (0B4h).
bsf PortA,RS ; ; Остальное - то же самое.
call ENTER_BF ; -----------"------------
;----> Возврат по стеку из ПП ENTER_BF.

movlw 0E5h ; Запись в W символа "ц" (0E5h).


bsf PortA,RS ; Остальное - то же самое.
call ENTER_BF ; -----------"------------
;----> Возврат по стеку из ПП ENTER_BF.

movlw 20h ; Запись в W символа "пробел" (20h).


bsf PortA,RS ; Остальное - то же самое.
call ENTER_BF ; -----------"------------
;----> Возврат по стеку из ПП ENTER_BF.
;------------------------------------------
; Выбор надписи (" 1c" или "10с").
;------------------------------------------
movfw Mem_1 ; Копирование содержимого регистра Mem_1 в W.
addwf PC,F ; Сам вычисляемый переход.
nop ; Для 1 сек.
goto SEC_1 ; Для 1 сек.
; Для 10 сек. - программа исполняется далее.
;------------------------------------------
; Вывод на индикацию надписи "10с".
;------------------------------------------
movlw 31h ; Запись в W символа "1" (31h).
bsf PortA,RS ; ; Остальное - то же самое.
call ENTER_BF ; -----------"------------
;----> Возврат по стеку из ПП ENTER_BF.
movlw 30h ; Запись в W символа "0" (30h).
bsf PortA,RS ; ; Остальное - то же самое.
call ENTER_BF ; -----------"------------
;----> Возврат по стеку из ПП ENTER_BF.
movlw 63h ; Запись в W символа "c" (63h).
bsf PortA,RS ; ; Остальное - то же самое.
call ENTER_BF ; -----------"------------
;----> Возврат по стеку из ПП ENTER_BF.
goto INKEY ; Переход на опрос клавиатуры
; (на новый цикл измерения).
;------------------------------------------
; Вывод на индикацию надписи " 1c".
;------------------------------------------
SEC_1 movlw 20h ; Запись в W символа "пробел" (20h).
bsf PortA,RS ; ; Остальное - то же самое.
call ENTER_BF ; -----------"------------
;----> Возврат по стеку из ПП ENTER_BF.
movlw 31h ; Запись в W символа "1" (31h).
bsf PortA,RS ; ; Остальное - то же самое.
call ENTER_BF ; -----------"------------
;----> Возврат по стеку из ПП ENTER_BF.
movlw 63h ; Запись в W символа "c" (63h).
bsf PortA,RS ; ; Остальное - то же самое.
call ENTER_BF ; -----------"------------
;----> Возврат по стеку из ПП ENTER_BF.

goto INKEY ; Переход на опрос клавиатуры


;(на новый цикл измерения).
;********************************************************************************
; Начало исполнения "программы".
;********************************************************************************
; Подготовительные операции.
;----------------------------------------
12
START clrf IntCon ; Запрет всех прерываний.
clrf PortA ; Сброс защелок порта A в "0"
; (RS=0, RW=0, E=0).
bsf Status,RP0 ; Переход в 1-й банк.
clrf TrisB ; Все выводы порта В работают на выход.
movlw b'00010000' ; Вывод RA4/TOCKI работает на вход,
movwf TrisA ; остальные - на выход.
movlw b'00100111' ; Предделитель с Кдел.=256 подключен к TMR0,
movwf OptionR ; выбор внешнего тактового сигнала с вывода
; RA4/TOCK1, приращение TMR0 по переднему
; фронту, подтягивающие резисторы порта В
; включены.
bcf Status,RP0 ; Переход в 0-й банк.

movlw 7 ; Выключение модуля


movwf CMCON ; компараторов.

movlw b'00000001' ; TMR1 включен, внутренний такт,


movwf T1CON ; Кдел. предделителя = 1.

movlw .01 ; По умолчанию - интервал


movwf Mem_1 ; времени измерения 1 сек.

call LCD_INIT ; Условный переход в процедуру "рабочей"


; инициализации.
;----> Возврат по стеку из ПП LCD_INIT.
;********************************************************************************
; Вывод тестовых надписей, свидетельствующих об успешном завершении процедуры
; "рабочей" инициализации и готовности к дальнейшей работе.
;********************************************************************************
; Вывод надписи " http://www. " в 1-ю строку.
;================================================================================
; Вывод символов в 1-ю строку.
;------------------------------
movlw .16 ; Запись числа .16 (количества выводимых в
movwf Count ; строку символов) в регистр Count.
WR_2 movf Count,W ; Копирование содержимого регистра Count в W.
sublw .16 ; .16-Count=... (результат записывается в W).
call EEPR ; Условный переход в ПП EEPR.
;----> Возврат по стеку из ПП EEPR.
bsf PortA,RS ; Установка на линии RS "1"
; (режим записи данных).
call ENTER_BF ; "Плавающая" задержка со стробом под вывод
; данных на индикацию.
;----> Возврат по стеку из ПП ENTER_BF.
decfsz Count,F ; Подготовка к выводу на индикацию
; следующего символа таблицы.
goto WR_2 ; Если результат декремента не=0, то переход
; в ПП WR_2.
; Если результат декремента =0, то программа
; исполняется далее.
;================================================================================
; Вывод надписи " ikarab.narod.ru" во 2-ю строку.
;================================================================================
; Переход в начало 2-й строки.
;------------------------------
movlw b'11000000' ; Выбор ячейки DD RAM с адресом 40h (установка
; курсора в крайнее левое знакоместо
; 2-й строки).
call ENTER_BF ; "Плавающая" задержка со стробом
; под команду 11000000.
;----> Возврат по стеку из ПП ENTER_BF.
;------------------------------
; Вывод символов во 2-ю строку.
;------------------------------
movlw .16 ; Запись числа .16 (количества выводимых в
13
movwf Count ; строку символов) в регистр Count.
WR_3 movf Count,W ; Копирование содержимого регистра Count в W.
sublw .32 ;.32-Count=... (результат записывается в W).
call EEPR ; Условный переход в ПП EEPR.
;----> Возврат по стеку из ПП EEPR.
bsf PortA,RS ; Установка на линии RS "1"
; (режим записи данных).
call ENTER_BF ; "Плавающая" задержка со стробом под вывод
; данных на индикацию.
;----> Возврат по стеку из ПП ENTER_BF.
decfsz Count,F ; Подготовка к выводу на индикацию
; следующего символа таблицы.
goto WR_3 ; Если результат декремента не=0, то переход
; в ПП WR_3.
; Если результат декремента =0, то программа
; исполняется далее.

;********************************************************************************
; ПОДПРОГРАММА ОПРОСА КЛАВИАТУРЫ.
;********************************************************************************
; Подготовка к опросу клавиатуры.
;------------------------------------------------------------
INKEY bsf Status,RP0 ; Переход в 1-й банк.
bcf OptionR,7 ; Включение, на время опроса клавиатуры,
; подтягивающих резисторов порта В.
movlw b'00001111' ; RB0...RB3 работают на вход,
movwf TrisB ; RB4...RB7 работают на выход.
bcf Status,RP0 ; Переход в 0-й банк.
clrf PortB ; Сброс всех защелок порта В.
;------------------------------------------------------------
; Опрос клавиатуры.
;------------------------------------------------------------
movf PortB,W ; Считывание числа с выводов порта B.
andlw b'00000011' ; "Нейтрализация" состояний RB7...RB2.
;------------------------------------------------------------
; Переход на заданное время измерения частоты.
;------------------------------------------------------------
MemK addwf PC,F ; Вычисляемый переход.
nop ; Установить интервал измерения 1 сек. 00
goto Sec1 ; Установить интервал измерения 1 сек. 01
goto Sec10 ; Установить интервал измерения 10 сек. 10
; Включение памяти. 4-е состояние клавиатуры. 11
;------------------------------------------------------------
; Включение памяти состояния клавиатуры после отжатия кнопок.
;------------------------------------------------------------
movf Mem_1,W ; Если обе кнопки клавиатуры отжаты, то, после
goto MemK ; окончания текущего цикла измерения,
; следующий цикл измерения будет производиться
; с таким же временем измерения частоты, как и
; в предшествующем цикле, не меняясь до
; следующего нажатия кнопок.
;--------------------------------------------------------------------------------
; Установка величины выбранного интервала времени измерения (1/10 сек.).
;--------------------------------------------------------------------------------
;-----------Для времени измерения = 10 сек.----------
Sec10 movlw .13 ; Запись константы
movwf SecH ; в регистр SecH.
movlw .181 ; Запись константы
movwf SecM ; в регистр SecM.
movlw .163 ; Запись константы
movwf SecL ; в регистр SecL.
movlw .250 ; Запись константы
movwf Kalib ; в регистр Kalib.
movlw .122 ; Запись константы
movwf Kalib_1 ; в регистр Kalib_1.
movlw .02 ; Запись константы
14
movwf Mem_1 ; в регистр Mem_1.
goto START_1 ; Переход на новый цикл измерения.

;-----------Для времени измерения = 1 сек.-----------


Sec1 movlw .2 ; -------"--------
movwf SecH ; -------"--------
movlw .69 ; -------"--------
movwf SecM ; -------"--------
movlw .220 ; -------"--------
movwf SecL ; -------"--------
movlw .251 ; -------"--------
movwf Kalib ; -------"--------
movlw .124 ; -------"--------
movwf Kalib_1 ; -------"--------
movlw .01 ; -------"--------
movwf Mem_1 ; -------"--------

;************************************************************************
; СНАЧАЛА ЗАМЕРЯЕМ ЧАСТОТУ.
;************************************************************************
; Подготовительные операции.
;-------------------------------------
START_1 clrf IntCon ; Запрет всех прерываний.
clrf TimerH ; Сброс в 0 регистра TimerH.
clrf TimerHH ; Сброс в 0 регистра TimerHH.
clrf Tmr0 ; Сброс в 0 регистра TMR0.
;-------------------------------------
; Начало счета - на метке "On"
;-------------------------------------
bsf Status,RP0 ; Переход в 1-й банк.
bsf OptionR,7 ; Выключение, на время измерений частоты и
; периода, подтягивающих резисторов порта В.
movlw b'00011000' ; Выводы RA3 и RA4/TOCKI работают на вход.
On movwf TrisA ; Копирование содержимого W в регистр TrisA,
; начало счета.
bcf Status,RP0 ; Переход в 0-й банк.
;--------------------------------------------------------------------------------
; Проверка TMR0 на переполнение №1
; (заполнение старшего байта счетчика частоты TimerH и, в случае его
; переполнения, - самого старшего байта счетчика частоты TimerHH).
;--------------------------------------------------------------------------------
CYCLE btfss IntCon,2 ; TMR0 переполнен или нет?
goto DO_NOTHING ; Нет - переход на DO_NOTHING.

incfsz TimerH,F ; Да - инкремент TimerH, и, если TimerH не


goto RESET ; переполнен, то переход на метку RESET.

incf TimerHH,F ; Если TimerH переполнен,


; то инкремент TimerHH.
RESET bcf IntCon,2 ; Сброс флага переполнения TMR0.
goto O_K ; Переход в ПП O_K.

DO_NOTHING goto $+1 ; Выравнивающая задержка


goto $+1 ; величиной 2х2=4 м.ц.
clrwdt ; Выравнивающая задержка в 1 м.ц.
; с функцией сброса WDT.
;--------------------------------------------------------------------------------
; Формирование значения интервала времени измерения "грубо".
;--------------------------------------------------------------------------------
O_K clrwdt ; Сброс WDT с функцией задержки на 1 м.ц.

decfsz SecL,F ; --------"---------


goto CYCLE ; --------"---------
; Трехбайтный вычитающий
decfsz SecM,F ; счетчик с "врезкой"

15
goto CYCLE ; в его полный цикл
; подпрограммы CYCLE.
decfsz SecH,F ; --------"---------
goto CYCLE ; --------"---------
;--------------------------------------------------------------------------------
; Формирование значения интервала времени измерения "точно".
;--------------------------------------------------------------------------------
; Шаг константы: 4 м.ц.
;-----------------------
PAUSE clrwdt ; Сброс WDT с функцией задержки на 1 м.ц.
decfsz Kalib,F ; Однобайтный вычитающий счетчик с шагом
goto PAUSE ; константы 4 м.ц.
; (за счет "врезки" команды clrwdt).
;-----------------------
; Шаг константы: 3 м.ц.
;-----------------------
PAUSE_1 decfsz Kalib_1,F ; Однобайтный вычитающий счетчик с шагом
goto PAUSE_1 ; константы 3 м.ц. (без "врезки").
;-------------------------------------
; Конец счета - на метке Off.
;-------------------------------------
clrf PortA ; Сброс в 0 содержимого регистра PortA.
bsf Status,RP0 ; Переход в 1-й банк.
movlw b'00010000' ; Запись в W константы.
Off movwf TrisA ; Копирование константы из W в регистр TrisA:
; вывод RA4 работает на вход, RA3 блокирует
; счет (конец счета).
bcf Status,RP0 ; Переход в 0-й банк.
;--------------------------------------------------------------------------------
; Проверка TMR0 на переполнение №2.
;--------------------------------------------------------------------------------
btfss IntCon,2 ; TMR0 переполнен?
goto ANALYSE ; Нет - переход в ПП ANALYSE.
incfsz TimerH,F ; Да - инкремент TimerH, и, если TimerH не
goto ANALYSE ; переполнен, то переход в ПП ANALYSE.
incf TimerHH,F ; Если TimerH переполнен, то инкремент TimerHH
;--------------------------------------------------------------------------------
; Копирование содержимого регистра TMR0 в регистр TimerM.
;--------------------------------------------------------------------------------
ANALYSE movf Tmr0,W ; Содержимое регистра TMR0
movwf TimerM ; копируется в регистр TimerM.
;--------------------------------------------------------------------------------
; Сброс в 0 содержимого регистра TimerL.
;--------------------------------------------------------------------------------
clrf TimerL ; Очистка регистра TimerL.
;--------------------------------------------------------------------------------
; Подпрограмма досчета.
;--------------------------------------------------------------------------------
COUNT_IT incf TimerL,F ; Инкремент содержимого регистра TimerL.
;--------------------------------------------------------------------------------
; Кратковременная разблокировка счетного входа TMR0.
;--------------------------------------------------------------------------------
bsf PortA,3 ; Формирование на выводе RA3 единицы.
nop ; Задержка в 1 м.ц.
bcf PortA,3 ; Формирование на выводе RA3 нуля.
nop ; Задержка в 1 м.ц.
clrwdt ; Сброс сторожевого таймера WDT.
;--------------------------------------------------------------------------------
; Сам досчет.
;--------------------------------------------------------------------------------
movf Tmr0,W ; Копирование содержимого TMR0 в W.
bcf Status,Z ; Сброс флага нулевого результата Z.
subwf TimerM,W ; Вычесть из TimerM содержимое W
; с сохранением в W.
btfsc Status,Z ; Результат операции вычитания равен
; или нет нулю?
16
goto COUNT_IT ; Да, равен --> переход в ПП COUNT_IT.
incf TimerL,F ; Нет, не равен --> инкремент содержимого
; TimerL.
comf TimerL,F ; Инвертирсия всех битов TimerL.
incf TimerL,F ; Инкремент TimerL.
incf TimerL,F ; Инкремент TimerL.

call BIN2_10 ; Условный переход в ПП двоично-десятичного


; преобразования.
;----> Возврат по стеку из ПП BIN2_10.

;================================================================================
; Процедура преобразования стандартного весового кода в код ASCII (ANSI).
;================================================================================
movlw 30h ; Запись в регистр W числа 30h.
iorwf LED0,F ; Логическое "ИЛИ" содержимого регистра W и
; регистра LED0 с сохранением результата
; в LED0.
iorwf LED1,F ; То же самое для LED1.
iorwf LED2,F ; -------"------- LED2.
iorwf LED3,F ; -------"------- LED3.
iorwf LED4,F ; -------"------- LED4.
iorwf LED5,F ; -------"------- LED5.
iorwf LED6,F ; -------"------- LED6.
iorwf LED7,F ; -------"------- LED7.
;================================================================================
; Процедура гашения незначащих нулей.
;================================================================================
movlw .7 ; Запись количества проверяемых разрядов
movwf Total ; (8-1=7) в указатель разрядов Total.
;----------------------------------------------------------
; Косвенная адресация к содержимому текущих LED... .
;----------------------------------------------------------
movlw LED7 ; Стандартная процедура косвенной адресации с
movwf FSR ; "переправкой" содержимого регистра, сначала,
; LED7, а затем и всех остальных текущих
; LED (6,5,4,3,2,1,0) в регистр W.
SNOVA_0 movlw 30h ; Запись числа 30h в регистр W.
subwf IndF,W ; Вычесть из данных регистра с адресом в FSR
; (из содержимого текущего LED) число 30h с
; сохранением результата в W.
;-------------------------------------
; 1-я проверка на нулевой результат.
;-------------------------------------
btfss Status,Z ; Проверка состояния флага нулевого результата
goto VIHOD ; Если содержимое W не=0, то выход
; из процедуры.
movlw 20h ; Если содержимое W =0, то запись символа
movwf IndF ; "пробел" в регистр с адресом в FSR
; (в текущий LED).
;-------------------------------------
; Переход на следующий, текущий LED.
;-------------------------------------
decf FSR,F ; Уменьшение адреса текущего LED на 1.
decf Total,F ; Уменьшение содержимого указателя разрядов
; на 1.
;-------------------------------------
; 2-я проверка на нулевой результат.
;-------------------------------------
btfss Status,Z ; Проверка состояния флага нулевого результата
goto SNOVA_0 ; Если содержимое Total не=0, то переход на
; повторную проверку на незначащие нули
; (goto SNOVA_0).

;************************************************************************

17
; ТЕПЕРЬ ЗАМЕРЯЕМ ПЕРИОД.
;************************************************************************
VIHOD movlw 2 ; Запись, в указатель конца замера
movwf Flag ; периода, числа перезаписей.
;--------------------------------------
; Настройка модуля CCP.
;--------------------------------------
clrf CCP1CON ; Выключение модуля CCP
; (рекомендовано разработчиками).
movlw b'00000111' ; Захват по каждому 16-му переднему
movwf CCP1CON ; фронту сигнала на выводе RB3.
;--------------------------------------
; Сброс флага прерываний от модуля CCP.
;--------------------------------------
bcf PIR1,CCP1IF ; Сброс флага CCP1IF.
;--------------------------------------------------------------------------------
; Разрешение прерываний от модуля CCP, подключение предделителя
; к WDT и разблокировка счетного входа модуля CCP.
;--------------------------------------------------------------------------------
clrwdt ; Сброс WDT.
clrf Tmr0 ; Сброс в ноль Tmr0 и предделителя.

clrf Bit ; Подготовка к работе бита №0 регистра Bit.

bcf Status,Z ; Подготовка флага Z к работе.

movlw b'11000000' ; Глобальное разрешение прерываний и


movwf IntCon ; разрешение прерываний от периферийных
; модулей.
bsf Status,RP0 ; Переход в 1-й банк.
bsf OptionR,3 ; Подключение предделителя к WDT (К=128).
bsf TrisA,3 ; Разблокировка счетного входа модуля CCP
; (начало счета).
movlw b'00000100' ; Разрешение прерываний
movwf PIE1 ; от модуля CCP.
bcf Status,RP0 ; Переход в 0-й банк.
;--------------------------------------------------------------------------------
; "Плавающая" задержка с выходом из нее через 2 прерывания от модуля CCP.
;--------------------------------------------------------------------------------
btfss Status,Z ; Если флаг Z опущен, то ждем его поднятия.
goto $-1 ; Если флаг Z поднялся, то программа
; исполняется далее.
;--------------------------------------------------------------------------------
; Запрет прерываний, подключение предделителя к Tmr0 и блокировка
; счетного входа модуля CCP.
;--------------------------------------------------------------------------------
clrf IntCon ; Запрет прерываний.
clrwdt ; Сброс WDT.
bsf Status,RP0 ; Переход в 1-й банк.
bcf TrisA,3 ; Блокировка счетного входа модуля CCP
; (конец счета).
bcf OptionR,3 ; Подключение предделителя к Tmr0 (К=256).
bcf Status,RP0 ; Переход в 0-й банк.
;--------------------------------------------------------------------
; ИТОГ: в Temp2_H/Temp2_L "лежит" результат замера, умноженный на 16.
;================================================================================
; Обработка результатов подсчета.
;================================================================================
; Деление содержимого Temp_H/Temp_L на 16.
;--------------------------------------------------------------------------------
bcf Status,C ; Сброс бита С.
rrf Temp_H,F ; Сдвиг вправо содержимого регистра Temp_H.
rrf Temp_L,F ; Сдвиг вправо содержимого регистра Temp_L.

bcf Status,C ;

18
rrf Temp_H,F ; Аналогично.
rrf Temp_L,F ;

bcf Status,C ;
rrf Temp_H,F ; Аналогично.
rrf Temp_L,F ;

bcf Status,C ;
rrf Temp_H,F ; Аналогично.
rrf Temp_L,F ;
;--------------------------------------------------------------------------------
; Блокировка ложных поднятий флага TMR1IF, если
; результат замера = 4095 (00001111 11111111).
; Примечание: 4096 = 00010000 00000000).
;--------------------------------------------------------------------------------
movf Temp_H,W ;
sublw .15 ; .15 - Temp_H = ...
btfss Status,Z ; Результат равен или не равен нулю ?
goto $+5 ; Если =0, то обход команд анализа
; содержимого Temp_L.
movf Temp_L,W ; Если не=0, то
sublw .255 ; .255 - Temp_H = ...
btfsc Status,Z ; Результат равен или не равен нулю ?
bcf Bit,0 ; Если =0, то бит указателя переполнения TMR1
; (Bit,0) сбрасывается в 0 ("убийство" ложного
; переполнения).
; Если не=0, то этого "убийства" не происходит
;--------------------------------------------------------------------------------
; Анализ указателя переполнения TMR1 (бита №0 регистра Bit).
; Если Bit,0 = 0, то переполнения TMR1 не было.
; Если Bit,0 = 1, то переполнение TMR1 было.
;--------------------------------------------------------------------------------
btfsc Bit,0 ; Если переполнение TMR1 было, то результат
bsf Temp_H,4 ; подсчета увеличивается на 4096.
; Если переполнения TMR1 не было,
; то этого не происходит.
;------------------------------------------------------
; ИТОГ: в Temp_H/Temp_L - истинный результат замера.
;********************************************************************************
; Процедура двоично-десятичного преобразования для замера периода.
;********************************************************************************
bcf Status,C ; Сброс флага С.
movlw .24 ; "Закладка" в регистр Count1 числа
movwf Count1 ; проходов преобразования.
clrf LED00 ; Сброс регистра LED00.
clrf LED11 ; ------"------ LED11.
clrf LED22 ; ------"------ LED22.
clrf Temp_0 ; Сброс "обманного" регистра.
;--------------------------------------------------------------------------------
; Сдвиг влево через бит С регистра Status.
;--------------------------------------------------------------------------------
LOOP_16_1 rlf Temp_L,F ; Побитная
rlf Temp_H,F ; "переправка"
rlf Temp_0,F ; содержимого
rlf LED00,F ; Temp_0/Temp_H/Temp_L
rlf LED11,F ; в
rlf LED22,F ; LED22/LED11/LED00.
;-----------------------------------
decfsz Count1,F ; Анализ числа проходов
goto adjDEC_1 ; преобразования.
;--------------------------------------------------------------------------------
; Порязрядное распределение полубайтов регистров LED00...22
; по младшим полубайтам регистров LED00...55.
;--------------------------------------------------------------------------------
swapf LED22,W ; Запись старшего полубайта LED22
andlw 0Fh ; в младший полубайт LED55.
19
movwf LED55 ; -------------------------------

movfw LED22 ; Запись младшего полубайта LED22


andlw 0Fh ; в младший полубайт LED44.
movwf LED44 ; -------------------------------

swapf LED11,W ; Запись старшего полубайта LED11


andlw 0Fh ; в младший полубайт LED33.
movwf LED33 ; -------------------------------

movfw LED11 ; Запись младшего полубайта LED11


andlw 0Fh ; в младший полубайт LED22.
movwf LED22 ; -------------------------------

swapf LED00,W ; Запись старшего полубайта LED00


andlw 0Fh ; в младший полубайт LED11.
movwf LED11 ; -------------------------------

movfw LED00 ; Запись младшего полубайта LED00


andlw 0Fh ; в младший полубайт LED00.
movwf LED00 ; -------------------------------
;--------------------------------------------------------------------------------
; ИТОГ: десятичные числа лежат в младших полубайтах
; LED55/LED44/LED33/LED22/LED11/LED00.
;--------------------------------------------------------------------------------
goto ASC ; Конец 2/10 преобразования.
; Переход в ПП преобразования кода.
;--------------------------------------------------------------------------------
; Подготовка к косвенной адресации (происходит в ПП adjBCD).
; После возврата по стеку, переход к обработке следующего LEDа.
;--------------------------------------------------------------------------------
adjDEC_1 movlw LED00 ; Запись в регистр FSR адреса регистра LED00
movwf FSR ; с дальнейшим переходом в ПП adjBCD.
call adjBCD ;
;---> Возврат по стеку из ПП adjBCD.
movlw LED11 ; То же самое для регистра LED11.
movwf FSR ;
call adjBCD ;
;---> Возврат по стеку из ПП adjBCD.
movlw LED22 ; То же самое для регистра LED22.
movwf FSR ;
call adjBCD ;
;---> Возврат по стеку из ПП adjBCD.
goto LOOP_16_1 ; Переход на следующее кольцо
; числовых преобразований.
;================================================================================
; Процедура преобразования стандартного весового кода в код ASCII (ANSI).
;================================================================================
ASC movlw 30h ; Запись в регистр W числа 30h.
iorwf LED00,F ; Логическое "ИЛИ" содержимого регистра W и
; регистра LED00 с сохранением результата
; в LED00.
iorwf LED11,F ; То же самое для LED11.
iorwf LED22,F ; -------"------- LED22.
iorwf LED33,F ; -------"------- LED33.
iorwf LED44,F ; -------"------- LED44.
iorwf LED55,F ; -------"------- LED55.
;================================================================================
; Процедура гашения незначащих нулей.
;================================================================================
movlw .5 ; Запись количества проверяемых разрядов
movwf Total ; (6-1=5) в указатель разрядов Total.
;----------------------------------------------------------
; Косвенная адресация к содержимому текущих LED... .
;----------------------------------------------------------
movlw LED55 ; Стандартная процедура косвенной адресации с
20
movwf FSR ; "переправкой" содержимого регистра, сначала,
; LED55, а затем и всех остальных текущих
; LED (44,33,22,11,00) в регистр W.
SNOVA_00 movlw 30h ; Запись числа 30h в регистр W.
subwf IndF,W ; Вычесть из данных регистра с адресом в FSR
; (из содержимого текущего LED) число 30h
; с сохранением результата в W.
;-------------------------------------
; 1-я проверка на нулевой результат.
;-------------------------------------
btfss Status,Z ; Проверка состояния флага нулевого результата
goto VIHOD_1 ; Если содержимое W не=0, то выход из
; процедуры (goto VIHOD_1).
movlw 20h ; Если содержимое W =0, то запись символа
movwf IndF ; "пробел" в регистр с адресом в FSR
; (в текущий LED).
;-------------------------------------
; Переход на следующий, текущий LED.
;-------------------------------------
decf FSR,F ; Уменьшение адреса текущего LED на 1.
decf Total,F ; Уменьшение содержимого указателя разрядов
; на 1.
;-------------------------------------
; 2-я проверка на нулевой результат.
;-------------------------------------
btfss Status,Z ; Проверка состояния флага нулевого результата
goto SNOVA_00 ; Если содержимое Total не=0, то переход на
; повторную проверку на незначащие нули
; (goto SNOVA_00).
;----------------------------------------------------------------------
; Теперь можно вывести результат замера частоты и периода на индикацию.
;----------------------------------------------------------------------
VIHOD_1 goto DISPLAY ; Переход в ПП вывода на индикацию.

;********************************************************************************
; Процедура двоично-десятичного преобразования для замера частоты.
;********************************************************************************
BIN2_10 bcf Status,C ; Сброс флага C.
movlw .32 ; "Закладка" в регистр Count1 числа
movwf Count1 ; проходов преобразования.
clrf LED0 ; Сброс регистра LED0.
clrf LED1 ; ------"------ LED1.
clrf LED2 ; ------"------ LED2.
clrf LED3 ; ------"------ LED3.
;--------------------------------------------------------------------------------
; Сдвиг влево через бит С регистра Status.
;--------------------------------------------------------------------------------
LOOP_16 rlf TimerL,F ;
rlf TimerM,F ; Побитная
rlf TimerH,F ; "переправка"
rlf TimerHH,F ; содержимого
rlf LED0,F ; TimerHH/TimerH/TimerM/TimerL
rlf LED1,F ; в
rlf LED2,F ; LED3/LED2/LED1/LED0.
rlf LED3,F ;
;-----------------------------------
decfsz Count1,F ; Анализ числа проходов
goto adjDEC ; преобразования.
;--------------------------------------------------------------------------------
; Порязрядное распределение полубайтов регистров LED0...3
; по младшим полубайтам регистров LED0...7.
;--------------------------------------------------------------------------------
swapf LED3,W ; Запись старшего полубайта LED3
andlw 0Fh ; в младший полубайт LED7
movwf LED7 ; -------------------------------

21
movfw LED3 ; Запись младшего полубайта LED3
andlw 0Fh ; в младший полубайт LED6
movwf LED6 ; -------------------------------

swapf LED2,W ; Запись старшего полубайта LED2


andlw 0Fh ; в младший полубайт LED5
movwf LED5 ; -------------------------------

movfw LED2 ; Запись младшего полубайта LED2


andlw 0Fh ; в младший полубайт LED4
movwf LED4 ; -------------------------------

swapf LED1,W ; Запись старшего полубайта LED1


andlw 0Fh ; в младший полубайт LED3
movwf LED3 ; -------------------------------

movfw LED1 ; Запись младшего полубайта LED1


andlw 0Fh ; в младший полубайт LED2
movwf LED2 ; -------------------------------

swapf LED0,W ; Запись старшего полубайта LED0


andlw 0Fh ; в младший полубайт LED1
movwf LED1 ; -------------------------------

movfw LED0 ; Запись младшего полубайта LED0


andlw 0Fh ; в младший полубайт LED0
movwf LED0 ; -------------------------------
;--------------------------------------------------------------------------------
; ИТОГ: десятичные числа лежат в младших полубайтах
; LED7/LED6/LED5/LED4/LED3/LED2/LED1/LED0.
;--------------------------------------------------------------------------------
return ; Конец 2/10 преобразования. Возврат по стеку.
;--------------------------------------------------------------------------------
; Подготовка к косвенной адресации (происходит в ПП adjBCD).
; После возврата по стеку, переход к обработке следующего LEDа.
;--------------------------------------------------------------------------------
adjDEC movlw LED0 ; Запись в регистр FSR адреса регистра LED0
movwf FSR ; с дальнейшим переходом в ПП adjBCD.
call adjBCD ;
;---> Возврат по стеку из ПП adjBCD.
movlw LED1 ; То же самое для регистра LED1.
movwf FSR ;
call adjBCD ;
;---> Возврат по стеку из ПП adjBCD.
movlw LED2 ; То же самое для регистра LED2.
movwf FSR ;
call adjBCD ;
;---> Возврат по стеку из ПП adjBCD.
movlw LED3 ; То же самое для регистра LED3.
movwf FSR ;
call adjBCD ;
;---> Возврат по стеку из ПП adjBCD.
goto LOOP_16 ; Переход на следующее кольцо
; числовых преобразований.
;--------------------------------------------------------------------------------
; Основные операции преобразования двоичных чисел в двоично-десятичные:
; операции сложения LED с константами 03h и 30h, с условиями
; по 3-му и 7-му битам.
;--------------------------------------------------------------------------------
adjBCD clrwdt ; Сброс сторожевого таймера WDT.
movlw 3 ; Суммирование содержимого текущего LED с
addwf 0,W ; числом 03h, с записью результата операции,
movwf Mem ; через регистр W, в регистр Mem.

btfsc Mem,3 ; Анализ состояния бита №3 регистра Mem.


movwf 0 ; Если бит №3 = 1, то содержимое регистра Mem
22
; копируется в текущий LED.

movlw 30 ; Если бит №3 = 0, то содержимое текущего LED


addwf 0,W ; складывается с константой 30h, с записью
movwf Mem ; результата операции, через регистр W,
; в регистр Mem.
btfsc Mem,7 ; Анализ состояние бита №7 регистра Mem.
movwf 0 ; Если бит №7 = 1, то содержимое регистра Mem
; копируется в текущий LED.
retlw 0 ; Если бит №7 = 0, то регистр W очищается и
; происходит возврат по стеку в ПП adjDEC
; или adjDEC_1.
;********************************************************************************
end ; Конец программы.

Темно – синим цветом выделено то, что непосредственно относится к процедуре


измерения периода, вплоть до окончания формирования истинного результата
измерения периода, в части касающейся неизмененного ("передрано" из "матери").
Темно – красным цветом выделены изменения (дополнения), внесенные в процедуру
измерения периода.
Синим цветом выделено то, что находится в "промежутке" между окончанием
формирования истинного результата измерения периода и окончанием его вывода на
индикацию.
Подпрограмма adjBCD используется как при 2/10-м преобразовании результата замера
частоты, так и при 2/10-м преобразовании результата замера периода.

Работа программы.
Так как работа "матери" (Hz_Mks_1.asm) "расписана" в предыдущем подразделе, то я
акцентирую Ваше внимание только на "нововведениях".
В начале процедуры измерения периода, бит указателя переполнения TMR1 (бит №0
регистра Bit) подготавливается к работе (сбрасывается в 0).
Я это сделал так: clrf Bit, но можно и bcf Bit,0.
В первом случае, воздействие на флаг Z есть (от поднимается), и команду clrf Bit
нужно разместить выше команды bcf Status,Z.
Во втором случае, воздействия на флаг Z нет, и команду bcf Bit,0 можно (но не
обязательно) поместить ниже команды bcf Status,Z.
Далее работа происходит по алгоритму "материнской" программы, вплоть до входа в
1-й сценарий ПП прерывания.
В его начале, производится предустановка TMR1H/TMR1L, заключающаяся в
"обнулении" регистра TMR1H и записи корректирующей константы в регистр TMR1L
(младший разряд).
В ходе подбора числового значения этой константы (с проверкой "реакции железа"),
выяснилось следующее (забегаю вперед, но это оправдано).
Блокирую точками с запятыми все команды процедуры блокировки ложных поднятий
флага TMR1IF (создаю наихудшую ситуацию).
Постепенно увеличивая значение корректирующей константы, добиваюсь "убийства"
ложных переполнений на "ближних подступах" к результату замера 4095 мкс.
(максимально сужаю "сектор бяки").
Конечный результат такого подбора должен быть следующий: все ОК, за исключением
замера периода длительностью 4095 мкс.
В этом случае, показания будут хаотически "скакать" (4095 или 4095 + 4096 = 8191).
Теперь разблокирую все команды процедуры блокировки ложных поднятий флага
TMR1IF (блокирую последнюю "бяку").
После этого получается "полнейшая красота и ляпота", и в душе "звучит райская
музыка".
Это счастье (кавычки ставить рука не поднимается) достигается при значении
корректирующей константы = .9.
"С какого потолка упало" число .9?
Начинаю расследование ("следствие ведут колобки").
"Виртуальная" команда call ("уход" в прерывание) исполняется за 2 м.ц.

23
Команды decfsz Flag,F и goto WRITE_1 исполняются за 3 м.ц. (в сумме).
Команды movlw .9, movwf TMR1L и clrf TMR1H исполняются за 3 м.ц. (в сумме).
Команды bcf Status,Z, bcf PIR1,TMR1IF, bcf PIR1,CCP1IF и retfie исполняются за 5 м.ц.
(в сумме).
Получается 13 м.ц.
Это означает то, что, с момента возникновения 1-го события захвата и до окончания
возврата рабочей точки программы в "плавающую" задержку, отрабатывается 13 м.ц.
В идеале, на момент окончания возврата рабочей точки программы в "плавающую"
задержку, в TMR1H/TMR1L должно "лежать" число .13.
Если предположить, что, на время исполнения команд movwf TMR1L и clrf TMR1H (на
время записи), счет TMR1 останавливается (а так оно и есть), то "вырисовывается"
следующее: предустановка .9, плюс, bcf Status,Z, bcf PIR1,TMR1IF, bcf PIR1,CCP1IF и
retfie - 5 м.ц. (в сумме).
Получается то, что, на момент окончания возврата рабочей точки программы в
"плавающую" задержку, в TMR1H/TMR1L будет "лежать" не число .13, а число .14, что
свидетельствует о нарушении "привязки" момента окончания счета по кольцу к моменту
фактического переполнения TMR1.
Но этого нарушения нет (против факта "не попрешь")!
Значит, на момент окончания возврата рабочей точки программы в "плавающую"
задержку, в TMR1H/TMR1L, по факту, "лежит" не число .14, а число .13.
Объяснение этому "феномену" следующее: мало того, что, при записи и в TMR1L, и в
TMR1H, TMR1 не считает (в данном случае, это команды movwf TMR1L и clrf TMR1H),
он не считает также и в следующем (после машинного цикла записи) машинном цикле.
Получается то, что, во время исполнения машинного цикла команды bcf Status,Z (в
данном случае, это и есть "следующий машинный цикл"), TMR1 не считает.
И именно по этой причине, в данном случае, предустанавливается константа .9, а не .
8, что есть учет машинного цикла, в котором TMR1 не считает и который "на помойку
никак не выбросишь".
Таким образом, разработчики просто исключили возможность инкремента содержимого
TMR1H/TMR1L во время исполнения процедуры записи, с учетом того, что запись и в
TMR1H, и в TMR1L может быть произведена не за один, а за 2 м.ц.

Честь им за это и хвала (без иронии). Молодцы ребятишки.


Вот Вам и ответ на вопрос: "С какого потолка упала цифра .9"?
А заодно можно сделать и "попутный" вывод: порядок записи констант в TMR1H/TMR1L
может быть любым (в том смысле, что это никоим образом не "породит бяку",
связанную с инкрементом содержимого TMR1H/TMR1L во время исполнения процедуры
записи. Это технически исключено).
Сказанное выше, нужно воспринимать как учебно - тренировочное описание процесса
"проникновения мысли через ноги в голову" (точь в точь как через регистр W), в
контексте высказывания "не доходит через голову, дойдет через ноги".
Это очень жизненное высказывание, так как "планета людей не есть планета ангелов"
и поэтому частенько приходится убеждаться в правоте и "аксиомности" этого
высказывания.
Лучше уж пусть так доходит, чем не доходит вообще (к тому же, победителей не
судят).
"Ковыляю" дальше.
После того, как в 1-м прерывании произошла предустановка, устанавливается признак
продолжения замера периода (Z=0), флаги TMR1IF и CCP1IF опускаются и рабочая
точка программы возвращается в "плавающую" задержку.
Примечание: в 1-м прерывании, флаг TMR1IF нужно обязательно сбросить, так как, на
момент возникновения события 1-го захвата, вероятность его поднятия высока.
К тому же, опущенный, на момент выхода из 1-го прерывания, флаг TMR1IF есть
необходимое условие осуществления точного замера периода (в границах рабочего
диапазона).
В "плавающей" задержке ожидается 2-е событие захвата.
После его возникновения, исполняется 2-й сценарий ПП прерывания.

24
Теперь можно скопировать результат измерения из CCPR1H/CCPR1L в Temp_H/Temp_L
(напоминаю, что счет производился от нуля) и опросить состояние флага переполнения
TMR1 (переполнение TMR1 было или не было?).
Тактически более правильным будет, в начале 2-го сценария ПП прерывания,
произвести опрос состояния флага переполнения TMR1, а только после этого
производить указанное выше копирование (что и сделано).
Почему?
Потому, что нужно как можно более минимизировать интервал времени между
моментом возникновением события 2-го захвата и моментом опроса состояния флага
переполнения TMR1.
Второй раз, почему?
Потому, что в этом интервале времени может произойти ложное переполнение TMR1.
Когда оно происходит?
Опять же, при замере "легендарного" ("пограничного") периода величиной 4095 мкс.
Вот Вам и еще один источник неприятностей.
Вопрос: "Как их убить"?
Ответ: а ложные переполнения уже "убиты", так как имеет место быть процедура
блокировки ложных поднятий флага TMR1IF, если результат замера = 4095 (о ней
говорилось ранее).
Таким образом, введение в текст программы процедуры блокировки ложных поднятий
флага TMR1IF, если результат замера = 4095, позволяет решить двуединую задачу:
- блокировка ложных поднятий флага переполнения TMR1 при отработке 1-го
прерывания,
- блокировка ложных поднятий флага переполнения TMR1 при отработке 2-го
прерывания.
Вопрос: "Что это есть такое"?
Ответ: это, с учетом всего комплекса проделанной работы, есть оптимальный способ
построения программы в части касающейся работы в прерываниях.
Как Вам такой "оптовый" способ "убийства бяк", и вообще, способ реализации подсчета
величины интервала времени между 2-мя событиями захватов?
Лично мне, очень нравится, и прежде всего с эстетической точки зрения.
"Шагаю" дальше.
Состояние флага TMR1IF "засечено" и "заложено на хранение" в бит №0 регистра Bit.
"Перегрузка" результата текущего замера осуществлена.
"Дела сделаны".
Далее - стандартный выход из прерывания с поднятым флагом Z (признак выхода из
"плавающей" задержки).
Текущий замер периода закончен.
Теперь нужно обработать результат этого замера.
В данном случае, вычитание по кольцу производить не нужно, так как счет происходит
от нуля.
Далее - деление на 16 (см. предыдущий подраздел).
Далее происходит "убийство" ложных срабатываний при замере периода длительностью
4095 мкс.
Оно происходит по принципу последовательного анализа байтов результата подсчета.
Сначала анализируется содержимое регистра старшего разряда на предмет его
равенства (или нет) числу .15, а затем анализируется содержимое регистра младшего
разряда на предмет его равенства (или нет) числу .255 (в совокупности, это число
00001111 11111111, то есть, .4095).
В случае последовательного совпадения этих чисел, нулевой бит пегистра Bit
сбрасывается в 0, что есть либо подтверждение нуля (если ложного переполнения
нет), либо "убийство" ложного переполнения.
Короче, через это "сито", ложное переполнение однозначно не "проскочит" (ему будет
"Гитлер капут").
Теперь можно внести "поддиапазонную поправку" в результат измерения.
Делается это очень просто.
Организуется анализ состояния бита №0 регистра Bit.
Если переполнения не было (Bit,0=0), то с результатом текущего замера периода
ничего не происходит (не меняется), и он "транзитом проскакивает" эту проверку.
25
А вот если переполнение было (Bit,0=1), то результат замера увеличивается на число
4096 (00010000 00000000).
Для того чтобы это произошло, нужно всего-навсего записать 1 в бит №4 регистра
старшего разряда результата замера (bsf Temp_H,4).
Итог: истинный результат текущего замера периода сформирован, и далее
отрабатывается группа стандартных процедур.
В результате - вывод результата текущего замера периода на индикацию в ЖКИ
модуль.

Анализ возможностей дальнейшего расширения диапазона измерения периода.

В простейшем случае, нужно просто перейти на более "мелкий" Кдел. предделителя


модуля CCP.
Соответственно, при Кдел. предделителя модуля CCP = 4, нужно использовать не
процедуру деления на 16, а процедуру деления на 4.
При Кдел. предделителя модуля CCP = 1, процедура деления вообще не нужна.
Эту "комбинацию из двух пальцев соорудить" достаточно просто, но есть одно "но": в
"автомате это дело не провернешь" (а именно это и интересно).
Сие означает то, что без внешнего переключения поддиапазонов измерения периода
(кнопки, тумблера, рубильники и т.п.) не обойтись, плюс, еще и думать нужно, какой из
поддиапазонов измерения включить.
Типичный пользователь этого не любит.
Типичный пользователь любит не напрягаться.
Значит напрячься должен конструктор.
Почему эти неудобства имеют место быть?
Потому, что, по причинам указанным выше, нельзя посчитать количество переполнений
TMR1 от 2 и более (вернее можно, но будет числовой "бардак"), и это "ставит крест"
на возможности автоматического переключения поддиапазонов измерения периода (от
3-х поддиапазонов и более), но с одной оговоркой - в случае, когда имеет место быть
измерительный прибор в виде "чистого" измерителя периода.
В случае же, когда производится комплексный замер и частоты, и периода (это то, о
чем, с "дальним прицелом", шла речь), автоматическое переключение поддиапазонов
измерения периода организовать можно.
Почему?
Потому, что, в качестве критерия автоматического выбора того или иного из
поддиапазонов измерения периода, можно использовать результаты замера частоты.
Причем, можно обеспечить отсутствие сбоев в показаниях измерителя периода (на
"пограничных участках" между поддиапазонами) даже с учетом естественной разницы
(обусловлена погрешностями измерений) между результатом текущего замера частоты и
значением частоты, вычисленным по результату текущего замера периода.
У меня на этот счет имеются кое-какие практические соображения.
Бог даст - реализую ("взбрыкнул"? Пора и о чувстве меры вспомнить), но только
после "свершения уже запланированных подвигов" (план есть основа упорядоченности,
которая, в нашем деле, исключительно важна).

Программа Hz_Mks_2.asm является рабочей (проверена в "железе").


Она работает в диапазоне длительностей периодов от 1 до 8191 мкс., что
соответствует диапазону частот от 1 Мгц. до 122 Гц.
Шаг замера периода - 1 мкс.
Основные технические характеристики частотомера не указываю, так как они давно
известны.
Пользуйтесь на здоровье (расширяйте, углубляйте, стимулируйте детородные функции,
генерируйте идеи и т.д.).
Для того и "ваял".
А я "пошел дальше".

26
"Практикум по конструированию устройств на PIC контроллерах"      http://ikarab.narod.ru       E-mail: karabea@lipetsk.ru

27