Файл: Самоучитель по программированию PIC контроллеров для начинающих (Е.А. Корабельников,2008).pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 16.06.2025

Просмотров: 3595

Скачиваний: 3

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

устанавливается в 0. То есть, переноса нет.

Вывод: если по фактам возникновения переносов, инкрементировать содержимое регистра более старшего разряда, то можно безошибочно суммировать "большие" (условно) числа.

До сих пор, речь шла об операции сложения. Давайте разберемся с операцией вычитания.

Если в проверочной группе команд, Вы замените команду сложения ADDLW, на команду вычитания SUBLW (а также замените команду bcf Status,0 на bsf Status,0. Почему? Поймете позже) и произведёте, аналогичную описанной выше (с учетом того, что результат может быть отрицательным), проверку, то выясните следующее:

1.Если арифметический результат вычитания двух чисел является числом положительным, (находится в числовом диапазоне .00 … .255 ), то флаг C

устанавливается в 1.

2.Если этот результат меньше нуля, то есть, он является отрицательным, то флаг C устанавливается в 0.

Теперь, речь идет о заёме.

В первом случае, заёма нет. Во втором случае, заём есть.

Вот Вам и ответ на вопрос: "Откуда, в комментарии к флагу C, взялось словосочетание

перенос - заем"?

Обязательно следует уточнить, что перенос (при суммировании) или заём (при вычитании) возникает не во всех случаях суммирования или вычитания, а только в тех случаях, когда результат суммирования или вычитания "выходит за рамки" числового диапазона .00 … .255 (с того или иного "края").

То есть, в тех случаях, когда арифметический результат суммирования больше числа

.255 (перенос), и когда арифметический результат вычитания является отрицательным числом (заём).

Во всех остальных случаях, речь идет просто о суммировании и просто о вычитании (без "прибамбасов").

Примеры:

-Если суммируются числа, например, .254 и .03, то фактической суммой будет являться не число .257 (в одном регистре отобразить его невозможно), а число .01, плюс перенос (флаг C установился в 1).

-Если из числа .01 вычитается число .03, то фактическим результатом вычитания не будет являться отрицательное число. Им будет являться число .254, плюс заём (флаг C

установился в 0).

Если Вы сравните эти примеры, то поймете, почему заем имеет инверсное значение (см. комментарии к флагу С).

И в самом деле, при переносе, бит флага С устанавливается в 1, а при заёме, он устанавливается в 0.

То есть, заём, по отношению к переносу, является действием типа "наоборот".

А теперь давайте разберемся с "легким бардачком", возникающим при этом, а заодно,

внесем ясность и в вопрос о том, обязательно ли нужно сбрасывать флаги 1-й группы (флаги Z, C, DC относятся к 1-й группе) или нет, перед выполнением команд, которые на эти флаги воздействуют?

Если с переносом все понятно (бит С установился в 1, то есть, флаг поднялся), то при заёме, флаг С, формально (так как мы ранее "привязались" к тому, что поднятию флага соответствует 1), опустился.

"Виной этому" то, что заем имеет инверсное значение.

В этом случае (при применении команд SUBWF или SUBLW), нужно просто "проинвертировать привязку", то есть, изменить критерий "поднятия": флаг поднят 0, флаг опущен 1.

В соответствии с этим, вносим коррективы в проверочную группу команд (ранее предполагалось, что флаг С устанавливается в 1 и при переносе, и при заёме).

193


Если производится суммирование, то нужно применить команду bcf Status,0, а если вычитание, то нужно применить команду bsf Status,0.

Вэтом случае, на момент исполнения команд ADDLW или SUBLW (с учетом сформулированных выше критериев "поднятия"), флаг С будет гарантированно опущен. Что касается ответа на вопрос, сформулированный выше (он выделен темно-зеленым

цветом), то ответ такой: нет, не обязательно, но для начинающих, желательно (я об этом уже говорил ранее).

Прочитайте определение флагов 1-й группы, которое дано выше. Это и есть первая часть ответа.

И в самом деле, зачем предварительно сбрасывать все те же флаги Z, C, DC, если их состояния полностью определяются результатами исполнения соответствующих команд. Проще говоря, в каком бы из двух состояний не находился, до момента исполнения команды (той, которая на него воздействует), флаг 1-й группы, после ее исполнения, этот флаг будет установлен в то состояние, которое соответствует результату выполненной операции. Таким образом, из группы команд, реализующих цифровой компаратор, и из группы проверочных команд, работающих с флагами C и DC, все команды сброса флагов можно смело убрать.

