Inhaltsverzeichnis:

AVR Assembler Tutorial 2: 4 Schritte
AVR Assembler Tutorial 2: 4 Schritte

Video: AVR Assembler Tutorial 2: 4 Schritte

Video: AVR Assembler Tutorial 2: 4 Schritte
Video: AVR Assembly Tutorial: Part 1 (Basic Commands) 2024, Kann
Anonim
AVR-Assembler-Tutorial 2
AVR-Assembler-Tutorial 2

Dieses Tutorial ist eine Fortsetzung von "AVR Assembler Tutorial 1"

Wenn Sie Tutorial 1 noch nicht durchlaufen haben, sollten Sie jetzt aufhören und das zuerst tun.

In diesem Tutorial werden wir unser Studium der Assemblersprachprogrammierung des atmega328p fortsetzen, das in Arduinos verwendet wird.

Du wirst brauchen:

  1. ein Steckbrett Arduino oder nur ein normales Arduino wie in Tutorial 1
  2. eine LED
  3. ein 220 Ohm Widerstand
  4. ein Druckknopf
  5. Anschlussdrähte zum Herstellen der Schaltung auf Ihrem Steckbrett
  6. Anleitungsset Anleitung: www.atmel.com/images/atmel-0856-avr-instruction-s…
  7. Datenblatt: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…

Die komplette Sammlung meiner Tutorials finden Sie hier:

Schritt 1: Aufbau der Schaltung

Aufbau der Schaltung
Aufbau der Schaltung

Zuerst müssen Sie die Schaltung konstruieren, die wir in diesem Tutorial untersuchen werden.

So ist es verbunden:

PB0 (digitaler Pin 8) - LED - R (220 Ohm) - 5V

PD0 (digitaler Pin 0) - Taster - GND

Sie können überprüfen, ob Ihre LED richtig ausgerichtet ist, indem Sie sie an GND anstelle von PB0 anschließen. Wenn nichts passiert, kehren Sie die Ausrichtung um und das Licht sollte aufleuchten. Verbinden Sie es dann erneut mit PB0 und fahren Sie fort. Das Bild zeigt, wie mein Steckbrett Arduino angeschlossen ist.

Schritt 2: Schreiben des Assembly-Codes

Schreiben des Assembly-Codes
Schreiben des Assembly-Codes

Schreiben Sie den folgenden Code in eine Textdatei namens pushbutton.asm und kompilieren Sie ihn mit avra wie in Tutorial 1.

Beachten Sie, dass wir in diesem Code viele Kommentare haben. Jedes Mal, wenn der Assembler ein Semikolon sieht, überspringt er den Rest der Zeile und geht zur nächsten Zeile über. Es ist eine gute Programmierpraxis (insbesondere in Assembler!), Ihren Code stark zu kommentieren, damit Sie bei einer späteren Rückkehr wissen, was Sie getan haben. Ich werde in den ersten Tutorials einiges kommentieren, damit wir genau wissen, was los ist und warum. Später, wenn wir in der Assemblercodierung etwas besser geworden sind, werde ich die Dinge etwas weniger detailliert kommentieren.

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

; geschrieben von: 1o_o7; Datum: 23.10.2014;************************************

.nolist

.include "m328Pdef.inc".list.def temp = r16; benennen Sie das Arbeitsregister r16 als temp rjmp Init; erste Zeile ausgeführt

Drin:

Ser-Temp; setze alle Bits in temp auf 1's. aus DDRB, temp; Setzen eines Bits als 1 am Datenrichtungs-E/A; Register für PortB, welches DDRB ist, setzt das; Pin als Ausgang, eine 0 würde diesen Pin als Eingang setzen; Hier sind also alle PortB-Pins Ausgänge (auf 1 gesetzt) ldi temp, 0b11111110; Laden Sie die `unmittelbare' Nummer in das temporäre Register; wenn es nur ld wäre, dann das zweite Argument; müsste ein Speicherort sein statt DDRD, temp; mv temp zu DDRD, Ergebnis ist, dass PD0 eingegeben wird; und der Rest sind Ausgaben clr temp; alle Bits in temp werden auf 0 gesetzt out PortB, temp; setze alle Bits (d.h. Pins) in PortB auf 0V ldi temp, 0b00000001; Laden Sie die sofortige Nummer nach temp out PortD, temp; temp auf PortD verschieben. PD0 hat einen Pull-Up-Widerstand; (d.h. auf 5V gesetzt), da es eine 1 in diesem Bit hat; der Rest ist 0V seit 0's.

Hauptsächlich:

in Temp, PinD; PinD hält den Zustand von PortD, kopiere diesen nach temp; wenn die Taste mit PD0 verbunden ist, ist dies; 0 wenn die Taste gedrückt wird, 1 sonst seit; PD0 hat einen Pull-Up-Widerstand, der normalerweise bei 5 V liegt, PortB, Temp; sendet die oben gelesenen Nullen und Einsen an PortB; das bedeutet, dass wir die LED an PB0,; wenn PD0 LOW ist, setzt es PB0 auf LOW und dreht; auf der LED (da die andere Seite der LED an 5V angeschlossen ist und dies PB0 auf 0V setzt, so dass Strom fließt) rjmp Main; Schleife zurück zum Anfang von Main

Beachten Sie, dass wir dieses Mal nicht nur viele weitere Kommentare in unserem Code haben, sondern auch einen Header-Abschnitt, der einige Informationen darüber enthält, wer und wann es geschrieben wurde. Der Rest des Codes ist ebenfalls in Abschnitte unterteilt.

Nachdem Sie den obigen Code kompiliert haben, sollten Sie ihn auf den Mikrocontroller laden und sehen, dass er funktioniert. Die LED sollte beim Drücken der Taste aufleuchten und beim Loslassen wieder ausgehen. Ich habe auf dem Bild gezeigt, wie es aussieht.

Schritt 3: Zeilenweise Analyse des Codes

Ich überspringe die Zeilen, die nur Kommentare sind, da ihr Zweck offensichtlich ist.

.nolist

.include "m328Pdef.inc".list

Diese drei Zeilen enthalten die Datei mit den Register- und Bit-Definitionen für den ATmega328P, den wir programmieren. Der Befehl.nolist weist den Assembler an, diese Datei nicht in die Datei pushbutton.lst aufzunehmen, die beim Assemblieren erzeugt wird. Es deaktiviert die Auflistungsoption. Nachdem wir die Datei eingebunden haben, schalten wir die Listing-Option mit dem.list-Befehl wieder ein. Der Grund dafür ist, dass die Datei m328Pdef.inc ziemlich lang ist und wir sie nicht wirklich in der Listendatei sehen müssen. Unser Assembler, avra, generiert nicht automatisch eine Listendatei und wenn wir eine möchten, würden wir mit dem folgenden Befehl montieren:

avra -l pushbutton.lst pushbutton.asm

Wenn Sie dies tun, wird eine Datei namens pushbutton.lst generiert und wenn Sie diese Datei untersuchen, werden Sie feststellen, dass sie Ihren Programmcode zusammen mit zusätzlichen Informationen anzeigt. Wenn Sie sich die zusätzlichen Informationen ansehen, werden Sie feststellen, dass die Zeilen mit einem C: beginnen, gefolgt von der relativen Adresse in Hex, wo der Code im Speicher abgelegt wird. Im Wesentlichen beginnt sie bei 000000 mit dem ersten Befehl und erhöht sich von dort mit jedem weiteren Befehl. Die zweite Spalte nach dem relativen Platz im Speicher ist der Hex-Code für den Befehl, gefolgt vom Hex-Code für das Argument des Befehls. Wir werden Listendateien in zukünftigen Tutorials weiter besprechen.

.def-Temp = r16; benennen Sie das Arbeitsregister r16 als temp

In dieser Zeile verwenden wir die Assembler-Direktive ".def", um die Variable "temp" gleich dem "Arbeitsregister" von r16 zu definieren. Wir verwenden das Register r16 als dasjenige, das die Nummern speichert, die wir in verschiedene Ports und Register kopieren möchten (die nicht direkt beschrieben werden können).

Übung 1: Versuchen Sie, eine Binärzahl direkt in einen Port oder ein spezielles Register wie DDRB zu kopieren und sehen Sie, was passiert, wenn Sie versuchen, den Code zusammenzusetzen.

