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

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

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

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

Добавлен: 16.06.2025

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

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

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

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

Принцип замены команды CALL на GOTO состоит в том, что, кроме команды GOTO, по которой происходит переход, необходима еще одна команда GOTO, которая организует возврат.

Вслучае организации условного перехода (CALL), возврат происходит на команду, которая не помечена меткой.

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

Вобщем виде, это выглядит так:

----------

goto

Cycle

Metka

xxxx

xxxx

-----

-----

-----

-----

Cycle

yyyy

yyyy

-----

-----

goto

Metka

----------

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

По команде безусловного перехода GOTO, сначала осуществляется переход в подпрограмму

Cycle.

Следующая, после GOTO, команда, обозначенная xxxx, для обеспечения дальнейшего

возврата, помечается меткой Metka.

Происходит переход в подпрограмму Cycle, и она, начиная с первой ее команды, обозначенной yyyy, исполняется до своей последней команды, в качестве которой используется все та же команда GOTO (goto Metka), но на этот раз, она обеспечивает возврат.

После этого, происходит возврат на команду, выделенную меткой Metka, и программа исполняется далее.

Между Metka и Cycle может быть, например, 10, 50, 100 или другое количество команд, в которых также могут быть организованы переходы и возвраты.

Таким образом можно организовать переходы и возвраты без использования команды CALL, то есть без работы со стеком.

Результат - один и тот же.

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

Для любознательных: описанная выше процедура, для случая условного перехода, выглядит так:

Первую сверху команду GOTO нужно заменить на команду CALL. Метка Metka не нужна.

Нижняя команда GOTO заменяется на специальную (для работы со стеком) команду возврата RETURN (точку возврата указывать не нужно, так как ее адрес "лежит" в вершине стека).

Команда RETLW также работает со стеком.

От команды RETURN она отличается только тем, что, при возврате, осуществляется действие: в регистр W загружается указанная константа.

Команда RETURN никаких действий, кроме возврата по стеку, не производит.

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

Команда CLRWDT используется для периодического, программного сброса сторожевого таймера WDT, если он включен.

В отличие от команды MOVWF, в которой все ясно и понятно (копирование байта из аккумулятора в указанный регистр), команда MOVF может вызвать у начинающих некоторые затруднения.

31


Команда MOVF копирует содержимое указанного регистра либо в регистр W (аккумулятор), либо в тот же, указанный регистр.

В основном, сохранение происходит в регистре W (команда MOVF копирует, в регистр W, содержимое указанного в команде регистра.

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

Если содержимое регистра равно 0, то флаг нулевого результата Z поднимется (установится в 1), а если нет - опустится (установится в 0).

"Манипуляции" с флагом Z будут рассмотрены позднее.

Чаще всего команда MOVF применяется для считывания данных, с выводов портов (регистры PORTA и PORTB это регистры специального назначения), в регистр W.

Выглядит это так: movf PortA,W или movf PortB,W.

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

Их (данные) нужно сначала скопировать, из регистра – адресата, в буферную память (в регистр W), а затем скопировать данные из нее, в регистр - получатель.

Для случая копирования данных из регистра PortB в регистр общего назначения XYZ, это выглядит так:

movf PortB,W movwf XYZ

Буквально: скопировать содержимое регистра PortB в аккумулятор, а затем скопировать содержимое аккумулятора в регистр XYZ.

Или проще: переслать содержимое регистра PortB в регистр XYZ. Примечание: в приведенных выше и ниже примерах использования команд, я не придерживаюсь правил, используемых при написании программ.

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

Стандартные логические операции с корнями AND ("И"), IOR ("ИЛИ"), XOR ("исключающее ИЛИ"), COM (инверсия) в особых пояснениях не нуждаются (это из основ цифровой техники). При выполнении команд циклического сдвига RLF или RRF, происходит циклический сдвиг влево или вправо содержимого регистра, к которому обращается команда сдвига. Циклический сдвиг происходит через флаг переноса - заёма (нулевой бит регистра STATUS), с названием С.

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

При организации этих процедур, используется также и команда SWAPF.

Начинающим советую пока, на время, "отложить последние 3 команды в сторону" и особо на них не "зацикливаться".

"Разборки" с ними будут позднее.

Команда NOP (нет операции) на первый взгляд может показаться не слишком значимой, но это не так.

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

В этих случаях, используется команда NOP.

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

Примечание: я постарался объяснить Вам только то, что необходимо для "старта". Детальные "разборки" с командами последуют далее, когда начнется "живая" работа с текстами программ и когда будут "подворачиваться" удобные поводы.

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

32


какое-то "недоношенное", и которое, по своей эффективности, "в подметки не годится" обучению на "живых" примерах.

Дополнительно.

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

Потом, когда поднаберетесь опыта, можно разобраться с ней более детально.

Об ошибках, допущенных в "фирменном" описании системы команд среднего семейства м/контроллеров PICmicro.

("Справочник по среднему семейству м/контроллеров PICmicro", перевод технической документации DS33023A компании Microchip Technology Incorporated, ООО "Микро-Чип",2002)

Пояснения:

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

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

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

Вот уж действительно внимательный и дотошный человек. Побольше бы таких.

Я наконец-то удосужился "въехать" в эти примеры, и у меня "волосы встали дыбом": никак не ожидал от профессионалов такого подвоха.

Вот уж воистину "доверяй, но проверяй".

Ниже, я постараюсь объяснить, в чем тут дело и привести таблицу команд в "божеский вид". Исходные данные:

"Кривая", "фирменная" таблица команд (та, что раньше "лежала" в этом разделе),

находится в "Приложении №10".

Вопросы Дмитрия Дубровенко выделены синим цветом.

1. По команде ADDWF, использующейся для вычисляемых переходов: "В примере

указано ADDWF PCL,0 , то есть, результат должен записываться в аккумулятор, а не в PCL. Если это какая-то особенность, у Вас нигде не сказано про это (что надо ставить 0, а не 1)".

Ну конечно же, нужно писать PCL,1 , а не PCL,0, а иначе приращения счетчика команд PC (вычисляемого перехода) не произойдет.

Ошибка в "чистом виде", причем, повторяющаяся в двух примерах для команды ADDWF. Почему в моих дальнейших объяснениях (в последующих разделах) ADDWF PC,1 , а не

ADDWF PCL,1?

А это зависит от того, под каким именем "прописан" регистр счетчика команд в "шапке" программы: можно "прописать" и так: PC, тогда ADDWF PC,1, а можно и по-другому: PCL,

тогда ADDWF PCL,1). "Это на любителя".

В примерах таблицы для команды ADDWF, название PCL указано потому, что в комментариях (в последнем примере) указан PCH (для обозначения "составных частей" PC и порядка их "взаимодействия", а точнее, если речь идет о вычисляемом переходе, "невзаимодействия", так как при переполнении PCL, приращения PCH не происходит).

33


2.По командам RLF и RRF (примеры 2 и 3): "Написано RLF INDF,1 (RRF INDF,1).

Значение W, до выполнения - неопределенное.

Почему, после выполнения команды, оно становится 17h?"?

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

Обращение команды к содержимому регистра INDF, если результат сохраняется в нем же (INDF,1), также не влияет на содержимое регистра W.

Регистр W нужно вообще убрать из комментариев 2-го и 3-го примеров для команд RLF и RRF, что я и сделал.

Как хххххххх "превратилось" в 17h?

"Тайна сия велика есть". Если кто-то знает, то поделитесь разгадкой.

3.По команде ADDLW и другим командам, где упоминается HIGH(LU_TAB):

"До выполнения: W=0x10, LU_TAB=0x9375 (адрес в памяти программ). После выполнения: W=0xA3.

Откуда берется этот адрес (он ведь больше максимального количества команд), и почему операции производятся со старшим байтом?"

Несоответствие (ошибка) обнаружено совершенно верно: для наибольшего "объема" памяти программ ПИКов, величиной в 8 килослов, максимальная величина адреса памяти программ составляет 1FFFh.

9375h больше 1FFFh, и это число (9375h) использовать нельзя (в памяти программ ПИКов нет таких адресов).

Если предположить, что речь идет не о адресе, а о содержимом 2-хбайтного регистра (9375h = 10010011 01110101), то, опять же, имеет место быть ошибка по той простой причине, что комментарий "адрес в памяти программ" не верен.

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

Ничего вредоносней таких "недоношенных" примеров и представить себе трудно, и поэтому я заменил их на примеры работы с заранее "прописанной", в "шапке" программы, константой CONST (этому названию или другому названию, директивой EQU, можно присвоить любое числовое значение в диапазоне 0 … 255 ).

Пояснения

По командам управления:

"11 бит адреса загружаются из кода команды в счетчик команд PC(10:0).

2 старших бита загружаются в счетчик команд PC(12:11) из регистра PCLATH. Вот это совсем не понял."

В "Справочнике по среднему семейству м/контроллеров PICmicro" (можно скачать на сайте Микрочипа), на странице 6-5, найдите рисунки 6-2б, в.

При переходах, это именно та специфика адресации, которая имеет место быть, и ее просто нужно принять как данность.

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

Это аппаратная "епархия" ПИКа.

"Он там все сам разрулит" (при исполнении команд переходов/возвратов).

"Возможно ли использовать аккумулятор в качестве аргумента именно в командах типа ADDWF (т.е. ADDWF W,d)? Насколько я Вас понял, нельзя?"

Никакого практического смысла в этом действии нет (разве только задержка, но для этого есть NOPы): обращение командой ADDWF, к содержимому W, не вызовет никаких изменений его содержимого (при любом значении d): что в нем "лежало до того", то и будет "лежать после того" (можете проверить в симуляторе).

"Какие еще регистры, кроме W, не надо прописывать в шапке?"

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

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

34


Регистру же W, адрес ставится в соответствие автоматически (он имеет строго фиксированный адрес).

Другое дело, в каком виде к нему обращаться в "рабочей" части программы: то ли нулем (d=0), то ли при помощи его общепринятого названия (W).

Если нулем, то его можно не "прописывать". Если W, то лучше, на всякий случай, "прописать".

Это было обязательным в самых первых версиях MPLAB, а в более поздних версиях (включая и рекомендованную мной), букве W автоматически ставится в соответствие 0. В этом случае, аккумулятор можно не "прописывать".

Это же относится и к символьному обозначению регистра (F / d=1).

Можете провести эксперимент: откройте текст программы Multi.asm и проассемблируйте его с "пропиской" F и без нее (в "рабочей" части программы укажите F, а не 0).

Вобеих случаях ассемблирование пройдет успешно. Выводы делайте сами.

С целью наиболее простого и удобного "въезда" в команды, "фирменную" таблицу команд я довольно-таки основательно видоизменил (см. "Приложение №11").

Эти изменения можно отследить, если сравнить

"Приложение №10" с "Приложением №11".

Если есть неясности, то пишите (просьба четко сформулировать вопрос).

Вконце концов, ведь должна же существовать максимально доходчивая и понятная таблица команд, а не "кроссворд, от которого крыша едет".

"Чудеса" продолжаются.

Валерий Галкин обнаружил еще три ошибки в "расшифровке" системы команд, опубликованной в "Справочнике по среднему семейству ...".

Все они связаны с неверными числовыми результатами выполнения команд. Вопросы Валерия Галкина выделены синим цветом.

1. Команда ANDWF f,d

Правильно ли указано в третьем примере, что результат исполнения команды - число

0x15 (по моему, 0x12)?

Валерий прав: при побитном "И" числа 00010111 (17h) и 01011010 (5Ah), получается число

00010010 (12h), а не 00010101 (15h).

2. Команда IORLW k

Правильно ли указано во втором примере, что после выполнения команды, W = 0x9F (по моему, W = 0xBF)?

Валерий прав: при побитном "ИЛИ" числа 10011010 (9Ah) и 00110111 (37h), получается число

10111111 (BFh), а не 10011111 (9Fh).

3. Команда XORLW k

Правильно ли указано во втором примере, что после выполнения команды, W = 0x18 (по моему, W = 0x98)?

Валерий опять прав: при побитном "Исключающее ИЛИ" числа 10101111 (AFh) и 00110111

(37h), получается число 10011000 (98h), а не 00011000 (18h).

Выводы делайте сами.

Валерий, спасибо за обнаружение "мин на этом минном поле".

Таблица команд ассемблера, которая не содержит ошибок,

находится в "Приложении №12".

"Самоучитель по программированию PIC контроллеров для начинающих" http://ikarab.narod.ru E-mail: karabea@lipetsk.ru

35