Продолжим разбор теоретических основ, без которых невозможно полноценное создание программ.
Основной синтаксис AVRASM совместим с AVRASM2 с исключениями, отмеченными ниже:
• Ключевые слова
• Директивы препроцессора
• Комментарии
• Продолжения строк
• Строки и символьные константы
• Составные инструкции в строке
Содержание статьи / Table Of Contents
Директивы препроцессора. AVRASM2 считает директивами препроцессора все строки, начинающиеся с '#' (или первый непустой символ в строке, так как пробелы и символы табуляции игнорируются).
Комментарии. Дополнительно к классическим комментариям Ассемблера, начинающимся с ';', AVRASM2 признает комментарии в Cи-стиле:
. . . . . . . . . . ; Остальная часть строки является комментарием.
// Подобно ';' остальная часть строки является комментарием.
/* Блок комментариев может располагаться в нескольких строках.
Этот стиль комментариев не может быть вложенным */
Ассемблер распознает разделители комментария (';') в стиле AVRASM, а также комментарии Cи-стиля. Однако ';' используется в синтаксисе языка Cи, что может привести к конфликту при использовании ';' в качестве разделителя комментария, поэтому не рекомендуется использовать комментарии в стиле Ассемблера вместе с директивами препроцессора AVRASM2.
Продолжение строки. Подобно Cи, строки исходных кодов могут быть продолжены посредством '\ '– обратной косой черты в конце строки. Это особенно полезно для длинных макроопределений препроцессора и для длинных директив .db.
Пример:
.db 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 11,12, 21, 214,235,634, \n', 0, 2, \
12, 3,"’это продолжение верхней строки", '\n', 0, 3, 0
Строки и символьные константы. AVRASM2 понимает строки и символы так же, как AVRASM, кроме того, служебные символы, которые не поддерживаются AVRASM. Строка, заключенная в двойные кавычки ("), может быть использована только вместе с директивой .db. Строка передается буквально, никакие служебные символы и NUL-окончания не распознаются. Символьные константы, заключенные в одиночные кавычки ('), могут использоваться везде, где допустимо целое выражение. Служебные символы в Cи-стиле распознаются с тем же значением, как в Cи
Ассемблером также распознаются \ooo (ooo = восьмеричное число) и \xhh (hh = шестнадцатеричное число).
Примеры:
.db "Hello\n"
// - эквивалент:
.db 'H', 'e', 'l', 'l', 'o', '\\', 'n,
Для того чтобы создать эквивалент Cи-строки "Привет, мир \n", делают следующим образом:
.db " Hello, world", '\n', 0
Составные инструкции в строке. AVRASM2 допускает составные инструкции (команды) и директивы в строке, но их использование не рекомендовано. Это нужно для того, чтобы поддерживать распаковку (расширение) многострочных макроопределений препроцессора.
Операнды. Дополнительно к операндам AVRASM, AVRASM2 поддерживает выражения с плавающей точкой.
Ассемблер поддерживает ряд операторов, которые перечислены в таблице (чем выше положение в таблице, тем выше приоритет оператора). Выражения могут заключаться в круглые скобки, такие выражения вычисляются перед выражениями за скобками.
↑ Операторы
↑ Функции AVRASM
В Ассемблере определены следующие функции:LOW (выражение) – возвращает младший байт выражения;
HIGH (выражение) – возвращает второй байт выражения;
BYTE2 (выражение) – то же, что и функция HIGH;
BYTE3 (выражение) – возвращает третий байт выражения;
BYTE4 (выражение) – возвращает четвёртый байт выражения;
LWRD (выражение) – возвращает биты 0-15 выражения;
HWRD (выражение) – возвращает биты 16-31 выражения;
PAGE (выражение) – возвращает биты 16-21 выражения;
EXP2 (выражение) – возвращает 2 в степени (выражение);
LOG2 (выражение) – возвращает целую часть log2(выражение).
↑ Функции AVRASM2
Следующие функции определены только в AVRASM2:INT (выражение) – преобразовывает выражение с плавающей точкой в целое (т.е. отбрасывает дробную часть);
FRAC (выражение) – выделяет дробную часть выражения с плавающей точкой (т.е. отбрасывает целую часть);
Q7 (выражение) – преобразовывает выражение с плавающей точкой в форму пригодную для инструкций FMUL/ FMULS/FMULSU (знак + 7-битовая дробная часть);
Q15 (выражение) – преобразовывает выражение с плавающей точкой в форму пригодную для инструкций FMUL/ FMULS/FMULSU (знак + 15-битовая дробная часть);
ABS ( ) – возвращает абсолютную величину постоянного выражения;
DEFINED (символ) – возвращает «истина», если символ прежде определен директивами .equ, .set или .def. Обычно используется вместе с директивами if (.if defined(foo)), но может быть использовано в любом контексте. В отличие от других функций DEFINED(символ) требует наличия круглых скобок вокруг своего аргумента.
Ассемблер AVRASM2 различает регистр символов (AVRASM не различает).
Ну, вот выложил минимум теории необходимой для программирования современных контроллеров. (Отрывки из учебного пособия по ассемблеру, автор Зубарев Александр Александрович).
Если вглядываться в последние тенденции, то видно, что ассемблер унифицируется и разные производители вынуждены “приходить к общему знаменателю”, а именно аналогии языка Си.
↑ Снова к практике!
Чтобы быстро писать программы, программист создает свои библиотеки – небольшие микропрограммы в виде сборок - макроопределений, например микропрограммы задержки, или модули инициализации ЖКИ. Удержать в голове множество таких микропрограмм физически невозможно, тем более, что многие программисты даже не помнят все команды, от силы 40-60, а остальные берут из справочника по мере необходимости. Проще один раз написать микропрограмму и использовать в виде макроопределения при создании других программ. Макроопределению можно дать любое произвольное название так, как вам нравиться. Чтобы пользоваться готовыми макроопределениями достаточно нескольких директив. Вспомним их..INCLUDE вложить другой файл
Встретив директиву INCLUDE, компилятор открывает указанный в ней файл, компилирует, его пока файл не закончится или не встретится директива EXIT, после этого продолжает компиляцию начального файла со строки, следующей за директивой INCLUDE. Вложенный файл может также содержать директивы INCLUDE.
Синтаксис:
.INCLUDE "имя_файла"
Пример:
; файл iodefs.asm:
.EQU sreg = 0x3f ; Регистр статуса.
.EQU sphigh = 0x3e ; Старший байт указателя стека.
.EQU splow = 0x3d ; Младший байт указателя стека.
; файл incdemo.asm
.INCLUDE iodefs.asm ; Вложить файл.
in r0,sreg ; Прочитать регистр статуса.
.MACRO начало макроса
С директивы MACRO начинается определение макроса. Макрос (макроопределение) – это микропрограмма, состоящая из последовательности операторов, которые несколько раз встречаются в тексте программы. Эта последовательность операторов включается в тело макроса. Кроме этого, макрос имеет заголовок, состоящий из имени и списка параметров, и окончание. После того как макрос определен, повторяющиеся участки кода в тексте программы заменяются на его имя. Это сокращает текст программы. При встрече имени макроса позднее в тексте программы компилятор заменит его имя на операторы из тела макроса. Таким образом, исполнение макроса заключается в его «расширении».
Макрос в AVRASM может иметь до 10 параметров, к которым в его теле обращаются через @0 – @9. Макрос в AVRASM2 может иметь неограниченное число параметров.
При вызове параметры перечисляются через запятые. Определение макроса заканчивается директивой ENDMACRO.
По умолчанию в листинг включается только вызов макроса, для разворачивания макроса необходимо использовать директиву LISTMAC. Макрос в листинге показывается знаком +.
Синтаксис:
.MACRO имя_макроса
Пример:
.MACRO SUBI16 ; Начало макроопределения.
subi @1,low(@0) ; Вычесть младший байт параметра 0 из параметра 1.
sbci @2,high(@0); Вычесть старший байт параметра 0 из параметра 2.
.ENDMACRO ; Конец макроопределения.
.CSEG ; Начало программного сегмента.
SUBI16 0x1234,r16,r17 ; Вычесть 0x1234 из r17:r16.
Пример:
Создадим макрос под именем write
.macro write
ldi @0,@5
ldi @2,@3
ldi @4,@1
.endm
Вызываем макрос и прописываем параметры
;
write r16,0x1,r17,0x2,r18,0x3
;
После выполнения макроса в регистры записываются числа
R16=3
R17=2
R18-1
.ENDM
.ENDMACRO конец макроса
Директива определяет конец макроопределения и не принимает никаких параметров. Для информации по определению макросов смотрите директиву MACRO.
Синтаксис:
.ENDMACRO
.ENDM
Пример:
.MACRO SUBI16 ; Начало определения макроса.
subi r16,low(@0) ; Вычесть младший байт первого параметра.
sbci r17,high(@0) ; Вычесть старший байт первого параметра.
.ENDMACRO
.LISTMAC включить разворачивание макросов в листинге
После директивы LISTMAC компилятор будет показывать в листинге содержимое макроса. По умолчанию в листинге показываются только вызов макроса и передаваемые параметры.
Синтаксис:
.LISTMAC
Пример:
.MACRO MACX ; Определение макроса.
add r0,@0 ; Тело макроса.
eor r1,@1
.ENDMACRO ; Конец макроопределения.
.LISTMAC ; Включить разворачивание макросов.
MACX r2,r1; Вызов макроса (в листинге будет показано тело макроса).
Рассмотрим практический пример в нашей последней программе (бегущие огни).
Если мы зададим программе задержки макроопределение PAUSE в виде
MACRO PAUSE
dec @0
brne PC-1
dec @1
brne PC-3
dec @2
brne PC-5
.ENDMACRO
То сама программа задержки примет вид:
delay:
PAUSE temp1, temp2, temp3
ret
Но не следует забывать, что фактический объем программы не уменьшается. Написанная таким образом программа приобретает более короткий и понятный для нас вид, но во время компиляции все строки указанные в макроопределении полностью прописываются.
Ну вот, вроде как пора и к программированию возвращаться!
Директивы, операторы и функции посмотрели и запомнили.
Теперь нужно инициализировать микроконтроллер. Под инициализацией понимается активация встроенных устройств, установка векторов прерываний, подключение портов, полная очистка оперативной памяти от возможного “мусора”. Конечно - же при корректно составленной программе вроде–бы и никакой очистки не нужно, но почему-то в силу неясных причин, нередко без начальной очистки ОЗУ, контроллер виснет или начинает вести себя непредсказуемо...
Чтобы избежать этого, нам нужно очистить область стека(RAMEND), область данных ОЗУ (SRAM_START-RAMEND), область регистров общего назначения(r0–r31).
Процедуры очистки выглядят следующим образом:
Очистка Стековой памяти
ldi r16,low(RAMEND);
out SPL,r16;
.if (RAMEND)>=0×0100;
ldi r16,high(RAMEND);
out SPH,r16;
.endif;
.ENDM;
Очистка области данных ОЗУ
LDI ZL,Low(SRAM_START); Адрес начала ОЗУ в индекс
LDI ZH,High(SRAM_START)
CLR R16 ; Очищаем R16
Flush:
ST Z+,R16 ; Сохраняем 0 в ячейку памяти
CPI ZH,High(RAMEND+1); Достигли конца оперативки?
BRNE Flush ; Нет? Крутимся дальше!
CPI ZL,Low(RAMEND+1); А младший байт достиг конца?
BRNE Flush
CLR ZL ; Очищаем индекс
CLR ZH
Очистка регистров общего назначения
LDI ZL, 30 ; Адрес самого старшего регистра
CLR ZH ; Обнуляем
DEC ZL ; Уменьшаем адрес на единицу
ST Z, ZH ; Записываем в регистр 0
BRNE PC-2 ; Зацикливаемся до полного обнуления
Все три микропрограммы можно записать как макроопределения, поместить в один отдельный файл, каждый под своим названием. Сам файл можно вызвать с помощью директивы .INCLUDE, после чего становятся доступными макроопределения.
В процессе инициализации, можно задать режим полноценного использования регистров r0-r15, так же как r16-r31. Макроопределение будет выглядеть следующим образом:
.MACRO LDIL
PUSH R17; Сохраним значение одного из старших регистров в стек.
LDI R17,@1; Загрузим в него наше непосредственное значение
MOV @0,R17; Перебросим значение в регистр младшей группы.
POP R17 ; Восстановим из стека значение старшего регистра.
.ENDM
Теперь с помощью одной команды LDIL rX,Y
можно легко записать в регистр rX, любое 1байтное число Y,
где rX может быть r0-r15
(Не забываем, что созданная нами команда LDIL rX,Y в пять раз медленнее настоящей команды LDI и занимает объем в пять строк…)
С помощью макросов (макроопределений) можно разрешить множество неоднозначных ситуаций, связанных с адресацией, управлением устройств ввода вывода, и.т.п. создавая свою библиотеку…
Далее рассмотрим прерывания, работу встроенных таймеров,
использование ШИМ
использование ШИМ
Камрад, рассмотри датагорские рекомендации
🌼 Полезные и проверенные железяки, можно брать
Опробовано в лаборатории редакции или читателями.