Файл: Самоучитель по программированию PIC контроллеров для начинающих (Е.А. Корабельников,2008).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 16.06.2025
Просмотров: 3621
Скачиваний: 3
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