Вы находитесь на странице: 1из 35

Микроконтроллеры AVR для начинающих -

1
Микроконтроллеры (далее МК) прочно вошли в нашу жизнь, на просторах интернета можно встретить очень
много интересных схем, которые исполнены на МК. Чего только нельзя собрать на МК: различные
индикаторы, вольтметры, приборы для дома (устройства защиты, коммутации, термометры…),
металлоискатели, разные игрушки, роботы и т.д. перечислять можно очень долго. Первую схему на
микроконтроллере я увидел лет 5-6 назад в журнале радио, и практически сразу же перелистнул страницу,
подумав про себя "все равно не смогу собрать". Действительно, в то время МК для меня были чем то очень
сложным и непонятым устройством, я не представлял как они работают, как их прошивать, и что делать с
ними в случае неправильной прошивки. Но около года назад, я впервые собрал свою первую схему на МК,
это была схема цифрового вольтметра на 7 сегментных индикаторах, и микроконтроллере ATmega8. Так
получилось, что микроконтроллер я купил случайно, когда стоял в отделе радиодеталей, парень передо мной
покупал МК, и я тоже решил купить, и попробовать собрать что-нибудь. В своих статьях я расскажу вам
про микроконтроллеры AVR фирмы ATMEL, научу вас работать с ними, рассмотрим программы для
прошивки, изготовим простой и надежный программатор, рассмотрим процесс прошивки и самое главное
проблемы, которые могут возникнуть и не только у новичков.

Основные параметры некоторых микроконтроллеров семейства AVR:

Микроконтролле Память Память Память Порты U


Частота
р FLASH ОЗУ EEPROM ввода/вывода питания
ATmega48 4 512 256 23 2,7-5,5 0-10-20
ATmega48V 4 512 256 23 1,8-4,8-5,50-4-10
ATmega8515 8 512 512 35 4,5-5,5 0-16
ATmega8515L 8 512 512 35 2,7-5,5 0-8
ATmega8535 8 512 512 32 4,5-5,5 0-16
ATmega8535L 8 512 512 32 2,7-5,5 0-8
ATmega8 8 1K 512 23 4,5-5,5 0-16
ATmega8L 8 1K 512 23 2,7-5,5 0-8
ATmega88 8 1K 512 23 2,7-5,5 0-10-20
ATmega88V 8 1K 512 23 4,5-5,5 0-4-10
ATmega16 16 1K 512 32 4,5-5,5 0-16
ATmega16L 16 1K 512 32 2,7-5,5 0-8
ATmega32 32 2K 1K 32 4,0-5,5 0-16
ATmega32L 32 2K 1K 32 2,7-5,5 0-8

Дополнительные параметры МК AVR mega:

Рабочая температура:  -55…+125*С
Температура хранения:  -65…+150*С
Напряжение на выводе RESET относительно GND: max 13В
Максимальное напряжение питания: 6.0В
Максимальный ток линии ввода/вывода: 40мА
Максимальный ток по линии питания VCC и GND: 200мА
Расположение выводов моделей ATmega 8X

Расположение выводов моделей ATmega48x, 88x, 168x


Расположение выводов у моделей ATmega8515x

Расположение выводов у моделей ATmega8535x


Расположение выводов у моделей ATmega16, 32x

Расположение выводов у моделей ATtiny2313

В конце статьи прикреплён архив с даташитами на некоторые микроконтроллеры

Установочные FUSE биты MK AVR 

BODEN BODLEVEL BOOTRST BOOTSZ0 BOOTSZ1 CKSEL0 CKSEL1 SPIEN


CKSEL2 CKSEL3 EESAVE FSTRT INCAP RCEN RSTDISBL SUT0
SUT1              

Запомните, запрограммированный фьюз – это 0, не запрограммированный – 1. Осторожно  стоит относиться к


выставлению фьюзов, ошибочно запрограммированный фьюз может заблокировать микроконтроллер. Если
вы не уверены какой именно фьюз нужно запрограммировать, лучше на первый раз прошейте МК без
фьюзов.

Самыми популярными микроконтроллерами у радиолюбителей являются ATmega8, затем идут ATmega48,


16, 32, ATtiny2313 и другие. Микроконтроллеры продаются в TQFP корпусах и DIP, новичкам рекомендую
покупать в DIP. Если купите TQFP, будет проблематичнее их прошить, придется купить или изготовить
переходник и паять плату т.к. у них ножки располагаются очень близко друг от друга. Советую
микроконтроллеры в DIP корпусах, ставить на специальные панельки, это удобно и практично, не придется
выпаивать МК если приспичит перепрошить, или использовать его для другой конструкции.

Почти все современные МК имеют возможность внутрисхемного программирования ISP, т.е. если ваш
микроконтроллер запаян на плату,  то для того чтобы сменить прошивку нам не придется выпаивать его с
платы.

Для программирования используется 6 выводов:


RESET - Вход МК
VCC - Плюс питания, 3-5В, зависит от МК
GND - Общий провод, минус питания.
MOSI - Вход МК (информационный сигнал в МК)
MISO - Выход МК (информационный сигнал из МК)
SCK - Вход МК (тактовый сигнал в МК)

Иногда еще используют вывода XTAL 1 и XTAL2, на эти вывода цепляется кварц, если МК будет работать от
внешнего генератора, в ATmega 64 и 128 вывода MOSI и MISO не применяются для ISP программирования,
вместо них вывода MOSI подключают к ножке PE0, a MISO к PE1.  При соединении микроконтроллера с
программатором, соединяющие провода должны быть как можно короче, а кабель идущий от программатора
на порт LPT так-же не должен быть слишком длинным.

В маркировке микроконтроллера могут присутствовать непонятные буквы с цифрами, например Atmega 8L


16PU, 8 16AU, 8A PU и пр. Буква L означает, что МК работает от более низкого напряжения, чем МК без
буквы L, обычно это 2.7В. Цифры после дефиса или пробела 16PU или 8AU говорят о внутренней частоте
генератора, который есть в МК. Если фьюзы выставлены на работу от внешнего кварца, кварц должен быть
установлен на частоту, не превышающей максимальную по даташиту, это 20МГц для ATmega48/88/168, и
16МГц для остальных атмег.

Первые цифры в названии микроконтроллера обозначают объем FLASH ПЗУ в килобайтах, например
ATtiny15 – 1 Кб, ATtiny26 – 2 Кб, AT90S4414 – 4 Кб, Atmega8535 – 8 Кб, ATmega162 – 16Кб, ATmega32 – 32 Кб,
ATmega6450 – 64Кб, Atmega128 – 128Кб.

Иногда встречаются схемы, где применены микроконтроллеры с названиями типа AT90S… это старые
модели микроконтроллеров, некоторые из них можно заменить на современные, например:
AT90S4433 – ATmega8
AT90S8515 – ATmega8515
AT90S8535 – ATmega8535
AT90S2313 – ATtiny2313
ATmega163 – ATmega16
ATmega161 – ATmega162
ATmega323 – ATmega32
ATmega103 – ATmega64/128

ATmega 8 имеет несколько выводов питания, цифровое – VCC, GND и аналоговое – AVCC, GND. В
стандартном включении обе пары выводов соединяют параллельно, т.е. вместе. Микроконтроллеры AVR не
любят повышенного напряжения, если питание выше 6 вольт, то они могут выйти из строя. Я обычно
применяю маломощный стабилизатор напряжения на 5 вольт, КР142ЕН5 или 78L05. Если напряжение
питания слишком низкое, то МК не прошьется, программа будет ругаться и выдавать ошибки (к примеру -24 в
PonyProg).

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

Микроконтроллеры AVR для начинающих -


2
Собираем программатор
Итак, после первой части статьи микроконтроллер у нас есть, теперь нам нужен программатор.
Программатор советую собирать так называемый STK-200, состоит он всего лишь из одной микросхемы,
данный программатор будет лучше чем PonyProg. У пони прога есть единственный плюс (а может быть и
минус), он не дает запрограммировать некоторые фьюзы, которые иногда еще называют опасными
(например RSTDSBL, DWEN, SPIEN). Если случайно запрограммировать такой опасный фьюз, то вы
отключите возможность пользоваться ISP программатором, МК заблокируется, для восстановления МК нужен
будет параллельный программатор (высоковольтный).

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


программатора STK-200, который так-же построен на нескольких проводках, собирать его я так же не
рекомендую, по той причине, что таким программатором очень легко спалить LPT порт, для сравнения - COM
порт спалить в разы труднее. Да и потом, программируя "5ти проводками" можно случайно замкнуть эти
провода между собой, спалить МК или опять же порт. Использование буферного повторителя в микросхеме
улучшает крутизну фронтов сигнала, а так же бережет LPT порт, от случайных КЗ и перенапряжений.

Ниже представлена схема программатора STK200


Как видите, схема очень простая, построена всего лишь на 1 микросхеме, отечественный аналог этой
микросхемы КР1533АП5. В схеме присутствуют 3 светодиода: питание, чтение, запись. Длина соединяющих
проводов должна быть как можно короче.

На фото, на плате программатора присутствует кварц и конденсаторы. Кварц нужно ставить на 4 мГц,
конденсаторы на 5-15 пФ, два штырька рядом со светодиодами на плате, используются для подачи питания,
5 вольт. Если вы будете собирать переходник, то питание подается с платы переходника, т.к. там стоит
"кренка" на 5 вольт, она защитит микроконтроллер и схему от переполюсовки или превышения напряжения.

Собранный программатор выглядит вот так:

К программатору дополнительно нужно будет изготовить плату, на которой будут размещаться панельки под
разные МК, в принципе, можно просто запаивать проводки к нужными выводам МК, как вам удобнее.

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

Главным преимуществом параллельного программатора является способность восстанавливать некоторые


неправильно установленные биты. После этого интерфейс ISP отключается, и при программировании
последовательным программатором в таких случаях выдается сообщение об ошибке.

Выбор программы и прошивка микроконтроллера


Популярными программами для записи программы в МК являются PonyProg и CodeVision AVR. Я пользуюсь
Code Vision AVR (далее CVAVR), но рассмотрим мы обе программы. В принципе, можете пользоваться
любыми другими программами, только запомните одно очень важное замечание, в разных программах
фьюзы могут выставляться по разному, т.е. зеркально.

В первую очередь подключаем программатор в нужный нам порт, только сначала ставим микроконтроллер в
панельку. Питание подавайте после того, как программатор будет подключен к порту. Для того, чтобы
запрограммировать микроконтроллер нам нужны файлы прошивки, файл с расширением .hex, в некоторых
проектах используется еще файл EEPROM .eep. (электрически изменяемая память данных, в нем хранятся
различного рода константы).

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

Прошивка микроконтроллера с помощью PonyProg

1) Скачиваем и запускаем программу PonyProg, появится окошко (возможно проиграется звук), нажимаем ОК.

2) Далее в списке микроконтроллеров нужно выбрать наш МК, кликаем меню "Устройство", затем выбираем
AVRmicro -> ATmega8.
3) Далее, нам нужно выбрать файлы прошивки, для этого кликаем Файл -> Открыть файл программы (FLASH)
… Откроется окошко, где нам нужно выбрать наш файл прошивки с расширением .hex.

4) В окошке не забываем в списке выбрать нужное нам расширение файла, иначе файл прошивки не
обнаружится.
После того как откроем файл, окошко будет заполнено непонятными цифрами и буквами. Это
шестнадцатеричное представление прошивки.
5) Теперь точно также нужно выбрать файл прошивки EEPROM с расширением .eep, если конечно он нужен и
прилагается.

Если вы еще не подключили программатор и не подали на него питание, сделайте это. Не забудьте вставить
МК в панельку, не перепутайте положение в панельке, обратите внимание на насечку или кружок на корпусе
МК, оттуда и начинается отсчет ножек.

6) Кликаем Установки -> Настройка оборудования. Все настройки выставляем как на рисунке ниже, затем
нажимаем кнопку "Проверка" – должно выйти окошко "Тест ОК"
Если этого не произошло и вышла окошко с надписью "Тест ОШИБКА"

Кликаем правой кнопкой мыши на значке "Мой компьютер" и выбираем "Диспетчер устройств" или можно
зайти в Панель управления -> Система -> Оборудование -> Диспетчер устройств.

Появится такое окошко, в списке находим Порты (COM и LPT ), кликаем правой кнопкой мыши на нем и
нажимаем "Обновить конфигурацию оборудования"
Затем закрываем окно, и возвращаемся к PonyProg, повторяем пункт 6, нажимаем "Проверка", должно выйти
окошко с надписью "Тест ОК".

7) Нажимаем "ОК", далее переходим в меню Установки -> Калибровка, нажимаем Yes.

Выйдет сообщение об успешно проведенной калибровке, нажимаем ОК


8) Сейчас нам нужно будет выставить фьюзы, если в вашей конструкции фьюзы не выставляются,
переходите к следующему пункту. Кликаем Команды -> Security and Configuration Bits… откроется окошко для
установки фьюзов

Ставим строго те галочки, которые нам нужно, для моего проекта нужно выставлять следующие фьюзы:
BOOTSZ1, BOOTSZ0, BODEN, CKSEL3, CKSEL2. Затем нажимаем ОК. Фьюзы при программировании нужно
выставлять только те, что указано, если запрограммируете ненужные вам фьюзы, МК может залочиться,
разблокировать его потом будет очень сложно, нужен будет более сложный программатор чем наш, будьте
внимательны! Записывать программу в МК можно без выставления фьюзов – заработает затем устройство
или нет, это другой вопрос, если даже и заработает, может очень сильно тормозить, или будет работать
частично, это ничем не грозит, МК лочится только после неправильно выставленных фьюзов. В некоторых
проектах могут использоваться фьюзы, которых может не оказаться в PonyProg, например SPIEN, этот фьюз
отключает возможность внутрисхемного программирования, поэтому во многих программах не показывается
специально, чтобы вы случайно его не установили.

9) Теперь все готово, и можно прошить микроконтроллер, нажимаем Команды -> Записать все. Если вы еще
не передумали, то в окошке которое вышло, нажимаем "Yes", и начнется процесс записи и проверки.
После чего выйдет сообщение об успешном завершении операции

Все, теперь можно вынимать МК из программатора и проверить наше устройство, если вы все сделали все
как я расписал, все будет работать, Если по какой то причине устройство не заработало, возможно допущены
ошибки при сборке программатора, перепроверьте все и попробуйте еще раз перепрошить МК.

10) Если спустя какое то время вам захочется сменить прошивку, или использовать МК для другого проекта,
то снова вставляем его в программатор, подключаем тот к ПК, открываем программу, кликаем Команды ->
Стереть, программа сотрет все данные с МК.

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

Что касается программы PonyProg, в разных версиях свой список устройств, если в вашей версии программы
вы не можете найти в списке МК скажем ATmega32, скачайте другую версию.

В следующей статье рассмотрим прошивку МК с помощью программы CodeVision AVR а так-же рассмотрим


часто возникающие ошибки и проблемы связанные с МК, постараемся их решить

Печатные платы для программатора прилагаются

Список радиоэлементов

Обозначени Количеств Примечани


Тип Номинал Магазин
е о е

Программатор STK200

ИС буфера, CD74HC24 Поиск в


DD1 1
драйвера 4 Utsource

Конденсато Поиск в
C1 0.1 мкФ 1
р Utsource

Поиск в
R1, R5-R9 Резистор 33 Ом 6
Utsource

Поиск в
R2-R4 Резистор 510 Ом 3
Utsource

Поиск в
VD1 Светодиод красный 1
Utsource

Поиск в
VD2 Светодиод желтый 1
Utsource

Поиск в
VD3 Светодиод зеленый 1
Utsource

XS1 Разъем DBR-25F 1 Поиск в


Обозначени Количеств Примечани
Тип Номинал Магазин
е о е

Utsource

Поиск в
XS2 Разъем IDC-10 1
Utsource

Микроконтроллеры AVR для начинающих -


3
В предыдущей части статьи я рассказывал про прошивку МК с помощью программы PonyProg, теперь
расскажу как прошить с помощью CodeVision AVR (далее CVAVR). Честно говоря, работа в программе не
сильно отличается от PonyProg.

Для начала можете микроконтроллер поставить в панельку программатора, и подключить его к LPT порту,
затем подавайте питание.

Запускаем программу CVAVR

1) Сначала нам нужно настроить порт, заходим в меню Settings -> Programmer.

Откроется окошко, все настройки выставляем как на рисунке ниже


