Съдържание:

Урок за асемблер на AVR 2: 4 стъпки
Урок за асемблер на AVR 2: 4 стъпки

Видео: Урок за асемблер на AVR 2: 4 стъпки

Видео: Урок за асемблер на AVR 2: 4 стъпки
Видео: Лекция 4. Архитектура AVR. Ассемблер 2024, Декември
Anonim
Урок за асемблер на AVR 2
Урок за асемблер на AVR 2

Този урок е продължение на „AVR Assembler Tutorial 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) - LED - R (220 ома) - 5V

PD0 (цифров извод 0) - бутон - GND

Можете да проверите дали вашият светодиод е ориентиран правилно, като го свържете към GND вместо PB0. Ако нищо не се случи, обърнете ориентацията и светлината трябва да светне. След това го свържете отново към PB0 и продължете. Картината показва как е свързан моят чертеж arduino.

Стъпка 2: Написване на кода за сглобяване

Писане на кодекса на асамблеята
Писане на кодекса на асамблеята

Напишете следния код в текстов файл, наречен pushbutton.asm и го компилирайте с avra, както направихте в Урок 1.

Забележете, че в този код имаме много коментари. Всеки път, когато асемблерът види точка и запетая, той ще пропусне останалата част от реда и ще премине към следващия ред. Добра практика на програмиране (особено на асемблер!) Е да коментирате силно кода си, така че когато се върнете към него в бъдеще, да знаете какво правите. Ще коментирам доста неща в първите няколко урока, за да знаем какво точно се случва и защо. По -късно, след като станем малко по -добри в асемблирането, ще коментирам нещата малко по -малко подробно.

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

; написано от: 1o_o7; дата: 23 октомври 2014 г.; ************************************

.nolist

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

В него:

ser temp; задайте всички битове в temp на 1. изход DDRB, темп.; задаване на бит като 1 на I/O за посока на данни; регистърът за 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; преместете temp към PortD. PD0 има дърпащ резистор; (т.е. настроен на 5V), тъй като има 1 в този бит; останалите са 0V от 0.

Основен:

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

Забележете, че този път не само имаме много повече коментари в нашия код, но имаме и заглавна секция, която дава известна информация за това кой го е написал и кога е написан. Останалата част от кода също е разделена на секции.

След като компилирате горния код, трябва да го заредите в микроконтролера и да видите, че работи. Светодиодът трябва да светне, докато натискате бутона и след това да се изключи отново, когато го пуснете. Показах как изглежда на снимката.

Стъпка 3: Последователен анализ на кода

Ще пропусна редовете, които са само коментари, тъй като тяхната цел е очевидна.

.nolist

.включва „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 като темп

В този ред използваме асемблерната директива ".def", за да определим променливата "temp" като равна на r16 "работен регистър." Ще използваме регистър r16 като този, който съхранява номерата, които искаме да копираме в различни портове и регистри (които не могат да бъдат записани директно).

Упражнение 1: Опитайте да копирате двоично число директно в порт или специален регистър като DDRB и вижте какво се случва, когато се опитате да сглобите кода.

Регистърът съдържа байт (8 бита) информация. По същество това обикновено е колекция от SR-ключалки, всяка от които е "бит" и съдържа 1 или 0. Можем да обсъдим това (и дори да го изградим!) По-късно в тази поредица. Може би се чудите какво е „работещ регистър“и защо избрахме r16. Ще обсъдим това в бъдещ урок, когато се потопим в тресавището на вътрешностите на чипа. Засега искам да разберете как да правите неща като писане на код и програмиране на физически хардуер. Тогава ще имате референтна рамка от това преживяване, което ще улесни разбирането на паметта и регистърните свойства на микроконтролера. Осъзнавам, че повечето уводни учебници и дискусии правят това по обратния начин, но открих, че първо да се играе видео игра, за да се получи глобална перспектива, преди да се прочете ръководството с инструкции, е много по -лесно, отколкото първо да се прочете ръководството.

rjmp Init; първият ред е изпълнен

Този ред е "относителен скок" към етикета "Init" и всъщност не е необходим тук, тъй като следващата команда вече е в Init, но ние я включваме за бъдеща употреба.

В него:

ser temp; задайте всички битове в temp на 1.

След етикета Init изпълняваме команда "set register". Това задава всички 8 бита в регистъра "temp" (който помните като r16) на 1. Така че сега temp съдържа 0b11111111.

изход DDRB, темп.; задаване на бит като 1 в регистъра за въвеждане/извеждане на данни

; за PortB, който е DDRB, задава този извод като изход; 0 ще зададе този щифт като вход; така че тук всички PortB пинове са изходи (зададени на 1)

Регистърът DDRB (регистър за посока на данни за PortB) казва кои пинове на PortB (т.е. PB0 до PB7) са определени като входни и кои са определени като изходни. Тъй като щифтът PB0 е свързан към нашия светодиод, а останалите не са свързани с нищо, ще зададем всички битове на 1, което означава, че всички те са изходи.

ldi temp, 0b11111110; заредете „незабавния“номер във временния регистър

; ако беше само ld, тогава вторият аргумент би; трябва да бъде място за памет

Този ред зарежда двоичното число 0b11111110 във временния регистър.

изход DDRD, темп; mv temp към DDRD, резултатът е, че PD0 е въведен и

; останалите са изходи

Сега задаваме регистъра за посока на данни за PortD от temp, тъй като temp все още съдържа 0b11111110, виждаме, че PD0 ще бъде обозначен като входен щифт (тъй като има 0 в най -дясното място), а останалите са определени като изходи, тъй като има 1 е на тези места.

clr temp; всички битове в temp са настроени на 0

изход PortB, temp; задайте всички битове (т.е. пинове) в PortB на 0V

Първо „изчистваме“регистрационната температура, което означава настройка на всички битове на нула. След това копираме това в регистъра PortB, който задава 0V на всички тези щифтове. Нула на PortB бит означава, че процесорът ще поддържа този щифт на 0V, един на бит ще доведе до това, че този щифт ще бъде настроен на 5V.

Упражнение 2: Използвайте мултицет, за да проверите дали всички пинове на PortB действително са нула. Случва ли се нещо странно с PB1? Някаква идея защо може да е така? (подобно на упражнение 4 по -долу, след това следвайте кода …) Упражнение 3: Премахнете горните два реда от кода си. Програмата продължава ли да работи правилно? Защо?

ldi temp, 0b00000001; заредете незабавен номер до темп

изход PortD, temp; преместете temp към PortD. PD0 е при 5V (има издърпващ резистор); тъй като той има 1 в този бит, останалите са 0V. Упражнение 4: Премахнете горните два реда от кода си. Програмата продължава ли да работи правилно? Защо? (Това е различно от упражнение 3 по -горе. Вижте схемата за изваждане. Каква е настройката по подразбиране за DDRD за PD0? (Вижте страница 90 от информационния лист)

Първо "зареждаме незабавно" номера 0b00000001 на temp. „Непосредствената“част е налице, тъй като зареждаме прав номер към temp, а не указател към място в паметта, съдържащо номера за зареждане. В този случай просто бихме използвали "ld", а не "ldi". След това изпращаме този номер на PortD, който задава PD0 на 5V, а останалата част на 0V.

Сега сме задали щифтовете като вход или изход и сме задали първоначалните им състояния като 0V или 5V (LOW или HIGH) и така влизаме в нашата програма "loop".

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

; ако бутонът е свързан към PD0, това ще бъде; a 0 при натискане на бутона, 1 в противен случай оттогава; PD0 има издърпващ резистор, който обикновено е при 5V

Регистърът PinD съдържа текущото състояние на PortD щифтовете. Например, ако сте прикрепили 5V проводник към PD3, тогава при следващия тактов цикъл (което се случва 16 милиона пъти в секунда, тъй като микроконтролерът е свързан към 16MHz тактов сигнал) битът PinD3 (от текущото състояние на PD3) ще стане 1 вместо 0. Така че в този ред копираме текущото състояние на щифтовете към temp.

изход PortB, temp; изпраща прочетените по -горе 0 и 1 към PortB

; това означава, че искаме светодиодът да е свързан към PB0, така че; когато PD0 е LOW, той ще настрои PB0 на LOW и ще се завърти; на светодиода (другата страна на светодиода е свързана; към 5V и това ще настрои PB0 на 0V, така че да тече ток)

Сега изпращаме състоянието на пиновете в PinD към изхода PortB. На практика това означава, че PD0 ще изпрати 1 до PortD0, освен ако бутонът не е натиснат. В този случай, тъй като бутонът е свързан към земята, този щифт ще бъде на 0V и той ще изпрати 0 до PortB0. Сега, ако погледнете схемата на веригата, 0V на PB0 означава, че светодиодът ще свети, тъй като другата му страна е на 5V. Ако не натискаме бутона, така че 1 се изпраща към PB0, това би означавало, че имаме 5V на PB0 и също 5V от другата страна на светодиода и така няма потенциална разлика и няма да тече ток и така LED няма да свети (в този случай това е светодиод, който е диод и така токът тече само в една посока, независимо от всичко).

rjmp Main; цикли обратно към Старт

Този относителен скок ни връща обратно към нашия етикет Main: и проверяваме отново PinD и така нататък. Проверка на всеки 16 милионни от секундата дали бутонът е натиснат и съответно настройване на PB0.

Упражнение 5: Променете кода си, така че вашият светодиод да е свързан с PB3 вместо с PB0 и вижте дали работи. Упражнение 6: Включете вашия LED в GND вместо в 5V и променете кода си съответно.

Стъпка 4: Заключение

В този урок ние допълнително изследвахме асемблерния език за ATmega328p и научихме как да управляваме светодиод с бутон. По -специално научихме следните команди:

ser регистър задава всички битове на регистър на 1

clr регистърът задава всички битове на регистър на 0

в регистъра, i/o регистърът копира номера от i/o регистър в работещ регистър

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

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

Препоръчано: