Cuprins:

Tutorial AVR Assembler 2: 4 pași
Tutorial AVR Assembler 2: 4 pași

Video: Tutorial AVR Assembler 2: 4 pași

Video: Tutorial AVR Assembler 2: 4 pași
Video: Лекция 4. Архитектура AVR. Ассемблер 2024, Iulie
Anonim
Tutorial AVR Assembler 2
Tutorial AVR Assembler 2

Acest tutorial este o continuare a „AVR Assembler Tutorial 1”

Dacă nu ați parcurs Tutorialul 1, ar trebui să vă opriți acum și să faceți asta mai întâi.

În acest tutorial vom continua studiul programării limbajului de asamblare al atmega328p utilizat în Arduino.

Vei avea nevoie:

  1. un panou de calcul Arduino sau doar un Arduino normal ca în Tutorialul 1
  2. un LED
  3. un rezistor de 220 ohmi
  4. un buton
  5. conectarea firelor pentru realizarea circuitului pe panoul dvs.
  6. Manual de instrucțiuni: www.atmel.com/images/atmel-0856-avr-instruction-s…
  7. Foaie de date: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…

Colecția completă a tutorialelor mele poate fi găsită aici:

Pasul 1: Construirea circuitului

Construirea circuitului
Construirea circuitului

Mai întâi trebuie să construiți circuitul pe care îl vom studia în acest tutorial.

Iată modul în care este conectat:

PB0 (pin digital 8) - LED - R (220 ohm) - 5V

PD0 (pin digital 0) - buton - GND

Puteți verifica dacă LED-ul dvs. este orientat corect conectându-l la GND în loc de PB0. Dacă nu se întâmplă nimic, inversați orientarea și lumina ar trebui să se aprindă. Apoi reconectați-l la PB0 și continuați. Imaginea arată cum este conectat arduino-ul meu pentru panou.

Pasul 2: Scrierea codului de asamblare

Scrierea Codului de asamblare
Scrierea Codului de asamblare

Scrieți următorul cod într-un fișier text numit pushbutton.asm și compilați-l cu avra așa cum ați făcut în Tutorialul 1.

Observați că în acest cod avem o mulțime de comentarii. De fiecare dată când ansamblul vede un punct și virgulă, acesta va sări peste restul liniei și va trece la următoarea linie. Este o bună practică de programare (mai ales în limbajul de asamblare!) Să comentezi cu strictețe codul tău, astfel încât, atunci când te vei întoarce la el, să știi ce faci. O să comentez lucrurile destul de mult în primele tutoriale, astfel încât să știm exact ce se întâmplă și de ce. Mai târziu, odată ce vom deveni un pic mai buni la codarea ansamblului, voi comenta lucrurile într-un pic mai puțin detaliat.

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

; scris de: 1o_o7; data: 23 octombrie 2014; ************************************

.nolist

.include "m328Pdef.inc".list.def temp = r16; desemnați registrul de lucru r16 ca temp rjmp Init; prima linie executată

Init:

ser temp; setați toți biții în temp la 1. afară DDRB, temp; setarea un pic ca 1 pe I / O direcție date; înregistrați-vă pentru PortB, care este DDRB, setează acest lucru; pin ca ieșire, un 0 ar seta acel pin ca intrare; deci aici, toți pinii PortB sunt ieșiri (setate la 1) ldi temp, 0b11111110; încărcați numărul „imediat” în registrul temporar; dacă ar fi doar ld atunci al doilea argument; ar trebui să fie o locație de memorie în afară de DDRD, temp; mv temp la DDRD, rezultatul este că PD0 este introdus; iar restul sunt ieșiri clr temp; toți biții din temp sunt setați la 0 PortB, temp; setați toți biții (adică pinii) în PortB la 0V ldi temp, 0b00000001; încărcați numărul imediat pentru a elimina PortD, temp; mutați temperatura în PortD. PD0 are un rezistor de tragere; (adică setat la 5V) deoarece are 1 în acel bit; restul sunt 0V de la 0.

Principal:

în temp, PinD; PinD deține starea PortD, copiați acest lucru în temp; dacă butonul este conectat la PD0 acesta va fi; 0 când butonul este apăsat, 1 altfel, deoarece; PD0 are un rezistor de tracțiune, este în mod normal la 5V afară PortB, temp; trimite 0 și 1 citite mai sus către PortB; asta înseamnă că vrem LED-ul conectat la PB0; când PD0 este LOW, setează PB0 la LOW și se întoarce; pe LED (deoarece cealaltă parte a LED-ului este; conectat la 5V și acest lucru va seta PB0 la 0V astfel; curentul va curge) rjmp Main; revine la începutul Main

Observați că de data aceasta nu numai că avem mai multe comentarii în codul nostru, dar avem și o secțiune antet care oferă câteva informații despre cine a scris-o și când a fost scrisă. Restul codului este, de asemenea, separat în secțiuni.

După ce ați compilat codul de mai sus, ar trebui să îl încărcați pe microcontroler și să vedeți că funcționează. LED-ul ar trebui să se aprindă în timp ce apăsați butonul și apoi să se stingă din nou când îl dați drumul. Am arătat cum arată în imagine.

Pasul 3: Analiza linie cu linie a codului

Voi sări peste rândurile care sunt doar comentarii, deoarece scopul lor este evident.

.nolist

.includeți „m328Pdef.inc”.list

Aceste trei linii includ fișierul care conține definițiile Register și Bit pentru ATmega328P pe care îl programăm. Comanda.nolist îi spune ansamblorului să nu includă acest fișier în fișierul pushbutton.lst pe care îl produce atunci când îl asamblați. Dezactivează opțiunea de listare. După includerea fișierului, reactivăm opțiunea de listare cu comanda.list. Motivul pentru care facem acest lucru este că fișierul m328Pdef.inc este destul de lung și nu este nevoie să îl vedem în fișierul listă. Asamblatorul nostru, avra, nu generează automat un fișier listă și, dacă am dori unul, ne-am asambla folosind următoarea comandă:

avra -l pushbutton.lst pushbutton.asm

Dacă faceți acest lucru, va genera un fișier numit pushbutton.lst și dacă examinați acest fișier, veți descoperi că acesta afișează codul de program împreună cu informații suplimentare. Dacă vă uitați la informațiile suplimentare, veți vedea că liniile încep cu un C: urmat de adresa relativă în hexagonală a locului în care este plasat codul în memorie. În esență, începe la 000000 cu prima comandă și crește de acolo cu fiecare comandă ulterioară. A doua coloană după locul relativ în memorie este codul hexagonal pentru comandă urmat de codul hexagonal pentru argumentul comenzii. Vom discuta fișierele de listă în continuare în tutoriale viitoare.

.def temp = r16; desemnați registrul de lucru r16 ca temp

În această linie folosim directiva de asamblare „.def” pentru a defini variabila „temp” ca fiind egală cu „registrul de lucru” r16. Vom folosi registrul r16 ca cel care stochează numerele pe care dorim să le copiem în diferite porturi și registre (care nu pot fi scrise direct).

Exercițiul 1: Încercați să copiați un număr binar direct într-un port sau un registru special precum DDRB și vedeți ce se întâmplă când încercați să asamblați codul.

Un registru conține un octet (8 biți) de informații. În esență, este de obicei o colecție de SR-Latches, fiecare este un „bit” și conține un 1 sau un 0. Putem discuta acest lucru (și chiar să construim unul!) Mai târziu în această serie. Poate vă întrebați ce este un „registru de lucru” și de ce am ales r16. Vom discuta acest lucru într-un viitor tutorial când ne vom scufunda în mlaștina internelor cipului. Deocamdată vreau să înțelegeți cum să faceți lucruri precum scrierea codului și programarea hardware-ului fizic. Apoi veți avea un cadru de referință din acea experiență care va face mai ușor de înțeles memoria și proprietățile înregistrării microcontrolerului. Îmi dau seama că majoritatea manualelor introductive și a discuțiilor fac acest lucru invers, dar am constatat că jocul unui joc video pentru o vreme pentru a obține o perspectivă globală înainte de a citi manualul de instrucțiuni este mult mai ușor decât a citi mai întâi manualul.

rjmp Init; prima linie executată

Această linie este un "salt relativ" la eticheta "Init" și nu este cu adevărat necesară aici, deoarece următoarea comandă este deja în Init, dar o includem pentru o utilizare viitoare.

Init:

ser temp; setați toți biții în temp la 1.

După eticheta Init executăm o comandă „set register”. Aceasta setează toți cei 8 biți din registrul „temp” (care vă amintiți este r16) la 1. Deci, temperatura conține acum 0b11111111.

afară DDRB, temp; setând un pic ca 1 pe registrul I / O de direcție date

; pentru PortB, care este DDRB, setează acel pin ca ieșire; un 0 ar seta acel pin ca intrare; deci aici, toți pinii PortB sunt ieșiri (setate la 1)

Registrul DDRB (Data Direction Register for PortB) indică ce pini de pe PortB (adică PB0 până la PB7) sunt desemnați ca intrare și care sunt desemnați ca ieșire. Deoarece avem pinul PB0 conectat la LED-ul nostru, iar restul nu este conectat la nimic, vom seta toți biții la 1, ceea ce înseamnă că sunt toate ieșiri.

ldi temp, 0b11111110; încărcați numărul „imediat” în registrul temporar

; dacă ar fi doar ld, atunci al doilea argument ar fi; trebuie să fie o locație de memorie

Această linie încarcă numărul binar 0b11111110 în registrul temporar.

afară DDRD, temp; mv temp la DDRD, rezultatul este că PD0 este introdus și

; restul sunt ieșiri

Acum setăm Registrul de direcție a datelor pentru PortD de la temp, deoarece temp conține încă 0b11111110, vedem că PD0 va fi desemnat ca pin de intrare (deoarece există un 0 în punctul din dreapta), iar restul sunt desemnate ca ieșiri, deoarece există Sunt în acele locuri.

clr temp; toți biții din temp sunt setați la 0

out PortB, temp; setați toți biții (adică pinii) din PortB la 0V

Mai întâi „ștergem” temperatura registrului, ceea ce înseamnă setarea tuturor biților la zero. Apoi o copiem în registrul PortB care setează 0V pe toți acei pini. Un zero pe un bit PortB înseamnă că procesorul va păstra acel pin la 0V, unul pe un bit va face ca acel pin să fie setat la 5V.

Exercițiul 2: Folosiți un multimetru pentru a verifica dacă toți pinii de pe PortB sunt de fapt zero. Se întâmplă ceva ciudat cu PB1? Ai vreo idee de ce ar putea fi asta? (similar cu Exercițiul 4 de mai jos, apoi urmați codul …) Exercițiul 3: Eliminați cele două linii de mai sus din cod. Programul rulează în continuare corect? De ce?

ldi temp, 0b00000001; încărcați numărul imediat la temp

out PortD, temp; mutați temperatura în PortD. PD0 este la 5V (are un rezistor pullup); deoarece are un 1 în acel bit, restul sunt 0V. Exercițiul 4: Eliminați cele două linii de mai sus din cod. Programul rulează în continuare corect? De ce? (Acest lucru este diferit de exercițiul 3 de mai sus. Consultați diagrama pin out. Care este setarea implicită DDRD pentru PD0? (A se vedea pagina 90 a fișei tehnice

Mai întâi „încărcăm imediat” numărul 0b00000001 la temp. Partea „imediată” este acolo, deoarece încărcăm un număr drept în sus, mai degrabă decât un pointer către o locație de memorie care conține numărul de încărcat. În acest caz, am folosi pur și simplu „ld” mai degrabă decât „ldi”. Apoi trimitem acest număr către PortD, care setează PD0 la 5V, iar restul la 0V.

Acum am setat pinii ca intrare sau ieșire și le-am configurat stările inițiale ca 0V sau 5V (LOW sau HIGH) și astfel intrăm acum în „bucla” programului nostru.

Principal: în temp, PinD; PinD deține starea PortD, copiați acest lucru în temp

; dacă butonul este conectat la PD0 atunci acesta va fi; un 0 când butonul este apăsat, 1 altfel, deoarece; PD0 are un rezistor de tracțiune, este în mod normal la 5V

Registrul PinD conține starea curentă a pinilor PortD. De exemplu, dacă ați atașat un fir de 5 V la PD3, atunci la următorul ciclu de ceas (care se întâmplă de 16 milioane de ori pe secundă, deoarece avem microcontrolerul conectat la un semnal de ceas de 16 MHz) bitul PinD3 (din starea curentă a PD3) ar deveni 1 în loc de 0. Deci, în această linie copiem starea curentă a pinilor în temp.

out PortB, temp; trimite 0 și 1 citite mai sus la PortB

; asta înseamnă că vrem LED-ul conectat la PB0, deci; când PD0 este LOW, va seta PB0 la LOW și se va transforma; pe LED (cealaltă parte a LED-ului este conectată; la 5V și acest lucru va seta PB0 la 0V astfel încât curentul să curgă)

Acum trimitem starea pinilor în PinD la ieșirea PortB. În mod efectiv, acest lucru înseamnă că PD0 va trimite un 1 la PortD0, cu excepția cazului în care butonul este apăsat. În acest caz, deoarece butonul este conectat la masă, acel pin va fi la 0V și va trimite un 0 la PortB0. Acum, dacă vă uitați la schema circuitului, 0V pe PB0 înseamnă că LED-ul va lumina, deoarece cealaltă parte a acestuia este la 5V. Dacă nu apăsăm butonul, astfel încât un 1 să fie trimis la PB0, asta ar însemna că avem 5V pe PB0 și, de asemenea, 5V pe cealaltă parte a LED-ului și astfel nu există nicio diferență de potențial și nu va curge curent și astfel LED-ul nu va străluci (în acest caz este un LED care este o diodă și astfel curentul curge doar într-o direcție, indiferent de orice).

rjmp Main; bucle înapoi la Start

Acest salt relativ ne revine la eticheta Main: și verificăm din nou PinD și așa mai departe. Verificarea la fiecare 16 milionimi de secundă dacă butonul este apăsat și setarea PB0 în consecință.

Exercițiul 5: Modificați codul astfel încât LED-ul dvs. să fie conectat la PB3 în loc de PB0 și să vedeți că funcționează.

Pasul 4: Concluzie

În acest tutorial am investigat în continuare limbajul de asamblare pentru ATmega328p și am învățat cum să controlăm un LED cu un buton. În special, am învățat următoarele comenzi:

registrul ser setează toți biții unui registru la 1

registrul clr setează toți biții unui registru la 0

în registru, registrul i / o copiază numărul dintr-un registru i / o într-un registru de lucru

În următorul tutorial vom examina structura ATmega328p și diferitele registre, operații și resurse conținute în acesta.

Înainte de a continua cu aceste tutoriale, voi aștepta să văd nivelul de interes. Dacă există un număr de oameni care se bucură de fapt să învețe cum să codeze programele pentru acest microprocesor în limbaj de asamblare, atunci voi continua și construi circuite mai complicate și voi folosi un cod mai robust.

Recomandat: