8-900-374-94-44
[email protected]
Slide Image
Меню

Машинные коды команд avr: AVR програмирование в кодах / AVR / Сообщество EasyElectronics.ru

Содержание

Устройство и программирование микроконтроллеров AVR. Микроконтроллер и как его победить.

Доброго дня уважаемые радиолюбители!
Приветствую вас на сайте “Радиолюбитель“

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

Микроконтроллер – микросхема, предназначенная для управления электронными устройствами, или по другому – простенький компьютер (микро-ЭВМ), способный выполнять несложные задачи.

То есть, по сути, микроконтроллер – это устройство, позволяющее воплотить в жизнь наши идеи (даже бредовые), но, естественно, в пределах своих возможностей. И самое главное, воплощение идеи в жизнь достигается не созданием навороченных электронных конструкций, а лишь только, в основном, силой нашей мысли (желаете стать волшебником?).
Наибольшей популярностью у радиолюбителей пользуются два вида микроконтроллеров:
PIC  – фирмы Microchip Technology
AVR – фирмы Atmel

Сразу хочу сделать небольшое отступление и пояснить одну свою позицию. Я не собираюсь ни сейчас, ни потом, рассуждать о достоинствах того или иного вида микроконтроллеров, того или иного программного обеспечения, и вообще всего, что связано с микроконтроллерами, что-то советовать, но а тем более – навязывать читателям. Все это дело вкуса, личных предпочтений и поставленных конечных целей в изучении микроконтроллеров. Ну а так как “необъятное – не объять”, все свое дальнейшее повествование я буду вести применительно к микроконтроллерам AVR и, не очень распространенной, но мной любимой, программы “Algorithm Builder”. У разных типов микроконтроллеров, программ, есть, конечно, различия, но многое у них и общее. А познавать мир микроконтроллеров мы будем так, чтобы потом, полученные знания можно было бы без проблем применить и к PICам, и к любому программному обеспечению. И еще раз напомню, данная серия статей – моя попытка помочь тем, кто впервые услышал о существовании микроконтроллеров и желает постичь работу с ними.

Что нужно для того, чтобы научиться работать с микроконтроллерами? Я бы выделил несколько, на мой взгляд, главных условий:
1. Желание и настойчивость.
Тут все очень просто: есть желание – все получится. А желание с настойчивостью – вообще, вещь суперская.
2. Знание устройства микроконтроллера.
Здесь не важны глубокие знания (да может и вообще не нужны), но знать, что имеется “на борту” микроконтроллера необходимо. Только зная из чего состоит микроконтроллер, какие устройства в нем есть, их возможности, как они работают – только тогда мы сможем использовать возможности микроконтроллера на полную катушку.
3. Знание языка программирования и команд управления микроконтроллером.
Как будет работать микроконтроллер, какие задачи вы на него возлагаете и как он будет их выполнять, определяется заложенной в него программой – программой которую для микроконтроллера составляете вы сами. И на этом пункте мы остановимся несколько подробней, чтобы рассмотреть вопросы, которые могут появиться в будущем.

Программа (в переводе это слово означает – “предписание”) – предварительное описание предстоящих событий или действий.

К примеру, мы хотим, чтобы микроконтроллер мигал светодиодом. Простенькая задача, но тем не менее, для того, чтобы микроконтроллер выполнил эту задачу, мы предварительно должны, шаг за шагом, описать все действия микроконтроллера, написать программу, которую он должен выполнить для получения нужного нам результата – мигающий светодиод. Нечто, вроде такого:
♦ Зажечь светодиод:
— настроить вывод к которому подключен светодиод для работы на вывод информации
— подать на этот вывод логический уровень, который позволит зажечь светодиод
♦ Подождать некоторое время:
— перейти к подпрограмме формирующей паузу (которую тоже нужно “разжевать”)
— по выполнению подпрограммы паузы вернуться в основную программу

♦ Погасить светодиод:
— подать на вывод логический уровень, гасящий светодиод
и так далее.
С термином Программа неразрывно связан другой термин – Алгоритм  (как Волк и Заяц, Том и Джерри).

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

Если в программе мы подробнейшим образом прописываем действия микроконтроллера, то в алгоритме мы определяем порядок действий микроконтроллера, на основе которых мы потом создадим программу. По аналогии с вышеприведенном примером:
♦ Зажечь светодиод
♦ Подождать некоторое время
♦ Погасить светодиод
и так далее.
Таким образом, алгоритм – это предшественник программы. И чем тщательно и продумано будет создан алгоритм, тем проще будет создавать программу.

Итого, программа для микроконтроллера – это последовательность действий микроконтроллера в виде набора команд и инструкций, которые он должен выполнить для достижения поставленных нами целей.

Команды для микроконтроллера имеют вид набора единичек и нулей:
00110101 011000100
так называемые – коды команд, а коды команд – это язык который понимает микроконтроллер.  А для того, чтобы перевести наш алгоритм с русского языка на язык микроконтроллера – в эти самые наборы нулей и единичек, существуют специальные программы.
Эти программы позволяют описать порядок работы для микроконтроллера на более-менее понятном для нас языке, а затем перевести этот порядок на язык понятный микроконтроллеру, в результате чего получается так называемый машинный код – последовательность команд и инструкций (те самые нули и единички) которые только и понимает микроконтроллер. Текст программы, написанный программистом, называется

исходным кодом. Перевод программы с языка программирования (исходного кода) на язык микроконтроллера (машинный код) производится  трансляторами. Транслятор превращает текст программы в машинные коды, которые потом записываются в память микроконтроллера.
В таких программах порядок работы микроконтроллера описывается специальным языком – языком программирования. Язык программирования отличается от нашего, человеческого языка. Если наш язык общения служит в основном для того, чтобы обмениваться информацией, то:

Язык программирования – это способ передачи команд, инструкций, чёткого руководства к действию для микроконтроллера.

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

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

01000110
10010011
01010010

Вряд-ли кто сможет разобраться в таком наборе комбинаций из двух цифр, а труд первых программистов был очень трудоемкий. Для облегчения своей жизни, программисты и стали создавать первые языки программирования. Так вот, чем ближе язык программирования к такому набору нулей и единиц тем больше он “низкого уровня”, а чем дальше от них – тем больше “высокого уровня”.
Самые распространенные языки программирования для микроконтроллеров:
— язык низкого уровня – Ассемблер
– язык высокого уровня – С (Си)
Давайте посмотрим на примере их различия (эти примеры абстрактные).
Допустим нам надо сложить два числа: 25 и 35.
В машинных кодах эта команда может выглядеть так:

00000101 1101001
На языке низкого уровня:
ADD Rd, Rr
На языке высокого уровня:
25+35
Различие языков низкого и высокого уровня видны невооруженным глазом, комментарии, как говорится, излишни.
Но давайте копнемся в этих примерах поглубже. Пример машинного кода разбирать не будем, так как он идентичен примеру на Ассемблере. По своей сути, Ассемблерные команды это те же машинные коды (команды) которым просто, чтобы не заблудиться в нулях и единицах, присвоены буквенные аббревиатуры. Ассемблерной командой ADD Rd, Rr мы ставим микроконтроллеру задачу сложить два числа, которые находятся (а для этого мы должны их туда предварительно записать) – первое в Rd, второе в Rr, а результат сложения поместить в Rd. Как видите мы ставим очень конкретную задачу микроконтроллеру: где взять, что с этим сделать и куда поместить результат. В этом случае мы работаем напрямую с микроконтроллером.
Команда на языке высокого уровня: 25+35, привычная для нас математическая запись, радующая наш глаз. Но в этом случае мы не работаем напрямую с микроконтроллером, мы просто ставим ему задачу сложить два числа. Результат и последовательность действий в данном случае будет тот-же, что и при выполнении ассемблерной команды: сначала эти два числа будут куда-то записаны, затем сложены а результат куда-то помещен.
И вот тут кроется главное отличие языков высокого уровня и низкого уровня. Если в Ассемблере мы контролируем весь процесс (хотим мы того, или нет): мы знаем где записаны эти два числа, и мы знаем где будет находиться результат, то в языке высокого уровня мы процесс не контролируем. Программа сама решает куда предварительно записать числа и куда поместить результат. В большинстве случаев нам это и не надо знать, ведь для нас главное итог – число 60 на выходе. Как результат, программы на языках высокого уровня более читаемы, приятны для глаза и меньше по размеру – ведь нам не приходится “лезть во все дыры” и расписывать каждый шаг микроконтроллера, программа это делает потом за нас, когда компилирует ее – переводит в машинные коды. Но тут есть и минус. Два одинаковых алгоритма написанных на Ассемблере и на Си, после преобразования их в машинные коды будут иметь разный размер: программа написанная на Ассемблере будет на 20-40% короче программы написанной на Си – черт его знает, каким путем идет Си для достижения нужного нам результата.
И бывают случаи, когда нет доверия к языку высокого уровня и в программе на Си делают вставки кода, написанные на Ассемблере.
Профессиональные программисты, как правило, знают несколько языков программирования (или работают в команде, в которой есть специалисты по разным языкам), творчески соединяя их возможности и преимущества в одной программе. Ну а нам, любителям, надо знать хотя бы один язык (для начала), и начинать надо (а я в этом твердо уверен, и никто меня не переубедит) с языка низкого уровня – Ассемблера.

Ну что, я думаю и тут нам все понятно, – язык программирования изучать надо, по-другому – никак.

Команды и инструкции для управления микроконтроллером.
У микроконтроллеров AVR более 130 различных команд, которые позволяют ему реализовать все заложенные в нем возможности. Но сразу скажу – мало кто из любителей знает их все и тем более пользуется всеми. Обычно, в любительской практике хватает знания и половины команд, а то и меньше. Но изучать команды надо. Чем больше команд вы будете знать, тем изощреннее (в хорошем смысле слова) и элегантнее программы будут получаться.

Итого, будем считать, что желание у нас есть, настойчивость проявим, язык изучим, команды освоим, и, как итог, – одержим победу!

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


Следующие статьи:
♦ Микроконтроллер и системы счисления
♦ Микроконтроллер и логические операции
♦ Общее устройство микроконтроллера
♦ Арифметико-логическое устройство и организация памяти – память программ, память данных, энергонезависимая память
♦ Регистры общего назначения, регистры ввода/вывода, стек, счетчик команд
♦ Регистр состояния SREG
♦ Порты ввода/вывода микроконтроллера



Информатика в 2086

Общая информация

Эта глава (и несколько следующих) посвящены программированию 8-битных микроконтроллеров семейства ATMega328, выпускаемых фирмой Atmel и использующихся повсеместно во всевозможных преобразователях сигналов и системах управления. Архитектура AVR, на которой основаны упомянутые микроконтроллеры, достаточно проста для использования в учебных целях и достаточно репрезентативна для того, чтобы составить представление об устройстве ЭВМ и об их программировании на уровне машинных кодов.

Для начала перечислим основные характеристики микроконтроллера ATMega328 и кратко прокомментируем их смысл.

Тактовая частота: регулируемая, не более 20 МГц. Частота (в герцах) – количество тактов, подаваемых на микросхему за одну секунду. Исполнение одной команды занимает один или несколько тактов (это утверждение, вообще говоря, не верно для более современных архитектур; например, для x86_64, используемой на персональных компьютерах).

Оперативная память: 2048 байт. Используется для хранения промежуточных данных, используемых для нужд решаемой задачи. Хранит данные только пока устройство включено. При выключении или перезагрузке устройства данные из оперативной памяти теряются.

EEPROM: 1024 байта. Хранит данные даже при выключенном питании. Используется в первую очередь для параметризации решаемой устройством задачи.

Память программы: 32768 байт. Хранит программу в виде последовательности команд. Большинство команд занимают 2 байта. Незначительное количество команд занимают 4 байта. Обычно последние 1-2 килобайта заняты программой-загрузчиком, которая позволяет обновлять программу, записанную в устройство, при помощи последовательного входа.

Структура оперативной памяти

Первые 256 байт оперативной памяти ATMega328 имеют специальную роль. Байты с 0го по 31й (нумерация байт традиционно начинается с нуля) называются рабочими регистрами. Рабочие регистры могут являться операндами для команд процессора. Всем командам, использующим рабочие регистры, доступны регистры с 16го по 31й. Некоторым командам доступны и первые 16 рабочих регистров. Рабочие регистры имеют традиционные названия: r0, r1, r2, … r31. Пара регистров r26 и r27 известна под названием X. Пара регистров r28 и r29 называется Y. Последняя пара регистров (r30 и r31) называется Z.

Остальные 224 байта называются регистрами ввода-вывода и используются для общения с периферией.

Ассемблер

Ассемблером называется набор человекочитаемых названий для команд процессора; язык программирования, основанный на этих названиях; а также – приложение, предназначенное для перевода человекочитаемого текста программы на этом языке в кодировку, понимаемую процессором, для которого эта программа написана.

Для трансляции программ мы будем использовать приложение-ассемблер GNU as. Для Windows это приложение доступно в составе приложения WinAVR. Для POSIX-систем это приложение входит в состав GNU binutils.

Сборка программы происходит в четыре этапа.

  1. Текст программы записывается в текстовый файл (далее – foobar.S).

  2. Текст программы скармливается приложению avr-as командой

    avr-as foobar.S -o foobar.o
  3. Полученный промежуточный файл скармливается приложению avr-ld командой

    avr-ld foobar.o -o foobar.elf
  4. Полученный файл скармливается приложению avr-objcopy командой

    avr-objcopy -O ihex foobar.elf foobar.hex

Итоговый файл foobar.hex содержит машинный код программы в формате Intel Hex. Этот формат де факто является стандартным для приложений, записывающих программу в память микроконтроллера.

На сайте доступен эмулятор, который позволяет пошагово исполнять программу, записанную в формате Intel Hex, и иллюстрирует некоторые процессы, происходящие при исполнении этой программы.

Пример программы

Приведём пример программы, складывающей числа 12 и 21.

LDI r16, 12
LDI r17, 21
ADD r16, r17

barrier:
RJMP barrier

В ней мы видим 4 команды и метку barrier. Метка не соответствует никакой команде процессора и используется только как аргумент для команд перехода.

Собрав эту программу так, как описано в предыдущем разделе, и выполнив её в эмуляторе, можно наблюдать как сначала в регистр r16 попадает число 12, затем в регистр r17 попадает число 21, затем сумма этих чисел записывается в регистр r16, после чего программа зацикливается на последней инструкции.

Некоторые команды процессоров архитектуры AVR

Здесь перечислены те команды, которые нам понадобятся на первых порах.

Для перемещения данных используются следующие команды:

  • LDI куда, что – перемещение указанного числа в указанный регистр
  • LD куда, откуда – перемещение байта из оперативной памяти в указанный регистр; второй аргумент – либо X, либо Y, либо Z – задаёт пару регистров, в которой записан адрес ячейки памяти
  • MOV куда, откуда – перемещение байта из регистра в регистр
  • ST куда, откуда – перемещение байта из регистра в оперативную память; первый аргумент – X, Y или Z

Арифметические операции (наиболее важные):

  • ADD куда, откуда – прибавление числа из указанного регистра к числу в указанном регистре
  • SUB куда, откуда – вычитание числа из указанного регистра из числа в указанном регистре
  • SUBI куда, что – вычитаение указанного числа из числа в указанном регистре (обратите внимание, что команды ADDI нет; SUBI для большинства целей заменяет ADDI)
  • NEG что – изменение числа в указанном регистре на противоположное
  • AND куда, откуда – поразрядная конъюнкция чисел в указанных регистрах
  • OR куда, откуда – поразрядная дизъюнкция чисел в указанных регистрах
  • EOR куда, откуда – поразрядная исключающая дизъюнкция чисел в указанных регистрах
  • ANDI куда, что – поразрядная конъюнкция указанного числа и числа в указанном регистре
  • ORI куда, что – поразрядная дизъюнкция указанного числа и числа в указанном регистре (обратите внимание, что команды EORI нет)

Сложение и вычитание производятся в арифметике по модулю 256, т. е с «переходом» через 0. Если такой переход произошёл, то этот факт запоминается выставлением 1 в специальный бит переноса (carry flag). Если перехода не было, в бит переноса попадает 0.

  • ADC куда, откуда – прибавление суммы числа из указанного регистра и бита переноса к числу в указанном регистре
  • SBC куда, откуда – вычитание суммы числа из указанного регистра и бита переноса из числа в указанном регистре
  • INC что – увеличение значения в указанном регистре на 1; бит переноса не затрагивается
  • DEC что – уменьшение значения в указанном регистре на 1; бит переноса не затрагивается

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

Эти биты можно исползовать для условных переходов:

  • BREQ метка – переход по метке, если выставлен бит нуля
  • BRNE метка – переход по метке, если не выставлен бит нуля
  • BRCS метка – переход по метке, если выставлен бит переноса
  • BRCC метка – переход по метке, если не выставлен бит переноса

Также есть безусловный переход:

  • RJMP метка – переход по метке

Скажем напоследок, что микроконтроллеры архитектуры AVR поддерживают ещё несколько более сложных арифметических операций, о которых мы поговорим позже.

Упражнения

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

  1. Напишите программу, которая ставит в регистр r0 единицу, если число, записанное в r16, больше числа, записанного в r17, и 0 – в противном случае.
;; Решение. Здесь и далее считается, что входные данные уже заданы.
start:  ;; часть меток используется просто в качестве комментариев
  SUB r17, r16
  BRCC greater_or_equal

less:
  LDI r16, 1
  MOV r0, r16
  RJMP end

greater_or_equal:
  LDI r16, 0
  MOV r0, r16

end:
  RJMP end
  1. Напишите программу, которая ставит в регистр r0 единицу, если число, записанное в r16, не меньше числа, записанного в r17, и 0 – в противном случае.
start:
  SUB r16, r17
  BRCC greater_or_equal

less:
  LDI r16, 0
  MOV r0, r16
  RJMP end

greater_or_equal:
  LDI r16, 1
  MOV r0, r16

end:
  RJMP end
  1. Напишите программу, которая ставит в пару регистров r0 и r1 сумму чисел, записанных в регистры r16 и r17. Младшие 8 бит суммы должны оказаться в регистре r0.
start:
  LDI r18,0
  MOV r1, r18

  MOV  r0, r16
  ADD  r0, r17
  ADC  r1, r18

end:
  RJMP end
  1. Напишите программу, которая ставит в пару регистров r0 и r1 произведение чисел, записанных в регистры r16 и r17. Младшие 8 бит произведения должны оказаться в регистре r0.
start:
  LDI r18, 0
  MOV r0, r18
  MOV r1, r18
  
  ;; это довольно стандартная проверка на равенство:
  SUB  r17, r18  
  BREQ end
  
loop:
  ADD  r0, r16
  ADC  r1, r18
  DEC  r17
  BRNE loop

end:
  RJMP end
  1. Программа получает на вход два числа (в регистрах r16 и r17). На выходе (в регистре r0) должен оказаться остаток от деления r16 на r17. Если r17=0, то в регистре r1 должен оказаться код ошибки 1. В противном случае код ошибки должен быть равен 0.

  2. Программа получает на вход два числа (в регистрах r16 и r17). На выходе (в регистре r0) должно оказаться неполное частное от деления r16 на r17. Если r17=0, то в регистре r1 должен оказаться код ошибки 1. В противном случае код ошибки должен быть равен 0.

  3. Программа получает на вход число (в регистре r16). На выходе (в регистрах r0 и r1) должна оказаться сумма всех целых чисел от 1 до r16. Разряд r0 – младший.

  4. Программа получает на вход число (в регистре r16). На выходе (в регистрах r0 и r1) должно оказаться произведение всех целых чисел от 1 до r16 (взятое по модулю 65536). Разряд r0 – младший.

  5. Программа получает на вход два числа (в регистрах r16 и r17). На выходе (в регистре r0) должен оказаться их НОД. Считается, что НОД пары нулей равен нулю.

Atmel AVR Assembler

Atmel AVR Assembler

Содержание:

Исходные коды

Компилятор работает с исходными файлами, содержащими инструкции, метки и директивы. Инструкции и директивы, как правило, имеют один или несколько операндов.

Строка кода не должна быть длиннее 120 символов.

Любая строка может начинаться с метки, которая является набором символов заканчивающимся двоеточием. Метки используются для указания места, в которое передаётся управление при переходах, а также для задания имён переменных.

Входная строка может иметь одну из четырёх форм:

[метка:] директива [операнды] [Комментарий]
[метка:] инструкция [операнды] [Комментарий]
Комментарий
Пустая строка

Комментарий имеет следующую форму:

; [Текст]

Позиции в квадратных скобках необязательны. Текст после точки с запятой (;) и до конца строки игнорируется компилятором. Метки, инструкции и директивы более детально описываются ниже.

Примеры:

label:════ .EQU var1=100 ; Устанавливает var1 равным 100 (Это директива)
══════════ . EQU var2=200 ; Устанавливает var2 равным 200

test:═════ rjmp test════ ; Бесконечный цикл (Это инструкция)
════════════════════════ ; Строка с одним только комментарием

════════════════════════ ; Ещё одна строка с комментарием

Компилятор не требует чтобы метки, директивы, комментарии или инструкции находились в определённой колонке строки.

═Инструкции процессоров AVR

Ниже приведен набор команд процессоров AVR, более детальное описание их можно найти в AVR Data Book.

Арифметические и логические инструкции

Мнемоника Операнды Описание Операция Флаги Циклы
ADD═ Rd,Rr═ Суммирование без переноса Rd = Rd + Rr═ Z,C,N,V,H,S═ 1
ADC Rd,Rr Суммирование с переносом Rd = Rd + Rr + C Z,C,N,V,H,S 1
SUB Rd,Rr Вычитание без переноса Rd = Rd — Rr Z,C,N,V,H,S 1
SUBI Rd,K8 Вычитание константы Rd = Rd — K8 Z,C,N,V,H,S 1
SBC Rd,Rr Вычитание с переносом Rd = Rd — Rr - C Z,C,N,V,H,S 1
SBCI Rd,K8 Вычитание константы с переносом Rd = Rd — K8 - C Z,C,N,V,H,S 1
AND Rd,Rr Логическое И Rd = Rd ╥ Rr Z,N,V,S═ 1
ANDI Rd,K8 Логическое И с константой Rd = Rd ╥ K8 Z,N,V,S 1
OR Rd,Rr Логическое ИЛИ Rd = Rd V Rr Z,N,V,S 1
ORI Rd,K8 Логическое ИЛИ с константой Rd = Rd V K8 Z,N,V,S 1
EOR Rd,Rr Логическое исключающее ИЛИ Rd = Rd EOR Rr Z,N,V,S 1
COM Rd Побитная Инверсия Rd = $FF — Rd Z,C,N,V,S 1
NEG Rd Изменение знака (Доп. код) Rd = $00 — Rd Z,C,N,V,H,S 1
SBR Rd,K8 Установить бит (биты) в регистре Rd = Rd V K8 Z,C,N,V,S 1
CBR Rd,K8 Сбросить бит (биты) в регистре Rd = Rd ╥ ($FF — K8) Z,C,N,V,S 1
INC Rd Инкрементировать значение регистра Rd = Rd + 1 Z,N,V,S 1
DEC Rd Декрементировать значение регистра Rd = Rd -1 Z,N,V,S 1
TST Rd Проверка на ноль либо отрицательность Rd = Rd ╥ Rd Z,C,N,V,S 1
CLR Rd Очистить регистр Rd = 0 Z,C,N,V,S 1
SER Rd Установить регистр Rd = $FF None 1
ADIW Rdl,K6 Сложить константу и слово Rdh:Rdl = Rdh:Rdl + K6═ Z,C,N,V,S 2
SBIW Rdl,K6 Вычесть константу из слова Rdh:Rdl = Rdh:Rdl — K 6 Z,C,N,V,S 2
MUL Rd,Rr Умножение чисел без знака R1:R0 = Rd * Rr Z,C 2
MULS Rd,Rr Умножение чисел со знаком R1:R0 = Rd * Rr Z,C 2
MULSU Rd,Rr Умножение числа со знаком с числом без знака R1:R0 = Rd * Rr Z,C 2
FMUL Rd,Rr Умножение дробных чисел без знака R1:R0 = (Rd * Rr) << 1 Z,C 2
FMULS Rd,Rr Умножение дробных чисел со знаком R1:R0 = (Rd *Rr) << 1 Z,C 2
FMULSU Rd,Rr Умножение дробного числа со знаком с числом без знака R1:R0 = (Rd * Rr) << 1 Z,C 2

Инструкции ветвления

