Содержание
Директивы ассемблера, “шапка” программы | RadioLaba.ru
- admin
- 20.12.2013
- Программирование PIC-контроллеров
“Шапка” программы начинается с подключения специального файла с расширением INC, в виде строчки #include , где include – директива подключения дополнительного файла. Эти файлы для каждого типа микроконтроллера хранятся в папке программы MPLAB. В этих файлах адресам регистров специального назначения присвоены их названия, а также названия битов, это сделано для удобного написания программы. Если этот файл не подключать, то придется вручную присваивать соответствующие названия, иначе MPLAB выдаст ошибки.
Далее идет строчка с указанием типа микроконтроллера в виде директивы LIST p=16F628A. После нее строчка __CONFIG 3F01h ( __CONFIG – директива установки битов конфигурации), здесь двухбайтным шестнадцатеричным числом 3F01h (значение слова конфигурации) настраиваются биты конфигурации микроконтроллера (выбор кварцевого генератора, сторожевой таймер, защита памяти программ и др. ). Для лучшей наглядности при настройке битов можно прописать значение слова конфигурации в двоичной форме __CONFIG b’11111100000001′.
Теперь необходимо присвоить названия адресам регистров ОЗУ, которые будут использоваться в программе. Делается это для удобного написания программы. Присвоение название осуществляется директивой equ (определение константы), например, var equ 0020h. Записи var в этом случае будет соответствовать число 0020h, которое является адресом регистра ОЗУ в памяти данных, кстати, в файле P16F628A.INC названия регистрам специального назначения присвоены таким же образом.
Я также использую директиву #define (определение текстовой последовательности для замены), чтобы присвоить названия линиям порта микроконтроллера. Например, строка #define led PORTВ,0 означает что, запись led будет заменяться на PORTB,0. Составляя текст программы не надо везде писать PORTB,0, а пишем просто led.
Все вышесказанное относится к “шапке” программы, после которой следует сам текст программы.
Директива end в конце программы указывает на окончание программы, конец всех команд.
Таким образом, шапка программы выглядит примерно следующим образом:
#include <P16F628A.
LIST p=16F628A ;указание типа процессора
__CONFIG 3F18h ;установка битов конфигурации
Sec equ 0020h ;присвоение названий регистрам ОЗУ
Sec0 equ 0021h
Sec1 equ 0022h
var equ 0023h
T1L equ 0024h
T1H equ 0025h
shet equ 0026h
W_temp equ 0079h
STATUS_temp equ 007Ah
#define led PORTA,7 ;присвоение названий линиям порта
#define knp1 PORTB,0
#define knp2 PORTB,1
#define signal PORTB,2
org 0000h ;начать выполнение программы с адреса 0000h
goto Start ;переход на метку Start
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Подпрограмма обработки прерываний
org 0004h ;начать выполнение подпрограммы с адреса 0004h
. ……………. ;тело подпрограммы
……………..
……………..
retfie ;выход из подпрограммы прерывания
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;Основная программа
Start …………….. ;тело основной программы
……………..
……………..
end ;конец всей программы
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
#include <P16F628A. LIST p=16F628A ;указание типа процессора __CONFIG 3F18h ;установка битов конфигурации
Sec equ 0020h ;присвоение названий регистрам ОЗУ Sec0 equ 0021h Sec1 equ 0022h var equ 0023h T1L equ 0024h T1H equ 0025h shet equ 0026h W_temp equ 0079h STATUS_temp equ 007Ah
#define led PORTA,7 ;присвоение названий линиям порта #define knp1 PORTB,0 #define knp2 PORTB,1 #define signal PORTB,2
org 0000h ;начать выполнение программы с адреса 0000h goto Start ;переход на метку Start
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Подпрограмма обработки прерываний
org 0004h ;начать выполнение подпрограммы с адреса 0004h
. …………….. ……………..
retfie ;выход из подпрограммы прерывания ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Основная программа
Start …………….. ;тело основной программы …………….. ……………..
end ;конец всей программы |
Как видно здесь нет ничего сложного, далее остается только написать текст программы.
Tags: asm, PIC, директива, шапка
Программирование микроконтроллеров PIC. Часть 3. Структура программы на ассемблере — radiohlam.ru
Итак, давайте разберёмся, какую структуру имеет программа на языке ассемблер и для чего предназначены отдельные части программы.
Для дальнейшего чтения неплохо иметь под рукой текст какой-нибудь программы и документацию на контроллер (либо распечатку, либо просто открыть их в соседних окошках), чтобы можно было в процессе чтения находить в тексте программы и в документации то, о чём читаешь. Так информация лучше доходит и запоминается.
Ниже показана типичная структура программы на ассемблере.
|
||
|
1. Шапка программы.
В шапке указывается различная служебная информация для компилятора. То есть, практически всё, что находится в шапке, служит инструкциями не для контроллера, а для компилятора (кроме слова конфигурации).
list — директива, которая имеет кучу опций и может быть использована для управления процессом ассемблирования. Её можно и не использовать вовсе (настройки компилятор всё равно возьмёт из менеджера проекта), но указав этой директивой тип процессора, программа становится удобнее для восприятия. Во-первых, сразу видно для какого контроллера она написана. Во-вторых, если контроллер, указанный директивой list, не совпадает с контроллером, указанным в менеджере, то при компиляции будет сгенерирована ошибка (чтобы вы уже точно заметили, что программа написана под другой контроллер).
__config значение — эта директива указывает значение слова конфигурации контроллера.
Поскольку каждый из параметров контроллера задаётся в слове конфигурации одним или несколькими битами, то это слово удобнее всего записывать в двоичной форме (для наглядности).
CBLOCK число — блок поименованных констант. Число — определяет константу, которой будет соответствовать первая запись (имя). Вторая запись будет соответствовать константе число+1, третья — константе число+2 и так далее. То есть, в нашем примере, везде, где компилятор увидит запись variable1, он вместо этой записи подставит число 20h (другая запись 0x20), вместо variable2 будет подставлено 21h и т.д.
С помощью этой директивы очень удобно присваивать имена сразу целому блоку пользовательских регистров (в которых программа будет хранить свои переменные или константы).
Пользовательские регистры контроллера PIC16F628A начинаются по адресу 20h.
То есть, с помощью директивы CBLOCK можно фактически определить блоки регистров (присвоить имена регистрам, расположенным по определённым адресам), которые будут использоваться в программе как переменные.
Блоков может быть несколько, главное внимательно следить, чтобы они не пересекались, а то получится, что две разных переменных расположены в одном и том же регистре. Конец блока обозначается директивой ENDC.
equ — эта директива сообщает компилятору, что запись, расположенная перед этой директивой соответствует числу, стоящему после этой директивы (компилятор будет заменять запись на число). В нашем примере, записи Const1 будет соответствовать число 1 (точка перед числом обозначает десятичную систему исчисления, а буква h — шестнадцатиричную), записи Const2 — число 5, записи TRISB — число 6 и т.д. С помощью этой директивы обычно задают константы, адреса используемых в программе регистров и присваивают имена отдельным битам в регистре.
Допустим в программе мы несколько раз в разных местах оперируем константой, значение которой равно 1 (например загружаем её в аккумулятор командой movlw .1). Если нам понадобится перекомпилировать программу с другим значением этой константы, то надо будет искать все куски кода, где мы её используем и в каждом случае отдельно менять код. Если же мы директивой equ указали, что записи Const1 соответствует константа 1, то теперь мы везде можем писать movlw Const1 и для изменения константы нам нужно всего лишь изменить её значение в шапке программы.
Аналогичным образом именуются используемые регистры. Например, мы хотим работать с портом B. Открываем документацию и смотрим, какие нам понадобятся регистры для работы с ним и по каким адресам они расположены. Мы видим, что для работы с портом B нам понадобится регистр, расположенный в нулевом банке по адресу 06h, который в доке называется PORTB и регистр, расположенный в первом банке по адресу 06h, который в доке называется TRISB. Пока компилятор не знает, что такое PORTB и TRISB, — мы можем обращаться к ним только по адресу. Например, если мы напишем movwf 06h, находясь в нулевом банке, то контроллер запишет данные из аккумулятора в регистр PORTB, а если та же команда будет выполнена, когда мы находимся в первом банке, то данные из аккумулятора будут записаны в регистр TRISB. Если мы в шапке программы пропишем, что имя PORTB соответствует числу 06h (PORTB equ 06h), то теперь в программе, для записи данных из аккумулятора в порт B, код может выглядеть так: movwf PORTB и компилятор будет знать, что запись PORTB нужно заменить на 06h.
Короче говоря, шапка программы (за исключением слова конфигурации) нужна для облегчения последующего процесса программирования. То, что в ней записано, — это не код, который будет исполняться контроллером, а инструкции для компилятора, поясняющие как работать с написанным в теле программы кодом.
2. Тело программы.
В теле программы пишутся те инструкции, которые будут исполняться контроллером, то есть это как раз и есть сама программа для контроллера. Хотя, здесь тоже встречаются директивы, предназначенные для компилятора, например для правильного размещения участков программы в памяти контроллера.
ORG число — эта директива как раз предназначена для компилятора. Число в данном случае — это адрес, начиная с которого в памяти программ контроллера располагается код, следующий за директивой ORG.
При возникновении прерывания счётчик так же всегда устанавливается по какому-то определённому адресу, например для PIC16F628A это будет адрес 0004h. Следовательно, если мы используем в программе прерывания, то перед подпрограммой обработки прерываний нужно написать ORG 04h, чтобы первая инструкция этой подпрограммы располагалась по адресу 0004h. Кроме того, в этом случае, необходимо обеспечить, чтобы основная программа не пересекалась с подпрограммой обработки прерывания.
Помните, мы указали, что программа должна располагаться в памяти программ, начиная с нулевого адреса. Соответственно, первая её инструкция будет по нулевому адресу, вторая по адресу 0001h, третья — по адресу 0002h, четвёртая по адресу 0003h, а пятая — по адресу 0004h.
Тело программы также можно поделить на 2 части — инициализацию и, собственно, выполнение какой-то задачи.
Инициализация — это настройка контроллера для решения нашей задачи. Поскольку контроллер — вещь многофункциональная и разные его части могут работать по разному, в зависимости от настроек (например, коэффициенты деления предделителя могут быть разные, ноги могут работать как входы или как выходы и т.д.), то сначала его нужно настроить для выполнения нашей конкретной задачи.
Пусть мы (для примера) хотим организовать мигание светодиодом, подключенным к 7-й ноге PIC16F628A. Для этого нам нужно, чтобы седьмая нога работала как выход. Седьмая нога — это канал RB0 порта B. Открываем доку и смотрим, что нужно сделать, чтобы настроить RB0 как выход. Мы видим, что направлением работы порта B управляет регистр TRISB, который находится по адресу 86h. 86h — это первый банк, смещение 06h от начала банка. Для того, чтобы RB0 работал как выход, нужно установить нулевой бит регистра TRISB в 0. То есть инициализация будет заключаться в следующем:
bcf PORTB, 0 ; устанавливаем начальное состояние
; (уровень, который установится на RB0
; после переключения направления работы
; на выход, 0 - низкий уровень, 1 - высокий
bsf Status, RP0 ; переходим в первый банк (нужный
; нам регистр TRISB находится там), для чего
; устанавливаем в 1 бит RP0 регистра Status
bcf TRISB, 0 ; устанавливаем в 0 нулевой бит регистра TRISB
; (устанавливаем направление работы RB0 - на выход)
bcf Status, RP0 ; возвращаемся в нулевой банк
|
Только теперь (после инициализации), изменяя соответствующий бит в регистре PORTB, мы можем управлять уровнем сигнала на выводе RB0.
На самом деле, несмотря на то, что инициализация находится в начале программы, процедуры инициализации, так же как регистры и переменные в шапке программы, прописываются в процессе разработки рабочей части программы. Мы ведь не можем заранее знать, какие нам понадобятся переменные, сколько, какими регистрами и модулями мы будем оперировать.
То есть, сначала нам надо разработать рабочую часть программы (которая и будет решать нашу конкретную задачу), а уже в процессе написания заполнять шапку и дописывать процедуры инициализации используемых модулей. Подробнее читайте об этом в следующей части.
- Часть 1. Необходимые инструменты и программы. Основы MPLAB
- Часть 2. Что такое микроконтроллер и как с ним работать
- Часть 3. Структура программы на ассемблере
- Часть 4.
Разработка рабочей части программы. Алгоритмы
- Часть 5. Ассемблер. Организация циклов и ветвлений
- Часть 6. Как перевести контроллер в режим программирования и залить в него прошивку
|
ГЛАВА Сборка Введение 3.1
3,5 Введение Способность к общению велика
Процесс общения Физически « Программа » представляет собой Пример: ВОЗВРАТ 00 0000 0000 Подобно приведенному выше примеру, каждая инструкция ассемблера Ан 3.1 Представление чисел в В ассемблере MPLAB числа могут быть
Десятичные числа начинаются с точки, шестнадцатеричные 3.2 Язык ассемблера Основные элементы языка ассемблера
Этикетки Этикетка представляет собой текстовое обозначение (обычно
Инструкции Инструкции уже определены с использованием определенного
Операнды Операнды являются элементами инструкции для инструкции
Комментарии Комментарий — это набор слов, который программист Директивы Директива аналогична инструкции, но
3,3 Вт мощность В следующем примере показан простой При написании программы помимо обязательных правил Так как эти данные не важны для Чтобы нормально функционировать, мы должны _CONFIG Когда все Инструкции находятся в Следующим шагом является выбор банка памяти 0 и размещение 3.4 Директивы управления 3.1 Синтаксис: Описание: Пример: #define включено 1 Аналогичные директивы: #UNDEFINE, 3.2 Синтаксис: Описание: Пример: 3.3 Синтаксис: Описание: Пример: Аналогично 3.4 Синтаксис: Описание: Пример: Аналогичные директивы: SET, 3,5 Синтаксис: Описание: Пример: Аналогичные директивы: EQU, 3.6 Синтаксис: Описание: Пример: Аналогичные инструкции: SET 3,7 Синтаксис: Описание: Пример: Первые две инструкции, следующие за первой 3.8 КОНЕЦ Конец Синтаксис: Описание: Пример: 3,9 Синтаксис: Описание: Пример: Аналогичные директивы: #ELSE, 3.10 Синтаксис: Описание: Пример: Аналогичные инструкции: 3.11 Синтаксис: Описание: Пример: Аналогичные директивы: ELSE, 3. Синтаксис: Описание: Пример: 3.13 Синтаксис: Описание: Пример: endw Аналогичные директивы: WHILE 3. Синтаксис: Описание: Пример: Аналогичные директивы: 3.15 Синтаксис: Описание: Пример: Аналогичные директивы: #DEFINE, ELSE, 3.16 Синтаксис: Описание: Пример: cblock 0x02 Аналогичные директивы: 3.17 Синтаксис: Описание: Аналогичные директивы: 3.18 Синтаксис: Описание: Пример: Аналогичные инструкции: DE, DT 3.19 Синтаксис: Описание: Пример: Аналогичные инструкции: DB, DT 3.20 Синтаксис: Описание: Пример: Аналогичные директивы: DB, DE 3. Синтаксис: Описание: Пример: Аналогичные директивы: _IDLOCS, 3.22 Синтаксис: Описание: Пример:
3.5 Файлы В результате процесса перевода
Первый файл содержит переведенную программу Пример файла «список» для программы в этом В конце файла «список» |
Язык ассемблера, часть 1 (микроконтроллер PIC)
Начиная с раздела 3, мы писали программы с веселой энергией.
С помощью набора инструкций устройства, см. раздел A, можно выполнить перевод из удобочитаемой символьной формы в машиночитаемую двоичную. Это не особенно сложно для такого устройства, как PIC, которое имеет сокращенный набор инструкций (RISC) и несколько режимов адресации. Однако это медленно и утомительно, особенно когда кодируются программы значительной длины. Кроме того, он подвержен ошибкам и его трудно поддерживать всякий раз, когда необходимо внести изменения.
Компьютеры умеют делать скучные вещи быстро и точно; и перевод символического кода в машинный определенно попадает в эту категорию. Здесь мы кратко рассмотрим различные программные пакеты, помогающие в этом процессе перевода.
После прочтения этого раздела вы:
• Знать, что такое язык ассемблера и как он связан с машинным кодом.
• Оцените преимущества символьного представления по сравнению с машиночитаемым кодом.
• Понимание функций ассемблера.
• Поймите разницу между абсолютной и перемещаемой сборкой.
• Понимать роль компоновщика.
• Оцените процесс перевода и локализации программы на языке ассемблера в абсолютный машинный код.
• Понимать структуру файла машинного кода и роль программы-загрузчика.
• Понимание роли симулятора.
• Оцените использование интегрированной среды разработки для автоматизации взаимодействия различных программных инструментов, необходимых для преобразования исходного кода в запрограммированное устройство MCU.
Суть процесса преобразования представлена на рис. 8.1. Здесь программа готовится ручным человеком в символической форме, переваривается компьютером и выводится в машиночитаемой форме.
Рис. 8.1 Преобразование исходного кода на уровне сборки в машинный код.
Вообще разные переводчики и утилиты 9Компьютерные пакеты 0773 пишутся и продаются многими компаниями-разработчиками программного обеспечения, поэтому фактические детали и процедуры несколько различаются между различными коммерческими продуктами. В конкретном случае устройств PIC MCU компания Microchip Technology Inc. в соответствии со своей политикой всегда бесплатно предоставляла свои программные инструменты на уровне сборки, что является важным фактором их популярности. По этой причине коммерческое программное обеспечение PIC встречается относительно редко, и то, что есть, обычно соответствует синтаксису Microchip. По этой причине мы проиллюстрируем эту тему набором инструментов компьютерного кодирования Microchip.
Использование компьютера для помощи в переводе кода из более удобных для пользователя форм (известных как исходный код) в удобный для машин двоичный код (известный как объектный код или машинный код, и загрузка его в память началась в конце 1940-х годов для мэйнфреймов. По крайней мере, он позволял использовать системы счисления более высокого порядка, такие как шестнадцатеричная.1 В этой базе фрагмент кода на рис. в назначенных ячейках памяти.Этот загрузчик может быть частью программного обеспечения в вашем программаторе PIC-EPROM.Шестнадцатеричное кодирование мало что дает в его пользу, за исключением того, что число нажатий клавиш уменьшено, но клавиш больше, и их немного легче обнаружить некоторые типы ошибок
Для серьезного программирования требуется как минимум транслятор символов, или ассемблер2. Это позволяет программисту использовать мнемонику для инструкций и внутренних регистров с именами для констант, переменных и адресов.
Давать имена адресам и константам особенно полезно для более длинных программ, которые легко могут превышать 1000 строк. Вместе с использованием комментариев это упрощает отладку, разработку и сопровождение кода. Таким образом, если мы хотим изменить файловые регистры, содержащие переменную NUM, с Файла 20:21 h на, скажем, Файл 36:37h, нам нужно всего лишь изменить строку:
cblock 20h
на:
. cblock 36h
, а затем ретранслировать в машинный код.
Псевдоинструкция cblock является примером директивы ассемблера. Директива — это команда программиста ассемблеру, касающаяся его работы или присвоения имени константе. Мы перечисляем небольшое подмножество директив ассемблера Microchip в конце темы, читатель должен обратиться к официальному руководству для подробного описания. Вкратце директивы, используемые в программе 8.1, таковы: cblock – endc
Скорее похоже на блок директив equ , дающий инкапсулированный список констант меток, начинающихся либо с указанного значения, например.
Программа 8.1 Абсолютный код уровня ассемблера для нашего модуля извлечения квадратного корня.
end
Сообщает ассемблеру, что это конец исходного кода. equ
Связывает значение с символом. Например, ассемблер заменяет имя STATUS на значение 3 везде, где оно появляется в операнде инструкции. Обычно используется для регистров специального назначения (SPR) и битов в файловых регистрах.
org
Указывает начальный адрес для следующего кода, в противном случае ассемблер по умолчанию использует 000h в хранилище программ. В этой программе подпрограмма SQR_ROOT запускается в 200 часов.
Конечно, символьные трансляторы требуют большей вычислительной мощности, чем простые шестнадцатеричные загрузчики, особенно в области памяти и хранилища резервных копий.
• Преобразование различных мнемоник и меток инструкций в их эквиваленты в машинном коде. тема
• Расположение инструкций и данных в соответствующей ячейке памяти.
Второй из них, пожалуй, труднее понять.
Программа 8.2 предназначена для обработки абсолютным ассемблером. Здесь программист использует директиву org, чтобы указать ассемблеру поместить код в указанный адрес хранилища программ. Это означает, что программист должен знать, где что размещать. Этот процесс абсолютной сборки показан на рис. 8.2. Абсолютная сборка подходит там, где в реальных проектах программа содержится в одном автономном файле; что характерно для большей части кода в этом тексте.
Большинство программ, работающих на PIC низкого и среднего уровня, адекватно обрабатываются абсолютным ассемблером. Чтобы прояснить процесс, мы проведем подпрограмму на рис.
Редактирование
Первоначально исходный файл должен быть создан с помощью текстового редактора. Текстовый редактор отличается от текстового процессора тем, что в него не вставляются встроенные управляющие коды, задающие форматирование и другую информацию.
Рис. 8.2 Абсолютная трансляция кода на уровне ассемблера.
Например, нет переноса строк; если вы хотите новую строку, нажмите клавишу [ENT]. Большинство операционных систем поставляются с простым текстовым редактором; например блокнот для Microsoft Windows. Также доступны сторонние продукты, и большинство текстовых процессоров имеют текстовый режим, который можно использовать как редактор программ.3 Имена исходных файлов на уровне сборки, совместимые с Microchip, имеют расширение .src.
Формат типичной строки исходного кода выглядит следующим образом:
За исключением строк только для комментариев, все строки должны содержать инструкцию (либо исполняемую MCU, либо директиву) и любую соответствующую операнд или операнды.
Необязательный комментарий выделяется точкой с запятой , разрешены комментарии целиком – см. строки 11-18 программы 8.1. Комментарии игнорируются ассемблером и предназначены исключительно для удобочитаемой документации. Примечания должны быть обильными и должны объяснять, что делает программа, а не просто повторять инструкцию. Например:
— это пустая трата энергии:
гораздо полезнее. Отсутствие или минимальное комментирование исходного кода является частым недостатком, и не ограничивается только студентами.
INC> ;подключение файла P16F628A.INC
……………. ;тело подпрограммы
основной программы
; инициализация
; решение задачи
end
Разработка рабочей части программы. Алгоритмы
Однако это возможно только в том случае, если оба
Мы проиллюстрируем
Его также можно использовать для выполнения ветвления программы (например, Goto
Обычно это регистров или переменных
Необходимый «конец» в конце каждой программы информирует
Директива «включить»
С помощью этой директивы мы
А если не было, то часть
12
14
21
HEX)
Первый столбец
Добавить комментарий