Workshop: Programmierung in Assembler (Atmel ATM8)

Workshops von Usern dieses Forums angeboten.

kschwi
InterRegio (IR)
Beiträge: 108
Registriert: Mo 19. Feb 2007, 10:01
Wohnort: Seelze

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#51

Beitrag von kschwi »

Muenchner Kindl hat geschrieben: Unten muss ISP Mode ausgewählt sein, die Frequenz stellst Du mit Hilfe des Buttons "Settings" ein.
Kleiner Hinweis auf einen beliebtes Problem: Die Programmierfrequenz muss immer niedriger sein als die Oszillatorfrequenz und auch davon nur ein Bruchteil. Wer also Probleme beim Programmieren hat, sollte hier mal schauen und ggf. die Frequenz auf den minimalen Wert setzen. Probleme beim Programmieren erkennt man zum einen daran, dass eigentlich lauffähige SW "nicht geht" -> ist immer irgendwie blöd oder und das ist der bessere Weg: Thomas hat in der Hardcopy der Main-Lasche des Programmers
Bild
den Haken bei "Verify Device after programming" gesetzt (oberster Block). Das erkennt schon ein Programmierproblem durch vergleich des Controller-Speichers mit dem Programm auf der Platte.
Ciao
Knut

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

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#52

Beitrag von Muenchner Kindl »

Hallo Knut,
Die Programmierfrequenz muss immer niedriger sein als die Oszillatorfrequenz
Vielen Dank für den Hinweis, darauf habe ich nie geachtet (weil es immer funktioniert hat ;-) ). Die Oszillatorfrequenz seht Ihr übrigens bei den "Fuses", da sollten "intern 8MhZ" stehen. Einen Screenshot habe ich im Moment leider nicht zur Hand.

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

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#53

Beitrag von Muenchner Kindl »

Byteoperationen, Logik und Arithmetik

Bevor wir diesen Themenkomplex bearbeiten möchte ich ein weiteres Register ins Spiel bringen, nämlich das Statusregister SREG.
Während wir uns bisher diverse Steuerregister (z.B. DDRx) immer als Byte angesehen haben, so müssen wir das SREG (und noch viele andere) bitweise betrachten. So besitzt also jedes der 8 Bit des Statusregisters eine eigene Funktion und wird von verschiedenen Befehlen beeinflusst, von anderen herangezogen.
Stellen wir uns das SREG zur Vereinfachung als "Einmerker" vor, quasi als Knoten im Taschentuch. Kommt bei einer Berechnung etwas bestimmtes heraus machen wir uns einen Knoten an einer bestimmten Stelle. Beim 8085 und vermutlich anderen hiess das SREG übrigens Flag-Register, die einzelnen Bits hiessen Flags (Flaggen... Fähnchen).

Damit das Ganze ein wenig klarer wird betrachten wir mal ein Bit dieses Statusregisters, nämlich das Bit1, namentlich das "Zero-Flag".
Ergibt z.B. eine Rechenoperation den Wert 0x00, so wird das Z-Flag gesetzt. Dies wiederum kann von anderen Befehlen interpretiert und ausgewertet werden, dazu ein Beispiel im Vorgriff:

Code: Alles auswählen

sub r16, r17 ; Inhalt von r17 von r16 abziehen und Ergebnis nach r16 schreiben
breq Ende ; Springe zum Label Ende wenn das Zeroflag gesetzt ist
Dieses Beispiel beginnt mit einer Subrtraktion zweier Register. Steht in beiden das gleiche drin ist das Ergebnis 0x00 und das Zeroflag wird gesetzt. Der unmittelbar danach folgende Sprungbefehl (das Thema kommt noch) frägt das Z-Flag ab und springt zum angegebenen Label, wenn Z gesetzt ist.
Eine Beschreibung des SREG und deren Bits findet Ihr im Datenblatt auf Seite 11. Wenn Ihr Euch in der Befehlsübersicht z.B. die Seite 152 anschaut, da geht es um den Befehl SUB aus meinem Beispiel. Bei jedem Befehl steht dabei, welche Bits des SREG dieser Befehl beeinflusst.

Mit dem Wissen um das Statusregister kommen wir nun zur Arithmetik und damit zu ein paar Befehlen, die wir schon gesehen haben, ADD und Sub.
Wahnsinnig viel gibt es dazu an und für sich nicht zu sagen, mit ADD werden die Inhalte zweier Register addiert, mit SUB eben subrtahiert. Wichtig ist, dass das Zielregister zuerst genannt werden muss, "add r16, r17" könnte man auch so schreiben: r16=r16+r17. Gleiches gilt auch beim Befehl SUB "sub r16, r17" entspricht r16=r16-r17.
Interessant ist, dass es für die Subtraktion auch einen Befehl mit Konstante gibt, für das Addieren leider nicht. So wird mit "subi r16, 0x05" der Wert 0x05 vom Inhalt des Registers r16 abgezogen. Zum Addieren braucht man leider zwei Register.
Erwähnenswert und evtl. auch wichtig sind die Flags, die durch Add, Sub und Subi beeinflusst werden, ganz wichtig hier die Flags "Zero", "Carry" und "Sign". Nachdem wir das Z-Flag bereits behandelt haben, schauen wir kurz auf das C-Flag. Dieses wird gesetzt, wenn ein Übertrag stattfindet, es handelt sich im Prinzip um ein neuntes Bit für den eben ermittelten Registerinhalt. So würde z.B. nach einer Addition von 0xFF und 0x01 ein Übertrag stattfinden, das C-Flag würde gesetzt. Im Übrigen würde, auch wenn im Register damit 0x00 stünde, das Z-Flag geöscht werden, das Ergebnis ist ja nicht 0 ;) .
Das Sign-Flag zeigt das Vorzeichen an. Ist das Ergebnis einer Subtraktion negativ wird das S-Flag gesetzt.

Wenn wir schon so schön beim Rechnen sind sollten wir nun zu zwei einfacheren und weit öfter benutzten Befehlen kommen. Auch wenn man es vermutlich nicht glauben mag, add und sub werden wir vermutlich in diesem Workshop eher selten bis gar nicht mehr benötigen.
Will man ein Register einfach nur um 1 hochzählen oder um 1 zurückzählen kann man sich die Verwendung weiterer Register und Konstanten sparen. Zum Inkrementieren und Dekrementieren gibt es eigene und vor allem übersichtliche Befehle:
INC R
DEC R
Während z.B. "inc r16" beim Aufruf zum Inhalt von r16 1 dazuzählt zieht "dec r16" den Wert 1 ab. INC und DEC finden wir in fast allen Zähl- und Zeitschleifen und die werden wir noch sehr oft zu sehen bekommen. Als Beispiel dafür eine einfaches Endloszählwerk:

Code: Alles auswählen

ldi r16, 0x00 ; Register 16 löschen
Zählwerk:
inc r16 ; r16 inkrementieren
out Portb, r16 ; r16 ausgeben
rjmp Zählwerk ; Springe zum Beginn des Zählwerks
Hier wird also r16 mit 0 vorgeladen und bei jedem Schleifendurchlauf eines dazuaddiert, das Ganze endlos. Falls Ihr das in Euere Boards programmiert werdet Ihr nicht viel sehen, ausser 8 leuchtenden LEDs. Das Zählen geht nämlich zu schnell für das menschliche Auge ;)

Wenn ich jetzt behaupte, dass das Thema Arithmetik damit abeschlossen ist, dann würde ich lügen. Wir haben das eine oder andere Flag vernachlässigt und es gibt auch noch weitere Befehle, die wir gar nicht angesprochen haben. Aus zumindest meiner persönlichen Praxis aber reicht das bisher besprochene. Ich selbst habe mir übrigens vor einiger Zeit die Befehlsliste ausgedruckt und einfach mal so erforscht, was es so alles gibt. Damit habe ich den einen oder anderen durchaus interessanten Befehl gefunden, der z.B. auch im Tutorial nicht angesprochen ist. Also, auch wenn es zur Arithmetik sicher noch Seiten zu schreiben gibt, wir wechseln damit zu einem ähnlichen Feld, der Logik :lego:

Vorweg möchte ich noch die drei elementaren logischen Verknüpfungen ansprechen, da wären also UND, ODER und EXclusivODER.
Wir stellen uns mal jeweils einen Baustein mit zwei Ein- und einem Ausgang vor und nennen den erstmal UND-Gatter:
Damit der Ausgang "1" wird müssen Eingang 1 UND Eingang 2 "1" sein. In einer Logiktabelle sieht das dann so aus:
E E A
0 0 0
0 1 0
1 0 0
1 1 1

Beim ODER-Gatter reicht es, wenn einer der Eingänge "1" ist, es können aber auch beide "1" sein, damit der Ausgang "1" wird.
E E A
0 0 0
0 1 1
1 0 1
1 1 1

Das EXclusiv-ODER verlangt für eine "1" am Ausgang dafür, dass nur einer der beiden Eingänge "1" ist:
E E A
0 0 0
0 1 1
1 0 1
1 1 0

Hier haben wir jeweils 2 Bit miteinander verknüpft, man kann das natürlich auch mit Bytes machen. Als Beispiel mal eine UND-Verknüpfung zweier Bytes:
0b00001111
0b00111100
=
0b00001100

Beide Bytes mit ODER verknüpft:
0b00001111
0b00111100
=
0b00111111

Oder mit XOR:
0b00001111
0b00111100
=
0b00110011

Evtl. stellt sich jetzt die Frage, wozu man das braucht und wenn ich behaupte, dass man das fast ständig braucht wird der eine oder andere vielleicht ungläubig schauen :|
In erster Linie werden wir die logischen Verknüpfungen zum Maskieren von Registern benötigen. Klingt komisch, ist aber so und damit es verständlicher wird hier ein Praxisbeispiel:
Wir lesen den kompletten PortD ein und wissen, dass wir nur an PD2 und PD3 ein Taster angeschlossen ist. Zwar haben wir unsere PullUps gesetzt, dennoch könnte sich ja ein Signal durch einen der unbelegten Pins durchschmuggeln und unser Ergebnis verfälschen. Ausserdem will ich, dass alle LEDs, ausser die den Tastern zugewiesenen, dauerhaft aus sind...
Dazu muss ich die Bits 2 und 3 maskieren, sinnigerweise mit UND:
0bxxxxxxxx
0b00001100
=
0b0000xx00
Ihr seht, dass mit der Maske 0b00001100 alle Bits im Register gelöscht werden, ausser Bit 2 und 3, welche ihren Zustand behalten.
Will ich alle nicht benötigten Bits setzen, dann muss ich eine negative Maske mit ODER über das Byte legen:
0bxxxxxxxx
0b11110011
=
0b1111xx11

Was wir hier in der Therorie sehr ausführlich durchgekaut haben schaut in der Praxis des Assemblers wieder recht einfach aus. Wir können entweder zwei Register miteinander logisch verknüpfen oder wir maskieren ein Register mit einer Konstante:
AND r16, r17
legt r17 als UND-Maske über das Register r16, maskiert also den Inhalt von r16 mit dem Inhalt von r17

OR r16, r17
macht das gleiche, nur halt mit ODER

EOR r16, r17
ermöglicht eine Abfrage, welche Bits bei den beiden Registern unterschiedlich sind.
Meistens geht es beim Maskieren aber um eine feste, vielleicht auch nur für den Programmteil feste Bitfolge, die darübergelegt werden soll. Damit wir hier keine Register verschwenden müssen gibt es die Kommandos auch mit Konstante:

ANDI r16, 0b00001111
Die Bits 4-7 in Register 16 werden gelöscht, Bit0-3 bleiben unberührt

ORI r16, 0x02
Bit 0 und 1 werden gesetzt, der Rest bleibt unverändert.

Erinnert Ihr Euch an die letzte Aufgabe? Eine Lösung könnte sein, das Register, welches zum Einlesen verwendet wurde, zu maskieren:
Hauptprogramm:
in r16, pind ; Eingaberegister C nach r16

andi r16, 0b00001100 ; Bit 2 und 3 maskieren

out portb, r16 ; Ausgabe von r16 nach Port B
rjmp Hauptprogramm ; Endlosschleife
Auch wenn die Aufgabe noch kommt, Ihr könnt gerne mit dieser Maske experimentieren. Macht ein OR daraus, spielt Euch gerne mit EOR.

Kommen wir nun zum dritten Begriff der Überschrift, den Byteoperationen. Eigentlich ist der Begriff Käse, letztendlich handelt der ganze Beitrag von "Byteoperationen" aber für die folgende fällt mir kein besserer Überbegriff ein:
Es kommt mitunter vor, dass es notwendig ist, dass wir Bits innerhalb eines Registers verschieben müssen. Das Thema S88 wurde ja schonmal angesprochen, das könnte so funktionieren: Ich lese die Zustände von 8 Eingangspins ein und schiebe die Inhalte zur Seite aus dem Register. Beim ersten "Schubs" bekomme ich den Zustand von Eingang 1, beim nächsten den von Eingang 2 usw. "Schieberegister" ist hier der passende Begriff und wie wir hier herumschieben wollen wir uns mal in der Praxis ansehen:
Aus welchem Grund auch immer hängen unsere Taster an PD2 und PD3. Wenn ich mit diesen beiden Tastern jedoch die Zahlen 0x00-0x03 darstellen will muss ich die eingelesenen Bits um zwei Stellen nach rechts schieben.
Dies funktioniert mit dem Befehl "ROR", also Rotate Right oder in die andere Richtung "ROL" Rotate Left, gefolgt vom Register, welches rotiert werden soll. Pro ROR (oder ROL) Kommando werden die Bits um eine Stelle verschoben.
Angenommen, unser Register r16 hat folgenden Inhalt:
0b00001100
jetzt kommt der Befehl ror r16
0b00000110
dann kommt noch einmal ror r16
0b00000011

Wir können oben genanntes Beispiel wie folgt ergänzen:
Hauptprogramm:
in r16, pind ; Eingaberegister C nach r16

andi r16, 0b00001100 ; Bit 2 und 3 maskieren

ror r16 ; Registerinhalt um eine Stelle nach rechts schieben
ror r16 ; Registerinhalt um eine Stelle nach rechts schieben


out portb, r16 ; Ausgabe von r16 nach Port B
rjmp Hauptprogramm ; Endlosschleife
Wenn wir nun in die heilige Fibel der Befehle auf Seite 119 schauen stellen wir fest, dass ich etwas verschwiegen habe. Der Befehl lautet "Rotate Right through Carry" oder eben "Rotate Left through Carry". Es wird also das Carryflag im SREG mit eingebunden, und was da passier möchte ich mit einem Beispiel belegen. Wir nehmen wieder ein blebiebiges Register, das etwas abgesetzte neunte Bit in jeder Zeile ist das Carryflag. Ich verzichte der Übersicht wegen auf die korrekte Schreibweise, bitte verfolgt das gesetzte Bit, sowie das C-Flag nach jedem Rotate-Befehl:

00000100 x
ror
00000010 0
ror
00000001 0
ror
00000000 1
ror
10000000 0
ror
01000000 0

Der Registerinhalt wird also, durch das C-Flag, im Kreis geschoben, mit ROL dreht sich das natürlich in die andere Richtung.

Kommen wir nun zur Aufgabe für die nächsten 2 oder auch 3 Wochen:
Wir lesen unsere beiden Taster lowaktiv an PD2 und PD3 ein.
Ich möchte ein Programm, dass diese beiden Taster mit einem EXOR logisch auswertet und das Ergebnis an PB0 (also die erste LED) ausgibt.
LED1 darf also nur leuchten wenn einer der beiden Taster gedrückt ist. Sind beide gedrückt oder beide nicht gedrückt bleibt die LED dunkel.
Bitte überlegt Euch genau die Schritte, die zu machen sind. Wir werden mit einer Kopie des Registers arbeiten müssen, dies geht mit dem Befehl MOV (z.B. mov r17, r16).
Natürlich stehe ich gerne für Fragen zur Verfügung, diese am besten hier im Forum. Die Ergebnisse dann aber bitte als Mail, hier bitte entweder den Text oder das ASM-File.

Viel Spass,

Thomas

PS: Da es heute zeitlich gut geklappt hat habe ich das, was ich am Wochenende machen wollte, um zwei Tage vorgeschoben. Das bereitet mir ein freues Wochenende 8) und Euch mehr Zeit ;)

Ferenc
Metropolitan (MET)
Beiträge: 3257
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)

#54

Beitrag von Ferenc »

Hallo,
so nun ist erst mal alles weg :bigeek: es leuchten keine LED mehr ausser der grünen.
Wollte nach der Anleitung weiter oben das neue Projekt laden und bin bestimmt auf einen falschen Knopf im Programm gekommen.
Werde mal nacher sehen ob ich noch weiter mache oder es lieber bleiben lasse.

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)

#55

Beitrag von Muenchner Kindl »

Ferenc hat geschrieben:Hallo,
so nun ist erst mal alles weg :bigeek: es leuchten keine LED mehr ausser der grünen.
Wollte nach der Anleitung weiter oben das neue Projekt laden und bin bestimmt auf einen falschen Knopf im Programm gekommen.
Werde mal nacher sehen ob ich noch weiter mache oder es lieber bleiben lasse.

Ferenc
Hallo Ferenc,

ich weis natürlich nicht, welches Programm Du geladen hast, aber wenn Du Dir sicher sein willst, dass Dein Controller erreichbar ist, einfach diese Maske öffnen:
Bild
und auf "Read Signature" klicken. Wenn unten Meldungen mit OK quittiert werden und eine Signatur gelesen wird funktioniert die Verbindung.
Vielleicht hast Du einfach nur einen Fehler im Programm, Du kannst mir das vermutlich fehlerhafte Programm gerne senden oder hier veröffentlichen.

Ferenc
Metropolitan (MET)
Beiträge: 3257
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)

#56

Beitrag von Ferenc »

Hi,
bei mir geht es hier schon los mit dem Ärger: Bild
Und hier die fehlermeldung unten im Bild:
FATAL ERROR: Cannot open output file C:\Users\Gõhsmetzger\Documents\DemoFehler.hex: No such file or directory


Ferenc
Zuletzt geändert von Ferenc am Sa 1. Okt 2011, 19:27, insgesamt 1-mal geändert.
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)

#57

Beitrag von Muenchner Kindl »

Hi,

kann es sein, dass das Zielverzeichnis schreibgeschützt ist? Ansonsten bitte mal ein neues Projekt in einem anderen Verzeichnis anlegen.

Ferenc
Metropolitan (MET)
Beiträge: 3257
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)

#58

Beitrag von Ferenc »

Hi Thomas,
war der Schreibschutz.
Habe nun das Programm geladen und die LED 1,3 und 4 leuchten nun nicht. Beim drücken der Tasten geschieht nichts. Das ist ja nun der Fehler den wir finden sollen, oder ?
Wie kann man falsch erstellte Projekte unter AVR Studio löschen ?

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)

#59

Beitrag von Muenchner Kindl »

