Оглавление:

Учебник по ассемблеру AVR 2: 4 шага
Учебник по ассемблеру AVR 2: 4 шага

Видео: Учебник по ассемблеру AVR 2: 4 шага

Видео: Учебник по ассемблеру AVR 2: 4 шага
Видео: Лекция 4. Архитектура AVR. Ассемблер 2024, Июль
Anonim
Учебник по ассемблеру AVR 2
Учебник по ассемблеру AVR 2

Этот учебник является продолжением «Учебного пособия по AVR Assembler 1».

Если вы еще не прошли Урок 1, вам следует остановиться и сделать это в первую очередь.

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

Тебе понадобится:

  1. макет Arduino или просто обычный Arduino, как в Уроке 1
  2. светодиод
  3. резистор 220 Ом
  4. кнопка
  5. соединительные провода для создания схемы на вашей макетной плате
  6. Руководство по установке: www.atmel.com/images/atmel-0856-avr-instruction-s…
  7. Лист данных: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…

Полную коллекцию моих руководств можно найти здесь:

Шаг 1: построение схемы

Построение схемы
Построение схемы

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

Вот как это связано:

PB0 (цифровой вывод 8) - светодиод - R (220 Ом) - 5В

PD0 (цифровой контакт 0) - кнопка - GND

Вы можете проверить правильность ориентации светодиода, подключив его к GND вместо PB0. Если ничего не происходит, поменяйте ориентацию, и индикатор должен загореться. Затем снова подключите его к PB0 и продолжайте. На картинке показано, как подключен мой макет Arduino.

Шаг 2: написание кода сборки

Написание кода сборки
Написание кода сборки

Напишите следующий код в текстовом файле с именем pushbutton.asm и скомпилируйте его с помощью avra, как вы это делали в Уроке 1.

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

;************************************

; автор: 1o_o7; дата: 23 октября 2014 г.; ************************************

.nolist

.include "m328Pdef.inc".list.def temp = r16; назначить рабочий регистр r16 как temp rjmp Init; первая строка выполнена

В этом:

ser temp; установить все биты в temp на 1. out DDRB, temp; установка бита как 1 для ввода / вывода Data Direction; регистр для PortB, то есть DDRB, устанавливает это; вывод как выход, 0 установит этот вывод как вход; Итак, здесь все контакты PortB являются выходами (установлено на 1) ldi temp, 0b11111110; загрузить "немедленное" число во временный регистр; если бы это было просто ld, то второй аргумент; вместо DDRD должно быть место в памяти, temp; mv temp в DDRD, в результате вводится PD0; а остальные - это выходы clr temp; все биты в temp устанавливаются в 0; out PortB, temp; установить все биты (т.е. контакты) в PortB на 0V ldi temp, 0b00000001; загрузить немедленный номер для временного выхода PortD, temp; переместите температуру в PortD. PD0 имеет подтягивающий резистор; (т.е. установлен на 5 В), поскольку в этом бите стоит 1; остальные - 0В, так как нули.

Главный:

по температуре, PinD; PinD содержит состояние PortD, скопируйте его в temp; если кнопка подключена к PD0, это будет; 0, когда кнопка нажата, 1 в противном случае, поскольку; PD0 имеет подтягивающий резистор, обычно на выходе 5V PortB, temp; отправляет в PortB считанные выше 0 и 1; это означает, что мы хотим, чтобы светодиод был подключен к PB0,; когда PD0 равен LOW, он устанавливает PB0 на LOW и переключает; на светодиоде (поскольку другая сторона светодиода; подключена к 5V, и это установит PB0 на 0V, поэтому ток будет течь) rjmp Main; возвращается к началу Main

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

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

Шаг 3: Построчный анализ кода

Я пропущу строки, которые являются просто комментариями, поскольку их цель очевидна.

.nolist

.include "m328Pdef.inc".list

Эти три строки включают файл, содержащий определения регистров и битов для программируемого нами ATmega328P. Команда.nolist указывает ассемблеру не включать этот файл в файл pushbutton.lst, который он создает при его сборке. Это отключает опцию листинга. После включения файла мы снова включаем опцию листинга с помощью команды.list. Причина, по которой мы это делаем, заключается в том, что файл m328Pdef.inc довольно длинный, и нам действительно не нужно видеть его в файле списка. Наш ассемблер, avra, не генерирует автоматически файл списка, и если бы он нам понадобился, мы бы собрали его, используя следующую команду:

avra -l pushbutton.lst pushbutton.asm

Если вы это сделаете, он сгенерирует файл с именем pushbutton.lst, и если вы изучите этот файл, вы обнаружите, что он показывает код вашей программы вместе с дополнительной информацией. Если вы посмотрите на дополнительную информацию, вы увидите, что строки начинаются с C:, за которым следует относительный адрес в шестнадцатеричном формате, по которому код помещается в память. По сути, он начинается с 000000 с первой команды и увеличивается с каждой последующей командой. Второй столбец после относительного места в памяти - это шестнадцатеричный код команды, за которым следует шестнадцатеричный код аргумента команды. Мы обсудим файлы списков далее в будущих уроках.

.def temp = r16; назначить рабочий регистр r16 как temp

В этой строке мы используем директиву ассемблера «.def», чтобы определить переменную «temp» как равную «рабочему регистру» r16. Мы будем использовать регистр r16 как тот, в котором хранятся числа, которые мы хотим скопировать в различные порты и регистры (которые не могут быть записаны напрямую).

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

Регистр содержит байт (8 бит) информации. По сути, это обычно набор SR-защелок, каждая из которых является «битом» и содержит 1 или 0. Мы можем обсудить это (и даже построить!) Позже в этой серии. Вам может быть интересно, что такое «рабочий регистр» и почему мы выбрали r16. Мы обсудим это в одном из следующих туториалов, когда погрузимся в трясину внутреннего устройства чипа. А пока я хочу, чтобы вы поняли, как писать код и программировать физическое оборудование. Тогда у вас будет справочная информация из этого опыта, которая упростит понимание свойств памяти и регистра микроконтроллера. Я понимаю, что большинство вводных учебников и дискуссий делают это наоборот, но я обнаружил, что сначала поиграть в видеоигру, чтобы получить глобальную перспективу, прежде чем читать руководство по эксплуатации, намного проще, чем сначала прочитать руководство.

rjmp Init; первая строка выполнена

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

В этом:

ser temp; установить все биты в temp на 1.

После метки Init мы выполняем команду «установить регистр». Это устанавливает все 8 бит в регистре «temp» (который, как вы помните, равен r16) в 1. Итак, temp теперь содержит 0b11111111.

out DDRB, temp; установка бита как 1 в регистре ввода-вывода Data Direction

; для PortB, который является DDRB, устанавливает этот вывод в качестве выхода; 0 установит этот вывод как вход; Итак, здесь все контакты PortB являются выходами (установлено на 1)

Регистр DDRB (регистр направления данных для PortB) сообщает, какие выводы на PortB (то есть с PB0 по PB7) обозначены как входные, а какие - как выходные. Поскольку у нас есть вывод PB0, подключенный к нашему светодиоду, а остальные ни к чему не подключены, мы установим все биты в 1, что означает, что все они являются выходами.

ldi temp, 0b11111110; загрузить "немедленное" число во временный регистр

; если бы это было просто ld, то второй аргумент был бы; должно быть место в памяти

Эта строка загружает двоичное число 0b11111110 во временный регистр.

out DDRD, temp; mv temp в DDRD, в результате вводится PD0 и

; остальное - выходы

Теперь мы устанавливаем регистр направления данных для PortD из temp, поскольку temp все еще содержит 0b11111110, мы видим, что PD0 будет обозначен как входной контакт (поскольку в крайнем правом месте стоит 0), а остальные обозначены как выходы, поскольку есть 1 в этих местах.

clr temp; все биты в temp установлены на 0

out PortB, temp; установите все биты (т.е. контакты) в PortB на 0V

Сначала мы «очищаем» регистр temp, что означает установку всех битов в ноль. Затем мы копируем это в регистр PortB, который устанавливает 0В на всех этих контактах. Ноль в бите PortB означает, что процессор будет поддерживать этот вывод на уровне 0 В, единичка на бите приведет к тому, что этот вывод будет установлен на 5 В.

Упражнение 2: Используйте мультиметр, чтобы проверить, все ли контакты на PortB действительно равны нулю. Что-то странное происходит с PB1? Есть идеи, почему это может быть? (аналогично упражнению 4 ниже, затем следуйте коду…) Упражнение 3: Удалите из кода две приведенные выше строки. Программа по-прежнему работает правильно? Почему?

ldi temp, 0b00000001; загрузить немедленный номер в темп

out PortD, temp; переместите температуру в PortD. PD0 на 5 В (имеет подтягивающий резистор); поскольку он имеет 1 в этом бите, остальные равны 0 В. Упражнение 4. Удалите из кода две приведенные выше строки. Программа по-прежнему работает правильно? Почему? (Это отличается от Упражнения 3 выше. См. Схему выводов. Какая настройка DDRD по умолчанию для PD0? (См. Стр. 90 спецификации)

Сначала мы «немедленно загружаем» число 0b00000001 в temp. «Непосредственная» часть присутствует, поскольку мы загружаем в temp прямое число, а не указатель на ячейку памяти, содержащую загружаемое число. В этом случае мы просто использовали бы «ld», а не «ldi». Затем мы отправляем это число в PortD, который устанавливает PD0 на 5 В, а остальные на 0 В.

Теперь мы установили контакты как вход или выход, и мы установили их начальные состояния как 0 В или 5 В (НИЗКИЙ или ВЫСОКИЙ), и теперь мы входим в «цикл» нашей программы.

Основная: по температуре, PinD; PinD содержит состояние PortD, скопируйте его в temp

; если кнопка подключена к PD0, то это будет; 0, когда кнопка нажата, 1 в противном случае, поскольку; PD0 имеет подтягивающий резистор, он обычно составляет 5 В.

Регистр PinD содержит текущее состояние контактов PortD. Например, если вы подключили провод 5 В к PD3, то в следующем тактовом цикле (который происходит 16 миллионов раз в секунду, поскольку у нас есть микроконтроллер, подключенный к тактовому сигналу 16 МГц) бит PinD3 (из текущего состояния PD3) станет 1 вместо 0. Итак, в этой строке мы копируем текущее состояние контактов в temp.

out PortB, temp; отправляет 0 и 1, прочитанные выше, в PortB

; это означает, что мы хотим, чтобы светодиод был подключен к PB0, поэтому; когда PD0 - НИЗКИЙ, он установит PB0 на НИЗКИЙ и включится; на светодиоде (другая сторона светодиода подключена; на 5 В, и это установит PB0 на 0 В, поэтому ток течет)

Теперь мы отправляем состояние контактов в PinD на выход PortB. Фактически это означает, что PD0 отправит 1 на PortD0, если кнопка не будет нажата. В этом случае, поскольку кнопка подключена к земле, этот вывод будет на 0 В, и он отправит 0 на PortB0. Теперь, если вы посмотрите на принципиальную схему, 0 В на PB0 означает, что светодиод будет светиться, поскольку на другой его стороне 5 В. Если мы не нажимаем кнопку, так что 1 отправляется на PB0, это будет означать, что у нас есть 5 В на PB0, а также 5 В на другой стороне светодиода, поэтому нет разницы потенциалов и ток не будет течь, и поэтому Светодиод не будет светиться (в данном случае это светодиод, который является диодом, и поэтому ток течет только в одном направлении независимо от того, что угодно).

rjmp Main; возвращается к началу

Этот относительный переход возвращает нас к нашей метке Main:, и мы снова проверяем PinD и так далее. Каждые 16 миллионных долей секунды проверять, нажимается ли кнопка, и соответственно настраивать PB0.

Упражнение 5: Измените свой код так, чтобы ваш светодиод был подключен к PB3 вместо PB0, и убедитесь, что он работает. Упражнение 6: Подключите ваш светодиод к GND вместо 5V и соответствующим образом измените свой код.

Шаг 4: Заключение

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

Регистр ser устанавливает все биты регистра в 1

Регистр clr устанавливает все биты регистра в 0

в регистре, регистр ввода-вывода копирует номер из регистра ввода-вывода в рабочий регистр

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

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

Рекомендуемые: