Workshop: Programmierung in Assembler (Atmel ATM8)

Workshops von Usern dieses Forums angeboten.

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Workshop: Programmierung in Assembler (Atmel ATM8)

#1

Beitrag von Muenchner Kindl »

Hallo,

bei diesem Workshop geht es darum, Interessierte an die Programmierung von Mikrocontrollern in der hardwarenahen Programmiersprache "Assembler" heranzuführen.
Als Basis dient der AT Mega 8 aus dem Hause Atmel. Assembler und dessen Syntax ist im Prinzip bei allen Prozessortypen zumindest ähnlich, erworbene Kenntnisse können also mehr oder weniger auch bei anderen Typen verwendet werden, was natürlich auch von der Architektur des jeweiligen Prozessorts abhängt.

Ich werde diesen ersten Beitrag primär als Inhaltsverzeichnis nutzen, hier kann man dann zu den jeweiligen Beiträgen dieses Threads klicken... ich werde diesen Beitrag also stetig bearbeiten und ergänzen.

Inhaltsverzeichnis:

Grundlagen
- Was ist ein Mikrocontroller? Grundlegendes zum Aufbau und Programmierung eines ATM8
- Register und deren Funktionen
- Ein/Ausgabe, die IO-Ports und deren Steuerregister
- Installation und erste Schritte mit ATMEL Studio 4
- Grundlegendes zum Aufbau eines Programms
- Byteoperationen, Logik und Arithmetik
- Bedingte und unbedingte Sprünge
- Schleifen und verschachtelte Schleifen
- Kalibrieren des internen Oszillators
- Unterprogramme
- Interrupts allgemein
- Timer
- Retten von Registern
- SRAM adressieren, beschreiben, auslesen Teil 1 (reservierte Bytes)
- SRAM adressieren, beschreiben, auslesen Teil 2 (Pointer)
- EEPROM beschreiben und auslesen
- Tasten auswerten und Entprellen
- Analog-Digital-Converter

Projekt Ampel
- Grundüberlegungen
- Initialisierung und Hauptprogramm Teil 1
- Hauptprogramm Teil 2
- Modul Verkehrsampel
- Modul Fussgängerampel
- Variable Zeiten

Weiterführende Links, Dokumente:
- Übersicht über Atmel Mega 8
- AVR Instruction Set (PDF)
- Datenblatt AT Mega 8 Zusammenfassung (PDF)
- Datenblatt AT Mega 8 komplett (PDF)
- Mikrocontroller Tutorial

Als Basis dieses Workshops dient die Universallichtsteuerung "UL1" von Modellbau Schönwitz mit einem von mir ergänzten Adapter zum Programmiergerät.
Bild
Diese, von Hand erweiterte, Platine (Fertigbaustein, ich bezeichne ihn ab jetzt als "UL-E)) ist bei mir für 31,00€ + Versandkosten 6,00 (DHL Paket) erhältlich. Selbstverständlich kann die Platine auch anderswo beschafft und in Eigenregie um den Adapter erweitert werden (ist kein grosser Aufwand) und selbstverständlich kann auch ein beliebiges Evaluationsboard, z.B. von Pollin, verwendet werden.

Wir arbeiten mit folgenden Voraussetzungen:
- ATM8
- 2 Taster (möglichst entprellt) gegen Masse auf PD2 und PD3
- 1 Poti auf ADC0 (interne Referenz), idealerweise jeweils Poti auf ADC1 und ADC2
- 8 LEDs highaktiv an PB0-PB7
- Programmer AVRISP MKII, Atmel-Studio 4

Wer die von mir hier angebotenen UL-E bestellen möchte, den bitte ich, dies bis 19.9.11 zu tun. Bis dahin sollten die ersten Themen hier zu lesen sein, so dass die Entscheidung für oder auch gegen die Teilnahme, sowie die Entscheidung für oder gegen die Anschaffung einer UL-E oder einer anderen Experimentierumgebung erleichtert sein sollte.
Ich biete zudem an, die erforderlichen Programmer zu besorgen und zusammen mit den UL-E zu auszuliefern. Da ich die Programmer selbst nicht vertreibe und deshalb zum gewöhnlichen Endkundenpreis bei Reichelt beziehe, werde ich genau diesen Preis 1:1 weiterreichen, erspare dem Besteller jedoch damit die Versandkosten.
Den aktuellen Endkundenpreis erfahrt Ihr hier, sollte sich dieser zum Zeitpunkt der eigentlichen Bestellung ändern werde ich den evtl. Preisvorteil, sowie leider auch den evtl. Preisnachteil auch weitergeben, allerdings hat sich hier seit etwa 4 Jahren nichts geändert.

Der Workshop richtet sich an Interessierte und soll ihnen einen Einstieg in die Programmierung bieten und diesen erleichtern. Er richtet sich nicht an erfahrene Hobbyprogrammierer, die zum Profi werden wollen.

Der Ablauf sieht so aus, dass ich, wenn möglich, wöchentlich einen Beitrag zu einem bestimmten Thema verfasse, diesen mit Beispielen belege und den Teilnehmern Aufgaben stelle, die sie selbst und mit Hilfe ihrer Experimentierumgebung lösen sollen. Die Lösungen sollen dann an mich per Mail gesendet werden, ich werde diese dann alle zusammen in einem Beitrag kommentieren und veröffentlichen, damit wir darüber gemeinsam diskutieren können.

Wir werden so gemeinsam an einem Projekt arbeiten, am Ende sollte das eine Ampelsteuerung werden. Diese wird folgendes können:
- Umschaltung von automaticher Verkehrsampel und Anforderungsampel (Fussgängerampel)
- Einstellbare Rot-Phase (ggf. weitere Phasen einstellbar)
- diverse kleine Funktionen, vielleicht auch mit Wünschen und Eigenkreationen ;)

Am Ende habt Ihr mit der UL-E eine Lichtsteuerung, die Ihr mit den erworbenen Kenntnissen selbst programmieren und anschliessend in Euere Anlage integrieren könnt oder zumindest die Kenntnisse und ein beliebiges Experimentierboard.

Die Bestellungen der UL-E und/oder Programmer sind nicht an die Teilnahme des Workshop gekoppelt! Fällt der Workshop aus oder muss dieser z.B. aus gesundheitlichen oder privaten Gründen verzögert oder gar eingestellt werden, ist die Rückgabe des Materials oder die Entschädigung irgendwelcher erbrachten Aufwände ausgeschlossen! Dies gilt auch dann, wenn ein Teilnehmer, aus welchen Gründen auch immer, nicht mehr weiter teilnehmen möchte.

Anregungen und Beschwerden ;) gerne hier, per PN oder Mail... dann lassen wirs mal krachen :idea: :D

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#2

Beitrag von Muenchner Kindl »

Was ist ein Mikrocontroller? Grundlegendes zum Aufbau und Programmierung eines ATM8

Während uns der Begriff "Prozessor" fast täglich begegnet ist der "Mikrocontroller" eher unbekannt, fast befremdlich. Das obwohl er eigentlich nicht viel anderes ist als ein Prozessor und wir eigentlich nahezu ständig damit umgehen, auch wenn wir das nicht bewusst wahrnehmen.
Während der klassische Prozessor, wie z.B. der Pentium, für Rechenoperationen optimiert ist und über einen sehr grossen Befehlssatz verfügt ist der Mikrocontroller mehr auf Steuerungsaufgaben ausgelegt, kennt nur einen reduzierten Befehlssatz und kostet auch nur einen Bruchteil.

Der Hauptunterschied ist, dass der Mikrocontroller i.d.R. alles zum Arbeiten integriert hat. So kommen die Atmels mit einem internen Oszillator, einem internen Flashspeicher für das Programm, einem integrierten SD-Ram als flüchtigen Spreicher und einem EEProm als nicht flüchtigen Speicher daher.

Der Mikrocontroller begegnet uns also überall da, wo es Steuerungsaufgaben zu bewältigen gibt, die vielleicht nur mit komplexen Schaltungen abzubilden sind. Der MC steckt in der Waschmaschine genauso wie in jeder digitalen Modell-Lok. Ihr findet den MC in Eueren Autos, Fernseher, MP3-Player... sogar im Computer stecken neben dem eigentlichen Prozessor noch eine Menge Mikrocontroller und sei es, um den Arm der Festplatte zu steuern.

Und so eignet sich so ein preisgünstiger Mikrocontroller auch wunderbar für Effekte und andere Steuerungen (Lichteffekte, Schrankensteuerung, Blockstellen ect...) im Modellbau, zumal der externe Schaltungsaufwand äusserst gering ist.

Bei dem von uns verwendeten Mega8 von Atmel handelt es sich um einen RISC-Prozessor. RISC steht dabei für "Reduced InstructionSet Computer", also einen reduzierten Befehlssatz. Dieser Befehlssatz ist fest verdrahtet, was für schnelle Programmlaufzeiten sorgt. Das Gegenteil sind CISC-Prozessoren (Complex InstructionSet Computer) wie sie z.B. beim PC verwendet werden, deren Befehlssatz quasi als eigenes Programm (Microcode) integriert ist, was eine langsamere Abarbeitung der einzelnen Befehle zur Folge hat.

Das beste Beispiel sind die damaligen Apple G4, die, sofern ich nicht ganz danebenliege, einen RISC-Prozessor (68000er-Reihe) besassen und mit weit geringeren Taktfrequenzen jeden hochgetakteten Pentium bei der Geschwindigkeit geschlagen haben.

Damit kommen wir auch langsam aber sicher zum Aufbau der Controller aus der ATMega - Reihe. Es gibt eine grosse Auswahl an ATMEGAS und jeder hat so seine Vorzüge und sein Einsatzgebiet. So ist der ATM8, sowie sein Nachfolger ATM88 ein relativ universell einsetzbarer Prozessor, während z.B. der ATM162 mit seinen beiden UART-Ports eher für Schnittstellenaufgaben zuständig ist. Es gibt auch welche mit mehr Speicher (ATM32) und sogar Typen mit integriertem USB-Controller.
Wir widmen uns in diesem Workshop dem universellen AT Mega 8, der neben der Schönwitz-Lichtsteuerungen auch z.B. im RailCom-Anzeigemodul RCA-24 von Tams steckt.
Bild

Wie schon geschrieben besitzt auch der ATM8 einen Flash, einen SD-Ram und einen EEPROM. Dabei dient der Flash zum Speichern des Programms und ist aus dem Programm selbst heraus nicht beschreibbar. Der Flash ist nicht flüchtig (wäre auch ziemlich doof), unbegrenzt auslesbar, jedoch bei den Schreibzyklen auf mehrere 1000 begrenzt.

Der SD-Ram beinhaltet die Register, einen sehr schnellen, frei adressierbaren Speicherbereich sowie den Stack, also sinngemäß, bis auf die Register, das was der Arbeitsspeicher beim PC ist. Es gibt keine Grenze beim Auslesen und Beschreiben, dafür ist das SD-Ram flüchtig und verliert seine Daten im stromlosen Zustand. Das SD-Ram wird ausschliesslich zur Laufzeit des Programms beschrieben oder gelesen.

Das EE-Prom ist ein nicht flüchtiger Festwertspeicher. Auch dieser kann, wie das Flash, nicht unbegrenzt beschrieben werden (mehrere tausend Schreibzyklen). Für das Auslesen gibt es keine Grenzen. Das EEProm kann zur Laufzeit und mit Hilfe eines Programmers beschrieben und ausgelesen werden. Hier legt man z.B. während der Entwicklung Seriennummern ab, das Programm selbst kann hier z.B. den aktuellen Zustand ablegen (für unser Projekt z.B. ob wir auf "Fussgängerampel" oder "automatische Verkehrsampel" gestellt haben).

Ferner besitzen die ATMEGAs, je nach Typ mehrere Ein-Ausgabe-Ports (IO-Ports), meist mit einer Breite von 8 Bit sowie i.d.R. einen UART (ser. Schnittstelle mit 5V). Die Datenrichtung der IO-Ports, sogar der einzelnen Pins wird dabei im Programm festgelegt. Ist ein Port oder ein Pin als Eingang definiert kann sogar ein interner PullUp oder PullDown-Widerstand festgelegt werden.

Auch ein oder mehrere A/D-Converter sind verfügbar, damit kann ein analoges Signal digitalisiert werden.

Die meisten Atmels erzeugen ihren Arbeitstakt entweder selbst (interner Oszillator) oder können auch von extern (Quarz oder Frequenznormal) getaktet werden. Für unsere und auch die meisten sonstigen Anwendungen reicht der interne Oszillator, spielt die Genauigkeit eine grosse Rolle sollte man extern takten.

Auf weitere Eigenschaften, Funktionen und Features, z.B. Timer, werde ich eingehen wenn wir so weit sind, und so widmen wir uns langsam immer spezieller unserem ATM8, auf dessen Datenblatt ich zunächst hinweisen möchte. Man findet darin eigentlich alles, was man wissen muss und will und man wird auch in diversen Foren immer wieder auf das Datenblatt hingewiesen. Wir könnten das Datenblatt Seite für Seite durchgehen... damit ist der WS in einem Jahr so weit, dass wir anfangen können... bringt aber nichts. Ich bitte Euch aber, das Datenblatt anzuschauen, vielleicht beantwortet es auch vorab die eine oder andere Frage, die in meinem Beitrag nicht oder noch nicht beantwortet ist. Macht Euch ein wenig damit vertraut, ein besseres Nachschlagewerk gibt es nicht.

Der 8-Bit-Controller kann mit max. 16 Mhz getaktet werden, wir werden 8Mhz verwenden. Es gibt einen UART (auf den wir nicht eingehen) und, das ist für uns sehr wohl von Bedeutung, drei IO-Ports:
PB : 8Bit (PB0-PB7)
PC : 7Bit (PC0-PC6)
PD : 8Bit (PD0-PD7)
Damit haben wir 23 einzelne Ein/Ausgabe-Pins, die wir nach Belieben definieren und ein/auslesen können.

Es gibt auch ein SPI-Interface, welches zur Kommunikation zwischen mehreren Controllern genutzt wird (so z.B, zwischen dem AVRISP-Programmer und dem Controller, auch das EasyNet von Tams basiert auf SPI) sowie zwei externe, maskierbare, Interupt-Eingänge (mit Reset, was ja im Prinzip auch ein Interupt ist, sind es dann 3).