Hi,
Wie kann man falsch erstellte Projekte unter AVR Studio löschen ?
Einfach das Verzeichnis löschen ;)
Habe nun das Programm geladen und die LED 1,3 und 4 leuchten nun nicht. Beim drücken der Tasten geschieht nichts. Das ist ja nun der Fehler den wir finden sollen, oder ?
Die anderen LEDs leuchten? Ja, das ist der Fehler... es sollten ja alle leuchten und 3 uind 4 je nach Taste ausgehen. Morgen kommt ein Tipp ;)

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

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#60

Beitrag von Muenchner Kindl »

Hallo an alle,

Vielleicht noch ein paar Tipps zur Verwaltung von Projekten:

Wenn ich an einem Auftrag arbeite, dann lege ich für jeden Version ein neues Projekt an. Im Projektnamen kommt der Name, gefolgt von einer Versionsnummer vor. Damit habt Ihr immer die Möglichkeit, wenn Ihr mal eine Version völlig verhaut habt, auf die Vorversion zurückzugehen.

Wenn Ihr eine Version habt, legt ein neues Projekt an, öffnet die ASM-Datei der Vorversion mit einem Editor und kopiert Euch den kompletten Inhalt mit Copy & Paste in das neue Projekt.

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

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#61

Beitrag von Muenchner Kindl »

Ferenc hat geschrieben:Hallo,
habe die Funktion hinbekommen und eine Mail an dich geschrieben.

Ferenc
Guten Morgen,

und diese ist jetzt auch korrekt...

Wie versprochen kommt jetzt ein kleiner Tipp: Ich habe bei meinem Programm eine ganze Zeile vergessen. Es trifft die Initialisierung und wenn man die Kommentare liest sollte man erkennen, wo es hakt ;)

Hier nochmal das fehlerhafte Programm:

Code: Alles auswählen

.include "m8def.inc"      ; Damit weis der Compiler, auf welchen Proz er compilieren muss


.org 0x0000
        rjmp    Init                     ; Springe nach einem Reset zum Label "Init"


Init:                           ; Hier beginnt die Initialisierung


      ; Setzen des Stackpointers
        ldi     r16, LOW(RAMEND)        ; unteres Byte der hächstmöglichen Adresse holen
        out     SPL, r16            ; Unteres Byte des SP beschreiben
        ldi     r16, HIGH(RAMEND)      ; oberes Byte der höchstmnöglichen Adresse holen
        out     SPH, r16            ; oberes Byte des SP beschreiben

      ; Initialisieren der Eingänge
      ldi r16, 0x00               ; alle Bits in r16 auf 0 setzen
      out ddrd, r16               ; Alle Pins von Port D sind Eingang


      out portd, r16               ; PullUp-Widerstände für PortD schalten

      ; Initialisieren des Ausgangs
      ldi r16, 0xFF
      out ddrb, r16               ; Alle Pins von Port B sind Ausgang
      
Hauptprogramm:
      in r16, pind               ; Eingaberegister C nach r16
      out portb, r16               ; Ausgabe von r16 nach Port B
      rjmp Hauptprogramm            ; Endlosschleife
BTW: Ich werde heute den Thread ein wenig aufräumen. Bearbeitete Hinweise zu Schreibfehlern, erledigte Rückfragen, Meldungen zum Status der Versendungen ect. werde ich entfernen.

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

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#62

Beitrag von Muenchner Kindl »

Hallo zusammen,

zur letzten grossen Aufgabe vielleicht noch ein paar Hinweise:

Unsere Probleme sind
- dass sich die beiden zu verknüpfenden Bits in ein und dem selben Register befinden
- dass sich die beiden zu verknüpfenden Bits an der falschen Stelle befinden

Wie so oft gibt es mehrere Lösungswege, einen davon habe ich bereits realisiert. Gefordert ist bei dieser Aufgabe das Kopieren von Registern (mov), das Rotieren, das Maskieren, sowie letztendlich das logische Verknüpfen zweier Register. Selbstverständlich auch das Ein/Ausgeben samt Initialisierung ;)

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

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#63

Beitrag von Muenchner Kindl »

Hi,

die ersten Lösungen sind da und funktionieren ;-) . Bei der letzten Aufgabe könnt Ihr gerne bis zum nächsten Thema mit "Und" und "Oder" experimentieren. Vermutlich werden Euch die Ergebnisse befremdlich vorkommen... denkt bitte daran, dass Ihr es mit Low-Aktiven Eingängen zu tun habt (Taste gedrückt = 0).

Eine Woche habt Ihr noch... dann kommt der nächste Klopfer :twisted:

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

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#64

Beitrag von Muenchner Kindl »

Guten Morgen,

da ich bisher nur eine Lösung und ein weiteres Lebenszeichen bekommen habe frage ich in die Runde, ob wir mit der Auflösung der letzten Aufgabe, sowie mit dem nächsten angeschriebenen Thema, noch eine weitere Woche warten wollen?

Ich hätte da schon gerne ein paar Wortmeldungen, denn auf der einen Seite wäre es schade, wenn Kollegen einerseits aus beruflichen Gründen nicht mehr aufschliessen können, die anderen andererseits aufgrund von Langeweile die Motivation verlieren.

Selbstverständlich können zwischendrin auch Fragen bearbeitet, beantwortet oder diskutiert werden und wenn es zu schnell, zu umfangreich oder auch zu fade ist, auch hier bitte melden.

Ferenc
Metropolitan (MET)
Beiträge: 3257
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)

#65

Beitrag von Ferenc »

Hi Thomas,
ich für meinen Teil hänge etwas Zeitlich hinter her und auch vom "ja ich habe es kappiert" bin ich noch etwas entfernt.
Letztes Wochende hatte ich keine Möglichkeit mich intensiv um einige Themen zu kümmern.
Dieses Wochende sieht es besser aus. Also bitte noch warten.

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

samson
InterRegioExpress (IRE)
Beiträge: 373
Registriert: Di 12. Mai 2009, 13:53
Nenngröße: H0
Stromart: AC
Steuerung: CS2 CS1r CdB
Gleise: Vitrine ;-)
Wohnort: Rheinhessen

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#66

Beitrag von samson »

Hallo,
Ferenc hat geschrieben:Hi Thomas,
ich für meinen Teil hänge etwas Zeitlich hinter her und auch vom "ja ich habe es kappiert" bin ich noch etwas entfernt.
Letztes Wochende hatte ich keine Möglichkeit mich intensiv um einige Themen zu kümmern.
Dieses Wochende sieht es besser aus. Also bitte noch warten.