Вопрос: "Так что же, в конце концов, делать-то, убирать или не убирать"?

Ответ: если информация, изложенная выше, Вам предельно ясна и понятна, и Вы имеете хотя бы небольшой опыт составления и отладки программ, то команды предварительных сбросов флагов можно не применять, а если у Вас с этим проблемы, то отказываться от этих сбросов не стОит.

По причине того, что в последнем случае, процесс отладки программ (в части касающейся отладки подпрограмм, в которых происходят опросы состояний флагов 1-й группы) становится более наглядным.

Пример.

Предварительного сброса флага С нет.

Врезультате исполнения предшествующих команд программы, на момент исполнения команды ADDLW, бит флага С установился в 1.

Допустим, суммируем числа .100 и .200.

Значит, после исполнения команды ADDLW, бит флага С должен установиться в 1. Но в нем и так "выставлена" 1.

Витоге, после исполнения команды ADDLW, состояние бита флага С не изменится

(подтверждение ранее установленного состояния).

Работа программы не нарушится, но в симуляторе, Вы не увидите изменения состояния бита флага С, что для начинающих, которые недостаточно хорошо ориентируются во "флаговых делах", совсем не комфортно.

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

При наличии такого рода затруднений, например, при суммировании, целесообразно предварительно установить бит флага С в ноль (bcf Status,0) так, как это сделано в группе проверочных команд, после чего можно будет с комфортом наблюдать за поднятием флага в окнах RAM или SFR.

Если нужно проконтролировать случаи сбросов флага, то перед исполнением соответствующей команды, можно либо вообще ничего не "врезать" (если "на влёте", флаг поднят), либо (если "на влёте", флаг опущен) предварительно поднять флаг С командой bsf Status,0 (по принципу типа "хуже от этого не будет и изменения увижу").

Подобного рода "манипуляции" есть элемент удобства отслеживания работы программы. Можно даже сказать, что "уловка".

Может быть, опытные программисты и посчитают такое внимание к контрольным (второстепенным) операциям излишним, но я так не считаю.

По той причине, что опытные программисты это знают, а начинающие не знают.

Именно осмысление подобного рода "деталей" и дает то, что называется профессиональной уверенностью.

Для Вашего удобства, сведу результаты в таблицу:

Таблица состояний битов Z и C.

- нулевой результат:

бит Z = 1,

- ненулевой результат: бит Z = 0,

- перенос есть:

бит С = 1,

194


- переноса нет:

бит С = 0,

-заём есть: бит C = 0 (заем имеет инверсное значение).

-заёма нет: бит C = 1 (заем имеет инверсное значение).

Перед тем, как работать с флагом DC, внесу некоторую ясность в вопрос о том, почему арифметические операции сложения и вычитания (в пределах одного байта) выполняются в ПИКах так быстро?

Или этот вопрос можно сформулировать так: "Почему флаги С и DC так быстро "реагируют" на результаты исполнения арифметических операций?"

Все познается в сравнении и поэтому приведу пример "доисторического" 4-хбитного арифметического устройства на двоичном счетчике с предустановкой 555ИЕ7 (полный цикл счета - 16 чисел).

Например, нужно сложить числа .02 и .10.

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

Счет начинается от числа .02 и прекращается на числе .12.

Это - пример арифметического устройства последовательного типа, которое работает "долго и нудно".

И в самом деле, ждать результата 10 тактов - "радость небольшая", а если этот "динозавр", например, многокаскадный и речь идет о больших числах?

Такие вычисления могут понравиться только любителям перекуров.

Для того чтобы произвести эти вычисления всего за время одного такта, в микроконтроллерах используются так называемые 8-разрядные полные сумматоры.

Кто этим интересуется: информации о них полным-полно в технической литературе широкого пользования.

Почему именно "сумматор" - понятно, так как он осуществляет операцию суммирования. А как быть с операцией вычитания, ведь сумматор может только складывать?

Дело в том, что применяя, при выполнении операции вычитания, специальные приемы (преобразования чисел), можно свести операцию вычитания к операции сложения.

В этом случае, операция вычитания является специфической разновидностью операции сложения, и с использованием полного сумматора, можно реализовать обе эти операции, что и имеет место быть.

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

8- разрядный полный, универсальный сумматор работает в стандартном, параллельном, весовом (обычном) коде и имеет 2 группы входов, по 8 разрядов каждая, на которые "подаются" слагаемые числа, 8 выходов, на которые выводится результат суммирования и один выход переноса - заёма.

Для "запуска механизма" суммирования, в этом случае, никаких дополнительных сигналов управления не нужно, так как, образно выражаясь, полный сумматор суммирует, "не спрашивая разрешения".

Какие числа "выставлены" на входах, такие он тут же и суммирует.

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

То есть, арифметические операции можно совершать очень быстро.

Таким образом, состояния бита флага С (DC) есть состояния выхода переноса - заёма такого универсального (задействуется и при сложении, и при вычитании), 8-разрядного полного сумматора.

Программист может только произвести опрос (программными средствами) состояний выхода переноса-заёма такого полного сумматора (что и есть опрос состояний флагов С/DC) и при необходимости, по результатам этого опроса, направить рабочую точку программы в один из двух сценариев.

Разрядность полного сумматора программист нарастить не может. Он производит операции только в пределах одного байта.

После завершения операции, байт результата должен быть своевременно скопирован в регистр общего назначения, а иначе, в результате исполнения следующей операции, он будет "потерян" ("за что кровь пролита"?).

Продолжу тему флагов.

195


"Пытаю" флаг DC.

"Орудия пытки" такие же, как и при "пытке" флага С, только контролировать нужно не нулевой бит регистра STATUS, а первый его бит.

Втексте программы Multi, замените проверочную группу команд для флага С на проверочную группу команд для флага DC.

Специфика работы с флагом DC такова, что лучше сначала прочитать пояснения, а потом начать с ним работать.

Байт состоит из 2-х полубайтов по 4 бита в каждом. Они разделяются на младший и старший полубайты.

Младший полубайт - тот, в состав которого входит нулевой бит байта.

Так как в каждом полубайте по 4 бита, то младший полубайт способен отобразить 16 чисел

(от .00 до .15).

Можно провести аналогию между байтом, в случае работы с флагом С, и младшим полубайтом, в случае работы с флагом DC.

Всё один к одному. Разница только в том, что в первом случае, переносы или заёмы происходят в случаях "выхода" результата арифметической операции, за пределы числового диапазона байта (от .00 до .255), а во втором случае, они происходят в случаях "выхода" результата арифметической операции, за пределы числового диапазона полубайта

(от .00 до .15).

При работе с флагом DC, перенос осуществляется в младший разряд старшего полубайта, а не в дополнительно задействованный регистр общего назначения, как в случае наращивания разрядности при работе с флагом С.

Если флаг DC работает с младшим полубайтом, то можно сделать следующий вывод:

флаги С и DC работают в комплексе.

Давайте посмотрим, как это будет выглядеть практически. "Привяжемся" к операции суммирования.

Давайте что-нибудь посчитаем с использованием все той же группы проверочных команд для случая суммирования (см. выше).

Можете убрать из нее команду bcf Status,0 по той причине, что при работе этой группы команд в составе программы Multi.asm, перед исполнением команды addlw Y, биты С и DC сброшены (флаги С и DC опущены) и эта команда (bcf Status,0) просто не нужна (это как раз то, о чем говорилось выше).

Оставшиеся 2 команды "вставьте" в текст программы Multi.asm на свое "проверочное место" (так, как это делалось ранее).

На место условных чисел X и Y, поставьте нули (начало анализа).

По ходу работы, можно производить и одновременную смену констант, но для того чтобы не запутаться, изначально, выгоднее зафиксировать одну из этих констант зафиксировать (.00). Например, X, а константу Y постепенно увеличивать, начиная с числа .00.

Основное внимание нужно направить на нулевой и первый биты регистра STATUS. Откройте окна RAM или SFR. Кому какое удобнее. А можно и оба.

Откройте также окно Watch, "заложив" в него регистр W (в нем Вы будете видеть результат суммирования).

Не забывайте, после каждой смены значения константы, производить ассемблирование. Напоминаю: используемая нами команда ADDLW воздействует на флаги Z, C, DC (см. распечатку команд).

"Встаньте" на команду movlw .00.

Это означает, что последней была выполнена команда bcf Status,5. Посмотрите на содержимое регистра STATUS.