Мнемоника Операнды Описание Операция Флаги Циклы
RJMP k Относительный переход PC = PC + k +1 None 2
IJMP Нет Косвенный переход на (Z) PC = Z None 2
EIJMP Нет Расширенный косвенный переход на (Z) STACK = PC+1, PC(15:0) = Z, PC(21:16) = EIND None 2
JMP k Переход PC = k None 3
RCALL k Относительный вызов подпрограммы STACK = PC+1, PC = PC + k + 1 None 3/4*
ICALL Нет Косвенный вызов (Z) STACK = PC+1, PC = Z═ None 3/4*
EICALL Нет Расширенный косвенный вызов (Z) STACK = PC+1, PC(15:0) = Z, PC(21:16) =EIND None 4*
CALL k Вызов подпрограммы STACK = PC+2, PC = k None 4/5*
RET Нет Возврат из подпрограммы PC = STACK None 4/5*
RETI Нет Возврат из прерывания PC = STACK I 4/5*
CPSE Rd,Rr Сравнить, пропустить если равны═ if (Rd ==Rr) PC = PC 2 or 3 None 1/2/3
CP Rd,Rr Сравнить Rd -Rr Z,C,N,V,H,S 1
CPC Rd,Rr Сравнить с переносом Rd — Rr — C Z,C,N,V,H,S 1
CPI Rd,K8 Сравнить с константой Rd — K Z,C,N,V,H,S 1
SBRC Rr,b Пропустить если бит в регистре очищен if(Rr(b)==0) PC = PC + 2 or 3 None 1/2/3
SBRS Rr,b Пропустить если бит в регистре установлен if(Rr(b)==1) PC = PC + 2 or 3 None 1/2/3
SBIC P,b Пропустить если бит в порту очищен if(I/O(P,b)==0) PC = PC + 2 or 3 None 1/2/3
SBIS P,b Пропустить если бит в порту установлен if(I/O(P,b)==1) PC = PC + 2 or 3 None 1/2/3
BRBC s,k Перейти если флаг в SREG очищен if(SREG(s)==0) PC = PC + k + 1 None 1/2
BRBS s,k Перейти если флаг в SREG установлен if(SREG(s)==1) PC = PC + k + 1 None 1/2
BREQ k Перейти если равно if(Z==1) PC = PC + k + 1 None 1/2
BRNE k Перейти если не равно if(Z==0) PC = PC + k + 1 None 1/2
BRCS k Перейти если перенос установлен if(C==1) PC = PC + k + 1 None 1/2
BRCC k Перейти если перенос очищен if(C==0) PC = PC + k + 1 None 1/2
BRSH k Перейти если равно или больше if(C==0) PC = PC + k + 1 None 1/2
BRLO k Перейти если меньше if(C==1) PC = PC + k + 1 None 1/2
BRMI k Перейти если минус if(N==1) PC = PC + k + 1 None 1/2
BRPL k Перейти если плюс if(N==0) PC = PC + k + 1 None 1/2
BRGE k Перейти если больше или равно (со знаком) if(S==0) PC = PC + k + 1 None 1/2
BRLT k Перейти если меньше (со знаком) if(S==1) PC = PC + k + 1 None 1/2
BRHS k Перейти если флаг внутреннего переноса установлен if(H==1) PC = PC + k + 1 None 1/2
BRHC k Перейти если флаг внутреннего переноса очищен if(H==0) PC = PC + k + 1 None 1/2
BRTS k Перейти если флаг T установлен if(T==1) PC = PC + k + 1 None 1/2
BRTC k Перейти если флаг T очищен if(T==0) PC = PC + k + 1 None 1/2
BRVS k Перейти если флаг переполнения установлен if(V==1) PC = PC + k + 1 None 1/2
BRVC k Перейти если флаг переполнения очищен if(V==0) PC = PC + k + 1 None 1/2
BRIE k Перейти если прерывания разрешены if(I==1) PC = PC + k + 1 None 1/2
BRID k Перейти если прерывания запрещены if(I==0) PC = PC + k + 1 None 1/2

* Для операций доступа к данным количество циклов указано при условии доступа к внутренней памяти данных, и не корректно при работе с внешним ОЗУ. Для инструкций CALL, ICALL, EICALL, RCALL, RET и RETI, необходимо добавить три цикла плюс по два цикла для каждого ожидания в контроллерах с PC меньшим 16 бит (128KB памяти программ). Для устройств с памятью программ свыше 128KB , добавьте пять циклов плюс по три цикла на каждое ожидание.

Инструкции передачи данных

Мнемоника Операнды Описание Операция Флаги Циклы
MOV Rd,Rr Скопировать регистр Rd = Rr None 1
MOVW Rd,Rr Скопировать пару регистров Rd+1:Rd = Rr+1:Rr, r,d even None 1
LDI Rd,K8 Загрузить константу Rd = K None 1
LDS Rd,k Прямая загрузка Rd = (k) None 2*
LD Rd,X Косвенная загрузка Rd = (X) None 2*
LD Rd,X+ Косвенная загрузка с пост-инкрементом Rd = (X), X=X+1 None 2*
LD Rd,-X Косвенная загрузка с пре-декрементом X=X-1, Rd = (X) None 2*
LD Rd,Y Косвенная загрузка Rd = (Y) None 2*
LD Rd,Y+ Косвенная загрузка с пост-инкрементом Rd = (Y), Y=Y+1 None 2*
LD Rd,-Y Косвенная загрузка с пре-декрементом Y=Y-1, Rd = (Y) None 2*
LDD Rd,Y+q Косвенная загрузка с замещением Rd = (Y+q) None 2*
LD Rd,Z Косвенная загрузка Rd = (Z) None 2*
LD Rd,Z+ Косвенная загрузка с пост-инкрементом Rd = (Z), Z=Z+1 None 2*
LD Rd,-Z Косвенная загрузка с пре-декрементом Z=Z-1, Rd = (Z) None 2*
LDD Rd,Z+q Косвенная загрузка с замещением Rd = (Z+q) None 2*
STS k,Rr Прямое сохранение (k) = Rr None 2*
ST X,Rr Косвенное сохранение (X) = Rr None 2*
ST X+,Rr Косвенное сохранение с пост-инкрементом (X) = Rr, X=X+1 None 2*
ST -X,Rr Косвенное сохранение с пре-декрементом X=X-1, (X)=Rr None 2*
ST Y,Rr Косвенное сохранение (Y) = Rr None 2*
ST Y+,Rr Косвенное сохранение с пост-инкрементом (Y) = Rr, Y=Y+1 None 2
ST -Y,Rr Косвенное сохранение с пре-декрементом Y=Y-1, (Y) = Rr None 2
ST Y+q,Rr Косвенное сохранение с замещением (Y+q) = Rr None 2
ST Z,Rr Косвенное сохранение (Z) = Rr None 2
ST Z+,Rr Косвенное сохранение с пост-инкрементом (Z) = Rr, Z=Z+1 None 2
ST -Z,Rr Косвенное сохранение с пре-декрементом Z=Z-1, (Z) = Rr None 2
ST Z+q,Rr Косвенное сохранение с замещением (Z+q) = Rr None 2
LPM Нет Загрузка из программной памяти R0 = (Z) None 3
LPM Rd,Z Загрузка из программной памяти Rd = (Z) None 3
LPM Rd,Z+ Загрузка из программной памяти с пост-инкрементом Rd = (Z), Z=Z+1 None 3
ELPM Нет Расширенная загрузка из программной памяти R0 = (RAMPZ:Z) None 3
ELPM Rd,Z Расширенная загрузка из программной памяти Rd = (RAMPZ:Z) None 3
ELPM Rd,Z+ Расширенная загрузка из программной памяти с пост-инкрементом Rd = (RAMPZ:Z), Z = Z+1 None 3
SPM Нет Сохранение в программной памяти (Z) = R1:R0 None
ESPM Нет Расширенное сохранение в программной памяти (RAMPZ:Z) = R1:R0 None
IN Rd,P Чтение порта Rd = P None 1
OUT P,Rr Запись в порт P = Rr None 1
PUSH Rr Занесение регистра в стек STACK = Rr None 2
POP Rd Извлечение регистра из стека Rd = STACK None 2

* Для операций доступа к данным количество циклов указано при условии доступа к внутренней памяти данных, и не корректно при работе с внешним ОЗУ. Для инструкций LD, ST, LDD, STD, LDS, STS, PUSH и POP, необходимо добавить один цикл плюс по одному циклу для каждого ожидания.

Инструкции работы с битами

Мнемоника Операнды Описание Операция Флаги Циклы
LSL Rd Логический сдвиг влево Rd(n+1)=Rd(n), Rd(0)=0, C=Rd(7) Z,C,N,V,H,S 1
LSR Rd Логический сдвиг вправо Rd(n)=Rd(n+1), Rd(7)=0, C=Rd(0) Z,C,N,V,S 1
ROL Rd Циклический сдвиг влево через C Rd(0)=C, Rd(n+1)=Rd(n), C=Rd(7) Z,C,N,V,H,S 1
ROR Rd Циклический сдвиг вправо через C Rd(7)=C, Rd(n)=Rd(n+1), C=Rd(0) Z,C,N,V,S 1
ASR Rd Арифметический сдвиг вправо Rd(n)=Rd(n+1), n=0,…,6 Z,C,N,V,S 1
SWAP Rd Перестановка тетрад Rd(3..0) = Rd(7..4), Rd(7..4) = Rd(3..0) None 1
BSET═ s Установка флага SREG(s) = 1 SREG(s) 1
BCLR s Очистка флага SREG(s) = 0 SREG(s) 1
SBI P,b Установить бит в порту I/O(P,b) = 1 None 2
CBI P,b Очистить бит в порту I/O(P,b) = 0 None 2
BST Rr,b Сохранить бит из регистра в T T = Rr(b) T 1
BLD Rd,b Загрузить бит из T в регистр Rd(b) = T None 1
SEC Нет Установить флаг переноса C =1 C 1
CLC Нет Очистить флаг переноса C = 0 C 1
SEN Нет Установить флаг отрицательного числа N = 1 N 1
CLN Нет Очистить флаг отрицательного числа N = 0 N 1
SEZ Нет Установить флаг нуля Z = 1 Z 1
CLZ Нет Очистить флаг нуля Z = 0 Z 1
SEI Нет Установить флаг прерываний I = 1 I 1
CLI Нет Очистить флаг прерываний I = 0 I 1
SES Нет Установить флаг числа со знаком S = 1 S 1
CLN Нет Очистить флаг числа со знаком S = 0 S 1
SEV Нет Установить флаг переполнения V = 1 V 1
CLV Нет Очистить флаг переполнения V = 0 V 1
SET Нет Установить флаг T T = 1 T 1
CLT Нет Очистить флаг T T = 0 T 1
SEH Нет Установить флаг внутреннего переноса H = 1 H 1
CLH Нет Очистить флаг внутреннего переноса H = 0 H 1
NOP Нет Нет операции Нет None 1
SLEEP Нет Спать (уменьшить энергопотребление) Смотрите описание инструкции None 1
WDR Нет Сброс сторожевого таймера Смотрите описание инструкции None 1


Ассемблер не различает регистр символов.

Операнды могут быть таких видов:

Rd: Результирующий (и исходный) регистр в регистровом файле
Rr: Исходный регистр в регистровом файле
b: Константа (3 бита), может быть константное выражение
s: Константа (3 бита), может быть константное выражение
P: Константа (5-6 бит), может быть константное выражение
K6; Константа (6 бит), может быть константное выражение
K8: Константа (8 бит), может быть константное выражение
k: Константа (размер зависит от инструкции), может быть константное выражение
q: Константа (6 бит), может быть константное выражение
Rdl:═ R24, R26, R28, R30. Для инструкций ADIW и SBIW
X,Y,Z: Регистры косвенной адресации (X=R27:R26, Y=R29:R28, Z=R31:R30)

Директивы ассемблера

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

Все директивы предваряются точкой.

BYTE - Зарезервировать байты в ОЗУ

Директива BYTE резервирует байты в ОЗУ. Если вы хотите иметь возможность ссылаться на выделенную область памяти, то директива BYTE должна быть предварена меткой. Директива принимает один обязательный параметр, который указывает количество выделяемых байт. Эта директива может использоваться только в сегменте данных(смотреть директивы CSEG и DSEG). Выделенные байты не инициализируются.

Синтаксис:
МЕТКА: .BYTE выражение

Пример:
. DSEG
var1:═══ .BYTE 1═══════════ ; резервирует 1 байт для var1
table:══ .BYTE tab_size════ ; резервирует tab_size байт

.CSEG
════════ ldi r30,low(var1)═ ; Загружает младший байт регистра Z
════════ ldi r31,high(var1) ; Загружает старший байт регистра Z
════════ ld r1,Z═══════════ ; Загружает VAR1 в регистр 1

CSEG - Программный сегмент

Директива CSEG определяет начало программного сегмента. Исходный файл может состоять из нескольких программных сегментов, которые объединяются в один программный сегмент при компиляции. Программный сегмент является сегментом по умолчанию. Программные сегменты имеют свои собственные счётчики положения которые считают не побайтно, а по словно. Директива ORG может быть использована для размещения кода и констант в необходимом месте сегмента. Директива CSEG не имеет параметров.

Синтаксис:
.CSEG

Пример:
.DSEG══════════════════════ ; Начало сегмента данных
vartab: .BYTE 4════════════ ; Резервирует 4 байта в ОЗУ

.CSEG══════════════════════ ; Начало кодового сегмента
const:═ .DW 2══════════════ ; Разместить константу 0x0002 в памяти программ
═══════ mov r1,r0══════════ ; Выполнить действия

DB — Определить байты во флэш или EEPROM

Директива DB резервирует необходимое количество байт в памяти программ или в EEPROM. Если вы хотите иметь возможность ссылаться на выделенную область памяти, то директива DB должна быть предварена меткой. Директива DB должна иметь хотя бы один параметр. Данная директива может быть размещена только в сегменте программ (CSEG) или в сегменте EEPROM (ESEG).

Параметры передаваемые директиве — это последовательность выражений разделённых запятыми. Каждое выражение должно быть или числом в диапазоне (-128..255), или в результате вычисления должно давать результат в этом же диапазоне, в противном случае число усекается до байта, причём БЕЗ выдачи предупреждений.

Если директива получает более одного параметра и текущим является программный сегмент, то параметры упаковываются в слова (первый параметр — младший байт), и если число параметров нечётно, то последнее выражение будет усечено до байта и записано как слово со старшим байтом равным нулю, даже если далее идет ещё одна директива DB.

Синтаксис:
МЕТКА:═ .DB список_выражений

Пример:
.CSEG
consts: .DB 0, 255, 0b01010101, -128, 0xaa

.ESEG
const2: .DB 1,2,3

DEF - Назначить регистру символическое имя

Директива DEF позволяет ссылаться на регистр через некоторое символическое имя. Назначенное имя может использоваться во всей нижеследующей части программы для обращений к данному регистру. Регистр может иметь несколько различных имен. Символическое имя может быть переназначено позднее в программе.

Синтаксис:
.DEF Символическое_имя = Регистр

Пример:
.DEF temp=R16
.DEF ior=R0

.CSEG
═ldi temp,0xf0═ ; Загрузить 0xf0 в регистр temp (R16)
═in ior,0x3f═ ; Прочитать SREG в регистр ior (R0)
═eor temp,ior═ ; Регистры temp и ior складываются по исключающему или

DEVICE — Определить устройство для которого компилируется программа

Директива DEVICE позволяет указать для какого устройства компилируется программа. При использовании данной директивы компилятор выдаст предупреждение, если будет найдена инструкция, которую не поддерживает данный микроконтроллер. Также будет выдано предупреждение, если программный сегмент, либо сегмент EEPROM превысят размер допускаемый устройством. Если же директива не используется то все инструкции считаются допустимыми, и отсутствуют ограничения на размер сегментов.

Синтаксис:
.DEVICE AT90S1200 |AT90S2313 | AT90S2323 | AT90S2333 | AT90S2343 | AT90S4414 | AT90S4433 | AT90S4434 | AT90S8515 | AT90S8534 | AT90S8535 | ATtiny11 | ATtiny12 | ATtiny22 | ATmega603 | ATmega103

Пример:
.DEVICE AT90S1200═ ; Используется AT90S1200

.CSEG
═══════ push r30══ ; Эта инструкция вызовет предупреждение
══════════════════ ; поскольку AT90S1200 её не имеет

DSEG — Сегмент данных

Директива DSEG определяет начало сегмента данных. Исходный файл может состоять из нескольких сегментов данных, которые объединяются в один сегмент при компиляции. Сегмент данных обычно состоит только из директив BYTE и меток. Сегменты данных имеют свои собственные побайтные счётчики положения. Директива ORG может быть использована для размещения переменных в необходимом месте ОЗУ. Директива не имеет параметров.

Синтаксис:
.DSEG═

Пример:
.DSEG═══════════════════════ ; Начало сегмента данных
var1:═ .BYTE 1══════════════ ; зарезервировать 1 байт для var1
table:═ .BYTE tab_size══════ ; зарезервировать tab_size байт.

.CSEG
═══════ ldi r30,low(var1)═══ ; Загрузить младший байт регистра Z
═══════ ldi r31,high(var1)══ ; Загрузить старший байт регистра Z
═══════ ld r1,Z═════════════ ; Загрузить var1 в регистр r1

DW — Определить слова во флэш или EEPROM

 

Директива DW резервирует необходимое количество слов в памяти программ или в EEPROM. Если вы хотите иметь возможность ссылаться на выделенную область памяти, то директива DW должна быть предварена меткой. Директива DW должна иметь хотя бы один параметр. Данная директива может быть размещена только в сегменте программ (CSEG) или в сегменте EEPROM (ESEG).

Параметры передаваемые директиве — это последовательность выражений разделённых запятыми. Каждое выражение должно быть или числом в диапазоне (-32768..65535), или в результате вычисления должно давать результат в этом же диапазоне, в противном случае число усекается до слова, причем БЕЗ выдачи предупреждений.

Синтаксис:
МЕТКА: .DW expressionlist

Пример:
.CSEG
varlist:═ .DW 0, 0xffff, 0b1001110001010101, -32768, 65535

.ESEG
eevarlst: .DW 0,0xffff,10

ENDMACRO — Конец макроса

Директива определяет конец макроопределения, и не принимает никаких параметров. Для информации по определению макросов смотрите директиву MACRO.

Синтаксис:
.ENDMACRO═

Пример:
.MACRO SUBI16══════════════ ; Начало определения макроса
═══════ subi r16,low(@0)═══ ; Вычесть младший байт первого параметра
═══════ sbci r17,high(@0)══ ; Вычесть старший байт первого параметра
.ENDMACRO

EQU - Установить постоянное выражение

Директива EQU присваивает метке значение. Эта метка может позднее использоваться в выражениях. Метка которой присвоено значение данной директивой не может быть переназначена и её значение не может быть изменено.

Синтаксис:
.EQU метка = выражение

Пример:
.EQU io_offset = 0x23
.EQU porta════ = io_offset + 2

.CSEG════════════════ ; Начало сегмента данных
═══════ clr r2═══════ ; Очистить регистр r2
═══════ out porta,r2═ ; Записать в порт A

ESEG — Сегмент EEPROM

Директива ESEG определяет начало сегмента EEPROM. Исходный файл может состоять из нескольких сегментов EEPROM, которые объединяются в один сегмент при компиляции. Сегмент EEPROM обычно состоит только из директив DB, DW и меток. Сегменты EEPROM имеют свои собственные побайтные счётчики положения. Директива ORG может быть использована для размещения переменных в необходимом месте EEPROM. Директива не имеет параметров.

Синтаксис:
.ESEG═══

Пример:
.DSEG═══════════════════ ; Начало сегмента данных
var1:══ .BYTE 1═════════ ; зарезервировать 1 байт для var1
table:═ .BYTE tab_size══ ; зарезервировать tab_size байт.

.ESEG
eevar1: .DW 0xffff═══════ ; проинициализировать 1 слово в EEPROM

EXIT — Выйти из файла

Встретив директиву EXIT компилятор прекращает компиляцию данного файла. Если директива использована во вложенном файле (см. директиву INCLUDE), то компиляция продолжается со строки следующей после директивы INCLUDE. Если же файл не является вложенным, то компиляция прекращается.

Синтаксис:
.EXIT

Пример:
.EXIT═ ; Выйти из данного файла

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════ ; Прочитать регистр статуса

LIST - Включить генерацию листинга

Директива LIST указывает компилятору на необходимость создания листинга. Листинг представляет из себя комбинацию ассемблерного кода, адресов и кодов операций. По умолчанию генерация листинга включена, однако данная директива используется совместно с директивой NOLIST для получения листингов отдельных частей исходных файлов.

Синтаксис:
.LIST

Пример:
.NOLIST═══════════════ ; Отключить генерацию листинга
.INCLUDE «macro.inc»══ ; Вложенные файлы не будут
.INCLUDE «const.def»══ ; отображены в листинге
.LIST═════════════════ ; Включить генерацию листинга

LISTMAC - Включить разворачивание макросов в листинге

После директивы LISTMAC компилятор будет показывать в листинге содержимое макроса. По умолчанию в листинге показывается только вызов макроса и передаваемые параметры.

Синтаксис:
.LISTMAC

Пример:
.MACRO MACX════════ ; Определение макроса
═══════ add═ r0,@0═ ; Тело макроса
═══════ eor═ r1,@1═
.ENDMACRO══════════ ; Конец макроопределения

.LISTMAC═══════════ ; Включить разворачивание макросов
═══════ MACX r2,r1═ ; Вызов макроса (в листинге будет показано тело макроса)

MACRO — Начало макроса

С директивы MACRO начинается определение макроса. В качестве параметра директиве передаётся имя макроса. При встрече имени макроса позднее в тексте программы, компилятор заменяет это имя на тело макроса. Макрос может иметь до 10 параметров, к которым в его теле обращаются через @0-@9. При вызове параметры перечисляются через запятые. Определение макроса заканчивается директивой 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

NOLIST - Выключить генерацию листинга

Директива NOLIST указывает компилятору на необходимость прекращения генерации листинга. Листинг представляет из себя комбинацию ассемблерного кода, адресов и кодов операций. По умолчанию генерация листинга включена, однако может быть отключена данной директивой. Кроме того данная директива может быть использована совместно с директивой LIST для получения листингов отдельных частей исходных файлов

Синтаксис:
.NOLIST

Пример:
.NOLIST═══════════════ ; Отключить генерацию листинга
.INCLUDE «macro.inc»══ ; Вложенные файлы не будут
.INCLUDE «const.def»══ ; отображены в листинге
.LIST═════════════════ ; Включить генерацию листинга

ORG - Установить положение в сегменте

Директива ORG устанавливает счётчик положения равным заданной величине, которая передаётся как параметр. Для сегмента данных она устанавливает счётчик положения в SRAM (ОЗУ), для сегмента программ это программный счётчик, а для сегмента EEPROM это положение в EEPROM. Если директиве предшествует метка (в той же строке) то метка размещается по адресу указанному в параметре директивы. Перед началом компиляции программный счётчик и счётчик EEPROM равны нулю, а счётчик ОЗУ равен 32 (поскольку адреса 0-31 заняты регистрами). Обратите внимание что для ОЗУ и EEPROM используются побайтные счётчики а для программного сегмента - пословный.

Синтаксис:
.ORG выражение

Пример:
.DSEG═══════════════ ; Начало сегмента данных

.ORG 0x37═══════════ ; Установить адрес SRAM равным 0x37
variable: .BYTE 1═══ ; Зарезервировать байт по адресу 0x37H

.CSEG
.ORG 0x10═══════════ ; Установить программный счётчик равным 0x10
═════════ mov r0,r1═ ; Данная команда будет размещена по адресу 0x10

SET - Установить переменный символический эквивалент выражения

Директива SET присваивает имени некоторое значение. Это имя позднее может быть использовано в выражениях. Причем в отличии от директивы EQU значение имени может быть изменено другой директивой SET.

Синтаксис:
.SET имя = выражение

Пример:
.SET io_offset = 0x23
.SET porta════ = io_offset + 2

.CSEG════════════════ ; Начало кодового сегмента
═══════ clr r2═══════ ; Очистить регистр 2
═══════ out porta,r2═ ; Записать в порт A

Выражения

Компилятор позволяет использовать в программе выражения которые могут состоять операндов, знаков операций и функций. Все выражения являются 32-битными.

Операнды

Могут быть использованы следующие операнды:

  • Метки определённые пользователем (дают значение своего положения).
  • Переменные определённые директивой SET
  • Константы определённые директивой EQU
  • Числа заданные в формате:
    • Десятичном (принят по умолчанию): 10, 255
    • Шестнадцатеричном (два варианта записи): 0x0a, $0a, 0xff, $ff
    • Двоичном: 0b00001010, 0b11111111
    • Восьмеричном (начинаются с нуля): 010, 077
  • PC — текущее значение программного счётчика (Programm Counter)

Операции

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

Логическое отрицание

Символ: !
Описание: Возвращает 1 если выражение равно 0, и наоборот
Приоритет: 14
Пример: ldi r16, !0xf0═ ; В r16 загрузить 0x00

Побитное отрицание

Символ: ~
Описание: Возвращает выражение в котором все биты проинвертированы
Приоритет: 14
Пример: ldi r16, ~0xf0═ ; В r16 загрузить 0x0f

Минус

Символ: —
Описание: Возвращает арифметическое отрицание выражения
Приоритет: 14
Пример: ldi r16,-2═ ; Загрузить -2(0xfe) в r16

Умножение

Символ: *
Описание: Возвращает результат умножения двух выражений
Приоритет: 13
Пример: ldi r30, label*2

Деление

Символ: /
Описание: Возвращает целую часть результата деления левого выражения на правое
Приоритет: 13
Пример: ldi r30, label/2

Суммирование

Символ: +
Описание: Возвращает сумму двух выражений
Приоритет: 12
Пример: ldi r30, c1+c2

Вычитание

Символ: —
Описание: Возвращает результат вычитания правого выражения из левого
Приоритет: 12
Пример: ldi r17, c1-c2

Сдвиг влево

Символ: <<
Описание: Возвращает левое выражение сдвинутое влево на число бит указанное справа
Приоритет: 11
Пример: ldi r17, 1<<bitmask═ ; В r17 загрузить 1 сдвинутую влево bitmask раз

Сдвиг вправо

Символ: >>
Описание: Возвращает левое выражение сдвинутое вправо на число бит указанное справа
Приоритет: 11
Пример: ldi r17, c1>>c2═ ; В r17 загрузить c1 сдвинутое вправо c2 раз

Меньше чем

Символ: <
Описание: Возвращает 1 если левое выражение меньше чем правое (учитывается знак), и 0 в противном случае
Приоритет: 10
Пример: ori r18, bitmask*(c1<c2)+1

Меньше или равно

Символ: <=
Описание: Возвращает 1 если левое выражение меньше или равно чем правое (учитывается знак), и 0 в противном случае
Приоритет: 10
Пример: ori r18, bitmask*(c1<=c2)+1

Больше чем

Символ: >
Описание: Возвращает 1 если левое выражение больше чем правое (учитывается знак), и 0 в противном случае
Приоритет: 10
Пример: ori r18, bitmask*(c1>c2)+1

Больше или равно

Символ: >=
Описание: Возвращает 1 если левое выражение больше или равно чем правое (учитывается знак), и 0 в противном случае
Приоритет: 10
Пример: ori r18, bitmask*(c1>=c2)+1

Равно

Символ: ==
Описание: Возвращает 1 если левое выражение равно правому (учитывается знак), и 0 в противном случае
Приоритет: 9
Пример: andi r19, bitmask*(c1==c2)+1

Не равно

Символ: !=
Описание: Возвращает 1 если левое выражение не равно правому (учитывается знак), и 0 в противном случае
Приоритет: 9
Пример: . c2)

Побитное ИЛИ

Символ: |
Описание: Возвращает результат побитового ИЛИ выражений
Приоритет: 6
Пример: ldi r18, Low(c1|c2)

Логическое И

Символ: &&
Описание: Возвращает 1 если оба выражения не равны нулю, и 0 в противном случае
Приоритет: 5
Пример: ldi r18, Low(c1&&c2)

Логическое ИЛИ

Символ: ||
Описание: Возвращает 1 если хотя бы одно выражение не равно нулю, и 0 в противном случае
Приоритет: 4
Пример: ldi r18, Low(c1||c2)

Функции

Определены следующие функции:

  • LOW(выражение) возвращает младший байт выражения
  • HIGH(выражение) возвращает второй байт выражения
  • BYTE2(выражение) то же что и функция HIGH
  • BYTE3(выражение) возвращает третий байт выражения
  • BYTE4(выражение) возвращает четвёртый байт выражения
  • LWRD(выражение) возвращает биты 0-15 выражения
  • HWRD(выражение) возвращает биты 16-31 выражения
  • PAGE(выражение) возвращает биты 16-21 выражения
  • EXP2(выражение) возвращает 2 в степени (выражение)
  • LOG2(выражение) возвращает целую часть log2(выражение)

Использование программы

Этот раздел описывает использование компилятора и встроенного редактора

Открытие файлов

В WAVRASM могут быть открыты как новые так и существующие файлы. Количество открытых файлов ограничено размером памяти, однако объём одного файла не может превышать 28 килобайт (в связи с ограничением MS-Windows). Компиляция файлов большего размера возможна, но они не могут быть редактируемы встроенным редактором. Каждый файл открывается в отдельном окне.

Сообщения об ошибках

После компиляции программы появляется окно сообщений. Все обнаруженные компилятором ошибки будут перечислены в этом окне. При выборе строки с сообщением о ошибке, строка исходного файла, в которой найдена ошибка, становится красной. Если же ошибка находится во вложенном файле, то этого подсвечивания не произойдёт.

Если по строке в окне сообщений клацнуть дважды, то окно файла с указанной ошибкой становится активным, и курсор помещается в начале строки содержащей ошибку. Если же файл с ошибкой не открыт (например это вложенный файл) то он будет автоматически открыт.

Учтите, что если вы внесли изменения в исходные тексты (добавили или удалили строки), то информация о номерах строк в окне сообщений не является корректной.

Опции

Некоторые установки программы могут быть изменены через пункт меню «Options».

В поле ввода, озаглавленном «List-file extension», вводится расширение, используемое для файла листинга, а в поле «Output-file extension» находится расширение для файлов с результатом компиляции программы. В прямоугольнике «Output file format» можно выбрать формат выходного файла (как правило используется интеловский). Однако это не влияет на объектный файл (используемый AVR Studio), который всегда имеет один и тот же формат, и расширение OBJ. Если в исходном файле присутствует сегмент EEPROM то будет также создан файл с расширением EEP. Установки заданные в данном окне запоминаются на постоянно, и при следующем запуске программы, их нет необходимости переустанавливать.

Опция «Wrap relative jumps» даёт возможность «заворачивать» адреса. Эта опция может быть использована только на чипах с объёмом программной памяти 4К слов (8К байт), при этом становится возможным делать относительные переходы (rjmp) и вызовы подпрограмм (rcall) по всей памяти.

Опция «Save before assemble» указывает программе на необходимость автоматического сохранения активного окна (и только его) перед компиляцией.

Если вы хотите, чтобы при закрытии программы закрывались все открытые окна, то поставьте галочку в поле «Close all windows before exit».


Atmel, AVR являются зарегистрированными товарными знаками фирмы Atmel Corporation

Перевод выполнил Руслан Шимкевич, [email protected]

Ассемблер Введение

Главная / Ассемблер / Для чайников /

Для начала разберёмся с терминологией.

Машинный код – система команд конкретной вычислительной машины (процессора), которая интерпретируется непосредственно процессором. Команда, как правило, представляет собой целое число, которое записывается в регистр процессора. Процессор читает это число и выполняет операцию, которая соответствует этой команде. Популярно это описано в книге Как стать программистом.

Язык программирования низкого уровня (низкоуровневый язык программирования) – это язык программирования, максимально приближённый к программированию в машинных кодах. В отличие от машинных кодов, в языке низкого уровня каждой команде соответствует не число, а сокращённое название команды (мнемоника). Например, команда ADD – это сокращение от слова ADDITION (сложение). Поэтому использование языка низкого уровня существенно упрощает написание и чтение программ (по сравнению с программированием в машинных кодах). Язык низкого уровня привязан к конкретному процессору. Например, если вы написали программу на языке низкого уровня для процессора PIC, то можете быть уверены, что она не будет работать с процессором AVR.

Язык программирования высокого уровня – это язык программирования, максимально приближённый к человеческому языку (обычно к английскому, но есть языки программирования на национальных языках, например, язык 1С основан на русском языке). Язык высокого уровня практически не привязан ни к конкретному процессору, ни к операционной системе (если не используются специфические директивы).

Язык ассемблера – это низкоуровневый язык программирования, на котором вы пишите свои программы. Для каждого процессора существует свой язык ассемблера.

Ассемблер – это специальная программа, которая преобразует (компилирует) исходные тексты вашей программы, написанной на языке ассемблера, в исполняемый файл (файл с расширением EXE или COM). Если быть точным, то для создания исполняемого файла требуются дополнительные программы, а не только ассемблер. Но об этом позже…

В большинстве случаев говорят «ассемблер», а подразумевают «язык ассемблера». Теперь вы знаете, что это разные вещи и так говорить не совсем правильно. Хотя все программисты вас поймут.

ВАЖНО!
В отличие от языков высокого уровня, таких, как Паскаль, Бейсик и т.п., для КАЖДОГО АССЕМБЛЕРА существует СВОЙ ЯЗЫК АССЕМБЛЕРА. Это правило в корне отличает язык ассемблера от языков высокого уровня. Исходные тексты программы (или просто «исходники»), написанной на языке высокого уровня, вы в большинстве случаев можете откомпилировать разными компиляторами для разных процессоров и разных операционных систем. С ассемблерными исходниками это сделать будет намного сложнее. Конечно, эта разница почти не ощутима для разных ассемблеров, которые предназначены для одинаковых процессоров. Но в том то и дело, что для КАЖДОГО ПРОЦЕССОРА существует СВОЙ АССЕМБЛЕР и СВОЙ ЯЗЫК АССЕМБЛЕРА. В этом смысле программировать на языках высокого уровня гораздо проще. Однако за все удовольствия надо платить. В случае с языками высокого уровня мы можем столкнуться с такими вещами как больший размер исполняемого файла, худшее быстродействие и т.п.

В этой книге мы будем говорить только о программировании для компьютеров с процессорами Intel (или совместимыми). Для того чтобы на практике проверить приведённые в книге примеры, вам потребуются следующие программы (или хотя бы некоторые из них):

  1. Emu8086. Хорошая программа, особенно для новичков. Включает в себя редактор исходного кода и некоторые другие полезные вещи. Работает в Windows, хотя программы пишутся под DOS. К сожалению, программа стоит денег (но оно того стоит))). Подробности см. на сайте http://www.emu8086.com.
  2. TASM – Турбо Ассемблер от фирмы Borland. Можно создавать программы как для DOS так и для Windows. Тоже стоит денег и в данный момент уже не поддерживается (да и фирмы Borland уже не существует). А вообще вещь хорошая.
  3. MASM – Ассемблер от компании Microsoft (расшифровывается как МАКРО ассемблер, а не Microsoft Assembler, как думают многие непосвящённые). Пожалуй, самый популярный ассемблер для процессоров Intel. Поддерживается до сих пор. Условно бесплатная программа. То есть, если вы будете покупать её отдельно, то она будет стоить денег. Но она доступна бесплатно подписчикам MSDN и входит в пакет программ Visual Studio от Microsoft.
  4. WASM – ассемблер от компании Watcom. Как и все другие, обладает преимуществами и недостатками.
  5. Debug — обладает скромными возможностями, но имеет большой плюс — входит в стандартный набор Windows. Поищите ее в папке WINDOWS\COMMAND или WINDOWS\SYSTEM32. Если не найдете, тогда в других папках каталога WINDOWS.
  6. Желательно также иметь какой-нибудь шестнадцатеричный редактор. Не помешает и досовский файловый менеджер, например Волков Коммандер (VC) или Нортон Коммандер (NC). С их помощью можно также посмотреть шестнадцатеричные коды файла, но редактировать нельзя. Бесплатных шестнадцатеричных редакторов в Интернете довольно много. Вот один из них: McAfee FileInsight v2.1. Этот же редактор можно использовать для работы с исходными текстами программ. Однако мне больше нравится делать это с помощью следующего редактора:
  7. Текстовый редактор. Необходим для написания исходных текстов ваших программ. Могу порекомендовать бесплатный редактор PSPad, который поддерживает множество языков программирования, в том числе и язык Ассемблера.
Все представленные в этой книге программы (и примеры программ) проверены на работоспособность. И именно эти программы используются для реализации примеров программ, приведённых в данной книге.

И еще – исходный код, написанный, например для Emu8086, будет немного отличаться от кода, написанного, например, для TASM. Эти отличия будут оговорены.

Большая часть программ, приведённых в книге, написана для MASM. Во-первых, потому что этот ассемблер наиболее популярен и до сих пор поддерживается. Во-вторых, потому что он поставляется с MSDN и с пакетом программ Visual Studio от Microsoft. Ну и в третьих, потому что я являюсь счастливым обладателем лицензионной копии MASM.

Если же у вас уже есть какой-либо ассемблер, не вошедший в перечисленный выше список, то вам придётся самостоятельно разобраться с его синтаксисом и почитать руководство пользователя, чтобы научиться правильно с ним работать. Но общие рекомендации, приведённые в данной книге, будут справедливы для любых (ну или почти для любых) ассемблеров.


Как программировать Arduino на ассемблере | by smith_rich

Сигнальный вывод датчика подключен к ноге 2 (PIN2) порта PORTD контролера или (что то же самое) к выводу D2 Arduino. Он же через резистор 4.7 kOm “подтянут” на “плюс” питания. Плюс и минус датчика подключены — к соответствующим проводам питания. USB-TTL переходник подключен к выходу Tx USART порта Arduino, что значит PIN1 порта PORTD контроллера.

В собранном виде на breadboard:

Разбираемся с датчиком и смотрим datasheet. Сам по себе датчик несложный, и использует всего один сигнальный провод, который надо подтянуть через резистор к +5V — это будет базовый «высокий» уровень на линии. Если линия свободна — т.е. ни контроллер, ни датчик ничего не передают, на линии как раз и будет базовый «высокий» уровень. Когда датчик или контроллер что-то передают, то они занимают линию — устанавливают на линии «низкий» уровень на какое-то время. Всего датчик передает 5 байт. Байты датчик передает по очереди, сначала показатели влажности, потом температуры, завершает все контрольной суммой, это выглядит как “HHTTXX”, в общем смотрим datasheet. Пять байт — это 40 бит и каждый бит при передаче кодируется специальным образом.

Для упрощения, будет считать, что «высокий» уровень на линии — это «единица», а «низкий» соответственно «ноль». Согласно datasheet для начала работы с датчиком надо положить контроллером сигнальную линию на землю, т.е. получить «ноль» на линии и сделать это на период не менее чем 20 милсек (миллисекунд), а потом резко отпустить линию. В ответ — датчик должен выдать на сигнальную линию свою посылку, из сигналов высокого и низкого уровня разной длительности, которые кодируют нужные нам 40 бит. И, согласно datasheet, если мы удачно прочитаем эту посылку контроллером, то мы сразу поймем что: а) датчик собственно ответил, б) передал данные по влажности и температуре, с) передал контрольную сумму. В конце передачи датчик отпускает линию. Ну и в datasheet написано, что датчик можно опрашивать не чаще чем раз в секунду.

Итак, что должен сделать микроконтроллер, согласно datasheet, чтобы датчик ему ответил — нужно прижать линию на 20 миллисекунд, отпустить и быстро смотреть, что на линии:

Датчик должен ответить — положить линию в ноль на 80 микросекунд (мксек), потом отпустить на те же 80 мксек — это можно считать подтверждением того, что датчик на линии живой и откликается:

После этого, сразу же, по падению с высокого уровня на нижний датчик начинает передавать 40 отдельных бит. Каждый бит кодируются специальной посылкой, которая состоит из двух интервалов. Сначала датчик занимает линию (кладет ее в ноль) на определенное время — своего рода первый «полубит». Потом датчик отпускает линию (линия подтягивается к единице) тоже на определенное время — это типа второй «полубит». Длительность этих интервалов — «полубитов» в микросекундах кодирует что собственно пытается передать датчик: бит “ноль” или бит “единица”.

Рассмотрим описание битовой посылки: первый «полубит» всегда низкого уровня и фиксированной длительности — около 50 мксек. Длительность второго «полубита» определят, что датчик собственно передает.

Для передачи нуля используется сигнал высокого уровня длительностью 26–28 мксек:

Для передачи единицы, длительность сигнала высокого увеличивается до 70 микросекунд:

Мы не будет точно высчитывать длительность каждого интервала, нам вполне достаточно понимания, что если длительность второго «полубита» меньше чем первого — то закодирован ноль, если длительность второго «полубита» больше — то закодирована единица. Всего у нас 40 бит, каждый бит кодируется двумя импульсами, всего нам надо значит прочитать 80 интервалов. После того как прочитали 80 интервалов будем сравнить их попарно, первый “полубит” со вторым.

Вроде все просто, что же требуется от микроконтроллера для того чтобы прочитать данные с датчика? Получается нужно значит дернуть ногой в ноль, а потом просто считать всю длинную посылку с датчика на той же ноге. По ходу, будем разбирать посылку на «полу-биты», определяя где передается бит ноль, где единица. Потом соберем получившиеся биты, в байты, которые и будут ожидаемыми данными о влажности и температуре.

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

Определения:

Как я уже писал сам датчик подключен на 2 ногу порта D. В Arduino Uno это цифровой выход D2 (смотрим для проверки Arduino Pinout).

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

Смотрим анализатором — а ответил ли датчик?

Да, ответ есть — вот те сигналы после нашего первого импульса в 20 милсек — это и есть ответ датчика. Для просмотра посылки я использовал китайский клон USBEE AX Pro который подключен к сигнальному проводу датчика.

Растянем масштаб так чтобы увидеть окончание нашего импульса в 20 милсек и лучше увидеть начало посылки от датчика — смотрим все как в datasheet — сначала датчик выставил низкий/высокий уровень по 80 мксек, потом начал передавать биты — а данном случае во втором «полубите» передается «0»

Значит датчик работает и данные нам прислал, теперь надо эти данные правильно прочитать. Поскольку задача у нас учебная, то и решать ее будем тупо в лоб. В момент ответа датчика, т.е. в момент перехода с высокого уровня в низкий, мы запустим цикл с счетчиком числа повторов нашего цикла. Внутри цикла, будем постоянно следить за уровнем сигнала на ноге. Итого, в цикле будем ждать, когда сигнал на ноге перейдет обратно на высокий уровень — тем самым определив длительность сигнала первого «полубита». Наш микроконтроллер работает на частоте 16 MHz и за период например в 50 микросекунд контроллер успеет выполнить около 800 инструкций. Когда на линии появится высокий уровень — то мы из цикла аккуратно выходим, а число повторов цикла, которые мы отсчитали с использованием счетчика — запоминаем в переменную.

После перехода сигнальной линии уже на высокий уровень мы делаем такую же операцию– считаем циклы, до момента когда датчик начнет передавать следующий бит и положит линию в низкий уровень. К счастью, нам не надо знать точный временной интервал наших импульсов, нам достаточно понимать, что один интервал больше другого. Понятно, что если датчик передает бит «ноль» то длительность второго «полубита» и соответственно число циклов, которые мы отсчитали будет меньше чем длительность первого «полубита». Если же датчик передал бит «единица», то число циклов которые мы насчитаем во время второго полубита будет больше чем в первым.

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

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

Аналогичная подпрограмма используется для того, чтобы посчитать сколько циклов у нас должно прокрутиться, пока датчик из состояния ноль на линии переложил линию в состояние единицы.

Для расчета временных задержек мы будет использовать тот же подход, который мы использовали при мигании светодиодом — подберем параметры пустого цикла для формирования нужной паузы. Я использовал специальный калькулятор. При желании можно посчитать число рабочих инструкций и вручную.

Памяти в нашем контроллере довольно много — аж 2 (Два) килобайта, так что мы не будем жлобствовать с памятью, и тупо сохраним данные счетчиков относительно наших 80 ( 40 бит, 2 интервала на бит) интервалов в память.

Объявим переменную

CYCLES: .byte 80 ; буфер для хранения числа циклов

И сохраним все считанные циклы в память.

Теперь, для отладки, попробуем посмотреть насколько удачно посчиталось длительность интервалов и понять действительно ли мы считали данные из датчика. Понятно, что число отсчитанных циклов первого «полубита» должно быть примерно одинаково у всех битовых посылок, а вот число циклов при отсчете второго «полубита» будет или существенно меньше, или наоборот существенно больше.

Для того чтобы передавать данные в большой компьютер будем использовать USART контроллера, который через USB кабель будет передавать данные в программу — терминал, например PuTTY. Передаем опять же тупо в лоб — засовываем байт в нужный регистр управления USART-а и ждем, когда он передастся. Для удобства я также использовал пару подпрограмм, типа — передать несколько байт, начиная с адреса в Y, ну и перевести каретку в терминале для красоты.

Отправив в терминал число отсчётов для 80 интервалов, можно попробовать собрать собственно значащие биты. Делать будем как написано в учебнике, т.е. в datasheet — попарно сравним число циклов первого «полубита» с числом циклов второго. Если вторые пол-бита короче — значит это закодировать ноль, если длиннее — то единица. После сравнения биты накапливаем в аккумуляторе и сохраняем в память по-байтово начиная с адреса BITS.

Итак, здесь мы собрали в памяти начиная с метки BITS те пять байт, которые передал контроллер. Но работать с ними в таком формате не очень неудобно, поскольку в памяти это выглядит примерно, как:
34002100ХХ, где 34 — это влажность целая часть, 00 — данные после запятой влажности, 21 — температура, 00 — опять данные после запятой температуры, ХХ — контрольная сумма. А нам надо бы вывести в терминал красиво типа «Temperature = 21.00». Так что для удобства, растащим данные по отдельным переменным.

Определения

И сохраняем байты из BITS в нужные переменные

После этого преобразуем цифры в коды ASCII, чтобы данные можно было нормально прочитать в терминале, добавляем названия данных, ну там «температура» из флеша и шлем в COM порт в терминал.

PuTTY с данными

Для того, чтобы это измерять температуру регулярно добавляем вечный цикл с задержкой порядка 1200 миллисекунд, поскольку datasheet DHT11 говорит, что не рекомендуется опрашивать датчик чаще чем 1 раз в секунду.

Основной цикл после этого выглядит примерно так:

Прошиваем, подключаем USB-TTL кабель (преобразователь)к компьютеру, запускаем терминал, выбираем правильный виртуальный COM порта и наслаждаемся нашим новым цифровым термометром. Для проверки можно погреть датчик в руке — у меня температура при этом растет, а влажность как ни странно уменьшается.

Ссылки по теме:
AVR Delay Calc
Как подключить Arduino для программирования в Atmel Studio 7
DHT11 Datasheet
ATmega DataSheet
Atmel AVR 8-bit Instruction Set
Atmel Studio
Код примера на github

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

Формат программ на ассемблере

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

Любая строка может начинаться с метки – строки из символов и (или) цифр, заканчивающейся двоеточием.

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

Строка исходного текста может иметь один из следующих видов:

1. [метка:] директива [аргументы директивы] [комментарий]

2. [метка:] мнемоника команды [аргументы команды] [комментарий]

3. Комментарий

4. Пустая строка

Комментарии всœегда начинаются с символа “;”.

Элементы, заключенные в квадратные скобки, могут отсутствовать. Текст, расположенный после символа “точка с запятой” до конца строки, полностью игнорируется ассемблером. Использование меток, мнемоник команд микроконтроллера и директив ассемблера подробнее будет рассмотрено позже.

Примеры записи строк:

Label1: .EQU var1=100 ;Директива определœения символьного

;имени var1, эквивалентного записи ʼʼ100ʼʼ

.EQU var2=200 ;Определœение имени var2, соответствующего ʼʼ200ʼʼ

test: rjmp test ;Бесконечный цикл (мнемоника команды)

;Пустая строка

Расположение меток, команд ассемблера и директив несущественно, важен только их порядок.

Транслятор ассемблера позволяет использовать в тексте программы мнемоники (обозначения команд микроконтроллера), полностью совпадающие с их названием в системе команд микроконтроллера.

Множество команд микроконтроллеров AVR семейства Classic можно представить несколькими группами:

· Команды пересылки данных;

· Команды арифметических операций и команды сдвига;

· Команды логических операций;

· Команды передачи управления;

· Команды операций с битами;

· Команды управления системой.

Рассмотрим каждую группу подробнее.

2.3.1. Команды логических операций

Эти команды позволяют выполнять стандартные логические операции над байтами, такие как ʼʼлогическое умножениеʼʼ (И), ʼʼлогическое сложениеʼʼ (ИЛИ), операцию ʼʼисключающее ИЛИʼʼ, а также вычисление обратного и дополнительного кодов числа. К этой группе можно отнести также команды очистки/установки регистров и команд перестановки тетрад. Все операции производятся над регистрами общего назначения, результат сохраняется в одном из РОН. Все логические операции выполняются за один машинный цикл.

Рассмотрим наиболее часто используемые команды логических операций с примерами ( всœе команды логических операций представлены в таблице 2.1):

1. ʼʼЛогическое Иʼʼ двух РОН- AND Rd, Rr

Операция: Rd=Rd AND Rr

Описание:Выполняет операцию ʼʼЛогическое Иʼʼ между содержимым регистров Rd и Rr

Пример: ldi R16,1 ;загрузить 1 в R16

and R2,R16 ;произвести операцию ʼʼЛогическое Иʼʼ

2. ʼʼЛогическое Иʼʼ РОН и константы- ANDI Rd, K

Операция: Rd=Rd AND K

Описание:Выполняет операцию ʼʼЛогическое Иʼʼ между содержимым регистра Rd и 8- разрядным числом. Результат помещается в регистр Rd. Команда применима только к 16 старшим РОН (R16…R31)

Пример: andi R17,$0F ; обнулить старший полубайт регистра R17

andi R18,$10 ;выделить 4-ый разряд в регистре R18

3. ʼʼЛогическое ИЛИʼʼ двух РОН- OR Rd ,Rr

Операция:Rd=RdÚRr

Описание: Выполняет операцию ʼʼЛогическое ИЛИʼʼ между регистрами Rd и Rr. Результат помещается в регистр Rd

Пример: or R15,R16 ;поразрядное ИЛИ R15 и R16

bst R15,6 ;записать 6-ой разряд регистра R15 в флаг Т

brts ok ;перейти если флаг Т равен ʼʼ1ʼʼ

ok:

4. ʼʼЛогическое ИЛИʼʼ РОН и константы-ORI Rd, К

Операция:Rd=RdÚK

Описание: Выполняет операцию ʼʼЛогическое ИЛИʼʼ между регистром Rd и константой К. Результат помещается в регистр Rd. Команда применима только к 16 старшим РОН (R16…R31)

Пример:ori R17,$0F ;установить старший полубайт регистра R17

ori R18,1 ;установить 0-й разряд регистра R18

5. ʼʼИсключающее ИЛИʼʼ двух РОН- EOR Rd, Rr

Операция:Rd=RdÅRr

Описание:Выполняет операцию ʼʼИсключающее ИЛИʼʼ между регистрами Rd и Rr. Результат помещается в регистр Rd

Пример:eor R2,R2 ;очистка регистра R2

eor R0,R22 ; побитовое ʼʼИсключающее ИЛИʼʼ между R0 и R22

6. Очистка РОН- CLR Rd

Операция:Rd= RdÅRd

Описание:Сбрасывает всœе разряды регистра общего назначения путем выполнения операции ʼʼИсключающее ИЛИʼʼ регистра с самим собой

Пример:Организация цикла с заданным числом повторений

clr R18 ;очистить регистр R18

loop:

ink R18 ;R18=R18+1

cpi R18,$50 ;завершить цикл?

brne loop

Таблица 2.1. Команды логических операций

Мнемоника Описание Операции Циклы Флаги
AND Rd,Rr ʼʼЛогическое Иʼʼ двух РОН Rd=Rd*Rr Z,N,V
ANDI Rd,K ʼʼЛогическое Иʼʼ РОН и константы Rd=Rd*K Z,N,V
EOR Rd,Rr ʼʼИсключающее ИЛИʼʼ двух РОН Rd=RdÅRr Z,N,V
OR Rd,Rr ʼʼЛогическое ИЛИʼʼ двух РОН Rd=RdÚRr Z,N,V
ORI Rd,K ʼʼЛогическое ИЛИʼʼ РОН и константы Rd=RdÚK Z,N,V
COM Rd Перевод в обратный код Rd=$FF-Rd Z,C,N,V
NEG Rd Перевод в дополнительный код Rd=$00-Rd Z,C,N,V,H
CLR Rd Сброс всœех разрядов РОН Rd= RdÅRd Z,N,V
SER Rd Установка всœех разрядов РОН Rd=$FF -
TST Rd Проверка РОН на отрицательное или нулевое значение Rd*Rd     Z,N,V
SWAP Rd Обмен местами тетрад в РОН Rd(3…0)=Rd(7…4) Rd(7…4)=Rd(3…0)   -

2. 3.2. Команды арифметических операций и команды сдвига

К данной группе относятся команды, выполняющие такие базовые операции, как сложение, вычитание, сдвиг (вправо и влево), инкремент и декремент. Все операции производятся только над РОН. При этом микроконтроллер позволяет легко оперировать как знаковыми, так и беззнаковыми числами, а также работать с числами, представленными в дополнительном коде.

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

Рассмотрим наиболее часто используемые команды арифметических операций и команды сдвига с примерами ( всœе команды арифметических операций и команды сдвига представлены в таблице 2.2):

:

1. Сложение двух РОН- ADD Rd, Rr

Операция: Rd=Rd+Rr

Описание:Выполняет операцию сложения между содержимым регистров Rd и Rr

Пример:add R16,R18 ;сложить два регистра

2. Сложение регистровой пары с константой- ADIW Rd, К

Операция:Rd+1:Rd=Rd+1:Rd+K

Описание:Складывает содержимое регистровой пары Rd+1:Rd с 6-разрядным числом. Результат помещается обратно в регистровую пару.

Пример:adiw R24,1 ;прибавить 1 к R25:R24

adiw R30,63 ;прибавить 63 к указателю Z(R31:R30)

3. Вычитание двух РОН- SUB Rd, Rr

Операция:Rd=Rd-Rr

Описание:Вычитает из регистра Rd содержимое регистра Rr. Результат помещается в регистр Rd

Пример:sub R13,R12 ;вычесть R12 из R13 (R13=R13-R12)

brne noteq ;перейти, в случае если R12 не равно R13

noteq:

4. Вычитание константы из регистра- SUBI Rd, K

Операция: Rd=Rd-K

Описание:Вычитает из регистраRd значение константы К. Результат помещается обратно в регистр.
Размещено на реф.рф
Данная команда применима только к старшей половинœе регистров общего назначения

Пример:subi R22,$11 ;вычесть $11 из R22

brne noteq ;перейти, в случае если R22 не равно $11

noteq:

5. Декремент РОН- DEC Rd

Операция:Rd=Rd-1

Описание:Уменьшает содержимое регистра Rd на единицу. Так как эта команда не влияет на флаг переноса С, она идеально подходит для организации счетчика числа интераций цикла при выполнении вычислений над многоразрядными числами. При работе с беззнаковыми числами для выполнения перехода в соответствии с результатом выполнения команды могут использоваться только команды условного перехода BREQ и BRNE. При работе с числами в дополнительном коде могут использоваться всœе команды условного перехода для знаковых проверок. Флаг V устанавливается в ʼʼ1ʼʼ только в том случае, в случае если до выполнения операции в регистре находилось значение $80

Пример:ldi R17,$10 ;записать число16 в регистр R17

loop:

add R1,R2 ;R1=R1+R2

dec R17 ;декрементировать R17

brne loop ;перейти, в случае если R17 не равно 0

6. Инкремент РОН- INC Rd

Операция:Rd=Rd+1

Описание:Увеличивает содержимое регистра Rd на единицу. Так как эта команда не влияет на флаг переноса С, она идеально подходит для организации счетчика числа интераций цикла при выполнении вычислений над многоразрядными числами. При работе с беззнаковыми числами для выполнения перехода в соответствии с результатом выполнения команды могут использоваться только команды условного перехода BREQ и BRNE. При работе с числами в дополнительном коде могут использоваться всœе команды условного перехода для знаковых проверок. Флаг V устанавливается в ʼʼ1ʼʼ только в том случае, в случае если до выполнения операции в регистре находилось значение $7F

Пример:clr R22 ;очистить регистр R22

loop:

inc R22 ;R22=R22+1

cpi R22,$4F ;

brne loop ;продолжать цикл, в случае если R22 не равно $4F

7. Арифметический сдвиг вправо- ASR Rd

Операция:Rd(n)=Rd(n+1),n=0…6

Описание:Сдвигает содержимое регистра Rd на 1 разряд вправо. Состояние 7-го разряда не изменяется. Значение 0-го разряда помещается в флаг С регистра SPEG. Часто используется для делœения чисел со знаком на два

Пример:ldi R16,$10 ;записать число 16 в регистр R16

asr R16 ; R16=R16/2

ldi R17,$FC ;записать число -4 в регистр R17

asr R17 ; R17=R17/2

8. Логический сдвиг влево- LSL Rd

Операция: Rd(n+1)=Rd(n),Rd(0)=0

Описание:Сдвигает всœе разряды регистра Rd влево. Разряд b0 сбрасывается в ʼʼ0ʼʼ, а разряд b7 загружается в флаг С регистра SPEG. Эквивалентна команде ADD Rd,Rd

Пример:add R0,R4 ; R0=R0+R4

lsl R0 ;R0=R0*2

9. Логический сдвиг вправо- LSR Rd

Операция: Rd(n)=Rd(n+1) Rd(7)=0

Описание:Сдвигает всœе разряды регистра Rd вправо. Разряд b7 сбрасывается в ʼʼ0ʼʼ, а разряд b0 загружается в флаг С регистра SPEG.

Пример: add R0,R4 ; R0=R0+R4

lsr R0 ;R0=R0/2

Таблица 2.2. Команды арифметических операций и команд сдвига

Мнемоника Описание Операция Циклы Флаги
ADD Rd,Rr Сложение двух РОН Rd=Rd+Rr Z,C,N,V,H
ADC Rd,Rr Сложение двух РОН с переносом Rd=Rd+Rd+C Z,C,N,V,H
ADIW Rd,K Сложение регистровой пары с константой Rdh:Rdl=Rdh:Rdl+K Z,C,N,V,S
SUB Rd,Rr Вычитание двух РОН Rd=Rd-Rr Z,C,N,V,H
SUBI Rd,K Вычитание константы из РОН Rd=Rd-K Z,C,N,V,H
SBC Rd,Rr Вычитание двух РОН с заемом Rd=Rd-Rr-C Z,C,N,V,H
SBCI Rd,K Вычитание константы из РОН с заемом Rd=Rd-K-C Z,C,N,V,H
SBIW Rd,K Вычитание константы из регистровой пары Rdh:Rdl=Rdh:Rdl-K Z,C,N,V,S
DEC Rd Декремнт РОН Rd=Rd-1 Z,N,V,
INC Rd Инкремент РОН Rd=Rd+1 Z,N,V
ASR Rd Арифметический сдвиг вправо Rd(n)=Rd(n+1), n=0…6 Z,C,N,V
LSL Rd Логический сдвиг влево Rd(n+1)=Rd(n),Rd(0)=0 Z,C,N,V
LSR Rd Логический сдвиг вправо Rd(n)=Rd(n+1) Rd(7)=0 Z,C,N,V
ROL Rd Сдвиг влево через перенос Rd(0)=C,Rd(n+1)=Rd(n),C=Rd(7) Z,C,N,V
ROR Rd Сдвиг вправо через перенос Rd(7)=C,Rd(n)=Rd(n+1), C=Rd(0) Z,C,N,V

2. 3.3. Команды операций с битами

К данной группе относятся команды, выполняющие установку или сброс заданного разряда РОН или РВВ. Причем для изменения состояния разрядов регистра состояния SREG имеются также дополнительные команды (точнее, эквивалентные мнемонические обозначения общих команд), т.к. проверка состояния разрядов именно этого регистра производится чаще всœего. Условно к этой группе можно отнести также две команды передачи управления типа ʼʼпроверка/пропускʼʼ, которые пропускают следующую команду исходя из состояния разрядов РОН или РВВ.

Всем командам данной группы (Таблица 2.3) требуется один машинный цикл для выполнения, за исключением случаев, когда в результате проверки происходит пропуск команды. В этом случае команда выполняется за два или три машинных цикла исходя из пропускаемой команды.

Рассмотрим наиболее часто используемые команды операций с битами с примерами ( всœе команды операций с битами представлены в таблице 2.3):

1. Сбросить разряд- CBI A, b

Операция: A.b = 0

Описание:Сбрасывает разряд b регистра ввода/вывода, расположенного по адресу А пространства ввода/вывода. Эта команда применима только к младшим 32 регистрам (адреса 0…31)

Пример: cbi $12,7 ;cбросить 7-ой разряд порта D

2. Установить разряд РВВ- SBI A, b

Операция:A.b = 1

Описание:Устанавливает разряд b регистра ввода/вывода, расположенного по адресу А пространства ввода/вывода. Эта команда применима только к младшим 32 регистрам (адреса 0…31)

Пример:out $1E,R0 ;задать адрес ячейки EEPROM

sbi $1C,0 ;установить запрос на чтение

in R1,$1D ;считать данные из EEPROM

3. Пересылка флага Т в разряд РОН- BLD Rd,b

Операция:Rd.b = T

Описание:Копирует флаг Т регистраSPEG в разряд b регистра Rd

Пример:bst R1,2 ;сохранить 2-ой разряд регистра R1 в T

Bld R0,4 ;записать флаг Т в 4-й разряд регистра R0

4. Запись разряда РОН в флаг Т- BST Rd,b

Операция:T=Rd.b

Описание:Копирует разряд b регистра Rd в флаг Т регистра SPEG

Пример:bst R1,2 ;сохранить 2-й разряд регистра R1 вТ

bld R0,4 ;записать флаг Т в 4-й разряд регистра R0

5. Общее запрещение прерываний- CLI

Операция:I=0

Описание:Сбрасывает в ʼʼ0ʼʼ флаг общего разрешения прерываний I регистра SPEG. Эквивалентна команде BCLR 7

Пример:cli ;запретить прерывания

in R11,$16 ;прочитать состояние порта В

sei ;разрешить прерывания

6. Общее разрешение прерываний- SEI

Операция: I=1

Описание:Устанавливает в ʼʼ1ʼʼ флаг общего разрешения прерываний I регистра SPEG. Эквивалентна команде BSET 7

Пример:cli ;запретить прерывания

in R13,$16 ;прочитать состояние порта В

sei ;разрешить прерывания

7. Cброс флага Т- CLT

Операция: T=0

Описание:Сбрасывает в ʼʼ0ʼʼ флаг Т регистра SPEG. Эквивалентна команде BCLR 6

Пример:clt ;сбросить флаг Т

8. Установка флага Т- SET

Операция:T=1

Описание:Устанавливает в ʼʼ1ʼʼ флаг Т регистра SPEG.Эквивалентна команде BSET 6

Пример:set ;установить флаг Т

Таблица 2.3. Команды операций с разрядами

Мнемоника Описание Операция Циклы Флаги
CBR Rd,K Сброс разряда(ов) РОН Rd=Rd*($FF-K) Z,N,V
SBR Rd,K Установка разряда(ов) РОН Rd=RdÚK Z,N,V
CBI A,b Сброс разряда РВВ A. b = 0 -
SBI A,b Установка разряда РВВ A.b = 1 -
BCLR s Сброс флага SREG.s = 0 SREG.s
BSET s Установка флага SREG.s = 1 SREG.s
BLD Rd,b Загрузка разряда РОН из флага Т (SREG) Rd.b = T -
BST Rr,b Запись разряда РОН в флаг Т (SREG) T=Rd.b T
CLC Сброс флага переноса C=0 C
SEC Установка флага переноса C=1 C
CLN Сброс флага отр.
Размещено на реф.рф
числа
N=0 N
SEN Установка флага отр.
Размещено на реф.рф
числа
N=1 N
CLZ Сброс флага нуля Z=0 Z
SEZ Установка флага отр.
Размещено на реф.рф
числа
Z=1 Z
CLI Общее запрещение прерываний I=0 I
SEI Общее разрешение прерываний I=1 I
CLS Сброс флага знака S=0 S
SES Установка флага знака S=1 S
CLV Сброс флага переполнения доп. Кода V=0 V
SEV Установка флага переполнения доп. Кода V=1 V
CLT Сброс флага Т T=0 T
SET Установка флага Т T=1 T
CLH Сброс флага половинного переноса H=0 H
SEH Установка флага половинного переноса H=1 H

2.3.4. Команды пересылки данных

Команды этой группы (Таблица 2.4) предназначены для пересылки содержимого ячеек, находящихся в адресном пространстве памяти данных. Разделœение адресного пространства на три части (РОН, РВВ, ОЗУ) предопределило разнообразие команд данной группы. Пересылка данных, выполняемая командами группы, может производиться в следующих направлениях:

· РОНÛРОН;

· РОНÛРВВ;

· РОНÛ память данных.

Также к данной группе можно отнести стековые команды PUSH и POP (отсутствуют вAT90S1200), позволяющие сохранять в стеке и восстанавливать из стека содержимое РОН.

На выполнение команд данной группы требуется от одного до трех машинных циклов исходя из команды.

Рассмотрим наиболее часто используемые команды пересылки данных с примерами:

1. Пересылка между РОН- MOV Rd, Rr

Операция: Rd=Rr

Описание:Копирует содержимое регистра Rr в регистр Rd. Регистр-источник (Rr) не изменяется

Пример: mov R16, R0 ;переслать содержимое R0 в R16

call chec ;вызвать подпрограмму

chec:

cpi R16, $11 ;сравнить R16 с $11

ret ;вернуться из подпрограммы

2. Загрузка константы в РОН- LDI Rd, K

Операция:Rd=K

Описание:Загружает 8-разрядное число в регистр общего назначения Rd. Данная команда применима только к старшей половинœе РОН (адреса 16…31)

Пример: сlr R31 ;очистить ст. байт индексного регистра Z

ldi R30, $F0 ;загрузить адрес в регистр Z

lpm ;загрузить константу из памяти программ

;по адресу $00F0

3. Косвенное чтение памяти данных- LD Rd, Y

Операция:Rd=[Y]

Описание:Загружает один байт из адресного пространства памяти данных в регистр общего назначения Rd. Адрес ячейки памяти, к которой производится обращение, содержится в индексном регистре Y

Пример:clr R29 ;очистить ст. байт индексного регистра

ldi R28, $60 ;загрузить мл. байт адреса

ld R1, Y ;R1=[$0060]

4. Непосредственная загрузка из памяти данных- LDS Rd, K

Операция: Rd=[k]

Описание:Загружает один байт из адресного пространства памяти данных в регистр общего назначения Rd. Адрес ячейки памяти, к которой производится обращение, задается константой К.

Пример:lds R2, $FF00 ;R2=[$FF00]

add R2, R1 ;R2=R2+R1

sts $FF000, R2 ;записать результат по тому же адресу

5. Косвенная запись в память данных- ST X, Rd

Операция:[X]=Rr

Описание:Сохраняет содержимое регистра общего назначения Rr в памяти данных. Адрес ячейки памяти, к которой производится обращение, содержится в индексном регистре Х

Пример: clr R27 ;очистить ст. байт индексного регистра

ldi R26, $60 ;загрузить мл. байт адреса

st X, R1 ;загрузить R1 по адресу $0060

6. Непосредственная запись в память данных- STS K, Rd

Операция:[k]=Rd

Описание:Сохраняет содержимое регистра общего назначения Rd в памяти данных. Адрес ячейки памяти, к которой производится обращение, задается константой К

Пример:lds R2, $FF00 ;R2=[$FF00]

add R2, R1 ;R2=R2+R1

sts $FF000,R2 ;записать результат по тому же адресу

Таблица 2.4. Команды пересылки данных

Мнемоника Описание Операции Циклы Флаги
MOV Rd,Rr Пересылка между РОН Rd=Rr  
LDI Rd,K Загрузка константы в РОН Rd=K  
LD Rd,X Косвенное чтение Rd=[X]  
LD Rd,X+ Косвенное чтение с постинкрементом Rd=[X],X=X+1  
LD Rd,-X Косвенное чтение с преддекрементом X=X-1,Rd=[X]  
LD Rd,Y Косвенное чтение Rd=[Y]  
LD Rd,Y+ Косвенное чтение с постинкрементом Rd=[Y],Y=Y+1  
LD Rd,-Y Косвенное чтение с преддекрементом Y=Y-1,Rd=[Y]  
LDD Rd,Y+q Косвенное относительное чтение Rd=[Y+q]  
LD Rd,Z Косвенное чтение Rd=[Z]  
LD Rd,Z+ Косвенное чтение с постинкрементом Rd=[Z],Z=Z+1  
LD Rd,-Z Косвенное чтение с преддекрементом Z=Z-1,Rd=[Z]  
LDD Rd,Z+q Косвенное относительное чтение Rd=[Z+q]  
LDS Rd,k Непосредственное чтение из ОЗУ Rd=[k]  
ST X,Rr Косвенная запись [X]=Rr  
ST X+,Rr Косвенная запись с постинкрементом [X]=Rr,X=X+1  
ST -X,Rr Косвенная запись с постинкрементом X=X-1,[X]=Rr  
ST Y,Rr Косвенная запись [Y]=Rr  
ST Y+,Rr Косвенная запись с постинкрементом [Y]=Rr,Y=Y+1  
ST -Y,Rr Косвенная запись с преддекрементом Y=Y-1,[Y]=Rr  
STD Y+q,Rr Косвенная относительная запись [Y+q]=Rr  
ST Z,Rr Косвенная запись [Z]=Rr  
ST Z+,Rr Косвенная запись с постинкрементом [Z]=Rr,Z=Z+1  
ST -Z,Rr Косвенная запись с преддекрементом Z=Z-1,[Z]=Rr  
STD Z+q,Rr Косвенная относительная запись [Z+q]=Rr  
STS k,Rr Непосредственная запись в ОЗУ [k]=Rr  
LPM Загрузка данных из памяти программ R0={Z}  
IN Rd,A Пересылка из РВВ в РОН Rd=A  
OUT A,Rr Пересылка из РОН в РВВ A=Rr  
PUSH Rr Сохранение байта в стеке STACK=Rr  
POP Rd Извлечение байта из стека Rd=STACK  

2.3.5. Команды передачи управления

В эту группу (Таблица 2.5) входят команды перехода, вызова подпрограмм и возврат из них и команд типа ʼʼпроверка/пропускʼʼ, пропускающие следующую за ними команду при выполнении некоторого условия. Также к этой группе относятся команды сравнения, формирующие флаги регистра SREG и предназначенные, как правило, для работы совместно с командами условного перехода.

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

Рассмотрим наиболее часто используемые команды передачи управления с примерами:

1. Относительный безусловный переход- RJMP k

Операция: PC=PC+k+1

Описание:Выполняет переход по адресу, равному сумме содержимого счетчика команд и константы k. На практике вместо числовых значений смещения используются метки

Пример: сpi R16, $42 ;сравнить R16 с числом $42

brne M1 ;перейти к M1, в случае если R16 не равно $42

rjmp ok ;безусловный переход

M1:

add R16, R17 ;прибавить R16 к R17

ink R16 ;R16=R16+1

ok:

2. Относительный вызов подпрограммы- RCALL k

Операция:PC=PC+k+1

Описание:Выполняет переход к подпрограмме, адрес которой получается сложением содержимого счетчика команд с константой k. Адрес следующей за RCALL командой (2 байта) сохраняется в стеке. На практике вместо числовых значений смещения указываются метки подпрограмм

Пример:rcall routine ;вызвать подпрограмму

routine:

push R14 ;сохранить содержимое R14

pop R14 ;восстановить содержимое R14

ret ;возврат из подпрограммы

3. Возврат из подпрограммы- RET

Операция:PC=STACK

Описание:Выполняет возврат в то место, откуда подпрограмма была вызвана

Пример: rcall routine ;вызвать подпрограмму

routine:

push R14 ;сохранить содержимое R14

pop R14 ;восстановить содержимое R14

ret ;возврат из подпрограммы

4. Пропуск команды при равенстве двух РОН- CPSE Rd, Rr

Операция:В случае если Rd=Rr, то PC=PC+2(3), иначе РС=РС+1

Описание:Сравнивает содержимое двух регистров общего назначения Rd и Rr и пропускает следующую команду, в случае если в регистрах записаны одинаковые значения

Пример:ink R4 ;увеличить R4 (R4=R4+1)

cpse R4, R0;сравнить содержимоеR4 и R0

neg R4 ;проинвертировать R4, в случае если R4 не равно R0

5. Сравнение РОН- CP Rd, Rr

Операция: Rd-Rr

Описание:Сравнивает содержимое двух РОН путем вычитания содержимого регистра Rr из содержимого регистра Rd. Данная команда влияет только на флаги регистра состояния SPEG, которые устанавливаются в соответствии с результатом вычитания. Содержимое регистров не изменяется. Как правило, данная команда используется совместно с одной из команд условного перехода

Пример:cp R4, R19 ;сравнить R4 с R19 (R=R4-R19)

brne noteq ;перейти, в случае если R4<>R19

noteq:

6. Сравнение содержимого РОН с константой- CPI Rd, K

Операция: Rd-K

Описание:Сравнивает содержимое РОН Rd с константой К путем вычитания константы из содержимого регистра Rd. Данная команда влияет только на флаги регистра состояния SPEG, которые устанавливаются в соответствии с результатом вычитания. Содержимое регистра Rd не изменяется. Как правило, данная команда используется совместно с одной из команд условного перехода

Пример:cpi R19, 3 ;сравнить R19 с числом 3 (R=R19-3)

brne noteq ;перейти, в случае если R19<>3

noteq:

7. Переход по условию ʼʼравноʼʼ- BREQ k

Операция: В случае если Z=1, то PC=PC+k+1, иначе РС=РС+1

Описание:Условный относительный переход. Проверяет флаг нуля (Z) и выполняет переход, в случае если данный разряд установлен. Величина смещения k представляется числом в дополнительном коде. При выполнении данной команды сразу же после команды CP, CPI, SUB или SUBI переход произойдет только в том случае, в случае если число (со знаком или без знака), находящееся в регистре Rd, будет равно числу (со знаком или без знака), находящемуся в регистре Rr. Эквивалентна команде BRBS 1, k

Пример:cp R1, R0 ;сравнить R1 с R0

breq equal ;перейти, в случае если R1=R2

equal:

8. Переход по условию ʼʼне равноʼʼ- BRNE k

Операция: В случае если Z=0, то PC=PC+k+1, иначе РС=РС+1

Описание:Условный относительный переход. Проверяет флаг нуля (Z) и выполняет переход, в случае если данный разряд сброшен. Величина смещения k представляется числом в дополнительном коде. При выполнении данной команды сразу же после команды CP, CPI, SUB или SUBI переход произойдет только в том случае, в случае если числа (со знаком или без знака), находящиеся в регистрах Rd и Rr, не будут равны. Эквивалентна команде BRBC 1,k

Пример: eor R27, R27 ;очистить R27

loop: inc R27 ;R27=R27+1

cpi R27, 5 ;сравнить R27 с 5

brne loop ;перейти, в случае если R27 не равно 5

9. Переход по условию ʼʼвыше или равноʼʼ (для беззнаковых данных)- BRSH k

Операция:В случае если C=0, то PC=PC+k+1, иначе РС=РС+1

Описание:Условный относительный переход. Проверяет флаг переноса (С) и выполняет переход, в случае если данный разряд сброшен. Величина смещения k представляется числом в дополнительном коде. При выполнении данной команды сразу же после команды CP, CPI, SUB или SUBI переход произойдет только в том случае, в случае если беззнаковое число, находящееся в регистре Rd, будет больше (или равно) беззнакового числа, находящегося в регистре Rr. Эквивалентна команде BRBC 0,k

Пример:subi R19,4 ;R19=R19-4

brsh hgsm ;перейти, в случае если R19 больше или равно 4

hgsm:

10. Переход по условию ʼʼменьшеʼʼ (для беззнаковых данных)- BRLO k

Операция:В случае если C=1, то PC=PC+k+1, иначе РС=РС+1

Описание:Условный относительный переход. Проверяет флаг переноса (С) и выполняет переход, в случае если данный разряд установлен. Величина смещения k представляется числом в дополнительном коде. При выполнении данной команды сразу же после команды CP, CPI, SUB или SUBI переход произойдет только в том случае, в случае если беззнаковое число, находящееся в регистре Rd, будет меньше беззнакового числа, находящееся в регистре Rr. Эквивалентна команде BRBS 0,k

Пример:eor R19, R19 ;очистить R19

loop: inc R19 ;R19=R19+1

cpi R19, $10 ;сравнить R19 с $10

brlo loop ;перейти, в случае если R19<$10

11. Переход по условию ʼʼотрицательное значениеʼʼ- BRMI k

Операция:В случае если N=1, то PC=PC+k+1, иначе РС=РС+1

Описание:Условный относительный переход. Проверяет флаг отрицательного значения (N) и выполняет переход, в случае если данный разряд установлен. Величина смещения k представляется числом в дополнительном коде. Эквивалентна команде BRBS 2, k

Пример:subi R18, 4 ;R18=R18-4

brmi minus ;перейти, в случае если результат отрицательный

minus:

12. Переход по условию ʼʼположительное значениеʼʼ- BRPL k

Операция:В случае если N=0, то PC=PC+k+1, иначе РС=РС+1

Описание:Условный относительный переход. Проверяет флаг отрицательного значения (N) и выполняет переход, в случае если данный разряд сброшен. Величина смещения k представляется числом в дополнительном коде. Эквивалентна команде BRBC 2, k

Пример:subi R26, $50 ;R26=R26-$50

brpl plus ;перейти, в случае если результат положительный

厠ਣ …

plus:

Таблица 2.5. Команды передачи управления

Мнемоника Описание Операция Цикл Флаги
RJMP k Относительный безусловный переход PC=PC+k+1 -
IJMP Косвенный безусловный переход PC=Z -
RCALL Относительный вызов подпрограммы PC=PC+k+1 -
ICALL Косвенный вызов подпрограммы PC=Z -
RET Возврат из подпрограммы PC=STACK -
RETI Возврат из подпрограммы обработки прерывания PC=STACK I
CP Rd,Rr Сравнение РОН Rd-Rr Z,N,V,C,H
CPC Rd,Rr Сравнение РОН с учетом переноса Rd-Rr-C Z,N,V,C,H
CPI Rd,K Сравнение РОН с константой Rd-K Z,N,V,C,H
CPSE Rd,Rr Сравнение и пропуск след. команды при равенстве В случае если Rd=Rr, то PC=PC+2(3) 1/2/3 -
SBRC Rr,b Пропуск след. команды, в случае если разряд РОН сброшен В случае если Rr.b=0, то PC=PC+2(3) 1/2/3 -
SBRS Rr,b Пропуск след. команды, в случае если разряд РОН установлен В случае если Rr.b=1, то PC=PC+2(3) 1/2/3 -
SBIC A,b Пропуск след. команды, в случае если разряд РВВ сброшен В случае если A.b=0, то PC=PC+2(3) 1/2/3 -
SBIS A,b Пропуск след. команды, в случае если разряд РВВ установлен В случае если A.b=1, то PC=PC+2(3) 1/2/3 -
BRBC s,k Переход, в случае если флаг s регистра SREG сброшен В случае если SREG.s =0, то PC=PC+k+1 1/2 -
BRBS s,k Переход, в случае если флаг s регистра SREG установлен В случае если SREG.s =1, то PC=PC+k+1 1/2 -
BRCS k Переход по переносу В случае если C=1, то PC=PC+k+1 1/2 -
BRCC k Переход, в случае если нет переноса В случае если C=0, то PC=PC+k+1 1/2 -
BREQ k Переход по условию ʼʼравноʼʼ В случае если Z=1, то PC=PC+k+1 1/2 -
BRNE k Переход по условию ʼʼне равноʼʼ В случае если Z=0, то PC=PC+k+1 1/2 -
BRSH k Переход по условию ʼʼвыше или равноʼʼ В случае если C=0, то PC=PC+k+1 1/2 -
BRLO k Переход по условию ʼʼменьшеʼʼ В случае если C=1, то PC=PC+k+1 1/2 -
BRMI Переход по условию ʼʼотрицательное значениеʼʼ В случае если N=1, то PC=PC+k+1 1/2 -
BRPL Переход по условию ʼʼположительное значениеʼʼ В случае если N=0, то PC=PC+k+1 1/2 -
BRGE Переход по условию ʼʼбольше или равноʼʼ (числа со знаком) В случае если (NÅV)=0, то PC=PC+k+1 1/2 -
BRLT Переход по условию ʼʼменьше нуляʼʼ (числа со знаком) В случае если (NÅV)=1, то PC=PC+k+1 1/2 -
BRHS Переход по половинному переносу В случае если H=1, то PC=PC+k+1 1/2 -
BRHC Переход, в случае если нет половинного переноса В случае если H=0, то PC=PC+k+1 1/2 -
BRTS Переход, в случае если флаг T установлен В случае если T=1, то PC=PC+k+1 1/2 -
BRTC Переход, в случае если флаг T сброшен В случае если T=0, то PC=PC+k+1 1/2 -
BRVS Переход по переполнению доп. кода В случае если V=1, то PC=PC+k+1 1/2 -
BRVC Переход, в случае если нет переполнения доп. кода В случае если V=0, то PC=PC+k+1 1/2 -
BRID Переход, в случае если прерывания запрещены В случае если I=0, то PC=PC+k+1 1/2 -
BRIE Переход, в случае если прерывания разрешены В случае если I=1, то PC=PC+k+1 1/2 -

2.3.6. Команды управления системой

В эту группу входят всœего 3 команды:

· NOP – пустая команда;

· SLEEP – перевод микроконтроллера в режим пониженного энергопотребления;

· WDR – сброс сторожевого таймера.

Команды NOP и WDR выполняются за один машинный цикл, а команда SLEEP – за четыре машинных цикла.

Настройка IDE Arduino для генерации листинга на ассемблере

Порой возникает необходимость в изучении кода, генерируемого компилятором. Это помогает понять, что получается «на выходе» при использовании тех или иных функций, приемов, алгоритмов. Существуют специализированные инструменты — дизассемблеры, преобразующие машинный код в текст на языке ассемблера, но это в основном платные программы. Мне же в большинстве случаев вполне хватает результатов программы avr-objdump, которой и посвящена эта публикация. avr-objdump — это программа, отображающая различную информацию из объектных файлов. Параметры, с которыми она вызывается, определяют, какая именно информация должна быть отображена. Например, для дизассемблирования объектного файла может быть использована следующая команда:

avr-objdump -d file_name.elf

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

avr-objdump -S file_name.elf

Полный список возможных параметров вы можете найти по ссылке: http://ccrma.stanford.edu/planetccrma/man/man1/avr-objdump.1.html

Хорошо, где взять avr-objdump и объектный файл? Первый входит в состав IDE Arduino и расположен в каталоге Arduino_dir\hardware\tools\avr\bin\, где Arduino_dir — это путь установки IDE Arduino.

Что касается объектных (elf) файлов, то они создаются средой разработки при компиляции проекта и находятся во временной папке скетча. Найти их можно следующим образом:

1. Если у вас еще не включен вывод сообщений при компиляции скетчей, то перейдите в окно настроек IDE Arduino и установите соответствующую опцию:

2. После компиляции скетча в последних строках, выводимых средой разработки, вы увидите примерно следующее:


Выделенный фрагмент — это временный каталог для текущего скетча, в него помещаются все файлы, генерируемые в ходе компиляции, в том числе объектный файл.

Остается перейти в этот каталог и скопировать elf файл в Arduino_dir\hardware\tools\avr\bin\. Теперь, когда у нас avr-objdump и объектный файл находятся в одном каталоге (конечно, это необязательно, если в командах указывать полный путь к файлам), запускаем командную строку и вводим одну из приведенных ранее команд для дизассемблирования с поправкой на имя файла. В моем случае команда выглядит так:

avr-objdump.exe -S sketch_jun18a.ino.elf

В интерпретаторе командной строки необязательно вводить имя файла целиком: достаточно ввести несколько первых символов, затем нажимать TAB, пока интерпретатор не подставит нужное имя.

Итак, выполняем введенную команду и видим весьма объемный результат ее работы:


Для вывода результата работы avr-objdump в файл воспользуемся перенаправлением вывода:

avr-objdump.exe -S sketch_jun18a.ino.elf > my_file.asm

При выполнении данной команды в каталоге с avr-objdump.exe будет создан файл my_file.asm, в который будет сохранен результат работы команды:


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

Я для генерации ассемблерного листинга использую команду меню Скетч->Экспорт бинарного файла среды разработки Ардуино. При таком способе результат дизассемблирования сохраняется в каталог скетча (вместе с hex файлом), не приходится лазить по временным папкам. Для настройки IDE Arduino на генерацию ассемблерного листинга выполним следующие действия:

1. Переходим в каталог Arduino_dir\hardware\arduino\avr\ и открываем файл platform.txt в текстовом редакторе.

2. Находим команды для сохранения hex файла и добавляем после них следующую строку:

recipe.hooks.savehex.postsavehex.1.pattern=»{compiler.path}/elf2asm.bat» «{compiler.path}avr-objdump» «{build.path}/{build.project_name}.elf» «{sketch_path}/{build.project_name}.asm»

Здесь мы используем так называемый хук, который выполняется после сохранения hex файла. Описание всех доступных хуков и самого файла platform.txt доступны по ссылке https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5-3rd-party-Hardware-specification. Ниже скриншот моего файла platform.txt после внесения указанных изменений:
3. Переходим в каталог Arduino_dir\hardware\tools\avr\bin\, создаем файл с именем elf2asm.bat и вставляем в него следующий текст:

%1 -S %2 > %3

Параметр -S определяет генерацию смешанного листинга. Использование bat файла обусловлено тем, что мы не можем использовать перенаправление вывода непосредственно в файле platform.txt.

На этом настройка IDE Arduino завершена. Можно переключиться в нее и проверить корректность работы, выбрав команду «Экспорт бинарного файла» в меню «Скетч». В каталоге со скетчем должен появиться файл с расширением asm. Приведенный способ дизассемблирования скетча пригодится нам в следующей публикации для анализа кода и продолжения темы обработки прерываний.

% PDF-1.6 % 8508 0 объект > эндобдж xref 8508 436 0000000016 00000 н. 0000010522 00000 п. 0000010657 00000 п. 0000010789 00000 п. 0000010826 00000 п. 0000011201 00000 п. 0000011350 00000 п. 0000011494 00000 п. 0000011864 00000 п. 0000012288 00000 п. 0000012933 00000 п. 0000013087 00000 п. 0000013394 00000 п. 0000013894 00000 п. 0000014358 00000 п. 0000014816 00000 п. 0000015488 00000 п. 0000016349 00000 п. 0000017295 00000 п. 0000018108 00000 п. 0000018210 00000 п. 0000046690 00000 н. 0000046922 00000 п. 0000047426 00000 п. 0000047537 00000 п. 0000086059 00000 п. 0000086286 00000 п. 0000086950 00000 п. 0000087039 00000 п. 0000102213 00000 н. 0000102452 00000 н. 0000102669 00000 н. 0000102745 00000 н. 0000102894 00000 п. 0000103112 00000 н. 0000103290 00000 н. 0000103472 00000 н. 0000103680 00000 п. 0000103843 00000 н. 0000104066 00000 н. 0000104284 00000 п. 0000104445 00000 н. 0000104676 00000 н. 0000104912 00000 н. 0000105072 00000 н. 0000105295 00000 н. 0000105511 00000 п. 0000105672 00000 н. 0000105895 00000 н. 0000106126 00000 п. 0000106287 00000 н. 0000106499 00000 н. 0000106735 00000 н. 0000106896 00000 н. 0000107107 00000 н. 0000107369 00000 н. 0000107531 00000 н. 0000107756 00000 н. 0000108001 00000 н. 0000108165 00000 н. 0000108390 00000 н. 0000108705 00000 н. 0000108865 00000 н. 0000109090 00000 н. 0000109407 00000 н. 0000109572 00000 н. 0000109796 00000 н. 0000110096 00000 н. 0000110259 00000 н. 0000110484 00000 н. 0000110722 00000 н. 0000110884 00000 н. 0000111108 00000 н. 0000111339 00000 н. 0000111499 00000 н. 0000111725 00000 н. 0000111914 00000 н. 0000112074 00000 н. 0000112301 00000 н. 0000112517 00000 н. 0000112677 00000 н. 0000112902 00000 н. 0000113127 00000 н. 0000113288 00000 н. 0000113512 00000 н. 0000113722 00000 н. 0000113884 00000 н. 0000114108 00000 н. 0000114332 00000 н. 0000114492 00000 н. 0000114718 00000 н. 0000114957 00000 н. 0000115117 00000 н. 0000115343 00000 н. 0000115570 00000 н. 0000115731 00000 н. 0000115956 00000 н. 0000116198 00000 н. 0000116358 00000 п. 0000116585 00000 н. 0000116814 00000 н. 0000116974 00000 н. 0000117202 00000 н. 0000117420 00000 н. 0000117582 00000 н. 0000117806 00000 н. 0000118058 00000 н. 0000118220 00000 н. 0000118444 00000 н. 0000118705 00000 н. 0000118866 00000 н. 0000119090 00000 н. 0000119321 00000 н. 0000119482 00000 н. 0000119706 00000 н. 0000119955 00000 н. 0000120115 00000 н. 0000120337 00000 н. 0000120597 00000 н. 0000120758 00000 н. 0000120986 00000 н. 0000121254 00000 н. 0000121414 00000 н. 0000121638 00000 н. 0000121874 00000 н. 0000122034 00000 н. 0000122255 00000 н. 0000122568 00000 н. 0000122727 00000 н. 0000122950 00000 н. 0000123178 00000 н. 0000123337 00000 н. 0000123557 00000 н. 0000123800 00000 н. 0000123960 00000 н. 0000124180 00000 н. 0000124416 00000 н. 0000124576 00000 н. 0000124797 00000 н. 0000125014 00000 н. 0000125172 00000 н. 0000125397 00000 н. 0000125629 00000 н. 0000125788 00000 н. 0000126012 00000 н. 0000126245 00000 н. 0000126404 00000 н. 0000126627 00000 н. 0000126872 00000 н. 0000127030 00000 н. 0000127253 00000 н. 0000127487 00000 н. 0000127648 00000 н. 0000127872 00000 н. 0000128108 00000 н. 0000128267 00000 н. 0000128488 00000 н. 0000128736 00000 н. 0000128896 00000 н. 0000129120 00000 н. 0000129359 00000 н. 0000129517 00000 н. 0000129738 00000 н. 0000129944 00000 н. 0000130104 00000 п. 0000130329 00000 н. 0000130541 00000 н. 0000130701 00000 п. 0000130923 00000 п. 0000131144 00000 н. 0000131302 00000 н. 0000131523 00000 н. 0000131775 00000 н. 0000131934 00000 н. 0000132160 00000 н. 0000132382 00000 н. 0000132541 00000 н. 0000132767 00000 н. 0000132989 00000 н. 0000133148 00000 н. 0000133368 00000 н. 0000133595 00000 н. 0000133756 00000 н. 0000133977 00000 н. 0000134191 00000 п. 0000134350 00000 н. 0000134573 00000 н. 0000134799 00000 н. 0000134958 00000 н. 0000135181 00000 н. 0000135408 00000 н. 0000135567 00000 н. 0000135788 00000 н. 0000136015 00000 н. 0000136173 00000 п. 0000136394 00000 н. 0000136653 00000 н. 0000136812 00000 н. 0000137034 00000 п. 0000137278 00000 н. 0000137436 00000 н. 0000137658 00000 н. 0000137876 00000 н. 0000138035 00000 н. 0000138257 00000 н. 0000138567 00000 н. 0000138725 00000 н. 0000138948 00000 н. 0000139255 00000 н. 0000139414 00000 н. 0000139635 00000 н. 0000139932 00000 н. 0000140091 00000 н. 0000140313 00000 н. 0000140534 00000 н. 0000140694 00000 п. 0000140914 00000 н. 0000141127 00000 н. 0000141285 00000 н. 0000141509 00000 н. 0000141724 00000 н. 0000141883 00000 н. 0000142109 00000 н. 0000142306 00000 н. 0000142464 00000 н. 0000142686 00000 н. 0000142892 00000 н. 0000143051 00000 н. 0000143262 00000 н. 0000143512 00000 н. 0000143671 00000 н. 0000143896 00000 н. 0000144113 00000 п. 0000144271 00000 н. 0000144493 00000 н. 0000144741 00000 н. 0000144902 00000 н. 0000145126 00000 н. 0000145401 00000 п. 0000145562 00000 н. 0000145784 00000 н. 0000146029 00000 н. 0000146190 00000 п. 0000146410 00000 н. 0000146659 00000 н. 0000146818 00000 н. 0000147042 00000 н. 0000147253 00000 н. 0000147412 00000 н. 0000147635 00000 н. 0000147884 00000 н. 0000148043 00000 н. 0000148264 00000 н. 0000148502 00000 н. 0000148663 00000 н. 0000148887 00000 н. 0000149154 00000 н. 0000149314 00000 н. 0000149534 00000 п. 0000149771 00000 п. 0000149916 00000 н. 0000150122 00000 н. 0000150281 00000 н. 0000150488 00000 н. 0000150721 00000 н. 0000150880 00000 н. 0000151102 00000 н. 0000151336 00000 н. 0000151498 00000 н. 0000151721 00000 н. 0000151946 00000 н. 0000152105 00000 н. 0000152325 00000 н. 0000152524 00000 н. 0000152682 00000 н. 0000152905 00000 н. 0000153126 00000 н. 0000153284 00000 н. 0000153505 00000 н. 0000153724 00000 н. 0000153883 00000 н. 0000154104 00000 н. 0000154331 00000 н. 0000154489 00000 н. 0000154713 00000 н. 0000154925 00000 н. 0000155084 00000 н. 0000155307 00000 н. 0000155532 00000 н. 0000155692 00000 н. 0000155915 00000 н. 0000156130 00000 н. 0000156288 00000 н. 0000156508 00000 н. 0000156733 00000 н. 0000156892 00000 н. 0000157116 00000 н. 0000157358 00000 н. 0000157519 00000 н. 0000157740 00000 н. 0000157970 00000 н. 0000158129 00000 н. 0000158353 00000 н. 0000158575 00000 н. 0000158734 00000 н. 0000158959 00000 н. 0000159191 00000 п. 0000159350 00000 н. 0000159573 00000 н. 0000159814 00000 н. 0000159973 00000 н. 0000160194 00000 п. 0000160436 00000 н. 0000160596 00000 н. 0000160820 00000 н. 0000161107 00000 н. 0000161267 00000 н. 0000161488 00000 н. 0000161707 00000 н. 0000161866 00000 н. 0000162088 00000 н. 0000162324 00000 н. 0000162486 00000 н. 0000162706 00000 н. 0000162949 00000 н. 0000163109 00000 н. 0000163336 00000 н. 0000163581 00000 н. 0000163739 00000 н. 0000163960 00000 н. 0000164211 00000 н. 0000164369 00000 н. 0000164589 00000 н. 0000164849 00000 н. 0000165007 00000 н. 0000165228 00000 н. 0000165446 00000 н. 0000165605 00000 н. 0000165826 00000 н. 0000166054 00000 н. 0000166212 00000 н. 0000166435 00000 н. 0000166657 00000 н. 0000166815 00000 н. 0000167040 00000 н. 0000167287 00000 н. 0000167447 00000 н. 0000167669 00000 н. 0000167910 00000 н. 0000168070 00000 н. 0000168290 00000 н. 0000168554 00000 н. 0000168716 00000 н. 0000168938 00000 н. 0000169205 00000 н. 0000169366 00000 н. 0000169587 00000 н. 0000169840 00000 н. 0000169999 00000 н. 0000170222 00000 п. 0000170487 00000 н. 0000170645 00000 н. 0000170869 00000 н. 0000171130 00000 н. 0000171291 00000 н. 0000171513 00000 н. 0000171732 00000 н. 0000171891 00000 н. 0000172113 00000 н. 0000172314 00000 н. 0000172473 00000 н. 0000172696 00000 н. 0000172924 00000 н. 0000173084 00000 н. 0000173306 00000 н. 0000173544 00000 н. 0000173702 00000 н. 0000173924 00000 н. 0000174169 00000 н. 0000174328 00000 н. 0000174551 00000 н. 0000174807 00000 н. 0000174965 00000 н. 0000175187 00000 н. 0000175484 00000 н. 0000175643 00000 н. 0000175866 00000 н. 0000176092 00000 н. {ч%.| ږ J WFGx | * # ̔? Ǡh H \ .t6CIࣿlkw.ujM {4Q 3B +? S»dlV% c.Ud (

Руководства по AVR — Работа с регистрами R0

Микроконтроллеры

AVR содержат 32 8-битных рабочих регистра общего назначения, которые напрямую подключены к Арифметико-логическому устройству (ALU). Почти все манипуляции с данными в вашей программе должны выполняться с помощью рабочих регистров общего назначения, поэтому важно понимать предлагаемые ими инструкции и способы их использования.

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

Передача данных

Архитектура AVR имеет много инструкций для передачи данных, но пока мы рассмотрим только те, которые относятся к передаче данных между рабочими регистрами общего назначения. Они показаны в таблице ниже.

Мнемоника Описание
ldi немедленная загрузка
мов копировальный аппарат
movw копировать регистровую пару

Одна из наиболее распространенных инструкций, которые вы будете использовать, — это ldi — загрузить немедленно . ldi позволяет загружать постоянное 8-битное значение непосредственно в указанный регистр. Например, константа 85 может быть загружена в регистр r16 следующим кодом:

  ldi r16,85; загрузить значение 85 в регистр 16  

Константу можно указать разными способами.Все нижеприведенные значения действительны и эквивалентны:

  ldi r16,85; загрузить 85 в регистр 16 (десятичный)
ldi r16,0x55; загрузить 85 в регистр 16 (шестнадцатеричный)
ldi r16 - 55 долларов США; загрузить 85 в регистр 16 (также шестнадцатеричный)
ldi r16,0125; загрузить 85 в регистр 16 (восьмеричный)
ldi r16,0b01010101; загрузить 85 в регистр 16 (двоичный)  

Обратите внимание на порядок инструкции и операндов. Сначала идет инструкция, за ней следует назначение, а затем , затем — вход. Сначала это может сбивать с толку, но полезно читать это как загружает r16 со значением 85 .

ldi является частью специального набора непосредственных инструкций . Непосредственные инструкции работают с регистром и требуют, чтобы в качестве операнда была указана константа. Непосредственные инструкции очень полезны, поскольку они позволяют вам работать с числом, которое вы указываете в самом коде. Однако не все из 32 регистров могут использоваться с непосредственными инструкциями — только регистры с 16 по 31.

Помните: Мгновенные инструкции работают только с регистрами с 16 по 31.Попытка использовать их с регистрами от 0 до 15 приведет к ошибке.

Содержимое одного регистра можно скопировать в другой регистр с помощью инструкции mov. Поскольку ldi работает только с регистрами с 16 по 31, инструкция mov — удобный способ загрузить константу в один из младших 16 регистров. Например, приведенный ниже код показывает, что константа загружается в r16, а mov используется для ее копирования в r0.

  ldi r16,0x55; загрузить 0x55 в r16
mov r0, r16; скопировать содержимое r16 в r0  

Как и в случае с ldi, порядок операндов в инструкции mov может сбивать с толку с указанием места назначения первым, а затем источника.Опять же, полезно читать как скопировать в r0 содержимое r16

Примечание: Хотя инструкция mov выглядит как переместить , она фактически копирует содержимое одного регистра в другой. После инструкции исходный регистр остается без изменений.

Хотя AVR имеет 8-битную архитектуру, есть несколько избранных инструкций, которые позволяют вам работать с 16-битными значениями (словами). Одна из них — инструкция movw, которая позволяет копировать содержимое одной пары регистров в другую пару регистров.Например, в приведенном ниже коде показано, как загрузить константу 0x1234 в r16 и r17 и скопировать ее в r0 и r1.

  ldi r16,0x34; загрузить младший байт 0x1234 в r16
ldi r17,0x12; загрузить старший байт 0x1234 в r17

movw r1: r0, r17: r16; скопируйте содержимое r17: r16 в r1: r0  

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

  movw r0, r16; скопируйте содержимое r17: r16 в r1: r0  

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

.
  movw r2: r1, r19: r18; r2: r1 - недопустимый операнд для movw  

Как и mov, movw выполняет копию , а не перемещение. Исходные регистры не меняются после этой инструкции.

Логические инструкции

ЦП поддерживает логические операции между двумя регистрами или регистром и константой. Основные логические инструкции показаны в таблице ниже.

Мнемоника Описание
и логическое И
andi логическое И с немедленным
или логическое ИЛИ
ори логическое ИЛИ с немедленным
eor эксклюзивно ИЛИ
ком одно дополнение
нег дополнение до двух

И ИЛИ и Эксклюзив

Есть две логические непосредственные инструкции — andi и ori, которые вычисляют логические И или ИЛИ между регистром и константой.Они записываются так же, как ldi, сначала с мнемоникой инструкций, затем с регистром, а затем с константой. Некоторые примеры показаны ниже:

  ldi r16,0x55; загрузить 0x55 в r16
andi r16,0x0F; маскировать верхние 4 бита r16 (результат = 0x05)

ldi r17,0x00; загрузить 0x00 в r17
ori r17,0x0F; установить младшие 4 бита r17 (результат = 0x0F)  

Как и ldi, andi и ori работают только с регистрами с 16 по 31.

Другие логические инструкции не являются непосредственными — они работают с двумя регистрами, а не с регистром и константой.ЦП может вычислить логическое ИЛИ И , ИЛИ или Исключающее ИЛИ между двумя регистрами и перезаписать первый регистр результатом. Например, ниже показаны значения 0x55 и 0x0F, загружаемые в r16 и r17 соответственно. Затем между ними вычисляется логическое И , и результат сохраняется в r16 .

  ldi r16,0x55; загрузить 0x55 в r16
        ldi r17,0x0F; загрузить 0x0F в r17
        и r16, r17; маскировать верхние 4 бита r16 (результат = 0x05)
  

Если бы порядок регистров был изменен, выход был бы сохранен в r17, i.е.

,  и r17, r16; маскировать верхние 4 бита r17 (результат = 0x05)  

Инструкции or и eor работают одинаково:

  ldi r16,0x55; загрузить 0x55 в r16
        ldi r17,0x0F; загрузить 0x0F в r17
        или r16, r17; установить младшие 4 бита r16 (результат = 0x5F)

        ldi r16,0x55; загрузить 0x55 в r16
        ldi r17,0xFF; загрузить 0xFF в r17
        eor r16, r17; переключить младшие 4 бита r16 (результат = 0xAA)  

Один и тот же регистр может использоваться для обоих операндов

  eor r16, r16; очистить r16 (результат = 0x00)
  

Дополнения и отрицания

Инструкция com позволяет переключать все биты регистра.Например

  ldi r16,0x55; загрузить 0x55 в r16
com r16; одно дополнение к r16 (результат = 0xAA)  

Инструкция neg принимает два дополнения регистра (т. Е. Отменяет его).

  ldi r16,5; загрузить 5 в r16
neg r16; отрицать r16 (результат = -5 = 0xFB)  

Арифметические инструкции

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

Декрет
Мнемоника Описание
доб. добавить без переноски
АЦП добавить с переноской
adiw добавить сразу к слову
суб вычесть без переноса
subi вычесть сразу
сбк вычесть с переносом
SBCI немедленно вычесть с переносом
сбив немедленно вычесть из слова
инк. шаг
дек
мул умножить без знака (1)
муль со множеством знаков (1)
Мульсу, умноженное на знак без знака (1)
фмул дробное умножение без знака (1)
мулс дробное умножение со знаком (1)
фмулсу дробное умножение со знаком и без знака (1)
1: Не реализовано в ATtiny

Сложение и вычитание

Регистры могут быть добавлены с помощью инструкции добавления.add принимает два регистра в качестве входных данных, вычисляет их сумму и сохраняет значение в первом входе. В приведенном ниже коде показано, как вычислить сумму 5 и 6 и сохранить результат в r16:

.
  ldi r16,5; загрузить 5 в регистр 16
ldi r17,6; загрузить 6 в регистр 17
добавить r16, r17; сложите r16 и r17 и сохраните результат в r16
  

Аналогично, команда adc — сложить с переносом , принимает два регистра в качестве входных данных и вычисляет их сумму, но добавит бит переноса , если предыдущая операция привела к переполнению.Это полезно при вычислении сумм 16-битных чисел. Как мы видели ранее, сложение 0x1234 и 0xABCD может быть вычислено как

  ldi r16,0x34; поместите младший байт 0x1234 в r16
ldi r17,0x12; поместите старший байт 0x1234 в r17
ldi r18,0xCD; поместите младший байт 0xABCD в r18
ldi r19,0xAB; поместите старший байт 0xABCD в r19

добавить r16, r18; вычислить сумму младших байтов (результат = 0x01)
adc r17, r19; вычислить сумму старших байтов с переносом (результат = 0xBE)  

Результатом выше является сумма 0xBE01, сохраненная в r16 и r17.

Как и в случае с сложением, есть пара инструкций вычитания, sub — вычитать и sbc — вычитать с переносом . sub и sbc используются так же, как add и adc. Например

  ldi r16,0x01; поместите младший байт 0xBE01 в r16
ldi r17,0xBE; поместите старший байт 0xBE01 в r17
ldi r18,0x34; поместите младший байт 0x1234 в r18
ldi r19,0x12; поместите старший байт 0x1234 в r19

sub r16, r18; вычесть младшие байты (результат = 0xCD)
sbc r17, r19; вычесть старшие байты с переносом (результат = 0xAB)  

Кроме того, существует команда немедленного вычитания — sbi, которая позволяет вычитать 8-битную константу из регистра.

  ldi r16,0x05; загрузить 0x05 в регистр 16
sbi r16,0x05; вычесть 5 из r16 (результат = 0x00)  

К сожалению, нет соответствующей инструкции добавить немедленно .

Пара inc и dec — это простые способы увеличения или уменьшения значения в регистре на единицу. Им требуется один регистр в качестве операнда, и они работают с любым из 32 регистров.

  clr r16; очистить r16 (результат = 0)
inc r16; приращение r16 (результат = 1)
dec r16; уменьшение r16 (результат = 0)  

Некоторые арифметические инструкции позволяют работать с 16-битными значениями, т.е.е. слова . Инструкция adiw — добавить сразу к слову , позволяет вам добавить постоянное значение из диапазона 0 — 63 в регистровую пару r24: r25 или X (r26: r27), Y (r28: r29) или Z (r30: r31) указатели. Если указан только один регистр, компилятор автоматически заполнит следующий (т.е. r24 → r25: r24). Ниже приведены способы использования adiw

  ldi r24,0x00; загрузить 0x1000 в
ldi r25,0x10; регистры r24: r25

adiw r24,0x0A; добавить 0x0A к r24: r25 (результат = 0x100A)

ldi XL, 0x80; загрузить 0x8080 в
ldi XH, 0x80; Указатель X

adiw X, 1; увеличить указатель X (результат = 0x80801)  

sbiw — немедленно вычесть из слова используется аналогично

  ldi YL, 0xAA; загрузить 0x55AA в
ldi YL, 0x55; Указатель Y

sbiw Y, 0x10; вычесть 0x10 из Y (результат = 0x559A)  

adiw и sbiw можно использовать только в регистрах с r24 по r31.Кроме того, они будут работать только с парами регистров, для которых нижнее число является четным, то есть r25: r24 — допустимые операнды, но r26: r25 — это , а не .

Умножение

Если вы используете микрокроллер ATmega или XMega, у вас есть возможность выполнить аппаратное умножение на (ATtiny не реализует эти инструкции).

Инструкция mul позволяет вычислить произведение без знака на любого из 32 рабочих регистров общего назначения.Поскольку произведение двух 8-битных чисел может достигать 16-битных значений, для результата требуются два регистра. Вывод mul всегда будет храниться в r1 и r0, независимо от операндов.

  ldi r16,0x18; загрузить r16 с помощью 0x18 (22)
ldi r17,0x37; загрузить r17 с помощью 0x37 (55)
mul r16, r17; умножить r16 и r17 (r1: r0 = 0x0528 = 1210)  

Инструкция muls вычисляет произведение двух 8-битных значений со знаком . И снова результат будет сохранен в r1 и r0. Однако muls работает только с регистрами с r16 по r31.Компонент со знаком должен быть в формате дополнения до двух.

  ldi r16,0xF2; загрузить r16 с помощью 0xF2 (-14)
ldi r17,0x37; загрузить r17 с помощью 0xDF (-33)
mul r16, r17; умножить r16 и r17 (r1: r0 = 0x01CE = 462)  

Если , один из ваших операндов подписан, а другой — без знака, тогда есть инструкция только для вас: mulsu. Эта инструкция будет рассматривать первый операнд как подписанный, а второй как беззнаковый. Недостатком является то, что mulsu еще более ограничивает регистры — он будет работать только с r16 по r23.

  ldi r16,0xF2; загрузить r16 с помощью 0xF2 (-14)
ldi r17,0x37; загрузить r17 с помощью 0x83 (131)
mul r16, r17; умножить r16 и r17 (r1: r0 = 0xF8D6 = -1834)  

Чтобы преобразовать значение в формат с дополнением до двух, просто добавьте 256, если оно меньше нуля. Например, для представления -14:

-14 + 256 = 242 (0xF2)

Дробное умножение

Помимо умножения целых чисел, AVR поддерживает ограниченное умножение дробных чисел.Это тема, которая сама по себе заслуживает целого учебника и, честно говоря, немного сложнее, чем необходимо для новичка. Не стесняйтесь пока пропустить эту часть, но ниже будет дан краткий обзор очень .

Дробные числа между [0, 2) могут быть представлены 8-битным числом в формате 1,7, где старший бит представляет собой целочисленный компонент 0 или 1, а остальные биты представляют собой дробный компонент, как показано на таблица ниже.

Бит 7 Бит 6 Бит 5 Бит 4 Бит 3 Бит 2 Бит 1 Бит 0
2 0 2 -1 2 -2 2 -3 2 -4 2 -5 2 -6 2 -7
1 0.5 0,25 0,125 0,0625 0,03125 0,015625 0,0078125

Например, двоичное число 01001100 при использовании этого формата будет равно 0 * 1 + 1 * 0,5 + 0 * 0,25 + 0 * 0,125 + 1 * 0,0625 + 1 * 0,03125 + 0 * 0,015625 + 0 * 0,0078125 = 0,59375.

Инструкция fmul вычисляет сумму двух 8-битных чисел в формате 1,7 и выводит результат в r1 и r0 в формате 1,15. В 1.15 первый бит старшего байта представляет собой целочисленный компонент, а остальные биты представляют собой дробные компоненты.fmul будет работать только с r16 по r23.

  ldi r16,0xC0; загрузить r16 с помощью 0xC0 (1.5)
ldi r17,0xA0; загрузить r17 с помощью 0xA0 (1.25)
fmul r16, r17; умножить r16 и r17 (r1: r0 = 0xF000 = 1.875)  

Как и в случае целочисленного умножения, есть знаковая версия fmuls, которая может умножать дробные числа между [-1,1)

  ldi r16,0xA0; загрузить r16 с помощью 0xA0 (-0,75)
ldi r17,0xF0; загрузить r17 с помощью 0xF0 (-0,125)
fmuls r16, r17; умножаем r16 и r17 (r1: r0 = 0x0C00 = 0.4375)  

И подписанная неподписанная версия fmulsu

  ldi r16,0xC0; загрузить r16 с помощью 0xC0 (-0,5)
ldi r17,0x60; загрузить r17 с помощью 0x60 (0,75)
fmulsu r16, r17; умножить r16 и r17 (r1: r0 = 0xD000 = -0,375)  

Чтобы преобразовать дробное значение в формат с дополнением до двух, просто добавьте 2, если оно меньше нуля. Например, для представления -0,75:

-0,75 + 2 = 1,25 (0xA0)

Битовые сдвиги

Архитектура AVR имеет ряд инструкций для сдвига битов в рабочем регистре общего назначения, показанных в таблице ниже.

Мнемоника Описание
LSL логический сдвиг влево
LSR логический сдвиг вправо
рол повернуть влево через перенос
рор повернуть вправо через перенос
asr арифметический сдвиг вправо

Команды lsl и lsr выполняют логических сдвигов на бит.Нулевой бит будет автоматически сдвинут в регистр в пустое пространство, оставшееся после сдвига, а сдвинутый бит будет сохранен как бит переноса в регистре состояния (подробнее об этом позже). Например

  ldi r16,0b10101010; загрузить 1010101010 в r16
lsl r16; сдвиг r16 влево (результат = 01010100)
lsr r16; сдвиг r16 вправо (результат = 00101010)  

Команды rol и ror работают очень похоже на lsl и lsr, за исключением того, что вместо автоматического смещения нуля в пустое пространство, смещается единица или ноль, если перенос произошел в предыдущей операции.

  ldi r16,0b10101010; загрузить 1010101010 в r16
ldi r17,0b10101010; загрузить 1010101010 в r17

lsl r16; сдвиг r16 влево (результат = 01010100)
rol r17; сдвинуть r17 влево и принести перенос (результат = 01010101)  

В приведенном выше примере старший бит r16 равен единице, поэтому, когда он сдвигается влево, он сохраняется в регистре состояния. Когда r17 повернут на влево, бит из предыдущей операции сдвигается в младший значащий бит.

Аналогично для ror

  ldi r16,0b10101010; загрузить 1010101010 в r16
ldi r17,0b10101010; загрузить 1010101010 в r17

lsr r17; сдвинуть r17 вправо (результат = 01010101)
ror r16; сдвинуть r16 вправо и принести перенос (результат = 01010101)  

Нуль смещается из r17, поэтому, когда r16 повернут на вправо на , ноль смещается в его старший значащий бит.

Манипуляции с битами

Наконец, есть несколько инструкций для управления битами регистра.

Мнемоника Описание
сбр установить бит (ы) в регистре
CBR очистить бит (ы) в регистре
сер установить регистр
clr очистить регистр
своп заменить полубайты

Инструкции sbr и cbr позволяют установить или очистить бит в регистре.sbr и cbr требуют постоянной маски в качестве операнда, определяющего, какие биты устанавливаются или очищаются.

  ldi r16,0x00; загрузить 0x00 в r16
sbr r16,0x0F; установить младшие 4 бита r16 (результат = 0x0F)
cbr r16,0x0F; очистить младшие 4 бита r16 (результат = 0x00)  

Вы можете заметить, что sbr выглядит идентично инструкции ori. На самом деле это так. Ваш ассемблер сгенерирует один и тот же машинный код независимо от того, какую инструкцию вы используете — sbr на самом деле просто псевдоним для инструкции ori.

cbr — это , почти идентично andi, за исключением того, что постоянная маска должна быть инвертирована для получения того же результата. Когда используется инструкция cbr, ассемблер автоматически отменяет предоставленную константу и генерирует машинный код для andi. Следующие пары инструкций произведут такой же вывод ассемблера.

  ori r16,0x0F; это тоже самое
sbr r16,0x0F; как это

andi r16,0xF0; это тоже самое
cbr r16,0x0F; как это  

Ассемблеры предоставляют инструкцию cbr для удобства, поэтому отрицательная битовая маска не обязательна, а sbr предоставляется как псевдоним для ori для поддержания согласованности.

Несмотря на то, что в имени нет слова немедленный , sbr и cbr работают только с регистрами с 16 по 31.

ser — устанавливает регистр и clr — очищает регистр , позволяет вам устанавливать или очищать все биты в регистре. Они вызываются с одним регистром от 16 до 31.

  ser r16; установить все биты в r16 (результат = 0xFF)
clr r16; очистить все биты в r16 (результат = 0x00)  

Как и раньше, вы можете заметить, что ser — это то же самое, что и использование ldi с 0xFF, а clr — то же самое, что выполнение eor с тем же регистром, что и источник и место назначения (на самом деле, есть несколько разных способов достижения любого из этих результатов) .Сборщик заменит

С машинным кодом для

ser и clr предоставляются ассемблером, так как они быстрее набираются, чем альтернативы.

ser и clr не являются фактическими инструкциями, реализованными в архитектуре, но предоставляются ассемблером как псевдонимы для других инструкций.

Наконец, команда обмена позволяет вам поменять местами младшие 4 бита на 4 старших бита регистра (также называемые полубайтами ).

  ldi r16,0x0F; загрузить 0x0F в r16
своп r16; поменять местами полубайты r16 (результат = 0xF0)  

Команда перестановки наиболее полезна при работе с двоично-десятичным кодированием (BCD), где двоичное значение двух чисел 0-9 хранится в верхнем и нижнем полубайтах 8-разрядного числа.

Выводы

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

Вероятно, пора взглянуть на другой практический пример, так что теперь мы рассмотрим настоящий «Hello World» встроенных систем: мигание светодиода.

hexagon5un / AVR-Programming: Примеры кода для книги «Make: AVR Programming»

Добро пожаловать!

Здесь вы найдете весь код (и многое другое!) Для книги Maker Media. Марка: Программирование AVR.

Начало работы

  • Сначала загрузите содержимое этого репозитория на свой жесткий диск.Самый простой способ находится с кнопкой «Загрузить ZIP» вверху и справа в этой самой сети страница. Распакуйте zip-файл в удобное место. (Не стесняйтесь клонировать репо, если вы удобны с Git.)

  • Большинство проектов имеют общий набор определений выводов и общий простой Последовательная библиотека USART в каталоге AVR-Programming-Library . В Файлы makefiles, которые я включил, по умолчанию зависят от структуры каталогов, поэтому не перемещайте папки, если вы также не измените путь к включенные файлы в make-файл.

  • Если вы используете Arduino IDE, вам нужно скопировать каталог AVR-Programming-Library в папку альбом / библиотеки . Если вы не знаете, где это, вы можете узнать в Диалог «Файл … Настройки» в Arduino. Теперь вы можете просто связать свой код, чтобы использовать эту библиотеку. используя «Эскиз … Импортировать библиотеку» и выбирая библиотеку из меню.

  • Теперь вы можете открыть код, отредактировать его и прошить в AVR, следуя инструкциям. в книге.

Репо Макет

Весь код проекта организован по главам в книге. Так что если вы ищете пример кода SPI, см. папку «Chapter16_SPI» для Проекты, связанные с SPI. Это очевидно.

Но несколько проектов интересны помимо темы, рассмотренной в глава. Например, «Chapter05_Serial-IO» включает проект, который использует последовательная связь между вашим настольным компьютером и AVR, чтобы включить ваш компьютерную клавиатуру в музыкальную клавиатуру, которая воспроизводит ноты, созданные на AVR, превратив его в орган, управляемый последовательным портом.Вы бы не подумали загляните в главу «Последовательный ввод / вывод», если вы не читали книгу.

Итак, для обзора всех проектов файл allProjectsList перечисляет их все по именам.

setupProject

Если вам нужен пустой шаблон для написания собственного кода AVR, загляните в каталог setupProject , который я здесь включил. Внутри вы найдете main.c и main.h файлы, которые практически пусты и готовы к работе. main.c использует мою простую библиотеку USART, которая также включена в файл сборки Makefile . Короче говоря, вы можете скопировать этот каталог, переименовать файлы и начать использовать его в своих проектах.

Но делать это вручную не обязательно. Запуск python setupProject.py myProjectName создаст для вас каталог с именем myProjectName , скопируйте пустые основные файлы, переименовав их соответствующим образом, и настройте Makefile соответственно. Все, что вам осталось сделать, это самая сложная часть — на самом деле кодирование.

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

Наконец, если вы хотите отобразить определения контактов в определениях макросов, запустите python createPinDefines.py . Программа спросит вас, как вы хотите позвонить макрос каждого вывода (например,грамм. «LED0»), а затем какой вывод на AVR вы хотите связать с ним (например, «PB1»). Когда вы закончите вводить свой PIN-код, он создаст файл «pinDefines.h» с макросами (я надеюсь) с красивыми именами. Двигаться этот файл в правильный каталог и включите его в свой код. Звонок LED0_SET_HIGH включит ваш светодиод.

Подробнее!

Вы прочитали книгу, вы создали проекты, вы проработали код. Но вы все равно жаждете новых проектов, примеров, еще, еще, еще! Если я могу потрубить себе в рог, вам следует посетить LittleHacks.org где я веду блог о любых проектах микроконтроллеров, над которыми я сейчас работаю.

В частности, если вы читаете Марка: Программирование AVR и вас интересуют полностью проработанные версии проектов с дополнительными фотографии, видео и объяснения, которые не поместятся в книге, перейдите к Раздел AVR-программирования LittleHacks.org.

После того, как вы исчерпали все эти ресурсы, вам обязательно нужно в финал ECE 4760 Корнельского университета Проекты страница списка.Это впечатляющая коллекция приложений, которая обязательно вызовет некоторые творческие мысли. Все это хорошо задокументировано, и есть масса источников код в C. [раздел ссылок профессора Лэнда] (http://people.ece.cornell.edu/land/courses/ece4760/#links) тоже на высшем уровне, и его лекции на YouTube тоже стоит посмотреть, если вы серьезно обо всей этой сделке с AVR.

AVR ASM ВВЕДЕНИЕ


РУКОВОДСТВО MORON ПО ЗАПУСКУ AVR ASSEMBLER v1.3


от RetroDan @ GMail.com
СОДЕРЖАНИЕ:
  • ВВЕДЕНИЕ
  • БАЗОВАЯ АРХИТЕКТУРА АРН
  • РЕГИСТРЫ
  • ОПРЕДЕЛЕНИЯ РЕГИСТРА
  • КОНСТАНТ
  • ДИРЕКТИВА ВКЛЮЧИТЬ ФАЙЛЫ
  • УСТАНОВКА МОНТАЖА
  • BEEP: НАША ПЕРВАЯ ПРОГРАММА
  • ЗВУК БАБОЧКИ
  • АДАПТАЦИИ ДЛЯ ДРУГИХ ЧИПОВ AVR
  • ДИРЕКТИВА ПРОИСХОЖДЕНИЯ
  • ЯРЛЫК
  • НИЗКОЕ И ВЫСОКОЕ ВЫРАЖЕНИЯ
  • УКАЗАТЕЛЬ СТЕКА
  • РЕГИСТРЫ НАПРАВЛЕНИЯ ДАННЫХ
  • КОМАНДЫ CLR и SER
  • АКТИВАЦИЯ ДИНАМИКА
  • ПОДПРОГРАММЫ И КОМАНДЫ RCALL & RETURN
  • КОМАНДЫ ДОБАВЛЕНИЯ И ВЫЧИСЛЕНИЯ
  • Прыжки и разветвления
  • ПРОГРАММА ПАУЗЫ
  • ИНСТРУКЦИЯ NOP
  • И ИНСТРУКЦИЯ
  • ИЛИ ИНСТРУКЦИЯ
  • ИНСТРУКЦИЯ МУН
  • ОБРАТНАЯ ОПЕРАЦИЯ ИЛИ «НЕ»
  • ИНСТРУКЦИИ SBI & CBI
  • СТЕК
  • ИНСТРУКЦИИ ПО PUSH & POP
  • ДИРЕКТИВА ВКЛЮЧИТЬ ФАЙЛЫ
  • УСТАНОВКА МОНТАЖА
  • BEEP: НАША ПЕРВАЯ ПРОГРАММА
  • ЗВУК БАБОЧКИ
  • АДАПТАЦИИ ДЛЯ ДРУГИХ ЧИПОВ AVR
  • ДИРЕКТИВА ПРОИСХОЖДЕНИЯ
  • ЯРЛЫК
  • НИЗКОЕ И ВЫСОКОЕ ВЫРАЖЕНИЯ
  • УКАЗАТЕЛЬ СТЕКА
  • РЕГИСТРЫ НАПРАВЛЕНИЯ ДАННЫХ
  • КОМАНДЫ CLR и SER
  • АКТИВАЦИЯ ДИНАМИКА
  • ПОДПРОГРАММЫ И КОМАНДЫ RCALL & RETURN
  • КОМАНДЫ ДОБАВЛЕНИЯ И ВЫЧИСЛЕНИЯ
  • Прыжки и разветвления
  • ПРОГРАММА ПАУЗЫ
  • ИНСТРУКЦИЯ NOP
  • И ИНСТРУКЦИЯ
  • ИЛИ ИНСТРУКЦИЯ
  • ИНСТРУКЦИЯ ПО МУН
  • ОБРАТНАЯ ОПЕРАЦИЯ ИЛИ «НЕ»
  • ИНСТРУКЦИИ SBI & CBI
  • СТЕК
  • ИНСТРУКЦИИ ПО PUSH & POP

ВВЕДЕНИЕ:

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

Сборка

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

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

Assembly позволяет создавать минимально возможный и быстрый код, однако для больших проектов это может быть долгим и утомительным занятием.

БАЗОВАЯ АРХИТЕКТУРА АРН:

В простейшей форме AVR состоят из регистров, портов и RAM:

 [РЕГИСТРЫ]
     [ПОРТЫ]
     [БАРАН]
 

Имеется 32 регистра, которые можно считать столь же быстрой оперативной памятью, поэтому большая часть работы выполняется именно там.Как я упоминал ранее, большая часть ассемблера перемещает данные в эти регистры и что-то с ними делает.

RAM — это место, где мы храним наши программы и данные.

Порты — это то, как мы общаемся с внешним миром, но они появляются в MCU как дополнительные регистры.

РЕГИСТРЫ:

Регистры имеют размер один байт (восемь бит) и являются внутренними для MCU, поэтому работают быстро. Мы можем думать о них как о рабочем пространстве, где нужно успеть кое-что сделать, прежде чем их куда-то отправят.Поэтому, прежде чем мы сможем писать программы сборки для AVR, важно хорошо разбираться в регистрах.

В AVR есть 32 внутренних регистра, обычно обозначаемых как R0-R31. Чаще всего используются R16-R31, потому что их проще использовать. Их можно загрузить напрямую с помощью константы. Например, если вы хотите загрузить 100 в регистр R16 со 100, вы должны использовать:

 LDI R16,100; LDI = LoaD Immediate 100 в регистр 16
 

LDI означает LoaD Immediate, R16 — регистр, а 100 — наша константа.Все, что находится после точки с запятой «;» комментарий, игнорируемый Ассемблером.

Если вы хотите переместить значение 100 в один из регистров R0-R15, вы НЕ МОЖЕТЕ сделать это:

 LDI R1,100; это ОШИБКА !!!
 

Чтобы переместить 100 в один из регистров R0-R15, вы должны сделать что-то вроде:

 LDI R16,100; Загрузить 100 в регистр 16
MOV R1, R16; переместить содержимое регистра 16 в регистр 1
 

Это сначала перемещает 100 в регистр R16, затем мы перемещаем его из R16, а затем в R0.Обратите внимание, что операнды читаются справа налево. MOV — от R16 до R1, а не наоборот, даже если может показаться, что это так.

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

ОПРЕДЕЛЕНИЯ РЕГИСТРА:

Мы можем использовать команду .DEF для присвоения нашим регистрам значимых имен.

 .DEF A = ​​R16
.DEF B = R18
.DEF N = R20
.DEF STUDENT_SCORE = R25
 

Теперь мы можем загрузить в регистр R16 значение 100, используя команду:

 ЛДИ А, 100
 

КОНСТАНТ:

Константы — это значения, которые не изменяют значение во время работы программы. Они определяются во время сборки вашей программы на машинном языке (двоичный код). и не меняются при выполнении вашей программы.

Константам можно присвоить имя с расширением.Команда SET (или .EQU). В нашем последнем примере мы загрузили регистр R16 со значением 100. Вместо использования константы 100 мы могли бы дать ей имя, например PERFECT_SCORE, с помощью операторов:

 .SET PERFECT_SCORE = 100
.EQU PERFECT_SCORE = 100
 

Позже в программе мы можем загрузить R16 со 100, используя команду:

 LDI R16, ИДЕАЛЬНЫЙ_ОСЧЕТ
 

Константы можно представить разными способами. Их можно определить как шестнадцатеричные, восьмеричные, двоичные и т. Д.Все следующие определения PERFECT_SCORE равны 100:

 .SET PERFECT_SCORE = 100; Десятичное представление
.SET PERFECT_SCORE = (2000 + 500) / 25; 2500 разделить на 25 = 100
.SET PERFECT_SCORE = 0x0064; Шестнадцатеричное представление
.EQU PERFECT_SCORE = $ 64; Шестнадцатеричное представление
.EQU PERFECT_SCORE = 0b0110_0100; Двоичная запись
.EQU PERFECT_SCORE = 0144; Восьмеричное представление
.EQU PERFECT_SCORE = 'd'; Нотация ASCII
 

Как мы видели ранее, константа может быть загружена непосредственно в регистры от R16 до 31.Все следующее загрузит R16 с 100:

 LDI 16 100 рупий
LDI R16, PERFECT_SCORE
LDI R16, (2000 + 500) / 25
LDI R16, 64 доллара США
LDI R16,0b0110_0100
LDI R16, 'd'
LDI A, PERFECT_SCORE; если вы определили A = R16
 

ДИРЕКТИВА ВКЛЮЧЕНИЯ ФАЙЛОВ:

AVR включают в себя большое семейство микросхем. Чтобы помочь нам создать код для различных процессоров, ATMEL предоставляет файл для каждого из них, который содержит серию стандартных определений .DEF и .EQU. адаптированный к этому конкретному чипу. Например, вот небольшой клип от M69DEF.INC файл для Процессор ATmega169, который используется в AVR Butterflys, который определяет регистры R26-R31 как двухбайтовые указатели, называемые X, Y и Z:

; ***** ОПРЕДЕЛЕНИЯ РЕГИСТРА ЦП *************
.def XH = r27
.def XL = r26
.def YH = r29
.def YL = r28
.def ZH = r31
.def ZL = r30
 

Директива .INCLUDE предписывает ассемблеру читать файл как часть нашей программы. Например, в верхней части программы для Butterfly вы обычно увидите:

. ВКЛЮЧИТЬ "M169DEF.INC "; БАБОЧКИ DEFS
 

Или для программы для ATtiny13:

. ВКЛЮЧИТЬ "TN13DEF.INC"
 

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

. ВКЛЮЧИТЬ «MYFILE.ASM»
 

УСТАНОВКА МОНТАЖА:

Лучший способ изучить Ассемблер — это сделать Ассемблер, поэтому, если вы еще этого не сделали, мы просто загружаем программное обеспечение Studio 4 с сайта Atmel.com и устанавливаем его в Windows. В прошлый раз, когда я его загрузил, мне пришлось сначала зарегистрироваться, что является простым и быстрым процессом.Если вы используете другую операционную систему, я полагаю, вы уже выяснили, как установить ассемблер и программист. Мы запускаем кабель от COM-порта на вашем ПК к программатору или микросхеме, и вы готовы к работе.

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

Если вы запускаете программу впервые, выберите «Atmel AVR Assembler.»Выберите« Ассемблер 2 » если спросят. Введите имя файла BEEP или что-то подобное, тогда вам будет представлен список фишек. для чего собирать, при использовании бабочки выберите ATmega169.

«БИП» НАША ПЕРВАЯ ПРОГРАММА:

Вот наша первая программа (полное объяснение последует), если вы скопируете и вставите ее в редактор Ассемблера. Затем нажмите кнопку компиляции, будет создан файл с именем BEEP.HEX, готовый для программирования в нашем чипе. Затем вы нажимаете кнопку подключения, а затем кнопку программы.
; ------------------------------------------------ -;
   ; BEEP.ASM для AVR BUTTERFLY;
   ; СДЕЛАЙТЕ ЭТО ИЗВЕСТНЫЙ ЗВУК! ;
   ; АВТОР: ДЭНИЕЛ Дж. ДОРИ [email protected];
   ; СОЗДАНО: 01 МАРТА 06 ОБНОВЛЕНО: 01 МАРТА 06;
   ; ПРИМЕЧАНИЕ. В СХЕМАТИЧЕСКОЙ СХЕМЕ ДИНАМИК ОЧИЩЕН НА PB5;
   ; ------------------------------------------------- -;

   .ВКЛЮЧАЙТЕ "M169DEF.INC"; (ОПРЕДЕЛЕНИЯ БАБОЧКИ)

   ; -----------------------------------------;
   ; Сначала мы определим НЕКОТОРЫЙ РЕГИСТР ДЛЯ ИСПОЛЬЗОВАНИЯ;
   ; -----------------------------------------;
   .DEF A = ​​R16; АККУМУЛЯТОР ОБЩЕГО НАЗНАЧЕНИЯ
   .DEF I = R21; ИНДЕКСЫ ДЛЯ КОНТРОЛЬНОГО УПРАВЛЕНИЯ
   .DEF J = R22

   .ORG $ 0000

   ; -----------------------------------------;
   ; Сначала мы настраиваем область стека, затем устанавливаем;
   ; НАПРАВЛЯЮЩАЯ БИТКА НА ПОРТ-B ДЛЯ ВЫХОДА / SPKR;
   ; -----------------------------------------;
   НАЧАЛО:
     LDI A, LOW (RAMEND); НАСТРОЙКА УКАЗАТЕЛЯ СТЕКА
     OUT SPL, A; ТАК ВЫЗЫВАЕТ ПОДПРОГРАММЫ
     LDI A, ВЫСОКИЙ (RAMEND); РАБОТАЙТЕ ПРАВИЛЬНО
     ВЫХОД SPH, A;
     LDI A, 0b1111_1111; УСТАНОВИТЬ ВСЕ ПОРТЫ ДЛЯ ВЫХОДА
     OUT DDRB, A; ЗАПИСАТЬ 1s В DIRECTN REGS

   ; --------------;
   ; ГЛАВНЫЙ РЕЖИМ;
   ; --------------;
   BEEP: CLR I
   BLUPE:
     SER A; ВКЛЮЧИТЬ SPKR
     ВЫХОДНОЙ ПОРТB, A
      RCALL PAUSE; ПОДОЖДИТЕ
     CLR A; ВЫКЛЮЧИТЬ
     ВЫХОДНОЙ ПОРТB, A
      RCALL PAUSE; ПОДОЖДИТЕ СНОВА
     ДЕКАБРЬ I
      BRNE BLUPE
   LOOP: RJMP LOOP; ОСТАВАЙТЕСЬ ЗДЕСЬ, КОГДА СДЕЛАНО

   ; ----------------;
   ; ПРИОСТАНОВИТЬ ПРОГРАММУ;
   ; ----------------;
   ПАУЗА:
     CLR J
   ПЛЮП:
     NOP
     ДЕКАБРЬ J
      BRNE PLUPE
       RET
 

БАБОЧКА:

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

Теперь рассмотрим программу более подробно. К настоящему времени вы должны понять первую часть программы. Он начинается с некоторых комментариев, начинающихся с символа «;» который Ассемблер проигнорирует. Затем мы используем директиву .INCLUDE, чтобы ассемблер читал файл M169DEF.INC. который содержит определения для микросхемы ATmega169.

; ------------------------------------------------ -;
; BEEP.ASM для AVR BUTTERFLY;
; СДЕЛАЙТЕ ЭТО ИЗВЕСТНЫЙ ЗВУК! ;
; АВТОР: ДЭНИЕЛ Дж. ДОРИ [email protected];
; СОЗДАНО: 01 МАРТА 06 ОБНОВЛЕНО: 01 МАРТА 06;
; ПРИМЕЧАНИЕ. В СХЕМАТИЧЕСКОЙ СХЕМЕ ДИНАМИК ОЧИЩЕН НА PB5;
; ------------------------------------------------- -;

.ВКЛЮЧАЙТЕ "M169DEF.INC"; (ОПРЕДЕЛЕНИЯ БАБОЧКИ)
 

АДАПТАЦИИ ДЛЯ ДРУГИХ ЧИПОВ AVR:

Если вы используете другой чип, например ATtiny13, вы должны использовать AT13DEF.INC. Если вы выполнили стандартную установку в Windows XP, эти файлы можно найти в C: \ Program Files \ Atmel \ AVR Tools \ AvrAssembler2 \ Appnotes, если вам нужно найти имя файла для используемого чипа.

Далее, если вы используете 3 вольта для питания вашего чипа, вам необходимо подключить небольшой динамик к порту B, контакт 5. Проверьте распиновку в верхней части паспорта вашего чипа. которую вы можете бесплатно скачать с сайта Atmel.com. Вы можете использовать маленький компьютерный динамик от старого компьютера и подключите порт B, контакт 5 к +3 В или к земле.(+3 Вольта должно быть чуть громче). Если вы используете 5 вольт для питания вашего чипа, установите небольшой резистор в диапазоне от 100 до 220 Ом. последовательно с динамиком для ограничения тока.

Если порт B, контакт 5 недоступен, например, на микросхемах ATtiny он используется как линия сброса. Затем подключитесь к другому контакту порта B, например, к нулевому контакту, и мы изменим программу позже.

Вернемся к нашей программе. Затем мы даем наши собственные имена (A, I, J) регистрам, которые мы будем использовать. Обратите внимание, что все они находятся в диапазоне от R16 до R25:

; -----------------------------------------;
; Сначала мы определим НЕКОТОРЫЙ РЕГИСТР ДЛЯ ИСПОЛЬЗОВАНИЯ;
; -----------------------------------------;
.DEF A = ​​R16; АККУМУЛЯТОР ОБЩЕГО НАЗНАЧЕНИЯ
.DEF I = R21; ИНДЕКСЫ ДЛЯ КОНТРОЛЬНОГО УПРАВЛЕНИЯ
.DEF J = R22
 

ДИРЕКТИВА ПРОИСХОЖДЕНИЯ:

Прежде чем мы действительно сможем создать наши первые строки кода, мы должны сообщить Ассемблеру, куда положить в программную память. Это делается с помощью Директивы происхождения «.ORG» и, как правило, программ AVR. начинать с нижней части памяти в нулевом месте, потому что именно туда смотрит микросхема AVR при включении или сбросить.

 .ORG $ 0000
 

$ 0000 — это просто еще одна константа, подобная тем, которые мы обсуждали ранее, поэтому мы могли бы использовать .ORG 0 или даже что-то сумасшедшее, например .ORG 100-100, но традиционно это делается с шестнадцатеричная запись $.

ЯРЛЫК:

Чтобы выполнять переходы, циклы и подпрограммы, нам нужен способ указать Ассемблеру, куда идти. Для этого используются ярлыки. Они также используются, чтобы помочь нам понять, что делает код. Например, поскольку мы находимся в начале нашей программы, давайте обозначим ее «СТАРТ:».

Ярлыки обычно начинаются в крайнем левом углу экрана, а остальная часть программы имеет отступ.Этикетки состоят из цифр и букв, но должны начинаться с буквы, и когда мы их определяем, они заканчиваться двоеточием »:« Чтобы использовать метку, мы не включаем «:». Например, чтобы перейти к СТАРТ: мы должны написать код:

 RJMP START
 

НИЗКОЕ И ВЫСОКОЕ ВЫРАЖЕНИЯ:

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

Внутри включаемых файлов (в нашем случае M169DEF.INC) константа (RAMEND) определена в верхней части память для нас. Проблема в том, что если у нас будет более 256 байт ОЗУ, будет больше чем мы можем вместить в один байт. Выражения LOW () и HIGH () разбивают шестнадцатиразрядный число в два байта для нас. LOW (RAMEND) даст нам младший байт, а HIGH (RAMEND) даст нам старший байт. Обратите внимание, что эти два байта являются константами, и если мы хотим записать их в Указатель стека, мы должны сначала загрузить их в регистр в диапазоне R16-R32, как и любую другую константу.

УКАЗАТЕЛЬ СТЕКА:

Указатель стека — это специальная двухбайтовая ячейка памяти, которая всегда указывает на верхний элемент стека. Две ячейки памяти определены как SPL (младший байт) и SPH (старший байт). На AVR они лечатся как порт, поэтому команда MOV не будет работать, и мы должны использовать команду OUT. OUT обычно отправляет содержимое регистра в расположение порта.

 НАЧАЛО:
LDI A, LOW (RAMEND); НАСТРОЙКА УКАЗАТЕЛЯ СТЕКА
OUT SPL, A; ТАК ВЫЗЫВАЕТ ПОДПРОГРАММЫ
LDI A, ВЫСОКИЙ (RAMEND); РАБОТАЙТЕ ПРАВИЛЬНО
ВЫХОД SPH, A;
 

РЕГИСТРЫ НАПРАВЛЕНИЯ ДАННЫХ:

Каждый порт ввода / вывода AVR имеет связанный регистр направления данных, который определяется в нашем включаемом файле (M169DEF.INC). Для порта B это DDRB, для порта C — DDRC и т. Д. Если мы установим бит в этом регистре на единицу, то этот вывод станет выходным выводом, с другой стороны. если мы напишем ноль, он станет и входным контактом.

Поскольку нам нужен порт B, контакт 5 должен быть выходным контактом, и мы не используем никаких других контактов на порту B, мы можем просто сделать их всеми выходными контактами, записав все по одному.

Мы снова перемещаем константу, на этот раз значение 0b1111_1111 в регистр R16. Поскольку регистры направления данных являются регистрами портов, мы не можем использовать команду MOV, мы должны снова использовать команду OUT:

 LDI A, 0b1111_1111; УСТАНОВИТЬ ВСЕ ПОРТЫ ДЛЯ ВЫХОДА
OUT DDRB, A; ЗАПИСАТЬ 1s В DIRECTN REGS
 

КОМАНДЫ CLR И SER:

Теперь, когда все наши инициализации выполнены, мы переходим к основной части нашей программы.Поскольку наша программа издает звуковой сигнал на динамике, я назвал его BEEP, но мы могли бы пометил его MAIN или любое другое имя, которое вы хотите использовать.

Мы не хотим, чтобы наш AVR издавал непрерывный звуковой сигнал и сильно раздражал, поэтому мы собираемся настроить счетчик для ограничения продолжительности звукового сигнала. Мы собираемся использовать регистр R21, который у нас был ранее. дано имя «I», и мы собираемся начать его с нулевого значения. Обычно мы могли бы это сделать загрузив его напрямую с помощью команды LDI I, 0, но мы собираемся использовать новую команду CLR, которая будет установить все биты в регистре в ноль.

Преимущество команды CLR заключается в том, что ее можно использовать для ВСЕХ регистров от R0 до R31. Противоположностью команде CLR является SER (регистр SEt), который устанавливает все биты в регистре в единицу. Фактически, ранее мы использовали LDI A, 0b1111_1111, чтобы установить все биты в регистре A в единицу, но мы могли вместо этого использовали SER A.

; --------------;
; ГЛАВНЫЙ РЕЖИМ;
; --------------;
BEEP: CLR I
 

АКТИВАЦИЯ ДИНАМИКА:

Чтобы создать звук, исходящий из динамика, нужно сначала активировать его, а затем деактивировать. это путем отправки серии единиц и нулей, например 10101010101… и т. д. Это заставит динамик входите и выходите, создавая таким образом тон. Чтобы сделать это эффективно, мы будем использовать цикл, а цикл будет требуется метка, поэтому мы называем эту часть кода BLUPE (Beep Loop):

 BLUPE:
 

Чтобы активировать динамик, нам нужно отправить единицу на PortB, контакт 5. Это перебор, но поскольку мы не используют какие-либо другие контакты на порту B, мы можем использовать команду SER A, чтобы установить ВСЕ биты на затем отправить их в порт B.

 SER A; ВКЛЮЧИТЬ SPKR
ВЫХОДНОЙ ПОРТB, A
 

ПОДПРОГРАММЫ И КОМАНДЫ RCALL & RET:

Так как процессор работает очень быстро, в Butterfly это 2 МГц и по умолчанию включена многие другие чипы имеют частоту 1 МГц, если мы не замедлим звук, излучаемый нашим динамиком будет слишком высоко, чтобы услышать.Итак, мы используем подпрограмму, которую мы называем PAUSE, к которой мы переходим с команда RCALL, которая просто тратит на нас время.

Команда RCALL сохраняет наше место в стеке, затем переходит к подпрограмме и продолжает для выполнения расположенных там команд. Противоположностью команде RCALL является команда RET (RETurn), который извлекает из стека наше предыдущее местоположение, чтобы программа могла вернуться туда, откуда оно пришло. Таким образом, каждая подпрограмма должна заканчиваться командой RET. Мы делаем отступ для команды RCALL PAUSE, чтобы напомнить нам, что программа переходит в другое место.

 RCALL PAUSE; ПОДОЖДИТЕ
 

Раньше мы активировали динамик, записывая все в порт B, теперь мы хотим сделать наоборот поэтому мы используем команду CLR, чтобы установить все биты в регистре A в ноль, а затем отправить их в порт B.

 CLR A; ВЫКЛЮЧИТЕ
ВЫХОДНОЙ ПОРТB, A
RCALL PAUSE; ПОДОЖДИТЕ СНОВА
 

КОМАНДЫ ДОБАВЛЕНИЯ И ВЫЧИСЛЕНИЯ:

Ранее мы настроили регистр I в качестве счетчика, чтобы ограничить время, в течение которого динамик будет издавать звуковой сигнал.Для этого мы вычтем единицу из нашего счетчика и остановимся после активации и деактивации. динамик 256 раз.

Чтобы вычесть единицу из регистра I, мы могли бы сначала LDI A, 1, затем SUB I, A, который вычитает значение в A от значения в I. Точно так же мы могли бы также ДОБАВИТЬ I, A, если бы мы хотели добавить содержимое A к I.

Лучшим способом вычесть единицу из регистра I является команда SUBtract Immediate SUBI I, 1, которая позволяет нам, чтобы вычесть константу из Регистра.К сожалению, нет команды немедленного добавления.

Еще лучший способ вычесть единицу из I — использовать команду DECrement DEC I. Добавление и вычитание единицы из регистров настолько распространено, что существуют отдельные команды, которые это делают. DECrement (DEC I) вычтет единицу из I, а INCrement (INC I) добавит единицу в регистр I.

 DEC I
 

Прыжки и разветвления:

Обычно, когда мы хотим перейти к местоположению и не обязательно возвращаться, мы используем команду RJMP, но если мы хотим прыгнуть только на основании определенных критериев, это обычно называется ВЕТВЬЮ, как ветка дерева.

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

Мы собираемся уменьшать регистр I каждый раз в основном цикле и останавливаться, когда он достигнет нуля. Для этого мы собираемся использовать инструкцию BRanch if Not Equal to zero (BRNE). Так что если я зарегистрируюсь еще не достиг нуля, затем мы возвращаемся к началу нашего основного цикла (BLUPE).Когда регистр I достигает нуля, программа НЕ разветвляется, а переходит к следующей инструкции. и выйдите из цикла.

 BRNE BLUPE
 

Если мы хотим безоговорочно перейти к другой части программы, мы обычно используем Relative JuMP. команда (RJMP). Инструкция RJMP быстро перенесет нас в другую часть программы, но расстояние, на которое мы можем пройти, ограничено. Если мы столкнемся с ошибкой, потому что метка, которую мы хотим перепрыгнуть to слишком далеко, то мы можем использовать более медленную команду JMP без каких-либо ограничений по расстоянию.

Некоторые из вас могут спросить, как получается, что мы начинаем с нуля, а затем каждый раз вычитаем единицу через цикл и по-прежнему останавливаться на нуле? Ответ таков: когда мы вычитаем единицу из нуля, восьмибитный register «перевернется» на 255, затем мы продолжаем вычитать единицу из 255, пока снова не дойдем до нуля.

После того, как мы издаем звуковой сигнал, нам больше не понадобится процессор, поэтому мы отправляем его в бесконечный цикл, заставляя его перескакивать на себя:

 LOOP: RJMP LOOP; ОСТАВАЙТЕСЬ ЗДЕСЬ, КОГДА СДЕЛАНО
 

ПРОГРАММА ПАУЗЫ:

Чтобы замедлить нашу программу, чтобы мы могли слышать звуковой сигнал, мы используем подпрограмму, которая ничего не делает. но выполните цикл 255 раз между активацией динамика и его деактивацией.Мы создаем этот цикл, используя другой регистр, который мы определили как J, и уменьшая его. пока он не достигнет нуля, как мы это делали в нашем основном цикле, с помощью инструкции BRanch if Not Equal to zero (BRNE).

ИНСТРУКЦИЯ NOP:

Чтобы еще больше замедлить нашу программу, мы можем вставить команду, которая ничего не делает, кроме как ждать один тактовый цикл называется «Нет операции» (NOP). На самом деле мы можем изменить частоту нашего тона добавив еще больше NOP.

; ----------------;
; ПРИОСТАНОВИТЬ ПРОГРАММУ;
; ----------------;
ПАУЗА:
CLR J
ПЛЮП:
NOP
ДЕКАБРЬ J
BRNE PLUPE
RET
 

Как упоминалось ранее, все подпрограммы должны заканчиваться инструкцией RETurn (RET).

ОПЕРАЦИЯ «И»:

Работа «И» может быть продемонстрирована на следующей схеме из двух последовательно соединенных переключателей и лампочки:

 Switch_1 Светодиод Switch_2
---- / --------- / -------- D
 

Ясно видно, что светодиод будет гореть только тогда, когда оба переключателя замкнуты, чтобы произвести c полная схема. Переключите один И второй переключатель, оба должны быть замкнуты, прежде чем светодиод заработает. Этот результат может быть отображен в таблице истинности, где ноль означает выключение, а единица — включение:

 SW1 SW2 Светодиод
      0 0 = 0
      0 1 = 0
      1 0 = 0
      1 1 = 1
 

Операция «И» может использоваться для сброса бита в ноль.Из приведенной выше таблицы истинности вы можете видеть, что все, что объединено операцией AND с нулем, равно нулю. Допустим, вы хотели очистить высокий бит реестра, следующий код будет именно так:

 ЛДИ А, 0b1111_1111; А = 11111111
ANDI A, 0b0111_1111; A = 01111111
 

Операции «И» также могут использоваться с «битовой маской» для удаления интересующих нас битов. Например, если нас интересуют только четыре старших бита байта. Мы можем использовать двоичное число 0b1111_0000, чтобы убрать старший младший бит этого регистра и игнорировать остаток:

 ЛДИ А, 0b1010_1111; А = 1111_1111
ANDI A, 0b1111_0000; A = 1010_0000
 

ОПЕРАЦИЯ «ИЛИ»:

Работа «ИЛИ» может быть продемонстрирована следующей схемой с двумя переключателями, включенными параллельно. подключен к свету:

 Switch_1
------------- / --------- +
| ВЕЛ
+ -------- D
Switch_2 |
------------ / ---------- +
 

Ясно видно, что светодиод загорается, когда один «ИЛИ» другой переключатель замкнут, и даже если оба закрыты.Это можно представить в виде таблицы истинности:

 SW1 SW2 Светодиод
      0 0 = 0
      0 1 = 1
      1 0 = 1
      1 1 = 1
 

Операция «ИЛИ» может использоваться для установки бита в единицу. Из приведенной выше таблицы истинности вы можете видеть, что все, что связано с единицей ИЛИ, является единицей. Допустим, вам нужно установить высокий бит реестра, это сделает следующий код:

 ЛДИ А, 0b0101_0101; А = 0101_0101
ORI A, 0b1000_0000; A = 1101_0101
 

ЭКСКЛЮЗИВНАЯ ОПЕРАЦИЯ ИЛИ «МУН»:

Операция «EOR» такая же, как операция «ИЛИ», за исключением того, что она выключена, когда оба переключателя закрыты.Это означает, что светодиод горит, если горит одно «ИЛИ», но не горит оба. Это можно продемонстрировать с помощью следующей таблицы истинности:

 SW1 SW2 Светодиод
      0 0 = 0
      0 1 = 1
      1 0 = 1
      1 1 = 0
 

Если вы посмотрите на приведенную выше таблицу истинности, вы увидите, что единичный EOR с нулем дает единицу, а единичный EOR, совмещенный с единицей, дает нам ноль. Использование EOR с одним дает нам противоположное или обратное. Это дает нам свойство немного переворачиваться. Если вам нужно «мигать» старшим битом регистра, следующий код сделает это, не нарушая другие биты регистра «A»:

 ЛДИ В, 0b1000_0000
LDI A, 0b0101_0101; A = 0101_0101
EOR A, B; A = 1101_0101
EOR A, B; A = 0101_0101
EOR A, B; A = 1101_0101
 

ОБРАТНАЯ ОПЕРАЦИЯ ИЛИ «НЕ»:

Операция «НЕ» или обратная операция означает, что вы хотите обратного, единицы становятся нулем, а нули становятся единицей.Таблица истинности для этого:

 A NOT_A
           0 1
           1 0
 

Если мы вспомним команду EOR, мы поймем, что когда мы EOR что-то с помощью единицы, мы переворачиваем этот бит. Итак, чтобы получить обратное или НЕ целое значение, мы можем выполнить его EOR со всеми единицами:

 LDI B, 0b1111_1111; ВСЕ
       LDI A, 0b1010_1010; A = 1010_1010
       EOR A, B; A = 0101_0101
 

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

Еще один способ добиться аналогичного результата — это прочитать значение на динамике, и если это единица, инвертируйте его в ноль, и если это ноль, инвертируйте его в единицу. Здесь можно использовать инструкцию Exclusive OR (EOR), потому что теперь мы знаем, что все EOR с единицами даст нам обратное или противоположное.

В нашей новой версии основной части нашей программы мы можем использовать регистр J для хранения значения всех и используйте его, чтобы инвертировать то, что находится на Порте B:

; --------------;
; ГЛАВНЫЙ РЕЖИМ;
; --------------;
BEEP: CLR I
BLUPE:
LDI J, 0b1111_1111; ЗАГРУЗИТЬ БИТМАСКУ
В А, ПОРТB; ЧИТАТЬ В ПОРТУ B
EOR A, J; ИНВЕРТИРОВАТЬ / ПЕРЕКЛЮЧИТЬ
ВЫХОДНОЙ ПОРТB, A; ЗАПИСАТЬ В ПОРТ B
RCALL PAUSE; ПОДОЖДИТЕ
ДЕКАБРЬ I
BRNE BLUPE
LOOP: RJMP LOOP; ОСТАВАЙТЕСЬ ЗДЕСЬ, КОГДА СДЕЛАНО
 

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

На Butterfly динамик подключен к контакту 5 порта B, поэтому мы можем загрузить регистр J. взамен со значением 0b0001_0000.

 LDI J, 0b0001_0000; ЗАГРУЗИТЬ БИТМАСКУ
В А, ПОРТB; ЧИТАТЬ В ПОРТУ B
 

ИНСТРУКЦИИ SBI & CBI:

Инструкции Set Bit in I / O Port (SBI) и Clear Bit in I / O Port (CBI) могут использоваться для установки или сбросить биты в порте ввода-вывода, который отправит единицу или ноль на соответствующий вывод.Например, мы могли бы использовать их в основном цикле нашей программы BEEP для активации динамика:

; --------------;
; ГЛАВНЫЙ РЕЖИМ;
; --------------;
BEEP: CLR I
BLUPE:
SBI PORTB, 5; АКТИВИРОВАТЬ ДИНАМИК
RCALL PAUSE; ПОДОЖДИТЕ
CBI PORTB, 5; ВЫКЛЮЧИТЬ ДИНАМИК
RCALL PAUSE; ПОДОЖДИТЕ СНОВА
ДЕКАБРЬ I
BRNE BLUPE
LOOP: RJMP LOOP; ОСТАВАЙТЕСЬ ЗДЕСЬ, КОГДА СДЕЛАНО
 

Таблица данных сообщает нам, что мы можем переключить выходной контакт, записав единицу на входной контакт (PINx).Каждый выходной порт имеет связанный входной регистр, для PORTB это будет PINB. Мы можем упростить переключение динамика:

; --------------;
; ГЛАВНЫЙ РЕЖИМ;
; --------------;
BEEP: CLR I
BLUPE:
SBI PINB, 5; ПЕРЕКЛЮЧЕНИЕ ДИНАМИКА
RCALL PAUSE; ПОДОЖДИТЕ
ДЕКАБРЬ I
BRNE BLUPE
LOOP: RJMP LOOP; ОСТАВАЙТЕСЬ ЗДЕСЬ, КОГДА СДЕЛАНО
 

СТЕКА:

Стек — это структура памяти, похожая на стопку пластин. Вы можете размещать только новые тарелки наверху стопки.Например, если в стеке было три элемента, и вы добавили четвертый:

 [TOP] [TOP]
                          [3] [4] <- НОВИНКА
                          [2] ==> [3]
                          [1] [2]
                        [СТЕК] [1]
                                       [КУЧА]
 

Теперь, если мы удалим [4] из вершины стека, тогда [3] снова станет вершиной стека. Затем, если мы удалим [3], то [2] окажется наверху:

 [TOP] [TOP] [TOP]
                          [4] [3] [2]
                          [3] ==> [2] ==> [1]
                          [2] [1] [СТЕК]
                          [1] [СТЕК]
                        [КУЧА]
  

ИНСТРУКЦИИ ПО PUSH & POP:

Инструкция PUSH копирует регистр в верхнюю часть стека, а POP удаляет значение из вверху стека и копирует его в регистр.

Обычно используется для сохранения значения регистра. Например, если бы мы хотели, чтобы наша программа BEEP использовать только регистр A как в основном цикле нашей программы, так и в подпрограмме PAUSE. Мы могли бы поместить A в стек перед вызовом подпрограммы PAUSE, а затем вытащить ее из стека после того, как вернемся:

; --------------;
; ГЛАВНЫЙ РЕЖИМ;
; --------------;
BEEP: CLR A; ИСПОЛЬЗОВАТЬ КАК СЧЕТЧИК
BLUPE: SBI PINB, 5; ПЕРЕКЛЮЧЕНИЕ ДИНАМИКА
НАЖАТЬ A; СОХРАНИТЬ СОДЕРЖАНИЕ A
RCALL PAUSE; ПОДОЖДИТЕ
POP A; ВОССТАНОВИТЬ A
ДЕКАБРЬ А
BRNE BLUPE
LOOP: RJMP LOOP; ОСТАВАЙТЕСЬ ЗДЕСЬ, КОГДА СДЕЛАНО

ПАУЗА: CLR A
ПЛЮП: НЕТ
ДЕКАБРЬ А
BRNE PLUPE
RET
 

Еще лучший способ сделать это — поместить инструкции PUSH & POP в подпрограмму PAUSE.Первое, что мы делаем, это сохраняем A в стеке до того, как его значение изменится, а затем просто восстанавливаем его. прежде чем мы вернемся. Это делает процедуру PAUSE многоразовой и переносимой, поскольку она сохраняет и восстанавливает значение регистра, которое он использует:

 ПАУЗА: НАЖАТЬ А; СОХРАНИТЬ СОДЕРЖАНИЕ А
CLR A
ПЛЮП: НЕТ
ДЕКАБРЬ А
BRNE PLUPE
POP A; ВОССТАНОВИТЬ A
RET
 

Теперь, когда у нас есть базовое понимание языка ассемблера AVR, вам будет проще следуйте более продвинутым руководствам.Еще один отличный способ научиться — смотреть на другой рабочий код и наблюдать методы, которые они используют. Я рекомендую вам взять рабочий код и поэкспериментировать с ним, потому что лучший способ изучить Ассемблер — это писать код на Ассемблере, учиться на практике.


Бесплатный счетчик

Какой машинный код соответствует инструкции языка ассемблера AVR sub r24, r15 E …

  • Какой машинный код соответствует инструкции языка ассемблера AVR и r31, rO En…

    Какой машинный код соответствует инструкции языка ассемблера AVR и r31, rO Введите свой ответ в виде 16-битного двоичного числа. Не должно быть пробелов или знаков препинания — просто введите шестнадцать единиц или нулей.) Какой машинный код соответствует инструкции языка ассемблера AVR и r31, rO Введите свой ответ в виде 16-битного двоичного числа. Не должно быть пробелов или знаков препинания — просто введите шестнадцать единиц или нулей.)

  • Пожалуйста, помогите, показывая шаги.Вопрос 4. (продолжение) (b) Рассмотрим 16-битное двоичное число, хранящееся в регистрах AVR r1 …

    Пожалуйста, помогите, показывая шаги. Вопрос 4. (продолжение) (b) Рассмотрим 16-битное двоичное число, хранящееся в регистрах AVR r15: r14, которое программист считает значением с дополнением до двух. (r15 содержит старший байт, r14 — наименее значимый.) Запишите последовательность инструкций языка ассемблера AVR, которые выполняют каждую из следующих операций. Результат должен оказаться в r15: r14.При необходимости можно свободно использовать другие регистры i) Устанавливает r15: r14 на постоянное значение -1 (2 отметки) …

  • Разберите следующую инструкцию (замените машинный код на ассемблер): 0x22b8f5cb Предоставляются номера регистров …

    Разберите следующую инструкцию (замените машинный код на ассемблер): 0x22b8f5cb Номера регистров указаны в таблице ниже. Коды операций и коды функций можно найти в Интернете. Номер регистра $ t0 $ ti $ t2 $ t3 $ t4 $ t5 $ t6 $ t7 $ 50 $ 51 $ s2 $ s3 $ 54 $ 55 $ 56 $ s7 $ t8 $ t9 Правила: • Все ответы должны быть отформатированы как допустимая сборка MIPS.• Немедленное следует записывать в десятичном формате. • Имена регистров должны начинаться с символа $. На регистры могут ссылаться …

  • Вопрос 2 Язык ассемблера ARM (25 баллов) Сводная информация о наборе команд ARM представлена ​​на …

    Вопрос 2 Язык ассемблера ARM (25 баллов) Краткое изложение набора команд ARM приведено в конце этого документа. (5 баллов) Объясните разницу между инструкциями eor и eors. Используйте пример, чтобы показать, почему обе формы полезны. а. б. (5 баллов) На примере объясните, что делает инструкция «Idr r3, [r7, # 4]».c. (10 баллов) Ниже приводится язык ассемблера, созданный с помощью типа компиляции mystery,% function mystery: args 0, pretend = 0, frame = …

  • Напишите код подпрограммы на ЯЗЫКЕ СБОРКИ (формат Мано), чтобы разделить два положительных числа на повторение …

    Напишите код подпрограммы в ASSEMBLY ЯЗЫК (формат Мано), чтобы разделить два положительных числа на метод повторного вычитания. Например, чтобы разделите 100 на 8, вычтя 8 из 100, пока не появится напоминание. меньше 8, затем подсчитайте время вычитания, чтобы получить результат.Первое число, второе число, а затем результат адреса должны соответствовать телефонному коду. ПОЖАЛУЙСТА, НАПИШИТЕ ОТВЕТ НА ЯЗЫКЕ СБОРКИ ТОЛЬКО.

  • 1. Напишите программу на языке ассемблера, используя набор инструкций MIPS, который считывает два целых числа …

    1. Напишите программу на языке ассемблера, используя набор инструкций MIPS, который считывает два целых числа от пользователя, названного начальным и конечным числом, и определяет все простые числа между началом и концом (включая начало и конец).Ваша программа должна выполнить проверку обоих чисел следующим образом: i. начальный номер должен быть меньше или равен конечному номеру. II. Оба числа должны быть положительными. iii. Максимальное значение для конечного числа — 10000 …

  • Ch04.2. [3 балла] Рассмотрим следующий код языка ассемблера: I0: ADD R4 R1RO I1: SUB R9R3 R4 …

    Ch04.2. [3 балла] Рассмотрим следующий код языка ассемблера: I0: ADD R4 R1RO I1: SUB R9R3 R4; I2: ДОБАВИТЬ R4 — R5 + R6 I3: LDW R2MEMIR3100]; 14: LDW R2 = MEM [R2 + 0]; 15: STW MEM [R4 + 100] = R3; I6: И R2R2 & R1; 17: BEQ R9R1, цель; I8: AND R9 R9 & R1 Рассмотрим конвейер с пересылкой, обнаружением опасностей и 1 слотом задержки для ответвлений.Конвейер представляет собой типичный 5-ступенчатый IF, ID, EX, MEM, WB MIPS …

  • Следует использовать инструкцию x86 на языке ассемблера Intel. 3. Что делает этот псевдокод? И преобразовать его в сборку. prime (1): # 2: prime [2]: # 3: {первое простое число} {второе простое число) кандидат …

    Инструкция на языке ассемблера Intel x86 должна быть использовал. 3. Что делает этот псевдокод? И преобразовать его в сборку. prime (1): # 2: prime [2]: # 3: {первое простое число} {второе простое число) кандидат-5 (первый кандидат на новое простое число) в то время как primeCount <100 цикл ndex: 1 while (index < primeCount) и (prime [index] не делят кандидата равномерно) добавляют 1 к концу индекса, тогда как if (index> primeCount) then (нет существующего простого числа равномерно делит кандидата, поэтому это новое простое число) добавить 1 к primeCount ;…

  • Мне нужен только язык программы ADA этого задания, если кто мог бы напечатать это …

    Мне нужен только язык программы ADA этого задания, если кто можно было бы ввести его в компилятор, и это было бы здорово. Только Версия ADA, а не Python или Visual Basic Вычисление двоичных данных В этом задании вам предлагается решить задачу, используя 3 разных языка программирования: Python (33,33%), Ada (33,33%) и VB (33,33%). Вам необходимо найти компилятор для каждого из упомянутых языков и протестировать их на своих машинах. Проблема, заданная…

  • Пожалуйста, ответьте на все вопросы! Спасибо. Для следующего оператора C, какой будет соответствующая сборка MIPS …

    Пожалуйста, ответьте на все вопросы! Спасибо. Каким будет соответствующий ассемблерный код MIPS для следующего оператора C? Предположим, что переменные a, b, c и d заданы и были объявлены как 32-битные целые числа a — b — (c + 7) + d; 1. 2. Покажите, как значение 0xB47CA034 будет расположено в машине с прямым порядком байтов и прямым порядком байтов. Предположим, что данные сохраняются, начиная с адреса 0 3.Преобразуйте следующие числа с основанием 16 в основание 2 a ….

  • AVR Tutorial — Как работает программирование

    Итак, у вас есть этот чип и его программируемый. Но как разместить свою программу на этом чипе?

    Комментариев? Предложения? Напишите на форум!

    Когда код компилируется в программу Windows, он превращается в «двоичный», что означает, что он не читается человеком (что трудно понять машинам), вместо этого он машиночитаемый.В Windows эти программы (часто называемые приложениями или исполняемыми файлами ) часто называются, оканчиваясь на .exe (например, notepad.exe или winword.exe ), на компьютерах Mac они часто называются, оканчиваясь на .App (хотя Finder это скрывает).

    Для микроконтроллеров двоичные файлы заканчиваются на .hex (сокращение от Intel Hex Format ). Возможны и другие форматы, но это в значительной степени стандартно.

    Ваш компилятор сгенерирует файл .hex из кода, а затем все, что вам нужно сделать, это передать эту программу .hex на чип!

    Как обсуждалось в разделе «Что это такое?», Микросхема AVR имеет небольшой объем флэш-памяти. В этой памяти хранится программа. Когда чип запускается (вы даете ему питание), он запускает любую программу во флеш-памяти. Итак, все, что нам нужно сделать, это выяснить, как записать на флэш-память

    .

    Хороший способ подумать о вспышке — это изучить ее так, как вы, вероятно, знакомы с ней.Флэш-память также используется в картах MMC / SD, которые обычно используются в MP3-плеерах или цифровых камерах. Если вы посмотрите на нижнюю часть карточки, вы увидите маленькие золотые пальчики. Это контактные площадки.


    Задняя сторона мультимедийной карты. Анджей Барабаш.

    Если вы посмотрите в спецификации карты MMC, вы обнаружите, что это то, для чего предназначены 7 контактов. (SD / высокоскоростные карты могут иметь дополнительные прокладки, но этот стандарт был добавлен позже, поскольку существующий стандарт считался слишком медленным для больших мегапиксельных камер)

    Штифт № Имя контакта Функция контактов
    1 #CS Выбор микросхемы (активировать)
    2 SI серийный номер
    3 ЗЕМЛЯ Земля
    4 VCC Мощность
    5 SCK

    Часы данных

    6 Не подключен Не подключен
    7

    SO

    Последовательный выход

    Контакты питания и заземления используются для подключения питания к микросхеме.Вывод Chip Select используется, чтобы сообщить микросхеме, что он должен проснуться, вывод Serial In pin используется для отправки данных на карту, Serial Out — как данные отправляются с карты, а Data Clock — как мы сообщаем карте, насколько быстро мы хотим, чтобы данные были сохранены или получены. Данные выталкиваются из чипа по одному биту за раз, поэтому, если тактовая частота составляет 100 кГц, это означает, что в секунду передается 100 000 бит.

    Если вы хотите восстановить или сохранить данные на флэш-карте, все, что вам нужно сделать, это вставить карту в устройство чтения флэш-карт.Затем у ридера есть драйвер на вашем компьютере, который позволяет вам просто перетаскивать файлы на изображение на экране, и он выполняет всю тяжелую работу по отправке данных на карту.

    Ладно, при чем тут программист? Что ж, микроконтроллер — это как флеш-карта, а картридер — как программист. Когда вы вставляете чип в программатор (или, что более вероятно, кабель программатора в печатную плату, которая содержит чипы), это позволяет программному обеспечению на вашем компьютере взаимодействовать с чипом через программатор.Обычно вы можете просто дать простую команду, например «отправить эту программу», и программист сделает всю тяжелую работу по программированию и проверке данных.

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

    Например, я большой поклонник микросхемы ATtiny2313, поэтому давайте откроем «сводную» таблицу (всего лишь дюжина страниц вместо стандартных 250.На первой странице есть всякая информация о том, что находится в чипе.

    Quick Quiz: Сколько EEPROM имеет чип? Как насчет флеш-памяти? БАРАН?

    На второй странице распиновка. Мы будем иметь дело с пакетом PDIP (пластиковый двухрядный пакет), который выглядит следующим образом:

    Распиновка воспроизводится здесь

    Микросхема имеет 20 контактов. Посмотрите внимательно, и вы заметите, что 6 из этих контактов эквивалентны контактам карты SD / MMC.Есть вывод питания ( VCC ) и вывод заземления ( GND ), также есть вывод SCK (почему-то он называется UCSK ) вывод Serial Out ( MISO — Master In Serial Out ) и Serial In pin ( MOSI — Master Out Serial In) . Единственное, чего у него нет, так это вывода / ChipSelect. Вместо этого он имеет вывод / RESET , который действует аналогичным образом. Когда вывод / RESET находится под положительным напряжением (такое же напряжение, как VCC ), микросхема запускает программу, когда вывод / RESET находится на земле (такое же напряжение, как GND ), тогда микросхема останавливается. и слушает инструкции по программированию на выводах программирования.

    У всех SD-карт одинаковые пальцы в одном месте, поэтому, когда вы вставляете карту в кардридер, подключаются правильные контактные площадки. Программирование AVR работает аналогичным образом. Вместо контактных площадок микросхему часто помещают на печатную плату, которая имеет контакты заголовка, в которые вставляется программатор, распиновка заголовка стандартизирована, так что любой программист может использоваться после правильного подключения заголовка.

    Существует два стандарта для внутрисистемного программирования AVR:

    Слева — стандартный 6-контактный разъем, справа — стандартный 10-контактный разъем, заголовки выглядят следующим образом:

    Вот пример фотографии, показывающей, как это выглядит, когда микросхема подключена с 6-контактным разъемом


    Фото любезно предоставлено EvilMadScientistLabs

    Черная метка на печатной плате и заголовок обозначают контакт №1.Вы можете проследить провода, чтобы сопоставить контакты микросхемы с контактами заголовка и убедиться, что эта макетная плата правильная.

    Упражнение: проверьте распиновку для этой целевой платы

    Чтобы узнать, как сделать «целевую плату», которая позволит вам программировать AVR (в частности, как сказал ATtiny2313), посетите учебник EvilMadScientistLab. Это довольно просто и будет покрывать часть того же материала.

    Теперь, когда вы знаете, как работает программирование, пришло время выбрать программатор weapon .

    Программирование AVR в шестнадцатеричном формате

    В этом посте я описываю, как я создал рабочую программу для AVR непосредственно в шестнадцатеричном формате без использования компилятора C или ассемблера. Непосредственно это не полезно, но помогает понять, как представлены инструкции по сборке. и переведен в двоичный.

    Это простой пример программы, которая устанавливает PB5 (светодиод на Arduino Pro Mini) на вывод и переключает его в бесконечном цикле.

      ldi r16, 0x20
        out DDRB, r16 // установить PB5 на вывод
    петля:
        out PINB, r16 // переключить PB5
        ldi r17, 0xff
    ждать:
        декабрь r17
        брне, подожди
    
        rjmp петля
      

    Кодировка инструкций определена в Руководстве по набору инструкций AVR. и адреса регистров, которые вы найдете в сводке регистров в таблице данных AVR.

    В качестве примера давайте посмотрим на строку из DDRB, r16 . Инструкция определяется следующим образом: Просто возьмите 16-битный код операции 1011 1AAr rrrr AAAA и вставьте r = 0x10 (r16) и A = 0x04 (DDRB), что дает 1011 1001 0000 0100 или 0xb904 в шестнадцатеричном формате.

    Вся программа переведена в двоичный код:

      asm bin шестигранник
    ldi r16, 0x20 1110 0010 0000 0000 e200
    выход 0x04, r16 1011 1001 0000 0100 b904
    выход 0x03, r16 1011 1001 0000 0011 b903
    ldi r17, 0xff 1110 1111 0001 1111 ef1f
    дек r17 1001 0101 0001 1010 951a
    brne -2 1111 0011 1111 0001 f7f1
    rjmp -5 1100 1111 1111 1011 cffb
      

    Для прошивки с avrdude программа должна быть в intel HEX формат.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *