Assembler atmega8535 - 1 Sekunde Schleife

Severin Fl

Neues Mitglied
02. Okt. 2012
11
0
0
29
Forsbach, Nordrhein-Westfalen, Germany, Germany
Sprachen
Heyhey,
unser Lehrer hat uns über die Ferien eine neue Aufgabe gegeben und diesmal möchte ich auch früh genug hier rein schreiben.
Also folgendes:
Wir sollen ein einfaches Programm programmieren, das für einen Durchlauf genau 1 Sekunde benötigen soll.
Folgende Commands dürfen wir benutzen: ADD; SUB; SUBI; AND; ANDI; OR; ORI; COM; INC; DEC; CLR; SER; LDI; RJMP; MOV; IN; OUT; LSL; LSR; ROL; ROR; CP; CPI; BREQ; BRNE; BRSH; BRCS; BRCC; BRLO.

Mein erster Gedanke war ein Counter, denn ich weiß das ein Counter 1,24 ms braucht um bis 1000 zu zählen. Jetzt müsste man nur aussrechnen bis wieviel der Zähler zählen soll, dass er eine Sekunde benötigt und dann dem entsprechend Programmieren oder irre ich mich da? :D


-LG-
Severin
 
Hi,

ich geb dir hier mal nen Beispiel ...
Code:
; ##################################################################
; ##################################################################
; #### Warte-Subroutinen ###########################################
; ##################################################################
; ##################################################################
; 62,5ns Zykluszeit bei 16MHz
; 50,0ns Zykluszeit bei 20MHz
 
; ==================================================================
; ===== 580ns warten ===============================================
; ==================================================================
;	rcall wait580n	; (3)     Warteschleife aufrufen
wait580n:			; 11 Cyclen (incl Call+Ret) => ~550ns@20MHz
	nop				; (1)
	nop				; (1)
	nop				; (1)
	nop				; (1)
	ret				; (4)     Schleifenende, Rueckkehr


; ==================================================================
; ===== r16 * 7ms warten ===========================================
; ==================================================================
;	rcall waitx7m	; (3)     Warteschleife aufrufen - Zeit in r16
; ================>	; 131843*x+18 Cyclen (incl Call+Ret) => ~x*86,59ms@20MHz
waitx7m: push r20	; (2)     r20 auf Stack retten (1tes)
	push r21		; (2)     r21 auf Stack retten (2tes)
	clr r20			; (1)     r16, r20 und r21 ergeben
	clr r21			; (1)     zusammen einen 3-Byte-Zaehler
					;                     3_____________________________
					; r16=1 8,241ms min   |    2____________131841_     |
					; r16=0 2,109s  max   |    |     1__513__      |    |
					;                     |    |     |       |     |    |
					;         123 Cycle = r16*((256*((256*2+1)+2)+1)+2)+1+17
loopx7m: dec r20	; (1)     ||| niedrigstes Byte -1
	brne loopx7m	; (1f,2t)_/|| 0 erreicht? nein -> Schleife
	dec r21			; (1)      || mittleres Byte -1
	brne loopx7m	; (1f,2t)__/| 0 erreicht? nein -> Schleife
	dec r16			; (1)       | höchstes Byte -1
	brne loopx7m	; (1f,2t)___/ 0 erreicht? nein -> Schleife
					;
	pop r21			; (2)     r21 zurueckholen (2tes)
	pop r20			; (2)     r20 zurueckholen (1tes)
	ret				; (4)     Schleifenende, Rueckkehr
an den beiden Beispielen kannst du sehen wie man es machen könnte. Du mußt es jetzt nur für dich umsetzen und vor allem die Rechnung verstehen. Das Verstehen ist das wichtigste. Dabei kann dir die PDF helfen auf die schon LotadaC hingewiesen hat ... "AVR-Instruction-Set"

In den Klammern (2) , (1f,2t) , ... sind die Taktzyklen verzeichnet die ein Befehl benötigt. Zusammen mit der Taktfrequenz kommst du auf die Ausführungszeit eines Befehls. Das mußt du dann so zusammenbasteln das eine Sekunde rauskommt. Bei Verzweigungen (Branch, Skip, ...) hast du je nach Entscheidung (False/True) einen oder zwei Taktzyklen.

Gruß
Dino
 
Hallo Severin,

euer Lehrer möchte ja, dass ihr eine Schleife programmiert und nicht einen Timer/Counter konfiguriert.

Ziemlich zum Schluss des Mikrocontroller-Datenblatts steht eine Liste mit den verfügbaren Instruktionen (Instruction Set Summary). Jede Instruktion benötigt für die Ausführung eine bestimmte Anzahl von Maschinenzyklen, die Anzahl ist dort angegeben. Viele Instruktionen benötigen lediglich nur einen Maschinenzyklus, bei den bedingten Sprüngen (breq, brlo ...) kommt es darauf an, ob die Bedingung wahr ist oder nicht.

1 Maschinenzyklus benötigt 1/fsys Zeit.

Programmiere eine Schleife in der du den Inhalt eines Registers inkrementierst oder dekrementierst. Für eine Gesamtzeit von 1 Sekunde wirst du zwei oder sogar drei Schleifen verschachteln müssen.

Dirk :ciao:
 
Die wesentlichen Ansatzpunkte sind ja jetzt schon genannt, Ohne die Taktfrequenz zu kennen/nennen kommt hier keiner weiter.
Erste Überlegung:
Wann braucht das (Unter-)Programm genau(!) eine Sekunde?
Wenn Die Summe der Zykluszeiten aller Befehle genau(!) eine Sekunde beträgt. Oder anders gesagt: Du mußt dem Protessor nur exakt 1s lang mit "unsinnigen" Befehlen beschäftigen.
Nebenüberlegung: nehmen wir mal 'ne Taktfrequenz von 1MHz an (Auslieferungszustand des Prozessors). Dann dauert ein Taktzyklus 1/1000000Hz=1µs. Dementsprechend könntest Du also zB 1000000 Ein-Takt-Befehle hintereinander ausführen lassen (NOP zB). Das wäre aber nicht nur unübersichtlich, sondern würde auch den Quellcode, und insbesondere auch die Flash-Verwendung unnötig erhöhen.
Nunja - in diesem Fall wird ja immer wieder ein und derselbe Befehl ausgeführt, hier kommt die Schleife ins Spiel.
Allerdings ist dabei zu berücksichtigen, daß alle Instruktionen die die Schleife betreffen (Lade-/Speicherbefehle, Zähler manipulieren, Vergleiche, Sprünge - da sie eben auch ausgeführte Instruktionen sind) eben auch Zeit "verbrauchen".
So, aber nun erstmal genug Hilfestellung - jetzt kannste erstmal allei weitergrübeln/-rechnen. Dazu noch folgender Rat:
...ein Counter 1,24 ms braucht um bis 1000 zu zählen. Jetzt müsste man nur aussrechnen bis wieviel der Zähler zählen soll, dass er eine Sekunde benötigt und dann dem entsprechend Programmieren...
versuch das "mal andersrum" anzugehen - alles, was Dein Programm (oder die Controller-Hardware) zählt basiert irgendwie auf dem Binärsystem und Bytes. Ein Byte zählt nur (von 0) bis 255, und läuft dann über. Klar, da kann man dann ein weiteres Byte drankoppeln, um bis zu 2^16-1 zu kommen, aber mein Rat ist: vergiß Dein dämliches Dezimalsystem hier - damit rechnest Du nur, weil der Mensch 10 Finger hat (und zu faul ist, sich ständig die Socken auszuziehen;))

Edit: grad noch festgestellt, daß NOP auch nicht erlaubt ist - aber die Aussage beeinfluß das ja nicht.
Achso, die andere wichtige pdf für Dich wäre dann sicher diese hier (die komplette mit den 321 Seiten).
 
Edit: grad noch festgestellt, daß NOP auch nicht erlaubt ist - aber die Aussage beeinfluß das ja nicht.
ist doch Sch...egal :p Dann füllt man eben mit nem LDI irgendein Register was nicht benötigt wird mit irgendeinem x-beliebigem Byte. Das benötigt auch einen Taktzyklus. Also sozusagen ein selbstgemachtes NOP ;) :cool:

Noch was zum Mega8535 ... das ist ein schöner Controller der Pinkompatibel zum Mega16/Mega32 ist. Sehr gut für den Anfang geeignet. Ich würde mal sagen man kann ihn als Mega8 im 40pol-Gehäuse bezeichnen.

Gruß
Dino
 
würde mich mal interessieren....was daraus wird!
Auch mal ein Lob an Severin, wie Er das als Neuling so
schnallt.

Grüße

Rolf
 
Hallo,

also Assembler ist eigentlich die am logischsten aufgebaute Sprache. Die Befehle sind eigentlich ja auch nur Abkürzungen von dem was die Befehle jeweils machen.
ldi = Load Immediate
add = Add (Without Carry)
adc = Add with Carry
brcc = Branch if Carry Cleared
Man muß lediglich bei jedem Befehl die Bits des Statusregisters im Hinterkopf haben. Die verändern sich oft und geben bei vielen Befehlen Zusatzinformationen zum Ergebnis des Befehle aus. Zum Beispiel ...
C = Carry - Es ist ein Überlauf/Übertrag entstanden
S = Sign - Das Ergebnis ist negativ
Z = Zero - Das Ergebnis ist Null
H = HalfCarry - Es ist ein Überlauf/Übertrag zwischen Bit3 und Bit4 entstanden (Nibble-Übertrag)
...
Wenn man also eine Zahl auf größer/kleiner/gleich zu einem anderen Wert testen will dann werden die beiden Zahlen einfach voneinander abgezogen. Die Bits im Statusregister geben einem alle Ergebnisse auf einmal. Das wird über den Befehl Compare (cp) erledigt
Zahl1 - Zahl2
Z=1 ... Die beiden Zahlen waren gleich
S=1 (und Z=0) ... Die zweite Zahl war größer (Ergebnis ist negativ, eine Null kann nicht negativ sein, darum kann es keine 0 sein)
Z=0 und S=0 ... Die erste Zahl war größer (Ergebnis positiv, und nicht Null)

Gruß
Dino
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)