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

2-2/2.

Детализация принципа отработки полного цикла программы, с "упором" на


обеспечение отсутствия "мертвых зон". Создание "проматеринской" программы
(PIC16F873A, MT-16S2D-2YLG, 4-разрядный интерфейс) и соответствующей
"железяки".

Ранее, о принципе отработки полного цикла программы (о "глобальном" замысле) было


сказано в достаточно общем виде.
Детализирую:

Напоминание: после программного задания режима работы модуля TMR2 и


программного включения этого модуля, его работа происходит только в
"аппаратном секторе".
То есть, если после этого, модуль TMR2 программно не выключать и не
перестраивать, то дальнейшее исполнение программы не влияет на его
работу.
Он просто "тупо" будет "отмерять" заранее заданные, калиброванные
интервалы времени, что очень даже и распрекрасно (под это и задуман).
Соответственно, периодически будет подниматься флаг TMR2IF (уход в
прерывание по переполнению TMR2, если оно разрешено), при условии
программной ("принудительной") организации дальнейшего сброса (после
поднятия) этого флага.

На рис. 1, изображена условная модель полного цикла программы, поясняющая


принцип его отработки.
Естественно, что в реальной программе, это "кольцо" будет состоять из гораздо
бОльшего количества "сегментов".
Необходимое условие реализации оного - разрешение, в ПП START, прерываний по
переполнению TMR2 и дальнейшее их "незапрещение".
За счет организации периодических уходов в прерывания по переполнению TMR2 и
разрешения прерываний (в ПП START), без дальнейшего их запрета, полные циклы
"основного тела" программы как бы "рвутся на куски" строго фиксированной

1
"протяженности" (во времени), каждый из которых равен периоду ухода в прерывание
(пока, задано 250 мкс.).
Каждый из периодов ухода в прерывание состоит из двух частей, выделенных
красным и желтым цветом.

Красным цветом выделены интервалы времени отработки ПП прерывания.


Желтым цветом выделены интервалы времени отработки тех или иных
частей "основного тела" программы.

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


а конец - на момент ухода в следующее прерывание.
В соответствии с этой "концепцией", в интервале времени отработки всех полных
циклов программы, уходы в прерывания будут происходить со строго фиксированной
периодичностью, период которой можно задать в ПП START.
Проще говоря, максимальное время срабатывания защиты будет определяться
периодом ухода в прерывание, а "мертвые зоны" ("зоны" запрета прерываний) будут
отсутствовать.
И это есть "мощнейший зер гут", благотворно влияющий на качество (инерционность)
защиты.
Но о законе сохранения энергии (о "расплате") забывать не нужно.
"Расплатой" является затягивание отработки полного цикла "основного тела" программы,
по сравнению со случаем отсутствия прерываний (см. "куски", выделенные желтым
цветом).
При таком "раскладе", в случае применения динамической индикации, будет иметь
место быть "бяка", связанная с усилением эффекта мерцаний.
Чем уже будет желтый сектор, и особенно на "фоне" большого соотношения времени
отработки красного сектора к времени отработки желтого сектора, тем сильнее будут
ощущаться мерцания.
Вплоть до "полнейшего безобразия".
Выход - запрет прерываний на время вывода данных на индикацию.
Но это "порождает мертвые зоны", в которых защита не работает, что совсем не есть
хорошо. И даже отвратительно/омерзительно.
Получается тупик.
И каким бы не был компромисс, получается что-то типа "голову вынул, хвост увяз" (и
наоборот).
А ведь главный интерес и заключается в том, что

наряду с максимально возможным сужением протяженности (во времени)


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

Причем, красный сектор особо-то сильно и не "обкарнаешь", а вот желтый,


теоретически, можно "обкарнать" вплоть до единиц машинных циклов.
Короче, с динамической индикацией "каши не сваришь" (в смысле обеспечения
действительно качественных показателей системы защиты).
Как не крути-верти, а получится конфуз, и не более того.
Поэтому, как это не печально признавать, но в данном случае, по большому счету,
все то, что связано с динамической индикацией, есть халтура.
Это к вопросу о выборе "глобальной" стратегии программы.
Ошибки в выборе оной, "стоят" ох как дорого. Выгоднее изначально подсуетиться.
Для того чтобы избавиться от мерцаний, индикатор должен иметь оперативную память
показаний.
Вот Вам и обоснование необходимости применения ЖК-модуля.
Это конечно дороже (по деньгам), но зато гораздо качественнее и перспектив "два
мешка" (вполне можно оправдать материальные затраты. И даже "переоправдать").
Теперь о протяженности желтого сектора.
Допустим, что протяженность красного сектора является фиксированной или
изменяющеся не значительно.

2
В этом случае, протяженность желтого сектора можно регулировать путем изменения
значения периода ухода в прерывание.
Практический интерес представляет максимально компромиссное его сужение.
Это уменьшает инерционность защиты, но замедляет скорость вывода на индикацию
результатов измерения.
По поводу последнего, "слезы лить" совсем не нужно.
Достаточно вспомнить о значительной инерционности жидких кристаллов (можно
ориентироваться на 150 000 мкс., и это минимум).
При "обычных" обстоятельствах (без прерываний), если время исполнения полного
цикла программы не велико, то для того чтобы обеспечить приемлемое качество
картинки, приходится применять вполне "солидную", фиксированную задержку.
Так вот, сужение протяженности желтых секторов равносильно созданию такой
фиксированной задержки.
И чем более они будут сужены, тем величина этой "виртуальной" задержки будет
бОльшей.
А так как речь идет об обеспечении задержки весьма внушительной величины, то и
протяженность желтых секторов можно сузить до "мизера".
Получается, что речь вовсе не идет о "несчастье".
В случае применения ЖК-модуля, никакого "несчастья" и в помине нет, а наоборот,
есть "счастье" от осознания того, что "вред можно превратить в пользу".
Вот такие "чудеса бывают на белом свете" (можно языком поцокать и глаза закатить).
Итак, "глобальный" замысел и направление работы по обеспечению минимальной
инерционности отклика защиты на "бяку", прояснились (надеюсь на это).
Теперь можно озаботиться программной реализацией этого замысла.
Нет проблем.
Самое главное в этом деле (а также и в других делах) - знать "откуда ноги растут", а
остальное - дело техники.
После "вышележащей артподготовки", получилась программа BP_1.asm (прилагается).
Принципиальная схема под нее - см. предыдущий подраздел.

;********************************************************************************
; BP_1.asm МОДУЛЬ ДЛЯ БЛОКОВ ПИТАНИЯ
; ("промать")
; ЧЕРНОВИК №1.
;********************************************************************************
; "Практикум по конструированию устройств на PIC контроллерах"
; (http://ikarab.narod.ru)
; Корабельников Евгений Александрович karabea@Lipetsk.ru
;********************************************************************************
; Задействуются модули АЦП и TMR2.
; Диапазон квантования напряжения от GND до +Uпит (5,12 в.).
; 2 активных канала АЦП (измерение U и I), правое выравнивание.
; Периодический уход в прерывания по переполнению TMR2 (через каждые 250 мкс.).
; Использована ПП 2/10 преобразования Сергея Рослика (переделана с 2х4 на 2х3).
;--------------------------------------------------------------------------------
; Функции выводов порта А:
; RA0 - активный входной канал АЦП (AN0),
; RA1 - активный входной канал АЦП (AN1),
; остальные выводы порта А не задействованы.
; Функции выводов порта В:
; RB4...RB7 - линии данных (работа по 4-разрядному интерфейсу),
; остальные выводы порта B не задействованы.
; Функции выводов порта С:
; RC5 - линия RS,
; RC6 - линия R/W,
; RC7 - линия E,
; остальные выводы порта C не задействованы.
; Кварц 4000 Кгц (1 м.ц.= 1 мкс.).
; М/контроллер PIC16F873A.
; ЖК-модуль MT-16S2D-2YLG (7-я версия). Работа в 0-й странице знакогенератора.
;--------------------------------------------------------------------------------
; Объем программы: 375 слов в памяти программ.
3
;================================================================================
LIST p=16F873A ; Задание типа микроконтроллера.
__CONFIG 3F71H ; Включено: XT-генератор, PWRT, сброс BOR,
; Выключено: защита, WDT, LVP, DEBUG.
;================================================================================
; Регистры специального назначения.
;================================================================================
IndF equ 00h ; Доступ к памяти через FSR.
PC equ 02h ; Счетчик команд.
Status equ 03h ; Регистр статуса.
FSR equ 04h ; Регистр косвенной адресации.
PortB equ 06h ; Регистр защелок порта B.
PortC equ 07h ; ----------"---------- C.
TrisB equ 06h ; Регистр выбора направлений работы
; выводов порта В (банк 1).
TrisC equ 07h ; ----------"---------- C (банк 1).
IntCon equ 0Bh ; Регистр прерываний.
PIR1 equ 0Ch ; Регистр флагов прерываний
; от периферийных модулей.
PIE1 equ 0Ch ; Регистр периферийных прерываний - банк 1.
T2CON equ 12h ; Регистр управления модулем TMR2.
PR2 equ 12h ; Регистр PR2 модуля TMR2 - банк 1.
TMR2 equ 11h ; Регистр модуля TMR2.
AdresH equ 1Eh ; Регистр старшего байта результата АЦП.
AdresL equ 1Eh ; Регистр младшего байта результата АЦП
; (банк 1).
Adcon0 equ 1Fh ; Регистр настройки модуля АЦП.
Adcon1 equ 1Fh ; Регистр настройки модуля АЦП (банк 1).
;================================================================================
; Регистры общего назачения.
;================================================================================
; Регистры, используемые в ПП 2/10 преобразования.
;--------------------------------------------------------------------------------
; НАПРЯЖЕНИЯ:
LED0 equ 30h ; Регистры, используемые при 2/10
LED1 equ 31h ; преобразовании двоичного результата
LED2 equ 32h ; измерения напряжения.
;-----------------------------------
; СИЛЫ ТОКА:
LED3 equ 33h ; Регистры, используемые при 2/10
LED4 equ 34h ; преобразовании двоичного результата
LED5 equ 35h ; измерения тока.
;--------------------------------------------------------------------------------
; Регистры хранения результата АЦП.
;--------------------------------------------------------------------------------
; ОСНОВНЫЕ:
Temp_U1L equ 36h ; Регистр младшего разряда результата
; 1-го АЦП.
Temp_U1H equ 37h ; Регистр старшего разряда результата
; 1-го АЦП.
Temp_I1L equ 38h ; Регистр младшего разряда результата
; 2-го АЦП.
Temp_I1H equ 39h ; Регистр старшего разряда результата
; 2-го АЦП.
;-----------------------------------
; ДОПОЛНИТЕЛЬНЫЕ:
Temp_U2L equ 3Ah ; Регистр младшего разряда результата
; 1-го АЦП.
Temp_U2H equ 3Bh ; Регистр старшего разряда результата
; 1-го АЦП.
Temp_I2L equ 3Ch ; Регистр младшего разряда результата
; 2-го АЦП.
Temp_I2H equ 3Dh ; Регистр старшего разряда результата
; 2-го АЦП.
;--------------------------------------------------------------------------------
; Регистры, используемые в ПП прерывания для сохранения содержимого W и Status.
4
;--------------------------------------------------------------------------------
W_Temp equ 20h ; Регистр сохранения содержимого W
; в 0-м банке.
W_Temp1 equ 0A0h ; Регистр сохранения содержимого W
; в 1-м банке.
Stat_Temp equ 21h ; Регистр сохранения содержимого Status
; в 0-м банке.
Stat_Temp1 equ 0A1h ; Регистр сохранения содержимого Status
; в 1-м банке.
;--------------------------------------------------------------------------------
; Всякая всячина.
;--------------------------------------------------------------------------------
Flag equ 22h ; Регистр флагов:
; бит №7 - флаг признака банка,
; бит №6 - флаг разрешения/запрета изменения
; результата измерения, который выводится
; на индикацию.
Count equ 23h ; Регистр счетчика проходов.
Temp equ 24h ; Универсальный регистр временного
; хранения данных.
Mem equ 25h ; Регистр временного хранения данных
; ПП ENTER_BF.

Reg equ 26h ; Регистр ПП универсальной задержки.


Reg_1 equ 27h ; Регистр ПП задержки, используемой
; в прерываниях.
Reg_2 equ 28h ; Регистры фиксированной задержки,
Reg_3 equ 29h ; определяющей скорость смены показаний.
;================================================================================
; Определение места размещения результатов операций.
;================================================================================
W equ 0 ; Результат направить в аккумулятор.
F equ 1 ; Результат направить в регистр.
;================================================================================
; Присвоение битам названий.
;================================================================================
C equ 0 ; Флаг переноса-заема.
Z equ 2 ; Флаг нулевого результата.
RP0 equ 5 ; Бит выбора банка.
RP1 equ 6 ; Бит выбора банка.
GO equ 2 ; Бит статуса модуля АЦП.
TMR2IF equ 1 ; Флаг прерывания по переполнению TMR2.
RW equ 6 ; Бит №6 регистра PortС
; (вывод RC6 - линия RW).
RS equ 5 ; Бит №5 регистра PortC
; (вывод RC5 - линия RS).
E equ 7 ; Бит №7 регистра PortC
; (вывод RC7 - линия E).
BF equ 7 ; Бит №7 регистра PortB (флаг BF).
;================================================================================
org 0 ; Начать выполнение программы с 0 адреса PC.
goto START ; Переход в ПП START.
org 4 ; Назначение вектора прерывания.
;********************************************************************************

;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
; ПОДПРОГРАММА ПРЕРЫВАНИЯ. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
; Процедура сохранения содержимого регистров Status и W.
;================================================================================
; Определение банка, из которого осуществлен уход в прерывание.
;--------------------------------------------------------------------------------
btfsc Status,RP0 ; Из какого банка осуществлен уход
; в прерывание ?
goto BANK_1 ; Если это 1-й банк.
; Если это 0-й банк, то программа
5
; исполняется далее.
;--------------------------------------------------------------------------------
; Процедура сохранения Status и W в регистрах 0-го банка (Stat_Temp, W_Temp).
;--------------------------------------------------------------------------------
movwf W_Temp ; W -> W_Temp.
swapf Status,W ; Смена п/байтов регистра Status.
; Результат -> W.
movwf Stat_Temp ; W -> Stat_Temp.
bcf Flag,7 ; Установка флага признака банка
; (Flag,7=0: 0-й банк).
; Используется при восстановлении.
goto ZAMER ; Переход на начало АЦП.
;--------------------------------------------------------------------------------
; Процедура сохранения Status и W в регистрах 1-го банка (Stat_Temp1, W_Temp1).
;--------------------------------------------------------------------------------
BANK_1 movwf W_Temp1 ; W -> W_Temp1.
swapf Status,W ; Смена п/байтов регистра Status.
; Результат -> W.
movwf Stat_Temp1 ; W -> Stat_Temp1.
bcf Status,RP0 ; Сброс 0-го банка.
bsf Flag,7 ; Установка флага признака банка
; (Flag,7=1: 1-й банк).
; Используется при восстановлении.

;********************************************************************************
; 1-е АЦП (напряжение). *********************************************************
;********************************************************************************
; Работа с регистром Adcon0.
;--------------------------------------------------------------------------------
ZAMER movlw b'01000001' ; Включение модуля АЦП, выбор канала AN0
movwf Adcon0 ; (RA0), источник тактового сигнала Fosc/8,
; состояние ожидания, конденсатор подключен к
; выбранному аналоговому входу и начал
; перезаряжаться.
call PAUSE ; Задержка для перезаряда конденсатора.
;----> Возврат по стеку из ПП PAUSE.
;--------------------------------------------------------------------------------
; Начало аналого-цифрового преобразования.
;--------------------------------------------------------------------------------
bsf Adcon0,GO ; Включение преобразования. Конденсатор
; отключается от аналогового входа на время
; преобразования.
;--------------------------------------------------------------------------------
; Ожидание окончания АЦП ("плавающая" задержка).
;--------------------------------------------------------------------------------
btfsc Adcon0,GO ; Ожидание окончания аналого-цифрового
goto $-1 ; преобразования.
;------------------------------------------------
; 1-е АЦП закончено. Результат - в AdresH/AdresL.
;================================================================================
; Копирование результата либо в Temp_U1H/Temp_U1L, либо в Temp_U2H/Temp_U2L.
;================================================================================
; Проверка состояния флага разрешения/запрета изменения
; результата измерения, который выводится на индикацию.
;--------------------------------------------------------------------------------
btfsc Flag,6 ; Каково состояние флага ?
goto OBH_1 ; Если поднят (1), то изменения
; Temp_U1H/Temp_U1L запрещаются.
; Если опущен (0), то изменения
; Temp_U1H/Temp_U1L разрешаются.
;--------------------------------------------------------------------------------
; Отрабатывается, если изменения Temp_U1H/Temp_U1L разрешаются.
;--------------------------------------------------------------------------------
movf AdresH,W ; AdresH --> W.
movwf Temp_U1H ; W --> Temp_U1H.

6
bsf Status,RP0 ; Переход в 1-й банк.
movf AdresL,W ; AdresL --> W.
bcf Status,RP0 ; Переход в 0-й банк.
movwf Temp_U1L ; W --> Temp_U1L.
goto ACP_1 ; Обход следующей ПП.
;--------------------------------------------------------------------------------
; Отрабатывается, если изменения Temp_U1H/Temp_U1L запрещаются.
;--------------------------------------------------------------------------------
OBH_1 movf AdresH,W ; AdresH --> W.
movwf Temp_U2H ; W --> Temp_U2H.

bsf Status,RP0 ; Переход в 1-й банк.


movf AdresL,W ; AdresL --> W.
bcf Status,RP0 ; Переход в 0-й банк.
movwf Temp_U2L ; W --> Temp_U2L.

;********************************************************************************
; 2-е АЦП (сила тока). **********************************************************
;********************************************************************************
; Работа с регистром Adcon0.
;--------------------------------------------------------------------------------
ACP_1 bsf Adcon0,3 ; Выбор канала AN1 (RA1). Остальные
; настройки Adcon0 не меняются.
call PAUSE ; Аналогично.
;----> Возврат по стеку из ПП PAUSE.
;--------------------------------------------------------------------------------
; Начало аналого-цифрового преобразования.
;--------------------------------------------------------------------------------
bsf Adcon0,GO ; Аналогично.
;--------------------------------------------------------------------------------
; Ожидание окончания АЦП ("плавающая" задержка).
;--------------------------------------------------------------------------------
btfsc Adcon0,GO ; Аналогично.
goto $-1 ; ----"----
;------------------------------------------------
; 2-е АЦП закончено. Результат - в AdresH/AdresL.
;================================================================================
; Копирование результата либо в Temp_I1H/Temp_I1L, либо в Temp_I2H/Temp_I2L.
;================================================================================
; Проверка состояния флага разрешения/запрета изменения
; результата измерения, который выводится на индикацию.
;--------------------------------------------------------------------------------
btfsc Flag,6 ; Аналогично, только для
goto OBH_2 ; регистров Temp_I1H/Temp_I1L.
;--------------------------------------------------------------------------------
; Отрабатывается, если изменения Temp_I1H/Temp_I1L разрешаются.
;--------------------------------------------------------------------------------
movf AdresH,W ; ----"----
movwf Temp_I1H ; ----"----
bsf Status,RP0 ; Аналогично, только для
movf AdresL,W ; регистров Temp_I1H/Temp_I1L.
bcf Status,RP0 ; ----"----
movwf Temp_I1L ; ----"----
goto ACP_2 ; Обход следующей ПП.
;--------------------------------------------------------------------------------
; Отрабатывается, если изменения Temp_I1H/Temp_I1L запрещаются.
;--------------------------------------------------------------------------------
OBH_2 movf AdresH,W ; ----"----
movwf Temp_I2H ; ----"----
bsf Status,RP0 ; Аналогично, только для
movf AdresL,W ; регистров Temp_I2H/Temp_I2L.
bcf Status,RP0 ; ----"----
movwf Temp_I2L ; ----"----
;--------------------------------------------------------------------------------
; Теперь модуль АЦП можно выключить.
;--------------------------------------------------------------------------------
7
ACP_2 clrf Adcon0 ; Для снижения потребляемого устройством тока,
; модуль АЦП выключается до конца отработки
; текущего полного цикла программы.
;================================================================================
; Процедура восстановления содержимого регистров Status, W и выхода
; из прерывания.
;================================================================================
btfss Flag,7 ; Каково состояние флага признака банка ?
goto VOSST_0 ; Если 0-й банк (Flag,7=0), то работа
; с Stat_Temp и W_Temp.
; Если 1-й банк (Flag,7=1), то работа
; с Stat_Temp1 и W_Temp1.
;--------------------------------------------------------------------------------
; Процедура восстановления Status и W из содержимого регистров 1-го банка
; (Stat_Temp1, W_Temp1).
;--------------------------------------------------------------------------------
bcf PIR1,TMR2IF ; Сброс флага прерывания по переполнению TMR2.

bsf Status,RP0 ; Установка 1-го банка.


swapf Stat_Temp1,W; Обмен п/байтами. Результат -> W.
movwf Status ; W -> Status.
swapf W_Temp1,F ; Обмен п/байтами. Результат -> W_Temp1.
swapf W_Temp1,W ; Обмен п/байтами. Результат -> W.
retfie ; Возврат из прерывания с 1-м банком (с 1-м
; банком вошли в прерывание, с 1-м банком
; и вышли).
;--------------------------------------------------------------------------------
; Процедура восстановления Status и W из содержимого регистров 0-го банка
; (Stat_Temp, W_Temp).
;--------------------------------------------------------------------------------
VOSST_0 swapf Stat_Temp,W ; Обмен п/байтами. Результат -> W.
movwf Status ; W -> Status.
swapf W_Temp,F ; Обмен п/байтами. Результат -> W_Temp.
swapf W_Temp,W ; Обмен п/байтами. Результат -> W.

bcf PIR1,TMR2IF ; Сброс флага прерывания по переполнению TMR2.


retfie ; Возврат из прерывания с 0-м банком (с 0-м
; банком вошли в прерывание, с 0-м банком
; и вышли).
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

;================================================================================
; Группа подпрограмм табличных вычисляемых переходов 1-го "кадра".
;================================================================================
; Надпись "Напряжение: в"
;-----------------------------------
TEXT_1 addwf PC,F ; Приращение PC на величину содержимого W.
dt 0x48,0x61,0xBE,0x70,0xC7,0xB6,0x65,0xBD
dt 0xB8,0x65,0x20,0x20,0x20,0x20,0x20,0xB3
;-----------------------------------
; Надпись "Сила тока: ма"
;-----------------------------------
TEXT_2 addwf PC,F ; Приращение PC на величину содержимого W.
dt 0x43,0xB8,0xBB,0x61,0x20,0xBF,0x6F,0xBA
dt 0x61,0x20,0x20,0x20,0x20,0x20,0xBC,0x61
;================================================================================
; Таблица констант делителей.
;================================================================================
CONST addwf PC,F ; Приращение PC на величину содержимого W.
dt .156, .255, .100, .0, .246, .255, .10, .0
;================================================================================
; Задержка для перезаряда конденсатора Hold.
;================================================================================
PAUSE movlw .5 ; Стандартный,
movwf Reg_1 ; вычитающий,
decfsz Reg_1,F ; однобайтный
8
goto $-1 ; счетчик.
return ; Возврат по стеку.
;================================================================================
; Универсальная задержка (время задержки определяется предустановленным
; содержимым W).
;================================================================================
PAUSE_X movwf Reg ; Стандартный, вычитающий,
decfsz Reg,F ; однобайтный
goto $-1 ; счетчик.
return ; Возврат по стеку.
;================================================================================
; ПП "плавающей" задержки на основе анализа состояния флага занятости BF
; (вариант для 4-разрядного интерфейса).
;================================================================================
ENTER_BF movwf Mem ; Переправка старшего п/байта регистра W на
movwf PortB ; линии RB4...7.
;-----------------------------------
; Запуск в работу старшего п/байта.
;-----------------------------------
nop ; Задержка в 1 м.ц.
bsf PortC,E ; Установка на линии Е "1".
nop ; Задержка в 1 м.ц.
bcf PortC,E ; Установка на линии Е "0".

swapf Mem,W ; Смена п/байтов. Результат -> W.


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

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


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

9
;********************************************************************************
; НАЧАЛО ИСПОЛНЕНИЯ ПРОГРАММЫ.
;********************************************************************************
; Подготовительные операции.
;================================================================================
START bcf Status,RP0 ; Подтверждение установки
bcf Status,RP1 ; 0-го банка.
clrf PortC ; Сброс защелок порта A в "0"
; (RS=0, RW=0, E=0).
clrf PortB ; Сброс защелок порта B в "0".
clrf IntCon ; Сброс IntCon.
clrf TMR2 ; Обеспечение начала отсчета TMR2 от 0.
movlw b'00000100' ; Модуль TMR2 включен с Кдел. предделителя = 1
movwf T2CON ; и Кдел. выходного делителя = 1.

bsf Status,RP0 ; Переход в 1-й банк.


clrf TrisB ; Все выводы порта В работают на выход.
clrf TrisC ; Все выводы порта С работают на выход.
; Выводы порта A, по умолчанию, работают на вход.
movlw b'10000100' ; Диапазон квантования напряжения от -Vss до
movwf Adcon1 ; + Vdd, AN0/RA0, AN1/RA1, AN3/RA3 -аналоговые
; входы, AN2/RA2, AN4/RA5 - цифровые каналы
; ввода/вывода, правое выравнивание.
movlw .249 ; Задание периода ухода в
movwf PR2 ; прерывания = 250 мкс. (F = 4 Кгц.).
movlw b'11000000' ; Глобальное разрешение прерываний и
movwf IntCon ; разрешение прерываний от периферийных
; модулей.
movlw b'00000010' ; Разрешение прерываний
movwf PIE1 ; по переполнению TMR2.
bcf Status,RP0 ; Переход в 0-й банк.

bcf PIR1,TMR2IF ; Сброс флага прерывания по переполнению TMR2.

;================================================================================
; Подпрограмма инициализации ЖКИ модуля.
;================================================================================
movlw b'00110000' ; Установка: 8-разрядный интерфейс, 1 строка,
; 5х8 точек, 0-я страница знакогенератора.
call ENTER_BF ; "Плавающая" задержка со стробом
; под команду 00110000.
;----> Возврат по стеку из ПП ENTER_BF.
movlw .15 ;
call PAUSE_X ; Задержка 50 мкс.
;----> Возврат по стеку из ПП PAUSE.
movlw b'00101000' ; Установка: 4-разрядный интерфейс, 2 строки,
; 5х8 точек, 0-я страница знакогенератора.
call ENTER_BF ; "Плавающая" задержка со стробом
; под команду 00101000.
;----> Возврат по стеку из ПП ENTER_BF.
movlw b'00101000' ; Установка: 4-разрядный интерфейс, 2 строки,
; 5х8 точек, 0-я страница знакогенератора.
call ENTER_BF ; "Плавающая" задержка со стробом
; под команду 00101000.
;----> Возврат по стеку из ПП ENTER_BF.
movlw b'00000001' ; Установка: очистка дисплея со сбросом
; данных, установка курсора в начало
; 1-й строки.
call ENTER_BF ; "Плавающая" задержка со стробом
; под команду 00000001.
;----> Возврат по стеку из ПП ENTER_BF.
movlw b'00001100' ; Установка: дисплей включен, видимое
; отображение курсора выключено.
call ENTER_BF ; "Плавающая" задержка со стробом
; под команду 00001100.
10
;----> Возврат по стеку из ПП ENTER_BF.
;////////////////////////////////////////////////////////////////////////////////
; Вывод на индикацию картинки 1-го "кадра".
;////////////////////////////////////////////////////////////////////////////////
; Вывод на индикацию надписи "Напряжение: в" (в 1-ю строку).
;================================================================================
; Вывод символов в 1-ю строку (установлена в ПП инициализации ЖК-модуля).
;--------------------------------------------------------------------------------
movlw .16 ; Запись числа .16 (количества выводимых в
movwf Count ; строку символов) в регистр Count.
movf Count,W ; Count -> W.
sublw .16 ; .16 - Count = ... (результат -> W).
call TEXT_1 ; Условный переход в ПП TEXT_1.
;----> Возврат по стеку из ПП TEXT_1.
bsf PortC,RS ; Установка на линии RS "1"
; (режим записи данных).
call ENTER_BF ; "Плавающая" задержка со стробом под вывод
; данных на индикацию.
;----> Возврат по стеку из ПП ENTER_BF.
decfsz Count,F ; Подготовка к выводу на индикацию
; следующего символа таблицы.
goto $-6 ; Если результат декремента не=0, то
; вывод на индикацию следующего символа.
; Если результат декремента =0, то программа
; исполняется далее.
;================================================================================
; Вывод на индикацию надписи "Сила тока: ма" (во 2-ю строку).
;================================================================================
; Переход в начало 2-й строки.
;--------------------------------------------------------------------------------
movlw b'11000000' ; Аналогично, только для адреса 40h (установка
call ENTER_BF ; курсора в крайнее левое знакоместо
; 2-й строки).
;----> Возврат по стеку из ПП ENTER_BF.
;--------------------------------------------------------------------------------
; Вывод символов во 2-ю строку.
;--------------------------------------------------------------------------------
movlw .16 ;
movwf Count ;
;
movf Count,W ;
sublw .16 ; Аналогично, только
call TEXT_2 ; для TEXT_2.
;----> Возврат по стеку из ПП TEXT_2.
bsf PortC,RS ;
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.
decfsz Count,F ;
goto $-6 ;
;////////////////////////////////////////////////////////////////////////////////

;################################################################################
; НАЧАЛО ЦИКЛА ПРОГРАММЫ (от 2-го "витка" и далее).
;################################################################################
; "Администраторская" группа команд.
;================================================================================
CYCLE call BIN2_10_U ; Вызов ПП преобразования 2-байтного двоичного
; числа в 3-разрядное десятичное число
; (работа с результатом измерения напряжения).
;----> Возврат по стеку из ПП BIN2_10_U.

call BIN2_10_I ; Вызов ПП преобразования 2-байтного двоичного


; числа в 3-разрядное десятичное число
; (работа с результатом измерения тока).
;----> Возврат по стеку из ПП BIN2_10_I.

11
;********************************************************************************
; Вывод результатов измерения в 1-ю строку.
;********************************************************************************
; Переход в 12-е слева знакоместо 1-й строки.
;--------------------------------------------
movlw b'10001011' ; Выбор ячейки DD RAM с адресом 0Bh, что
movwf PortB ; соответствует установке курсора в 12-е слева
; знакоместо 1-й строки.
call ENTER_BF ; "Плавающая" задержка со стробом
; под команду 10001011.
;----> Возврат по стеку из ПП ENTER_BF.
;================================================================================
; Формирование и вывод на индикацию символов результата измерения напряжения.
;================================================================================
movf LED2,W ; Вывод на индикацию
movwf PortB ; содержимого LED2.
bsf PortC,RS ; Установка на линии RS "1"
; (режим записи данных).
call ENTER_BF ; "Плавающая" задержка со стробом под вывод
; данных на индикацию.
;----> Возврат по стеку из ПП ENTER_BF.

movf LED1,W ;
movwf PortB ; Аналогично,
bsf PortC,RS ; но для LED1.
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.

movlw 2Eh ;
movwf PortB ; Аналогично, но для
bsf PortC,RS ; символа "точка" (2Eh).
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.

movf LED0,W ;
movwf PortB ; Аналогично,
bsf PortC,RS ; но для LED0.
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.
;********************************************************************************
; Вывод результатов измерения во 2-ю строку.
;********************************************************************************
; Переход в 11-е слева знакоместо 2-й строки.
;--------------------------------------------
movlw b'11001010' ; Аналогично, только для адреса 0Ah, что
movwf PortB ; соответствует установке курсора в 11-е слева
; знакоместо 2-й строки.
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.
;================================================================================
; Формирование и вывод на индикацию символов результата измерения тока.
;================================================================================
movf LED5,W ;
movwf PortB ; Аналогично,
bsf PortC,RS ; но для LED5.
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.

movf LED4,W ;
movwf PortB ; Аналогично,
bsf PortC,RS ; но для LED4.
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.

movlw 2Eh ;
movwf PortB ; Аналогично, но для
12
bsf PortC,RS ; символа "точка" (2Eh).
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.

movf LED3,W ;
movwf PortB ; Аналогично,
bsf PortC,RS ; но для LED3.
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.
;================================================================================
; Задержка, задающая скорость смены показаний.
;================================================================================
movlw .250 ;
movwf Reg_2 ;
movlw .250 ;
movwf Reg_3 ;

decfsz Reg_2,F ; Стандартный,


goto $-1 ; 2-байтный,
decfsz Reg_3,F ; вычитающий
goto $-3 ; счетчик.
;================================================================================
goto CYCLE ; Переход на следующий цикл программы.
;********************************************************************************

;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; ПП преобразования 2-байтного двоичного числа в 3-разрядное десятичное число
; (работа с результатом измерения напряжения).
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Подготовительные операции.
;--------------------------------------------------------------------------------
BIN2_10_U bsf Flag,6 ; Запрет изменения результата измерения.
clrf LED1 ; Подготовка к работе регистров
clrf LED2 ; результата 2/10 преобразования.
clrf Count ; Подготовка к работе счётчика выбора
; констант.
movlw LED2 ; Начало процедуры косвенной адресации:
movwf FSR ; запись в FSR адреса регистра LED2.
;================================================================================
; Блокировка нулями результата измерения с числовым значением от .1000 и более.
; Примечание: разложение числа 1000 = 256 х 3 + 232
;================================================================================
movlw 3 ; Проверка байта Temp_U1H на "залегание в нем"
subwf Temp_U1H,W ; числа 3 (0000 0011).
btfss Status,Z ; Если это число 3, то переход на проверку
goto NEXT ; байта Temp_U1L, а если нет, то переход
; в ПП NEXT.
movlw .232 ; Проверка байта Temp_U1L на "залегание в нем"
subwf Temp_U1L,W ; числа 232 (1110 1000).
btfss Status,C ; Если это число меньше или равно 232 (С=1),
goto NEXT ; то переход в NEXT.
clrf Temp_U1H ; Если это число больше 232 (С=0), то
clrf Temp_U1L ; результат замера сбрасывается в 0.
;================================================================================
; ПП формирования десятичного значения текущего LED с "привязкой" к конкретному
; делителю (константам).
;================================================================================
NEXT movf Count,W ; Подготовка к выбору константы
; (то есть, к выбору делителя).
call CONST ; Выбор константы для регистра Temp_U1L.
;----> Возврат по стеку из ПП CONST.
addwf Temp_U1L,F ; Temp_U1L - CONST = ... (вычитание делителя
; из делимого в приложении к Temp_U1L).
btfsc Status,C ; Перенос был или нет?
incf Temp_U1H,F ; Если был, то Temp_U1H + 1 = ...
13
; (перенос в Temp_U1H)
; Результат -> в Temp_U1H.
incf Count,W ; Если не было (а также после Temp_U1H + 1),
; то подготовка к выбору следующей константы.
call CONST ; Выбор константы для регистра Temp_U1H.
;----> Возврат по стеку из ПП CONST.
addwf Temp_U1H,F ; Temp_U1H - CONST = ... (вычитание делителя
; из делимого в приложении к Temp_U1H).
incf IndF,F ; Подсчёт кол-ва вычитаний
; (текущий LED + 1 = ... Результат -> в LED).
btfsc Status,C ; Перенос был или нет (делимое больше или
; меньше делителя)?
goto NEXT ; Если был (делимое больше делителя), то
; продолжаем вычитать.
;================================================================================
; Работа с остатком от деления.
;================================================================================
bsf Count,1 ; Если не был, то подготовка к выбору
movf Count,W ; константы (с адресом PC+02h) для
; формирования истинного значения остатка.
call CONST ; Выбор константы для регистра Temp_U1L.
;----> Возврат по стеку из ПП CONST.
;-----------------------------------------
; Формирование истинного значения остатка.
;-----------------------------------------
addwf Temp_U1L,F ; Операция суммирования содержимого
; W и Temp_U1L.
btfsc Status,C ; Перенос был или нет?
incf Temp_U1H,F ; Если был, то Temp_U1H + 1 = ...
; (перенос в Temp_U1H)
; Результат -> в Temp_U1H.
incf Count,W ; Если не было (а также после Temp_U1H + 1),
; то подготовка к выбору константы.
call CONST ; Выбор константы для регистра Temp_U1H.
;----> Возврат по стеку из ПП CONST.
addwf Temp_U1H,F ; Операция суммирования содержимого
; W и Temp_U1H.
decf IndF,F ; Устранение "перебора" в текущем LED.
;--------------------------------------------------------------------------------
; Работа с текущей четверкой констант закончена
; (содержимое текущего LED сформировано).
;================================================================================
; Переход на следующий LED и следующий, "привязанный" к нему, делитель.
;================================================================================
decf FSR,F ; Загрузка в FSR адреса следующего
; регистра LED.
incf Count,F ; Настройка для выбора необходимой константы
incf Count,F ; (для перехода на следующий делитель):
; Count + 2 = ... Результат -> в Count.
movf Count,W ; Count -> W.
sublw .7 ; Проверка: все константы (делители)
btfsc Status,C ; перебрали или нет?
goto NEXT ; Если не все, то начинается работа со
; следующим LEDом и с "привязанной" к нему
; группой констант.
; Если все, то работаем с остатком от
; комплексного деления.
;================================================================================
; Работа с остатком от комплексного деления (окончание процедуры).
;================================================================================
movf Temp_U1L,W ; Загружаем остаток в регистр
movwf LED0 ; десятичного разряда единиц.
bcf Flag,6 ; Разрешение изменения результата измерения.
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
; Преобразование кода (+30h) и гашение незначащего нуля в старшем (левом)
; десятичном разряде.
14
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
movlw 30h ; -----"-----
addwf LED0,F ; LED0 + 30h = ...

movlw 30h ; -----"-----


addwf LED1,F ; LED1 + 30h = ...

movf LED2,W ; LED2 -> W.


btfss Status,Z ; В LED2 ноль или другое число ?
goto $+4 ; Если не 0, то обход следующих 3-х команд.
movlw 20h ; Если 0, то в LED2 записывается код
movwf LED2 ; символа "пробел" (20h).
return ; Возврат по стеку.

movlw 30h ; -----"-----


addwf LED2,F ; LED2 + 30h = ...
return ; Возврат по стеку.

;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; ПП преобразования 2-байтного двоичного числа в 3-разрядное десятичное число
; (работа с результатом измерения тока).
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; ТОЧНО ТАКАЯ ЖЕ ПРОЦЕДУРА, КАК И В СЛУЧАЕ ОБРАБОТКИ РЕЗУЛЬТАТА ИЗМЕРЕНИЯ
; НАПРЯЖЕНИЯ, ТОЛЬКО ВМЕСТО РЕГИСТРОВ Temp_U1H/Temp_U1L ИСПОЛЬЗУЮТСЯ РЕГИСТРЫ
; Temp_I1H/Temp_I1L, А ВМЕСТО РЕГИСТРОВ LED2,1,0 ИСПОЛЬЗУЮТСЯ РЕГИСТРЫ LED5,4,3.
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Подготовительные операции.
;--------------------------------------------------------------------------------
BIN2_10_I bsf Flag,6 ;
clrf LED4 ;
clrf LED5 ; Аналогично.
clrf Count ;
movlw LED5 ;
movwf FSR ;
;================================================================================
; Блокировка нулями результата измерения с числовым значением от .1000 и более.
; Примечание: разложение числа 1000 = 256 х 3 + 232
;================================================================================
movlw 3 ;
subwf Temp_I1H,W ;
btfss Status,Z ;
goto NEXT_1 ;
movlw .232 ; Аналогично.
subwf Temp_I1L,W ;
btfss Status,C ;
goto NEXT_1 ;
clrf Temp_I1H ;
clrf Temp_I1L ;
;================================================================================
; ПП формирования десятичного значения текущего LED с "привязкой" к конкретному
; делителю (константам).
;================================================================================
NEXT_1 movf Count,W ;
call CONST ;
;----> Возврат по стеку из ПП CONST.
addwf Temp_I1L,F ;
btfsc Status,C ;
incf Temp_I1H,F ;
incf Count,W ; Аналогично.
call CONST ;
;----> Возврат по стеку из ПП CONST.
addwf Temp_I1H,F ;
incf IndF,F ;
btfsc Status,C ;
goto NEXT_1 ;
;================================================================================
15
; Работа с остатком от деления.
;================================================================================
bsf Count,1 ;
movf Count,W ; Аналогично.
call CONST ;
;----> Возврат по стеку из ПП CONST.
;-----------------------------------------
; Формирование истинного значения остатка.
;-----------------------------------------
addwf Temp_I1L,F ;
btfsc Status,C ;
incf Temp_I1H,F ;
incf Count,W ; Аналогично.
call CONST ;
;----> Возврат по стеку из ПП CONST.
addwf Temp_I1H,F ;
decf IndF,F ;
;--------------------------------------------------------------------------------
; Работа с текущей четверкой констант закончена
; (содержимое текущего LED сформировано).
;================================================================================
; Переход на следующий LED и следующий, "привязанный" к нему, делитель.
;================================================================================
decf FSR,F ;
incf Count,F ;
incf Count,F ;
movf Count,W ; Аналогично.
sublw .7 ;
btfsc Status,C ;
goto NEXT_1 ;
;================================================================================
; Работа с остатком от комплексного деления (окончание процедуры).
;================================================================================
movf Temp_I1L,W ;
movwf LED3 ; Аналогично.
bcf Flag,6 ;
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
; Преобразование кода (+30h) и гашение незначащего нуля в старшем (левом)
; десятичном разряде.
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
movlw 30h ;
addwf LED3,F ;
movlw 30h ;
addwf LED4,F ;
movf LED5,W ;
btfss Status,Z ;
goto $+4 ; Аналогично.
movlw 20h ;
movwf LED5 ;
return ;
movlw 30h ;
addwf LED5,F ;
return ;
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
end ; Конец программы.

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


Синим цветом выделено то, что относится к работе с модулем TMR2.
Зеленым цветом выделена группа команд инициализации ЖК-модуля.
Темно-синим цветом выдено то, что относится к обработке результатов измерения.
Темно-красным цветом выделено то, что относится к выводу на индикацию
результатов измерения.

16
Последовательность производимых действий (упрощенная блок – схема) показана на
рис. 2:

Работа программы

Начну с начала. То есть, с ПП START:


;********************************************************************************
; НАЧАЛО ИСПОЛНЕНИЯ ПРОГРАММЫ.
;********************************************************************************
; Подготовительные операции.
;================================================================================
START bcf Status,RP0 ; Подтверждение установки
bcf Status,RP1 ; 0-го банка.
clrf PortC ; Сброс защелок порта A в "0"
; (RS=0, RW=0, E=0).
clrf PortB ; Сброс защелок порта B в "0".
clrf IntCon ; Сброс IntCon.
clrf TMR2 ; Обеспечение начала отсчета TMR2 от 0.
movlw b'00000100' ; Модуль TMR2 включен с Кдел. предделителя = 1
movwf T2CON ; и Кдел. выходного делителя = 1.

bsf Status,RP0 ; Переход в 1-й банк.


clrf TrisB ; Все выводы порта В работают на выход.
clrf TrisC ; Все выводы порта С работают на выход.
; Выводы порта С, по умолчанию, работают на вход.
movlw b'10000100' ; Диапазон квантования напряжения от -Vss до
movwf Adcon1 ; + Vdd, AN0/RA0, AN1/RA1, AN3/RA3 -аналоговые
; входы, AN2/RA2, AN4/RA5 - цифровые каналы
; ввода/вывода, правое выравнивание.
movlw .249 ; Задание периода ухода в
movwf PR2 ; прерывания = 250 мкс. (F = 4 Кгц.).
movlw b'11000000' ; Глобальное разрешение прерываний и
movwf IntCon ; разрешение прерываний от периферийных

17
; модулей.
movlw b'00000010' ; Разрешение прерываний
movwf PIE1 ; по переполнению TMR2.
bcf Status,RP0 ; Переход в 0-й банк.

bcf PIR1,TMR2IF ; Сброс флага прерывания по переполнению TMR2.


;================================================================================
; Подпрограмма инициализации ЖКИ модуля.
;================================================================================

В ее начале, производится "перестраховочное" подтверждение установки 0-го банка.


Можно обойтись и без него (по умолчанию, установлен 0-й банк).
То же самое относится и к защелкам портов В и С, и к регистру IntCon.
Что касается последнего, то по умолчанию, все биты регистра IntCon устанавливаются
в нули, за исключением бита №0 (RBIF).
Он может принять любое зачение (см. даташит).
Поэтому, после грядущего, "глобального" разрешения прерываний, имеется вероятность
ухода в прерывание по изменению уровней на выводах RB4…7 (см. принципиальную
схему), с последующим "глюком" (в прерывании, флаг RBIF программно не опускается).
Поэтому и clrf IntCon.
Далее, в связи с грядущим задействованием модуля TMR2, с целью обеспечения счета
от нуля (не обязательно, но для порядка), содержимое регистра TMR2 сбрасывается
в 0 (clrf TMR2).
Теперь можно включить модуль TMR2 и выбрать Кдел. предделителя и выходного
делителя.
Для обеспечения периода ухода в прерывания = 250 мкс., нужно установить оба этих
Кдел. = 1.
Для обеспечения успешности дальнейших настроек, нужно перейти в 1-й банк, что и
имеет место быть.
Все выводы порта В и С настраиваются на работу "на выход" потому, что в
дальнейшем, нужно исполнять инструкции процедуры инициализации.
Настраивать направления работы порта С не нужно, так как по умолчанию, они
настроены на работу "на вход".
Но можно и "подстраховаться". Это как кому хочется.
Далее, следует часть настройки модуля АЦП (работа с регистром Adcon1).
Зачем ее "засовывать" в ПП прерывания (в ней, каждый м.ц. "на вес золота"), если ее
нужно выполнить только один раз?
Вот и пусть она "торчит" в ПП START.
Далее, заканчивается настройка модуля TMR2 (запись времязадающей константы в
регистр периода PR2) и стандартно разрешаются прерывания по переполнению TMR2.
Далее, переход в 0-й банк и сброс флага прерывания по переполнению TMR2.
Все… "Часы тикают".
Далее, прерывания по переполнению TMR2 не запрещаются и будет классическое,
равномерно-периодическое "на колу мочало" (периодические уходы в прерывания).
И этому "мочалу", в данном случае, абсолютно "по барабану" всё дальнейшее, так как
работа модуля TMR2 происходит в аппаратной "епархии" и никаких его программных
перенастроек нет.
Оно ("аппаратное мочало") "порвет на одинаковые куски" любого, даже самого "крутого,
программного мэна".
Только "пух и перья лететь будут".
Но порвет не абы как, а с умом и осторожненько.
В том смысле, что если последовательно "прислонить эти куски" друг к другу, то
получится "вполне упитанное, основное тело" программы.
Причем, в "полнейшем здравии" (со всеми конечностями, дырками, волосами, мозгами
и т.д.).
"Даже диву даешься, каких высот достигла шахматная мысль" ("нетленка" от Ильфа и
Петрова. Талантищи. Аналогов нет и наверное не будет. Сколько их знаю, столько и
смеюсь. Лет эдак 30).
А раз это так, то пока, "понятийновыгодно, прерывательно не заморачиваться" тем, что
следует после разрешения прерываний по переполнению TMR2.
18
То есть, с целю обеспечения наивысшей, понятийной комфортности осознания
алгоритма работы "основного тела" пограммы, выгодно временно и осознанно "закрыть
глаза" на все то, что связано с деталями отработки ПП прерывания и рассматривать
это "все то", как нечто "отвлеченно-безгрешное", и не в коем разе не нарушающее
процесс отработки "основного тела" программы.
Проще говоря, выгодно предположить, что ПП прерывания отрабатывается идеально.
Это означает: "на выходе" ПП прерывания, имеет место быть истинный результат
измерения Uвых. и Iвых., представленный в двоичной форме, и никаких
"прерывательных бяк" нет.
Из этого предположения, пока, и буду исходить.

После завершения отработки ПП START, начинается отработка ПП инициализации


ЖК-модуля:
;================================================================================
; Универсальная задержка (время задержки определяется предустановленным
; содержимым W).
;================================================================================
PAUSE_X movwf Reg ; Стандартный, вычитающий,
decfsz Reg,F ; однобайтный
goto $-1 ; счетчик.
return ; Возврат по стеку.
;================================================================================
....................................
....................................
;================================================================================
; Подпрограмма инициализации ЖКИ модуля.
;================================================================================
movlw b'00110000' ; Установка: 8-разрядный интерфейс, 1 строка,
; 5х8 точек, 0-я страница знакогенератора.
call ENTER_BF ; "Плавающая" задержка со стробом
; под команду 00110000.
;----> Возврат по стеку из ПП ENTER_BF.
movlw .15 ;
call PAUSE_X ; Задержка 50 мкс.
;----> Возврат по стеку из ПП PAUSE.
movlw b'00101000' ; Установка: 4-разрядный интерфейс, 2 строки,
; 5х8 точек, 0-я страница знакогенератора.
call ENTER_BF ; "Плавающая" задержка со стробом
; под команду 00101000.
;----> Возврат по стеку из ПП ENTER_BF.
movlw b'00101000' ; Установка: 4-разрядный интерфейс, 2 строки,
; 5х8 точек, 0-я страница знакогенератора.
call ENTER_BF ; "Плавающая" задержка со стробом
; под команду 00101000.
;----> Возврат по стеку из ПП ENTER_BF.
movlw b'00000001' ; Установка: очистка дисплея со сбросом
; данных, установка курсора в начало
; 1-й строки.
call ENTER_BF ; "Плавающая" задержка со стробом
; под команду 00000001.
;----> Возврат по стеку из ПП ENTER_BF.
movlw b'00001100' ; Установка: дисплей включен, видимое
; отображение курсора выключено.
call ENTER_BF ; "Плавающая" задержка со стробом
; под команду 00001100.
;----> Возврат по стеку из ПП ENTER_BF.
;////////////////////////////////////////////////////////////////////////////////
; Вывод на индикацию картинки 1-го "кадра".
;////////////////////////////////////////////////////////////////////////////////

Это то же самое, о чем шла речь в предыдущем подразделе.


ПП ENTER_BF "разрисовывать" не стал по причине ее "абсолютной стандартности".
Общее замечание: все ЖК-модули не критичны к "номиналу" скорости обмена.

19
То есть, в процессе вывода данных на индикацию, в любые моменты (синхронно
с тактом ПИКа), можно изменить скорость обмена или вообще снизить ее до
"нулевой", а затем опять продолжить обмен.
И эти "дерганья" могут повторяться многократно.
Что и имеет место быть при уходе в прерывания ("нулевая" скорость обмена).
И никакой "бяки" при этом не будет.
Это относится не только к процедуре инициализации ЖК-модуля, но и ко всей
остальной его работе.
Просто соответствующие процедуры "основного тела" программы будут отрабатываться
медленнее ("кусочнорвано").

Но зато все "прерывательные дела" будут делаться быстро.


И это, в данном случае (и во многих других случаях), главное (напоминаю
про инерционность защиты)!!!

Именно это и обьясняет то, что при вхождении процедуры инициализации ЖК-модуля
в "зону" разрешения прерываний (а это и имеет место быть), ее отработка происходит
без ошибок, но медленнее, чем в случае, если процедура инициализации ЖК-модуля
не будет входить в "зону" разрешения прерываний.
И это должно быть понятным, так как любая ПП прерывания отрабатывается не за
ноль секунд.
Примечание: то же самое будет наблюдаться и при работе по интерфейсу I2C и
вообще, при работе по любым "рабским" интерфейсам, в "правилах игры" которых
отсутствуют временные ограничения, связанные с "принудительным втискиванием"
интервала времени отработки того или иного процесса в "жесткие, временные рамки".

После отработки процедуры инициализации ЖК-модуля, на индикацию выводится


картинка 1-го "кадра":
;================================================================================
; Группа подпрограмм табличных вычисляемых переходов 1-го "кадра".
;================================================================================
; Надпись "Напряжение: в"
;-----------------------------------
TEXT_1 addwf PC,F ; Приращение PC на величину содержимого W.
dt 0x48,0x61,0xBE,0x70,0xC7,0xB6,0x65,0xBD
dt 0xB8,0x65,0x20,0x20,0x20,0x20,0x20,0xB3
;-----------------------------------
; Надпись "Сила тока: ма"
;-----------------------------------
TEXT_2 addwf PC,F ; Приращение PC на величину содержимого W.
dt 0x43,0xB8,0xBB,0x61,0x20,0xBF,0x6F,0xBA
dt 0x61,0x20,0x20,0x20,0x20,0x20,0xBC,0x61
;================================================================================
....................................
....................................
;////////////////////////////////////////////////////////////////////////////////
; Вывод на индикацию картинки 1-го "кадра".
;////////////////////////////////////////////////////////////////////////////////
; Вывод на индикацию надписи "Напряжение: в" (в 1-ю строку).
;================================================================================
; Вывод символов в 1-ю строку (установлена в ПП инициализации ЖК-модуля).
;--------------------------------------------------------------------------------
movlw .16 ; Запись числа .16 (количества выводимых в
movwf Count ; строку символов) в регистр Count.
movf Count,W ; Count -> W.
sublw .16 ; .16 - Count = ... (результат -> W).
call TEXT_1 ; Условный переход в ПП TEXT_1.
;----> Возврат по стеку из ПП TEXT_1.
bsf PortC,RS ; Установка на линии RS "1"
; (режим записи данных).
call ENTER_BF ; "Плавающая" задержка со стробом под вывод
; данных на индикацию.
20
;----> Возврат по стеку из ПП ENTER_BF.
decfsz Count,F ; Подготовка к выводу на индикацию
; следующего символа таблицы.
goto $-6 ; Если результат декремента не=0, то
; вывод на индикацию следующего символа.
; Если результат декремента =0, то программа
; исполняется далее.
;================================================================================
; Вывод на индикацию надписи "Сила тока: ма" (во 2-ю строку).
;================================================================================
; Переход в начало 2-й строки.
;--------------------------------------------------------------------------------
movlw b'11000000' ; Аналогично, только для адреса 40h (установка
call ENTER_BF ; курсора в крайнее левое знакоместо
; 2-й строки).
;----> Возврат по стеку из ПП ENTER_BF.
;--------------------------------------------------------------------------------
; Вывод символов во 2-ю строку.
;--------------------------------------------------------------------------------
movlw .16 ;
movwf Count ;
;
movf Count,W ;
sublw .16 ; Аналогично, только
call TEXT_2 ; для TEXT_2.
;----> Возврат по стеку из ПП TEXT_2.
bsf PortC,RS ;
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.
decfsz Count,F ;
goto $-6 ;
;////////////////////////////////////////////////////////////////////////////////

Это определение (1-й "кадр"), на мой взгляд, на столько удачно ("ноги растут" из
графических модулей), что я им буду пользоваться и далее.
В 1-м "кадре", на индикацию выводятся две "технологические" надписи,
"обслуживающие" результаты дальнейших измерений величин напряжения и тока.
1-й "кадр" выводится на индикацию только на 1-м "витке" полного цикла программы,
сразу же после инициализации ЖК-модуля.
Ничего нового в этой процедуре нет. Она просто "передрана" из ранее "нарытого".
А кто не понял, тому имеет смысл вернуться назад.
В данном случае, а также и далее, имеют место быть кодировки нулевой страницы
знакогенератора ЖК-модуля MT-16S2D-2YLG, которые совместимы с кодировками всех
"забугорных", русифицированных ЖК-модулей.
Примечание 1: если имеется нечто "забугорное" и подключенное к ПИКу без ошибок,
но "не желающее" работать (после прошивки ПИКа HEX-файлом программы BP_1.asm),
то "корень зла" нужно искать в процедуре инициализации ЖК-модуля.
Возможно, что потребуется замена предлагаемой процедуры инициализации ЖК-модуля,
на ту процедуру (для 4-разрядного интерфейса), которая "лежит" во 2-й части
"Самоучителя …".
Примечание 2: при таком "раскладе", защита начнет работать практически сразу же
после начала исполнения программы.
А если конкретнее, то примерно через 72 мс. после включения питания (я включил
PWRT).
Если обеспечивается высокая скорость нарастания Uпит. ПИКа, то выключив PWRT,
можно уменьшить это значение примерно до 300 … 400 мкс. (для Fкв. = 4 Мгц.).

После окончания вывода на индикацию картинки 1-го "кадра", начинается цикл


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

;################################################################################

21
; НАЧАЛО ЦИКЛА ПРОГРАММЫ (от 2-го "витка" и далее).
;################################################################################
; "Администраторская" группа команд.
;================================================================================
CYCLE call BIN2_10_U ; Вызов ПП преобразования 2-байтного двоичного
; числа в 3-разрядное десятичное число
; (работа с результатом измерения напряжения).
;----> Возврат по стеку из ПП BIN2_10_U.

На момент начала этого "действа", "железобетонно" будет отработано, как минимум,


одно прерывание (а реально - больше).
Это означает то, что уже на первом "витке" полного цикла программы, на момент
начала отработки "администраторской" группы команд, имеют место быть текущие
результаты измерения значений напряжения и тока.
Но в двоичной форме.
Значит, для того чтобы можно было вывести на индикацию результат измерения "того-
сего" в форме символов цифр, нужно осуществить 2/10 преобразование, адресную
перекодировку его результата под нулевую страницу знакогенератора ЖК-модуля и
гашение незначащих нулей.
Причем, это нужно сделать два раза, так как имеет место быть два результата
измерения (напряжения и тока).
Для осознания "сермяжной сути" этих двух "действ", достаточно осознать "сермяжную
суть" одного из них, так как речь идет о "клонах", но только с различными исходными
данными.
А так как первым обрабатывается результат измерения напряжения, то его и "терзать"
буду:
;================================================================================
; Таблица констант делителей.
;================================================================================
CONST addwf PC,F ; Приращение PC на величину содержимого W.
dt .156, .255, .100, .0, .246, .255, .10, .0
;================================================================================
....................................
....................................
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; ПП преобразования 2-байтного двоичного числа в 3-разрядное десятичное число
; (работа с результатом измерения напряжения).
;@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
; Подготовительные операции.
;--------------------------------------------------------------------------------
BIN2_10_U bsf Flag,6 ; Запрет изменения результата измерения.
clrf LED1 ; Подготовка к работе регистров
clrf LED2 ; результата 2/10 преобразования.
clrf Count ; Подготовка к работе счётчика выбора
; констант.
movlw LED2 ; Начало процедуры косвенной адресации:
movwf FSR ; запись в FSR адреса регистра LED2.
;================================================================================
; Блокировка нулями результата измерения с числовым значением от .1000 и более.
; Примечание: разложение числа 1000 = 256 х 3 + 232
;================================================================================
movlw 3 ; Проверка байта Temp_U1H на "залегание в нем"
subwf Temp_U1H,W ; числа 3 (0000 0011).
btfss Status,Z ; Если это число 3, то переход на проверку
goto NEXT ; байта Temp_U1L, а если нет, то переход
; в ПП NEXT.
movlw .232 ; Проверка байта Temp_U1L на "залегание в нем"
subwf Temp_U1L,W ; числа 232 (1110 1000).
btfss Status,C ; Если это число меньше или равно 232 (С=1),
goto NEXT ; то переход в NEXT.
clrf Temp_U1H ; Если это число больше 232 (С=0), то
clrf Temp_U1L ; результат замера сбрасывается в 0.
;================================================================================
; ПП формирования десятичного значения текущего LED с "привязкой" к конкретному
22
; делителю (константам).
;================================================================================
NEXT movf Count,W ; Подготовка к выбору константы
; (то есть, к выбору делителя).
call CONST ; Выбор константы для регистра Temp_U1L.
;----> Возврат по стеку из ПП CONST.
addwf Temp_U1L,F ; Temp_U1L - CONST = ... (вычитание делителя
; из делимого в приложении к Temp_U1L).
btfsc Status,C ; Перенос был или нет?
incf Temp_U1H,F ; Если был, то Temp_U1H + 1 = ...
; (перенос в Temp_U1H)
; Результат -> в Temp_U1H.
incf Count,W ; Если не было (а также после Temp_U1H + 1),
; то подготовка к выбору следующей константы.
call CONST ; Выбор константы для регистра Temp_U1H.
;----> Возврат по стеку из ПП CONST.
addwf Temp_U1H,F ; Temp_U1H - CONST = ... (вычитание делителя
; из делимого в приложении к Temp_U1H).
incf IndF,F ; Подсчёт кол-ва вычитаний
; (текущий LED + 1 = ... Результат -> в LED).
btfsc Status,C ; Перенос был или нет (делимое больше или
; меньше делителя)?
goto NEXT ; Если был (делимое больше делителя), то
; продолжаем вычитать.
;================================================================================
; Работа с остатком от деления.
;================================================================================
bsf Count,1 ; Если не был, то подготовка к выбору
movf Count,W ; константы (с адресом PC+02h) для
; формирования истинного значения остатка.
call CONST ; Выбор константы для регистра Temp_U1L.
;----> Возврат по стеку из ПП CONST.
;-----------------------------------------
; Формирование истинного значения остатка.
;-----------------------------------------
addwf Temp_U1L,F ; Операция суммирования содержимого
; W и Temp_U1L.
btfsc Status,C ; Перенос был или нет?
incf Temp_U1H,F ; Если был, то Temp_U1H + 1 = ...
; (перенос в Temp_U1H)
; Результат -> в Temp_U1H.
incf Count,W ; Если не было (а также после Temp_U1H + 1),
; то подготовка к выбору константы.
call CONST ; Выбор константы для регистра Temp_U1H.
;----> Возврат по стеку из ПП CONST.
addwf Temp_U1H,F ; Операция суммирования содержимого
; W и Temp_U1H.
decf IndF,F ; Устранение "перебора" в текущем LED.
;--------------------------------------------------------------------------------
; Работа с текущей четверкой констант закончена
; (содержимое текущего LED сформировано).
;================================================================================
; Переход на следующий LED и следующий, "привязанный" к нему, делитель.
;================================================================================
decf FSR,F ; Загрузка в FSR адреса следующего
; регистра LED.
incf Count,F ; Настройка для выбора необходимой константы
incf Count,F ; (для перехода на следующий делитель):
; Count + 2 = ... Результат -> в Count.
movf Count,W ; Count -> W.
sublw .7 ; Проверка: все константы (делители)
btfsc Status,C ; перебрали или нет?
goto NEXT ; Если не все, то начинается работа со
; следующим LEDом и с "привязанной" к нему
; группой констант.
; Если все, то работаем с остатком от
23
; комплексного деления.
;================================================================================
; Работа с остатком от комплексного деления (окончание процедуры).
;================================================================================
movf Temp_U1L,W ; Загружаем остаток в регистр
movwf LED0 ; десятичного разряда единиц.
bcf Flag,6 ; Разрешение изменения результата измерения.
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
; Преобразование кода (+30h) и гашение незначащего нуля в старшем (левом)
; десятичном разряде.
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Применена ПП 2/10 преобразования Сергея Рослика (см. подраздел 5/11/1 первой


части "Практикума…"), как наиболее "скоростная".
В данном случае, это довольно-таки существенно.
И я абсолютно ничем не погрешил против истины (ПП прекрасно работает. Нет
вопросов), а заодно и отдал дань уважения Сергею.
Только я переделал ПП Сергея, с преобразования 2х4, на преобразование 2х3, так как
самый старший, десятичный разряд просто не нужен.
Эта переделка очень проста:
- константа .11 заменяется на константу .7,
- из таблицы вычисляемого перехода ПП CONST, убираются первые четыре константы.
Естественно, что после такой переделки, скорость отработки ПП вполне прилично
увеличилась (по сравнению с тем, что было). Ну и ладушки.
Красным цветом выделены регистры (Temp_U1H/Temp_U1L), в которых изначально
"заложен" результат измерения напряжения, а по ходу исполнения ПП, они
используются как регистры хранения "промежуточных" результатов обработки чисел.
Еще раз напоминаю, что работа ПП Сергея Рослика детально "расписана" в
подразделе 5/11/1 первой части "Практикума…"
Кому не понятно à милости просим туда.

Особенность: в начале ПП 2/10 преобразования, флаг запрета изменения результата


измерения поднимается
bsf Flag,6 ; Запрет изменения результата измерения.
....................................
....................................
а в конце ПП 2/10 преобразования, он опускается.
bcf Flag,6 ; Разрешение изменения результата измерения.

То есть, цикл отработки ПП 2/10 преобразования, как бы, "помечается флажками" (см.
охоту на волков. Вспоминается Высоцкий. Вечная ему память).
Вопрос: "Зачем это нужно"?
Ответ: представьте себе, что за время отработки ПП 2/10 преобразования, произошел
один или более уходов в прерывание.
Это означает то, что в текущий результат вычислений, будет внесена
совершенно не нужная, "паразитная" поправка, которая благополучно исказит
конечный результат вычислений, ведь регистры Temp_U1H/Temp_U1L используются и
как регистры хранения "промежуточных" результатов обработки чисел.
Проще говоря, числовые значения результатов измерений, выводимые на индикацию,
будут "скакать/гулять/прыгать" даже при абсолютно стабильном уровне напряжения,
которое измеряется.
Примечание: в случае применения стандартной ПП 2/10 преобразования, имеет место
быть точно такая же "бяка" ("хрен редьки не слаще").
И это совсем не "голое" утверждение. Я на этом "фингал заработал"/"раздраконился".
Нельзя сказать, что он совсем уж "синюшный" ("сотрясения мозга" нет), но приятности,
как сами понимаете, вообще никакой. Только злоба и желание отомстить.
Именно для того, чтобы прекратить это "безобразие", в начале ПП 2/10
преобразования, флаг запрета изменения результата измерения поднимается, а в конце
ПП 2/10 преобразования, он опускается.

24
Уже из названия флага понятно, что поднятый флаг запрета изменения результата
измерения, запрещает измерение в течение всего интервала времени исполнения ПП
2/10 преобразования.
Проще говоря, процедура измерения, в части касающейся данных, выводимых на
индикацию в ЖК-модуль (подчеркнуто не просто так), обходится (не исполняется).
При этом, пара регистров Temp_U1H/Temp_U1L временно "выходит из под
контроля" процедуры измерения, и те "промежуточные" результаты
обработки чисел, которые имеют в них место быть (в ходе исполнения ПП
2/10 преобразования), никоим образом не будут "паразитно корректироваться".
Что и нужно.
После этого, в части касающейся отображения результатов измерений, - полнейшая
"красота и ляпота".
Даже диву даешься (напоминаю про "шахматную мысль").
Та неравномерность смен показаний, которая при этом возникает, визуально просто не
ощущается. "Слишком короток миг и слишком инерционен глаз".
Но далее, возникает ехидный вопрос (напоминаю про "голос из-за кулис"): "Если
процедура измерения обходится в течение всего времени отработки ПП 2/10
преобразования (а по времени, это совсем не слабо), то сие соответствует
значительному увеличению инерционности отклика на "бяку" (наличие "мертвых зон").
За что кровь пролита? Как изворачиваться будешь? Если не извернешься, то пощады
не жди"?
Ответ: изворачиваться буду молча, спокойно и коварно. Я тоже ехидный и кусаться
умею.
Продолжение (детализация) следует при "разборках" с ПП прерывания ("укушу", но
позднее).
А пока, "поковылял" дальше.
В ПП 2/10 преобразования "врезана" группа команд с названием Блокировка нулями
результата измерения с числовым значением от .1000 и более.
То есть, если имеют место быть результаты измерения с числовым значением более .
999, то на индикацию будут выведены нулевые показания, с погашенным, незначащим
нулем.
Кто не в курсе этой "механики", тех отсылаю в раздел, посвященный АЦП.
Предположим, что ПП 2/10 преобразования благополучно отработана.
Результат "осел" в LEDах.
Далее, этот результат нужно "подогнать" под адресацию нулевой страницы
знакогенератора (перекодировать) и погасить незначащие нули.
В ранее рассмотренных случаях, речь шла о двух отдельных процедурах.
А так как жизнь нужно разнообразить, то на этот раз, я их совместил:

;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
; Преобразование кода (+30h) и гашение незначащего нуля в старшем (левом)
; десятичном разряде.
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
movlw 30h ; -----"-----
addwf LED0,F ; LED0 + 30h = ...

movlw 30h ; -----"-----


addwf LED1,F ; LED1 + 30h = ...

movf LED2,W ; LED2 -> W.


btfss Status,Z ; В LED2 ноль или другое число ?
goto $+4 ; Если не 0, то обход следующих 3-х команд.
movlw 20h ; Если 0, то в LED2 записывается код
movwf LED2 ; символа "пробел" (20h).
return ; Возврат по стеку.

movlw 30h ; -----"-----


addwf LED2,F ; LED2 + 30h = ...
return ; Возврат по стеку.
По-моему, получилось симпатично.

25
Незначащий ноль гасится только в самом старшем, десятичном разряде, так как при
наличии десятичной точки, гашение незначащего нуля в среднем, десятичном разряде
как-то "рогато" смотрится.
Но можно и погасить. Особенно в том случае, когда десятичной точки нет.
Нет проблем. Дело техники.
Итак, результат измерения напряжения подготовлен к выводу на индикацию в ЖК-
модуль.
А как быть с подготовкой к выводу на индикацию результата измерения тока?
Да точно так же, как и с подготовкой к выводу на индикацию результата измерения
напряжения.
Только исходные данные другие и используются регистры с другими названиями (см.
текст ПП BIN2_10_I.
Предположим, что результаты измерения напряжения и тока подготовлены к выводу на
индикацию в ЖК-модуль.
Вполне разумно предположить, что в дальнейшем, речь пойдет о выводе на
индикацию, в ЖК-модуль, всего того, что "добыто тяжким и непосильным трудом":
....................................
....................................
;----> Возврат по стеку из ПП BIN2_10_I.
;********************************************************************************
; Вывод результатов измерения в 1-ю строку.
;********************************************************************************
; Переход в 12-е слева знакоместо 1-й строки.
;--------------------------------------------
movlw b'10001011' ; Выбор ячейки DD RAM с адресом 0Bh, что
movwf PortB ; соответствует установке курсора в 12-е слева
; знакоместо 1-й строки.
call ENTER_BF ; "Плавающая" задержка со стробом
; под команду 10001011.
;----> Возврат по стеку из ПП ENTER_BF.
;================================================================================
; Формирование и вывод на индикацию символов результата измерения напряжения.
;================================================================================
movf LED2,W ; Вывод на индикацию
movwf PortB ; содержимого LED2.
bsf PortC,RS ; Установка на линии RS "1"
; (режим записи данных).
call ENTER_BF ; "Плавающая" задержка со стробом под вывод
; данных на индикацию.
;----> Возврат по стеку из ПП ENTER_BF.

movf LED1,W ;
movwf PortB ; Аналогично,
bsf PortC,RS ; но для LED1.
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.

movlw 2Eh ;
movwf PortB ; Аналогично, но для
bsf PortC,RS ; символа "точка" (2Eh).
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.

movf LED0,W ;
movwf PortB ; Аналогично,
bsf PortC,RS ; но для LED0.
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.
;********************************************************************************
; Вывод результатов измерения во 2-ю строку.
;********************************************************************************
; Переход в 11-е слева знакоместо 2-й строки.
;--------------------------------------------
movlw b'11001010' ; Аналогично, только для адреса 0Ah, что
26
movwf PortB ; соответствует установке курсора в 11-е слева
; знакоместо 2-й строки.
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.
;================================================================================
; Формирование и вывод на индикацию символов результата измерения тока.
;================================================================================
movf LED5,W ;
movwf PortB ; Аналогично,
bsf PortC,RS ; но для LED5.
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.

movf LED4,W ;
movwf PortB ; Аналогично,
bsf PortC,RS ; но для LED4.
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.

movlw 2Eh ;
movwf PortB ; Аналогично, но для
bsf PortC,RS ; символа "точка" (2Eh).
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.

movf LED3,W ;
movwf PortB ; Аналогично,
bsf PortC,RS ; но для LED3.
call ENTER_BF ;
;----> Возврат по стеку из ПП ENTER_BF.
;================================================================================
; Задержка, задающая скорость смены показаний.
;================================================================================
movlw .250 ;
movwf Reg_2 ;
movlw .250 ;
movwf Reg_3 ;

decfsz Reg_2,F ; Стандартный,


goto $-1 ; 2-байтный,
decfsz Reg_3,F ; вычитающий
goto $-3 ; счетчик.
;================================================================================
goto CYCLE ; Переход на следующий цикл программы.
;********************************************************************************

Данные выводятся на индикацию на "линейном" участке программы ("прыжки"


отсутствуют).
В данном случае (малое количество LEDов), с косвенной адресацией связываться не
стоит. Себе дороже выйдет.
"Механика" работы процедуры вывода данных на индикацию в ЖК-модуль, была
объяснена-переобъяснена в "стародавние времена".
Поэтому, "занудством" заниматься не буду.
Что касается фиксированной задержки, то она - "технологическая".
Не смотря на то, что скорость исполнения полного цикла "основного тела" программы
замедлилась (за счет уходов в прерывания), но он все-равно отрабатывается быстро.
Проще говоря, смена показаний будет происходить с такой высокой скоростью, что
"цифирь не успевает как следует высветиться" (напоминаю про большую инерционность
жидких кристаллов и связанную с этим "блёклость").
Вот Вам и необходимость применения дополнительной, фиксированной задержки, что и
имеет место быть.
Она замедляет скорость смены показаний.

27
После отработки фиксированной задержки, происходит переход на следующий "виток"
полного цикла "основного тела" программы, и описанное выше повторяется.
Итак, "прогулка" по "основному телу" программы закончена.
Теперь нужно как следует разобраться с таким "важняком", как ПП прерывания:

;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
; ПОДПРОГРАММА ПРЕРЫВАНИЯ. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
; Процедура сохранения содержимого регистров Status и W.
;================================================================================
; Определение банка, из которого осуществлен уход в прерывание.
;--------------------------------------------------------------------------------
btfsc Status,RP0 ; Из какого банка осуществлен уход
; в прерывание ?
goto BANK_1 ; Если это 1-й банк.
; Если это 0-й банк, то программа
; исполняется далее.
;--------------------------------------------------------------------------------
; Процедура сохранения Status и W в регистрах 0-го банка (Stat_Temp, W_Temp).
;--------------------------------------------------------------------------------
movwf W_Temp ; W -> W_Temp.
swapf Status,W ; Смена п/байтов регистра Status.
; Результат -> W.
movwf Stat_Temp ; W -> Stat_Temp.
bcf Flag,7 ; Установка флага признака банка
; (Flag,7=0: 0-й банк).
; Используется при восстановлении.
goto ZAMER ; Переход на начало АЦП.
;--------------------------------------------------------------------------------
; Процедура сохранения Status и W в регистрах 1-го банка (Stat_Temp1, W_Temp1).
;--------------------------------------------------------------------------------
BANK_1 movwf W_Temp1 ; W -> W_Temp1.
swapf Status,W ; Смена п/байтов регистра Status.
; Результат -> W.
movwf Stat_Temp1 ; W -> Stat_Temp1.
bcf Status,RP0 ; Установка 0-го банка.
bsf Flag,7 ; Сброс флага признака банка
; (Flag,7=1: 1-й банк).
; Используется при восстановлении.
;********************************************************************************
; 1-е АЦП (напряжение). *********************************************************
;********************************************************************************
; Работа с регистром Adcon0.
;--------------------------------------------------------------------------------
ZAMER movlw b'01000001' ; Включение модуля АЦП, выбор канала AN0
movwf Adcon0 ; (RA0), источник тактового сигнала Fosc/8,
; состояние ожидания, конденсатор подключен к
; выбранному аналоговому входу и начал
; перезаряжаться.
call PAUSE ; Задержка для перезаряда конденсатора.
;----> Возврат по стеку из ПП PAUSE.
;--------------------------------------------------------------------------------
; Начало аналого-цифрового преобразования.
;--------------------------------------------------------------------------------
bsf Adcon0,GO ; Включение преобразования. Конденсатор
; отключается от аналогового входа на время
; преобразования.
;--------------------------------------------------------------------------------
; Ожидание окончания АЦП ("плавающая" задержка).
;--------------------------------------------------------------------------------
btfsc Adcon0,GO ; Ожидание окончания аналого-цифрового
goto $-1 ; преобразования.
;------------------------------------------------
; 1-е АЦП закончено. Результат - в AdresH/AdresL.
;================================================================================

28
; Копирование результата либо в Temp_U1H/Temp_U1L, либо в Temp_U2H/Temp_U2L.
;================================================================================
; Проверка состояния флага разрешения/запрета изменения
; результата измерения, который выводится на индикацию.
;--------------------------------------------------------------------------------
btfsc Flag,6 ; Каково состояние флага ?
goto OBH_1 ; Если поднят (1), то изменения
; Temp_U1H/Temp_U1L запрещаются.
; Если опущен (0), то изменения
; Temp_U1H/Temp_U1L разрешаются.
;--------------------------------------------------------------------------------
; Отрабатывается, если изменения Temp_U1H/Temp_U1L разрешаются.
;--------------------------------------------------------------------------------
movf AdresH,W ; AdresH --> W.
movwf Temp_U1H ; W --> Temp_U1H.

bsf Status,RP0 ; Переход в 1-й банк.


movf AdresL,W ; AdresL --> W.
bcf Status,RP0 ; Переход в 0-й банк.
movwf Temp_U1L ; W --> Temp_U1L.
goto ACP_1 ; Обход следующей ПП.
;--------------------------------------------------------------------------------
; Отрабатывается, если изменения Temp_U1H/Temp_U1L запрещаются.
;--------------------------------------------------------------------------------
OBH_1 movf AdresH,W ; AdresH --> W.
movwf Temp_U2H ; W --> Temp_U2H.

bsf Status,RP0 ; Переход в 1-й банк.


movf AdresL,W ; AdresL --> W.
bcf Status,RP0 ; Переход в 0-й банк.
movwf Temp_U2L ; W --> Temp_U2L.

;********************************************************************************
; 2-е АЦП (сила тока). **********************************************************
;********************************************************************************
; Работа с регистром Adcon0.
;--------------------------------------------------------------------------------
ACP_1 bsf Adcon0,3 ; Выбор канала AN1 (RA1). Остальные
; настройки Adcon0 не меняются.
call PAUSE ; Аналогично.
;----> Возврат по стеку из ПП PAUSE.
;--------------------------------------------------------------------------------
; Начало аналого-цифрового преобразования.
;--------------------------------------------------------------------------------
bsf Adcon0,GO ; Аналогично.
;--------------------------------------------------------------------------------
; Ожидание окончания АЦП ("плавающая" задержка).
;--------------------------------------------------------------------------------
btfsc Adcon0,GO ; Аналогично.
goto $-1 ; ----"----
;------------------------------------------------
; 2-е АЦП закончено. Результат - в AdresH/AdresL.
;================================================================================
; Копирование результата либо в Temp_I1H/Temp_I1L, либо в Temp_I2H/Temp_I2L.
;================================================================================
; Проверка состояния флага разрешения/запрета изменения
; результата измерения, который выводится на индикацию.
;--------------------------------------------------------------------------------
btfsc Flag,6 ; Аналогично, только для
goto OBH_2 ; регистров Temp_I1H/Temp_I1L.
;--------------------------------------------------------------------------------
; Отрабатывается, если изменения Temp_I1H/Temp_I1L разрешаются.
;--------------------------------------------------------------------------------
movf AdresH,W ; ----"----
movwf Temp_I1H ; ----"----
bsf Status,RP0 ; Аналогично, только для
29
movf AdresL,W ; регистров Temp_I1H/Temp_I1L.
bcf Status,RP0 ; ----"----
movwf Temp_I1L ; ----"----
goto ACP_2 ; Обход следующей ПП.
;--------------------------------------------------------------------------------
; Отрабатывается, если изменения Temp_I1H/Temp_I1L запрещаются.
;--------------------------------------------------------------------------------
OBH_2 movf AdresH,W ; ----"----
movwf Temp_I2H ; ----"----
bsf Status,RP0 ; Аналогично, только для
movf AdresL,W ; регистров Temp_I2H/Temp_I2L.
bcf Status,RP0 ; ----"----
movwf Temp_I2L ; ----"----
;--------------------------------------------------------------------------------
; Теперь модуль АЦП можно выключить.
;--------------------------------------------------------------------------------
ACP_2 clrf Adcon0 ; Для снижения потребляемого устройством тока,
; модуль АЦП выключается до конца отработки
; текущего полного цикла программы.
;================================================================================
; Процедура восстановления содержимого регистров Status, W и выхода
; из прерывания.
;================================================================================
btfss Flag,7 ; Каково состояние флага признака банка ?
goto VOSST_0 ; Если 0-й банк (Flag,7=0), то работа
; с Stat_Temp и W_Temp.
; Если 1-й банк (Flag,7=1), то работа
; с Stat_Temp1 и W_Temp1.
;--------------------------------------------------------------------------------
; Процедура восстановления Status и W из содержимого регистров 1-го банка
; (Stat_Temp1, W_Temp1).
;--------------------------------------------------------------------------------
bcf PIR1,TMR2IF ; Сброс флага прерывания по переполнению TMR2.

bsf Status,RP0 ; Установка 1-го банка.


swapf Stat_Temp1,W; Обмен п/байтами. Результат -> W.
movwf Status ; W -> Status.
swapf W_Temp1,F ; Обмен п/байтами. Результат -> W_Temp1.
swapf W_Temp1,W ; Обмен п/байтами. Результат -> W.
retfie ; Возврат из прерывания с 1-м банком (с 1-м
; банком вошли в прерывание, с 1-м банком
; и вышли).
;--------------------------------------------------------------------------------
; Процедура восстановления Status и W из содержимого регистров 0-го банка
; (Stat_Temp, W_Temp).
;--------------------------------------------------------------------------------
VOSST_0 swapf Stat_Temp,W ; Обмен п/байтами. Результат -> W.
movwf Status ; W -> Status.
swapf W_Temp,F ; Обмен п/байтами. Результат -> W_Temp.
swapf W_Temp,W ; Обмен п/байтами. Результат -> W.

bcf PIR1,TMR2IF ; Сброс флага прерывания по переполнению TMR2.


retfie ; Возврат из прерывания с 0-м банком (с 0-м
; банком вошли в прерывание, с 0-м банком
; и вышли).
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
....................................
....................................
;================================================================================
; Задержка для перезаряда конденсатора Hold.
;================================================================================
PAUSE movlw .5 ; Стандартный,
movwf Reg_1 ; вычитающий,
decfsz Reg_1,F ; однобайтный
goto $-1 ; счетчик.
return ; Возврат по стеку.
30
;================================================================================
....................................
....................................
В этом "куске" программы, процедура сохранения/восстановления содержимого
регистров W и Status выделена черным цветом.
Алгоритм ее работы был "расписан" в предыдущем подразделе.
Добавлю только то, что при грамотном (с учетом "банковской" специфики применяемого
типа ПИКа) сохранении/восстановлении содержимого регистра W и Status (а также и
содержимого других "жизненно важных" регистров, если это необходимо), никаких
проблем с прерываниями не будет.
И никаких "локальных зон" запрета прерываний - тоже.
Но при условиии, что:
- "периферия" есть "раб", в "правилах игры" которого отсутствуют временные
ограничения, связанные с "принудительным втискиванием" интервала времени
отработки того или иного процесса в "жесткие, временные рамки",
- и при условии, что не производится записи в EEPROM память данных и/или в
FLACH память программ (на счет FLACH: как-нибудь нужно заняться. Зманчивая
"штучка").
В последнем случае, на время исполнения обязательной последовательности команд
записи, прерывания нужно запретить.
При чтении как первого, так и второго, прерывания можно не запрещать.
Вот так, по ходу дела, и обозначаются "важняки".
И это естественно, нормально и справедливо.
Знания и опыт это не "манна небесная".
Посто так, они "с неба не падают".
Просто так, падают только кирпичи, а иногда и радиаторы батарей отопления (в особо
тяжких и бестолковых случаях).
Надеюсь, что в результате осознания этого "словоблудия", будут сделаны
соответствующие выводы.
А если кто не понял, то я не виноват. Но лучше понять.
Это способствует укреплению веры в человечество и спокойному сну.
Перехожу к самому интересному.
В процедурах АЦП ничего нового нет (см. раздел, посвященный 10-разрядному АЦП).
Сначала измеряется величина напряжения, а затем, величина тока.
А теперь обратите внимание на то, что в "границах" каждого такого измерения, имеет
место быть не одна "перегрузка" результата АЦП в пару регистров общего назначения,
а две.
Соответственно, и пар регистров общего назначения не одна, а две.
Например, в случае измерения напряжения, это две пары регистров с названиями
Temp_U1H/Temp_U1L и Temp_U2H/Temp_U2L.
Так как речь идет о "проматери", то я пока не стал "заморачиваться пороговыми
делами", и "проистекающим" из этого, отключением выхода блока питания от его
внешней нагрузки (в случае наличия "бяки").
Совершенно понятно, что подобного рода дела должны "вершиться" сразу же после
того, как будет осуществлен замер.
То есть, в перспективе, сразу же после копирования результатов измерения (из
регистров AdresH/AdresL, в пару регистров общего назначения), должны начаться
"порогово/блокировочно/неблокировочные дела".
Хотя этого, на данном этапе работы, и нет, но это будет. В дальнейшем.
А сейчас просто нужно предположить, что это есть.
Да будет так.
В подобных случаях, пофантазировать не то что можно, но и архинужно.
Итак, в пару регистров Temp_U1H/Temp_U1L, копируется результат АЦП,
предназначенный для вывода на индикацию в ЖК-модуль.
С этим, я надеюсь, все понятно.
Если это копирование происходит (процедура отрабатывается), то автоматически
"делаются и пороговые дела".
И чуть что (в смысле "бяки"), так "шлагбаум быстренько закрывается".
А если прерывание пришлось на ПП 2/10 преобразования? Что будет?
31
Будет "мертвая зона", в которой не делаются не только АЦПэшные дела, но и
пороговые тоже (процедура копирования AdresH/AdresL в Temp_U1H/Temp_U1L
обходится/не исполняется).
Но ведь это же форменное безобразие! За что кровь пролита?!
Вопрос: "Как быть"?
Ответ: если процедура копирования AdresH/AdresL в Temp_U1H/Temp_U1L не
исполняется, то должна исполниться другая, аналогичная процедура, только в ней
необходимо обеспечить копирование AdresH/AdresL не в Temp_U1H/Temp_U1L, а в
другую пару регистров общего назначения.
В данном случае, это Temp_U2H/Temp_U2L.
Их содержимое никоим образом не будет "паразитно" влиять на отработку ПП 2/10
преобразования, так как в этой ПП, обращений к ним нет.
Вопрос: "А зачем копировать AdresH/AdresL в Temp_U2H/Temp_U2L? Какой смысл"?
Ответ: смысл очень даже глубокий.
А что если, после "свершения этого действа", "штатно" сделать
"порогово/блокировочно/неблокировочные дела"?
Получается, что не смотря на обход процедуры АЦП, которая обеспечивает
копирование AdresH/AdresL в Temp_U1H/Temp_U1L, "мертвых зон" не будет, так как
анализ порогов будет полноценно осуществляться в течение всего времени отработки
ПП 2/10 преобразования.
Но только в другой, "подставной" процедуре.
И без какой бы то ни было "паразитной коррекции" результата измерения,
предназначенного для вывода на индикацию в ЖК-модуль.
Приятный вывод: в данном случае, вне зависимости от чего-либо, "мертвые
зоны" будут отсутствовать ("отряд не заметил потери бойца").
"Кровь пролита не зря". И это есть мой ехидный ответ тому, кто "сидит за кулисами".
В общем виде (да и в другом тоже), все просто.
После окончания процедуры АЦП, производится анализ состояния флага
разрешения/запрета изменения результата измерения, который выводится на индикацию
(btfsc Flag,6).
Если этот флаг опущен (ПП 2/10 не отрабатывается), то исполняется "штатное"
копирование AdresH/AdresL в Temp_U1H/Temp_U1L, а если поднят (отрабатывается ПП
2/10 преобразования), то группа команд "штатного" копирования обходится, а вместо
нее исполняется "подстава" (AdresH/AdresL à Temp_U2H/Temp_U2L).
При этом подразумевается наличие, в конце любой из этих процедур, групп команд,
которые работают с порогами.
Конечным итогом работы любой из этих процедур является либо "неотключение"
внешней нагрузки от выхода блока питания (если заданный порог не привышен), либо
ее отключение (если заданный порог превышен).
Пару слов о ПП PAUSE, которая используется при отработке ПП прерывания.
Применяемый в ней регистр Reg_1, не должен использоваться нигде, кроме ПП
PAUSE.
То есть, он не должен использоваться в "зоне" разрешения прерываний.
Потому, что на выходе из любого прерывания, в нем всегда "лежит" 0, который может
"подложить свинью".
И вообще, ПП PAUSE - принадлежность только ПП прерывания.
Ее не стоит вызывать из "зоны" разрешения прерываний (кому нужны лишние
неприятности?).

Подвожу итог этого этапа работы:


- практически сразу же после начала исполнения программы и до выключения питания
устройства, обеспечены строго периодические (с калиброванным периодом) уходы в
прерывания.
- "мертвые зоны" отсутствуют,
- вывод на индикацию картинки 1-го "кадра" и результатов измерения происходит без
ошибок/сбоев.
Сие есть "суперважняки", уважение к которым нужно обеспечить в первую очередь.
"Промать" создана. А что дальше будет - посмотрим …

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

33

Оценить