Der Workshop wird grundsätzlich folgende Elemente des ATmega8 behandeln:
- I/O
- Timer (damit auch Software-Interupt)
- ADC (AD-Converter)
- EE-Prom

Was vielleicht erstmal nach wenig aussieht wird uns sicher lange Zeit beschäftigen, damit lassen sich bereits sehr sehr viele Steuerungs- und Überwachungsaufgaben realisieren.

Ich möchte Euch auch hier das Tutorial als Nachschlagewerk empfehlen. Ich werde hier und da darauf verweisen und verlinken...

Bis zum nächsten Beitrag, wo ich auf Begrifflichkeiten wie "Register", "Ports", "Stack" und "Interupts" eingehen werde, könnt Ihr nun Fragen ect. hier im Anschluss posten. In die Praxis steigen wir nach dem 19.9. bzw. wenn alle ihre Experimentierumgebungen oder auch Simulatoren haben, ein.

Danke für die Aufmerksamkeit und als "Hausaufgabe"... bitte mit dem Datenblatt vertraut machen, bzw. es wenigstens anschauen ;) .

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#3

Beitrag von Muenchner Kindl »

Die Register und deren Funktionen

Nachdem wir ein paar Grundlagen erörtert haben fällt der Blick nun auf die Register und deren Funktionen.
In diesem Beitrag überstreifen wir dabei nur die wichtigsten und grundlegenden Register, viele andere kommen im Laufe des Workshops noch dazu.
Auch wenn wir mit dem einen oder anderen in der Praxis eher nicht mehr, bzw. nicht mehr direkt konfrontiert werden, die Grundlagen sollte man zumindest gehört haben.

Wenn wir beim ATM8 und anderen vergleichbaren von Registern sprechen, dann sind damit immer 1 oder 2 Byte grosse Speicherbereiche, zum Teil im SRam gemeint. Dabei gibt es Register, die uns als Programmierer als schneller Speicher frei zur Verfügung stehen und eben solche, die eine feste Aufgabe zu erfüllen haben und mit denen wir hier erstmal anfangen.

Das wohl wichtigste Register ist der Programmzähler bzw. Programmcounter, PC:
In diesem Register steht immer die Adresse des abzuarbeitenden Befehls.
Hierbei ist zu beachten, dass es Befehle mit einer Größe von 1 Byte, 2 Byte oder 3 Byte gibt. So handelt es sich bei Befehlen ohne Operanden um solche mit einer Größe von 1 Byte (der Befehl selbst), anderen hängt noch ein Byte für z.B. eine Konstante oder eben 2 Byte für eine Adresse (z.B. Sprungbefehl) an. Je nachdem, wie gross der jeweilige Befehl ist wird der PC bei dessen Abarbeitung entsprechend erhöht und zeigt dann eben auf den nächst folgenden Befehl.
Nach einem Reset zeigt der PC auf Adresse 0x0000 und an dieser steht für gewöhnlich ein Sprungbefehl zum Programmanfang. Dieser Umstand ist notwendig, weil nach der Adresse 0x0000 die Interupt-Vektortabelle, auf die wir noch gesondert eingehen werden, folgt, die natürlich übersprungen werden muss.
Mit dem Stichwort "Sprungbefehl" kommen wir zu den Möglichkeiten, mit denen wir den PC beeinflussen können. Dies ist natürlich mit einem Reset (PC -> 0x0000) möglich, in erster Linie werden wir es aber mit Sprungbefehlen zu tun haben.
Da es während der Entwicklungszeit unmöglich ist, Einsprungadressen von vielleicht noch gar nicht vorhandenen Programmteilen zu ermitteln bieten uns die meisten Compiler die Möglichkeit, mit Labeln zu arbeiten. Wir springen also nicht zur Adresse 0x0815 sondern z.B. zum frei benennbaren Label "Zaehler:" In so fern werden wir mit dem PC nicht direkt in Berührung kommen, um dessen Bedeutung sollten wir jedoch wissen.

Wir haben hier auch schon eines gesehen, was wir für die Zukunft festlegen, nämlich die Schreibweise von Zahlen:
Wenn wir es mit einer Hexadezimalzahl zu tun haben, dann steht da bitte ein 0x vorne dran, Binärzahlen haben ein 0b, mit anderen Zahlensystemen werden wir seltenst bis gar nicht in Berührung kommen.
So stellen wir die Dezimalzahl 3 folgendermassen dar:
Hexadezimal: 0x03
Binär: 0b00000011

Ein weiteres, ebenfalls elementares und wichtiges Register ist der Stapelzeiger, bzw. Stackpointer, SP.
Dazu folgende Vorgeschichte:
Es gibt einen Bereich des Arbeitsspeichers, der als Stapelspeicher, Stack, genutzt wird. Was zunächst fürchterlich kompliziert klingt ist eigentlich ganz simpel. Stellen wir uns einfach einen Bereich auf unserem Schreibtisch vor, auf dem wir unsere Arbeit in Form von Zetteln kurzzeitig ablegen. Wenn wir etwas ablegen, dann oben drauf, wenn wir etwas vom Stapel entnehmen, dann nur von oben. Dies nennt man auch FirstIn-LastOut-Speicher, Neudeutsch auch Filo und genau so funktioniert auch der Stack. Lege ich etwas auf dem Stack ab kümmere ich mich nicht weiter darum, wo das genau ist... was ich zuletzt abgelegt habe bekomme ich auch zuerst wieder.
Der einzige Unterschied zum Schreibtischstapel ist, dass der Stack des ATM nach unten "wächst", der erste "Zettel" kommt also an die höchste verfügbare Speicheradresse und jetzt sind wir da, wo ich eigentlich hinwollte... woher weis der Prozessor, welches die höchste Speicheradresse ist und wo der nächste Zettel abzulegen ist...? Genau, das weis entweder der Geier oder eben der Stapelzeiger, Stackpointer ;) .
Zu Beginn des Programmes sollte dieser stets (man kann den Stack auch anders definieren, bringt aber keinen Sinn) mit der zuletzt verfügbaren SRam-Adresse geladen werden (dazu kommen wir noch). Danach wird der SP jedesmal dekrementiert, wenn etwas auf dem Stack abgelegt wird. Dabei kann es sich um eine Adresse (2Byte) oder um ein einzelnes Byte handeln, entsprechend wird der SP um 1 oder 2 erniedrigt (dekrementiert) und zeigt damit auf den nächsten freien Platz im Stapel.
Entnehme ich eine Adresse oder ein Byte, werden im SP wieder 1 oder 2 dazugezählt, der SP wird inkrementiert und so wird der Stapel wieder kleiner.
Hier liegt auch eine grosse Gefahr für den Programmierer verborgen! Dem Stackpointer ist es egal, wie die Daten im Stapel angekommen sind, die Reihenfolge und die Systematik muss der Programmierer wissen und dringendst beachten! Dafür gibt es aber auch nur drei Möglichkeiten, etwas im Stack abzulegen und damit den SP zu beeinflussen:
- Retten von Registern
- Aufruf von Unterprogrammen
- Ausführen von Interuptserviceroutinen
Das Retten von Registern geschieht durch einen einfachen Befehl "Push reg", welcher den Inhalt (1 Byte) eines bestimmten Arbeitsregisters auf dem Stack ablegt (und natürlich den SP dekrementiert). Mit "Pop reg" holt man sich das, auf das der SP aktuell zeigt ins angegebene Register zurück und inkrementiert den SP.
Wird ein Unterprogramm oder eine Interuptserviceroutine aufgerufen, dann wird der Inhalt des Programmzählers im Stack abgelegt (und der SP um 2 dekrementiert). Damit wird die Rücksprungadresse gesichert, das Unterprogramm endet mit einem Returnbefehl, der wiederum die letzten 2 Byte des Stack in den PC lädt und natürlich den SP wieder um 2 inkrementiert.

Damit ist die erwähnte Gefahr sicher gut nachvollziebar:
- Ich rufe ein Unterprogramm auf (Rücksprungadresse wird auf Stapel gelegt)
- Zuerst rette ich ein Register (Inhalt wird auf Stapel gelegt)
- Ich vergesse das Zurückretten des Registers (Stackpointer zeigt auf gerettetes Byte!)
- Ich beende mein Unterprogramm (der Inhalt des geretteten Registers wird damit Bestandteil der Rücksprungadresse)
- Das Programm wird an einer undefinierten Stelle fortgeführt, vermutlich nicht lauffähig.

Um auch den korrekten Ablauf zu zeigen:
- Ich rufe ein Unterprogramm auf (Rücksprungadresse wird auf Stapel gelegt)
- Ich rette ein Register (Inhalt wird auf Stapel gelegt)
- Ich arbeite mein UP ab
- Ich rette mein Register zurück (SP wird inkrementiert)
- Ich beende mein UP (korrekte Rücksprungadresse wird in PC geladen und SP wird zweimal inkrementiert)
Tja... und wenn da mehrere Register gerettet werden, dann müssen die in der umgekehrten Reihenfolge zurückgerettet werden.

Zur Entspannung widmen wir uns noch kurz zu den Registern, mit denen wir uns wirklich direkt und intensiv beschäftigen werden... den Registern ;) . Wirkliche Namen haben die eigentlich nicht, nennen wir die mal Arbeitsregister.
Der ATM8 ermöglicht es uns, auf 17 Register direkt zuzugreifen, diese zu beschreiben, auszulesen, mit Ihnen Rechenoperationen durchzuführen usw.
Diese Register sind benannt mit r16, r17,... bis r32. Jedoch können wir, ähnlich wie mit den Labels für Adressen, diesen auch Namen geben. Ein Register mit dem Namen "Temp" zum Beispiel können wir uns sicher besser merken als die Tatsache, dass wir das Register r18 als temporäre Variable nutzen. Die Zuordnung wird am Anfang gemacht, die Benamsung ist, wie die Labels, keine Eigenschaft des Prozessors sondern eine Funktion des Compilers, die aber i.d.R. unterstützt wird. Zu erwähnen ist, dass kein Register den Namen eines Labels tragen darf und umgekehrt. Gibt es also ein Label "Zeit:", dann darf es kein Register mit dem Namen "Zeit" geben, ansonsten weigert sich der Compilier und meldet brav den Fehler.

Ich hatte eigentlich noch vor, auf weitere Register (Steuerregister, Datenrichtungsregister ect.) einzugehen, werde es aber hier erstmal belassen. Ich weis, dass so mancher Begriff fürcherlich abschreckend klingt, letztendlich verbergen sich aber einfache und logische Funktionen dahinter und wenn man es ein wenig verinnerlicht werden Stackpointer und Programmcounter plötzlich ganz harmlos und logisch.

Zwei klitzekleine Aufgaben zum Überlegen habe ich für Euch dieses Mal :twisted:
Es folgen ein paar Programmzeilen in Assembler, ohne grosse Funktion. Es werden ein Label enthalten sein, ebenso ein paar Push- und Pop-Befehle.
Diese sind in der ersten Aufgabe unnvollständig und ich bitte Euch, diese zu vervollständigen:

Code: Alles auswählen

Unterprogramm:
Push r16
Push
Push r20
Push
Push
...
...
...
...
Pop r25
Pop r24
Pop
Pop r18
Pop
ret
Die zweite Aufgabe besteht darin, einen Fehler zu finden. Nach Aufruf des Unterprogramms "Fehler" funktioniert das ganze Programm nicht mehr:

Code: Alles auswählen

Fehler:
push Temp
push Zeit
push Zahl
push Ergebnis
...
...
...
pop Ergebnis
pop Zeit
pop Zahl
pop Temp
ret
Bei diesen Aufgaben geht es weniger um die Befehle sondern um das Verständnis um den Stack.

Ich schlage vor, die Lösungen hier zu veröffentlichen, dann können wir uns direkt darüber unterhalten. Viel Spass beim Knobeln, selbstverständlich können im Anschluss auch Fragen gestellt oder diskutiert werden.
Bis denn, und nicht von den Begriffen Angst einjagen lassen :mad:

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#4

Beitrag von Muenchner Kindl »

Ferenc hat geschrieben:Hallo,
bei der zweiten Aufgabe muß pop Zeit und pop Zahl getauscht werden ?
Bei der ersten Aufgabe bin ich noch am knobbeln :D

Ferenc
Hi,

damit es für die anderen nicht langweilig wird ist es per Mail wohl doch besser...

Also die Lösungen bitte an thomas@twyschkony.de

Danke,

Thomas
Benutzeravatar

DeMorpheus
Metropolitan (MET)
Beiträge: 3632
Registriert: Mi 22. Dez 2010, 13:08
Nenngröße: H0
Steuerung: MS2 Gleisbox + BPi
Gleise: C-Gleis: ML+2L-fähig
Wohnort: Aachen
Alter: 30
Kontaktdaten:

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#5

Beitrag von DeMorpheus »

Hier meine Lösungen, ich hab das mal extra klein gemacht, damit niemand das versehentlich liest. Könnte man dafür nicht evtl. [spoiler="..."][/spoiler] einführen?

Code: Alles auswählen

Unterprogramm:
Push r16
Push r18
Push r20
Push r24
Push r25
...
...
...
...
Pop r25
Pop r24
Pop r20
Pop r18
Pop r16
ret

Code: Alles auswählen

Fehler:
push Temp
push Zeit
push Zahl
push Ergebnis
...
...
...
pop Ergebnis
pop Zahl
pop Zeit
pop Temp
ret
[/size]

Schöne Grüße,
Moritz
Viele Grüße,
Moritz
'Nitwit! Blubber! Oddment! Tweak!'

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#6

Beitrag von Muenchner Kindl »

Ferenc hat geschrieben:Hallo,
mit lauter "Herr Lehrer, Herr lehrer " ich weis was :oops:
Das nächste mal an die Mail-Adresse, versprochen :pflaster:

Ferenc
Hey, das war meine Schuld, ich hatte ja bei den Aufgaben vorgeschlagen, das gleich hier zu lösen. Dürfte aber nicht ideal sein, vielleicht aber mit der Kleinschrift von Moritz.

Eines noch:
Anregungen, Wünsche, Verbesserungsvorschläge... immer gerne. Auch wenn ich was umständlich geschrieben habe... oder sich irgendwo ein Fehler eingeschlichen hat... nur zu!
Ich weis, am Anfang ist das ein trockenes Thema aber es entwickelt sich langsam so, wie ich das vorhabe, so dass wir zeitnah und gut vorbereitet in die Praxis einsteigen können, sobald alle ihre Platinen haben.

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#7

Beitrag von Muenchner Kindl »

Hi,

ein kleiner Nachtrag noch zur ersten Aufgabe:
Push und Pop alleine gibt es nicht. Die fehlenden Register bei den Push-Befehlen könnt Ihr bei den Pop-Kommandos herleiten und umgekehrt.
Über die Bedeutung der Kommandos werden wir uns noch eingehender unterhalten, wenngleich Push und Pop zu sen simplen gehören ;)

Die zweite Aufgabe scheint keine grosse Herausforderung zu sein ;-)
Hierzu aber noch eine kleine Zusatzfrage, und die kann/soll auch wirklich öffentlich diskutiert werden:
Ist bei dem fehlerhaften Unterprogramm "Fehler" mit Instabilität, also einem gar nicht mehr funktionierenden Programm zu rechnen?
Benutzeravatar

DeMorpheus
Metropolitan (MET)
Beiträge: 3632
Registriert: Mi 22. Dez 2010, 13:08
Nenngröße: H0
Steuerung: MS2 Gleisbox + BPi
Gleise: C-Gleis: ML+2L-fähig
Wohnort: Aachen
Alter: 30
Kontaktdaten:

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#8

Beitrag von DeMorpheus »

Muenchner Kindl hat geschrieben:Nach Aufruf des Unterprogramms "Fehler" funktioniert das ganze Programm nicht mehr:
Muenchner Kindl hat geschrieben:Ist bei dem fehlerhaften Unterprogramm "Fehler" mit Instabilität, also einem gar nicht mehr funktionierenden Programm zu rechnen?
:lol:

Trotzdem habe ich das Gefühl, dass das Programm irgendwie weiterlaufen würde, kann das aber im Moment nicht wirklich begründen.
Wird zu Beginn des Unterprogramms automatisch der Inhalt des Programcounters in den Stack gelegt oder muss man das ausdrücklich in den Code schreiben?
Viele Grüße,
Moritz
'Nitwit! Blubber! Oddment! Tweak!'

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#9

Beitrag von Muenchner Kindl »

Hi,

zu Deinem Gefühl schau ma mal was andere sagen ;)
Wird zu Beginn des Unterprogramms automatisch der Inhalt des Programcounters in den Stack gelegt oder muss man das ausdrücklich in den Code schreiben?
Wird ein Unterprogramm aufgerufen kommt der PC automatisch in den Stack, das muss/darf nicht im Code passieren. Gleiches gilt für das Ausführen einer Interuptserviceroutine, also auch einem Unterprogramm, nur dass in dieses durch einen Interupt gesprungen wurde.

Man kann also behaupten, dass der Befehl "rcall" oder eben das Eintreten eines Interupt den Prozessor dazu verlanlassen, den Programmzähler zu sichern.
Das Rücksichern geschieht durch die Befehle "ret" oder "reti".
(Die erwähnten Kommandos werdet Ihr bald nicht mehr sehen können :mrgreen: )

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#10

Beitrag von Muenchner Kindl »

Hallo nochmal,
Nach Aufruf des Unterprogramms "Fehler" funktioniert das ganze Programm nicht mehr:
Peinlich, da steckt die Lösung gleich in der Aufgabe oder war ich einfach nur zu schlampig bei der Formulierung? Vielleicht sollten wir auch Begriffe wie "instabil" besser definieren. Ich kenne das so (ganz ohne Wikipedia und Co), dass ein Program instabil ist, wenn es nicht mehr geordnet beendet werden kann. Ist bei den meisten Firmwares natürlich Blödsinn, die Waschmaschine schalten wir aus und auch den Drucker beenden wir nicht, bevor wir den Stecker ziehen.
Lasst uns den Begriff "instabil" so definieren, dass ein Programm in einem nicht definierten Zustand verharrt und aus diesem nicht mehr, ausser mit Reset, zu retten ist.
Dies kann z.B. passieren, wenn eine Rücksprungadresse verfälscht wird und der PC dann irgendwo lustig im SD-Ram herumspringt.

Im Gegensatz dazu ist ein Programm, welches falsche oder unerwünschte Ergebnisse liefert, auch nicht mehr wert, aber es kann durchaus stabil sein ;)

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#11

Beitrag von Muenchner Kindl »

Hallo,

nachdem die wenigen Lösungen, die mich erreicht haben, alle korrekt waren, ist es natürlich blödsinn, die alle hier zu veröffentlichen.

Ich schnappe mir eine Lösung und stelle die hier aus:

Code: Alles auswählen

Unterprogramm:
Push r16
Push r18
Push r20
Push r24
Push r25
...
...
...
...
Pop r25
Pop r24
Pop r20
Pop r18
Pop r16
ret
Es wurde richtig erkannt, dass man das, was man zuletzt in den Stack legt, zuerst wieder abholen muss und umgekehrt.

Die zweite Aufgabe wurde ebenfalls von allen, die gewantwortet haben, korrekt gelöst:

Code: Alles auswählen

Fehler:
push Temp
push Zeit
push Zahl
push Ergebnis
...
...
...
pop Ergebnis
pop Zeit  --> Zahl
pop Zahl ---> Zeit
pop Temp
ret
Richtig, beim Zurückretten wurden die Register vertauscht.

Nun zur "Zusatzaufgabe":
Trotzdem habe ich das Gefühl, dass das Programm irgendwie weiterlaufen würde, kann das aber im Moment nicht wirklich begründen.
Das Programm würde ganz normal weiterlaufen und nicht in einen instabilen oder undefinierten Zustand geraten. Da nur zwei Register vertauscht wurden, würde das den Ablauf an sich nicht erschüttern, die Funktion würde das Programm aber sicher nicht erfüllen. Die Auswirkung hängt dabei natürlich von der Verwendung der Register ab. "Zahl" könnte vielleicht ein Rechenergebnis sein... dies wäre dann am Ende falsch. "Zeit" könnte eine Variable einer Zeitschleife sein. Diese würde dann nicht mehr wie gewollt funktionieren, was das Programm zwar per Definition nicht instabil macht, jedoch von den Auswirkungen her gewaltig negativ beeinflussen könnte.

Das Problem bei solchen Vertauschern ist, dass man den Fehler dann zu gerne woanders sucht. Da wird vielleicht die Zeitschleife Zeile für Zeile ergebnislos abgeklopft, da sucht man sich bei der Berechnung der "Zahl" einen Wolf... und am Ende ist es ein Dreher in irgendeinem Unterprogramm. Fataler ist es, wenn dieses Unterprogramm bis zur Ergebnisermittlung mehrmals aufgerufen wird... dann kann es nämlich passieren, dass das Ergebnis zwischendrin auch korrekt ist :shame:

Ich werde dieses Wochenende die IO-Geschichten ansprechen. Damit will ich niemanden unter Druck setzen, ich weis nur nicht, wie es das Wochenende drauf bei mir aussehen wird. Ich werde zwischendrin auch die Adapter an die Platinen löten müssen, so dass Ihr nach der IO-Theorie erstmal 2 Wochen verdauen könnt ;)

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#12

Beitrag von Muenchner Kindl »

