Оглавление:
- Шаг 1: построение схемы
- Шаг 2: написание кода сборки
- Шаг 3: Построчный анализ кода
- Шаг 4: Заключение
Видео: Учебник по ассемблеру AVR 2: 4 шага
2024 Автор: John Day | [email protected]. Последнее изменение: 2024-01-30 11:47
Этот учебник является продолжением «Учебного пособия по AVR Assembler 1».
Если вы еще не прошли Урок 1, вам следует остановиться и сделать это в первую очередь.
В этом руководстве мы продолжим изучение программирования на ассемблере atmega328p, используемого в Arduino.
Тебе понадобится:
- макет Arduino или просто обычный Arduino, как в Уроке 1
- светодиод
- резистор 220 Ом
- кнопка
- соединительные провода для создания схемы на вашей макетной плате
- Руководство по установке: www.atmel.com/images/atmel-0856-avr-instruction-s…
- Лист данных: 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 и содержащиеся в нем различные регистры, операции и ресурсы.
Прежде чем продолжить эти уроки, я собираюсь подождать и посмотреть, насколько интересен. Если есть несколько людей, которым действительно нравится учиться кодировать программы для этого микропроцессора на языке ассемблера, то я продолжу и буду создавать более сложные схемы и использовать более надежный код.
Рекомендуемые:
Учебник по ассемблеру AVR, 1: 5 шагов
AVR Assembler Tutorial 1: Я решил написать серию руководств о том, как писать программы на языке ассемблера для Atmega328p, который является микроконтроллером, используемым в Arduino. Если люди останутся заинтересованными, я буду продолжать выпускать по одному в неделю или около того, пока у меня не закончатся
Учебник по ассемблеру AVR 8: 4 шага
AVR Assembler Tutorial 8: Добро пожаловать в Tutorial 8! В этом коротком руководстве мы немного отвлечемся от введения новых аспектов программирования на языке ассемблера, чтобы показать, как переместить наши компоненты прототипирования в отдельный «печатный» каталог. печатная плата
Учебник по ассемблеру AVR 7:12 шагов
Учебник 7 AVR Assembler: Добро пожаловать в Урок 7! Сегодня мы собираемся сначала показать, как очистить клавиатуру, а затем показать, как использовать порты аналогового ввода для связи с клавиатурой. Мы сделаем это, используя прерывания и один провод в качестве Вход. Мы подключим клавиатуру так, чтобы
Учебник по ассемблеру AVR 9: 7 шагов
AVR Assembler Tutorial 9: Добро пожаловать в Урок 9. Сегодня мы покажем, как управлять как 7-сегментным, так и 4-значным дисплеем, используя наш код языка ассемблера ATmega328P и AVR. В ходе этого нам придется отвлечься от того, как использовать стек
Учебник по ассемблеру AVR 11: 5 шагов
AVR Assembler Tutorial 11: Добро пожаловать в Tutorial 11! В этом коротком руководстве мы, наконец, собираемся построить первую часть нашего финального проекта. Первое, что вам нужно сделать, это перейти к самому последнему шагу этого руководства и посмотреть видео. Тогда вернись сюда. [пауза, пока вы