Ein Register enthält ein Informationsbyte (8 Bit). Im Wesentlichen ist es normalerweise eine Sammlung von SR-Latches, von denen jeder ein "Bit" ist und eine 1 oder eine 0 enthält. Wir können dies später in dieser Serie diskutieren (und sogar einen bauen!). Sie fragen sich vielleicht, was ein "Arbeitsregister" ist und warum wir uns für r16 entschieden haben. Wir werden das in einem zukünftigen Tutorial besprechen, wenn wir in den Sumpf der Interna des Chips eintauchen. Fürs Erste möchte ich, dass Sie verstehen, wie Sie beispielsweise Code schreiben und physische Hardware programmieren. Dann haben Sie aus dieser Erfahrung einen Bezugsrahmen, der die Speicher- und Registereigenschaften des Mikrocontrollers leichter verständlich macht. Mir ist klar, dass die meisten einführenden Lehrbücher und Diskussionen dies umgekehrt tun, aber ich habe festgestellt, dass es viel einfacher ist, zuerst ein Videospiel zu spielen, um eine globale Perspektive zu erhalten, bevor Sie die Bedienungsanleitung lesen, als zuerst die Anleitung zu lesen.

rjmp-Init; erste Zeile ausgeführt

Diese Zeile ist ein "relativer Sprung" zum Label "Init" und ist hier nicht wirklich notwendig, da der nächste Befehl bereits in Init ist, wir ihn aber für die zukünftige Verwendung einbinden.

Drin:

Ser-Temp; setze alle Bits in temp auf 1's.

Nach dem Init-Label führen wir einen "set register"-Befehl aus. Dadurch werden alle 8 Bits im Register "temp" (das Sie sich erinnern, ist r16) auf 1 gesetzt. Also enthält temp jetzt 0b11111111.

aus DDRB, temp; Setzen eines Bits als 1 im Datenrichtungs-E/A-Register

; für PortB, das DDRB ist, legt diesen Pin als Ausgang fest; eine 0 würde diesen Pin als Eingang setzen; Hier sind also alle PortB-Pins Ausgänge (auf 1 gesetzt).

Das Register DDRB (Data Direction Register for PortB) teilt mit, welche Pins an PortB (d. h. PB0 bis PB7) als Eingang und welche als Ausgang bezeichnet werden. Da der Pin PB0 mit unserer LED verbunden ist und der Rest mit nichts verbunden ist, werden alle Bits auf 1 gesetzt, was bedeutet, dass sie alle Ausgänge sind.

ldi-Temp, 0b1111110; Laden Sie die `sofortige' Nummer in das temporäre Register

; wenn es nur ld wäre, dann würde das zweite Argument; muss ein Speicherort sein

Diese Zeile lädt die Binärzahl 0b11111110 in das Temp-Register.

aus DDRD, temp; mv temp zu DDRD, Ergebnis ist, dass PD0 eingegeben wird und

; der Rest sind Ausgaben

Jetzt setzen wir das Datenrichtungsregister für PortD von temp, da temp immer noch 0b11111110 enthält, sehen wir, dass PD0 als Eingangspin bezeichnet wird (da ganz rechts eine 0 steht) und der Rest als Ausgänge bezeichnet wird, da gibt es 1 ist an diesen Stellen.

clr temp; alle Bits in temp sind auf 0 gesetzt

aus PortB, temp; setze alle Bits (d.h. Pins) in PortB auf 0V

Zuerst "löschen" wir die Registertemp, was bedeutet, dass alle Bits auf Null gesetzt werden. Dann kopieren wir das in das PortB-Register, das 0V an all diesen Pins setzt. Eine Null an einem PortB-Bit bedeutet, dass der Prozessor diesen Pin auf 0 V hält, eine Eins an einem Bit bewirkt, dass dieser Pin auf 5 V gesetzt wird.

Übung 2: Verwenden Sie ein Multimeter, um zu überprüfen, ob alle Pins an PortB tatsächlich Null sind. Passiert etwas Seltsames mit PB1? Irgendeine Idee, warum das sein könnte? (ähnlich wie in Übung 4 unten folgen Sie dann dem Code…)Übung 3: Entfernen Sie die beiden obigen Zeilen aus Ihrem Code. Läuft das Programm noch richtig? Wieso den?

ldi-Temp, 0b00000001; Laden Sie die sofortige Nummer auf temp

aus PortD, temp; temp auf PortD verschieben. PD0 liegt bei 5V (hat einen Pullup-Widerstand); da es eine 1 in diesem Bit hat, sind der Rest 0V. Übung 4: Entfernen Sie die beiden obigen Zeilen aus Ihrem Code. Läuft das Programm noch richtig? Wieso den? (Dies unterscheidet sich von Übung 3 oben. Siehe das Pinbelegungsdiagramm. Was ist die Standard-DDRD-Einstellung für PD0? (Siehe Seite 90 des Datenblatts

Zuerst "laden wir sofort" die Nummer 0b00000001 auf temp. Der "unmittelbare" Teil ist da, da wir eine direkte Zahl nach temp laden und nicht einen Zeiger auf einen Speicherort, der die zu ladende Zahl enthält. In diesem Fall würden wir einfach "ld" statt "ldi" verwenden. Dann senden wir diese Nummer an PortD, das PD0 auf 5V und den Rest auf 0V setzt.

Jetzt haben wir die Pins als Eingang oder Ausgang gesetzt und wir haben ihre Anfangszustände entweder als 0V oder 5V (LOW oder HIGH) eingerichtet und so geben wir nun unser Programm "Loop" ein.

Main: in temp, PinD; PinD hält den Zustand von PortD, kopiere diesen nach temp

; Wenn der Taster mit PD0 verbunden ist, ist dies; a 0, wenn die Taste gedrückt wird, sonst 1 seit; PD0 hat einen Pull-Up-Widerstand, der normalerweise bei 5V. liegt

Das Register PinD enthält den aktuellen Zustand der PortD-Pins. Wenn Sie beispielsweise ein 5-V-Kabel an PD3 angeschlossen haben, wird beim nächsten Taktzyklus (der 16 Millionen Mal pro Sekunde passiert, da der Mikrocontroller an ein 16-MHz-Taktsignal angeschlossen ist) das PinD3-Bit (vom aktuellen Zustand von PD3) würde eine 1 statt einer 0 werden. In dieser Zeile kopieren wir also den aktuellen Zustand der Pins nach temp.

aus PortB, temp; sendet die oben gelesenen Nullen und Einsen an PortB

; Dies bedeutet, dass wir die LED an PB0 anschließen möchten, also; wenn PD0 LOW ist, wird PB0 auf LOW gesetzt und gedreht; auf der LED (die andere Seite der LED ist angeschlossen; an 5V und dies setzt PB0 auf 0V, damit Strom fließt)

Jetzt senden wir den Zustand der Pins in PinD an den PortB-Ausgang. Dies bedeutet effektiv, dass PD0 eine 1 an PortD0 sendet, es sei denn, die Taste wird gedrückt. Da die Taste in diesem Fall mit Masse verbunden ist, liegt dieser Pin auf 0 V und sendet eine 0 an PortB0. Wenn Sie sich nun den Schaltplan ansehen, bedeutet 0 V an PB0, dass die LED leuchtet, da die andere Seite 5 V beträgt. Wenn wir die Taste nicht drücken, so dass eine 1 an PB0 gesendet wird, bedeutet dies, dass wir 5 V an PB0 und auch 5 V auf der anderen Seite der LED haben und es daher keine Potenzialdifferenz gibt und kein Strom fließt und so die LED leuchtet nicht (in diesem Fall handelt es sich um eine LED, die eine Diode ist und der Strom daher nur in eine Richtung fließt, aber was auch immer).

rjmp-Haupt; schleift zurück zum Start

Dieser relative Sprung führt uns zurück zu unserem Main:-Label und wir überprüfen erneut PinD und so weiter. Überprüfen Sie alle 16 Millionstel Sekunden, ob die Taste gedrückt wird und stellen Sie PB0 entsprechend ein.

Übung 5: Ändern Sie Ihren Code so, dass Ihre LED an PB3 statt an PB0 angeschlossen ist und sehen Sie, dass es funktioniert. Übung 6: Stecken Sie Ihre LED in GND statt 5V und ändern Sie Ihren Code entsprechend.

Schritt 4: Fazit

In diesem Tutorial haben wir die Assemblersprache für den ATmega328p weiter untersucht und gelernt, wie man eine LED mit einem Taster ansteuert. Insbesondere haben wir die folgenden Befehle gelernt:

ser Register setzt alle Bits eines Registers auf 1

clr register setzt alle Bits eines Registers auf 0's

in register, i/o register kopiert die Nummer von einem i/o-register in ein Arbeitsregister

Im nächsten Tutorial werden wir den Aufbau des ATmega328p und die darin enthaltenen verschiedenen Register, Operationen und Ressourcen untersuchen.

Bevor ich mit diesen Tutorials fortfahre, werde ich das Interesse abwarten. Wenn es eine Reihe von Leuten gibt, denen es Spaß macht, Programme für diesen Mikroprozessor in Assembler zu codieren, dann werde ich weitermachen und kompliziertere Schaltungen konstruieren und robusteren Code verwenden.

Empfohlen: