Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Samouchitel

.pdf
Скачиваний:
16
Добавлен:
13.02.2015
Размер:
3.65 Mб
Скачать

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

Принцип замены команды 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

4. Что такое программа и правила ее составления. Пример создания программы автоколебательного мультивибратора. Директивы.

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

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

При создании устройств на дискретных элементах цифровой техники, конструктор, преимущественно, имеет дело с машинными кодами.

Если речь идет об устройствах, в состав которых входит большое количество корпусов м/схем, то такое конструирование весьма хлопотно и трудоемко.

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

В процессе конструирования устройств на м/контроллерах, конструктор, преимущественно, имеет дело не с машинными кодами, а с языком программирования ассемблер (или с какимлибо языком высокого уровня, но в конечном итоге, все сводится к ассемблеру), c помощью которого, задачи решаются гораздо проще и эффективнее.

Устройства, по схемотехнике, получаются очень простыми, компактными, экономичными и относительно дешевыми.

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

Потом, когда Вы "войдете во вкус", все станет гораздо понятнее и интереснее (по себе знаю). А пока, наберитесь терпения (а куда деваться? Выбора-то нет... В этой "епархии ничего с неба не падает).

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

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

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

составляется не для самой себя, а под конкретное устройство.

Пример:

Необходимо разработать простую программу, реализующую функцию автоколебательного мультивибратора, с одним-разъединственным выходом.

Форма сигнала - меандр (отношение периода к длительности импульса =2). Используется PIC16F84A.

Под этот выход, можно назначить любой из выводов порта А или В. Пусть это будет, например, вывод RB0 порта В.

Следовательно, необходимо установить режим работы вывода RB0 "на выход". Кроме того, что полный цикл программы должен исполняться циклически (периодически), внутри полного цикла программы, должны иметь место быть еще и внутренние циклы.

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

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

36

случае, она должна "закольцеваться", на какое-то время, в подпрограмме формирования уровня 1.

Потом опять 0, потом опять 1 и т.д ....

И так, до "бесконечности" (пока включено питание).

Форма сигнала "меандр" предполагает одинаковое время формирования этих уровней. Предположим, что для каждого из уровней, это время должно быть равным 100 мкс. "Выстраиваем упрощенный скелет" программы:

После команды Start, переводим вывод RB0 на работу "на выход". Далее, на выводе RB0, в течение 100 мкс., формируем нулевой уровень.

Далее, на выводе RB0, в течение 100 мкс., формируем единичный уровень. Переходим на новый, полный цикл программы, то есть, на команду Start, и так до "бесконечности" (пока питание не будет выключено).

Как составляется эта программа?

Текст программы выглядит так:

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

; Multi.asm Автоколебательный мультивибратор.

; PIC16F84A Кварц 4 мГц.

;===============================================================================

LIST

p=16F84A

; Установка типа

микроконтроллера.

__CONFIG

03FF1H

;

Бит защиты выключен, WDT выключен,

 

 

;

стандартный XT

- генератор.

;===============================================================================

; Определение положения регистров специального назначения.

;===============================================================================

Status

equ

03h

; Регистр выбора банка.

TrisB

equ

06h

; Регистр

выбора направления работы выводов

 

 

 

;

порта В.

PortB

equ

06h

;

Регистр

управления защелками порта В.

;===============================================================================

; Определение названия и положения регистров общего назначения.

;===============================================================================

Sec equ 0Ch ; Счетчик времени полупериода.

;===============================================================================

org

0

;

Начать выполнение программы с адреса 0 PC.

goto

Start

;

Переход в ПП Start.

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

; Текст рабочей части программы.

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

; Установка

направления

работы RB0 - на выход.

 

 

 

 

;-------------------------------------------------------------------------------

 

 

 

 

 

 

 

Start

bsf

Status,5

; Перейти в 1-й

банк

(установить в 1

5-й бит

 

 

 

; регистра

Status).

 

 

 

movlw

.0

; Записать

константу

0 в аккумулятор

(W).

 

movwf

TrisB

; Скопировать 0

из W

в регистр TrisB.

 

 

bcf

Status,5

; Перейти в 0-й

банк

(установить в 0

5-й бит

 

 

 

; регистра

Status).

 

 

;-------------------------------------------------------------------------------

; Определение времени полупериода (закладка константы в регистр Sec).

;-------------------------------------------------------------------------------

 

 

 

 

movlw

.32

;

Записать в регистр W

константу .32

movwf

Sec

;

Скопировать .32 из W

в регистр Sec.

;-------------------------------------------------------------------------------

 

 

 

 

; Формирование на выводе RB0 нулевого

уровня.

 

;-------------------------------------------------------------------------------

 

 

 

 

bcf

PortB,0

;

Установить на выходе

защелки RB0 ноль.

nop

 

;

Калибровочный машинный цикл.

37

 

nop

 

; -------------

"--------------

Pause_1

decfsz

Sec,F

; Декремент содержимого регистра Sec с

 

goto

Pause_1

; помещением результата в этот же регистр.

 

 

 

; Если этот результат не=0, то

 

 

 

; осуществляется переход

 

 

 

; в ПП Pause_1 ("закольцовка" в этой ПП).

 

 

 

; Если =0, то программа исполняется далее.

;-------------------------------------------------------------------------------

 

 

 

 

; Определение времени полупериода (закладка константы в регистр Sec).

;-------------------------------------------------------------------------------

 

 

 

 

 

movlw

.30

; Записать в регистр W константу .30

 

movwf

Sec

; Скопировать .30 из W в регистр Sec.

;-------------------------------------------------------------------------------

 

 

 

 

; Формирование на выводе RB0 единичного уровня.

 

;-------------------------------------------------------------------------------

 

 

 

 

 

bsf

PortB,0

; Установить на выходе защелки RB0 единицу.

 

nop

 

; То же самое, что и для нулевого уровня,

 

nop

 

; только "закольцовка" происходит в ПП Pause_2.

Pause_2

decfsz

Sec,F

; ------------------

"-------------------

 

goto

Pause_2

; ------------------

"-------------------

 

goto

Start

; Переход на новый полный цикл программы.

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

end

;

Директива

конца программы (всегда

 

;

последняя

снизу).

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

программы.

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

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

Устанавливаем биты конфигурации: бит защиты CP выключен, сторожевой таймер WDT выключен, кварцевый генератор (используем кварц на 4мГц.) работает в режиме стандартного генератора (XT).

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

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

О них будет рассказано ниже, а также и в других разделах "Самоучителя..." ("хорошего понемножку". Сейчас "перенапряг" совсем не нужен).

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

Так как нужно только определить направление работы вывода порта В и далее работать только с этим выводом, то необходимо "прописать" названия и адреса регистров TRISB (выбор направления работы выводов порта В), PORTB (работа с защелками порта В) и STATUS (необходимо переключение банков, так как регистр TRISB находится в 1-м банке). Остальные регистры специального назначения "прописывать" не нужно, так как в программе они не задействованы (обращений к ним нет).

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

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

В предыдущем разделе, речь шла о задержках. Это как раз то, что нужно, так как в данном случае, необходима задержка на 100 мкс.

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

38

Назовем его, например, Sec, и с помощью директивы equ, назначим ему адрес в области оперативной памяти. Например, 0Ch

Всё. Регистр "прописан".

В конце "шапки", располагается директива, определяющая, с какого именно адреса памяти программ (PC) начать исполнение программы (обычно, org 0 С нулевого адреса PC) и команда перехода на начало исполнения программы (обычно, goto Start).

Так как команда goto Start расположена сразу же после директивы org 0 (у команды goto Start нулевой адрес в памяти программ), то с этой команды и начнется исполнение программы.

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

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

Если содержимое "шапки" программы сейчас "мутно" (что вполне естественно), то ничего страшного: далее это прояснится.

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

Итак, приступаем к составлению программы:

После "срабатывания" команды goto Start (она находится в конце "шапки" программы), происходит переход на первую команду рабочей части программы (адрес в счетчике команд

PC 0001h).

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

Обычно, его считают стандартным названием подпрограммы (слово "старт" всем понятно). Далее, работаем в соответствии с "выстроенным скелетом" программы.

После инициализации ПИКа (по умолчанию), автоматически устанавливается 0-й банк. Так как регистр TrisB "дислоцируется" в 1-м банке, то переходим в 1-й банк (а иначе, с регистром TrisB невозможно будет работать).

Для этого нужно установить 5-й бит регистра Status в единицу. Делаем это (bsf Status,5). Так как направления работы выводов порта В RB1…RB7 нас не интересуют (они не задействованы), то проще всего установить все биты регистра TrisB в ноль (все выводы порта В работают "на выход"), то есть, записать в этот регистр константу 0.

Примечание: по умолчанию, все выводы портов настроены на работу "на вход".

Так как напрямую записать константу в регистр (любой) нельзя, то "транспортировка" этого нуля, в регистр TrisB, производится через аккумулятор (через регистр W), что Вы и видите в тексте программы (movlw .0 и movwf TrisB).

Вэтом случае, задействуются байт-ориентированные команды. Другой способ: задействуем бит-ориентированную команду BCF.

Вэтом случае, в ноль устанавливаются не все биты регистра TrisB, а только бит с номером ноль: bcf TrisB,0 (сброс нулевого бита регистра TrisB в 0).

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

Всё. Дело сделано. Теперь вывод RB0 работает "на выход".

То есть, к нему подключился выход "своей" защелки.

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

нулевой банк (устанавливаем 5-й бит регистра Status в 0: bcf Status,5). Все перечисленные выше операции, можно считать подготовительными. То есть, подпрограмма Start начинается именно с таких операций.

А как же иначе? Ведь к "боевым действиям" нужно подготовиться.

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

Предположим, что с этим содержимым мы определились: в регистр Sec записывается число

.32 (почему именно оно см. далее). Посмотрите в текст программы.

39

Число .32 (точка - атрибут десятичной системы исчисления) является константой (то, что задается программистом).

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

(movlw .32 и movwf Sec).

Число .32 в регистр Sec записано, и в дальнейшем, содержимое этого регистра (то есть, число) можно последовательно декрементировать (уменьшать на 1).

Итак, все готово для управления защелкой RB0 порта В. Формируем нулевой уровень на выводе RB0.

Применяем бит - ориентированную команду BCF и указываем в ней нулевой бит регистра PortB, который нужно сбросить в 0 (bcf PortB,0).

Все. Дело сделано, и на выводе RB0, ноль зафиксируется до тех пор, пока мы его не заменим на 1 (вспомните о том, что защелка является банальным триггером, то есть, элементом оперативной памяти).

Теперь нужно сделать так, чтобы с момента установки, на выводе RB0, нуля и до момента смены этого нуля на единицу, прошло ровно 100 мкс.

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

Установка, на выводе RB0, единицы происходит так же, как и установка нуля, только применяется команда BSF (bsf PortB,0).

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

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

Применяется кварц на 4мГц., следовательно, один машинный цикл равен 1мкс. Таким образом, с момента установки, на выводе RB0, нуля и до смены его на единицу должно пройти 100 машинных циклов.

Подпрограмма задержки Pause_1 - циклическая.

Если результат декремента содержимого регистра Sec не равен нулю, то один "виток" этой подпрограммы будет отработан за 3 машинных цикла: команда DECFSZ выполняется за один м.ц., а команда GOTO - за два.

Таким образом, уменьшение содержимого регистра Sec на единицу происходит за 3 м.ц. (3мкс.).

Если результат декремента содержимого регистра Sec равен нулю, то команда GOTO не исполняется (вместо нее, "виртуальный" NOP), и выход из подпрограммы Pause_1 происходит за 2 м.ц.

Следовательно, константе .32 соответствует 32х3-1=95 м.ц. (95мкс.).

К 95-ти, плюсуем 2 м.ц. двух команд NOP, 2 м.ц. команд установки константы в регистр Sec для формирования времени единичного уровня на выводе RB0 (movlw .30 и movwf Sec) и 1 м.ц. команды установки, на выводе RB0, единицы (bsf PortB,0).

Получаем ровно 100 мкс.

Теперь о двух NOPах, которые стоят перед ПП Pause_1.

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

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

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

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

Такая запись и имеет место быть (movlw .30 и movwf Sec).

Различие числовых значений констант (.32 и .30) при формировании нулевого и единичного уровней, объясняется тем, что при формировании, на выводе RB0, единичного уровня, исполняется команда GOTO (2 м.ц.) и команды ПП Start (в конце текста программы, осуществляется безусловный переход в ПП Start).

Кто имеет такое желание - посчитайте.

40

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]