Файл: Самоучитель по программированию PIC контроллеров для начинающих (Е.А. Корабельников,2008).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 16.06.2025
Просмотров: 3594
Скачиваний: 3
Остается только "раскидать" их (также с соблюдением порядка старшинства) по младшим полубайтам регистров LED0, LED1, LED2, LED3, LED4, LED5, LED6, LED7, а в их старшие полубайты, "принудительно" записать нули (см. пример двоично-десятичного представления числа 99999999).
Всё. Конец преобразования.
После этого, содержимое регистров LED0...7 можно обрабатывать в ПП динамической индикации.
Не смотря на достаточно сложный алгоритм числовых преобразований, работать с ПП преобразований чисел совсем не сложно.
В"шапке" программы, "прописываются" 2 регистра общего назначения: счетчик числа проходов и регистр оперативной памяти (в нашем случае, Count и Mem).
Вначале ПП преобразований чисел, в счетчик проходов, нужно записать число битов двоичного числа.
Например, если нужно отобразить символы десятичных чисел не в 8-ми, а в 4-х знакоместах (максимальное значение 9999), то речь идет о 2-байтном регистре
(например, TimerM/TimerL).
Таким образом, в счетчик числа проходов, необходимо записать константу 8х2=.16.
Вэтом случае, лишние LEDы (LED4...7) и группы команд, которые работают с их содержимым, нужно убрать.
Должны остаться только регистры LED0, LED1, LED2, LED3 и те группы команд, которые "работают" с их содержимым.
Соответственно, в ПП преобразований, вплоть до ее концовки (до процедуры распределения полубайтов), нужно использовать только регистры LED0 и LED1.
То есть, в этом случае, из текста программы Bin2_10.asm, нужно "изъять" некоторые команды.
Для того чтобы стало понятно, какие именно команды нужно "изымать" или добавлять (в случае работы с двоичными числами, отображаемыми более чем в 4-х байтах), нужно поподробнее разобраться со стратегией работы ПП преобразований чисел (Bin2_10). ПП Bin2_10 начинается с подготовительных операций.
Флаг переноса-заёма С опускается (бит С регистра STATUS сбрасывается в 0), в счетчик проходов Count записывается константа, числовое значение которой равно общему количеству битов 4-байтного регистра TimerL/TimerM/TimerH/TimerHH (.32), и содержимое всех LEDов сбрасывается в 0.
Подготовительные операции закончены. Далее, начинается отработка ПП Loop16.
Вее начале, происходит циклический сдвиг влево, на одну позицию (через бит С регистра STATUS), содержимого 8-разрядного регистра
TimerL/TimerM/TimerH/TimerHH/LED0/LED1/LED2/LED3 (8 команд rlf),
а потом, содержимое счетчика количества проходов (Count) декрементируется (decfsz Count,F, результат декремента сохраняется в нем же).
Так как байт-ориентированная команда decfsz является командой ветвления, то возможны два сценария работы программы.
Предположим, что результат декремента равен 0 ("отмотаны" все 32 "витка").
Вэтом случае, начнется отработка группы команд поразрядного распределения содержимого регистров LED0...3 (обеих полубайтов), по младшим полубайтам регистров LED0...7 (концовка).
Этой группе команд вполне можно было бы "присвоить статус" отдельной подпрограммы, но так как на 1-ю команду этой группы команд, переходов нет, то формально, она входит в состав ПП Loop16.
Если результат декремента содержимого регистра Count не равен 0, то осуществляется безусловный переход в ПП adjDEC.
Всостав ПП adjDEC входят 4 группы команд, в каждой из которых осуществляется условный переход в ПП adjBCD.
Возврат происходит на следующую (из этих четырех) группу команд.
Каждая из этих групп команд работает со "своим" регистром LED (с нулевого по третий), то есть, соблюдается порядок старшинства (в начале ПП adjDEC, происходит "работа" с содержимым регистра LED0, а в конце, с содержимым регистра LED3).
Предположим, что с содержимым регистра LED3 осуществлены необходимые действия и произошел возврат на команду goto Loop16.
250
После исполнения этой команды, начнется "отмотка нового витка" внутреннего цикла ПП Bin2_10 (напоминаю, что таких "витков" 32 штуки).
На каждом таком "витке", происходит сдвиг содержимого 8-байтного регистра
TimerL/TimerM/TimerH/TimerHH/LED0/LED1/LED2/LED3 на одну позицию (байт), по направлению от TimerL к LED3.
То есть, 4-байтный регистр LED0/LED1/LED2/LED3 будет последовательно заполняться результатами числовых преобразований.
ПП adjDEC никаких числовых преобразований не осуществляет.
Она исполняет функцию "администратора", то есть, определяет порядок обработки содержимого регистров LED0...3.
Он следующий: LED0, LED1, LED2, LED3 (учтен порядок старшинства).
Всостав каждой из этих 4-х групп команд, входят две команды процедуры "разорванной", косвенной адресации.
Слово "разорванной" означает то, что в ПП adjDEC, осуществляется только запись, в регистр FSR, адреса регистра, с содержимым которого будут производиться действия, а сами эти действия производятся в другой подпрограмме (в ПП adjBCD).
После исполнения команды movlw LED... (0, 1, 2, 3), в регистр W, записывается адрес текущего LEDа (вспомните про то, о чем говорилось ранее).
После исполнения команды movwf FSR, этот адрес, из регистра W, копируется в регистр FSR Далее, осуществляется условный переход в ПП adjBCD (call adjBCD).
После того, как ПП adjBCD будет отработана, то есть, будет исполнена команда retlw 0, содержимое регистра W сбрасывается в 0 и происходит возврат на команду записи, в регистр W, адреса следующего LEDа.
Проще говоря, начинается обработка содержимого следующего LEDа.
И так будет происходить до тех пор, пока не будет обработано содержимое регистра LED3. После этого, осуществляется безусловный переход в ПП Loop16, то есть, на новый "виток" внутреннего цикла ПП Bin2_10.
А теперь обратим внимание на стек.
Ранее рассматривались случаи, когда в стек "закладывался" только один адрес возврата.
Вданном случае, адрес возврата на 1-ю, после команды call Bin2_10, команду программы, будет "лежать" в стеке вплоть до окончания отработки ПП Bin2_10, но только преимущественно (основную часть времени), не в вершине стека (в 1-й его строке), а во 2-й строке, так как после исполнения команд call adjBCD, вершину стека поочередно будут занимать соответствующие адреса возвратов.
Таким образом, если на момент исполнения текущей команды call, в стеке уже находится адрес (адреса) возврата, "заложенный" в него ранее, то в таблице стека (всего в этой таблице 8 строк), он (они) сместится на одну позицию вниз, а в вершину стека запишется адрес возврата, соответствующий текущей (последней) команде call.
После освобождения вершины стека, все "имеющиеся в наличии", активные адреса возвратов, синхронно сместятся вверх на одну позицию (строку), после чего, в
вершине стека будет "лежать" адрес возврата, ранее "дислоцировавшийся" во 2-й строке.
Итак, после записи, в регистр FSR, адреса текущего LEDа (в ПП adjDEC), происходит условный переход в ПП adjBCD.
ПП adjBCD является как бы "основной кухней" преобразования чисел.
Она обрабатывает результаты групповых, циклических сдвигов, находящиеся в регистрах
LED0...3.
Вначале ПП adjBCD, с помощью команды movlw 3, в регистр W, записывается константа 03h (00000011), которая далее, с помощью команды addwf 0,W, суммируется с содержимым текущего LEDа.
Этой командой завершается, ранее начатая, процедура косвенной адресации.
Обращение к регистру INDF происходит не непосредственно (с "пропиской" его названия), а через его адрес (косвенно).
Вданном случае, происходит обращение к числу 0, то есть, к адресу регистра.
Вобласти оперативной памяти, по адресу 00h, "дислоцируется" регистр INDF. Следовательно, обращение происходит именно к нему.
251
Аесли происходит обращение к регистру INDF (прямое или косвенное, без разницы), то по определению, происходит обращение к содержимому того регистра, адрес которого записан в регистре FSR.
В регистре FSR, "лежит" адрес текущего LEDа, следовательно, произойдет суммирование содержимого текущего LEDа c ранее записанной, в регистр W, константой 03h. Результат суммирования сохранится в регистре W.
Затем, результат суммирования, из регистра W, копируется в регистр оперативной памяти Mem, после чего, состояние бита №3 анализируется (btfsc Mem,3).
Если в этом бите 1, то исполняется команда movwf 0, которая также относится к "разряду хитрых".
В ней также (см. выше) происходит обращение не к названию регистра INDF, а к адресу регистра INDF (00h).
То есть, происходит обращение к содержимому текущего LEDа.
Таким образом, команда movwf 0 копирует содержимое регистра W, в текущий LED.
Атак как в регистре W находится то же самое, что и в регистре Mem, то фактически, происходит копирование содержимого регистра Mem, в текущий LED.
Если бит №3 регистра Mem равен 0, то процедура суммирования повторится снова, но только в регистр W будет записана не константа 03h, а константа 30h (00110000), и будет произведен анализ состояния не бита №3, а бита №7 регистра Mem.
Напоминаю, что если в тексте программы указано число без атрибутов систем исчисления (а в данном случае так оно и есть), то речь идет о 16-ричной системе исчисления, а не о десятичной.
Вместо movlw 30, можно написать movlw .48 или movlw b'00110000'.
Результат будет одним и тем же.
Если бит №7 регистра Mem будет равен 0, то произойдет возврат в ПП adjDEC, после чего произойдет смена текущего LEDа, следующий переход в ПП adjBCD, ее отработка, возврат в ПП adjDEC и т.д.
До тех пор, пока не будет закончена обработка содержимого регистра LED3.
Далее, исполняется команда goto Loop16, после чего начинается "отмотка" следующего витка/кольца ПП Bin2_10.
И так 32 раза.
После того, как содержимое счетчика количества проходов Count станет равным 0, начнется отработка группы команд поразрядного распределения содержимого регистров LED0...3 по младшим полубайтам регистров LED0...7 ("концовочная" процедура).
На момент начала ее отработки, в 8-ми полубайтах регистров LED0...3, в порядке старшинства, будут "лежать" результаты преобразований, в виде восьми 4-битных, двоично-десятичных чисел.
Направление возрастания старшинства: от младшего полубайта регистра LED0, и далее по-порядку.
Распределение начинается со старшего полубайта регистра LED3 (он "уходит" в младший полубайт регистра LED7. В его старший полубайт записывается 0) и далее, по-порядку (в сторону уменьшения разрядности) и аналогично.
Примечание: если начать это распределение с младшего полубайта регистра LED0 ("с другого конца"), то содержимое некоторых полубайтов будет утеряно.
То есть, в данном случае (8 знакомест), в наличии должно быть 8 групп команд распределения.
Причем, при распределении каждого полубайта, должна гарантированно обеспечиваться запись нуля в старший полубайт регистра-получателя (почему? См. выше).
Эти 8 групп команд, по сути, выполняют одну и ту же "работу", но разными способами: - 4 группы команд, работающие с младшими полубайтами регистров LED0...3, - одним
способом,
- и 4 группы команд, работающие со старшими полубайтами регистров LED0...3, - другим. Это обусловлено положением полубайта в байте.
Если полубайт старший, то необходимо задействовать команду смены местами старшего и младшего полубайтов (swapf), а если полубайт младший, то этого не требуется. Рассмотрим работу первых двух групп команд (остальные пары групп команд работают аналогично).
Распределение начинается со старшего полубайта регистра LED3.
252
Спомощью команды swapf LED3,W, старший и младший полубайты регистра LED3 меняются местами. Результат этой операции сохраняется в регистре W.
Спомощью команды andlw 0Fh, выполняется логическая операция побитного И содержимого регистра W и константы 0Fh (.15 или 00001111).
То есть, фактически, выполняется побитное И содержимого регистра LED3 (с учетом смены местами его полубайтов) и константы 0Fh, с сохранением результата этой операции в регистре W.
В результате этого, старший полубайт "встает на место" младшего, и в старшем полубайте устанавливается 0.
По логике операции И, результат побитного И с нулем, всегда есть ноль, а результат побитного И с единицей, повторяет состояние второго "участника" этой логической операции.
Таким образом (movwf LED7), в младший полубайт регистра LED7, записывается старший полубайт регистра LED3, и в старшем полубайте регистра LED7 устанавливается 0. Следующая группа команд работает с младшим полубайтом регистра LED3.
В этом случае, менять местами полубайты регистра LED3 не нужно.
Первая команда (movfw LED3) копирует содержимое регистра LED3 в регистр W. Примечание: movfw - не опечатка. Такой команды в распечатке команд Вы не найдете (есть movwf), но тем не менее, команда movfw работает (можете добавить в список).
Команды movfw (название регистра) и movf (название регистра),W производят одно и то же
действие.
Работа второй команды (andlw 0Fh) описана выше.
Работа третьей команды (movwf LED6) описана выше, разница только в том, что содержимое регистра W копируется не в регистр LED7, а в регистр LED6.
Таким образом (movwf LED6), в младший полубайт регистра LED6, записывается младший полубайт регистра LED3, и в старшем полубайте регистра LED6 устанавливается 0. Остальные 3 пары групп команд распределения полубайтов работают аналогично.
При распределении полубайтов в регистры LED0...3, содержимое их ранее распределенных полубайтов во внимание не берется (они уже распределены), и с ними можно проводить описанные выше операции, не опасаясь "уничтожить нужный" полубайт.
На конечной стадии распределения, младший полубайт регистра LED0 копируется в него же. В чем смысл? Ведь содержимое младшего полубайта регистра LED0, казалось бы, можно вообще "не трогать" (ничего с ним не делать).
Но в этом случае, старший полубайт регистра LED0 не будет установлен в ноль.
Поэтому, последняя группа команд распределения должна быть исполнена в полном объеме. После ее исполнения, рабочая точка программы переходит на команду возврата return (ПП Bin2_10 отработана), и далее, осуществляется возврат на следующую, после команды
call Bin2_10, команду ПП START.
После отработки команд ПП START, начинается исполнение ПП динамической индикации (данные для ее работы подготовлены), и так далее.
До следующего "влёта" рабочей точки программы в ПП Bin2_10.
"На фоне" сказанного, ПП adjBCD является некой "темной лошадкой".
В том смысле, что формально (без "привязки" ко всему остальному), ее работа понятна, но в комплексе со всем остальным, получается довольно-таки объемная и трудно воспринимаемая "круговерть" чисел.
В это можно "въехать", но на данной стадии обучения, какого-то большого, практического смысла в этом нет.
И выбора у меня тоже нет, так как в дальнейшем, без подпрограммы преобразования двоичных чисел в двоично-десятичные, никак не обойтись.
Эта подпрограмма работает четко, и в ее "эксплуатации", никаких сложностей не будет. Нужно только научиться "трансформировать" ее текст под то, что Вам нужно (а вот в этом есть "могучий", практический смысл).
Как это делается?
Пример
"Переоборудуем" ПП Bin2_10 под линейку, состоящую из 4-х знакомест.
Вэтом случае, необходимо произвести следующие действия:
В"шапке" программы
253