Ferenc
genau meine Argumente :lol: :lol:

Tut mir leid, nach einer Woche Urlaub ist mein Gehirn noch auf Freizeit eingestellt und ich muss mich erst wieder reindenken.

Gerade stecke ich die Hardware zusammen und probiere die ersten Schritte noch mal aus. Aber so langsam wird es wieder.
Gruß
Christoph

CS3+ 1.2.0 // CS2 4.1.0 // CS1 Aufgeladen 4.1.0 // MS2 // MS1 // 6021 // CAN-digital-Bahn by TM // Vitrinenbahner // Ausprobierer

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

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#67

Beitrag von Muenchner Kindl »

Hi Leute,

ist überhaupt klein Problem, wir können gerne noch eine Woche "pausieren". Wer so weit ist kann jetzt mit dem erlenten ein wenig experimentieren und die anderen können sich gerne eine Woche länger Zeit lassen, deswegen war es mir ja so wichtig, auf den aktuellen Stand zu kommen, also das letzte Thema durchzuziehen.

Mir sind drei Dinge ganz wichtig:
- Ich will keinen Zeitdruck
- Es soll keiner "zurückbleiben"
- Es soll sich keiner "langweilen"
vom "ja ich habe es kappiert" bin ich noch etwas entfernt.
Wenn es irgendwo Klärungsbedarf gibt, dann her damit. Öffentlich für alle, meinetwegen auch per Mail, wobei ersteres wohl eher was bringt. Wenn etwas unverständlich geschrieben ist, haut die Bremse rein oder fragt... egal wie doof die Frage klingen mag. Idealst wäre natürlich, wenn auftretende Fragen von den Kollegen beantwortet werden, die schon so weit sind...

Also, ich schlage folgendes vor:
Zum Wochenende werde ich die kleine Fehlersuchaufgabe auflösen. Der eingebaute Fehler ist schnell erklärt. Ansonsten lasse ich Euch noch eine Woche in Ruhe.
Wer Fragen hat, das wird behandelt. Wenn jemand, basierend auf den bisherigen Themen experimentiert und dazu Fragen hat... her damit.
Denkt bitte daran, besondes wichtig sind mir die Logikfunktionen und das Statusregister... die werden uns verfolgen!
Benutzeravatar

DeMorpheus
Metropolitan (MET)
Beiträge: 3633
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)

#68

Beitrag von DeMorpheus »

Hallo,

nachdem ich mir jetzt aus gutem Grund ( :D ) nochmal angeschaut habe, wozu genau PullUp- bzw. PullDown-Widerstände gut sind, wollte ich mal mit einem highaktiven Eingang eine LED zum leuchten bringen.
Klappt nicht, hätte ich mir ja denken können. Nicht umsonst haben wir bisher LED immer nur ausgemacht ...
Können wir denn mit dem, was wir bisher haben, überhaupt schon eine LED einschalten?

Code: Alles auswählen

.include "m8def.inc"      			; Damit weiss der Compiler, auf welchen Proz er kompilieren muss


.org 0x0000
        rjmp    Init               	; Springe nach einem Reset zum Label "Init"


Init:                          	 	; Hier beginnt die Initialisierung


      ; Setzen des Stackpointers
        ldi     r16, LOW(RAMEND)    ; unteres Byte der hächstmöglichen Adresse holen
        out     SPL, r16            ; Unteres Byte des SP beschreiben
        ldi     r16, HIGH(RAMEND)   ; oberes Byte der höchstmnöglichen Adresse holen
        out     SPH, r16            ; oberes Byte des SP beschreiben

	  ; Initialisieren der Eingänge
	  	ldi r16, 0x00				; alle Bits in r16 auf 0 setzen
		out ddrd, r16				; alle Pins an Port D sind Eingang

		ldi r16, 0b00000100			; diese Bits in r16 schreiben
		out portd, r16				; PullDown-Widerstände für Port D, Pin 3 mit PullUp-Widerstand

	  ; Initialisieren der Ausgänge
	  	ldi r16, 0xFF				; alle Bits in r16 setzen
	    out ddrb, r16				; alle Pins an Port B sind Ausgänge

Hauptprogramm:

	  ; Einlesen der Eingänge
	    in r18, pind				; Eingaberegister D nach r18 einlesen
		andi r18, 0b00001100		; Filtern des Eingangs

	  ; Verschieben der eingelesenen Bits
	  	ror r18						; Bits in r18 nach rechts verschieben
		ror r18						; Bits in r18 nach rechts verschieben

	  ; Ausgabe an Port B
	   	
		out portb, r18				; Ausgabe des Ergebnisses an Port B

	  ; Endlosschleife

	  	rjmp Hauptprogramm
[/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)

#69

Beitrag von Muenchner Kindl »

Hi Moritz,

Können wir denn mit dem, was wir bisher haben, überhaupt schon eine LED einschalten?

Ja, können wir. Mit dem low-aktiven T1 schalten wir ja LED3 aus. Wenn wir den eingelesenen Taster aber mit Exclusiv-Oder verknüpfen könnten wir die LED auch einschalten.

Wir lesen also ein und maskieren das eingelesene Byte mit EOR und einem Register mit folgendem Inhalt: 0b00001100 (wichtig ist, dass die Tasten jeweils mit 1 maskiert sind). Im nicht betätigten Zustand ergibt das als Ergebnis 0b00000000. Wird eine Taste gedrückt, dann ist das entsprechende Bit 0, was logisch mit EOR ausgewertet 1 ergibt... die jeweilige LED würde leuchten.

Auch hier... experimentieren erwünscht ;-)

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

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#70

Beitrag von Muenchner Kindl »

Hi,

nicht erschrecken, kein neues Thema, aber die Auflösung unserer ersten kleine Aufgabe...

Gesucht war ein Fehler im Code und eigentlich alle, die mir ihre Lösung geschickt haben lagen richtig!

ldi r16, 0x00 ; alle Bits in r16 auf 0 setzen
out ddrd, r16 ; Alle Pins von Port D sind Eingang

ldi r16, 0xFF ; Bits setzen
out portd, r16 ; PullUp-Widerstände für PortD schalten

Es hat die Zeile gefehlt, in der die Pull-Up-Widerstänge für den Eingangsport PD gesetzt werden.

Es erreichte mich auch eine zusätzliche Frage, die ich gerne weitergeben und öffentlich beantworten möchte:
Eine Frage dem Setzen der Bits auf 1. Wenn ich nur Ausgang2 und 3 setzen wollte, dachte ich an

ldi r16, 0x00001100
Das ist korrekt, das alleinige Setzen von Bit 2 und Bit 3 würde unser Programm ebenfalls funktionieren lassen, sehr gut erkannt. Dass ich da alle Bits setze ist reine Bequemlichkeit.

Also, weitere Fragen, auch zur zweiten Aufgabe, werden gerne beantwortet, eine Woche haben wir noch ;)

Weiter so!

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

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#71

Beitrag von Muenchner Kindl »

Hallo Ihr lieben Leute,

die drei Wochen sind vorbei und ich präsentiere Euch eine der Lösungen zur letzten Aufgabe:
.include "m8def.inc" ; Damit weis der Compiler, auf welchen Proz er compilieren muss


.org 0x0000
rjmp Init ; Springe nach einem Reset zum Label "Init"


Init: ; Hier beginnt die Initialisierung


; Setzen des Stackpointers
ldi r16, LOW(RAMEND) ; unteres Byte der hächstmöglichen Adresse holen
out SPL, r16 ; Unteres Byte des SP beschreiben
ldi r16, HIGH(RAMEND) ; oberes Byte der höchstmnöglichen Adresse holen
out SPH, r16 ; oberes Byte des SP beschreiben

; Initialisieren der Eingänge
ldi r16, 0x00 ; alle Bits in r16 auf 0 setzen
out ddrd, r16 ; alle Pins an Port D sind Eingang

ldi r16, 0xFF ; alle Bits in r16 setzen
out portd, r16 ; PullDown-Widerstände für Port D

; Initialisieren der Ausgänge
out ddrb, r16 ; alle Pins an Port B sind Ausgänge


Hauptprogramm:

; Einlesen der Eingänge
in r16, pind ; Eingaberegister D nach r16 einlesen


; Verschieben der eingelesenen Bits
ror r16 ; Bits in r16 nach rechts verschieben
ror r16 ; Bits in r16 nach rechts verschieben

; Kopieren von r16

mov r17, r16 ; r16 nach r17 kopieren

; Verschieben der Bits in r17

ror r17 ; Bits in r17 nach rechts verschieben

; Filtern der Register

andi r16, 0b00000001 ; alle Bits außer Bit 0 löschen
andi r17, 0b00000001 ; alle Bits außer Bit 0 löschen

; Vergleich von r16, r17

eor r16, r17 ; XOR-Abfrage der Register

; Ausgabe an Port B

out portb, r16 ; Ausgabe des Ergebnisses an Port B

; Endlosschleife

rjmp Hauptprogramm
Ich habe die Lösung zwar nicht getestet, das dürft Ihr gerne machen, sie stimmt aber weitestgehend mit meiner Lösung überein.

Im Prinzip haben wir ja schon darüber gesprochen aber hier nochmals zusammenfassend die Vorgehensweise:
1. Wir lesen den Port D komplett ein, darin enthalten sind ja die Bits 2 und 3, die uns ja interessieren
2. Wir schieben das Ganze zweimal nach rechts. Damit steht Bit2 nun an Bit0 und Bit3 an Bit1
3. Nun kopieren wir das Ganze in ein weiteres Register
4. Wir schieben das weitere Register um eine Stelle nach rechts, damit steht das ursprüngliche Bit3 nun auch auf Bit0
5. Wir maskieren beide Register so, dass nur noch Bit0 übrig bleibt
6. Beide Register werden logisch verknüpft, danach steht das Ergebnis auf Bit0 des Zielregisters
7. Das Zielregister wird ausgegeben
8. Das Ganze ab Punkt 1 wiederholen

Wir haben in dieser kleinen Aufgabe Grundlagen Arithmetik praktisch angewendet und Register verschoben. Das Maskieren wird uns noch sehr oft begegnen, genauso wie die I/O-Geschichten.

Wenn es zu diesem Thema noch Fragen gibt, wenn jemand meint, etwas nicht verstanden zu haben oder andere Schwierigkeiten hat, bitte melden! Es muss keiner befürchten, ausgelacht zu werden und wenn, dann beantworte ich Fragen auch per Mail. Es ist allerdings sehr wichtig, dass wir bis hier auf dem selben Stand sind, morgen oder Sonntag geht es mit einem neuen Thema weiter. Schaut Euch bitte auch das Thema Statusregister an, auch damit werden wir noch unsere Freude haben.

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

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#72

Beitrag von Muenchner Kindl »

Bedingte und unbedingte Sprünge


Bisher haben wir ja bei jedem Thema eine Grundlage vorweg behandelt, meist ging es um irgendein besonderes Register und dessen Bedeutung. Das für das heutige Thema wichtige Register, das Statusregister, hatten wir ja bereits, deshalb gibt es heute nur was Kleines vorweg ;)
Ich habe ja schonmal angedeutet, dass wir unsere Arbeitsregister irgendwann mal mit Namen ansprechen wollen, also lasst uns das vorweg behandeln:
Am Anfang eines Programmes besteht die Möglichkeit, Registern Namen zuzuordnen. Dies ist übrigens ein Zuckerl des Compilers und hat eigentlich mit dem Prozessor nichts zu tun... was uns aber nicht weiter stören soll.
Die Benamsung sieht folgendermassen aus:

Code: Alles auswählen

.def temp  = r16
Das Register r16 bekommt also den Namen "temp" und kann künftig auch damit angesprochen werden. Anstatt "ldi r16, 0xFF" können wir also künftig auch "ldi temp, 0xFF" schreiben, was uns die Arbeit schon immens vereinfacht.
Die Benamsung kommt gleich nach der Include-Anweisung, es können alle Arbeitsregister einen individuellen Namen bekommen.

Code: Alles auswählen

.include "m8def.inc"
 
.def Temp  = r16						;Temporäre Variable
.def Zaehlerstand = r17					;Zählerstand
Umlaute würde ich übrigens vermeiden, ich glaube aber, das hatten wir schon bei den Labels. Apropos: Es darf keine Namensgleichheiten bei Labels und Registern geben, betreffend der drei Zeilen darf es also kein Einsprunglabel mit dem Namen "Temp" geben!

Damit kommen wir zum eigentlichen Thema dieses Beitrags, den unbedingten und bedingten Sprüngen.

Der unbedingte Sprung ist dabei schnell abgehandelt, den hatten wir auch schon. Es gibt viele Situationen, in denen, ohne eine Bedingung zu erfüllen, ein Sprung ausgeführt werden muss, wir hatten das bereits beim Terminieren eines Programmes. Der Befehl lautet rjmp, gefolgt vom Einsprunglabel. Kommt das Programm irgendwann zu einem rjmp-Befehl wird die Adresse, welche durch das Label definiert ist, in den Programmzähler geladen und das Programm an dieser Stelle weitergeführt. Viel mehr ist zum rjmp nicht zu schreiben, der Vollständigkeit halber noch die Syntax:

Code: Alles auswählen

rjmp Irgendwo    ; Springe zum Label "Irgendwo"
Interessanter und umfangreicher sind die bedingten Sprünge. Gesprungen wird also, wenn eine bestimmte Bedingung erfüllt ist. In der Regel geht es um Register und deren Werte, die wir als Bedingung ansehen, aber auch auch einzelne Bits können wir zur Bedingung machen, das allerdings weiter unten.
Wenn wir die Thematik intern beleuchten, dann ist die eigentliche Vorbedingung für einen bedingten Sprung im Statusregister zu suchen. Alle jetzt behandelten bedingte Sprungbefehle springen, wenn ein bestimmtes Bit im SREG gesetzt oder gelöscht ist, was bedeutet, dass wir unmittelbar vor dem Sprung das Statusregister beeinflussen müssen. Sowohl der Compiler als auch der Befehlssatz der Atmels nehmen uns hier viel Arbeit ab und so müssen wir uns in der Regel keine Gedanken darum machen, was da jetzt im Statusregister passiert und welchen Sprungbefehl wir verwenden. Bei den 8085 z.B. musste man wissen, welcher Befehl welches Flag abfrägt, bei den Atmels ergibt sich deren Funktion im Namen. Ich gehe hier auf einen bedingten Sprunbefehl tiefer ein und erwähne danach noch weitere, die wir sicher öfter brauchen.
Unsere bedingten Sprünge heissen ausgeschrieben "Branch..." was so viel wie "sich verzweigen" bedeutet. In der Syntax kommt danach, immer noch als Bestandteil des Namens die Bedingung, gefolgt vom Einsprunglabel. Schauen wir uns zunächst das Kommando "Branch if Equal" an:

Code: Alles auswählen

BREQ Irgendwoanders   ; Springe zu Irgendwoanders wenn das Ergebnis gleich ist
Wie Ihr seht ist der Sprunbefehl alleine ziemlich wert- und sinnlos, eines möchte ich aber erwähnen. BREQ springt zum Label, sobald das Zero-Bit im Statusregister gesetzt ist ;)
Wir müssen also unmittelbar vor dem bedingten Sprung nachsehen, ob die Bedingung erfüllt ist und die jeweiligen Bits im Statusregister setzen oder löschen.
Was haltet Ihr von einem Vergleichsbefehl? Wir vergleichen ein Register also z.B. mit einem bestimmten Wert und wenn beide gleich, also "equal" sind, dann springen wir.

Code: Alles auswählen

cpi Zaehler, 0x05   ;ist der Wert 5 erreicht?
breq Schleifenende ;wenn ja, dann springe zum Label "Schleifenende"
CPI bewirkt dabei lediglich, dass der angegebene Wert vom Inhalt des Registers abgezogen wird, ohne das Ergebnis zu speichern. Gespeichert werden jedoch die jeweiligen Flags im Statusregister. Wird also in unserem Beispiel der Wert 5 erreicht würde das Ergebnis, wenn man 5 davon abzieht, 0 ergeben. Damit wird das Zeroflag gesetzt, was wiederum den nachfolgenden Branch-Befehl dazu veranlasst, den Sprung auszuführen.

Wollen wir dagegen springen, wenn eine Bedingung nicht erfüllt ist, dann muss man sich den passenden Befehl in den Unterlagen suchen, die bedingten Sprünge fangen eigentlich alle mit BR.. an.

Code: Alles auswählen

cp Zaehler, Temp        ;Ist der Zaehlerstand gleich Temp?
brne Schleifenanfang   ;Wenn nein, dann springe zum Anfang der Schleife
Branch If No Equal lässt unseren Prozessor in diesem Beispiel springen wenn der Inhalt von "Zaehler" nicht gleich dem Inhalt von "Temp" ist, das Z-Bit also gelöscht ist.

Hier noch zwei weitere Branch-Befehle:
BRLO = Branch if lower (Springe wenn kleiner)
BRGE = Branch if eqal or grater (Springe wenn gleich oder größer)
Es gibt noch einige mehr, ich habe mich auf die wichtigsten und trivialen beschränkt.

Nun haben wir bisher nur Werte zur Bedingung gemacht. Auch wenn wir letztendlich doch nur aufgrund gesetzter oder gelöschter Bits im SREG springen, die Flags müssen aufgrund einer Rechen- oder Vergleichsoperation gesetzt oder gelöscht werden, um die Bedingung zu schaffen.
Es gibt aber auch die Möglichkeit, aufgrund einzelner Bits in Registern oder im IO-Port zu springen, wenngleich das auf dem ersten Blick vermutlich umständlicher aussieht.
Nehmen wir zunächst mal den Befehl "sbrs". Ausgeschrieben heisst das "Skip if bit in Register is set" und bewirkt, dass der nachfolgende Befehl übersprungen wird, wenn das angegebene Bit gesetzt ist. Der zu überspringende Befehl kann entweder ein unbedingter Sprunbefehl sein oder auch etwas anderes, was sich in einem einzelnen Befehl abarbeiten lässt. Dazu zwei Beispiele:

Code: Alles auswählen

sbrs Temp, 0          ;Ist das Bit 0 in Temp gesetzt?
ldi Temp, 0x01        ;Wenn ja, dann wird dieser Befehl übersprungen
out PortB, Temp      ;Hier geht es weiter
Das weitere Beispiel zeigt den umgekehrten Fall:

Code: Alles auswählen

sbrc Temp, 0         ;Ist Bit0 in Temp geloescht (clear)?
rjmp Rechnen        ;Wenn die Bedingung nicht erfüllt ist springe zum Label Rechnen
out PortB, Temp    ;weiterer Programmablauf
Ihr seht, man spart damit evtl. etwas Schreibarbeit und vor allem Programmlaufzeit. Es gibt viele Situationen, in denen nur einzelnen Bits abgefragt werden, da macht man sich mit Branch-Befehlen und den vorangehenden Compares nur wenig Freude.

Noch interessanter und vor allem sparsamer ist die Abfrage einzelner Bits in Bezug auf die IO-Ports. Auch hier können wir Skippen und zwar mit "sbis" und "sbic" (Skip if bit in IO is set/clear), die Syntax ist wie bei sbrc/sbrs.
Fragen wir mal unseren T1 ab:

Code: Alles auswählen

sbis PinD, 2        ;Ist Bit2 vom IO-Port D gesetzt?
ldi Temp, 0xFF     ;Wenn nein, dann ist T1 betätigt, also alle Bits in Temp setzen
out portB, Temp  ;Temp ausgeben
Unsere Taster sind Low-Aktiv und mit der ersten Zeile prüfe ich Bit2, an dem ja Taster1 angeschlossen ist, ab. Ist dieser im Ruhezustand ist das Bit auf High, also gesetzt und der folgende Befehl wird übersprungen. Ist der taster jedoch betätigt, dann ist das Bit2 des IO-Ports nicht gesetzt, es wird nicht übersprungen und somit wird Temp vor der Ausgabe mit 0xFF gefüllt.
Wie Ihr seht muss der IO-Port nicht vorher eingelesen werden, um z.B. mit einem Wert verglichen zu werden. Mit sbis und sbic kann ein IO-Port direkt ausgewertet werden, wenn auch nur einzelne Bits.


Nun folgt die Aufgabe für die nächsten, ich schlage mal vor 2, Wochen:
Unser Projekt heisst "Taschenlampe" und die soll wie folgt funktionieren:
- Nach der Inbetriebnahme sind alle LEDs aus.
- Wird T1 betätigt, gehen alle LEDs an (und bleiben auch nach dem Loslassen an)
- Mit T2 gehen alle LEDs wieder aus (und bleiben auch nach dem Loslassen aus)
- Werden T1 und T2 gleichzeitig gedrückt leuchtet jede zweite LED... solange beide gleichzeitig gedrückt sind

Die Aufgaben werden umfangreicher und anspruchsvoller. Lasst Euch bitte Zeit und überlegt Euch in eigenen Worten Schritt für Schritt wie Ihr zur Lösung vorgeht. Dann versucht, die eigenen Worte in Assembler umzusetzen. Zur Lösung dieser Aufgabe braucht Ihr vermutlich ein paar Labels, mindestens zwei Register, bedingte und unbedingte Sprünge, Einlesen einzelner Bits und ganzer Ports, evtl. Maskieren... eigentlich alles. Fragen bitte hier, auch wenn sie für den Fragenden noch so doof klingen wollen. Die Lösungen wieder per Mail an mich.

PS: Bevor ich Euch so eine ätzende Aufgabe stelle löse ich die selbst, so dass nie etwas unmögliches gefordert wird ;) . Es gibt also auch hier bereits eine funktionsfähige Lösung ;)

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

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#73

Beitrag von Muenchner Kindl »

Hi,

die erste Woche ist schon fast wieder rum und ich wollte mal fragen, wie es Euch so geht. Reicht die weitere Woche oder wollt Ihr dann noch eine dritte haben?
Und bitte... bei Unklarheiten und Unbehagen fragen fragen fragen...
Benutzeravatar

kaeselok
ICE-Sprinter
Beiträge: 6785
Registriert: Mo 30. Apr 2007, 13:15
Nenngröße: 1
Stromart: digital
Steuerung: ECoS II / TAMS
Gleise: Hübner nach NEM
Wohnort: Brühl

Re: Workshop: Programmierung in Assembler (Atmel ATM8)

#74

Beitrag von kaeselok »

Muenchner Kindl hat geschrieben:die erste Woche ist schon fast wieder rum und ich wollte mal fragen, wie es Euch so geht. Reicht die weitere Woche oder wollt Ihr dann noch eine dritte haben?
Hallo Thomas,

ich habe das "Sprung-Kapitel" nur mal angelesen. Mehr noch nicht. Aber morgen wollte ich eigentlich etwas Zeit für den Atmel Workshop investieren ... es soll hier aber auch 17 Grad warm werden ... :oops:

Viele Grüße,

Kalle
Benutzeravatar

DeMorpheus
Metropolitan (MET)
Beiträge: 3633
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)

#75

Beitrag von DeMorpheus »

Hallo,

am heutigen Feiertag habe ich mich mit der Taschenlampe beschäftigt und habe mittlerweile auch eine Lösung gefunden. Mir ist während der Entstehung aber etwas aufgefallen, was ich mir überhaupt nicht erklären kann (der entsprechende Programmabschnitt befindet sich auch gar nicht ind er Lösung, da mein Grundgedanke falsch war).

Zur Erklärung: Ich hatte vorgesehen, dass das Label "Ausgabe" in einer Schleife so lange durchläuft, wie mindestens ein Taster gedrückt ist und erst bei Loslassen beider Taster ins Hauptprogramm springt. Folgender Abschnitt sollte das leisten:

Code: Alles auswählen

Ausgabe:
		ldi Eingang, pind             ; Input nach Eingang schreiben
		andi Eingang, 0b00001100      ; Maskieren von Eingang
		cpi Eingang, 0b00001100       ; Vergleich von Eingang mit 0b00001100
		breq Hauptprogramm            ; Springe zum Hauptprogramm
		rjmp Ausgabe                  ; Schleife
[/size]
Ergebnis: Das Programm bleibt in der Schleife hängen :shock:

Die beiden folgenden Varianten hingegen zeigen absolut identisches Verhalten: Die Schleife wird sofort verlassen, egal welcher oder wie viele Taster gedrückt sind.

Code: Alles auswählen

Ausgabe:
		ldi Eingang, pind             ; Input nach Eingang schreiben
		andi Eingang, 0b00001100      ; Maskieren von Eingang
		cpi Eingang, 0x00             ; Vergleich von Eingang mit 0
		breq Hauptprogramm            ; Springe zum Hauptprogramm
		rjmp Ausgabe                  ; Schleife
[/size]

Code: Alles auswählen

Ausgabe
		rjmp Hauptprogramm            ; Springe zum Hauptprogramm
[/size]
Ich steh wahrscheinlich einfach nur auf dem Schlauch und sehe offensichtliches nicht, wie bei den PullUp-Widerständen, aber verstehen würde ich das trotzdem ganz gerne. Hat da jemand eine Idee?
Warum ich die Schleife jetzt ganz weggelassen hab, kann ich euch noch nicht verraten ;)

Schöne Grüße und frohes Programmieren,

Moritz
Viele Grüße,
Moritz
'Nitwit! Blubber! Oddment! Tweak!'
Antworten

Zurück zu „Workshops“