Внем - число 18h (00011000).

Разбираемся: биты флагов -PD (3-й бит) и -TO (4-й бит) установлены в 1.

Причина: после ассемблирования (что эквивалентно включению питания), ПИК сбрасывает сторожевой таймер WDT, то есть, автоматически исполняется команда CLRWDT (см. распечатку команд).

Почему при сбросе WDT 3-й и 4-й биты устанавливаются в 1, а не в 0?

Обратите внимание на дефис перед названием этих флагов: он означает инверсию. Таким образом, флаги -PD и -TO опущены (установлены в 1).

Далее, на эти флаги можно не обращать внимания. Флаги С и DC также опущены (установлены в 0).

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

196


addlw .00.

После этого, содержимое регистра STATUS изменится с 18h на 1Ch (00011100). Все правильно, 0+0=0. Флаг нулевого результата Z (2-й бит) поднялся.

Вкоманде addlw, меняем .00 на .01. Ассемблируем.

Сбрасываем программу на начало.

Впошаговом режиме доходим до команды addlw .01 и исполняем ее. Смотрим на содержимое регистра STATUS.

Внем опять число 18h (00011000).

???

Вывод: так как результат суммирования не равен 0, флаг нулевого результата Z сбросился. Переносов нет, так как значение суммы не выходит "за границы (см. выше) рабочих зон". Точно такая же "картина" будет наблюдаться, если Вы будете, в качестве числа Y, использовать любое число до .15 включительно.

При этом, значение числа суммы не превысит .15 и переполнения младшего полубайта не будет.

Суммируем .00 и .16. Сумма равна .16.

Это число большее, чем способен отобразить младший полубайт и казалось бы, флаг DC должен подняться).

Смотрим на содержимое регистра STATUS.

Внем 18h (00011000). Почему флаг DC не поднялся?

Посмотрите на младший полубайт числа .16.

Он равен .00 (0000), а 0+0=0.

То есть, "выхода за границы" младшего полубайта нет, и флаг DC, по определению, подняться не должен.

Возникает вопрос: "Почему, когда мы раньше суммировали нули, флаг нулевого результата Z поднялся, а теперь он не поднялся"?

Ответ: флаг Z работает с байтом, а .00+.16=.16.

То есть, результат не является нулевым (в отличие от .00+.00=.00).

Точно так же флаг DC (а также флаги C и Z) не поднимется и в тех случаях, если Вы будете, вместо числа Y, "подставлять" любые другие числа, большие чем .16 (вплоть до .255). Можете поменять местами числа X и Y, результат будет тот же самый.

Учитывая то, что флаг DC не поднимался и тогда, когда вместо числа Y "подставлялись" числа от .00 до .15, можно утверждать следующее: если одно из слагаемых (или оба)

является нулем, то при любом числовом значении второго слагаемого, флаги C и DC никогда не поднимутся, а флаг Z поднимется только в случае .00+.00=.00.

На основании этого утверждения, можно сделать следующий практический вывод: если одно

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

В сущности, это объясняется просто: в этом случае (одно слагаемое равно 0), из-за того что другое слагаемое, по определению, не может быть больше числа .255 (для байта) и .15 (для полубайта), выхода, отображаемых байтом и полубайтом, чисел, за границы соответствующих "зон", не происходит.

Следовательно, не происходит и переносов. Поэтому флаги C и DC будут опущены. А теперь замените .00 на .01, а вместо .16 поставьте .15.

Сумма - такая же, как и в случае .00+.16=.16. Посмотрим, что изменится.

После выполнения команды addlw, в регистре STATUS зафиксируется число 1Ah (00011010). Теперь флаг DC поднялся.

Он также поднимется и в случаях 2+14, 3+13, 4+12 и т.д. до 15+1 включительно, а также и при других комбинациях чисел, если ни одно из них не является нулем и их сумма больше 15-ти.

Аесли меньше 15-ти (в том числе и в случаях "применения" нулей), то флаг DC будет сброшен.

Атеперь посмотрим, что произойдет, если слагаемые "выходят" из числового диапазона полубайта (.00 .15) в других случаях?

Например, просуммируем числа .08 (00001000) и .150 (10010110). Флаг DC сбросился.

Почему?

Еще раз напоминаю, что флаг DC работает только в пределах младшего полубайта.

197