2) Нажимаем ОК, далее заходим в меню Tools -> Chip Programmer

Откроется следующее окно:


Ничего лишнего в этом окошке не трогаем, галочки не ставим и ничего не переключаем.

3) Выбираем нужный нам микроконтроллер из выпадающего меню, я выбрал ATmega8. Если у вас в названии
микроконтроллера после ATmegaX стоит буква, к примеру V или L,  то в списке выбирайте такой же МК, с
такой же буквой.

4) Теперь нам нужно открыть файл прошивки, в этом окошке нажимаем File -> Load FLASH

5) Откроется окно, где нужно будет выбрать файл прошивки с расширением .hex, кстати, не забудьте внизу из
выпадающего меню "Тип файлов" выбрать этот тип файла.
6) Файл EEPROM выбираем точно так-же, для этого нажимаем меню File -> Load EEPROM, расширение этого
файла .eep, если к вашему проекту такой файл не прилагается, значит нужно прошивать только FLASH т.е.
.hex.

Имейте ввиду, что процессы прошивки программы, фьюзов


и ПЗУ (EEPROM) это самостоятельные отдельные процедуры.
И рекомендуется шить сначала программу, затем ПЗУ, затем фьюзы, в принципе что у нас и происходит,
когда мы загружаем все файлы для прошивки, и выставляем фьюзы в окошке.

7) Итак, файлы прошивки мы загрузили, теперь нам нужно выставить фьюзы, для моего проекта фьюзы
следующие: BOOTSZ1, BOOTSZ0, SUT1, CKSEL3, CKSEL2, выставляем их.
8) Затем ставим галочку Program Fuse Bit(s), если вы не поставите галочку – фьюзы не будут записываться.

Чтобы проверить, видит ли наша программа программатор, подключенный к LPT порту, нажимаем кнопку
Reset Chip, на программаторе должны мигнуть светодиоды чтения/записи. Если светодиоды не мигают,
значит нам нужно проделать операции после пункта 6, описанные в предыдущей части статьи.

9) Теперь можно прошить МК, нажимаем кнопку Program All, и начнется процесс прошивки.

Если вы загружали только файл прошивки FLASH, .hex, то по ходу прошивки программа предложит загрузить
файл EEPROM, жмем NO, т.е. НЕТ.
После чего пробегут еще 2 полоски и процесс прошивки завершится

Во время прошивки МК нельзя выключать или перезагружать ПК!

Теперь можете проверить МК, поставив его в панельку вашего устройства. Если вы хотите записать другую
прошивку на этот же МК, новую прошивку можно записать поверх старой, или же стереть сначала старую,
потом записать новую, как вам удобнее, разницы особой нету.

11) Чтобы стереть данные с МК нажимаем Program -> Erase Chip.

В данной программе можно считать данные FLASH или EEPROM с МК, или просмотреть Lock биты,
установленные Fuse биты. Все это в меню Read (считать).

Решение некоторых проблем с AVR

C разными программаторами, и с разными программами возникают разные ошибки, но некоторые ошибки


между собой очень похожи, и устраняются одинаково. Во первых микроконтроллеры подключайте строго к
указанным выводам: RESET, VCC, GND, MOSI, MISO, SCK. Если  спутаете вывода, или забудете припаять
один из контактов, МК не прошьется. Случайно МК в панельку можно поставить спутав вывода, т.е. задом на
перед, от этого МК не сгорит, но и не прошьется конечно тоже.  Напомню еще раз, что у некоторых МК,
например в ATmega 64 и 128 вывода MOSI и MISO не применяются для ISP программирования, вместо них
вывода MOSI подключают к ножке PE0, a MISO к PE1. Напряжение питания не должно быть ниже чем нужно,
иначе МК не будет программироваться, программы будут выдавать ошибки о том, что не могут обнаружить
МК.
Записывать левые программы, например программу, предназначенную для ATmega8 в ATmega48 нельзя.
Бывает, что вы запрограммировали МК и файлы прошивки удалили с компьютера, а найти прошивку чтобы
прошить другой МК не можете найти. В таких случаях просто считываете программу с микроконтроллера,
например с помощью CVAVR и сохраняете его на ПК, затем этой прошивкой прошиваете другой МК.

Если вы случайно запрограммировали какой то фьюз, после чего МК залочился, вспомните, что это был за
фьюз, некоторые МК с неправильно зашитыми фьюзами удается восстановить, есть несколько способов как
это сделать. На К155ЛА3 можно собрать генератор чтобы восстановить МК с запрограммированным
RSTDSBL, если вы выставили фьюзы на работу от внешнего генератора, подавая сигнал на вывод XTAL1
некоторые умудряются таким способом восстановить МК. Также фьюзами можно выставить тактирование от
внешней RC цепочки. В таком случае придется собрать RC цепочку, чтобы опять запустить МК. Ещё есть
фьюзы DWEN, SPIEN..., установив которые, вы отключите возможность пользоваться ISP программатором,
тут поможет только параллельный программатор, другие программаторы (к примеру тритон) или приборы,
которые встречаются на просторах интернета: например этот, или ATmega Fusebit Doctor, данный прибор я
собирал, но почему то он не заработал должным образом, плата все еще валяется где то в ящике, как нибудь
снова надо взяться за него. Честно говоря, у меня до сих пор валяются три залоченных микроконтроллера,
две меги48 и одна мега8, однажды мегу 8ю, которая перестала определяться в программаторе PonyProg
(работает через COM порт) я восстановил собрав программатор STK200. Притом знаете ли, свой первый
купленный микроконтроллер я прошил с первого раза, и он до сих пор работает у меня в лабораторном блоке
питания.

Теги:
 AVR
 Микроконтроллер
 CodeVisionAVR

Арифметические и логические команды


Табл.1. Арифметические и логические команды:

Код
Команда Описание Действие Циклы Флаги ATtiny ATmega
операции
0000 11rd Z,C,S,
add   Rd,Rr Add two Registers Rd←Rd+Rr 1 + +
dddd rrrr N,V,H
Add with Carry two 0001 11rd Z,C,S,
adc   Rd,Rr Rd←Rd+Rr+C 1 + +
Registers dddd rrrr N,V,H
1001 0110
Add Immediate to Z,C,S,
adiw  Rdl,K Rdh:Rdl←Rdh:Rdl+K 2 KKdd ± +
Word N,V
KKKK
Subtract two 0001 10rd Z,C,S,
sub   Rd,Rr Rd←Rd-Rr 1  + +
Registers dddd rrrr N,V,H
sbc   Rd,Rr Subtract with Carry Rd←Rd-Rr-C 1 0000 10rd Z,C,S, + +
two Registers dddd rrrr N,V,H
1010
Subtract Constant KKKK Z,C,S,
subi  Rd,K Rd←Rd-K 1 + +
from Register dddd N,V,H
KKKK
0100
Subtract with Carry
KKKK Z,C,S,
sbci  Rd,K Constant from Rd←Rd-K-C 1 + +
dddd N,V,H
Register
KKKK
1001 0111
Subtract Immediate Z,C,S,
sbiw  Rdl,K Rdh:Rdl←Rdh:Rdl-K 2 KKdd ± +
from Word N,V
KKKK
 
Logical AND 0010 00rd
and   Rd,Rr Rd←Rd AND Rr 1 + +
Registers dddd rrrr
Z,S,N
0111
 
Logical AND KKKK
andi  Rd,K Rd←Rd AND K 1 + +
Register and Constant dddd
Z,S,N
KKKK
 
0010 10rd
or   Rd,Rr Logical OR Registers Rd←Rd OR Rr 1  + +
dddd rrrr
Z,S,N
0110
 
Logical OR Register KKKK
ori   Rd,K Rd←Rd OR K 1 + +
and Constant dddd
Z,S,N
KKKK
 
 
0010 01rd
eor   Rd,Rr Rd←Rd EOR Rr 1 + +
Exclusive OR dddd rrrr
Z,S,N
Registers
 
1001 010d
com   Rd One’s complement Rd←0xFF-Rd 1 + +
dddd 0000
Z,S,N
1001 010d Z,C,S,
neg   Rd Two’s complement Rd←0x00-Rd 1 + +
dddd 0001 N,V,H
0110
KKKK
sbr   Rd,K Set Bit(s) in Register Rd←Rd OR K 1 Z,S,N + +
dddd
KKKK
0111
 
Clear Bit(s) in Rd←Rd AND (0xFF- KKKK
cbr   Rd,K 1 + +
Register K) dddd
Z,S,N
KKKK
1001 010d Z,S,
inc   Rd Increment Rd←Rd+1 1 + +
dddd 0011 N,V
1001 010d Z,S,
dec   Rd Decrement Rd←Rd-1 1 + +
dddd 1010 N,V
 
Test for Zero or 0010 00dd
tst   Rd Rd←Rd AND Rd 1 + +
Minus dddd dddd
Z,S,N
clr   Rd Clear Register Rd←Rd EOR Rd 1 0010 01dd   + +
dddd dddd
Z,S,N
1110 1111
ser   Rd Set Register Rd←0xFF 1 None + +
dddd 1111
 
1001 11rd
mul   Rd,Rr R1:R0←Rd*Rr 2 Z,C - +
dddd rrrr
Multiply Unsigned
0000 0010
muls  Rd,Rr Multiply Signed R1:R0←Rd*Rr 2 Z,C - +
dddd rrrr
Multiply Signed with 0000 0011
mulsu Rd,Rr R1:R0←Rd*Rr 2 Z,C - +
Unsigned  0ddd 0rrr
Fractional Multiply 0000 0011
fmul  Rd,Rr R1:R0←(Rd*Rr)<<1 2 Z,C - +
Unsigned 0ddd 1rrr
Fractional Multiply 0000 0011
fmuls  Rd,Rr R1:R0←(Rd*Rr)<<1 2 Z,C - +
Signed 1ddd 0rrr
fmulsu Fractional Multiply 0000 0011
R1:R0←(Rd*Rr)<<1 2  Z,C - +
Rd,Rr Signed with Unsigned 1ddd 1rrr

Как видно из табл.1, AVR имеют всего 3 разновидности команды сложения. Инструкции add Rd,Rr (Сложение
двух регистров), adc Rd,Rr (Сложение двух регистров с учётом переноса), позволяют складывать как
однобайтовые числа, так числа произвольной разрядности. В последнем случае для связи байтов
используется флаг переноса C в регистре SREG, который устанавливается всякий раз, когда разрядность
суммы превысит 8 бит. Этот перенос должен быть добавлен к сумме старших байтов командой adc Rd,Rr:

?
1 add  R18,R16  ; сложение двухбайтовых чисел
2 adc  R19,R17  ; R19:R18 = R19:R18 + R17:R16

Сложение регистра с константой, к сожалению, отсутствует. И это приносит некоторые неудобства, так как
приходится использовать две команды, одна из которых занесение числа во вспомогательный регистр, а уже
вторая - сложение регистров:

?
1 ldi  R17,0x30 ; заносим в R17 константу 0x30   
2 add  R16,R17  ; R16 = R17 + 0x30

Во многих случаях вышеуказанную проблему можно решить, используя команду adiw Rdl,K (Сложение
константы со словом). Она позволяет прибавить 6-разрядную константу, лежащую в диапазоне 0…63, к одной
из 4х регистровых пар Rdh:Rdl (R25:R24, R27:R26, R29:R28, R31:R30). С её помощью также удобно
реализовать 16-разрядный счётчик событий:

?
1 key_press: 
2    adiw  R24,1    ; инкрементируем счётчик  R25:R24
3    sbis  PINB,0   ; пока на линии 0 порта PORTB низкий
   rjmp  key_press ; логический уровень
4

Из всех арифметических операций, вычитание в AVR - наиболее разнообразно по способам адресации.

Имеется две команды с прямой адресацией РОН:  sub Rd,Rr (Вычитание двух регистров),  sbc Rd,Rr
(Вычитание двух регистров с учётом заема). При совместном использовании они позволяют получить
разность чисел любой разрядности. Для связи байт, при этом, и здесь служит флаг C. Но в отличие от
сложения он устанавливается, когда разность оказалась отрицательной(Rd < Rr) и имеет в этом случае
смысл заема, который должен быть вычтен из разности старших байт командой sbc Rd,Rr:

?
1 sub  R18,R16  ; вычитание двухбайтовых чисел
2 sbc  R19,R17  ; R19:R18 = R19:R18 - R17:R16

Ещё две команды с непосредственной адресацией используются для тех же целей:  subi Rd,K (Вычитание
константы из регистра) и sbсi Rd,K (Вычитание константы из регистра c учётом заёма). Эти инструкции очень
универсальны. Их с одинаковым успехом можно применять как в целях вычитания, так и сложения. В
последнем случае необходимо использовать представление числа K в дополнительном коде (т.е. изменить
знак числа –K = 0xFF+1-K):

?
1 subi  R16,-0x30 ; R16 = R16 + 0x30 = R16 – (-0x30)

Существует также инструкция, работающая в двухбайтовом формате, sbiw Rdl,K (Вычитание константы из


слова). Подобно сложению adiw Rdl,K, с её помощью можно вычесть 6-разрядную K (0…63) из тех же самых
регистровых пар (R25:R24, R27:R26, R29:R28, R31:R30). Главная область применения команды – реализация
счётчика циклов:

?
1 delay: 
2    sbiw  R24,1 ; декрементируем счётчик  R25:R24 пока
3    brne  delay ; его содержимое не станет 0, формируя
   .   ; задержку времени  4*R25:R24 циклов
4

Группа логических инструкций представлена 6-ю командами.


Операция “НЕ” производится по команде com Rd (Дополнение до одного). При этом фактически производится
действие Rd←0xFF-Rd, при котором инвертируются все биты регистра. Команды and Rd,Rr (“Логическое И”
регистров), andi Rd,K (“Логическое И” регистра и константы) и or Rd,Rr (“Логическое ИЛИ” регистров), ori Rd,K
(“Логическое ИЛИ” регистра и константы) производят соответствующие логические операции как с
регистрами, так и регистра с константой. Операция же “Исключающее ИЛИ” возможна только между
регистрами. Для этих целей служит команда eor Rd,Rr (“Исключающее ИЛИ” регистров). Если провести
операцию “Исключающее ИЛИ” регистра с самим собой (eor Rd,Rd), то будет получен нулевой результат
(Rd←Rd EOR Rd = 0). Это свойство часто применяется, когда необходимо отчистить регистр, а команда eor
Rd,Rd может иметь при этом альтернативную форму записи clr Rd (Очистить регистр). Кроме того, с помощью
последовательности всего трех команд eor Rd,Rr можно обменять содержимое двух регистров (команда
обмена РОНов у AVR отсутствует) местами не используя ни одной дополнительной ячейки памяти:

?
1 eor  R16,R17 ; R16 = R16 XOR R17 
2 eor  R17,R16 ; R17 = R17 XOR (R16 XOR R17) = R16 
3 eor  R16,R17 ; R16 = (R16 XOR R17) XOR R16 = R17 

Все описанные логические операции могут использоваться, например, в следующем контексте:

?
1 in   R16,PORTB ;заносим для изменения РВВ PORTB в РОН R16
2 ori  R16,(1«PB0)|(1«PB5)  ;устанавливаем биты 0,5 в R16
3 andi R16,~((1«PB1)|(1«PB2));сбрасываем биты 1,2 в R16
4 out  PORTB,R16 ;выводим изменённое значение R16 в PORTB
5 eor  R16,R16   ;обнуляем R16
6 out  PORTC,R16 ;заносим 0 в PORTC

Как во многих других случаях, у инструкции ori Rd,K существует другая форма написания sbr Rd,K (Установка
бита(ов) в регистре), числящаяся, однако, как самостоятельная команда. Применяя её необходимо помнить,
что, несмотря на аббревиатуру, на самом деле производится именно операция “Логическое ИЛИ” регистра и
битовой маски, а не установка битов в регистре. Так, например, для установки бита n в регистре Rd
правильно писать sbr Rd,1<

?
1 in   R16,PORTB ; заносим для изменения РВВ PORTB в РОН R16
2 sbr  R16,(1«PB0)|(1«PB5) ; устанавливаем биты 0,5 в R16
3 cbr  R16,(1«PB1)|(1«PB2) ; сбрасываем биты 1,2 в R16
4 out  PORTB,R16 ; выводим изменённое значение R16 в PORTB
5 clr  R16       ; обнуляем R16
6 out  PORTC,R16 ; заносим 0 в PORTC

Когда надо установить все биты регистра, то можно воспользоваться инструкцией ser Rd (Установка
регистра), которая заносит константу 0xFF в регистр Rd. Она является частной формой написания команды
ldi Rd,K (Загрузка константы в регистр), относящейся к группе команд пересылки. Подобное действие можно
осуществить и другими способами. Например, в результате команды ori R16,0xFF все биты регистра R16
также будут установленными.

Но в отличие от ser R16 или, что тоже самое, ldi R16,0xFF - будут изменены значения флагов N и Z, что может
повлиять на правильный ход выполнение программы. По той же причине в ряде случаев для обнуления
регистра лучше использовать ldi R16,0 вместо clr R16 (eor R16,R16); все флаги в регистре SREG останутся
неизменными. Единственное, что мешает повсеместному применению команды ldi Rd,K, это ограниченный
набор РОН с которыми она работает. Только регистры R16…R31 могут использоваться с этой инструкцией.
Это существенное ограничение наложено и на все арифметические и логические команды, которые
используют непосредственную адресацию (действие над регистром и константой).

На практике часто необходимо осуществить проверку регистра на нуль. Проще всего это сделать если
произвести операцию “Логическое И” регистра самим с собой (and Rd,Rd). При этом содержимое Rd
останется неизменным, а в регистре SREG будет установлен флаг нулевого результата Z если Rd = 0.
Параллельно с ним будет переопределён и флаг N, который в случае использования знаковых чисел будет
свидетельствовать об отрицательном содержимом регистра. Для инструкции and Rd,Rn где Rd = Rn, может
быть использована  другая форма записи в виде псевдокоманды tst Rd (Проверка на нуль и минус).

В тех случаях, когда необходимо изменить знак однобайтового числа используют команду neg Rd
(Дополнение до двух). Она осуществляет действие Rd←0x00-Rd и используется только для чисел
представленных дополнительном коде.

У AVR имеются также команды прямого и обратного счёта. Первая из них inc Rd (Инкремент) увеличивает на
единицу содержимое регистра, а вторая dec Rd (Декремент), соответственно, уменьшает. Конечно, действия
инкрементирования и декрементирования могут быть заменены, например, на прибавление 1 и вычитание 1
из регистра Rd (subi R16,-1 эквивалентно inc R16, а subi R16,1  тоже, что и dec Rd). Но, несмотря на это обе
команды имеют большое самостоятельное значение. Главная их особенность в том, что они не влияют на
флаг переноса C и тем самым оптимизированы для формирования циклов, в которых он может
использоваться:

?
1         ldi  R16,SIZE  ; производим сдвиг числа,
2         clc            ; находящегося в ОЗУ и состоящего из
3 rleft:  ld   R17,X     ; SIZE байт на один разряд влево
4         rol  R17        
5         st   X+,R18     
6         dec  R16                
7         brne rleft
8         .

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


представления множителя и множимого, существует 6 вариаций данной операции. Произведение,
полученное по команде умножения (2-байтовая величина) заносится в регистровую пару R1:R0. Эта
интересная аппаратная особенность AVR позволяет очень быстро обработать результат, так как он
размещается в РОН и может быть перемещён в любое место всего одной командой пересылки. Следует,
однако, предварительно позаботится о сохранении содержимого регистров R1, R0 если они используются в
программе.

Для умножения целых чисел разработаны три команды: mul Rd,Rr (Умножение беззнаковых чисел), muls
Rd,Rr (Умножение знаковых чисел), mulsu Rd,Rr (Умножение знакового числа на беззнаковое).
Знаковые числами должны быть представлены в дополнительном коде. При этом для первых двух команд
безразлично на месте каких операндов будут находиться множитель и множимое. К одному и тому же
результату приведут, например,  mul R16,R17 и mul R17,R16 (muls R16,R17 и muls R17,R16). В случае mulsu
Rd,Rr на месте Rd обязательно должно стоять знаковое число. Помимо флага Z (один из операндов 0),
умножение оказывает влияние и на флаг переноса C. В него заносится старший бит регистра R1. Таким
образом, в командах  muls Rd,Rr, mulsu Rd,Rr он фактически является флагом знака и устанавливается когда
произведение отрицательное. Необходимо также помнить, что инструкция muls Rd, Rr может использоваться
только с регистрами R16…R31. Ещё меньше поддерживает команда mulsu Rd,Rr, только R16…R23.

Существует ряд специфических приложений, в которых может понадобиться умножения дробных чисел с
фиксированной запятой. Главная задача, где это может быть необходимо, - цифровая обработка сигналов.
Для дробного умножения имеются команды: fmul Rd,Rr (Умножение дробных беззнаковых чисел), fmuls Rd,Rr
(Умножение дробных знаковых чисел), fmulsu Rd,Rr (Умножение дробного знакового числа на беззнаковое).
Все команды работают с числами в формате (1.7). Старший бит регистра является целой частью числа, а
младшие 7 битов отведены под дробную. Произведение приводится к формату (2.14). Для этого
производится сдвиг результата на один разряд влево так, что старший разряд оказывается во флаге C. Все
инструкции дробного умножения работают только с регистрами R16…R23.

Перейти к следующей части: Команды пересылки данных

Теги:
 AVR
 Микроконтроллер

 Синтаксис ассемблера
 Исходный текст программы на языке ассемблера может быть создан в любом текстовом редакторе и
должен быть размещен в файлах с расширениями .asm, .inc или .txt. Удобнее всего для этих целей
использовать встроенный в AVR Studio редактор.
 Алфавит ассемблера состоит из набора символов ACSII. Однако в комментариях к программам
допускается использовать любой удобный язык, основанный на наборе символов в кодировке
UNICODE. Изначально ассемблер не чувствителен к регистру символов. Так, например, между тремя
именами переменной TEMP, temp, Temp нет ни какой разницы.
 Ассемблер может работать с числами, представленными в одной из четырех системах исчисления:
десятичной (примеры чисел: 10, 255),
шеснадцатиричной (примеры чисел: 0x0A, $FF),
двоичной (примеры чисел: 0b00001010, 0b11111111),
восмиричной (примеры чисел: 012, 0377).
 По умолчанию применяется десятичная система, и любое число без префикса будет воспринято
именно как десятичное. Необходимо обратить внимание также на то, что ассемблер AVR не
поддерживает распространенную форму записи 16-тиричных чисел c постфиксом “H” (1AH, 0FFH).
 В табл.1 приведен список операторов, доступных для использования в числовых выражениях.
Операторы расположены в порядке убывания приоритета. Как и всегда, очередность следования
арифметических и логических операций можно изменять, применяя круглые скобки.
 Табл 1. Арифметические и логические операторы, использующие в числовых выражениях:

Обозначение
Описание Пример использования
оператора
Унарный оператор. Изменяет знак числа, используя
- ldi R16,-3   R16 <- 0xFD
дополнительный код.
ldi R16,x1 * x2 ;R16 <-
* Бинарный оператор. Производит умножение двух чисел.
x1*x2 
ldi R16,x1 / x2 ;R16 <-
/ Бинарный оператор. Производит деление двух чисел.
x1/x2 
Бинарный оператор. Возвращает целочисленный остаток ldi R16,x1 % x2 ;R16 <-
%
от деления двух чисел. {x1/x2} 
ldi R16,!x   R16 <- 1 если
Унарный оператор сравнения. Возвращает 1 если число
! x=0
было равно нулю и 0 если число не было равно нулю.
;R16 <- 0 если x ≠ 0 
Унарный оператор. Производит инвертирование всех ldi R16,~0xF0   R16 <-
~
битов в числе. 0x0F
ldi R16,x1 + x2 ;R16 <-
+ Бинарный оператор. Производит сложение двух чисел.
x1 + x2 
ldi R16,x1 - x2 ;R16 <- x1
- Бинарный оператор. Производит вычитание двух чисел.
- x2 
Бинарный оператор. Производит сдвиг выражения
ldi R16,1<<5   R16 <-
<<  стоящего слева от оператора на число разрядов, стоящих
0b00100000
справа от оператора. Сдвиг происходит в левую сторону.
Бинарный оператор. Производит сдвиг выражения
стоящего слева от оператора на число разрядов, стоящих ldi R16,0x50>>4 ;R16 <-
>> 
справа от оператора. Сдвиг происходит в правую 0b00000101
сторону. 
Бинарный оператор сравнения. Возвращает 1 если число, ldi R16,x1 < x2 ;R16 <- 1
<  стоящее слева от оператора, меньше числа, стоящего если x1 < x2 
справа. В противном случае возвращает 0. ;R16 <- 0 если x1 >=x2 
Бинарный оператор сравнения. Возвращает 1 если число, ldi R16,x1 <= x2 ;R16 <-
<= стоящее слева от оператора, меньше или равно числу, 1 если x1 <=x2 
стоящему справа. В противном случае возвращает 0. ;R16 <- 0 если x1 > x2 
Бинарный оператор сравнения. Возвращает 1 если число, ldi R16,x1 > x2 ;R16 <- 1
>  стоящее слева от оператора, больше числа, стоящего если x1 > x2 
справа. В противном случае возвращает 0. ;R16 <- 0 если x1 <=x2 
Бинарный оператор сравнения. Возвращает 1 если число, ldi R16,x1 >= x2 ;R16 <-
>= стоящее слева от оператора, больше или равно числу, 1 если x1 >=x2 
стоящему справа. В противном случае возвращает 0. ;R16 <- 0 если x1 < x2 
Бинарный оператор сравнения. Возвращает 1 если числа, ldi R16,x1 == x2 ;R16 <-
== стоящие по обе стороны от оператора, равны. В 1 если x1 = x2 
противном случае возвращает 0. ;R16 <- 0 если x1 ≠ x2 
Бинарный оператор сравнения. Возвращает 1 если числа, ldi R16,x1 != x2 ;R16 <- 1
!= стоящие по обе стороны от оператора, не равны. В если x1 ≠ x2 
противном случае возвращает 0. ;R16 <- 0 если x1 = x2 
Бинарный оператор. Производит побитовую операцию ldi R16,x1 | x2 ;R16 <- x1|
&
“Логическое И” между двух чисел. x2 
Бинарный оператор. Производит побитовую операцию ldi R16,x1 ^ x2 ;R16 <-
^
“Исключающее ИЛИ” между двух чисел. x1^x2 
Бинарный оператор. Производит побитовую операцию ldi R16,x1 & x2 ;R16 <-
|
“Логическое ИЛИ” между двух чисел. x1&x2 
ldi R16,x1 && x2 ;R16 <-
Бинарный оператор сравнения. Возвращает 1 если числа,
1 если x1 и x2 ≠ 0 
&& стоящие по обе стороны от оператора, не равны нулю. В
;R16 <- 0 если x1 или
противном случае возвращает 0.
x2 = 0
ldi R16,x1 || x2 ;R16 <- 1
Бинарный оператор сравнения. Возвращает 1 если хотя
если x1 или x2 ≠ 0 
|| бы одно из чисел, стоящих по обе стороны от оператора,
;R16 <- 0 если x1 и x2 =
не равно нулю. В противном случае возвращает 0.
0

 Ассемблер содержит также ряд встроенных функций. Некоторые из них, имеющие наибольшее
практическое значение, приведены в табл.2.
 Табл 2. Некоторые встроенные в ассемблер функции:

Обозначение
Описание Пример использования
функции
Функция возвращает младший байт
low() low(0x1234)  = 0x34
выражения.
Функция возвращает второй байт high(0x1234) = byte2(0x1234) =
high(), byte2()
выражения. 0x12
Функция возвращает третий байт
byte3() byte3(0xABCD1234) = 0xCD
выражения.
Функция возвращает четвертый байт
byte4() byte4(0xABCD1234) = 0xAB
выражения.
Функция возвращает младшее слово
lwrd() lwrd(0xABCD1234) = 0x1234
выражения.
Функция возвращает второе слово
hwrd() hwrd(0xABCD1234) = 0xABCD
выражения.
Функция возвращает абсолютное значение
abs() abs(-1000) = 1000
выражения.

 Операторы арифметических и логических операций, а также символы “;”, “.”, “(”, “)”, “[”, “]”, “{”, “}”, “?” -
зарезервированы компилятором. Они не должны встречаться в обозначениях констант, инструкций и
меток. Кроме этого, пользовательские имена, не могут начинаться с цифр и не должны совпадать с
названиями регистров (R0…R31,X,Y,Z) и встроенными в ассемблер функциями.
 Перейти к следующей части: Директивы ассемблера

 Директивы ассемблера
 Директивы представляют собой команды управления компилятором. Объявление каждой из них
должно начинается с точки. Практика показывает, что в любом ассемблере наиболее интенсивно
используется только порядка 10…20 директив. Все остальные либо не являются обязательными,
либо отвечают за управление, лишь незначительными свойствами компилятора. К “основным”,
характерным и для ассемблеров других процессоров, относятся директивы .equ, .org, .def, .сseg, .dseg
и т.д. Ну, а такие директивы, как .dq, .exit, .listmac  в реальных программах встречаются
действительно очень редко. Ниже приведен перечень, описание и примеры использования директив
фирменного ассемблера микроконтроллеров AVR.
 Директива .include подставляет текстовый файл в то место программы, где происходит ее
употребление. В дополнении к этому сам файл подстановки также может содержать директиву
.include. Если файл расположен в директории проекта или в одной из служебных папок, то вместо
полного пути, допускается указывать, лишь ссылку на его имя.
 Директива .include
Синтаксис написания:
.include "{путь к файлу}"
Пример использования:
 ?
1 .include  "m8def.inc" ;вставка стандартного заголовочного файла

 Директива .exit указывает ассемблеру место окончания файла исходного текста. Все операторы,
находящиеся после директивы, становятся невидимыми для компилятора. Если .exit встречается в
подключаемом файле, то сборка проекта заканчивается строкой, где расположена директива .include.
В случае отсутствия директивы .exit, конечной точкой сборки считается последняя строка исходного
текста.
 Директива .exit
Синтаксис написания:
.exit
Пример использования:
 ?
1 .exit ;конец файла

 Директивы .nolist и .list служат для управления файлом листинга, который обычно генерируется после
сборки проекта. Первая из них запрещает, а другая, соответственно, разрешает вывод информации в
файл. Директива .list отменяет действие .nolist и наоборот. 
 Директивы .nolist, .list
Синтаксис написания:
.nolist, .list
Пример использования:
 ?
1 .nolist              ;запретить вывод текста файла “m8def.inc” 
2 .include "m8def.inc" ;в файл листинга программы
3 .list                ;продолжить вывод информации
 Директива .equ присваивает символьному имени некоторое числовое значение. Символьное имя
должно быть уникальным и не может быть изменено в процессе написания программы. Директива не
может применяться для назначения символьных имен регистрам общего назначения.
 Директива .equ
Синтаксис написания:
.equ {символьное имя} = {выражение}
Пример использования:
 ?
1 .equ DDRB  = 0x17    ;присвоение имени DDRB значения 0x17
2 .equ PORTB = DDRB + 1 ;присвоение имени PORTB значения 0x18

 Директива .set производит то же самое действие, что и .equ. Но в отличии от последней, символьное
имя может быть переопределено в любом месте программы.
 Директива .set
Синтаксис написания:
.set {символьное имя} = {выражение}
Пример использования:
 ?
1 .set OFFSET = 0x100    ;присвоение имени OFFSET значения 0x100
2 .
3 .set OFFSET = OFFSET + 1 ;переопределение значения OFFSET

 Директива .def присваивает символьное имя одному из регистров общего назначения. В дальнейшем
ходе программы данное имя может быть отменено директивой .undef. 
 Директивы .def, .undef
Синтаксис написания:
.def {символьное имя} = {регистр}
.undef {символьное имя} 
Пример использования:
 ?
1 .def temp = R16 ;присвоение регистру R16 имя temp
2 .undef temp    ;отмена дальнейшего использования имени temp

 Директивы .db, .dw, .dd, .dq предназначены для резервирования памяти микроконтроллера под
инициализированные данные. Все они могут применяться только в сегментах кода и EEPROM-памяти.
Разница между этими директивами заключается в разрядности, представляемых данных.
Директива .db резервирует байты, .dw – слова, .dd – двойные слова. В редких случаях может так же
оказаться удобным использование директивы .dq, резервирующей 64-разрядные данные.
 Директивы .db, .dw, .dd, .dq
Синтаксис написания:
{метка}: .db {8-разрядные данные}
{метка}: .dw {16-разрядные данные}
{метка}: .dd {32-разрядные данные} 
{метка}: .dq {64-разрядные данные} 
Пример использования:
 ?
1 label:
2  .db 0xFA, 250, -6, 0b11111010
3  .dw 0xFADE, 64222, -1314, 0b1111101011011110
4  .dd 0xFADEEFCA, 4208914378, -86052918
 .dq 0xFADEEFCAEFBACDEF, 18077149609196178927, -521103510453211
5

 Директива .byte резервирует память под неинициализированные данные в сегментах SRAM и


EEPROM.
 Директива .byte
Синтаксис написания:
{метка}: .byte {количество резервируемых данных} 
Пример использования:
 ?
1 .equ PAGESIZE = 0x20
2 buffer: . byte 2*PAGESIZE ;резервирование 64 байт в SRAM
 Директивы .dseg, .eseg, .cseg определяют начало сегментов данных, EEPROM-памяти и кода
соответственно. В исходном файле каждый из сегментов может быть представлен только в одном
экземпляре. В случае если все эти директивы отсутствуют в программе, компилятор по умолчанию
считает, что все операторы расположены в секции кода.
 Директивы .dseg, .eseg, .cseg
Синтаксис написания:
.dseg
.eseg
.cseg
Пример использования:
 ?
1
   .dseg         ;начало сегмента данных
2 buffer: . byte 32 ;резервирование 32 байт под буфер в SRAM
3  
4    .cseg         ;начало сегмента кода
5    rjmp  initial
6    .
string: .db "ATmega8",0 ;строка, хранящаяся во FLASH-памяти
7
8  
   .eseg          ;начало сегмента EEPROM-памяти
9 _var:  .byte 2    ;резервирование 2-ух байт под переменную _var
10 _cnst: .db   0xAA ;резервирование байта под переменную _cnst = 0xAA
11

 Директива .org позволяет задать компилятору начальный адрес в пределах сегментов кода, данных и
EEPROM-памяти. В случае применения в сегменте кода, директива определяет адрес размещения
16-разрядного слова программ.
 Директива .org
Синтаксис написания:
.org  {начальный адрес}
Пример использования:
 ?
1
2 .equ    SRAM_START = 0x60
3 .equ    RAMEND     = 0x045F
4  
5    .dseg          ;начало сегмента данных
   .org SRAM_START ;резервирование 32 байт в SRAM под буфер,
6 buffer: . byte 32 ;начиная с адреса 0x60
7  
8    .cseg         ;начало сегмента кода
9    .org 0         ;вектор сброса по адресу 0
10    rjmp  initial
   .
11    .org 0x50      ;начало основной программы с адреса 0x50
12 initial:          
13    ldi   temp,high(RAMEND) ;инициализация стека
14    out   SPH,temp
15    ldi   temp,low(RAMEND)
16    out   SPL,temp
17    .
18

 Директивы .macro, .endmacro (.endm), определяющие начало и конец макроса соответственно.


 Директивы .macro, .endmacro (.endm)
Синтаксис написания:
.macro {имя макроса}
Пример использования:
 ?
1 .macro set_bit ;объявление макроса установки бита порта
2 sbi  @0,@1   ;установить бит @1 регистра порта @0
3 sbi  @0-1,@1 ;настроить на вывод линию @1 регистра DDRx   
.endm
4
.
5 set_bit PORTB,0 ;установить на линии 0 порта B лог.1
6

 Директива .listmac разрешает расширенный вывод текста макросов в файле листинга. В этом случае
содержимое каждого макроопределения, встретившегося в программе, отображается целиком. Если
директива не используется, то код в нутрии макроса не приводится.
 Директива .listmac
Синтаксис написания:
.listmac
Пример использования:
 ?
1 .listmac ;разрешить разворачивать текст макросов в файле листинга

 Директивы .message, .warning, .error предназначены для вывода в окно сборки проекта
дополнительной информации о ходе компиляции программы. Директива .message генерирует
сообщение для строки, в которой был встречен ее вызов. Применение .warning приводит к выдачи
предупреждения, а .error – к сообщению об ошибки. В последнем случае сборка проекта
прекращается.
 Директивы .message, .warning, .error
Синтаксис написания:
.message "{текст сообщение}"
.warning "{текст предупреждения}"
.error "{текст соодщения об ошибки}"
Пример использования:
 ?
1 .message "Macros has been called here."
2 .warning "Too high frequency!"
3 .error "Wrong macro argument!"

 Группа директив условной компиляции .ifdef, .ifndef, .if, .else, elif, .endif используются для вставок
программного кода в зависимости от различных условий. Директива .ifdef проверяют наличие
объявления некоторого символьного имени. За директивой может следовать набор команд, которые
будут подставлены в текст, если условие проверки “истина” (имя было объявлено). Директива .ifndef
противоположна .ifdef проверяет отсутствие объявления символьного имени. Директива .if производит
подстановку кода, когда выполняется условие сравнения, указанное в качестве ее параметра.
Команды, которые должны выполняться, в случае если условие директивы .if “ложно” –
располагаются после директивы .else. Ветвление типа “если” - “то” может иметь несколько уровней
вложения благодаря директиве .elif. Каждый блок проверки, начинающийся с .ifdef, .ifndef, .if, должен
быть закрыт директивой .endif.
 Директивы if, .ifdef, .ifndef, .else, elif, .endif
Синтаксис написания:
.ifdef {символ} (или .ifndef {символ})
.if {условие}
.else {выражение} (или .elif { условие}) 
.endif
Пример использования:
 ?
1 .macro del_ms ;макрос, формирующий задержку времени в мс
 .ifndef FREQ ;если не объявлена константа FREQ (частота в Гц),
2   .warning "Undefined FREQ constan!" ;выдаем предупреждение и
3   .equ FREQ = 1000000 ;присваиваем по умолчание значение 1 МГц
4  .endif
5  .equ DELAY = (@0*FREQ)/4000 ;величина задания задержки времени
6  .if DELAY > 65535           ;если DELAY размером больше 2 байт, то
  .error “Integer overflow in DELAY!” ;реализация макроса не возможна
7
 .else                              
8   push XL   ;сохраняем в стеке рабочие регистры XL, XH
9   push XH
10   ldi  XH,high(DELAY) ;цикл задержки времени
11   ldi  XL,low(DELAY)
12   sbiw XH:XL,1
13   brne PC-1
14
15
  pop  XH
16
  pop  XL   ;восстанавливаем из стека рабочие регистры XH, XL
17  .endif
18 .endm
19  .
20  .equ FREQ = 2000000 ;объявление тактовой частоты 2 МГц
21  .
del_ms 25          ;формирование задержки времени в 25 мс
22
23

 Перейти к следующей части: Содержимое заголовочного файла

 Структура программы
 Рассмотрим синтаксические особенности ассемблера AVR на примере небольшой тестовой
программы для микроконтроллера ATmega8, приведенной ниже. Программа формирует на выводе
PB2 импульсы с частотой следования ≈ 2.5 Гц при частоте внутреннего RC-генератора 1 МГц.
 ?
1. ;  Тестовая программа для ATmega8.
2. // Светодиод подключен к выводу PB0 микроконтроллера.       
3. /* Биты конфигурации: 
        Low Fuse                     High Fuse    
      BODLEVEL = 1                  RSTDISBL = 1     
      BODEN    = 1                  WDTON    = 1
      SUT1     = 1                  SPIEN    = 1  
      SUT0     = 0                  CKOPT    = 1  
      CKSEL3   = 0 |                EESAVE   = 1  
      CKSEL2   = 0 |_ RC-генератор  BOOTSZ1  = 1        
      CKSEL1   = 0 |     1 МГц      BOOTSZ0  = 1     
      CKSEL0   = 1 |                BOOTRST  = 1   */
     
4.        .nolist ;подключение стандартного заголовочного файла
5.        .include "m8def.inc"
6.        .list
                      
7.        .equ PAUSE  = 50000 ;задержка времени
8.        .equ LED    = PB2  ;вывод для подключения светодиода
             
9.        .def temp   = R16 ;регистр для промежуточных операций
10.       .def buffer = R17 ;регистр для чтения порта
 
11.              .cseg
12.             .org 0
13.          rjmp  initial                                         ;0xC01F
14.          rjmp  0 ;rjmp  service_INT0 ;внешнее прерывание 0      0xCFFE
15.          rjmp  0 ;rjmp  service_INT1 ;внешнее прерывание 1     0xCFFD
16.          rjmp  0 ;rjmp  service_OC2  ;совпадение TCNT2 и OCR2   0xCFFC
17.          rjmp  0 ;rjmp  service_OVF2 ;переполнение TCNT2        0xCFFB
18.          rjmp  0 ;rjmp  service_ICP1 ;захват в ICP1           0xCFFA
19.          rjmp  0 ;rjmp  service_OC1A ;совпадение TCNT1 и OCR1A  0xCFF9
20.          rjmp  0 ;rjmp  service_OC1B ;совпадение TCNT1 и OCR1B  0xCFF8
21.          rjmp  0 ;rjmp  service_OVF1 ;переполнение TCNT1       0xCFF7
22.          rjmp  0 ;rjmp  service_OVF0 ;переполнение TCNT0         0xCFF6
23.          rjmp  0 ;rjmp  service_SPI  ;прерывание от модуля SPI  0xCFF5
24.          rjmp  0 ;rjmp  service_URXC ;получение байта по USART   0xCFF4
25.          rjmp  0 ;rjmp  service_UDRE ;опустошение UDR в USART    0xCFF3
26.          rjmp  0 ;rjmp  service_UTXC ;передача байта по USART    0xCFF2
27.          rjmp  0 ;rjmp  service_ADCC ;прерывание от АЦП          0xCFF1
28.          rjmp  0 ;rjmp  service_ERDY ;завершение записи в EEPROM 0xCFF0
29.          rjmp  0 ;rjmp  service_ACI  ;прерывание от компаратора  0xCFEF
30.          rjmp  0 ;rjmp  service_TWI  ;прерывание от модуля TWI   0xCFEE
31.          rjmp  0 ;rjmp  service_SPMR ;завершение выполнения spm  0xCFED
 
32.             .org 0x20
33. initial: ldi   temp,low(RAMEND)                                  ;0xE50F       
34.          out   SPL,temp                                          ;0xBF0D
35.          ldi   temp,high(RAMEND)                                 ;0xE004
36.          out   SPH,temp                                          ;0xBF0E
37.          cbi   PORTB,LED                                         ;0x98C2
38.          sbi   DDRB,LED                                          ;0x9ABA
39.          ldi   temp,1«LED                                        ;0xE004
 
40. main:    in    buffer,PORTB                                      ;0xB318       
41.          eor   buffer,temp                                       ;0x2710
42.          out   PORTB,buffer                                      ;0xBB18
43.          rcall delay                                             ;0xD001
44.          rjmp  main                                              ;0xCFFB
 
45. delay:   ldi   XH,high(PAUSE)                                    ;0xECB3
46.          ldi   XL,low(PAUSE)                                     ;0xE5A0
47.          sbiw  XH:XL,1                                           ;0x9711
48.          brne  PC-1                                              ;0xF7F1
49.          ret                                                     

 В строках 1…3 приведено 3 возможных варианта оформления комментариев. Комментарий в строке


1, начинающийся со знака “;”, распознается любым ассемблером и поэтому является наиболее
предпочтительным. Комментарии в строках 2 и 3 подобны тем, которые используются в нотациях
языков высокого уровня. Последний из них дает возможность выделить сразу несколько строк
(маркерами начала и конца фрагмента текста является “/*” и “*/” соответственно).
 В строке 5 директивой .include к программе подключается стандартный заголовочный файл
"m8def.inc". Необязательные директивы .nolist и .list (строки 4 и 6 соответственно) запрещают вывод
содержимого подключаемого файла в файл листинга.
 Все директивы допускается размещать в одной строке. Так, например, можно было бы записать
.nolist .include "m8def.inc" .list
 Несмотря на это, желательно придерживаться правильного стиля программирования, в соответствии
с которым в одной строке должна быть расположена только одна директива ассемблера.
 Объявление констант находится в строках 7 и 8. Константе LED присваивается номер линии ввода-
вывода PB2 = 2, описание которой находится в заголовочном файле. В строках 9 и 10 двум рабочим
РОНам назначаются пользовательские имена.
 Секция рабочего кода открывается директивой .cseg в строке 11. Директива .org 0 (строка 12)
устанавливает начальный адрес в памяти программ. В строке 13 должна находиться инструкция
перехода на метку начала основной программы initial.
 Метка представляет собой адрес в пределах секции кода или данных. В ассемблере AVR она должна
быть записана в начале строки и завершаться в конце двоеточием.
 Конечно, адрес в команде может быть указан и явно. В данном примере его можно задать
безошибочно (rjmp 0x20 вместо rjmp initial). Но, это возможно только потому, что директива .org 0x20
(строка 32) заставляет компилятор поместить команду в строке 33 по адресу 32-го слова FLASH-
памяти программ. Однако, в большинстве случаев, адрес размещения той или иной команды заранее
неизвестен и, кроме того, он может изменяться по мере того, как в программу будут вноситься
изменения. Именно поэтому предпочтительней использовать метки. Назначение их адресов
производится автоматически на этапе компиляции. Да и символьные имена меток, предоставляют об
объектах намного больше информации, чем просо какие-то числа.
 На месте каждого неиспользуемого в программе прерывания желательно поставить “заглушки” в виде
команды возврата rjmp 0 или reti, как это сделано в строках 14…31.
 В строках 33…39 производится инициализация ресурсов микроконтроллера. Любая программа
обязательно должна начинаться с установки начального значения указателя стека (если он имеется).
В строках 33…36 в SPH:SPL заносится значение RAMEND = 0x045F (вершина стека перемещается в
самый верх SRAM). Для выделения младшего и старшего полубайтов константы RAMEND
используются встроенные функции low(RAMEND)=0x5F и high(RAMEND)=0x04 соответственно.   
 Далее разряд LED в регистре данных порта B сбрасывается на 0 (стока 37), а сама линия управления
светодиодом настраивается на вывод (стока 38). В строке 39 в регистр temp заносится константа 1<
 ?
1 ldi temp,(1«PB0)|(1«PB5) или ldi temp,(1«PB0)+(1«PB5)   ;temp <- 0b00100001 
2 out DDRB,temp                                           ;DDRB <- temp 
 Код в строках 40…44 представляет собой тело основной программы. В регистр buffer считывается
текущее состояние порта данных PORTB (строка 40). Далее между содержимым buffer и temp
производится операция “Исключающее ИЛИ” (строка 41), после чего модифицированное содержимое
buffer снова выводится в порт (строках 42). В результате такого действия логический уровень на
линии PB2 изменится на противоположный. В строке 43 происходит вызов подпрограммы задержки
времени delay (≈200 мс), а в строке 44 расположена инструкция перехода на метку main. Так
образуется основной цикл программы, в котором происходит постоянное повторение операторов в
строках 40…44: инвертирование уровня на выводе LED, задержка, затем снова инвертирование и т.д.
 Внутри подпрограммы delay в регистровую пару XH:XL заносится число PAUSE = 50000,
определяющее длительность задержки времени (строки 45, 46). В строке 47 из содержимого XH:XL
вычитается 1. Команда условного перехода в строке 48 проверяет, значение флага Z из SREG. Если Z
= 1 (результат предыдущей операции не равен нулю), то управление передается на строку 47. Так
будет повторяться до тех пор, пока не выполнится условие XH:XL = 0 (т.е. пока не пройдет 50000
циклов вычитания). Команда выхода ret в строке 49 возвращает управление в то место основной
программы, с которого произошел вызов delay.
 Отдельно стоит обратить внимание на встроенную в ассемблер переменную PC в строке 48. Она
представляет собой текущее содержимое программного счетчика. Адрес PC-1 будет указывать на
предыдущее слово в памяти программ микроконтроллера. В данном случае в этом слове находится
команда sbiw XH:XL,1. Использовать PC очень удобно для программных переходов в небольших
пределах. Однако здесь всегда необходимо помнить, что у микроконтроллеров AVR имеются
команды, которые имеют размер в 2 16-разрядных слова программ (lds Rd,k, jmp k и др.), из-за чего
при вычислении смещения необходимо будет добавлять\отнимать к PC значение 2 вместо 1 на
каждую такую инструкцию.
 Справа, в листинге приведен машинный код, который будет сгенерирован после компиляции
программы.
 Перейти к следующей части: Макросы