Ein/Ausgabe, die IO-Ports und deren Steuerregister

Wir nähern uns langsam aber sicher den ersten praktischen Anwendungen und da das Verschieben und Bearbeiten von Bits und Bytes innerhalb des Prozessors wohl ziemlich langweilig sein wird möchte ich mit diesem Beitrag die IO beleuchten. IO steht für Input Output und diese Einheit ist bei den Atmels ziemlich mächtig und äusserst universell einsetzbar.

Zunächst aber noch ein Ausflug zu den Registern und einem Punkt, in dem sie sich unterscheiden:
Es gibt Register, die sich direkt beschreiben lassen und zur Verarbeitung des Inhaltes herangezogen werden können und solche, die sich nicht direkt* beschreiben lassen und die auch nicht, z.B. als Rechenoperatoren, verwenden* lassen.
So lassen sich die Arbeitsregister r16 - r32 direkt mit einem entsprechenden Befehl mit Inhalten befüllen oder zu Operationen verwenden:

Code: Alles auswählen

ldi r18, 0x03
Mit dem Assemblerbefehl ldi (Load Immediate) wird die Hexzahl 0x03 direkt in das Register r18 geschrieben.

Code: Alles auswählen

Add r17, r18
Diese Zeile ist eine Rechenoperation. Die Inhalte von r17 und r18 werden addiert und das Ergebnis in r17 geschrieben.

Steuerregister und IO-Ports müssen über Umwege* befüllt werden. Will ich die Hexzahl 0x03 an den Port B ausgeben (um die LEDs 0 und 1 zum Leuchten zu bringen), dann muss ich das über ein Arbeitsregister bewerkstelligen:

Code: Alles auswählen

ldi Temp, 0x03
out PortB, Temp
Es wird also zuerst das Arbeitsregister geladen, anschliessend wird dessen Inhalt an den Port oder an das Steuerregister ausgegeben.

Code: Alles auswählen

in r18, Pinc
add r17, r18
Auch umgekehrt geht es nur über Register. Um das, was an Port C anliegt mit dem Inhalt von r17 zu addieren muss das Portregister erst in ein Arbeitsregister eingelesen werden.

* Es gibt einen Weg, Port- und Steuerregister direkt auszulesen oder zu verändern. Diese wird zu einem späteren Zeitpunkt angesprochen aber der Vollständigkeit halber möchte ich erwähnen, dass einzelne Bits per Befehl direkt gesetzt oder gelöscht werden können.

Wir sehen auch gleich einen wichtigen Punkt zur Syntax in Assembler: Wenn wir Daten verschieben oder bearbeiten wird zuerst immer das Ziel genannt:

Code: Alles auswählen

ldi r16, 0x03               ;Das Ziel ist r16
add r16, r17               ;r16 und r17 werden addiert, das Ergebnis der Operation steht danach in r16
mov Ergebnis, Temp    ;Der Inhalt von "Temp" wird nach "Ergebnis" kopiert
Kommen wir nun zum eigentlichen Thema IO:
Unser ATM8 besitzt, wie bereits erwähnt, 3 IO-Ports mit einer Breite von 7 bzw. 8 Bit. Diese Ports sind nichts anderes als Steuerregister und pro Port gibt es sogar zwei davon, tatsächlich sogar drei!

Zunächst gibt es für jeden IO-Port ein Richtungsregister (ich nenne das einfach mal so) mit der korrekten Bezeichnung DDR (Data Direction Register). In diesem Register kann für jedes Bit, also für jeden Pin, eines Ports die Datenrichtung festgelegt werden. Für die Bits eines Ports, welche als Ausgang verwendet werden sollen muss dabei das entsprechende Bit im DDR-Register gesetzt sein, gelöschte Bits im DDR-Register definieren den jeweiligen Pin eines Ports als Eingang. Klingt kompliziert ist aber mit Hilfe eines Beispieles sicher nachvollziehbar:

Alle Pins von Port B sollen als Ausgang dienen -> es müssen alle Bits in DDRB gesetzt werden.

Code: Alles auswählen

ldi r16, 0xFF                ;alle Bits setzen
out ddrb, r16              ;Inhalt von r16 (alle gesetzten Bits) ins Steuerregister DDRB schreiben
Die Pins 0-3 von Port B sollen als Ausgang dienen, 4-7 als Eingang -> hier müssen im DDRB Bit0-Bit3 gesetzt (1) und Bit 4-7 gelöscht (0) sein.

Code: Alles auswählen

ldi r16, 0b00001111     ;Bit0-3 sind gesetzt (entsprechende IO-Pins sind Ausgänge) Bit4-7 gelöscht (IO-Pins sind Eingänge)
out ddrb, r16              ;Inhalt von r16 ins Steuerregister DDRB schreiben
Die Bestimmung der Richtung kann im Prinzip jederzeit erfolgen, wirklich Sinn macht sie eigentlich nur zum Programmstart, nennen wir diesen Vorgang mal Initialisieren.

Als Ausgang definierte Ports können, wie weiter oben schonmal angedeutet, u.a. mit dem Kommando "Out" beschrieben werden. Gehen wir mal davon aus, wir haben in DDRB den Wert 0xFF (0b11111111) geschrieben, dann brächten folgende Zeilen Sinn:

Code: Alles auswählen

ldi r16, 0x55  ;Wert 0x55 in r16 laden
out portb, r16         ;Inhalt von r16 ins Ausgangsregister von Portb schreiben
Es würde jede zweite LED an Port B leuchten.

Nun haben wir von den erwähnten 3 Registern pro Port nur 2 bearbeitet... das dritte benötigen wir, wenn der jeweilige Port, oder einzelne Bits davon, als Eingang definiert ist.
Nehmen wir mal an, wir haben den Wert 0x00 in das Register DDRC geschrieben und damit den Port C komplett als Eingang definiert.

Code: Alles auswählen

in r16, pinc     ;Eingangsregister von Portc in r16 einlesen
Zusammenfassend sprechen wir also, wenn wir von einem IO-Port reden, von drei Registern:
DDRx = Data Direction Register (Datenrichtung)
Pinx = Port Input (Eingangsregister)
Portx = Port Output (Ausgangsregister)
(x steht jeweils für den Buchstaben des Ports)

Damit wir alle so richtig was zu Beissen haben kommt dem Register Portx auch eine Bedeutung zu, wenn der jeweilige Port als Eingang definiert wurde. In diesem Fall ist es möglich, für jeden Eingang einen Pullup- oder Pulldown-Widerstand quasi zu schalten, was man im Rahmen der Initialisierung auch dringend beachten sollte.
Nehmen wir mal an, wir wollen an die Eingänge PD0 und PD1 jeweils einen Schalter anschliessen, der gegen - schaltet und die natürlich eingelesen werden sollen.

Code: Alles auswählen

Initialisierung:
 ldi r16, 0x00      ;alle Bits in r16 auf 0
 out ddrd, r16     ;alle PD0-PD7 sind Eingänge

 ldi r16, 0x03      ;Bit 0 und 1 sind gesetzt
 out portd, r16   ;PD0 und PD1 haben einen PullUp-Widerstand, die anderen Pins einen PullDown-Widerstand

Programm:
 in r16, pind       ;Schalter nach r16 einlesen
 ...
Damit haben wir die IO-Geschichte erstmal theoretisch und auch noch nicht ganz vollständig abgearbeitet. Wer es bisher ohne Kopfweh und Knoten in den Gehirnwindungen geschafft hat besitzt die besten Voraussetzungen für den weiteren Workshop ;-)
Ja, ist trocken und teilweise ziemlich strange aber schlimmer wird es nicht mehr... eher dank Praxis einfacher, da noch besser nachvollziehbarer.

Kommen wir nun zur Aufgabe der Woche und damit vielleicht zu einer ersten grossen Herausforderung. Wir haben hier exemplarisch die ersten Befehle gesehen und auch wie man mit den Ein- und Ausgängen umgeht. Für diese "Hausaufgabe" werden wir auch ausnahmsweise mal etwas schwindeln und wir tun so, als wären alle IO-Ports des ATM8 gleich breit, also 8 Bit. Künfig, und auch für eigene Projekte, bitte beachten, dass Port C nur 7 Bit breit ist, also nur PC0-PC6. Da dies aber eine Trockenübung ist und das Programm erstmal nicht lauffähig sein wird wollen wir das ausnahmsweise ausser Acht lassen.

Als Aufgabe hätte ich gerne einen "Taschenrechner" :twisted:
Dabei werden an PB0-PB7, sowie PC0-PC7 jeweils Schalter (highaktiv, also gegen +) angeschlossen, an PD0-PD7 kommen LEDs zur Anzeige.
Die Schalter an Port B stehen dabei für den Operand 1, die an Port C für Operand 2
Die LEDs sollen das Ergebnis von Operand1+Operand2 anzeigen, es sollen also die Schalterstellungen addiert werden.
Lösungsvorschläge bitte per Mail an thomas@twyschkony.de . Fragen dagegen bitte hier im Thread, damit alle was davon haben. Damit überlasse ich Euch Euerem Schicksal und Ihr habt erstmal für 1-2 Wochen Ruhe von mir :redzwinker: ... natürlich stehe ich aber für Fragen und Antworten zur Verfügung ;)
Benutzeravatar

DeMorpheus
Metropolitan (MET)
Beiträge: 3632
Registriert: Mi 22. Dez 2010, 13:08
Nenngröße: H0
Steuerung: MS2 Gleisbox + BPi
Gleise: C-Gleis: ML+2L-fähig
Wohnort: Aachen
Alter: 30
Kontaktdaten:

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#13

Beitrag von DeMorpheus »

Hallo, dann habe ich zwei kleine Fragen:

Code: Alles auswählen

in r18, Pinc
add r17, r18
Auch umgekehrt geht es nur über Register. Um das, was an Port C anliegt mit dem Inhalt von r18 zu addieren muss das Portregister erst in ein Arbeitsregister eingelesen werden.
Wird das an Port C anliegende nicht mit r17 addiert statt mit r18?

Außerdem verstehe ich die Aufgabenstellung nicht :oops:
Sollen wir einfach die aktiven Schalter „zählen“ und dementsprechend viele LED leuchten lassen?
Viele Grüße,
Moritz
'Nitwit! Blubber! Oddment! Tweak!'

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#14

Beitrag von Muenchner Kindl »

Hi,
Wird das an Port C anliegende nicht mit r17 addiert statt mit r18?
Stimmt, Du hast Recht. Werde das ausbessern :oops:
Sollen wir einfach die aktiven Schalter „zählen“ und dementsprechend viele LED leuchten lassen?
An Port B liegt eine Binärzahl an, an Port C auch. Beide Binärzahlen sollen addiert werden und an Port D ausgegeben werden.

Beispiel:
PortB: 0 0 0 0 0 1 1 0 (=6)
+
PortC: 0 0 0 0 0 0 1 0 (=2)
=
PortD: 0 0 0 0 1 0 0 0 (=8)

In erster Linie geht es mir hier um die korrekte Initialisierung und Bedienung der Ports.
Benutzeravatar

DeMorpheus
Metropolitan (MET)
Beiträge: 3632
Registriert: Mi 22. Dez 2010, 13:08
Nenngröße: H0
Steuerung: MS2 Gleisbox + BPi
Gleise: C-Gleis: ML+2L-fähig
Wohnort: Aachen
Alter: 30
Kontaktdaten:

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#15

Beitrag von DeMorpheus »

Ah, jetzt hat's klick gemacht :)
Aber die nächste Frage folgt sofort: Befinden sich die Ausgangsregister zum Programmstart in einem definierten Zustand ( 0x00 )?
Viele Grüße,
Moritz
'Nitwit! Blubber! Oddment! Tweak!'

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#16

Beitrag von Muenchner Kindl »

Hi,
Befinden sich die Ausgangsregister zum Programmstart in einem definierten Zustand ( 0x00 )?
Ja, Nein, Vielleicht... wenn ich ehrlich bin weis ich das nicht sicher ;) . Wenn es auf einen definierten Zustand ankommt ist es auf jeden Fall ratsam und wird auch überall empfohlen, für diesen bei der Initialisierung selbst zu sorgen.

Wenn man also z.B. sicher stellen will, dass keine LED leuchtet sollte man beim Initialisieren 0x00 in das entsprechende Portausgangsregister schreiben.

Für unsere Aufgabe spielt das keine Rolle, da auf jeden Fall etwas ausgegeben wird.

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#17

Beitrag von Muenchner Kindl »

Moin,

noch ein Hinweis zu unserer Aufgabe "Taschenrechner":
Es geht bei dieser Trockenübung in erster Linie um die notwendigen Schritte, die zur Lösung führen. Das Programm wird, so wie es bis jetzt gefordert ist, nicht ganz lauffähig sein, dazu fehlen ein paar Kleinigkeiten. Aber selbst wenn es lauffähig wäre, es würde nur einmal die Schalter auslesen, addieren und ausgeben... danach käme es ohne weitere Punkte, die wir bis jetzt nicht angesprochen haben, in einen undefinierten Zustand und müsste für den nächsten Durchlauf neu gestartet werden.
Für die Trockenübung reicht das, die erste Praxisübung wird so ähnlich aufgebaut sein und auch etwas ähnliches machen... bis dahin werden wir aber auch noch ein oder zwei Themen ansprechen müssen ;)

Viel Spass noch :calc:

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#18

Beitrag von Muenchner Kindl »

Nachtrag:
Muenchner Kindl hat geschrieben:Hi,
Befinden sich die Ausgangsregister zum Programmstart in einem definierten Zustand ( 0x00 )?
Ja, Nein, Vielleicht... wenn ich ehrlich bin weis ich das nicht sicher ;) . Wenn es auf einen definierten Zustand ankommt ist es auf jeden Fall ratsam und wird auch überall empfohlen, für diesen bei der Initialisierung selbst zu sorgen.

Wenn man also z.B. sicher stellen will, dass keine LED leuchtet sollte man beim Initialisieren 0x00 in das entsprechende Portausgangsregister schreiben.

Für unsere Aufgabe spielt das keine Rolle, da auf jeden Fall etwas ausgegeben wird.
Die fett markierte Aussage bezieht sich übrigens nur auf den Ausgabeport D, auf den ja was ausgegeben wird. Die Ausgaberegister von Port B und C sind damit nicht gemeint, dies bitte beim Initialisieren der Pullup/Pulldown-Widerstände beachten.

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#19

Beitrag von Muenchner Kindl »

Zwei Hinweise:

aus organisatorischen Gründen habe ich die bestellten UL-E bereits heute in Auftrag gegeben. Für den Fall, dass noch jemand dazustossen möchte habe ich eine Platine auf Vorrat dazubestellt.
Die UL-E kann ich aber bei Bedarf nachbestellen, halt nicht sofort.
Die AVR-Programmer bestelle ich morgen Abend, die bestelle ich in absehbarer Zeit nicht mehr nach.

Evtl. gibt es zu diesem Workshop noch einen weiteren, ergänzenden. Mehr möchte ich dazu nicht schreiben, das überlasse ich dem Kollegen, der diesen evtl. anbietet. Wenn das klappt wird das eine richtig lohnende Geschichte! Schau ma mal ;-)

Achja, ich habe mir eine Mailverteilerliste für diesen WS angelegt, für den Fall, dass es mal etwas dringendes ausserhalb des Forums mitzuteilen gibt. In dieser Liste stehen derzeit natürlich nur die, die etwas bestellt haben. Wer auch mit eingetragen werden möchte, natürlich ohne Verpflichtungen und ohne Spam, bitte eine kurze Mail an thomas@twyschkony.de

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#20

Beitrag von Muenchner Kindl »

Hallo,

nachdem die ersten Lösungsvorschläge eingetroffen sind und bis auf vielleicht den einen oder anderen Zahlendreher sachlich korrekt sind möchte ich mich mit einer Lösung ein wenig befassen.

Zuerst ein Hinweis (habe ich hier schon ausgebessert), Kommentare beginnen mit einem Semikolon.
Meine Kommentare sind in roter Schrift dargestellt.
; Pins der Ports B und C auf 0 setzen um als Eingänge zu dienen
ldi r16, 0x00
out ddrb, r16

ldi r17, 0x00
out ddrc, r17

ldi r16, 0x00
out portb, r16

ldi r17, 0x00
out portc, r17
; Hier wurden die Direction-Register von Port B und C korrekt als Eingänge definiert und die PullDownwiderstände gesetzt. Es hätte allerdings gereicht, ein Register zu verwenden und dieses nur einmal zu laden*
;Ausgang

;Pins des Ports D auf 0 setzen um als Ausgänge zu dienen
ldi r18, 0x00
out ddrd, r18
; Hier steckt ein Fehler drin, den dürft Ihr gerne suchen... Antworten bitte im Thread.
* Die Verwendung mehrerer Register habe ich bei mehreren Lösungen gesehen. Sie ist nicht falsch und bringt auch keine Nachteile, solange man dies beim Initialisieren belässt. Wenn man im weiteren Programm so grosszügig damit umgeht, dann gehen einem erstens recht schnell die Register aus, zweitens baut man sich evtl. unnötige Stolperfallen ein. Spätestens wenn die Register Namen bekommen und wir ihnen damit quasi einen Zweck zuordnen, müssen wir sparsamer damit umgehen.
;Addition der Eingänge B unc C
in r16, pinb
in r17, pinc
add r18,r16
add r18, r17
; Wenn man davon ausgehen kann, dass in r18 nichts drinsteht, bzw. r18 vorher geleert wurde, ist diese Lösung zumindest funktionsfähig. Ansonsten wird zu einem Wert x das von PinB dazuaddiert, anschliessend das von Pinc. Wird das, was man von den Ports einliest auch noch andersweitig verwendet, dann ist das dritte Register notwendig. Dies war hier nicht gefordert und so kann Pinb in r16, PinC in r17 eingelesen werden und mit "add r16, r17" addiert werden.

;Ausgabe auf Port D
ldi r19, r18
out portb, r19
; Den Befehl "ldi r19, r18" wird uns der Compiler mit einer Fehlermeldung quittieren. Wenn Registerinhalte kopiert werden sollen, dann mit "mov r1, r2". Das Kopieren eines Registers ist jedoch hier auf keinen Fall notwendig, das z.B. in r16 stehende Additionsergebnis kann direkt mit "out PortD, r16" ausgegeben werden.

Ferenc
Metropolitan (MET)
Beiträge: 3255
Registriert: Mo 26. Sep 2005, 23:35
Nenngröße: H0
Stromart: AC
Steuerung: Ecos 1 + Tams MC + TC Gold
Gleise: Märklin M-K-C Gleis
Wohnort: HDH
Alter: 56

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#21

Beitrag von Ferenc »

Hallo Thomas,
du schreibst: ldi r18, 0x00, muß es nicht ldi r18, 0xFF heissen ?

Ferenc
Immer eine Handbreit Schotter unter der Schwelle ;-)

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#22

Beitrag von Muenchner Kindl »

Ferenc hat geschrieben:Hallo Thomas,
du schreibst: ldi r18, 0x00, muß es nicht ldi r18, 0xFF heissen ?

Ferenc
Beziehst Du Dich da drauf?

;Pins des Ports D auf 0 setzen um als Ausgänge zu dienen
ldi r18, 0x00
out ddrd, r18
; Hier steckt ein Fehler drin, den dürft Ihr gerne suchen... Antworten bitte im Thread.

Ferenc
Metropolitan (MET)
Beiträge: 3255
Registriert: Mo 26. Sep 2005, 23:35
Nenngröße: H0
Stromart: AC
Steuerung: Ecos 1 + Tams MC + TC Gold
Gleise: Märklin M-K-C Gleis
Wohnort: HDH
Alter: 56

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#23

Beitrag von Ferenc »

Hi,
ja auf die Zeile.

Ferenc
Immer eine Handbreit Schotter unter der Schwelle ;-)

Threadersteller
Muenchner Kindl
Moderator
Beiträge: 10214
Registriert: Di 26. Apr 2005, 21:26

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#24

Beitrag von Muenchner Kindl »

Hallo Ferenc,

Volltreffer ;)

Ja, das war einer der beiden Zahlendreher.

Nun, was dramatisch klingt ist letztendlich nicht sooo schlimm.... man bemerkt recht schnell, dass das Programm nicht funktioniert und auch die Ursache ich recht schnell gefunden.

Ich muss sagen... bisher gute Arbeit, wird Zeit, dass die Schaltungen endlich fertig werden... wer macht die gleich wieder?... Oh... :mrgreen:

Ferenc
Metropolitan (MET)
Beiträge: 3255
Registriert: Mo 26. Sep 2005, 23:35
Nenngröße: H0
Stromart: AC
Steuerung: Ecos 1 + Tams MC + TC Gold
Gleise: Märklin M-K-C Gleis
Wohnort: HDH
Alter: 56

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#25

Beitrag von Ferenc »

Hi,
wurde hier glaube ich erklärt:

Alle Pins von Port B sollen als Ausgang dienen -> es müssen alle Bits in DDRB gesetzt werden.
Code:
ldi r16, 0xFF ;alle Bits setzen
out ddrb, r16 ;Inhalt von r16 (alle gesetzten Bits) ins Steuerregister DDRB schreiben

liege ich hier richtig ? Im Beispiel oben halt mit dem Port B statt Port D.

Ferenc
Immer eine Handbreit Schotter unter der Schwelle ;-)
Antworten

Zurück zu „Workshops“