.
Создание ассемблерных модулей |
||||
Как
создать перемещаемый код - исследуем вопрос
..........Содержание: ..........Введение ..........Создание перемещаемого модуля .......... - Исходные подпрограммы .......... - Начало модуля .......... - Переделка исходных подпрограмм .......... - Сборка модуля .......... - Настройка всех модулей проекта на один контроллер ..........Подключение перемещаемого ассемблерного модуля к проекту ..........Директива EXTERN. Задействуем модуль в основной программе ..........Подключаемый заголовочный файл ..........Блокировка повторного включения заголовочных файлов ..........Литература Введение..........Прежде всего, звучит вопрос: «Зачем всё это нужно?» ..........Отвечу на него в меру своего понимания. Когда вы только начинаете осваивать программирование на ассемблере для PIC микроконтроллеров и делаете свои первые проекты, модули вовсе не нужны, и вполне можно обходиться без них. ..........Если продолжаете программировать, и счёт вашим проектам идёт уже на десятки, то, возможно, вы начнёте замечать, что часто повторяете одни и те же действия со своим собственным кодом. То есть создаёте новый проект в MpLab, копируете в него пустой шаблон-заготовку для будущей программы, копируете в текст свои готовые программы и подпрограммы, а потом часть программы доделываете уже под конкретные задачи конкретного устройства. Чтобы избежать рутинных операций с копированием готового ассемблерного кода в новый проект, разработано несколько инструментов. ..........- Макроопределения (макросы) ..........- Перемещаемые модули ..........- Библиотеки подпрограмм ..........- Языки высокого уровня Модули применяются для
облегчения
ПОВТОРНОГО ИСПОЛЬЗОВАНИЯ КОДА ..........Разработчики MpLab, в частности - ассемблера Mpasm, позаботились о том, чтобы облегчить программистам труд и создали систему, пользуясь которой, возможно создавать библиотеки своих собственных программ и легко подключать их к новым проектам. ..........В этой статье будет рассмотрен способ оформления и использования модулей ассемблерного кода для микроконтроллеров Microchip среднего семейства. Создание перемещаемого модуля..........При создании единичного проекта переменные задаются в фиксированных регистрах при помощи директив equ и cblock, а программа и подпрограммы сразу же размещаются в фиксированных адресах при помощи директивы org...........При создании перемещаемого кода ситуация другая. Адреса для переменных и адреса размещения модулей кода назначаются автоматически в процессе компиляции проекта. ..........Это основное преимущество перемещаемого кода – он может располагаться в любых адресах и работать с регистрами в любых банках микроконтроллера (речь идёт о средней серии PIC16Fxxx, в которой присутствует страничная адресация ОЗУ). ..........Основное преимущество оборачивается и некоторой избыточностью. В перемещаемом коде нужно обеспечивать выбор нужного банка используемых регистров и учесть, что код может оказаться в любых страницах памяти программ. То есть нужно предустанавливать биты PR0, PR1, IRP регистра STATUS при работе с переменными и регистр PCLATH при вызове подпрограмм. ..........К примеру, имеется две готовых подпрограммы, осуществляющих математические операции, и мы задаёмся целью разместить их в отдельном модуле, чтобы впоследствии его можно было включить в любой проект. Исходные подпрограммы..........Первая подпрограмма преобразует число в диапазоне 0…99 из двоичного кода в десятичный. Вторая делает обратное преобразование и из десятичного вида переводит число в двоичный код. ..........Подпрограммы сделаны для устройства с двухзначной динамической индикацией для ввода с клавиатуры и вывода на дисплей числа в диапазоне 0…99. Рассчитаны они на работу с нулевым банком регистров и в нулевой странице памяти программ. ..........Вот эти подпрограммы:
..........Наша
задача сформировать из этих подпрограмм перемещаемый ассемблерный
модуль. Учесть, что регистры, с которыми будут работать подпрограммы,
могут оказаться в любых адресах и банках. Сам код подпрограмм также
может быть размещён при компиляции в любых страницах памяти программ.
..........Чтобы сделать этот код перемещаемым, его придётся переделать и дополнить. Начало модуля..........Итак, начало нашего будущего модуля:
..........Директива udata объявляет (говорит ассемблеру о начале) секцию переменных. Директива res резервирует место под переменные, то есть транслятор (ассемблер) выделяет столько байт, сколько указано. ..........В нашем случае мы объявили три переменные e_rez, d_rez, temp, под каждую из которых будет выделен один байт памяти. ..........Ассемблер Mpasm может быть использован двумя способами: ..........- для генерации абсолютного кода, который может быть напрямую исполнен микроконтроллером ..........- для генерации перемещаемого кода, который может быть объединён с другими отдельно скомпилированными файлами ..........Мы задействуем его во втором варианте, для объединения нескольких файлов в один проект. ..........Когда файлов в проекте больше одного, то в дело включается линкер. Ассемблер генерирует из каждого АСМ файла объектный файл. Линкер собирает отдельные объектные файлы в цельный проект, пересчитывает адреса размещения кода и адреса меток в коде, а также размещает переменные в RAM области с назначением им конкретных адресов. ..........Почему модуль кода называется перемещаемым? ..........Он может быть размещён линкером в любых адресах памяти программ и работать с регистрами в любых адресах памяти данных микроконтроллера. ..........В объектном файле находится готовый код и информация об используемых данных. Адрес начала этого кода в будущем проекте можем определить заранее или оставить его определение на усмотрение линкера, что обычно и делается. В некоторых случаях необходимо зафиксировать размещение кода. ..........Директива code объявляет начало секции кода и позволяет при необходимости зафиксировать секцию кода в конкретных адресах памяти программ микроконтроллера. ..........Наращиваем «шапку» нашего перемещаемого модуля.
..........Если бы мы хотели разместить начало кода в определённых адресах памяти, например с адреса 0x0000, то пришлось бы сделать запись:
..........Применяется фиксация адреса в перемещаемом коде в редких случаях. Когда необходимо указать на начало программы, при объявлении начала адреса программы прерывания, размещении загрузчика или таблицы с данными. Фиксирование адреса в перемещаемом коде в других случаях практически не применяется, так как фиксированные адреса могут привести к несовместимости модулей. ..........Каждую секцию кода можно назвать отдельным именем. Впоследствии, если будет необходимость включить подпрограммы в библиотечный файл, это пригодится. Так и сделаем:
..........Именование секций, подпрограмм, переменных и других элементов при программировании с возможностью повторного использования кода – отдельная тема. Имя подпрограммы должно отражать её функциональное назначение и способствовать тому, чтобы, всего лишь взглянув на имя подпрограммы, модуля или файла, можно было легко вспомнить и понять функциональное назначение кода. Подробнее с правилами оформления кода и именования меток можно ознакомиться в статье: «MPASM. Как правильно оформлять программы на ассемблере для PIC-контроллеров» Переделка исходных подпрограмм..........Для того чтобы определить в, каких именно банках находятся регистры с переменными, в ассемблере имеется директива banksel...........В абсолютном коде заранее известно, в каком банке находятся переменные. В перемещаемом коде необходимо выбрать банк регистров, соответствующий конкретной переменной. Директива banksel предустанавливает биты RP0, RP1 регистра STATUS и производит выбор адресации между банками: ..........Банк 0 - адреса - 0x000…0x07F ..........Банк 1 - адреса - 0x080…0x0FF ..........Банк 2 - адреса - 0x100…0x17F ..........Банк 3 - адреса - 0x180…0x1FF ..........Предустанавливать биты выбора банка для каждой из переменных, объявленных в одной секции udata не требуется. Достаточно выбрать банк единожды для одной секции. Линкер размещает переменные из одной секции в одном и том же банке. ..........Работая с регистрами INDF и FSR при использовании косвенной адресации в перемещаемом коде, необходимо определить бит IRP регистра STATUS. В микроконтроллерах PIC16Fxxx два банка косвенной адресации. Нулевой банк 0x000…0x0FF и первый банк 0x100…0x1FF. Бит IRP регистра STATUS определяет, к какому именно банку регистров мы обращаемся. Для выбора банка косвенной адресации в перемещаемом коде служит директива bankisel.
..........Постоянное применение директив banksel и bankisel создаёт ту самую избыточность перемещаемого кода, которой обычно удаётся избежать при создании кода абсолютного. Перемещаемый код требует несколько больше места в памяти программ контроллера. Это относится в основном к микроконтроллерам средней серии типа PIC16Fxxx. В 18-й серии и более новых микроконтроллерах Microchip большей разрядности ОЗУ устроено по–другому, и перемещаемый код более компактен. Сборка модуля..........Следующий необходимый шаг – объявить подпрограммы Bin99Dec, Dec99Bin глобальными, чтобы основная программа могла их вызвать. Что происходит при объявлении метки глобальной, вы наверное уже догадались. Ассемблер в объектном файле создаёт таблицу меток, к которым разрешён доступ извне. Если основная программа обращается к глобальной метке внешнего модуля, то всё отлично, доступ разрешён. Если же метка глобальной не является, то доступа к ней нет. При попытке обращения к внутренней метке модуля ассемблер выдаст ошибку, что идентификатор неопределен...........Это позволяет использовать одинаковые имена меток в разных модулях одного проекта и обеспечивает разделение регистров между модулями. Ячейки, которые глобальными не являются, используются только внутри модуля и к ним нет доступа из других модулей программы. ..........Для окончательной сборки модуля остаётся добавить в него строку подключения файла определений для микроконтроллера среднего семейства и завершить всё директивой end
Настройка всех модулей проектана один контроллер..........Если модулей в проекте несколько, то придётся настроить их все на один и тот же тип микроконтроллера. Можно для этого во всех модулях переделать строку:
..........Впоследствии,
внеся изменения в этот файл, можно внести изменения и перенастроить
проект на другой микроконтроллер, не изменяя код в файлах модулей.
..........Файл модуля примет вид:
Подключение перемещаемого ассемблерногомодуля к проекту..........Так как речь идёт о применении в проекте нескольких файлов, то нужно создать ещё один файл – основной файл проекта. Создадим простейшую «пустышку». Для понимания того, как задействовать модули, этого достаточно. Вот код основного файла проекта:
..........Создаём для проекта новую папку и размещаем в неё подготовленные файлы. При сборке и компиляции проекта задействуется линкер, поэтому в папку проекта копируем файл скрипта линкера для микроконтроллера PIC16F628A «16f628a.lkr». ..........Воспользовавшись Project Wizard, создаем новый проект. Первоначально у нас задействовано четыре файла, и окно проекта выглядит так: ..........Жмём кнопку «Build All». Всё компилируется. Проверим, что получилось в памяти программ.
..........Сразу
видим, что секции кода (подпрограммы Bin99Dec,Dec99Bin и основная
программа) поменялись местами. На будущее нужно учесть, что при
сборке проекта отдельные секции кода могут быть размещены линкером в
любых адресах. Ну, да на то они и перемещаемые.
..........Если заглянуть в файл регистров проекта (в символьном виде), то ячейки e_rez, d_rez, temp обнаружим в адресах 0x120, 0x121, 0x122, то есть во втором банке ОЗУ. Директива EXTERNЗадействуем модуль в основной программе..........Для того чтобы основной файл смог использовать внешние метки необходимо добавить в него строку:
..........Директива extern даёт понять ассемблеру, что метки, указанные за ней, внешние и находятся в других файлах проекта. Линкеру предстоит найти их при компиляции в других объектных файлах и добавить в таблицу используемых меток основного файла. ..........В первом примере подпрограммы модуля не вызываются, и обращения к ячейкам e_rez, d_rez из основной программы нет. Добавим несколько обращений к программам модуля, усложним немного основную программу.
..........Так как модуль может оказаться в любой странице памяти программ, при вызове подпрограмм используется встроенный макрос lcall. Макрос lcall перед вызовом подпрограммы настраивает соответствующие биты регистра PCLATH на ту страницу памяти программ, где расположена вызываемая подпрограмма. ..........При возврате из подпрограммы регистр PCLATH восстанавливается макрокомандой pagesel и настраивает биты PCLATH в соответствие с текущей страницей памяти программ.
..........Применение этих команд в перемещаемом коде необходимо и также создаёт некоторую избыточность перемещаемого кода по сравнению с кодом абсолютным. ..........После пошагового прогона скомпилированного проекта в отладчике убеждаемся, что ошибок при преобразовании нет. Модуль готов. Единственное, что может смутить на данном этапе, чтобы основной файл смог обращаться к подпрограммам модуля, в него необходимо добавить строку, в которой определены внешние метки подключенного перемещаемого кода.
..........Допустим мы собрали несколько таких модулей, в каждом решили конкретные задачи и разместили по несколько подпрограмм, каждая из которых работает со своими переменными и решает определённую задачу. Например, в одном модуле все подпрограммы, обеспечивающие связь по USART, в другом решается задача измерения температуры с датчика DS18B20, в третьем - подпрограммы работы с внешней памятью, типа 24Cxx, и т.д. ..........Для каждого из модулей придётся добавить в основной файл проекта строки с определением внешних меток директивой extern. Подключаемый заголовочный файл..........Основной способ, применяемый при подключении модуля к проекту, – использование заголовочного файла...........Создаём ещё один файл с названием «bin99dec.inc». В этом файле прописываем:
..........В файле фактически дублируется строка:
..........Комментарии позволят разобраться в функциональном назначении модуля, не заглядывая в код самого модуля, только лишь заглянув в заголовочный файл. Кроме того, в заголовочный файл можно включить макроопределения (макросы) и константы, необходимые для работы с модулем. ..........Создав файл «bin99dec.inc», подключаем его к основному проекту при помощи директивы include. ..........Основной файл проекта в этом случае будет выглядеть так:
..........При компиляции, если модуль подключен к проекту с использованием заголовочного файла, получается тот же результат, что и со строкой объявления внешних меток.
..........Директива include работает по тому же принципу, что и макрос, но в приложении к целому файлу, являясь удобным способом подстановки готового кода всего лишь одной строкой. ..........В окно основного проекта можно добавить файл заголовка, в пункт «Header Files», чтобы можно было одним кликом заглянуть в него при необходимости: Блокировка повторного включениязаголовочных файлов..........Когда модули становятся основным способом программирования, могут возникнуть довольно сложные ситуации, когда один модуль использует подпрограммы другого модуля. Возможно включение в заголовочный файл одного модуля заголовочного файла другого модуля и повтор включенных заголовков. Если произойдёт дублированное включение одного и того же заголовка, то неизбежна ошибка при компиляции:..........«Дублирование меток». ..........Чтобы этого избежать, применяется блокировка повторного включения файла заголовка в один и тот же файл. Для блокировки повторного включения применяются директивы условной компиляции:
..........С помощью этих директив исключается повторное включение заголовка в один тот же файл проекта. Заголовочный файл в нашем примере примет такой вид:
..........Вот и весь принцип построения модулей и подключения их к проектам. Создавать модули несколько сложнее, чем единичный проект. Но выигрыш от такого подхода хорошо осознаётся со временем, когда программ и проектов становится много, и что-то найти в собственных исходниках иногда сложнее, чем переписать весь код по-новой. Готовая собственная библиотека модулей - отличное подспорье, если вы много программируете. ..........Литература: ..........1. MPASM. Как правильно оформлять программы на ассемблере для PIC-контроллеров (пособие для начинающих) ..........2. Как оформлять модули ..........3. MPASM. Руководство пользователя |
||||||||||||||||||||||||||||