PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Tutorial für alle Assembler-Anfänger _



Seiten : 1 [2] 3

Tekeli
13.08.2005, 14:19
ich dachte, die erste Teilaufgabe ist gelöst worden? Zwar nicht mit einer 16-Bit Adressenrechnung, wie das Florian vor hatte, aber es funzt. :)

Ist das richtig so?

toeoe
13.08.2005, 14:19
So, da bin ich wieder vom essen.

@ Thomas:
Was verstehst Du derzeit nicht?
Das mit cp und cpc, also wie das genau funktioniert, aber das test ich dann erstmal im Simulator aus, wie Sebastian meinte.
Und wir meinten ja, dass der String "Hallo" 6 Bytes hat, ne? Wieso passt der dann in ein 8 Bit Register? 8 Bit sind doch nur 1 Byte, also sehr viel kleiner als 6 Byte. Oder bring ich da nun was durcheinander?

cpi kennen wir schon. Hab ich auch oben in meinem Code verwendet, aber ich glaub, so wolltest du das ja nicht haben, oder?

Florian
13.08.2005, 14:24
Hallo Thomas!
Jedes Byte wird nacheinander in das r0 geladen, nicht der ganze String auf einmal!
Sonst müsstest Du ja nicht immer lpm ausführen!

Soll ich einen Beispielcode posten?

toeoe
13.08.2005, 14:30
Ja, dass es nacheinander passiert, ist mir klar, aber am Ende kann in r16 ja nicht "Hallo!" stehen, oder? Denn "Hallo!" braucht ja nunmal 6 Bytes. Also steht am Ende im Register nur ein "!"?


Soll ich einen Beispielcode posten?
Wenns zum Verständniss beiträgt ;) Die Teilaufgabe haben wir ja schon gelöst, also verrätst du ja nichts.

[edit]
Der Befehl
cp reg1, reg2
rechnet im prinzip reg2 - reg1, oder?

cpc funzt aber irgendwie anders, wie hab ich leider noch nicht rausgefunden.

Florian
13.08.2005, 14:34
Ja, dass es nacheinander passiert, ist mir klar, aber am Ende kann in r16 ja nicht "Hallo!" stehen, oder? Denn "Hallo!" braucht ja nunmal 6 Bytes. Also steht am Ende im Register nur ein "!"?Ja, ich wollte ja nur, dass ihr ungefähr den Ablauf versteht, wie man den Speicher ausliest!

Ok, hier kommt die Lösung!


.include "m8def.inc"

.def lpm_reg = r0
.def temp = r16

.equ daten_laenge = 6



reset:
stack: ; Stack
ldi temp , LOW (RAMEND) ; LOW-Byte
out SPL , temp

ldi temp , HIGH (RAMEND) ; HIGH-Byte
out SPH , temp

z_register:
ldi ZL , LOW (daten * 2) ; Z-Register laden, mit der Adresse der Daten
ldi ZH , HIGH (daten * 2)



main:
lpm ; Daten laden

mov temp , lpm_reg ; in temp verschieben

adiw ZL , 1 ; Z um eins erhöhen

ldi temp , LOW ((daten * 2) + daten_laenge) ; vergleiche LOW-Byte
cp ZL , temp

ldi temp , HIGH ((daten * 2) + daten_laenge) ; vergleiche HIGH-Byte
cpc ZH , temp

breq ende ; springe zu ende, wenn letztes Byte ausgelesen

rjmp main ; nochmal



ende: ; Endschleife
rjmp ende



daten: ; Daten
.DB "Hallo!"

Jetzt erklärt mir bitte die einzelnen Segmente des Codes!

toeoe
13.08.2005, 14:38
Ich kann dir leider nicht sagen, was das hier bedeutet

ldi temp , LOW ((daten * 2) + daten_laenge) ; vergleiche LOW-Byte
cp ZL , temp

ldi temp , HIGH ((daten * 2) + daten_laenge) ; vergleiche HIGH-Byte
cpc ZH , temp
Da ich das mit dem unteren und oberen Byte ja immer noch nicht verstanden habe :(

Tekeli
13.08.2005, 14:45
So wie ich das jetzt verstanden habe, sind die Adressen selbst auch 16 Bit.
Die Adresse 0x020F z.B. besteht aus zwei Byte nähmlich:
0x02 - High Byte
0x0F - Low Byte
Der ATmega8 kann aber nur 8-Bit-Zahlen vergleichen. (richtig so?)
Deswegen verglechen wir erstmal Low-Bytes von der Adressen und dann High-Bytes. Siehe die Post von Florian um 13:23

toeoe
13.08.2005, 14:47
Und wieso dann einmal cp und einmal cpc?
also with carry heißt doch dann mit Übertrag. Was bedeutet in dem Zusammenhang "Übertrag". Ich kenn Übertrag nur beim schriftlichen Rechnen, bspw. beim Addieren, wenn die oberen beiden Zahlen > 9 sind, dann muss man bei sagen wir mal 10 ne 0 hinschreiben (unterm strich) und dann 1 als übertrag, die wir dann mitaddieren.

izaseba
13.08.2005, 14:50
Hallo, o weh, es kommt Knüppeldick!

Ich erlaube mir einen Auszug aus www.avr-asm-tutorial.net zu zitieren, damit
Ihr hoffe ich sieht was wir hier machen!



Addition, Subtraktion und Vergleich
Ungeheuer schwierig in Assembler ist Addieren, Dividieren und Vergleichen. Zart-besaitete Anfänger sollten sich an dieses Kapitel nicht herantrauen. Wer es trotzdem liest, ist übermütig und jedenfalls selbst schuld.

Um es gleich ganz schwierig zu machen, addieren wir die 16-Bit-Zahlen zu den Registern R1:R2 die Inhalte von R3:R4 (Das : heißt nicht Division! Das erste Register gibt das High-Byte, das zweite nach dem : das Low-Byte an).

ADD R2,R4 ; zuerst die beiden Low-Bytes
ADC R1,R3 ; dann die beiden High-Bytes

Anstelle von ADD wird beim zweiten Addieren ADC verwendet. Das addiert auch noch das Carry-Bit dazu, falls beim ersten Addieren ein Übertrag stattgefunden hat. Sind sie schon dem Herzkasper nahe?

Wenn nicht, dann kommt jetzt das Subtrahieren. Also alles wieder rückwärts und R3:R4 von R1:R2 subtrahiert.

SUB R2,R4 ; Zuerst das Low-Byte
SBC R1,R3 ; dann das High-Byte

Wieder derselbe Trick: Anstelle des SUB das SBC, das zusätzlich zum Register R3 auch gleich noch das Carry-Bit von R1 abzieht. Kriegen Sie noch Luft? Wenn ja, dann leisten sie sich das folgende: Abziehen ohne Ernst!

Jetzt kommt es knüppeldick: Ist die Zahl in R1:R2 nun größer als die in R3:R4 oder nicht? Also nicht SUB, sondern CP, und nicht SBC, sondern CPC:

CP R2,R4 ; Vergleiche untere Bytes
CPC R1,R3 ; Vergleiche obere Bytes

Wenn jetzt das Carry-Flag gesetzt ist, kann das nur heißen, dass R3:R4 größer ist als R1:R2. Wenn nicht, dann eben nicht.

Thomas hat schon richtig erkannt, was cp macht, hat sich aber noch nicht mit SREG
so richtig auseinander gesetzt

Gruß Sebastian

Florian
13.08.2005, 15:00
Also Thomas!

So wie ich das jetzt verstanden habe, sind die Adressen selbst auch 16 Bit.Ja klar! *lol*
Das Adressregister ist 16Bit, da man sonst die ganzen Flashbytes nicht aufrufen könnte, sondern nur 255, das wäre etwas wenig! ;o)

Der ATmega8 kann aber nur 8-Bit-Zahlen vergleichen. (richtig so?) Deswegen verglechen wir erstmal Low-Bytes von der Adressen und dann High-Bytes.Genau richtig!

Nochmal ganz langsam:
ldi temp , LOW ((daten * 2) + daten_laenge)
daten ist die Adresse, zu der gesprungen werden soll.
Das nimmt man wie gewohnt mal zwei.
Dazu addieren wir dann 6 (Bytes), um das Ende festzulegen.
Das vergleichen wir dann mit der Funktion cp mit ZL, dem LOWbyte.
Das ganze dann nochmal mit dem Highbyte.
Fertig, das ist alles!

toeoe
13.08.2005, 15:05
Nochmal ganz langsam:
ldi temp , LOW ((daten * 2) + daten_laenge)
daten ist die Adresse, zu der gesprungen werden soll.
Das nimmt man wie gewohnt mal zwei.
Dazu addieren wir dann 6 (Bytes), um das Ende festzulegen.
Das vergleichen wir dann mit der Funktion cp mit ZL, dem LOWbyte.
Das ganze dann nochmal mit dem Highbyte.
Fertig, das ist alles!

Nichts fertig :(
Ich verstehs einfach nicht.
Wir haben doch dort eine Schleife, also rufen wir doch auch 6x diesen Befehl hier auf:
ldi temp , LOW ((daten * 2) + daten_laenge)
Wenn nun das Zeichen "!" kommt, dann sind wir ja nicht mehr bei 6 Byte, sondern schon beim 12 Byte.
Hmm..egal ](*,)

Florian
13.08.2005, 15:10
Lieber Thomas!
Tust Du mir den gefallen und schaust Du Dir das mal im Simulator an?
Immer mit F11 weiter und beobachte die Register uvm. ...

toeoe
13.08.2005, 15:22
Hab ich doch schon alles gemacht.

ldi reg1, 0b00000000
ldi reg2, 0b11111111
cp reg2, reg1
Dann hat sich im SREG was geändert, schau ich hier nach: http://www.avr-asm-tutorial.net/avr_de/beginner/pdetail.html#SREG
S und N sind auch 1. Was sagt mir das?
Dass das Vorzeichen negativ ist und das Ergebnis dann logischerweise auch negtativ. Was schließe ich daraus? das der Befehl cp reg1 - reg2 rechnet. Aber das ist ja nicht so, denn bei deinem Bild, was du hier eingefügt hast, steht:
cp Rd, Rr Compare Rd - Rr
Müsste dann bei "cp reg2, reg1" wohl reg2 - reg1 sein. Aber da ist das Ergebnis ja nicht negativ, sonder = reg2.
Bei cpc kommt bei den Werte oben dasselbe raus, soweit so gut, doch bei folgenden Werten:
ldi reg1, 0b11111111
ldi reg2, 0b11111111
kommt bei cp reg2, reg1
Z von SREG = 1, also Ergebnis ist 0, also die Werte beider Register sind
gleich, logisch. Aber bei "cpc reg2, reg1" kommt auf einmal nichts raus. Also alle Bits von SREG sind 0. Somit heißt es unter anderem auch, dass die Werte der Register reg1 und reg2 ungleich sind, sind sie aber doch nicht.
Ich hoffe, du weißt nun, wo mein Problem liegt :(

Florian
13.08.2005, 15:33
Lieber Thomas! ;o)
Also bei mir funktioniert das richtig!
Ich komme irgendwie mit dem Problem nicht zurecht! ;o9 *schmunzel*

toeoe
13.08.2005, 15:36
Naja, dann is auch egal, muss ich eben so hinnehmen.

Florian
13.08.2005, 15:38
*g* Ist die Funktion trotzdem in etwa klar?

toeoe
13.08.2005, 15:45
Naja, weniger...

main:
lpm ;Daten laden
mov tmp, lpm_reg ;in temp verschieben
adiw ZL, 1 ;Z um eins erhöhen
ldi tmp, LOW ((daten1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((daten1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
breq ende ;springe zu ende, wenn letztes Byte ausgelesen
rjmp main ;nochmal

ende: ;Endschleif
rjmp ende
Nach diesem Befehl hier
mov tmp, lpm_reg
erhöhen wir den Zeiger ja um 1, ne? Damit wir das nächste Byte bekommen, also den nächsten Buchstaben. Und dann benutzt du aber wieder das gleich Register, wo wir ja eigentlich das Wort "Hallo!" drinne haben wollten:
ldi tmp, LOW ((daten1 * 2) + daten_laenge)
Da schreiben wir dann in tmp wieder einen völlig anderen Wert.

[EDIT]
Was mir grad auffällt, nach der Zeile hier:
cp ZL, tmp
da werten wir den Vergleich gar nicht aus, sonder nur nach cpc wird ausgewertet. Wieso das denn? :o

Florian
13.08.2005, 16:10
In dem Moment, wo wir die main-Schleife betreten haben wir ja noch die Adresse von 'H' in Z stehen, also daten1 + 0.
Wir lesen dann das 'H' aus.
Danach erhöhen wir auf daten1 + 1 und vergleichen mit dem Wert für das erste Byte nach dem String, also daten1 + 6 (das siebte Byte).
Nun springen wir wieder an den Anfang.
und mache das gleiche mit 'a', 'l', 'l', 'o' und '!'.
Sobald wir nach dem '!' wiederholen, sieht das ganze so aus:
Wir haben im Z daten1 + 5 (6. Byte) und laden mit lpm das Byte in r0.
Das verschieben wir nach temp.
Danach erhöhen wir Z um 1, also haben wir daten1 + 6 (7.Byte, was nciht mehr da ist).
Wir vergleichen nun die Register mit jeweils dem Endwert und da glaich, springen wir mit breq zum Ende!

Ich hoffe es war verständlicher?

toeoe
13.08.2005, 16:17
Ja, schon ein wenig, zwar nicht ganz...naja, für manche Sachen ist mal wohl zu doof. Das wird auch nicht das wichtigste sein, denk ich. Ich hoffe ich kann auch weitermachen, wenn ich das jetzt hier nicht so richtig verstanden habe.

Aber wenn wir beim "!" angekommen sind, dann ist doch das aktuelle Byte das Byte 6 oder? Weil ja das Ausrufezeichen das 6. Zeichen ist. Und wenn wir dann noch mit daten1+6 vergleichen, dann sind wir ja bei 12, denn 6 (Byte 6 = !) + 6 (Das Ende von der Zeichenkette) = 12.

Florian
13.08.2005, 16:33
Hallo Thomas!
Du würfelst jetzt alles durcheinander!
Nur zu Anfang wird die Adresse mal 2 genommen!
Zum Schluss wird immer nur + 1 genommen, ohne dass mal 2 genomnmen wird!
'H' ist das 1. Byte und wird bei (daten1 * 2) + 0 ausgelesen.
'a' ist das 2. Byte und wird bei (daten1 * 2) + 1 ausgelesen.
'l' ist das 3. Byte und wird bei (daten1 * 2) + 2 ausgelesen.
'l' ist das 4. Byte und wird bei (daten1 * 2)1 + 3 ausgelesen.
'o' ist das 5. Byte und wird bei (daten1 * 2) + 4 ausgelesen.
'!' ist das 6. Byte und wird bei (daten1 * 2) + 5 ausgelesen.
FF ist das leere Byte nach dem Strin (Zeichenkette) und wird mit (daten1 * 2) + 6 ausgelesen.


ich glaube ich habe Dich durcheinander gebracht! *lol*

toeoe
13.08.2005, 16:36
Ich glaub, ich habs nun in etwa verstanden. Ich hab immer gedacht, dass sich daten1 auch verändert. Weil wir ja den Z-Zeiger immer um 1 erhöhen. Also dachte ich, dass sich daten1 erhöht, aber stimmt ja, der Zeiger erhöht sich ja nur, oder? Und das das erhöhen zeigt er auf das nächste Byte, also auf das nächste Zeichen, ne?
Wenn das so stimmt, dann können wir denk ich jetzt weiter machen. Sorry, dass ich uns so aufgehalten hab *g*

michaelb
13.08.2005, 16:37
Hallo Zusammen,
total interessiert hab ich den kompletten Thread jetzt gelesen! Und habe total Lust darauf bekommen!! Und ich jetzt einsteigen will! Ihr bekommt nen Assembler Mitwohner dazu! Da ich euch net mit blöden Einsteigerfragen belästigen will suche ich ein gutes Tutorial! Kennt ihr ein gutes? Doch eine Frage hab ich jetzt schon: Welches Programm zum programmieren könnt ihr empfehlen?
Gruß Michi

toeoe
13.08.2005, 16:39
Ich programmiere mit AVR Studio, das ist eigentlich sehr gut. Und das ist hier ein gutes Tutorial ;)

michaelb
13.08.2005, 16:43
hi,
ist AVRStudio kostenlos? Und mit was proggst du die hex in den Controller? mit ponyprog?
gruß michi

toeoe
13.08.2005, 16:47
Jo, AVR Studio ist kostenlos. Brennen tu ich mit TwinAVR, da ich mit PonyProg nicht zu meinem Chip conntecten kann.

Gruß
Thomas

michaelb
13.08.2005, 16:51
hi,
hab gerade gelesen das man ponyprog auch verweden kann! des isch gut weil ich bis jetzt auch mit ponyprog gemacht hab!
gruß michi

Florian
13.08.2005, 16:56
Hallo Michael!
Wenn Du alles gelesen hast, dann müsstest Du Sebastains und meine Anfängertipps gelesen haben, wo wir beschrieben haben, wo bzw. wie man anfängt! ;o)
Wilkommen in der ASM-Familie! :o)


Ich glaub, ich habs nun in etwa verstanden.Sehr schön! *freu*
Auf gehts, zu neuen Abenteuern! ;o)

Ich hab immer gedacht, dass sich daten1 auch verändert.Nein, eben nicht, das sind feste Konstanten, die wir aus dem Flash-Speicher lesen können!
Also eine Art Datenbankbibliothek oder Wertetabelle!

Weil wir ja den Z-Zeiger immer um 1 erhöhen. Also dachte ich, dass sich daten1 erhöht, aber stimmt ja, der Zeiger erhöht sich ja nur, oder? Und das das erhöhen zeigt er auf das nächste Byte, also auf das nächste Zeichen, ne?Genau, Du hast's geschnallt! *freu* *glückwunsch*

Wenn das so stimmt, dann können wir denk ich jetzt weiter machen. Sorry, dass ich uns so aufgehalten hab *g*Kein Problem, dafür heißt das ja jetzt auch Tutorial! ;o)
Los gehts! *freu*


Ps:
Ich verwende AVR-Studio (www.atmel.com) und Ponyprog (www.lancos.com), sind beide sehr gut!

toeoe
13.08.2005, 17:00
Los gehts! *freu*

*Aufgabe such* :-b *gg*

Florian
13.08.2005, 17:02
Bitte berechne die Werte für die tongebenden Register für den Kammerton a', c' und c''!

Schaut mal:
https://www.roboternetz.de/phpBB2/statistics.php

*FREU*

michaelb
13.08.2005, 17:10
Hallo Florian,
welches Programm muss ich hier ruterladen?
Link (http://www.atmel.com/dyn/products/tools_card.asp?tool_id=2725)
Gruß Michi

toeoe
13.08.2005, 17:12
Ok, also wir nehmen als Prescaler 512 *einfach mal so festleg* *g*
Den Timer laden wir wie folgt vor (bei 3,68 MHz)
c': 3686400 / 512 / 261,63 = 27,5 = 27 --> 256 - 27
a': 3686400 / 512 / 440 = = 16,3 --> 256 - 16
c'': 3686400 / 512 / 523,25 = 13,7 --> 256 - 14

Müsste soweit richtig sein.

In Assembler sieht das dann erstmal so aus:

.equ time0_1 = 256-27 ;Timer für c'
.equ time0_2 = 256-16 ;Timer für a'
.equ time0_3 = 256-14 ;Timer für c''

[edit]
Michi: Ich hab das hier: AVR Studio 4.11 (build 401) (41 MB, updated 01/05)

Florian
13.08.2005, 17:14
Alles wo AVR Studio 4.11 dransteht, also das zweite und dritte von oben!
AVR Studio 4.11 (build 401)
und wenn Du möchtest
AVR Studio 4.11 Service Pack 3

michaelb
13.08.2005, 17:18
hi,
danke ich seh gerade hier (http://www.mikrocontroller.net/) gibt's auch ein gutes Tutorial!
Gruß Michi

toeoe
13.08.2005, 17:20
Wieso brauchen wir eigentlich nur die Werte für die 3 Töne? Berechnen
wir die anderen irgendwie? Aber hab auch grad festgestellt, dass die Töne nciht alle denselben Abstand haben.

Mehto
13.08.2005, 17:28
Ähh, ich will ja echt nicht stören, aber so viel wie ihr postet wird euer " Tutorial für alle Assembler-Anfänger" doch etwas zu unübersichtlich und geht nach hinten los. ;)

Ich glaub nicht das sich da ein Anfänger durchquälen möchte...

Hat jemand Lust das alles zusammen zu tragen und eine strukturierte Einführung in Assembler zu machen? *niemand anguck*

Gruß,
Mehto

Florian
13.08.2005, 17:29
Hallo Thomas!
Wir wollen erstmal klein anfangen, da reichen 3 Töne erstmal! ;o)

c': 3686400 / 512 / 261,63 = 27,5 = 27 --> 256 - 27
a': 3686400 / 512 / 440 = = 16,3 --> 256 - 16
c'': 3686400 / 512 / 523,25 = 13,7 --> 256 - 14Ich meine mich dunkel erinnern zu können, dass man ab 5 nach dem Komma aufrundet! *lol*
Also:
c' : 28
a' : 16
c'' : 14
Sehr gut, ich habe jetzt nicht nachgerechnet! *lol*


.equ time0_1 = 256-27 ;Timer für c'
.equ time0_2 = 256-16 ;Timer für a'
.equ time0_3 = 256-14 ;Timer für c''Nee, wir wollen jetzt die Töne nicht als Konstante im Code ablegen, das wird zu kompliziert! ;o)

Du erstellst jetzt ein neues Programm, mit Standarddefinitionen, wie Stack usw. plus einem Timer, z.B. dem Timer0 oder 2!
Dann setzt Du ganz unten folgendes an den Code:

tonleiter1:
.db 28 , 16 , 14 , 0
Die Null am Ende ist nur Füllmaterial, da wir ja nur drei Töne derzeit haben! ;o)

Und dann zeige mal Deinen bisherigen Code!

toeoe
13.08.2005, 17:30
Man könnte ja hinterher die "weniger wichtigen" Beiträge löschen, das dient dann auch der Übersicht. Oder das wichtigste zusammentragen, wär auch ne Möglichkeit ;)
Aber erstmal müssen wir unser Tutorial zu Ende machen.

Gruß
Thomas

michaelb
13.08.2005, 17:31
Hallo,
ich hab jetzt das AVRStudio und such noch wie ich das Programm einstellen muss um ne hex zu erstellen! Gibt es kein gescheites Tutorial?
Gruß Michi

Florian
13.08.2005, 17:31
Hallo Mehto!
Ich werde das später mal machen! *freiwillig meld*

toeoe
13.08.2005, 17:40
Hi Michi, einfach das Programm, was du geschrieben hast, kompilieren, dann ist automatisch eine *.hex Datei in deinem Ordner.
Kompilieren kannst du mit F7.

@Florian:
So, erstmal gut soweit:

.include "m8def.inc"

.def tmp = r16 ;Mein Universallregister
.def lpm_reg = r0 ;Mein lpm-Register

.equ time0 = 256-90 ;Damit wird der Timer0 vorgeladen
.equ daten_laenge = 6 ;Länge des Strings

.org 0x000
rjmp reset ;Interruptvektor "reset:"

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;Low-Byte der obersten RAM-Adresse
out SPL, tmp

;Timer Register werden belegt, hier Timer 0
ldi tmp, (1<<CS02) ;Prescaler ist 1024
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, time0 ;Hier wird der Timer vorgeladen
out TCNT0, tmp
ldi tmp, (1<<TOIE0) ;Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

;Z-Register mit daten1 füllen
ldi ZH, HIGH(tonleiter1 * 2)
ldi ZL, LOW(tonleiter1 * 2)

sei ;Interrupts zulassen

main:
lpm ;Daten laden
mov tmp, lpm_reg ;in temp verschieben
adiw ZL, 1 ;Z um eins erhöhen
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
breq ende ;springe zu ende, wenn letztes Byte ausgelesen
rjmp main ;nochmal

ende: ;Endschleife
rjmp ende

tonleiter1:
.db 28, 16, 14, 0 ;Werte zum Vorladen des Timers für die Töne
;c', a' und c''
Allerdings muss hier noch die daten_laenge angepasst werden, oder? Also jetzt sind das ja 4 Bytes und nicht mehr 6, oder?

Gruß
Thomas

michaelb
13.08.2005, 17:43
Sorry dass ich nochmal frag! 8-[
Wie muss ich AVRStudio einstellen, dass ich ein Programm erstellen kann und die .hex erstellen kann?
Gruß Michi

michaelb
13.08.2005, 17:45
Hallo,
am Anfang kommen so Menüs was muss ich da auswählen?
Gruß Michi

toeoe
13.08.2005, 17:46
Hab ich doch eben geschrieben :o
Also du startest AVR Studio. Dann klickst du auf Projekt -> New Projekt. Wählst nen Ordner aus und gibst deinem Projekt einen Namen. Dann tickerst du deinen Code da ein und drückst F7. Dann hast du in dem Ordner, wo sich auch dein Projekt befindet, eine *.hex Datei. Die kannst du dann mit PonyProg oder TwinAVR oder was auch immer auf deinen Chip brennen.

Gruß
Thomas

[edit]
Erstell dir am besten auf deiner Festplatte einen Ordner mit dem Namen "Mikrocontroller", den wählste dann im AVR Studio aus. Die beiden Haken bei "Create Initial File" und "Create Folder" lässte drinne.
Da ist doch echt nicht viel einzustellen bei dem Menü :o

michaelb
13.08.2005, 17:53
Hi,
tut mir echt leid dich mit so blöden Fragen zu stressen! Was muss ich das einstellen?
Gruß Michi

toeoe
13.08.2005, 17:56
Ach das Fenster meinst du. Da musst du deinen Chip einstellen. Das ist sinnvoll, wenn du später mal simulieren willst, spart Schreibzyklen auf den Chip ;)

michaelb
13.08.2005, 17:58
Was machst du da?
Gruß Michi

Florian
13.08.2005, 18:04
AVR Simulator M8 oder M16 oder was Du für einen µC hast! ;o)

Florian
13.08.2005, 18:07
Hallo Thomas!
Jetzt schriebe bitte den Code so um, dass immer bei jedem Timerinterrupt die Werte aus dem Datenregister nacheinander reingeladen werden!

izaseba
13.08.2005, 18:15
Hallo Leute,
Wie ich sehe seid Ihr schon Musik ammachen, schön!
Leider hab ich hier vieles verpasst, aber wie ich sehe, hat sich Florian
tapfer geschlagen, obwohl er schon mit den Nerven am Ende zu sein scheint O:)

Ich stelle leider fest, daß 16 bit Manipulationen eine ziemlich schwere Aufgabe ist.
Ich habe auch damit gerechnet, weil ich da auch mal Probleme mit hatte.

Da ich leider, von Musik nicht viel verstehe, versuche ich gleich das mit dem
Vergleichen usw. etwas einfacher zu beschreiben, vielleicht gelingt es mir.

Gruß Sebastian

Florian
13.08.2005, 18:18
Hallo Sebastian!

Da ich leider, von Musik nicht viel verstehe, versuche ich gleich das mit dem Vergleichen usw. etwas einfacher zu beschreiben, vielleicht gelingt es mir.Na dann viel Erfolg! ;o)

Hast Du zu meinem bisherigen Werk noch was zu ergänzen!?
War etwas schwierig zu erklären, aber eigentlich ist es ja nicht schwer! *lol*

michaelb
13.08.2005, 18:18
Ok hex erstellt! Auf was klicke ich bei PonyProg?
Gruß Michi

Florian
13.08.2005, 18:19
Lieber Michi!
Schau mal bitte hier http://www.s-huehn.de !
Ansonsten gibt es hier im Forum auch schon einiges dazu!


*edit*
Genauer http://s-huehn.de/elektronik/avr-prog/avr-prog.htm

izaseba
13.08.2005, 18:28
War etwas schwierig zu erklären, aber eigentlich ist es ja nicht schwer! *lol*



Das glaub ich Dir gerne!!!


Hast Du zu meinem bisherigen Werk noch was zu ergänzen!?

Eigentlich nicht, nur um den ganzen Streß mit cp cpc aus dem Weg zu gehen, hätte ich doch einen Nullterminierten String genommen.

Und einen großen Nachteil, hat man ja auch, wenn man diese methode anwendet,
man muß immer genau wissen, wieviele Zeichen man hat, aber das hat ja Thomas schon mal gesagt.

Aber um den Umgang mit dem Z-Pointer zu zeigen wirklich gut.

Gruß Sebastian

Florian
13.08.2005, 18:31
Hi Sebastian!
Natürlich hätt's die Null-Variante auch getan, von mir aus könnten wir auch umsteigen, wenn's für euch verständlicher ist!?

@ Thomas:
Warum hast Du TCNT0 mit time0 255-90 vorgeladen?

michaelb
13.08.2005, 18:34
Haaaaaaalllllllllloooo,
juuuuuhuuuuuuuuu meine Led leuchtet!!!!!! Danke
Gruß Michi

izaseba
13.08.2005, 18:37
von mir aus könnten wir auch umsteigen

Nein Florian, lass es so wie es ist, kann man später machen, aber so mittendrin alles umzuschmeißen ist nicht gut, glaube ich.

Florian
13.08.2005, 18:39
Hi Sebastian!
Was würde man nur machen, wenn man einen Wert von 0 bräuchte?
Dann wäre meine Möglichkeit besser, oder?

izaseba
13.08.2005, 18:49
Was würde man nur machen, wenn man einen Wert von 0 bräuchte?


Sicherlich, ich habe auch nicht gesagt, daß Deine Möglichkeit schlecht ist, es hängt alles davon ab was man machen will.

So ist es gut, jetzt haben wir ja den Vergleichoperator kennengelernt.

@Thomas,
dieses 256 - 90 ist ja noch vom allem Programm geblieben ?
Wir wollen keine Sekunde erzeugen.

toeoe
13.08.2005, 18:49
@ Thomas:
Warum hast Du TCNT0 mit time0 255-90 vorgeladen?

Weil ich den Prescaler nun auf 1024 habe und so besser auf eine Sekunde komme. Jede Sekunde soll er also nun den nächsten Ton laden. Hier der Code.

;Programm
;CDurTonleiter rauf und runter spielen
.include "m8def.inc"

.def tmp = r16 ;Mein Universallregister
.def zaehlerSek = r17 ;Mein Zählregister
.def zaehlerTon = r18 ;Mein Zählregister, um zu prüfen, ob alle Töne geladen wurden
.def lpm_reg = r0 ;Mein lpm-Register

.equ time0 = 256-90 ;Damit wird der Timer0 vorgeladen
.equ daten_laenge = 4 ;Anzahl der Werte

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF2addr
rjmp pruefSek ;Interruptvektor "ladeTon:"

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;Low-Byte der obersten RAM-Adresse
out SPL, tmp

;Timer Register werden belegt, hier Timer 0
ldi tmp, (1<<CS02) | (1<<CS00) ;Prescaler ist 1024
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, time0 ;Hier wird der Timer vorgeladen
out TCNT0, tmp
ldi tmp, (1<<TOIE0) ;Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

;Z-Register mit daten1 füllen
ldi ZH, HIGH(tonleiter1 * 2)
ldi ZL, LOW(tonleiter1 * 2)

ldi zaehlerSek, 0b00000000 ;ZählerSek auf 0 setzen

sei ;Interrupts zulassen

main:
cpi zaehlerSek, 0b00010100 ;wenn ZählerSek != 40 ist
brne main ;dann immer wieder zu "main:" springen

pruefTonleiter:
clr zaehlerSek ;Zähler auf 0 setzen
cpi zaehlerTon, 0b11111111 ;Wenn ZählerTon != 255 ist
brne ladeTon ;dann spring zu "ladeTon:"
rjmp main ;sonst wieder zurück zu "main:"

pruefSek:
push tmp ;tmp sichern
in tmp, SREG ;SREG sichern
push tmp
inc zaehlerSek ;ZählerSek um 1 erhöhen
ldi tmp, time0 ;Hier wird der Timer vorgeladen
out TCNT0, tmp
pop tmp ;SREG wiederholen
out SREG, tmp
pop tmp ;tmp wiederholen
reti ;wieder dahin, wo du hergekommen bist

ladeton:
clr zaehlerSek ;Zähler wieder auf 0 setzen
lpm ;Daten von tonleiter1: holen
mov tmp, lpm_reg ;erstes Byte in tmp verschieben
adiw ZL,1 ;Z um 1 erhöhen, nächstes Byte
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
breq endeTon ;springe zu "endeTon:", wenn letztes Byte ausgelesen
rjmp main ;wieder zurück zur "main:"

endeTon:
ldi zaehlerTon, 0b11111111 ;ZählerTon auf 255 setzen -> keine weiteren Wert
;mehr von "tonleiter1:" holen
rjmp main ;wieder zurück zu "main:"

tonleiter1:
.db 28, 16, 14, 0 ;Werte zum Vorladen des Timers für die Töne
;c', a' und c''
Bitte auch prüfen, ob ich das Sichern von tmp und SREG über den Stack richtig gemacht habe, nicht das es hinterher daran liegt.
Also das Programm macht nun folgendes:
Er zählt hoch bis 1 Sekunde, dann prüft er, ob alle Töne schon geladen worden sind, wenn nicht, lädt er den nächsten Ton. Ist er beim letzten Ton angekommen, setzte er ein Register auf 255 und somit weiß ich dann, dass alle Töne geladen worden sind.

Hoffe mal, das ist das was du wolltest :)

Gruß
Thomas

[edit]
Wir wollen keine Sekunde erzeugen? Aber wenn wir eine Tonleiter machen, dann muss doch irgendwo festgelegt sein, in welchem Abstand der neue Ton kommen soll, oder?

Florian
13.08.2005, 18:50
dieses 256 - 90 ist ja noch vom allem Programm geblieben ?
Wir wollen keine Sekunde erzeugen.Achja!
Gibt es schon ein Update?

toeoe
13.08.2005, 18:51
Über dir, hehe ;)

Florian
13.08.2005, 18:55
Hallo Thomas!
Jetzt hast Du ja schon mehr gemacht, als Du solltest, das wäre die nächste Aufgabe gewesen! *lol*
Herzlichen Glückwunsch!
Mal sehn ob ich Fehler finde! ;o)

izaseba
13.08.2005, 18:59
Hallo Thomas,

Ich sehe, Du hast es mit dem lpm Befehl begriffen, so wie ich sehe springst Du im
Sekundentakt die Datenbank durch, und wenn Du am ende angekommen bist gehst Du in eine Endlosschleife , richtig?

Ich denke, daß man aber mit den Zahlen, die Du mit lpm holst noch was füttern sollte.

toeoe
13.08.2005, 19:02
Jo, so ist das erstmal richtig, wie du sagst.

Jo, die Zahlen die ich von der Datenbank hole, müssten irgendwie in einen zweiten Timer rein, der dann den Sound abspielt, ne?

Florian:

Jetzt schriebe bitte den Code so um, dass immer bei jedem Timerinterrupt die Werte aus dem Datenregister nacheinander reingeladen werden!
Du hast was von Timerinterrupt geschrieben, du bist schuld :P *g*

Florian
13.08.2005, 19:08
Also irgendwie steige ich durch den Code nicht durch! *lol*

izaseba
13.08.2005, 19:12
Thomas, es ist nicht schlimm,
damit hast Du durch den Rücken ins Auge geschossen!

Ausgelesen hast Du schon alles, dann versuche doch einen zweiten Timer, der Dir den Ton erzeugt damit zu laden, also bei jedem Sekundeninterrupt mit hilfe von lpm den Wert auslesen, und damit den Timer für den Ton zu stopfen,
Ist fast das gleiche, wie bei der Sirene, aber eben nur fast

toeoe
13.08.2005, 19:14
Also irgendwie steige ich durch den Code nicht durch! *lol*

Lol, auch net schlecht *g*

Ok, also ich werds versuchen mit dem zweiten Timer.

Argh, bei Timer2 gibt es gar kein Prescaler von 512, hrmph...muss ich die Timer tauschen....also Timer2 für die Sekunde und Timer0 für den Ton.

Florian
13.08.2005, 19:46
Ok, also ich werds versuchen mit dem zweiten Timer.Na, wie sieht's aus?

toeoe
13.08.2005, 19:48
So, Update:

;Programm
;CDurTonleiter rauf und runter spielen
.include "m8def.inc"

.def tmp = r16 ;Mein Universallregister
.def zaehlerSek = r17 ;Mein Zählregister
.def zaehlerTon = r18 ;Mein Zählregister, um zu prüfen, ob alle Töne geladen wurden
.def Tonwert = r19 ;aktueller Wert für den Ton, damit wird der Timer0 vorgeladen
.def lpm_reg = r0 ;Mein lpm-Register

.equ Summer = PB2 ;Summer an B.2
.equ time0 = 256-255 ;Timer0 für die Tonleiter
.equ time2 = 256-90 ;Damit wird der Timer2 vorgeladen, für die Sekunde
.equ daten_laenge = 4 ;Anzahl der Werte

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF2addr
rjmp pruefSek ;Interruptvektor "pruefSek:"

.org OVF0addr
rjmp timerSummer ;Interruptvektor "timerSummer:"

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;Low-Byte der obersten RAM-Adresse
out SPL, tmp

;Timer Register für Ton werden belegt, hier Timer 0
ldi tmp, (1<<CS02) ;Prescaler ist 512
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, time0 ;Hier wird der Timer vorgeladen
out TCNT0, tmp

;Timer Register für Sekunde werden belegt, hier Timer 2
ldi tmp, (1<<CS22) | (1<<CS21) | (1<<CS20) ;Prescaler ist 1024
out TCCR2, tmp ;Register TCRR2 ist für den Prescaller zuständig


ldi tmp, (1<<TOIE0) | (1<<TOIE2);Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

;Z-Register mit daten1 füllen
ldi ZH, HIGH(tonleiter1 * 2)
ldi ZL, LOW(tonleiter1 * 2)

ldi zaehlerSek, 0b00000000 ;ZählerSek auf 0 setzen

sbi DDRB, Summer ;B.2 als Ausgang
sbi PORTB, Summer ;B.2 auf HIGH stellen

sei ;Interrupts zulassen

;Die Hauptschleife
main:
cpi zaehlerSek, 0b00010100 ;wenn ZählerSek != 40 ist
brne main ;dann immer wieder zu "main:" springen

;Wenn eine Sekunde um ist, dann springe hier rein
;und prüfe, ob es noch einen Wert für die Tonleiter
;zu laden gibt, wenn nicht, spring wieder zurück zu main
pruefTonleiter:
clr zaehlerSek ;Zähler auf 0 setzen
cpi zaehlerTon, 0b11111111 ;Wenn ZählerTon != 255 ist
brne ladeTon ;dann spring zu "ladeTon:"
rjmp main ;sonst wieder zurück zu "main:"

;Wird aufgerufen, wenn Timer2 überläuft (Timer für Sekunde)
;Hier wird das Zählregister für den Timer um 1 erhöht und der
;Timer neu geladen
pruefSek:
push tmp ;tmp sichern
in tmp, SREG ;SREG sichern
push tmp
inc zaehlerSek ;ZählerSek um 1 erhöhen
ldi tmp, time2 ;Hier wird der Timer vorgeladen
out TCNT2, tmp
pop tmp ;SREG wiederholen
out SREG, tmp
pop tmp ;tmp wiederholen
reti ;wieder dahin, wo du hergekommen bist

;Wenn 1 Sekunde vorbei ist und es noch einen Ton gibt,
;der noch nicht geladen ist dann springe hier hin und
;lade den nächsten Wert von der Datenbank "tonleiter"
ladeton:
clr zaehlerSek ;Zähler wieder auf 0 setzen
lpm ;Daten von tonleiter1: holen
mov tonwert, lpm_reg ;erstes Byte in tmp verschieben
adiw ZL,1 ;Z um 1 erhöhen, nächstes Byte
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
breq endeTon ;springe zu "endeTon:", wenn letztes Byte ausgelesen
rjmp main ;wieder zurück zur "main:"

;Wenn alle Töne geladen sind, dann wird das Register "zaehlerTon"
;auf 255 gesetzt
endeTon:
ldi zaehlerTon, 0b11111111 ;ZählerTon auf 255 setzen -> keine weiteren Wert
;mehr von "tonleiter1:" holen
rjmp main ;wieder zurück zu "main:"

;Wird aufgerufen, wenn Timer0 überläuft (Timer für Ton)
;Hier wird geprüft, ob an B.2 HIGH oder LOW anliegt
;und dementsprechend umgesetzt
timerSummer:
push tmp ;tmp sichern
in tmp, SREG ;SREG sichern
push tmp

sbis PINB, Summer ;überspringe, wenn B.2 = 1 ist
rjmp timerSummer1 ;wenn B.2 = 0 ist, dann spring zu "umschalten1:"
cbi PORTB, Summer ;wenn B.2 = 1 ist, dann B.2 auf 0 setzen
rjmp timerSummer2 ;zu "timerSummer2:" springen

timerSummer1:
sbi PORTB, Summer ;wenn B.2 = 0 ist, dann auf 1 setzen

;Hier wird Timer0 mit dem aktuellen Tonwert vorgeladen
timerSummer2:
ldi tmp, 255-tonwert ;Timer mit dem aktuellen Tonwert vorladen
out TCNT0, tmp
rjmp timerSummer4 ;zu "timerSummer4:" springen

timerSummer4:
pop tmp ;SREG wiederholen
out SREG, tmp
pop tmp ;tmp wiederholen
reti


;Das sind die Werte, womit der Timer0 (Tonleiter-Timer) vorgeladen wird
tonleiter1:
.db 28, 16, 14, 0 ;Werte zum Vorladen des Timers für die Töne
;c', a' und c''
Aber bei der Zeile hier meckert er:

ldi tmp, 255-tonwert ;Timer mit dem aktuellen Tonwert vorladen
Habs auch mit in und out versucht, geht aber auch nicht. Was anderes fällt mir leider nicht ein :(
Der Code wächst ja ganz schön, hab auch versucht vor jedem Label eine kleine Beschreibung zu schreiben, was darin geschieht. Hoffe, so versteht es auch Florian ;) *g*

Gruß
Thomas

izaseba
13.08.2005, 19:55
ldi tmp, 255-tonwert ;Timer mit dem aktuellen Tonwert vorladen


Er meckert, weil tonwert eine Variable ist, die erst zum Zeitlauf geändert wird,
woher soll der Assembler wissen, was mit tonwert gemeint ist.
Es gab da aber ein Assemblerbefehl fürs Subtrahieren .......

Edit: noch schlimmer, tonwert ist ein register,
Du versuchst von 255 einen register abzuziehen.....

toeoe
13.08.2005, 20:03
Hmm..aber wenn Tonwert kein Register ist, dann meckert er hier:
mov tonwert, lpm_reg ;erstes Byte in tmp verschieben

[edit]
subi tonwert,255
Es ist aber immer noch ein Register...hmm...Also muss ich den Wert von Tonwert in einer Variable bringen...
bspw. in
.equ vorlader = 0?

izaseba
13.08.2005, 20:11
Hmm..aber wenn Tonwert kein Register ist, dann meckert er hier:
mov tonwert, lpm_reg ;erstes Byte in tmp verschieben


klar, weil mov 2 Register als Argumente erwartet

aber was hälst Du von dem sub Befehl?
Du richtest Dir einen Register und belegst Ihn mit 255,
dann machst Du folgendes:
sub irgendein_register,tonwert
im irgendein_register bleibt dann was über, ich frag mich was...

toeoe
13.08.2005, 20:14
Ähhmm..in irgendein_register bleibt dann 255-tonwert übrig. Aber das hilft mir doch nichts :o
Und dann hab ich ja beim Vorladen vom Timer immer noch ein Register :(

izaseba
13.08.2005, 20:27
Aber das hilft mir doch nichts Surprised
Und dann hab ich ja beim Vorladen vom Timer immer noch ein Register Sad


Thomas, lass Dich nicht hängen!

Schau:

ldi irgendein_register,0xFF
sub irgendein_register,tonwert
out TCNT0,irgendein_register

Na, hat es Klick gemacht?

toeoe
13.08.2005, 20:38
Ach, mist. Ja sicher machts Klick *g*
Hab ja vorher immer noch in tmp und dann schön mit 255-x
Aber klar, oje *g*
Aber komisch anhören tut sich das. Vor allem macht er nicht immer dasselbe.
Einmal ist nur ein hoher Ton zu hören, einmal ist der hohe Ton zu hören und wechselt dann auf niederen Ton. Komisch irgendwie.

izaseba
13.08.2005, 20:39
Eine andere und vielleicht auch bessere Möglichkeit, wäre es in der tonleiter1:
nicht 28,16,14 zu schreiben, sondern 256-28 -> 228 265-16 -> 240 256-14 -> 242

damit hast Du direkt den Wert mit dem der Timer geladen werden soll,
fiel mir gerade so ein.

Nu ob der Florian das so haben möchte weiß ich auch nicht

toeoe
13.08.2005, 20:42
Stimmt, so spar ich mir ein Register. Florian wollt ja nur die Tonleiter haben ;) Aber die hört sich ja wie oben gesagt, komisch an:

;Programm
;CDurTonleiter rauf und runter spielen
.include "m8def.inc"

.def tmp = r16 ;Mein Universallregister
.def zaehlerSek = r17 ;Mein Zählregister
.def zaehlerTon = r18 ;Mein Zählregister, um zu prüfen, ob alle Töne geladen wurden
.def tonwert = r19 ;aktueller Wert für den Ton
.def lpm_reg = r0 ;Mein lpm-Register

.equ Summer = PB2 ;Summer an B.2
.equ time0 = 256-255 ;Timer0 für die Tonleiter
.equ time2 = 256-90 ;Damit wird der Timer2 vorgeladen, für die Sekunde
.equ daten_laenge = 4 ;Anzahl der Werte

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF2addr
rjmp pruefSek ;Interruptvektor "pruefSek:"

.org OVF0addr
rjmp timerSummer ;Interruptvektor "timerSummer:"

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;Low-Byte der obersten RAM-Adresse
out SPL, tmp

;Timer Register für Ton werden belegt, hier Timer 0
ldi tmp, (1<<CS02) ;Prescaler ist 512
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, time0 ;Hier wird der Timer vorgeladen
out TCNT0, tmp

;Timer Register für Sekunde werden belegt, hier Timer 2
ldi tmp, (1<<CS22) | (1<<CS21) | (1<<CS20) ;Prescaler ist 1024
out TCCR2, tmp ;Register TCRR2 ist für den Prescaller zuständig


ldi tmp, (1<<TOIE0) | (1<<TOIE2);Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

;Z-Register mit daten1 füllen
ldi ZH, HIGH(tonleiter1 * 2)
ldi ZL, LOW(tonleiter1 * 2)

ldi zaehlerSek, 0b00000000 ;ZählerSek auf 0 setzen

sbi DDRB, Summer ;B.2 als Ausgang
sbi PORTB, Summer ;B.2 auf HIGH stellen

sei ;Interrupts zulassen

;Die Hauptschleife
main:
cpi zaehlerSek, 0b00010100 ;wenn ZählerSek != 40 ist
brne main ;dann immer wieder zu "main:" springen

;Wenn eine Sekunde um ist, dann springe hier rein
;und prüfe, ob es noch einen Wert für die Tonleiter
;zu laden gibt, wenn nicht, spring wieder zurück zu main
pruefTonleiter:
clr zaehlerSek ;Zähler auf 0 setzen
cpi zaehlerTon, 0b11111111 ;Wenn ZählerTon != 255 ist
brne ladeTon ;dann spring zu "ladeTon:"
rjmp main ;sonst wieder zurück zu "main:"

;Wird aufgerufen, wenn Timer2 überläuft (Timer für Sekunde)
;Hier wird das Zählregister für den Timer um 1 erhöht und der
;Timer neu geladen
pruefSek:
push tmp ;tmp sichern
in tmp, SREG ;SREG sichern
push tmp
inc zaehlerSek ;ZählerSek um 1 erhöhen
ldi tmp, time2 ;Hier wird der Timer vorgeladen
out TCNT2, tmp
pop tmp ;SREG wiederholen
out SREG, tmp
pop tmp ;tmp wiederholen
reti ;wieder dahin, wo du hergekommen bist

;Wenn 1 Sekunde vorbei ist und es noch einen Ton gibt,
;der noch nicht geladen ist dann springe hier hin und
;lade den nächsten Wert von der Datenbank "tonleiter"
ladeton:
clr zaehlerSek ;Zähler wieder auf 0 setzen
lpm ;Daten von tonleiter1: holen
mov tonwert, lpm_reg ;erstes Byte in tmp verschieben
adiw ZL,1 ;Z um 1 erhöhen, nächstes Byte
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
breq endeTon ;springe zu "endeTon:", wenn letztes Byte ausgelesen
rjmp main ;wieder zurück zur "main:"

;Wenn alle Töne geladen sind, dann wird das Register "zaehlerTon"
;auf 255 gesetzt
endeTon:
ldi zaehlerTon, 0b11111111 ;ZählerTon auf 255 setzen -> keine weiteren Wert
;mehr von "tonleiter1:" holen
rjmp main ;wieder zurück zu "main:"

;Wird aufgerufen, wenn Timer0 überläuft (Timer für Ton)
;Hier wird geprüft, ob an B.2 HIGH oder LOW anliegt
;und dementsprechend umgesetzt
timerSummer:
push tmp ;tmp sichern
in tmp, SREG ;SREG sichern
push tmp
sbis PINB, Summer ;überspringe, wenn B.2 = 1 ist
rjmp timerSummer1 ;wenn B.2 = 0 ist, dann spring zu "umschalten1:"
cbi PORTB, Summer ;wenn B.2 = 1 ist, dann B.2 auf 0 setzen
rjmp timerSummer2 ;zu "timerSummer2:" springen

timerSummer1:
sbi PORTB, Summer ;wenn B.2 = 0 ist, dann auf 1 setzen

;Hier wird Timer0 mit dem aktuellen Tonwert vorgeladen
timerSummer2:
out TCNT0, tonwert ;Timer dementsprechen vorladen
rjmp timerSummer4 ;zu "timerSummer4:" springen

timerSummer4:
pop tmp ;SREG wiederholen
out SREG, tmp
pop tmp ;tmp wiederholen
reti


;Das sind die Werte, womit der Timer0 (Tonleiter-Timer) vorgeladen wird
tonleiter1:
.db 255-28, 255-16, 255-14, 0 ;Werte zum Vorladen des Timers für die Töne
;c', a' und c''
Hier nochmal der aktuelle Code.

izaseba
13.08.2005, 20:47
Einmal ist nur ein hoher Ton zu hören, einmal ist der hohe Ton zu hören und wechselt dann auf niederen Ton. Komisch irgendwie.

Naja,
1. man muß 256 - tonwert nehmen, mit dem sub machen wir aber 255- tonwert,
ich denke, es verfälscht den Ton irgendwie.
Ich falle selber immerwieder auf 255 rein ](*,)
2. Es muß micht heißen, daß Dein Programm 100% funktioniert,

Wie war das Du hörst nur 2 Töne?

Florian
13.08.2005, 20:49
Ich hatte eigentlich daran gedacht im Programm vorher das abzuziehen, nicht von vorneherein im Datenspeicher, aber das können wir auch machen!

michaelb
13.08.2005, 20:51
hey leute,
dieses Tutorial ist der hammer!!! Gerade arbeitete ich mich durch jeden Thread durch! Und hab jetzt endlich die Timer verstanden!!
Gruß Michi

izaseba
13.08.2005, 20:58
@ Florian, sorry ich hoffe, Du bist mir nicht böse

@Michi

Ich freue mich daß Du auch gefallen an unserem Tutorial gefunden hast.

michaelb
13.08.2005, 20:59
Ich muss euch echt loben *g* ist echt anfängertauglich!
Gruß Michi

Florian
13.08.2005, 21:00
sorry ich hoffe, Du bist mir nicht böseWarum sollte ich das sein?

toeoe
13.08.2005, 21:06
So, habs jetzt mal mit extremeren Werten probiert, damit man besser den Unterschied hört:
.db 256-117, 256-16, 256-2, 0
2 Fehler hab ich noch drinne:

1.
Also wenn ich den Mikrocontroller einschalte (also Strom an), dann spielt er die drei Töne hintereinander ab. Also zuerst 256-117, dann 256-16 und dann 256-2, das soll er ja auch, doch dann spielt er noch einen Ton ab, den ich gar nicht aufgeführt hab. Auch einen hohen Ton, der noch höher ist als 256-2 ist. Und dann spielt er noch ein Ton, der wieder 256-117 entspricht. Also er spielt 5 Töne ab.

2:
Wenn ich den Mikrocontroller dann ausschalte und dann wieder anschalte, dann spielt er die Tonleiter nicht mehr ab, sondern spielt nur den niedrigsten Ton ab. Ich muss also ne Zeit lang warten, bis er wieder die Töne hintereinander spielt.

Und die 1 Sekunde hält er auch nicht ein, die Töne wechseln viel zu schnell.

Gruß
Thomas

[edit]
Der 1 Ton dauert eine Sekunde, dann wechselt er zum zweiten, aber dieser dauert sehr viel kürzer als eine Sekunde und wechselt dann zu dritten.

Florian
13.08.2005, 21:09
Kann es sein, dass Du die Begrenzung der Bytes vergessen hast, also er weiterspielt, selbst wenn da kein richtiger To mehr angefügt ist sondern er nur 0xFF spielt?

Tekeli
13.08.2005, 21:10
So hier kommt der Musik-Asuro. :)
Ich hatte leider keine Lust die Tonwerte auszurechen, aber das ist auch nicht unser Ziel. :) Deswegen habe ich irgenwas gennomen.

Ich habe aber noch ein Problem. Wenn der Asuro alles abgespielt hat,
fängt er einen Modem zu spielen(Es klingt so!). Das liegt aber wahrscheinlich
an der Hardware. Oder?

toeoe
13.08.2005, 21:12
Kann es sein, dass Du die Begrenzung der Bytes vergessen hast, also er weiterspielt, selbst wenn da kein richtiger To mehr angefügt ist sondern er nur 0xFF spielt?

Hmm..ja kann sein...aber wie soll ich denn eine Begrenzung bauen?

[edit]
Hatte als daten_laegen 4 stehen, weil ich die 0 mitgezählt habe, darf ich natürlich nicht.
So, jetzt spielt er nur noch die 3 Töne ab, am Ende spielt er dann in einer Endlosschleife des letzten Ton, so wies erstmal sein soll. Aber die Sekunde stimmt nach wie vor nur vom ersten zu zweiten Ton. Vom zweiten zum dritten Ton vergeht sehr viel weniger als eine Sekunde.

izaseba
13.08.2005, 21:17
Ich sag mal so,
Es gibt da 3 Sachen, die mir nicht so recht gefallen,

1. zaehlerton, brauchst Du das wirklich ?
2. datenlänge, muß die wirklich 4 betragen?
Du hast zwar 4 Werte drin, aber warum? weil es mit 3 nicht geht...
3. Du machst da irgendwo 2. clr zaehlersek,

schau da erstmal nach, ob es nicht eleganter gehen würde

toeoe
13.08.2005, 21:21
ahh, das mit der datenlänge hab ich ja noch rechzeiti selbst gemerkt :)

Die anderen Punkte schau ich nacher mal nach, geh erstmal was essen.

Hast du vlt. noch eine Idee, warum er, nach aus und gleich wieder anschalten die Töne nicht abspielt, sondern einfach nur in einer Endlosschleife den letzten Ton spielt? Muss immer mind. 10 Sekunden warten, bis ich ihn wieder anschalten kann, sodass er dann auch die Töne nacheinander abspielt.

Ich merk grad, er spielt trotzdem noch 4 Töne ab. Die 3, die ich definiert hab und hinterher noch einen höheren. Hrmph...

So, nu aber essen.

izaseba
13.08.2005, 21:23
Das liegt aber wahrscheinlich
an der Hardware. Oder?

Das glaube ich kaum, hast Du dafür gesorgt, daß er auch in eine Endlosschleife übergeht, wenn keine Töne mehr da sind?
Ich vermute, daß er sich einfach weitere sachen aus dem Flash holt und die abspielt,
aber ich schaue mir erst Dein Programm an!
P.S. könntest Du das nächste mal direkt posten ?
So muß ich mir das abspeichern, lesen, wieder löschen, Du weißt ja , ich bin Faul :oops:

izaseba
13.08.2005, 21:29
@Tekeli,
Ich hab Dich !!!!
schau
BREQ exit ; Ein ja bedeutet, dass wir am Ende sind -> Ende
Du meintest sicher ende , oder?

Der Asuro läuft Amok guck, wo Du ihn hingeschickt hast, nachdem er 0 gefunden hat

izaseba
13.08.2005, 21:32
an Euch beide,

Wenn alle Töne durch sind, sollte Der Timer 0 abgeschaltet werden, sonst spielt er den letzten ton ja weiter, wie das geht, kriegt Ihr sicher selber raus.

Florian
13.08.2005, 21:43
So, da bin ich wieder, musste erstmal mein Gehirn durchlüften, habe den ganzen Tag in der Bude rumgehockt! ;o) *couchpotato*
Ich lese mir jetzt die beiden Codes durch bzw. versuche es sie durchzulesen!

Florian
13.08.2005, 21:52
Wie ist jetzt eigentlich der Stand?
Tekeli's Programm funktioniert jetzt, oder?
Was ist mit Thomas Programm?

izaseba
13.08.2005, 21:53
Ja den ganzen Tag in der Bude, das ist hart,
ich hatte als alternative am Feuerwehrfest in unserem Dorf teilzunehmen, dann
wäre ich jetzt aber paar Euros ärmer, und der Kopf wäre auch nicht mehr so ganz klar
:twisted:

izaseba
13.08.2005, 21:59
Wie ist jetzt eigentlich der Stand?

Der Stand ist jetzt so, daß Tekeli seinen Asuro wohl ziemlich abstürzen liess,
aber so wie es aussieht ist es eine Kleingkeit.

Und Thomas sollte sein Programm etwas aufräumen denke ich,

Florian
13.08.2005, 22:02
Der Stand ist jetzt so, daß Tekeli seinen Asuro wohl ziemlich abstürzen liess, aber so wie es aussieht ist es eine Kleingkeit.Oh, das ist nicht gut!
Wie tief ist er denn gefallen? *kleiner dummer scherz*


Und Thomas sollte sein Programm etwas aufräumen denke ich,Das denke ich auch, denn ich steige da nicht mehr durch! *lol*

Siehst Du die Programme eigentlich nur so durch, oder lässt Du sie im Simulator durchlaufen?

toeoe
13.08.2005, 22:05
So, da bin ich wieder, ich werd jetzt mal die Punkte von Sebastian durchgehen.

izaseba
13.08.2005, 22:14
Florian, ich bin eine arme Sau.
Wie Du weißt habe ich nur Linux auf meiner Kiste UND AVRStudio will sich nicht dazu
überreden lassen unter Linux zu funktionieren.
Selbst mit wine, dem Windows Übersetzer sag ich mal nicht.
Und für Linux gibt es keine brauchbaren Simulatoren.
Ich hab hier avr_simulator, das Programm hat aber jedemenge Bugs, der Author hat wohl kein Bock mehr drauf, er antwortet auf keine Posts auf sourceforge, naja und ich
bin nicht gut genug um es selber zu verbessern.
Es gibt da noch den gdb, aber den kann ich auch nicht dazu überreden, mit .hex Dateien
zu arbeiten, habe mal ein Thread auf www.mikrocontroller.net geöffnet, aber selbst da
weißt keiner einen Rat, und das soll schon was heißen....

Also Zeile für Zeile durchlesen, und schauen was passiert.
Aber ich klage nicht, es geht auch ohne.

toeoe
13.08.2005, 22:22
Ich weiß nimmer weiter. Hab nun erstmal Punke verbessert (wenn man das so nennen darf), die du mir gesagt hast:

;Programm
;CDurTonleiter rauf und runter spielen
.include "m8def.inc"

.def tmp = r16 ;Mein Universallregister
.def zaehlerSek = r17 ;Mein Zählregister
.def tonwert = r19 ;aktueller Wert für den Ton
.def lpm_reg = r0 ;Mein lpm-Register

.equ Summer = PB2 ;Summer an B.2
.equ time0 = 256-255 ;Timer0 für die Tonleiter
.equ time2 = 256-90 ;Damit wird der Timer2 vorgeladen, für die Sekunde
.equ daten_laenge = 3 ;Anzahl der Werte

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF2addr
rjmp pruefSek ;Interruptvektor "pruefSek:"

.org OVF0addr
rjmp timerSummer ;Interruptvektor "timerSummer:"

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;Low-Byte der obersten RAM-Adresse
out SPL, tmp

;Timer Register für Ton werden belegt, hier Timer 0
ldi tmp, (1<<CS02) ;Prescaler ist 512
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, time0 ;Hier wird der Timer vorgeladen
out TCNT0, tmp

;Timer Register für Sekunde werden belegt, hier Timer 2
ldi tmp, (1<<CS22) | (1<<CS21) | (1<<CS20) ;Prescaler ist 1024
out TCCR2, tmp ;Register TCRR2 ist für den Prescaller zuständig


ldi tmp, (1<<TOIE0) | (1<<TOIE2);Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

;Z-Register mit daten1 füllen
ldi ZH, HIGH(tonleiter1 * 2)
ldi ZL, LOW(tonleiter1 * 2)

ldi zaehlerSek, 0b00000000 ;ZählerSek auf 0 setzen

sbi DDRB, Summer ;B.2 als Ausgang
sbi PORTB, Summer ;B.2 auf HIGH stellen

sei ;Interrupts zulassen

;Die Hauptschleife
main:
cpi zaehlerSek, 0b00010100 ;wenn ZählerSek != 40 ist
brne main ;dann immer wieder zu "main:" springen

;Wenn eine Sekunde um ist, dann springe hier rein
;und setze zaehlerSek wieder zurück auf 0 und
;lade den nächsten Ton
pruefTonleiter:
clr zaehlerSek ;Zähler auf 0 setzen
rjmp ladeTon ;nächsten Ton laden
rjmp main ;sonst wieder zurück zu "main:"

;Wird aufgerufen, wenn Timer2 überläuft (Timer für Sekunde)
;Hier wird das Zählregister für den Timer um 1 erhöht und der
;Timer neu geladen
pruefSek:
push tmp ;tmp sichern
in tmp, SREG ;SREG sichern
push tmp
inc zaehlerSek ;ZählerSek um 1 erhöhen
ldi tmp, time2 ;Hier wird der Timer vorgeladen
out TCNT2, tmp
pop tmp ;SREG wiederholen
out SREG, tmp
pop tmp ;tmp wiederholen
reti ;wieder dahin, wo du hergekommen bist

;Wenn 1 Sekunde vorbei ist und es noch einen Ton gibt,
;der noch nicht geladen ist dann springe hier hin und
;lade den nächsten Wert von der Datenbank "tonleiter"
ladeton:
lpm ;Daten von tonleiter1: holen
mov tonwert, lpm_reg ;erstes Byte in tmp verschieben
adiw ZL,1 ;Z um 1 erhöhen, nächstes Byte
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
breq endeTon ;springe zu "endeTon:", wenn letztes Byte ausgelesen
rjmp main ;wieder zurück zur "main:"

;Wenn alle Töne geladen sind, dann wird das Register "zaehlerTon"
;auf 255 gesetzt
endeTon:
cpi zaehlerSek, 0b00010100 ;wenn ZählerSek = 40 ist
breq TonAus ;springe zu Tonaus
rjmp main ;wieder zurück zu "main:"

TonAus:
sbiw ZL,1 ;Z um 1 erniedrigen
ldi tmp, 0 ;Timer mit 0 laden, damit er nicht mehr abspielt
out TCNT0, tmp
rjmp main ;wieder zurück zu "main:"

;Wird aufgerufen, wenn Timer0 überläuft (Timer für Ton)
;Hier wird geprüft, ob an B.2 HIGH oder LOW anliegt
;und dementsprechend umgesetzt
timerSummer:
push tmp ;tmp sichern
in tmp, SREG ;SREG sichern
push tmp
sbis PINB, Summer ;überspringe, wenn B.2 = 1 ist
rjmp timerSummer1 ;wenn B.2 = 0 ist, dann spring zu "umschalten1:"
cbi PORTB, Summer ;wenn B.2 = 1 ist, dann B.2 auf 0 setzen
rjmp timerSummer2 ;zu "timerSummer2:" springen

timerSummer1:
sbi PORTB, Summer ;wenn B.2 = 0 ist, dann auf 1 setzen

;Hier wird Timer0 mit dem aktuellen Tonwert vorgeladen
timerSummer2:
out TCNT0, tonwert ;Timer dementsprechen vorladen
rjmp timerSummer4 ;zu "timerSummer4:" springen

timerSummer4:
pop tmp ;SREG wiederholen
out SREG, tmp
pop tmp ;tmp wiederholen
reti


;Das sind die Werte, womit der Timer0 (Tonleiter-Timer) vorgeladen wird
tonleiter1:
.db 256-117, 256-16, 256-2, 0 ;Werte zum Vorladen des Timers für die Töne
;c', a' und c''
Es kommt nichts gescheites bei raus. Er spielt irgendwie die Töne ab, aber immer noch nicht im 1 Sekundenabstand. Außerdem ist da noch ein Ton drinne, der da nicht reinsoll. :(

Florian
13.08.2005, 22:24
Wie programmierst Du dann deine Programme?
Du armer Programmierer! *lol*
Ich finde den Simulator sehr hilfreich, aber hauptsächlich bei großen, vielverzweigten Programmen!

Normalerweise habe ich bei der Größe der Codes, wie hier, keine Probleme, aber irgendwie will mein Gehirn nicht! ;o)

Florian
13.08.2005, 22:31
.equ time0 = 256-255 ;Timer0 für die Tonleiter
.equ time2 = 256-90 ;Damit wird der Timer2 vorgeladen, für die SekundeMuss das nicht normalerweise von 255 abgezogen werden? *irgendwie klappts heute mit meiner Rechenleistung nicht mehr*

izaseba
13.08.2005, 22:33
Thomas, jetzt ganz langsam,
Du hast ein chaos im Deinem Programm, da blikt wirklich kein Mensch meh durch....

z.B. hier:

pruefTonleiter:
clr zaehlerSek ;Zähler auf 0 setzen
rjmp ladeTon ;nächsten Ton laden
rjmp main ;sonst wieder zurück zu "main:"

wozu springst Du nach ladeTon,könnte das nicht direkt drunter ?
und rjmp main
Da kommt er wohl nie hin , oder ?

oder das :

TonAus:
sbiw ZL,1 ;Z um 1 erniedrigen
ldi tmp, 0 ;Timer mit 0 laden, damit er nicht mehr abspielt
out TCNT0, tmp
rjmp main ;wieder zurück zu "main:"

wozu Zl -1 ?
bist Du Dir sicher daß 0 den Timer wirklich abschaltet?

@Florian, wie gesagt, ich klage nicht, es gibt andere Wege Programme wirksam zu debuggen!
Und es gibt echt geile Editoren , z.B. Emacs geht in der Konsole, und ist das Schweizer Taschenmesser unter den Editoren.

toeoe
13.08.2005, 22:38
Von Seite 5:




bedenke Der Zähler läuft von dem Wert was Du vorlädst bis 255 dann kommt der Überlauf.

mithin

.equ time = 255 - 90

255 bleibt immer gleich weil das ja die Schwelle ist wo er überläuft.....

Gruß Sebastian

Sicher?
Der Zähler Interrupt kommt doch erst in dem Moment, wenn der Zähler überläuft. Das Interrupt-Flag ist ja so was wie ein neuntes Bit, das allerdings nur gesetzt wird.
Da der Interrupt ausgelöst wird, wenn der Zähler auf 0 springt, muss es doch heissen.

.equ timer = 256 - 90

Gruß, Georg-Johann

izaseba
13.08.2005, 22:41
Nein, 256 ist schon richtig, der Sprinter hat das irgendwo 10 Seiten zurück mal gesagt,

es geht sich ja um den Punkt, wo der Timer überläuft, und nicht, wo er
seinen höchsten Wert erreicht hat.

Edit: Danke Thomas, sehr aufmerksam

toeoe
13.08.2005, 22:44
Thomas, jetzt ganz langsam,
Du hast ein chaos im Deinem Programm, da blikt wirklich kein Mensch meh durch....
Sorry :(



pruefTonleiter:
clr zaehlerSek ;Zähler auf 0 setzen
rjmp ladeTon ;nächsten Ton laden
rjmp main ;sonst wieder zurück zu "main:"

wozu springst Du nach ladeTon,könnte das nicht direkt drunter ?
und rjmp main
Da kommt er wohl nie hin , oder ?

Ja, hast recht, hab ich grad verbessert. Aber ich denk, die Hauptsache ist doch erstmal dass es funktioniert und dann kann man an der Übersicht arbeiten, oder?
In C++ hatte ich nie Probleme mit der Übersicht, versteh ich nicht...



TonAus:
sbiw ZL,1 ;Z um 1 erniedrigen
ldi tmp, 0 ;Timer mit 0 laden, damit er nicht mehr abspielt
out TCNT0, tmp
rjmp main ;wieder zurück zu "main:"

wozu Zl -1 ?
bist Du Dir sicher daß 0 den Timer wirklich abschaltet?
Das ZL-1 hab ich gemacht, damit er beim nächsten mal keinen Müll mehr einliest, sondern immer wieder die 0 aus der DB tonleiter.

Und das 0 den Timer abschaltet, dacht ich, jo, denn was soll er denn noch hochzählen, wenn ich den Timer mit 256-256 vorlade? Hmm...

[edit]
Das hier stoppt den Timer.

ldi tmp, 0b00000000 ;Timer stoppen
out TCCR0, tmp
Datenblatt Seite 70 hilft.

toeoe
13.08.2005, 23:22
Hier nochmal der ganze Code, habs mal auf 2 Sekunden geändert, aber der spielt total verrückt :(

;Programm
;CDurTonleiter rauf und runter spielen
.include "m8def.inc"

.def tmp = r16 ;Mein Universallregister
.def zaehlerSek = r17 ;Mein Zählregister
.def tonwert = r19 ;aktueller Wert für den Ton
.def lpm_reg = r0 ;Mein lpm-Register

.equ Summer = PB2 ;Summer an B.2
.equ time0 = 256-255 ;Timer0 für die Tonleiter
.equ time2 = 256-90 ;Damit wird der Timer2 vorgeladen, für die Sekunde
.equ daten_laenge = 3 ;Anzahl der Werte

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF2addr
rjmp pruefSek ;Interruptvektor "pruefSek:"

.org OVF0addr
rjmp timerSummer ;Interruptvektor "timerSummer:"

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;Low-Byte der obersten RAM-Adresse
out SPL, tmp

;Timer Register für Ton werden belegt, hier Timer 0
ldi tmp, (1<<CS02) ;Prescaler ist 512
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, time0 ;Hier wird der Timer vorgeladen
out TCNT0, tmp

;Timer Register für Sekunde werden belegt, hier Timer 2
ldi tmp, (1<<CS22) | (1<<CS21) | (1<<CS20) ;Prescaler ist 1024
out TCCR2, tmp ;Register TCRR2 ist für den Prescaller zuständig


ldi tmp, (1<<TOIE0) | (1<<TOIE2);Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

;Z-Register mit daten1 füllen
ldi ZH, HIGH(tonleiter1 * 2)
ldi ZL, LOW(tonleiter1 * 2)

ldi zaehlerSek, 0b00000000 ;ZählerSek auf 0 setzen

sbi DDRB, Summer ;B.2 als Ausgang
sbi PORTB, Summer ;B.2 auf HIGH stellen

sei ;Interrupts zulassen

;Die Hauptschleife
main:
cpi zaehlerSek, 0b01010000 ;wenn ZählerSek != 80 ist
brne main ;dann immer wieder zu "main:" springen

;Wenn eine Sekunde um ist, dann springe hier rein
;und setze zaehlerSek wieder zurück auf 0 und
;lade den nächsten Ton
pruefTonleiter:
clr zaehlerSek ;Zähler auf 0 setzen

;Wenn 1 Sekunde vorbei ist und es noch einen Ton gibt,
;der noch nicht geladen ist dann springe hier hin und
;lade den nächsten Wert von der Datenbank "tonleiter"
ladeton:
lpm ;Daten von tonleiter1: holen
mov tonwert, lpm_reg ;erstes Byte in tmp verschieben
adiw ZL,1 ;Z um 1 erhöhen, nächstes Byte
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
breq endeTon ;springe zu "endeTon:", wenn letztes Byte ausgelesen
rjmp main ;wieder zurück zur "main:"

;Wenn alle Töne geladen sind, dann wird das Register "zaehlerTon"
;auf 255 gesetzt
endeTon:
cpi zaehlerSek, 0b01010000 ;wenn ZählerSek = 80 ist
breq TonAus ;springe zu Tonaus
rjmp main ;wieder zurück zu "main:"

TonAus:
ldi tmp, (1<<CS02) ;Timer stoppen
out TCCR0, tmp
rjmp main ;wieder zurück zu "main:"

;Wird aufgerufen, wenn Timer2 überläuft (Timer für Sekunde)
;Hier wird das Zählregister für den Timer um 1 erhöht und der
;Timer neu geladen
pruefSek:
push tmp ;tmp sichern
in tmp, SREG ;SREG sichern
push tmp
inc zaehlerSek ;ZählerSek um 1 erhöhen
ldi tmp, time2 ;Hier wird der Timer vorgeladen
out TCNT2, tmp
pop tmp ;SREG wiederholen
out SREG, tmp
pop tmp ;tmp wiederholen
reti ;wieder dahin, wo du hergekommen bist

;Wird aufgerufen, wenn Timer0 überläuft (Timer für Ton)
;Hier wird geprüft, ob an B.2 HIGH oder LOW anliegt
;und dementsprechend umgesetzt
timerSummer:
push tmp ;tmp sichern
in tmp, SREG ;SREG sichern
push tmp
sbis PINB, Summer ;überspringe, wenn B.2 = 1 ist
rjmp timerSummer1 ;wenn B.2 = 0 ist, dann spring zu "umschalten1:"
cbi PORTB, Summer ;wenn B.2 = 1 ist, dann B.2 auf 0 setzen
rjmp timerSummer2 ;zu "timerSummer2:" springen

timerSummer1:
sbi PORTB, Summer ;wenn B.2 = 0 ist, dann auf 1 setzen

;Hier wird Timer0 mit dem aktuellen Tonwert vorgeladen
timerSummer2:
out TCNT0, tonwert ;Timer dementsprechen vorladen
rjmp timerSummer4 ;zu "timerSummer4:" springen

timerSummer4:
pop tmp ;SREG wiederholen
out SREG, tmp
pop tmp ;tmp wiederholen
reti


;Das sind die Werte, womit der Timer0 (Tonleiter-Timer) vorgeladen wird
tonleiter1:
.db 256-117, 256-16, 256-2, 0 ;Werte zum Vorladen des Timers für die Töne
;c', a' und c''
Weiß echt nicht, was ich da noch machen soll :(

izaseba
13.08.2005, 23:24
In C++ hatte ich nie Probleme mit der Übersicht, versteh ich nicht...


Ich habe Dir gesagt, Du darfst nicht in Kategorien der Hochsprachen denken.



Sorry Sad

Wieso wieder Traurig,
Es war nicht böse gemeint, sondern die springerei hin und her trägt nicht gerade der Übersicht bei.


Ja, hast recht, hab ich grad verbessert. Aber ich denk, die Hauptsache ist doch erstmal dass es funktioniert und dann kann man an der Übersicht arbeiten, oder?

Hmmmm, ich glaube eher weniger, wer rührt schon ein funktionierendes Programm an?

P.S. Warst Du das mit dem Edit:

toeoe
13.08.2005, 23:27
Ja, das war ich, aber ich hab den Code geändert, aber beides stoppt leider nicht den Timer.

Ich weiß ja, dass du es nicht böse meinst, aber man ist nunmal traurig bzw. mehr enttäuscht, wenn man einfach nicht mehr weiß, was man verkehrt macht und es trotzdem nicht funktioniert und dann hat man doch die Angst, dass man es nimmer zum laufen bringt.

Florian
13.08.2005, 23:29
wie sag ichs meinem avr.pdfDas habe ich vor 10 Seiten schonmal angesprochen, das ist sehr gut!
Das ist dann aber schon die zweite Version, die erste Version ist auch sehr interessant!

izaseba
13.08.2005, 23:31
Thomas, geh doch mal punkt für Punkt alles durch, so wie gestern mit dem tatü-tata.
Wenn es sein muß schmeiß Dein Programm weg, und fang neu an, geh auf den Balkon,
Veranstallte was mit Deiner Freundin, krieg wieder klaren Kopf.
Dann fang neu an, Du schaffst es, ich weiß, hier erwartet keiner was von Dir, es ist nicht leicht,Du hast heute jedemenge neue Sachen kennengelernt, wenn Du dann immer nicht weiterkommst dann gehe ich nochmal alles mit Dir durch, kein Problem

toeoe
13.08.2005, 23:38
Ok, ich denk, das ist das beste. Ich werd jetzt schlafen gehen und morgen früh aufstehen und mich ransetzen.
Danke für eure Unterstützung :)

Gute Nacht und Gruß
Thomas

izaseba
13.08.2005, 23:41
Das habe ich vor 10 Seiten schonmal angesprochen, das ist sehr gut!
In der Tat, das hast Du :oops:
Die Datei ist mir gerade so "vor die Füße gelaufen" als ich ein Dattenblatt gesucht habe,
und dann dachte ich mir, warum soll ich sie hier nicht posten...

Florian
13.08.2005, 23:41
Gute Nacht!
Morgen wird alles wieder gut! ;o)

michaelb
14.08.2005, 09:59
Guten Morgen,
hab zwei Fragen:
Was bringt das?:


; Reset and Interrupt vector ; VNr. Beschreibung
rjmp main ; 1 POWER ON RESET
reti ; 2 Int0-Interrupt
reti ; 3 Int1-Interrupt
reti ; 4 TC2 Compare Match
reti ; 5 TC2 Overflow
reti ; 6 TC1 Capture
reti ; 7 TC1 Compare Match A
reti ; 8 TC1 Compare Match B
reti ; 9 TC1 Overflow
reti ; 10 TC0 Overflow
reti ; 11 SPI, STC Serial Transfer Complete
reti ; 12 UART Rx Complete
reti ; 13 UART Data Register Empty
reti ; 14 UART Tx Complete
reti ; 15 ADC Conversion Complete
reti ; 16 EEPROM Ready
reti ; 17 Analog Comparator
reti ; 18 TWI (I²C) Serial Interface
reti ; 19 Store Program Memory Ready

und das?:


ldi temp, LOW(RAMEND) ;LOW-Byte der obersten RAM-Adresse
out SPL, temp
ldi temp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, temp

Gruß Michi

Florian
14.08.2005, 10:03
Guten morgen Michael!
Das Erste ist die sogenannte Interruptvektortabelle, schau Dir das ganze am Besten mal bei http://www.mikrocontroller.net/tutorial/interrupts an!
Das Zweite ist die Konfigurierung bzw. aktivierung des sogenannten Stacks! http://www.mikrocontroller.net/tutorial/stack

michaelb
14.08.2005, 10:06
Hi Florian,
was bringt das ganze denn das erste wird hier in diesem Thread immer weggelassen! Und das zweite scheint ja auch nicht nötig zu sein denn mein Programm funktioniert auch ohne diesen Teil!
Gruß Michi

Tekeli
14.08.2005, 10:08
Moin moin,

@Masters
Danke fuer die Pdf's. Die sind wirklich sehr gut!
Und danke für den Bug-Hinweis.
Um AVRStudio zu benutzen, kann ja Sebastian unter Linux VMware einsetzen. Das funktioniert sehr gut!

@Thomas
siehe mal, was man hier so alles findet :)
https://www.roboternetz.de/phpBB2/dload.php?action=file&file_id=169

Und hier ist die bereinigte Version. Ich schalte die beiden Timer am Ende aus, damit der uC auch in der Looping-Schleife bleibt.

.include "m8def.inc"

;; Wir wollen mit dem Timer2 einen Interrupt pro Sekunde ausloesen.
;; Asuro hat einen 8MHz Quarz. Wir waehlen eine Prescaler von 1024 aus( 8, 32, 64, 128, 256, 1024)
;; 8MHz/1024 = 8000000Hz/1024 = 7812,5 Hz die Timerfrequenz.
;; -> ein Timerschritt dauert 1/7812,5 = 0,000128s = 128us.
;; -> Fuer eine Sekunde muessten also 1s/128us = 7812 Timerschritte ausgef?hrt werden.
;; Da das nur ein 8-bit Timer ist, macht er 256 Schritte zum Ueberlauf.
;; -> Des wegen barauchen wir eine Hilfsvariable,
;; um speichern zu koennen , wieviele Ueberlaeufe er schon hinter sich hat.
;; -> 7812 = 36 * 217
;; -> 36 mal soll der Timer bis 217 Zahlen -> dann ist eine Sekunde vergangen
.EQU TIMER2 = 256 - 217
.EQU counts2 = 36

.EQU FRONT_LED = PD6 ; Dran haengt auch ein Summer :)

.DEF erg = R0 ; Das Register wird vom LPM-Befehl benutzt
.DEF tmp = R16 ; Fuer algemeine Zwecke
.DEF counter2 = R17 ; Hier speichern wir, wieviele Ueberlaufe der Timer2 schon hatte
.DEF frequence = R18 ; Hier wird die aktuelle Frequenz des Summers gespeichert


.CSEG
.ORG 0x000
RJMP RESET ; Interruptvektor reset

.ORG OVF2addr ; Interruptvektor fuer Timer2 Ueberlauf.
RJMP TIM2_OVR ; Dahin wird gesprungen, wenn der Timer2 ueberlaeuft

.ORG OVF0addr ; Interruptvektor fuer Timer0 Ueberlauf.
RJMP TIM0_OVR ; Dahin wird gesprungen, wenn der Timer0 ueberlaeuft


RESET:
;; Initializierung des Stackpointers
LDI tmp, LOW(RAMEND) ; LOW-Byte der obersten RAM-Adresse
OUT SPL, tmp
LDI tmp, HIGH(RAMEND) ; HIGH-Byte der obersten RAM-Adresse
OUT SPH, tmp


SBI DDRD,FRONT_LED ; Der Pin an dem die FrontLed und jetzt der Summer haengen
; als Ausgang definieren

LDI frequence, 255 ; Die zahl mit dem der Counter vom Timer0 geladen wird

;; Timer2 Register werden belegt
;; Es wird Timer2 benutzt
LDI tmp,(1<<CS22)|(1<<CS21)|(1<<CS20) ; Prescaller ist 1024. Also Lade (0000 0100 V 0000 0010 V 0000 0001) = 0000 0111 in tmp
OUT TCCR2,tmp ; Register TCCR2 ist fuer den Prescaller zustaendig (siehe Datenblatt)
LDI tmp,TIMER2 ; Hier wird der Timer2 vorgeladen und zwar mit TIMER2. Siehe oben.
OUT TCNT2,tmp ; Er laeuft counts2 mal durch bevor ein Interrupt auftritt. Siehe CPI- Vergleich in main

;; Timer0 Register werden belegt
;; Es wird Timer0 benutzt
LDI tmp,(1<<CS01) | (1<<CS00) ; Prescaller ist 64. Also Lade (0000 0010 V 0000 0001) = 0000 00011 in tmp
OUT TCCR0,tmp ; Register TCCR0 ist fuer den Prescaller zustaendig (siehe Datenblatt)

OUT TCNT0,frequence ; Counter vom Timer0 laden.

LDI tmp,(1<<TOIE2) | (1<<TOIE0) ; Hier werden Interrupts fuer einen Ueberlauf von Timer2 bzw. Timer0 eingeschaltet
OUT TIMSK,tmp ; Register TIMSK ist dafuer zustaendig

LDI ZH,HIGH(2*toene) ; Lade die Adresse unserer Daten in das z-Register
LDI ZL,LOW(2*toene) ; "2*" - es ist so :)

SEI ; Interrupts allgemein zulassen

main:
CPI counter2, counts2 ; Vergleiche ob der Timer2 schon counts2 mal durchgelaufen hat
BREQ switch_frequence ; Wenn ja, gehe zu switch_frequnce
RJMP main ; sonts Looping

switch_frequence:
CLR counter2 ; Counter2 auf 0 setzen. Der Timer2 soll wieder counts2 mal durchlaufen

LPM ; Lese ein Byte aus dem Programmspeicher - Naechster Tonewert
TST erg ; R0 auf 0 testen
BREQ ende ; Ein ja bedeutet, dass wir am Ende sind -> Ende

LDI frequence, 255 ; Kein Kommentar
SUB frequence, erg ; Rechne den entsprechenden Wert fuer den Counter
OUT TCNT0, frequence ; Beschreibe den Counter von Time0 mit dem Wert

ADIW ZL,1 ; Pointer auf das naechste Byte
RJMP main ; Kehre zurueck zum main

ende:
LDI tmp,(0<<CS02)|(0<<CS01)|(0<<CS00) ; Den Timer0 ausschalten
OUT TCCR0,tmp ; Register TCCR0 ist dafuer zustaendig (siehe Datenblatt)
LDI tmp,(0<<CS22)|(0<<CS21)|(0<<CS20) ; Den Timer2 ausschalten
OUT TCCR2,tmp ; Register TCCR2 ist dafuer zustaendig (siehe Datenblatt)

looping:
RJMP looping ; Die unendliche Geschichte :)

TIM2_OVR:
PUSH tmp ; Benutztes Register auf den Stack
IN tmp, SREG ; Zustand des Statusregisters einlesen
PUSH tmp ; Ebenfalls auf den Stack ablegen

INC counter2 ; Der Timer2 hat wieder mal einen Ueberlauf

LDI tmp, TIMER2 ; Kein Kommentar
OUT TCNT2, tmp ; Conuter vom Timer2 erneut laden

POP tmp ; Alten SREG-Inhalt vom Stapel holen
OUT SREG, tmp ; und das Statusregister wiederherstellen
POP tmp ; tmp wiederherstellen
RETI ; Setzte das durch den Interrupt unterbrochenes Programm fort


TIM0_OVR:
PUSH tmp ; Benutztes Register auf den Stack
IN tmp, SREG ; Zustand des Statusregisters einlesen
PUSH tmp ; Ebenfalls auf den Stack ablegen

SBIC PIND,FRONT_LED ; Was ist auf dem PIND6?
RJMP off ; Wenn 1, dann schalte auf 0
RJMP on ; Wenn 0, dann schalte auf 1
off:
CBI PORTD,FRONT_LED ; schalte auf 0
RJMP exit ; gehe zu exit
on:
SBI PORTD,FRONT_LED ; schalte auf 1
exit:
OUT TCNT0,frequence ; Conuter vom Timer0 erneut laden

POP tmp ; Alten SREG-Inhalt vom Stapel holen
OUT SREG, tmp ; und das Statusregister wiederherstellen
POP tmp ; tmp wiederherstellen
RETI ; Setzte das durch den Interrupt unterbrochenes Programm fort

toene:
.DB 213, 234, 150, 34, 123, 45, 123, 100, 80, 200, 214, 0 ; ich hatte keine Lust die richtigen Tonwerte auszurechenn :)

So, wann kommt die naechste Aufgabe? :)
Best wishes

Florian
14.08.2005, 10:10
Hallo Michael!
Wir haben uns hier auf die Kurzschreibweise geeinigt:

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF2addr
rjmp pruefSek ;Interruptvektor "pruefSek:"

.org OVF0addr
rjmp timerSummer ;Interruptvektor "timerSummer:"

Es kann sein, dass es auch ohne den Stack funktioniert, solange Du keine Sprünge mit rcall o.ä. machst!
Lies Dir die oben beschriebenen Artikel durch und frag was Du nicht verstehst! *aber erst lesen -> intensiv*

Florian
14.08.2005, 10:12
Hallo Tekeli!
Wir sind ja noch nicht fertig! *lol*
ich bitte um ein wenig Gedult! ;o)

toeoe
14.08.2005, 10:36
@Thomas
siehe mal, was man hier so alles findet :)
https://www.roboternetz.de/phpBB2/dload.php?action=file&file_id=169
Der erzeugt ja Basic-Code ;)


So, wann kommt die naechste Aufgabe? :)

Bitte wartet auf mich...

toeoe
14.08.2005, 11:31
Es geht :)
Er spielt die 3 Töne hintereinander ab (immer 1 Sekunde) und dann hört er auf.
Hier der Code:

;Programm
;CDurTonleiter rauf und runter spielen
.include "m8def.inc"

.def tmp = r16 ;Mein Universallregister
.def zaehlerSek = r17 ;Mein Zählregister
.def tonwert = r19 ;aktueller Wert für den Ton
.def lpm_reg = r0 ;Mein lpm-Register

.equ Summer = PB2 ;Summer an B.2
.equ time0 = 256-255 ;Timer0 für die Tonleiter
.equ time2 = 256-90 ;Damit wird der Timer2 vorgeladen, für die Sekunde
.equ daten_laenge = 4 ;Anzahl der Werte

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF2addr
rjmp pruefSek ;Interruptvektor "pruefSek:"

.org OVF0addr
rjmp timerSummer ;Interruptvektor "timerSummer:"

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;Low-Byte der obersten RAM-Adresse
out SPL, tmp

;Timer Register für Ton werden belegt, hier Timer 0
ldi tmp, (1<<CS02) ;Prescaler ist 512
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, time0 ;Hier wird der Timer vorgeladen
out TCNT0, tmp

;Timer Register für Sekunde werden belegt, hier Timer 2
ldi tmp, (1<<CS22) | (1<<CS21) | (1<<CS20) ;Prescaler ist 1024
out TCCR2, tmp ;Register TCRR2 ist für den Prescaller zuständig


ldi tmp, (1<<TOIE0) | (1<<TOIE2);Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

;Z-Register mit DB "tonleiter1" füllen
ldi ZH, HIGH(tonleiter1 * 2)
ldi ZL, LOW(tonleiter1 * 2)

sbi DDRB, Summer ;B.2 als Ausgang
sbi PORTB, Summer ;B.2 auf HIGH stellen

sei ;Interrupts zulassen

;Hier wird der nächste Ton geladen und in "tonwert" gespeichert
;Z-Zeiger wird um 1 erhöht, damit er beim nächsten mal den nächsten
;Ton lädt. Es wird hier auch verglichen,, ob der letzte Ton erreicht,
;wenn ja, dann springt er zu "endeTon"
tonLaden:
clr zaehlerSek ;ZählerSek auf 0 setzen
lpm ;Daten von tonleiter1: holen
mov tonwert, lpm_reg ;erstes Byte in tmp verschieben
adiw ZL,1 ;Z um 1 erhöhen, nächstes Byte
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
breq endeTon ;springe zu "endeTon:", wenn letztes Byte ausgelesen
rjmp main ;sonst springe zu "main:"

;Hier wird der Timer gestoppt, indem wir den Prescaler auf 0 setzen
endeTon:
ldi tmp, (0<<CS02) ;Timer stoppen
out TCCR0, tmp

;Die Hauptschleife, die sich immer wiederholt
main:
cpi zaehlerSek, 0b00101000 ;ist zaehlerSek = 40 (also 1 Sekunde um?)
breq tonLaden ;wenn ja, dann lade den nächsten Ton
rjmp main ;immer wieder zurück zu main springen

;Läuft Timer2 über, so wieder zaehlerSek um 1 erhöht und
;Timer2 neu vorgeladen
pruefSek:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
inc zaehlerSek ;ZählerSek um 1 erhöhen
ldi tmp, time2 ;Hier wird der Timer vorgeladen
out TCNT2, tmp
pop tmp
out SREG, tmp ;SREG wiederholen
pop tmp ;tmp wiederholen
reti ;Spring wieder dahin, wo du hergekommen bist

;Läuft Timer0 über, so wird B.2 umgeschaltet, sodass Ton
;aus dem Summer zu hören ist
timerSummer:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
sbis PINB, Summer ;ist B.2 = 1?
rjmp timerSummer1 ;NEIN -> spring zu timerSummer1:"
cbi PORTB, Summer ;JA -> setze B.2 auf 0
rjmp timerSummer2 ;zu "timerSummer2:" springen

timerSummer1:
sbi PORTB, Summer ;wenn B.2 = 0 ist, dann auf 1 setzen

;Hier wird Timer0 mit dem aktuellen Tonwert vorgeladen
timerSummer2:
out TCNT0, tonwert ;Timer dementsprechen vorladen

timerSummer3:
pop tmp ;SREG wiederholen
out SREG, tmp
pop tmp ;tmp wiederholen
reti

;Das sind die Werte, womit der Timer0 (Tonleiter-Timer) vorgeladen wird
tonleiter1:
.db 256-117, 256-16, 256-2, 0 ;Werte zum Vorladen des Timers für die Töne
;c', a' und c''

Ich hoffe, er ist nicht wieder so unübersichtlich.

Musste aber "daten_laenge" auf 4 stellen, da er sonst nach dem 2. Ton schon aufgehört hätte. Liegt also daran, wie man das programmiert, ob man da nun 3 oder 4 stehen hat. Bei Tekeli hab ich gar keine daten_laenge mehr gesehen, hast es wohl ganz anders gemacht, oder?
So, das Label "tonLaden:" steht vor der main, da er da zuerst hingehen muss, um den ersten Ton zu laden. Ich hoffe, das ist ok so.

Also dann schreibt mal bitte eure Meinungen zu meinem Code.

Sorry nochmals, dass ich uns aufgehalten habe.

Tekeli: Darf ich mal fragen, wie lang du schon in Assembler programmierst?

Gruß
Thomas

Florian
14.08.2005, 11:37
Hallo Thomas!
Herzlichen Glückwunsch zum Code!
Ich habe leider gerade garkeine Zeit, muss noch n bischen hier arbeiten, ich schaue mir das anchher mal an!
Vielleicht kommt ja Sebastian bald online und arbeitet mit euch weiter!? *hoff*

Viel Erfolg!


Ps:
Versucht das ganze doch mal auf die ganze Tonleiter zu erwitern!
c' - c'' -> ohne Halbtöne ;o)

toeoe
14.08.2005, 11:43
Ok, das ist denk ich erstmal ne leichtere Aufgabe ;)
Nur die DB anpassen und die daten-laenge.

Gruß
Thomas

PS: Kann man das hier:
.db 265-28, 256-16......, 0
auch irgendwie mit nem Zeilenumbruch dazwischen schreiben?
Denn sonst wirds bei zu vielen Tönen ja zu lang - die Zeile.

Florian
14.08.2005, 11:46
PS: Kann man das hier:
.db 265-28, 256-16......, 0
auch irgendwie mit nem Zeilenumbruch dazwischen schreiben?
Denn sonst wirds bei zu vielen Tönen ja zu lang - die Zeile.Ja, das kannst Du, soweit ich weiß machen, das ist aber die elegantere Variante:
.db a , b
.db c , d
.db e , f
...........

toeoe
14.08.2005, 12:07
Ahh, ok danke.
Schläft Sebastian noch? ;) *g*

[edit]
So, ich mach mal eben den Braten, der braucht ja sicher ne Zeit im Ofen ;)
Is ja heut eh nicht viel los hier mit Unterricht *schnüff* Manch anderer würde sich über Unterrichtsfrei freuen *g*

izaseba
14.08.2005, 13:33
Hallo Leute,
Nein ich schlafe schon lange nicht mehr, bin aber noch nicht dazugekommen Euch
zu begüßen.

Ihr habt beide andere Wege genommen um die Aufgabe zu lösen.
Beide haben vor und Nachteile.
Es wäre wohl nicht schwer zu sagen welche vor und nachteile es sind...
Da hier irgendwie über langeweile geklagt wird, versucht den Timer 1, anstatt den Timer 2
zu nehmen, das ist ein 16 Bit Timer, und man kann damit locker eine Sekunde in einem "rutsch" Programmieren, damit wäre der Register, wo Ihr den Timer 2 zählt wohl überflüssig.
der Timer 1 hat auch viele "neue" register, wir brauchen aber nur die standardeinstellungen, also prescaler, timsk und jetzt achtung, wer hätte es gedacht zwei Register zum Vorladen.

die benutzt man z.B. so:
.equ time = 10000

ldi tmp,HIGH(time)
out irgendwas,tmp
ldi tmp,LOW(time)
out irgendwas,tmp

Ja ich hoffe, daß es nicht zu schwer ist, und den Umgang mit 16-Bit Zahlen etwas näher bringt.

Gruß Sebastian

michaelb
14.08.2005, 13:34
Hallo Zusammen,
hab mich in der Zwischenzeit mehr in Assembler eingearbeitet! Hab ne Frage da ich mich gerade mit Timer auseinandersetze hat jemand ein kleines Programm das eine LED im Sekundentakt blinken lässt?
Gruß Michi

izaseba
14.08.2005, 13:36
Hallo Michi,
Du hattest keine Lust die 18 Seiten bis jetzt durchzublätern, was?
kann ich verstehen, aber das Programm findest Du hier irgendwo, wir haben eine LED blinken lassen!

Gruß Sebastian

michaelb
14.08.2005, 13:51
Hi,
ok ich kämpf mich durch!!
Gruß Michi

izaseba
14.08.2005, 13:52
Ein kleiner Nachtrag zu Timer1

es muß natürlich heißen
.equ time = 65536 -10000

:oops:

michaelb
14.08.2005, 14:04
Juhu meine LED blinkt! Mann ist das geil :D :D
Jetzt versuch ich mal das ganze zu verstehen!
Gruß Michi

izaseba
14.08.2005, 14:05
Da der Michi, das Problem mit Stack angesprochen hat, von wegen ohne seine
Initialisierung wollte ich hier auch mal dazu sagen.

Klar geht es ohne, aber sobald man die Befehle rcall, pop und push benutzt (hat schon Florian angesprochen) gibt es Probleme.
Genauso mit Interrupts, dabei wird auf dem Stack die Rücksprung Adresse abgelegt.
Und wenn wir nicht deklarieren, wo das ende von Sram liegt, bleiben die SPL und SPH adressen leer, also 0x00.
Was passiert dann, wenn man ungewollt(z.B. ein Interrupt) irgendwas auf dem Stack ablegt?
Sram von Adresse 0x00 bis 0x60(Mega8) ist für alle Register R0-R31 und alle anderen Register reserviert (sehe Dattenblatt Seite 16) damit überschreiben wir uns den Inhalt von R0 . Da der Stack aber rückwärts geht also von hinten nach vorne, wird er mit sbiw vom µC
um 1 verringert und wo landen wir wenn 0x000 -1 gerechnet wird?

Ich hoffe, daß hier auch ein Schlaumeier ist, der diese Ausführung bestätigen könnte.

Gruß Sebastian

michaelb
14.08.2005, 14:08
Hallo hab jetzt ne Frage:
Warum wird TCNT0 mit 255-254 geladen? 255-254 gibt doch 1!?
Gruß Michi

michaelb
14.08.2005, 14:12
Ich seh gerade das Tutorial ist bei der Statistik auf Rang 2 gestiegen!
Glückwunsch
an alle die hier so kräftig helfen!
Gruß Michi

toeoe
14.08.2005, 14:15
Hallo hab jetzt ne Frage:
Warum wird TCNT0 mit 255-254 geladen? 255-254 gibt doch 1!?
Gruß Michi

Wir hatten doch hier im Thread eine Formel:
1/ 3,6864 Mhz = 271ns

271ns * 1024 (prescaler) = 278µs

278µs * 90 (ausprobierter Wert) = 25ms

Wir wollen ja 1 Sekunde, erreichen, deswegen suche ich einen Wert, der gut mit einer Zahl multipliziert werden kann, sodass dann 1 Sekunde = 1000ms rauskommt.

25ms * 40 = 1 Sekunde oder 1000ms

Also muss du deinen Timer mit 256-90 vorladen (warum 256? --> ein paar Post wurde das nochmal erklärt)
und diesen Timer musst du dann 40x überlaufen lassen, dann hast du 1 Sekunde erreicht.

Gruß
Thomas

toeoe
14.08.2005, 14:18
Ein kleiner Nachtrag zu Timer1

es muß natürlich heißen
.equ time = 65536 -10000

:oops:

Das stimmt auch nicht *glaub*
Denn dann ist es viel länger als 1 Sekunde. Meiner Meinung nach müsste es
.equ time = 65536-1000
heißen, aber dann ist es viel schneller als 1 Sekunde. Ein Teufelskreis ;)

Ich versuch da mal ne Formel zu bekommen.

Hier mal der Code mit Timer1:

;Programm
;CDurTonleiter rauf und runter spielen
.include "m8def.inc"

.def tmp = r16 ;Mein Universallregister
.def helpSek = r17
.def tonwert = r19 ;aktueller Wert für den Ton
.def lpm_reg = r0 ;Mein lpm-Register

.equ Summer = PB2 ;Summer an B.2
.equ time0 = 256-255 ;Timer0 für die Tonleiter
.equ time1 = 65536-1000 ;Damit wird der Timer1 vorgeladen, für die Sekunde
.equ daten_laenge = 9 ;Anzahl der Werte

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF1addr
rjmp pruefSek ;Interruptvektor "pruefSek:"

.org OVF0addr
rjmp timerSummer ;Interruptvektor "timerSummer:"

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;Low-Byte der obersten RAM-Adresse
out SPL, tmp

;Timer Register für Ton werden belegt, hier Timer 0
ldi tmp, (1<<CS02) ;Prescaler ist 256
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, time0 ;Hier wird der Timer vorgeladen
out TCNT0, tmp

;Timer Register für Sekunde werden belegt, hier Timer 1
ldi tmp, (1<<CS12) | (1<<CS10) ;Prescaler ist 1024
out TCCR1B, tmp
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde (10000) speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"

ldi tmp, (1<<TOIE0) | (1<<TOIE1);Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

;Z-Register mit DB "tonleiter1" füllen
ldi ZH, HIGH(tonleiter1 * 2)
ldi ZL, LOW(tonleiter1 * 2)

sbi DDRB, Summer ;B.2 als Ausgang
sbi PORTB, Summer ;B.2 auf HIGH stellen

sei ;Interrupts zulassen

;Hier wird der nächste Ton geladen und in "tonwert" gespeichert
;Z-Zeiger wird um 1 erhöht, damit er beim nächsten mal den nächsten
;Ton lädt. Es wird hier auch verglichen,, ob der letzte Ton erreicht,
;wenn ja, dann springt er zu "endeTon"
tonLaden:
clr helpSek ;helpSek auf 0 setzen
lpm ;Daten von tonleiter1: holen
mov tonwert, lpm_reg ;erstes Byte in tmp verschieben
adiw ZL,1 ;Z um 1 erhöhen, nächstes Byte
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
breq endeTon ;springe zu "endeTon:", wenn letztes Byte ausgelesen
rjmp main ;sonst springe zu "main:"

;Hier wird der Timer gestoppt, indem wir den Prescaler auf 0 setzen
endeTon:
ldi tmp, (0<<CS02) ;Timer stoppen
out TCCR0, tmp

;Die Hauptschleife, die sich immer wiederholt
main:
cpi helpSek, 0b11111111 ;Ist helpSek auf 255? (also 1 Sekunde um?)
breq tonLaden ;dann lade den nächsten Ton
rjmp main ;immer wieder zurück zu main springen

;Läuft Timer2 über, so wieder zaehlerSek um 1 erhöht und
;Timer2 neu vorgeladen
pruefSek:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
ldi helpSek, 0b11111111 ;Hilfsvariable mit 255 belegen
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde (10000) speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
pop tmp
out SREG, tmp ;SREG wiederholen
pop tmp ;tmp wiederholen
reti ;Spring wieder dahin, wo du hergekommen bist

;Läuft Timer0 über, so wird B.2 umgeschaltet, sodass Ton
;aus dem Summer zu hören ist
timerSummer:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
sbis PINB, Summer ;ist B.2 = 1?
rjmp timerSummer1 ;NEIN -> spring zu timerSummer1:"
cbi PORTB, Summer ;JA -> setze B.2 auf 0
rjmp timerSummer2 ;zu "timerSummer2:" springen

timerSummer1:
sbi PORTB, Summer ;wenn B.2 = 0 ist, dann auf 1 setzen

;Hier wird Timer0 mit dem aktuellen Tonwert vorgeladen
timerSummer2:
out TCNT0, tonwert ;Timer dementsprechen vorladen

timerSummer3:
pop tmp ;SREG wiederholen
out SREG, tmp
pop tmp ;tmp wiederholen
reti

;Das sind die Werte, womit der Timer0 (Tonleiter-Timer) vorgeladen wird
tonleiter1:
.db 256-55, 256-49, 256-44, 256-41 ;Wert zum Vorladen für den Timer
.db 256-37, 256-33, 256-29, 256-27, 0 ;für die Tonleiter

Soweit funktioniert er ja. Aber wie man genau die Zeit festlegt ist noch ein Problem.

Achja...die Hilfsvariable brauchte ich, um festzustellen, ob schon eine Sekunde um ist, wusste sonst nicht, wie ich das ohne hätte lösen sollen.

michaelb
14.08.2005, 14:21
Hallo,
also kann mann statt 255-254 einfach 1 schreiben, oder?
Oder was bringen die schreibweisen mit 256-90 oder 255-254?
Gruß Michi

izaseba
14.08.2005, 14:22
Das stimmt auch nicht *glaub*

Klar stimmt das nicht, war nur als Beispiel ! :-)

toeoe
14.08.2005, 14:24
Vergiss einfach die 255, schreib immer 256 (Erklärun wie gesagt weiter oben).
Und ja, anstelle von 256-255 könnte man auch 1 schreiben. Aber ich denke, dass 256-255 die bessere Schreibweise, denn dann sieht man gleich, mit wieviel Bit man arbeitet.

Gruß
Thomas

michaelb
14.08.2005, 14:29
Hi,
ich hab's jetzt kapiert! Der Timer wird mit 1 vorgeladen damit er schon nach 254 Timerschritten nen Interrupt auslöst! <= ist das so oder? Sonst löst er doch nach 255 einen aus, oder?
Gruß Michi

toeoe
14.08.2005, 14:33
Er löst nach 256 den Interrupt erst aus, da er bei 255 noch nicht überläuft, deswegen ja auch die 256 (so, nun hast die Erklärung ;) )
Und wenn du schreibst: 256-255, dann lädst du den Timer also mit 1 vor. Also läuft er nach 255 Schritten über und löst den Interrupt aus.

@Sebastian:
.equ time 65536-3600
Denn
1/3,6864 MHz = 271ns

271ns * 1024 = 278µs

278µs * 3600 = 1000,8 ms

Wunderbar :)

izaseba
14.08.2005, 15:00
@Thomas, klappt es jetzt mit dem Timer1 ?
Ich meine Dein Musikprogramm?
@Tekeli,
Danke für den Tip mit VMware, aber ich glaube daß Programm unterliegt nicht der GPL
und Du kannst mich für bekloppt erklären, bei mit kommt nur opensource drauf

Gruß Sebastian

toeoe
14.08.2005, 15:03
@Sebastian:
Jo, klappt nun mit dem Timer1. Allerdings brauche ich immer noch eine Hilfsvariable, die ich beim Überlauf von Timer1 auf 255 setze und dann in der main prüfe und dementsprechend dann den nächsten Ton lade. Hier nochmal der Code:

;Programm
;CDurTonleiter rauf und runter spielen
.include "m8def.inc"

.def tmp = r16 ;Mein Universallregister
.def helpSek = r17
.def tonwert = r19 ;aktueller Wert für den Ton
.def lpm_reg = r0 ;Mein lpm-Register

.equ Summer = PB2 ;Summer an B.2
.equ time0 = 256-255 ;Timer0 für die Tonleiter
.equ time1 = 65536-3600 ;Damit wird der Timer1 vorgeladen, für die Sekunde
.equ daten_laenge = 9 ;Anzahl der Werte

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF1addr
rjmp pruefSek ;Interruptvektor "pruefSek:"

.org OVF0addr
rjmp timerSummer ;Interruptvektor "timerSummer:"

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;Low-Byte der obersten RAM-Adresse
out SPL, tmp

;Timer Register für Ton werden belegt, hier Timer 0
ldi tmp, (1<<CS02) ;Prescaler ist 256
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, time0 ;Hier wird der Timer vorgeladen
out TCNT0, tmp

;Timer Register für Sekunde werden belegt, hier Timer 1
ldi tmp, (1<<CS12) | (1<<CS10) ;Prescaler ist 1024
out TCCR1B, tmp
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde (10000) speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"

ldi tmp, (1<<TOIE0) | (1<<TOIE1);Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

;Z-Register mit DB "tonleiter1" füllen
ldi ZH, HIGH(tonleiter1 * 2)
ldi ZL, LOW(tonleiter1 * 2)

sbi DDRB, Summer ;B.2 als Ausgang
sbi PORTB, Summer ;B.2 auf HIGH stellen

sei ;Interrupts zulassen

;Hier wird der nächste Ton geladen und in "tonwert" gespeichert
;Z-Zeiger wird um 1 erhöht, damit er beim nächsten mal den nächsten
;Ton lädt. Es wird hier auch verglichen,, ob der letzte Ton erreicht,
;wenn ja, dann springt er zu "endeTon"
tonLaden:
clr helpSek ;helpSek auf 0 setzen
lpm ;Daten von tonleiter1: holen
mov tonwert, lpm_reg ;erstes Byte in tmp verschieben
adiw ZL,1 ;Z um 1 erhöhen, nächstes Byte
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
breq endeTon ;springe zu "endeTon:", wenn letztes Byte ausgelesen
rjmp main ;sonst springe zu "main:"

;Hier wird der Timer gestoppt, indem wir den Prescaler auf 0 setzen
endeTon:
ldi tmp, (0<<CS02) ;Timer stoppen
out TCCR0, tmp

;Die Hauptschleife, die sich immer wiederholt
main:
cpi helpSek, 0b11111111 ;Ist helpSek auf 255? (also 1 Sekunde um?)
breq tonLaden ;dann lade den nächsten Ton
rjmp main ;immer wieder zurück zu main springen

;Läuft Timer2 über, so wieder zaehlerSek um 1 erhöht und
;Timer2 neu vorgeladen
pruefSek:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
ldi helpSek, 0b11111111 ;Hilfsvariable mit 255 belegen
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
pop tmp
out SREG, tmp ;SREG wiederholen
pop tmp ;tmp wiederholen
reti ;Spring wieder dahin, wo du hergekommen bist

;Läuft Timer0 über, so wird B.2 umgeschaltet, sodass Ton
;aus dem Summer zu hören ist
timerSummer:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
sbis PINB, Summer ;ist B.2 = 1?
rjmp timerSummer1 ;NEIN -> spring zu timerSummer1:"
cbi PORTB, Summer ;JA -> setze B.2 auf 0
rjmp timerSummer2 ;zu "timerSummer2:" springen

timerSummer1:
sbi PORTB, Summer ;wenn B.2 = 0 ist, dann auf 1 setzen

;Hier wird Timer0 mit dem aktuellen Tonwert vorgeladen
timerSummer2:
out TCNT0, tonwert ;Timer dementsprechen vorladen

timerSummer3:
pop tmp ;SREG wiederholen
out SREG, tmp
pop tmp ;tmp wiederholen
reti

;Das sind die Werte, womit der Timer0 (Tonleiter-Timer) vorgeladen wird
tonleiter1:
.db 256-55, 256-49, 256-44, 256-41 ;Wert zum Vorladen für den Timer
.db 256-37, 256-33, 256-29, 256-27, 0 ;für die Tonleiter


Weiß nicht, wie ich das sonst anders lösen sollte, also ohne Hilfsvariable.

izaseba
14.08.2005, 15:45
Hallo Thomas,
Daß Dein Programm noch nicht ganz Perfekt ist, ist auch kein Wunder,
überleg mal seit wann Du im Assembler programmierst?

Jetzt nicht traurig sein, es war nicht böse gemeint!

Hauptsache ist, daß es funktioniert, und verbessern kann man es immer noch.

Ich persönlich hätte es fast nur in den Interrupt Routinen gelöst.
Ob es besser oder schlechter als Deine Lösung ist, möchte ich hier nicht urteilen,
ich bin selber kein Profi, sondern genauso wie Du und die meisten hier ein Freak,
der von der Technik fasziniert ist.

Ich schreibe Dir meinen Ansatz, wie ich mir daß vorstelle, ok ?
Du kannst versuchen, das in einem Programm umzusetzen,
abe tue mir ein Gefallen, schreib bitte ein neues Programm, sonst kommt
wieder ein durcheinander dabei raus O:)

Also:
ein Register, wo dein Tonwert gespeichert wird.
wie gehabt ein universallregister.

in loop zuerst mit lpm den ersten Ton in Tonwert einlesen, und den Timer0 damit laden.
als nächstes den Timer1 laden, (könnte auch in reset stehen denke mir)
und zum Schluß eine Endlosschleife.

die Timer0 Interruptroutine, ist sehr einfach, nur mit Tonwert neu laden und raus.
Timer1 Interruptroutine muß natürlich jetzt alles enthalten, was bis jetzt in Deinem Main:
gestanden hat, also
1. mit adiw den Z zeiger um eins erhöhen,
2. Schauen ob Du schon den letzten Ton hattest, wenn ja noch in der routine Timer stopen
und raus, sonst mit lpm Wert in den Register Tonwert schieben, Timer0 neu laden, und auch Interrupt verlassen.

Nach Tekeli Version kommt unter 2 dann halt Wert einlesen, nach 0 prüfen und dann entsprechend verzweigen.

Ob es schöner ist weiß ich nicht, aber es müßte auch so klappen (habe es nicht ausprobiert) , zum üben natürlich sehr gut geeignet.

Wenn Du,Ihr Lust habt dann könnt Ihr es versuchen.

Ich hoffe, daß ich es gut beschrieben habe, und vor allem, daß es so funktioniert :-&

Gruß Sebastian

izaseba
14.08.2005, 15:50
Ihr habt beide andere Wege genommen um die Aufgabe zu lösen.
Beide haben vor und Nachteile.
Es wäre wohl nicht schwer zu sagen welche vor und nachteile es sind...

:-k Habt Ihr das übersehen, oder war das zu einfach? :-k

toeoe
14.08.2005, 16:09
Zu schwer würd ich sagen, wüsste nicht, welche Vorteile meine Version gegenüber Tekelis hätte.

So, hier der Code ohne Hilfsvariable - geht ja doch ohne ;)

;Programm
;CDurTonleiter rauf und runter spielen
.include "m8def.inc"

.def tmp = r16 ;Mein Universallregister
.def tonwert = r17 ;aktueller Wert für den Ton
.def lpm_reg = r0 ;Mein lpm-Register

.equ Summer = PB2 ;Summer an B.2
.equ time0 = 256-255 ;Timer0 für die Tonleiter
.equ time1 = 65536-3600 ;Damit wird der Timer1 vorgeladen, für die Sekunde
.equ daten_laenge = 9 ;Anzahl der Werte

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF1addr
rjmp pruefSek ;Interruptvektor "pruefSek:"

.org OVF0addr
rjmp timerSummer ;Interruptvektor "timerSummer:"

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;Low-Byte der obersten RAM-Adresse
out SPL, tmp

;Timer Register für Ton werden belegt, hier Timer 0
ldi tmp, (1<<CS02) ;Prescaler ist 256
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, time0 ;Hier wird der Timer vorgeladen
out TCNT0, tmp

;Timer Register für Sekunde werden belegt, hier Timer 1
ldi tmp, (1<<CS12) | (1<<CS10) ;Prescaler ist 1024
out TCCR1B, tmp
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde (10000) speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"

ldi tmp, (1<<TOIE0) | (1<<TOIE1);Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

;Z-Register mit DB "tonleiter1" füllen
ldi ZH, HIGH(tonleiter1 * 2)
ldi ZL, LOW(tonleiter1 * 2)

sbi DDRB, Summer ;B.2 als Ausgang
sbi PORTB, Summer ;B.2 auf HIGH stellen

sei ;Interrupts zulassen

;Die Hauptschleife, die sich immer wiederholt
main:
lpm ;Daten von tonleiter1: holen
mov tonwert, lpm_reg ;erstes Byte in tmp verschieben - ersten Tonwert speichern
rjmp main ;immer wieder zurück zu main springen

;Läuft Timer2 über, so wieder zaehlerSek um 1 erhöht und
;Timer2 neu vorgeladen
pruefSek:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
rjmp tonPruefen ;springe zu "tonPruefen"
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde (10000) speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
pop tmp
out SREG, tmp ;SREG wiederholen
pop tmp ;tmp wiederholen
reti ;Spring wieder dahin, wo du hergekommen bist

;Hier wird Z-Zeiger um 1 erhöht. Es wird hier auch verglichen,
;ob der letzte Ton erreicht, wenn ja, dann springt er zu "endeTon"
tonPruefen:
lpm ;Daten von tonleiter1: holen
adiw ZL,1 ;Z um 1 erhöhen, nächstes Byte
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
breq endeTon ;springe zu "endeTon:", wenn letztes Byte ausgelesen
ret ;zurück, wo du hergekommen bist

;Hier wird der Timer gestoppt, indem wir den Prescaler auf 0 setzen
endeTon:
ldi tmp, (0<<CS02) ;Timer stoppen
out TCCR0, tmp
ret ;zurück wo du hergekommen bist

;Läuft Timer0 über, so wird B.2 umgeschaltet, sodass Ton
;aus dem Summer zu hören ist
timerSummer:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
sbis PINB, Summer ;ist B.2 = 1?
rjmp timerSummer1 ;NEIN -> spring zu "timerSummer1:"
cbi PORTB, Summer ;JA -> setze B.2 auf 0
rjmp timerSummer2 ;springe zu "timerSummer2:"

timerSummer1:
sbi PORTB, Summer ;B.2 auf 1 setzen

timerSummer2:
pop tmp ;SREG wiederholen
out SREG, tmp
pop tmp ;tmp wiederholen
reti

;Das sind die Werte, womit der Timer0 (Tonleiter-Timer) vorgeladen wird
tonleiter1:
.db 256-55, 256-49, 256-44, 256-41 ;Wert zum Vorladen für den Timer
.db 256-37, 256-33, 256-29, 256-27, 0 ;für die Tonleiter


Man muss sich erstmal den Ablauf vor Augen halten, darin liegt denk ich noch mein größtes Problem. Aber das bekomm ich schon hin mit der Zeit.

Gruß
Thomas

izaseba
14.08.2005, 16:56
Thomas,
aber Du hast Das Programm doch nicht neu geschrieben, oder?

z.B
.org OVF1addr
rjmp pruefSek ;Interruptvektor "pruefSek:"

mithin gehe ich davon aus daß pruefSek der Interrupthandler von Timer1 ist....

und schau was über pruefSek drüber steht :-k

Hmmmm

michaelb
14.08.2005, 17:03
Hallo,
ich hab me Frage zu diesem hier:


push tmp ;Rette Universallregister
in tmp, SREG ;Rette Statusregister
push tmp

beim ersten wird doch was in den Stack geschrieben und was ich nicht versteh beim zweiten wird das doch überschrieben! Wie kann man dann beides nacheinander wieder aus dem Stack holen mit diesem hier?:


pop tmp ;stelle SREG wieder her
out SREG, tmp
pop tmp ;stelle Universalregister wieder her

Gruß Michi

Florian
14.08.2005, 17:06
Lieber Michael! ;o)
Du hast wohl noch immer nicht die xx Seiten durchgelesen, oder? *lol*
Die Adressen werden ans Ende des SRAMs gesetzt und wie ein Bücherstapel behandelt!
Du legst ein Buch drauf und nimmst eins runter, danach noch eins oder Du packst 3 drauf und nimmst 20 runter! *lol*
BücherSTAPEL!

PicNick
14.08.2005, 17:13
Du legst ein Buch drauf und nimmst eins runter, danach noch eins oder Du packst 3 drauf und nimmst 20 runter! *lol*

Wenn 3 da sind und du nimmst 5 weg, mußt du zwei zurücklegen, damit dann kein's mehr da ist ?

:mrgreen:

izaseba
14.08.2005, 17:14
Hallo Florian,
Ich habe irgendwo den Stack ausführlich behandelt (Hoffe ich),

Nur ich kann mir wirklich gut vorstellen, daß die Leute keine große Lust haben 19 Seiten durchzublättern....
Irgendwas müssen wir uns einfallen lassen, nur was

Gruß Sebastian

toeoe
14.08.2005, 17:16
@Sebastian: Ganz neu hab ich den Code natürlich nicht geschrieben. Die Standardsaches hab ich übernommen. Ok, hätt natürlich die Kommentare anpassen können ;) Sorry. Dient ja dann auch der Übersichtlichkeit.

Um zur eigentlichen Aufgabe zurück zu kommen. Wir wollten die Tonleiter doch hoch und runter spielen, oder?
Ein Anfänger, so wie ich einer bin, würde jetzt einfach noch die Werte in umgekehrter Reihenfolge ans Ende zu der Datenbank dazuschreiben. Aber das ist sicher nicht das Ziel und auch sicher nicht die eleganteste Lösung. Funktionieren würde es ja, sicher. Aber man muss doch sicher auch irgendwie von hinten anfangen können zum laden, oder wie wollen wir das realisieren?

Gruß
Thomas

[edit]
Hab irgendwie bei meiner Rechtschreibung geschlampt und hiermit verbessert :)

Florian
14.08.2005, 17:19
Hmmm, gute Frage, den Stack hatten wir behandelt!
Wir könnten den Thread in Einzelteile zerhacken!
Ihr müsst mir nur sagen, wo ich die Schere ansetzen muss!
Dann hätten wir einen Anfang, Timer, Tonerzeugung, 16 Bit, ...
Was haltet ihr davon?

Dann wirds auch für mich später einfacher daraus ein Tutorial zu schreiben! *lol*



*edit*
@ Thomas:
Dann such aml den umgekehrten Befehl von adiw!
Das ist dann jetzt nur noch ein Katzensprung!

izaseba
14.08.2005, 17:20
Aber man muss doch sicher auch irgendwie von hinten anfangen können zum laden, oder wie wollen wir das realisieren?


Wenn Du schon selber danach fragst :-)

ich sag nur, wir haben bisjetzt adiw benutzt, es gibt aber noch sbiw (wie gut das Atmel
Entwickler sich sowas einfallenlassen haben, oder wer hat den Befehlsatz entwickelt? Intel?)
Recht Dir das?
dann lass jetzt deinen Kopf qualmen.

Gruß Sebastian

toeoe
14.08.2005, 17:21
@Florian: Ja, aber lass uns das doch später machen, oder?

@Sebastian: Wieso bin ich so dumm wie ich bin? *g*
Ich hab den Befehl sbiw selbst schonmal fälschlicherweiße benutzt, wenn du dich noch erinnern kannst.
Argh, das ärgert mich aber nun *g*

[edit]
Aber da muss ich ja nun wieder eine Hilfsvariable nutzen, die speichert, ob ich schon am Ende war, oder nicht, ne?

Florian
14.08.2005, 17:23
Ruhig Blut! *rofl*

Na klar können wir das mit dem Auteilen auch später machen, aber so ist es halt ein Saurier unter Zwergen!
Da traut sich keiner mehr dran!

izaseba
14.08.2005, 17:26
@Florian: Ja, aber lass uns das doch später machen, oder?

Ja aber dann würden wir uns Fragen sparen, auf die es schon eine Antwort gibt.


@Sebastian: Wieso bin ich so dumm wie ich bin? *g*
:lol:



Ihr müsst mir nur sagen, wo ich die Schere ansetzen muss!
Das ist keine einfache Frage!
Weil, wie soll man sortieren, nach Themen wie z.B Tasten,Blinken,Ton,Musik
Oder lieber nach Themen wie Interrupts, Ports Pins, Stack, Zeiger :-k

izaseba
14.08.2005, 17:28
Aber da muss ich ja nun wieder eine Hilfsvariable nutzen, die speichert, ob ich schon am Ende war, oder nicht, ne?
Ich glaube, Du liebst Variablen was?
Du weißt schon wann Du zuende bist ! oder warum ist Dein Piezo zum schluß stumm ?????

Gruß Sebastian

toeoe
14.08.2005, 17:32
Ich würd nach Themen wie Interrupts, Ports, Pins usw. sortieren. Darin sind dann Beispiele und dann kann man ja schauen, ob man das Beispiel machen möchte oder nicht.


Ich glaube, Du liebst Variablen was?
Du weißt schon wann Du zuende bist ! oder warum ist Dein Piezo zum schluß stumm ?????
Ja, ich weiß, wann zuende ist. Aber das Problem ist ja, wenn ich Z um 1 erniedrige, dann erhöht er es ja wieder beim nächsten mal um 1, weil er dann ja wieder nicht am Ende ist.
Und ja, ich mag Variablen, in denen kann man so viel speichern ;)

izaseba
14.08.2005, 17:40
Dann leg Dir eine schöne Variable an, wo Du dann speicherst, ob Du rauf oder runter möchtest, und jenachdem machst Du sbiw oder adiw, aber vorsicht, nicht außerhalb der db.
kommen!
achso, und belege nicht einen Register damit, sondern nur ein Bit Du weißt ja sbr cbr damit kannst Du 8 Variablen !!! in einem Register Speichern, cool was ?

michaelb
14.08.2005, 17:49
Hallo Zusammen,
danke dass ihr soviel Geduld habt!!
Also muss ich immer die Reihenfolge behalten!
Es muss also immer so aussehen oder?:
Speicher Info 1
Speicher Info 2
Speicher Info 3
-----------------------
-----------------------
Lade Info 3
Lade Info 2
Lade Info 1

ich kann aber nicht zuerst die 2 laden! <= stimmt so, oder?
Gruß Michi

toeoe
14.08.2005, 17:51
Hab grad gemerkt, dass meine geänderte Version von der Tonleiter gar nicht geht. Also die ohne Hilfsvariable. Hab vorhin nämlich das alte nochmal draufgebrannt und nicht gemerkt ](*,)

Wäre nett, wenn du da mal reinschauen könntest:

;Programm
;CDurTonleiter rauf spielen
.include "m8def.inc"

.def tmp = r16 ;Mein Universallregister
.def tonwert = r17 ;aktueller Wert für den Ton
.def lpm_reg = r0 ;Mein lpm-Register

.equ Summer = PB2 ;Summer an B.2
.equ time0 = 256-255 ;Timer0 für die Tonleiter
.equ time1 = 65536-3600 ;Damit wird der Timer1 vorgeladen, für die Sekunde
.equ daten_laenge = 9 ;Anzahl der Werte

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF1addr
rjmp pruefSek ;Interruptvektor "pruefSek:"

.org OVF0addr
rjmp timerSummer ;Interruptvektor "timerSummer:"

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;Low-Byte der obersten RAM-Adresse
out SPL, tmp

;Timer Register für Ton werden belegt, hier Timer 0
ldi tmp, (1<<CS02) ;Prescaler ist 256
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, time0 ;Hier wird der Timer vorgeladen
out TCNT0, tmp

;Timer Register für Sekunde werden belegt, hier Timer 1
ldi tmp, (1<<CS12) | (1<<CS10) ;Prescaler ist 1024
out TCCR1B, tmp
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde (10000) speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"

ldi tmp, (1<<TOIE0) | (1<<TOIE1);Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

;Z-Register mit DB "tonleiter1" füllen
ldi ZH, HIGH(tonleiter1 * 2)
ldi ZL, LOW(tonleiter1 * 2)

sbi DDRB, Summer ;B.2 als Ausgang
sbi PORTB, Summer ;B.2 auf HIGH stellen

sei ;Interrupts zulassen

;Die Hauptschleife, die sich immer wiederholt und den Tonwert neu einliest
main:
lpm ;Daten von tonleiter1: holen
mov tonwert, lpm_reg ;Wrt von lpm_reg in tonwert speichern
rjmp main ;immer wieder zurück zu main springen

;Läuft Timer1 über, so wird erstmal geprüft, ob der letzte Ton
;erreicht ist und der Timer1 wird neu vorgeladen
pruefSek:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
rjmp tonPruefen ;springe zu "tonPruefen"
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde (10000) speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
pop tmp
out SREG, tmp ;SREG wiederholen
pop tmp ;tmp wiederholen
reti ;Spring wieder dahin, wo du hergekommen bist

;Hier wird Z-Zeiger um 1 erhöht. Es wird hier auch verglichen,
;ob der letzte Ton erreicht, wenn ja, dann springt er zu "endeTon"
tonPruefen:
lpm ;Daten von tonleiter1: holen
adiw ZL,1 ;Z um 1 erhöhen, nächstes Byte
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
breq endeTon ;springe zu "endeTon:", wenn letztes Byte ausgelesen
ret ;zurück, wo du hergekommen bist

;Hier wird der Timer gestoppt, indem wir den Prescaler auf 0 setzen
endeTon:
ldi tmp, (0<<CS02) ;Timer stoppen
out TCCR0, tmp
ret ;zurück wo du hergekommen bist

;Läuft Timer0 über, so wird B.2 umgeschaltet, sodass Ton
;aus dem Summer zu hören ist
timerSummer:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
sbis PINB, Summer ;ist B.2 = 1?
rjmp timerSummer1 ;NEIN -> spring zu "timerSummer1:"
cbi PORTB, Summer ;JA -> setze B.2 auf 0
rjmp timerSummer2 ;springe zu "timerSummer2:"

timerSummer1:
sbi PORTB, Summer ;B.2 auf 1 setzen

timerSummer2:
pop tmp ;SREG wiederholen
out SREG, tmp
pop tmp ;tmp wiederholen
reti

;Das sind die Werte, womit der Timer0 (Tonleiter-Timer) vorgeladen wird
tonleiter1:
.db 256-55, 256-49, 256-44, 256-41 ;Wert zum Vorladen für den Timer
.db 256-37, 256-33, 256-29, 256-27, 0 ;für die Tonleiter

Er spielt einfach nur den ersten Ton ab (Endlosschleife)

@Michi: Genau so muss das aussehen! Richtig.

izaseba
14.08.2005, 17:55
ich kann aber nicht zuerst die 2 laden! <= stimmt so, oder?

Genau, daß habe ich irgendwo oben schon geschrieben, stell Dir vor Du hast einen Gabelstapler, der nur eine Palette aufnehmen kann, du mußt also immer die oberste abnehmen, zwei auf einmal geht nicht, und dazwischen geht auch nicht, immer einen drauf oder einen runter.

izaseba
14.08.2005, 17:57
Wäre nett, wenn du da mal reinschauen könntest:

Ich wollte vorhin schon was sagen, ich glaube Timer0 wird nicht mehr geladen, kann das?

toeoe
14.08.2005, 18:08
Ja, das war der Fehler, allerdings muss sich da noch irgendwo ein Fehler eingeschlichen haben, denn er spielt zwar jetzt nen anderen Ton, aber dann auch nur den. Und jede Sekunde kommt ein kurzer Stocker. Dann spielt er aber weiter den gleichen Ton, wieder für eine Sekunde.

;Programm
;CDurTonleiter rauf spielen
.include "m8def.inc"

.def tmp = r16 ;Mein Universallregister
.def tonwert = r17 ;aktueller Wert für den Ton
.def lpm_reg = r0 ;Mein lpm-Register

.equ Summer = PB2 ;Summer an B.2
.equ time0 = 256-255 ;Timer0 für die Tonleiter
.equ time1 = 65536-3600 ;Damit wird der Timer1 vorgeladen, für die Sekunde
.equ daten_laenge = 9 ;Anzahl der Werte

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF1addr
rjmp pruefSek ;Interruptvektor "pruefSek:"

.org OVF0addr
rjmp timerSummer ;Interruptvektor "timerSummer:"

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;Low-Byte der obersten RAM-Adresse
out SPL, tmp

;Timer Register für Ton werden belegt, hier Timer 0
ldi tmp, (1<<CS02) ;Prescaler ist 256
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, time0 ;Hier wird der Timer vorgeladen
out TCNT0, tmp

;Timer Register für Sekunde werden belegt, hier Timer 1
ldi tmp, (1<<CS12) | (1<<CS10) ;Prescaler ist 1024
out TCCR1B, tmp
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde (10000) speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"

ldi tmp, (1<<TOIE0) | (1<<TOIE1);Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

;Z-Register mit DB "tonleiter1" füllen
ldi ZH, HIGH(tonleiter1 * 2)
ldi ZL, LOW(tonleiter1 * 2)

sbi DDRB, Summer ;B.2 als Ausgang
sbi PORTB, Summer ;B.2 auf HIGH stellen

sei ;Interrupts zulassen

;Die Hauptschleife, die sich immer wiederholt und den Tonwert neu einliest
main:
lpm ;Daten von tonleiter1: holen
mov tonwert, lpm_reg ;Wrt von lpm_reg in tonwert speichern
rjmp main ;immer wieder zurück zu main springen

;Läuft Timer1 über, so wird erstmal geprüft, ob der letzte Ton
;erreicht ist und der Timer1 wird neu vorgeladen
pruefSek:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
rjmp tonPruefen ;springe zu "tonPruefen"
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde (10000) speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
pop tmp
out SREG, tmp ;SREG wiederholen
pop tmp ;tmp wiederholen
reti ;Spring wieder dahin, wo du hergekommen bist

;Hier wird Z-Zeiger um 1 erhöht. Es wird hier auch verglichen,
;ob der letzte Ton erreicht, wenn ja, dann springt er zu "endeTon"
tonPruefen:
lpm ;Daten von tonleiter1: holen
adiw ZL,1 ;Z um 1 erhöhen, nächstes Byte
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
breq endeTon ;springe zu "endeTon:", wenn letztes Byte ausgelesen
ret ;zurück, wo du hergekommen bist

;Hier wird der Timer gestoppt, indem wir den Prescaler auf 0 setzen
endeTon:
ldi tmp, (0<<CS02) ;Timer stoppen
out TCCR0, tmp
ret ;zurück wo du hergekommen bist

;Läuft Timer0 über, so wird B.2 umgeschaltet, sodass Ton
;aus dem Summer zu hören ist
timerSummer:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
sbis PINB, Summer ;ist B.2 = 1?
rjmp timerSummer1 ;NEIN -> spring zu "timerSummer1:"
cbi PORTB, Summer ;JA -> setze B.2 auf 0
rjmp timerSummer2 ;springe zu "timerSummer2:"

timerSummer1:
sbi PORTB, Summer ;B.2 auf 1 setzen

timerSummer2:
out TCNT0, tonwert ;Timer0 neu vorladen
pop tmp ;SREG wiederholen
out SREG, tmp
pop tmp ;tmp wiederholen
reti

;Das sind die Werte, womit der Timer0 (Tonleiter-Timer) vorgeladen wird
tonleiter1:
.db 256-55, 256-49, 256-44, 256-41 ;Wert zum Vorladen für den Timer
.db 256-37, 256-33, 256-29, 256-27, 0 ;für die Tonleiter

Hab schon überprüft, also richtig hochzählen müsst er ja, also nach 1 Sekunde immer den nächsten Wert holen. Komisch...

Ahh, es gibt Probleme mit dem Befehll "ret", irgendwann ruft er dann wieder "reset:" auf, hmm..mal schaun, wie ich das löse :o

izaseba
14.08.2005, 18:17
pruefSek:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
rjmp tonPruefen ;springe zu "tonPruefen"
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde (10000) speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
pop tmp
out SREG, tmp ;SREG wiederholen
pop tmp ;tmp wiederholen
reti ;Spring wieder dahin, wo du hergekommen bist

;Hier wird Z-Zeiger um 1 erhöht. Es wird hier auch verglichen,
;ob der letzte Ton erreicht, wenn ja, dann springt er zu "endeTon"
tonPruefen:
lpm ;Daten von tonleiter1: holen
adiw ZL,1 ;Z um 1 erhöhen, nächstes Byte
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
breq endeTon ;springe zu "endeTon:", wenn letztes Byte ausgelesen
ret ;zurück, wo du hergekommen bist

;Hier wird der Timer gestoppt, indem wir den Prescaler auf 0 setzen
endeTon:
ldi tmp, (0<<CS02) ;Timer stoppen
out TCCR0, tmp
ret ;zurück wo du hergekommen bist


Das alles sieht echt nicht gut aus:

1. Verlasse nie die Interruptroutine(außer mit reti), es sei denn es geht wirklich nicht anders!
bezogen auf rjmp tonpruefen
muß das sein? warum springst Du dahin? wäre es nicht einfacher das alles direkt
drunter zu schreiben?
2. Mal angenommen, es müßte wirklich so gelöst werden, was sollen die ret's darunter?
bist Du Dir in klarem, wo er hinspringt, wenn da ein ret steht, und vorallem was danach passiert ?

P.S. Jetzt nicht traurig werden, aber das ist Deine häufige Fehlerquelle !

toeoe
14.08.2005, 18:21
Mit dem ret wollte ich bezwecken, dass er wieder dahin springt, wo er hergekommen ist. Also in dem Falle hier:

pruefSek:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
rjmp tonPruefen ;springe zu "tonPruefen"
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde (10000) speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
pop tmp
out SREG, tmp ;SREG wiederholen
pop tmp ;tmp wiederholen
reti ;Spring wieder dahin, wo du hergekommen bist

;Hier wird Z-Zeiger um 1 erhöht. Es wird hier auch verglichen,
;ob der letzte Ton erreicht, wenn ja, dann springt er zu "endeTon"
tonPruefen:
lpm ;Daten von tonleiter1: holen
adiw ZL,1 ;Z um 1 erhöhen, nächstes Byte
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
breq endeTon ;springe zu "endeTon:", wenn letztes Byte ausgelesen
ret ;zurück, wo du hergekommen bist

;Hier wird der Timer gestoppt, indem wir den Prescaler auf 0 setzen
endeTon:
ldi tmp, (0<<CS02) ;Timer stoppen
out TCCR0, tmp
ret ;zurück wo du hergekommen bist
Springt er beim ersten ret wieder in die Zeile hier:
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
also 1 unter dem rjmp-Befehl, wo er ja vorher weggesprungen ist.

PS: Traurig bin ich nicht, will nur nicht, dass du böse bist *g* Weiß ja nicht, wie du die Hände übern Kopf zusammenschlägst und dir denkst "Wie kann man nur so blöd sein" ;)

izaseba
14.08.2005, 18:36
denkst "Wie kann man nur so blöd sein"
Na ja jetzt übertreibst Du aber.
aber mit ret hollt der sich zwei Bytes vom Stack und versucht zu dieser Adresse hinzuspringen....
und was liegt ganz oben auf dem Stack?
Abbild von SREG und darunter abbild von tmp #-o
Welche Rücksprungadresse errechnet er sich wohl daraus ?
Lass das mit dem ret sein, versuche lieber tonPruefen: und ende_ton: in die Routine mit reinzunehmen (über reti) und springe da nicht so einfach hin, wozu (ich sehe du hast in C Programmiert bis jetzt hä?)

Gruß Sebastian

toeoe
14.08.2005, 18:45
Ja, hab vorher in C, genauer gesagt in C++ programmiert.
Also muss ich das dann so machen? Denn eine Sprungmarke brauch ich ja, damit ich den Timer dann abstelle, wenn er das Ende erreicht hat, oder?

pruefSek:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
lpm ;Daten von tonleiter1: holen
adiw ZL,1 ;Z um 1 erhöhen, nächstes Byte
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
breq endeTon ;springe zu "endeTon:", wenn letztes Byte ausgelesen
pop tmp
out SREG, tmp ;SREG wiederholen
pop tmp ;tmp wiederholen
reti ;Spring wieder dahin, wo du hergekommen bist

endeTon:
ldi tmp, (0<<CS02) ;Timer stoppen
out TCCR0, tmp
pop tmp
out SREG, tmp ;SREG wiederholen
pop tmp ;tmp wiederholen
reti
Doch dann hab ich ja 3 Zeilen doppelt. Die 3 Zeilen zum Wiederholen der Register vom Stack. Ist also auch nicht die beste Möglichkeit.

izaseba
14.08.2005, 18:55
und zweimal reti, das hatten wir schonmal, oder?
Logisch denken muß man hier, wenn Du in C/C++ mißt programmierst, merkt das der Compiler meistens und haut Dir auf die Finger, der Assembler macht das nicht.

schau Du benutzt breq endeton,
nimm doch einfach brne irgendwas:
darunter schreibst Du die Befehle um den Timer anzuhalten
dann das label irgendwas:
und hier den Ausklang , verstanden?

toeoe
14.08.2005, 19:06
Also so?

pruefSek:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
lpm ;Daten von tonleiter1: holen
adiw ZL,1 ;Z um 1 erhöhen, nächstes Byte
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
brne regwiederholen ;Ist Ende erreicht?
ldi tmp, (0<<CS02) ;JA -> Timer0 abstellen
out TCCR0, tmp

regwiederholen:
pop tmp
out SREG, tmp ;SREG wiederholen
pop tmp ;tmp wiederholen
reti
Fällt mir noch ein wenig schwer, so zu denken, aber ich werds denk ich noch hinbekommen.

Mal so nebenbei, beim compilieren bringt er mir folgendes Warnung:

warning: .cseg .db misalignment - padding zero byte
Wenn ich die 0 hinten bei meiner DB rausnehmen, ist die Warnung weg. Soll ich die Warnung einfach ignorieren, oder wann kommt es dann zu Fehlern?

izaseba
14.08.2005, 19:27
Der Florian wat Euch das schon gesagt, die Anzahl der Bytes in .db muß GERADE sein
Der Speicher ist in Worten (2 Bytes) organisiert, und wenn Du eine ungerade Zahl hast hängt er eine 0 hinter und meckert, also noch eine null hintern dran, oder wenn eh nicht erforderlich die null wegmachen.

Klappt das jetzt ?

toeoe
14.08.2005, 19:32
Axo, daher weht der Wind. Ok, danke.
Werd jetzt versuchen, das wieder rückwärts abzuspielen, aber ohne Hilfsvariable, denn wenn du sagst, dass es auch ohne geht, dann muss es ja auch ohne gehen.

Mal schaun *kopf anstreng*

toeoe
14.08.2005, 20:06
Hmm..irgendwas stimmt da noch nicht...Er läuft wie gehabt bis zum Ende durch, dann soll er aber wieder zurück laufen, also subtrahiere ich wieder 1. Aber beim nächsten Durchlauf addiert er ja wieder 1 und wir sind wieder beim letzten Ton. Also dacht ich mir, subtrahiere ich einfach 2. Dann liest er, sobald er den letzten Ton erreicht hat, bei der nächsten Sekunde den vorletzten Ton aus (klingt gut). Aber bei der nächsten Sekunde sagt er dann wieder, dass er noch nicht am Ende ist, stimmt ja auch, weil er beim vorletzten Ton wieder ist und nicht beim letzten.
Lange Rede, kurzer Sinn, hier mal der Code:

pruefSek:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
lpm ;Daten von tonleiter1: holen
adiw ZL,1 ;Z um 1 erhöhen, nächstes Byte
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
brne regwiederholen ;Ist Ende erreicht?
;JA -> wieder zurückzählen
lpm ;Daten von tonleiter1: holen
sbiw ZL,2 ;Z um 2 erniedrigen, vorheriges Byte
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
brne regwiederholen ;Ist Anfang erreicht?
ldi tmp, (0<<CS02) ;JA -> Timer0 abstellen
out TCCR0, tmp

regwiederholen:
pop tmp
out SREG, tmp ;SREG wiederholen
pop tmp ;tmp wiederholen
reti
Weiß nicht, ob ich überhaupt mit 2 subtrahieren muss oder ob es da eine ganz andere Möglichkeit gibt. Denn ich möcht das nun gerne ohne Hilfsvariable schaffen.

izaseba
14.08.2005, 20:12
O weh Thomas,
ich glaube da habe ich etwas zu schnell gesagt :frown:
Du hast recht, man müßte sich irgendwie merken, ob man rauf oder runter will :frown:
Ich habe Dich durcheinander gebracht mach Dir wirklich eine schöne Variable :frown:

:cry: :cry: :cry:

toeoe
14.08.2005, 20:52
Hmm...bin kurz davor, dass es geht, aber irgendwie will es noch nicht. Teste die ganze Zeit im Simulator. Er macht aber nie den Timer aus, also er springt nie da rein, wo er den Timer0 abestellen soll.

pruefSek:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
lpm ;Daten von tonleiter1: holen
sbrc tonleiterEnde, 0 ;Ist tonleiterEnde = 0?
rjmp subtrahiereZ ;Wenn tonleiter = 1, dann subtrahiere Z um 1

addiereZ:
adiw ZL, 1 ;Z um 1 erhöhen
rjmp pruefSek2 ;zu "pruefSek2:" springen

subtrahiereZ:
sbiw ZL, 1 ;Z um 1 erniedrigen

pruefSek2:
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
brne pruefTonaus ;zu "pruefTonaus:" springen
sbr tonleiterEnde, 1 ;Hilfsvariable.1 auf 1 setzen

pruefTonaus:
cp ZL, tmp ;Ist Anfang erreicht?
brne regwiederholen
ldi tmp, (0<<CS02) ;JA -> Timer0 abstellen
out TCCR0, tmp

regwiederholen:
pop tmp
out SREG, tmp ;SREG wiederholen
pop tmp ;tmp wiederholen
reti
Er müsste aber eigentlich reinspringen, denn irgendwann ist doch ZL wieder auf 0, oder?

izaseba
14.08.2005, 21:05
naja , der Ansatz ist schon ok, ABER
schau da

pruefTonaus:
cp ZL,tmp

was wird da Verglichen?
welchen Wert has tmp? vielleicht HIGH((tonleiter1 * 2) + daten_laenge)

Du Vergleichst Äpfel mit Birnen ! :-)

1. Dieser Gespann cp cpc müßte da auch hin.
2. Du müßtest eigentlich mit (tonleiter1 * 2) vergleichen weil Du Dich wieder nach vorne Bewegt hast, oder sehe ich das falch ? :-k

toeoe
14.08.2005, 21:17
Äpfel mit Birnen ist nicht gut ;)
Hmm...nu hab ich dieses Label so:

pruefTonaus: ;Ist Anfang erreicht?
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
breq regwiederholen
ldi tmp, (0<<CS02) ;JA -> Timer0 abstellen
out TCCR0, tmp
Aber dann spielt er nur den ersten Ton ab und dann ist Schluss.
Das "HIGHT-Byte" interessiert mich doch zu dem Zeitpunkt gar nicht mehr, oder?

izaseba
14.08.2005, 21:23
Das "HIGHT-Byte" interessiert mich doch zu dem Zeitpunkt gar nicht mehr, oder?

wie kommst Du darauf ?
Stell Dir mal vor wir hätten so ein 2 Bytes gespann:
High 11000111 Low 00000000
was passiert , wenn ich jetzt sbiw benutze ?
wie sehen die Bytes aus ?

ich wiederhole mich :
Du müßtest eigentlich mit (tonleiter1 * 2) vergleichen weil Du Dich wieder nach vorne Bewegt hast, oder sehe ich das falch ? Think

toeoe
14.08.2005, 21:27
Ahjo, vergleichen mit (tonleiter * 2) geht das so alleine erstmal nicht, wahrscheinlich auch logisch.

Stell Dir mal vor wir hätten so ein 2 Bytes gespann:
High 11000111 Low 00000000
was passiert , wenn ich jetzt sbiw benutze ?
wie sehen die Bytes aus ?
Das ist ja eben mein Problem. Florian konnte mir das leider nicht erklären.

Also wenn jetzt sbiw benutzt wird..hmm...kommt drauf an, wo der Z-Zeiger grad ist, oder? Der liest doch immer 2 Bits aus, ne? Hmm..sorry, keine Ahnung, ich kapiers nicht.

izaseba
14.08.2005, 21:36
Warum soll er 2 Bytes rauslesen?

Pass auf

daten:
.db 10,20,30,40,50,60

ldi ZH,HIGH(daten * 2 )
ldi ZL,LOW(daten * 2 )
Der Z Zeiger zeigt auf den ersten Byte bei daten,
wenn ich jetzt lpm mache erhalte ich eine 10 in den R0
adiw ZL,1 , der Zeiger zeigt auf die 20
nochmal adiw ZL,1 der Zeiger zeigt auf die 30
jetzt mal adiw ZL,2 der Zeiger zeigt auf die 50
sbiw ZL,1 der Zeiger Zeigt auf die 40
sbiw ZL,1 der Zeiger Zeigt auf die 30
ldi ZH,HIGH(daten * 2 )
ldi ZL,LOW(daten * 2 ) er zeigt wieder auf die 10

Dieses mal 2 kommt davon daß der Speicher Wortweise (2 Bytes) organisiert ist
Nimm das einfach so hin, merke Dir nur daß da immer * 2 hinkommt
Vielleicht findet sich hier jemand der das einfacher erklären kann,
ich kann es nicht :-(

toeoe
14.08.2005, 21:41
Ok, und das HIGH und LOW kommt dadurch, weil wir hier mit 16Bit arbeiten, richtig?
Und wieso muss ich LOW mit cp vergleichen und HIGH mit cpc? Hab mir das ja auch schon alles im Simulator angeschaut und gesehen, wie sich das auch im SREG ändert, aber bin leider nicht dahinter gestiegen.

izaseba
14.08.2005, 22:05
Also cp ist wie Du schon irgendwomal geschrieben hast in der Tat Subtrahieren, mit dem Unterschied daß die Inhalte unverändert bleiben, nur die Bits Im SREG Register entsprechend dem Ergebnis gesetzt werden.
Wie Vergleicht man in der Mathe 2 Zahlen ??
Wenn wir draufschauen und zwei Zahlen sehen, dann können wir sagen daß 10 gleich 10 ist oder 10 kleiner als 20 ist.
Aber woher soll der AVR das wissen?
Er kann nur eine Zahl von der anderen abziehen, und anhand des SREG exakt sagen welche kleiner größer oder halt ob beide gleich sind!

Nehmen wir als Beispiel zwei gleiche Zahlen

100 und 100

wenn du jetzt sie in zwei register lädst und mit cp "behandelst"
Verändern sie sich nicht, nur im SREG wird das Z Bit gesetzt
was heißt daß das ergebnis 0 ist (klar 100 -100 = 0) :-)

so, wenn jetzt danach z.B. der befehl breq kommt schaut er nach ob Z Bit im SREG 0 oder 1 ist wenn 1 springt er, wenn 0 nicht das war doch einfach oder,
so gleich machen wir des mit 16 Bit und cpc *schüttel*
Du kannst auch die Zwei Zahlen mit verschiedenen Werten laden, und schauen wie sich die Bits im SREG verändern (ganz interresant sind Vorzeichen, Negativ und Übertragsflags

toeoe
14.08.2005, 22:11
Bis dahin hab ichs erstmal verstanden.
Den Übertragsflag versteh ich nicht ganz. Ist das denn der Übertrag, den man auch aus dem schriftlichen rechnen kennt?

[edit]
Also nehmen wir mal die Zeilen hier:

ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
brne pruefTonaus ;zu "pruefTonaus:" springen
Nach der Zeile "cp ZL, tmp" wird doch gar nicht mit breq oder brne geprüft, wie das Ergebnis war, wozu vergleicht man dann eigentlich? :-k

izaseba
14.08.2005, 22:45
Du hast es erkannt!

jetzt machen wir knüppeldick....

machen wir jetzt 2 16 Bit Zahlen (naja eigentlich 10 aber wir brauchen jetzt 16 Bit um sie zu speichern)

erste Zahl 1000 zweite Zahl 1001

die passen ja nicht in 8 Bit rein :-(
also werden sie zerschlagen in High Byte und Low Byte tipe mal in Deinem Taschenrechner 1000 ein und wandle es in binär um, was kommt dabei raus ?
1111101000 also werden sie so aufgeteilt High -> 00000011 Low -> 11101000
und 1001 ?
11111010001 High -> 00000011 Low->11101001

und da wir Menschen sind und dezimale schreibweise voll geil finden wandeln wir uns das um ,
die beiden Highs ergeben 3 juhu gleich!
und Lows 232 und 233 naja da ist nix meh gleich :-(

so jetzt rechnen!

cp 232,233 -> schaue was sich im SREG getan hat Vorzeichen, Halbübertrag,Negativ und Übertrag sind gesetzt,
bis auf den Halbübertrag ist wohl klar oder?
Bei Halbübertrag muß ich auch passen, was der zu sagen hat habe ich bis jetzt noch nicht raus :-( vielleicht weiß der Florian das, oder sonst jemand
aber die anderen Flags sind wohl klar oder ?

Jetzt müssen wir noch die zwei Highs vergleichen, könnten ja auch anders sein, ABER
wir müssen den Übertrag(auch Carry) mit in die Rechnung nehmen (wie Du schon sagtest schriftliches rechnen)
und dazu ist der Befehl cpc (cp mit Carry)
also 3,3 wäre eigentlich ja gleich, dann wäre nur Z SREG 1, aber da wir noch den Carry (der ja 1 ist) mit abziehen, haben wir ja, naja schau selber im Sim
ich hoffe daß es jetzt doch etwas einfacher ist.

Das bezieht sich auch auf add und adc! Genau dasselbe nur halt addieren

P.S. Genauso wird auch mit Zahlen, die 24 32 und mehr Bytes haben verfahren...

Ich weiß daß es schwer ist aber leider leider (man denke da an c if a>10000 ....)

Aber Du wolltest ja selber auf der Hardwareebene programmieren :-) :-)

Edit:
Nach der Zeile "cp ZL, tmp" wird doch gar nicht mit breq oder brne geprüft, wie das Ergebnis war, wozu vergleicht man dann eigentlich? Think

nach cp nicht aber nach cpc, ich hoffe daß jetz alles klar ist [-o<

Florian
14.08.2005, 22:49
Hallo ihr Eifrigen!
Entschuldigt bitte, dass ich mich ein Wenig aus diesem Thread zurückgezogen habe, ich muss noch einiges Wichtiges erledigen und habe kaum Zeit hier mitzulesen!
Seit mir bitte nicht böse! *hoff*

Schaffst Du das auch ohne mich Sebastian? *dumme Frage, Du hast es ja sowieso fast hauptsächlich gemacht -> g*

izaseba
14.08.2005, 22:53
Hallo Florian , ich hoffe es [-o<
wir stoßen jetzt auf Sachen , die wirklich sauschwer für uns Menschen sind,
aber ich hoffe, daß [-o<

Aber um unser Werk zu vollenden werden wir Dich ja noch brauchen :-)

Du kennst Ja unser Ziel in dieser Übung :-)

toeoe
14.08.2005, 22:56
Nette Erklärung, danke :)
Es ist schon ein wenig klarer geworden, aber ich versteh immer noch nicht, wie ich herrausfinde, wann ich cp und wann ich cpc nehmen muss.
Wenn ich zwei gleiche Zahlen habe und diese mit cpc vergleiche, dann sind alle Flags im SREG auf 0, also keines ist gesetzt.

izaseba
14.08.2005, 23:03
aber ich versteh immer noch nicht, wie ich herrausfinde, wann ich cp und wann ich cpc nehmen muss.

Ja wie ? immer bei Zahlen die 8 bit haben cp

und bei zahlen die 16 und mehr die lows mit cp und den ganzen rest (meistens nur Highs)
cpc

genauso addieren mache mal sim und addiere mal zwei 16 bit zahlen,
das ist einfacher zu verstehen wenn ergebnis von add also beide lows höher als 255 ist
wir carry gesetzt und bei den highs machst Du adc also beide hight und den carry dabei
(wie normales rechnen auf papier, wenn ergebnis höcher 9 wird eine 1 mit eine Zeile weiter mitgenommen die 1 ist halt auch carry)

also bei lows cp sub oder add
bei highs cpc sbc oder adc
klar?

toeoe
14.08.2005, 23:07
Achsooooooooo...also ist der Befehl "cp ZL, tmp" gar nicht unnütz, denn er speichert sich dass dann, wenn nen Übertrag sein muss und dieses nutzt er dann bei cpc?
Na denn is klar :)

So, nun muss ich nur noch rausfinden, wie ich wieder prüfe, ob er am Anfang ist. *grübel*

izaseba
14.08.2005, 23:16
na klar Z zeiger ist eine 16 Bit Zahl und da muß Low und High geprüft weden, nicht nur die hälfte stell Dir mal vor Du willst wissen ob 1000 und 1001 gleich sind und schaust Dir nur die ersten Ziffern an, und zu deinem Problem, überlege wo Du ankommen willst, wo liegt Dein erster Ton ? (also in diesem Falle der letzte)

wenn Du den letzten haben wolltest hast Du geprüft, od der zeiger schon bei ((ton * 2) +laenge) lag und anders rum? vielleicht bei (ton *2) ups....
Ich lasse Dich jetzt damit alleine, übe mal mit den cp cpc sub sbc und add adc Befehlen .

Gute Nacht

Gruß Sebastian

toeoe
14.08.2005, 23:38
YES!! \:D/
Geht nun endlich :)
Hier der komplette Code:

;Programm
;CDurTonleiter rauf spielen
.include "m8def.inc"

.def tmp = r16 ;Mein Universallregister
.def tonwert = r17 ;aktueller Wert für den Ton
.def tonleiterEnde = r18 ;Hier wird gespeichert, ob die Tonleiter schon
;bis zu Ende gespielt wurde
.def lpm_reg = r0 ;Mein lpm-Register

.equ Summer = PB2 ;Summer an B.2
.equ time0 = 256-255 ;Timer0 für die Tonleiter
.equ time1 = 65536-1800 ;Damit wird der Timer1 vorgeladen, für die Sekunde
.equ daten_laenge = 7 ;Anzahl der Werte

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF1addr
rjmp pruefSek ;Interruptvektor "pruefSek:"

.org OVF0addr
rjmp timerSummer ;Interruptvektor "timerSummer:"

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;Low-Byte der obersten RAM-Adresse
out SPL, tmp

;Timer Register für Ton werden belegt, hier Timer 0
ldi tmp, (1<<CS02) ;Prescaler ist 256
out TCCR0, tmp ;Register TCCR0 ist für den Prescaller zuständig
ldi tmp, time0 ;Hier wird der Timer vorgeladen
out TCNT0, tmp

;Timer Register für Sekunde werden belegt, hier Timer 1
ldi tmp, (1<<CS12) | (1<<CS10) ;Prescaler ist 1024
out TCCR1B, tmp
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde (10000) speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"

ldi tmp, (1<<TOIE0) | (1<<TOIE1);Hier werden Interrupts nach Timer0 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

;Z-Register mit DB "tonleiter1" füllen
ldi ZH, HIGH(tonleiter1 * 2)
ldi ZL, LOW(tonleiter1 * 2)

sbi DDRB, Summer ;B.2 als Ausgang
sbi PORTB, Summer ;B.2 auf HIGH stellen

ldi tonleiterEnde, 0b00000000 ;tonleiterEnde auf 0 stellen

sei ;Interrupts zulassen

;Die Hauptschleife, die sich immer wiederholt und den Tonwert neu einliest
main:
lpm ;Daten von tonleiter1: holen
mov tonwert, lpm_reg ;erstes Byte in tmp verschieben - ersten Tonwert speichern
rjmp main ;immer wieder zurück zu main springen

;Läuft Timer1 über, so wird erstmal geprüft, ob der letzte Ton
;erreicht ist und der Timer1 wird neu vorgeladen
pruefSek:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
lpm ;Daten von tonleiter1: holen
sbrc tonleiterEnde, 0 ;Ist tonleiterEnde = 0?
rjmp subtrahiereZ ;Wenn tonleiter = 1, dann subtrahiere Z um 1

addiereZ:
adiw ZL, 1 ;Z um 1 erhöhen
rjmp pruefSek2 ;zu "pruefSek2:" springen

subtrahiereZ:
sbiw ZL, 1 ;Z um 1 erniedrigen

pruefSek2:
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
cpc ZH, tmp
brne pruefTonrueckwaerts ;zu "pruefTonaus:" springen
sbr tonleiterEnde, 1 ;Hilfsvariable.1 auf 1 setzen

pruefTonrueckwaerts:
sbrc tonleiterEnde, 1 ;Ist tonleiter.1 = 0?
rjmp regwiederholen ;Wenn NEIN, dann zu "regiwederholen:"
;Wenn JA, dann prüfen, ob erster Ton
;wieder geladen wurde
prueftonaus:
ldi tmp, LOW (tonleiter1 * 2 - 1) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH (tonleiter1 * 2 - 1) ;vergleiche HIGH-Byte
cpc ZH, tmp
brne regwiederholen
ldi tmp, (0<<CS02) ;JA -> Timer0 abstellen
out TCCR0, tmp

regwiederholen:
pop tmp
out SREG, tmp ;SREG wiederholen
pop tmp ;tmp wiederholen
reti

;Läuft Timer0 über, so wird B.2 umgeschaltet, sodass Ton
;aus dem Summer zu hören ist
timerSummer:
push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern
sbis PINB, Summer ;ist B.2 = 1?
rjmp timerSummer1 ;NEIN -> spring zu "timerSummer1:"
cbi PORTB, Summer ;JA -> setze B.2 auf 0
rjmp timerSummer2 ;springe zu "timerSummer2:"

timerSummer1:
sbi PORTB, Summer ;B.2 auf 1 setzen

timerSummer2:
out TCNT0, tonwert ;Timer0 neu vorladen
pop tmp ;SREG wiederholen
out SREG, tmp
pop tmp ;tmp wiederholen
reti

;Das sind die Werte, womit der Timer0 (Tonleiter-Timer) vorgeladen wird
;Anzahl der Wert muss GERADE sein
tonleiter1:
.db 256-55, 256-49, 256-44, 256-41, 256-37, 256-33, 256-29, 256-27

Musste allerdings bei der Prüfung, ob der erste Ton wieder erreicht ist eine 1 abziehen, sonst hät er einen Ton zu früh aufgehört.
Wäre nett, wenn mir noch eine sagen könnte, ob er nun immer noch so unübersichtlich ist.
Und ein Bedenken hab ich noch. Meine Interruptroutine für Timer1, also wo er zu "pruefSek" springt, ist die nicht zu lang?


übe mal mit den cp cpc sub sbc und add adc Befehlen .

Werd ich machen, danke.

Gruß
Thomas

toeoe
15.08.2005, 09:45
Guten Morgen ;)
Bin ja mal gespannt, was als nächstes kommt. Hab auch noch gelesen, dass man Daten auf dem EPROM speichern kann oder auch auf dem RAM. Bin mal gespannt, ob wir das auch noch machen werden.

Najo, wenn hier heut nicht grgoßartiges läuft, werd ich denk ich mal ein Lauflicht programmieren und evtl. dann noch eine Ampel.

Gruß
Thomas

Florian
15.08.2005, 14:12
Moin!

Najo, wenn hier heut nicht grgoßartiges läuft, werd ich denk ich mal ein Lauflicht programmieren und evtl. dann noch eine Ampel.
Tu das!
Je mehr Du praktisch versuchst etwas selbstständig zu erarbeiten, desto besser wirst Du!

Gruß Flori

toeoe
15.08.2005, 14:15
Jup. Das Lauflicht geht ja, aber siehe selbst ;)
https://www.roboternetz.de/phpBB2/viewtopic.php?t=11730

michaelb
15.08.2005, 14:21
Hallo Zusammen,
ich acker gerade das Tutorial durch! *puh* *schwitz*
leute ich bekomm die Krise wegen dem hier: *g*


ldi temp , LOW ((daten * 2) + daten_laenge) ; vergleiche LOW-Byte
cp ZL , temp

ldi temp , HIGH ((daten * 2) + daten_laenge) ; vergleiche HIGH-Byte
cpc ZH , temp
das kennt ihr schon!! *gg* ich freu mich schon wenn das verstanden habe!!
Gruß Michi

toeoe
15.08.2005, 14:31
Ohja, das ist ein sehr kompliziertes Thema.
Les dir mal das Gespräch von gestern abend von Sebastian und mir durch. Das hier nochmal zu erklären bringt es glaube ich nicht. Wenn du dann noch fragen hast, dann kannst du sie immer noch stellen.

Gruß
Thomas

michaelb
15.08.2005, 14:33
Ja denkst du was ich seit mehr als ner Stunde mache!!! *g*
Ich versuch es erstmal alleine hinzubekommen!
Gruß Michi

toeoe
15.08.2005, 14:37
Ja, also ich finde, besser als es Sebastian mir erklärt hat, kann man es schon gar nicht mehr. Ich habs dabei verstanden und ich bin ja auch net der Schlauste ;)
Schaffste schon.

Gruß
Thomas

michaelb
15.08.2005, 14:40
auf welcher Seite hier ist das? blick da nimmer so ganz durch! ich such gerade auf Seite 13/14 das ist aber von Samstag!
Gruß Michi

toeoe
15.08.2005, 14:42
Auf Seite 20 in der Mitte fängt es an.

Gruß
Thomas

So, nun erstmal was essen.

michaelb
15.08.2005, 14:46
Danke!! Da wird es besser erklärt als auf Seite 13/14!!
Gruß Michi

michaelb
15.08.2005, 14:56
Hallo,
wie kann ich im Simulator schauen was in den egistern r16 und SREG passiert?
Gruß Michi

toeoe
15.08.2005, 15:00
Wie du den Sumulator startest, weißt du?
Einfach auf den blauen Pfeil oben klicken oder Debug -> Start Debugging.
Dann brauchst du das Fenster "Workspace", wenn das noch nicht da ist, klicke auf View -> Workspace.
Hier siehst du oben die Register und SREG ist unter I/O Atmega8 (oder was du eben hast) -> CPU versteckt.

Gruß
Thomas

michaelb
15.08.2005, 15:09
Hi,
des isch ja cool!!!
hab ne Frage:
bei diesen Zeilen:


ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
cp ZL, tmp
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte cpc ZH, tmp

ist ((tonleiter1 * 2) + daten_laenge) doch immer gleich, oder?
Gruß Michi

Florian
15.08.2005, 15:12
ist ((tonleiter1 * 2) + daten_laenge) doch immer gleich, oder?Ja, das ist eine oben festgelegte Konstante, also konstant, immer gleich!

toeoe
15.08.2005, 15:13
Willst du wissen, ob:
((tonleiter1 * 2) + daten_laenge)
immer den gleichen Wert hat? Jo, dort ändert sich nichts. Es ändert sich immer nur das Z-Register, dass dann auf einen anderen Wert zeigt.

So, nun aber wirklich essen *g*

Gruß
Thomas

[edit]
Da war der Florian schneller ;)

michaelb
15.08.2005, 15:24
danke danke!!
Ich glaube ich hab es verstanden!
Die Zeile:
ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) ;vergleiche LOW-Byte
und die Zeile:
ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) ;vergleiche HIGH-Byte
sind nur dazu da das Register tmp wieder mit der Adresse des letzten Bytes von Daten zu füllen!
Und cp ZL, tmp und cpc ZH, tmp vergleichen die aktuelle Adresse mit der Endadresse wenn die gleich ist dann wird das Z Bit gesetzt und mit breq ende springt das Programm zu Ende! Ich hoffe meine Theorie stimmt so!
Gruß Michi

izaseba
15.08.2005, 17:28
@Michi, ich freue mich, daß Du das verstanden hast, es wird langsam mit den 16 Bit Zahlen...

@Thomas, es freut mich auch, daß Du Dir selber Aufgaben ausdenkst, Übung macht den Meister, wie der Florian schon sagte.
Wie ist das mit dem Lauflicht ? wie lange muß es Raus aus der Steckdose sein?

Gruß Sebastian

toeoe
15.08.2005, 17:31
@Sebastian: Das weiß ich ja eben nicht genau, hab nochmal genau untersucht. Also es kam auch vor, dass ich länger gewartet habe (halbe Minute) und das es dann trotzdem nicht ging, also die rote LED leuchtet dann einfach.
je länger ich warte, desto wahrscheinlicher ist es, dass es dann wieder funktioniert.
Denkst du, dass an meinem Board etwas kaputt ist?

Gruß
Thomas

michaelb
15.08.2005, 17:50
Hi,
freut mich auch dass ich es verstanden habe!! Es was einfacher als ich dachte!! Stimmt das jetzt auch was ich da geschrieben habe? Was glaub ich mein Problem war: Ich dachte der Ausdruck (tonleiter1 * 2) wird von Byte zu Byte größer was ja eben falsch ist! Ich denk mal ihr benutzt den Ausdruck damit man später nur daten_laenge verändern und nicht überall im Code wo ldi tmp, LOW ((tonleiter1 * 2) + daten_laenge) und ldi tmp, HIGH ((tonleiter1 * 2) + daten_laenge) vorkommt! Assembler macht langsam echt Spaß! Damit lernt man echt was über den Controller! Und nicht solche Highlevel Befehle wie bei Bascom!
Gruß Michi

izaseba
15.08.2005, 17:52
Hmmm, hört sich komisch an, ich hab mir das Myavr Board angeguckt, finde die Resetbeschaltung etwas komisch ohne Pullup Widerstand einfach an dem Parport....
oder habe ich da was an den Augen ?
Was ich tippe, daß da noch ein Kondensator geladen ist, und der Mega weiterhin läuft.
Ich würde mal folgendes versuchen:
1 Stück Draht und wenn der Stecker gezogen ist !!!!! einfach die zwei Anschlüße von C6
kurzschließen, vielleicht hilft das.
Aber ich übernehme keine Haftung, wenn was ist!

toeoe
15.08.2005, 17:58
Aber glaub gar nicht, dass wir schon am Ende sind ;) So wie ich das sehe, gibt es noch sooooooooooo viele Befehle *g*

Gruß
Thomas

izaseba
15.08.2005, 17:59
freut mich auch dass ich es verstanden habe!! Es was einfacher als ich dachte!! Stimmt das jetzt auch was ich da geschrieben habe? Was glaub ich mein Problem war: Ich dachte der Ausdruck (tonleiter1 * 2) wird von Byte zu Byte größer was ja eben falsch ist!

nein der Bleibt konstant, das einzigste was sich verändert ist der Z zeiger, aber nur nach adiw oder sbiw !


Assembler macht langsam echt Spaß! Damit lernt man echt was über den Controller! Und nicht solche Highlevel Befehle wie bei Bascom!

Da hast Du recht, man muß sich aber vor den Augen halten, welche Leistung die
Bascom Entwickler erbracht haben, damit aus einem Eizeiler, Maschinencode entsteht.

Es ist auch manchmal ganz nützlich über den Tellerrand zu schauen!

Gruß Sebastian

toeoe
15.08.2005, 18:03
Ich würde mal folgendes versuchen:
1 Stück Draht und wenn der Stecker gezogen ist !!!!! einfach die zwei Anschlüße von C6
kurzschließen, vielleicht hilft das.
Aber ich übernehme keine Haftung, wenn was ist!

Ich hab bei PortC eh nur von 0 - 5. Wüsste also eh nicht, welche ich da genau verbinden soll. Ich denk, ich lass das lieber, nicht dass da wirklich noch was kaputt geht, dann wende ich mich lieber gleich an den Support, dann sind die wenigstens schuld und ich bekomm nen neues, falls es kaputt geht :lol:
Kann man das denn vlt. irgendwie Softwaremäßig lösen?
Komisch ist ja auch, dass es nicht bei jedem Programm ist.

Gruß
Thomas

izaseba
15.08.2005, 18:15
Ich hab bei PortC eh nur von 0 - 5

:-& ich meinte eigentlich Kondensator 6 und nicht PortC :-s
dann lass das wirklich lieber, und frag bei Myavr nach, ich schaue mir das Programm von Dir an.

P.S. bei einem Lauflicht sind die Befehle lsl register, lsr register, rol register, ror register, vielleicht noch swap register sehr nützlich, schade daß Du nur 3 LED's hast.

Schaue Dir die Befehle an und dann wirst Du sehen, wie einfach ein Lauflicht gemacht wird,
also sim an,ein register mal mit 10101010 beladen, und schauen was passiert, andere Werte auch ausprobieren !

Gruß Sebastian

Edit: achja und schön SREG und Carry dabei beobachten !!!
dann sagst Du uns hier was rol und ror machen :-)

toeoe
15.08.2005, 18:18
Ok, hab grad ne mail an Support geschrieben, scheinen ja sehr nett zu sein :)
Mal schaun, was dann als Antwort kommt. Die Befehle hab ich ja noch nie gehört, werd sie mir gleich anschauen, muss also meine Ampel noch ein wenig warten ;)

Gruß
Thomas

toeoe
15.08.2005, 18:58
Hey, die Befehle gefallen mir :)
Hab das nun auch gleich umgesetzt:


;Dieses Programm produziert ein Lauflicht. Jede halbe Sekunde geht die
;nächste LED an.
;Das Programm läuft in einer Endlosschleife durch.
.include "m8def.inc"

.equ time1 = 65536-1800 ;Damit wird der Timer1 vorgeladen, für die halbe Sekunde

.def tmp = r16 ;Mein Universallregister
.def statusLED = r17 ;In diesem Register wird gespeichert, welche LED gerade leuchtet

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF0addr
rjmp hSekEnde ;Interruptvektor für Timer0 Überlauf, hier springt
;das Programm hin, wenn der Timer überläuft

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;LOW-Byte der obersten RAM-Adresse
out SPL, tmp

ldi tmp, 0b11111111
out DDRB, tmp ;PortB als Ausgang
ldi statusLED, 0b00000010
out PORTB, statusLED ;Am Anfang soll LED1 leuchten

;Timer Register für halbe Sekunde werden belegt, hier Timer1
ldi tmp, (1<<CS12) | (1<<CS10) ;Prescaler ist 1024
out TCCR1B, tmp
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1/2 speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
ldi tmp, (1<<TOIE1) ;Hier werden Interrupts nach Timer1 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

sei ;Interrupts zulassen

main:
rjmp main ;Immer wieder die main durchlaufen

;Jede halbe Sekunde wird geprüft, welche LED an ist und dementsprechen die
;nächste LED angemacht. Die aktuelle wird dabei natürlich ausgeschaltet
hSekEnde:
push tmp ;tmp-Register auf Stack sichern
in tmp, SREG
push tmp ;SREG auf Stack sichern
;TIMER1 wird neu geladen
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
cpi statusLED, 0b00001000 ;Leuchtet die 3. LED?
breq wiederholen ;Wenn JA...
lsl statusLED ;Wenn NEIN, Register "statusLED" mit 2 multiplizieren
;dadurch verschieben sich alle Bits um 1 nach link ->
;nächste LED blinkt
rjmp ende

wiederholen:
ldi statusLED, 0b00000010 ;Wieder erste LED leuchten lassen

ende:
out PORTB, statusLED ;nächste LED leuchtet
pop tmp
out SREG, tmp ;SREG wiederherstellen
pop tmp ;tmp wiederherstellen
reti ;springe wieder dahin, wo du hergekommen bist

Funktioniert alles wunderbar. Mit diesem Code hab ich auch nicht mehr das Problem, dass ab und zu das Lauflicht nicht mehr geht. Ich kann das Netzteil an und ausschalten wie ich will, das Lauflicht geht immer.
Komisch naja...ich hab dem Support ja meinen Code mitgeschickt, vlt. ist ja daran doch was nicht in Ordnung. :-k

PS: Hab durch die neuen Befehle 8 Zeilen gespart :) Hört sich wenig an, aber bei Assembler muss man ja "sparsam" sein ;)

izaseba
15.08.2005, 19:10
Assembler muss man ja "sparsam" sein

:-k

:-)

Nebenbei hast Du auch gelernt, wie man Multiplikation und Division durch zwei im Assembler
macht .

toeoe
15.08.2005, 20:08
Ich schäme mich diese Frage zu stellen, aber wie bekomme ich ein 16 Bit Register definiert? Hier erstmal der Code, vlt. verstehst du es dann besser:


;Dieses Programm produziert ein Lauflicht. Jede halbe Sekunde geht die
;nächste LED an.
;Das Programm läuft in einer Endlosschleife durch.
.include "m8def.inc"

.equ time1 = 65536-1 ;Damit wird der Timer1 vorgeladen, für die halbe Sekunde

.def tmp = r16 ;Mein Universallregister
.def statusAmpel = r17 ;Hier wird gespeichert, welche Lampen grad leuchten
.def zeitwert = r18 ;Hier wird der Wert gespeichert, mit dem der Timer1 vorgeladen wird
.def lpm_reg = r0 ;Mein lpm-Register

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF0addr
rjmp zeitum ;Interruptvektor für Timer0 Überlauf, hier springt
;das Programm hin, wenn der Timer überläuft

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;LOW-Byte der obersten RAM-Adresse
out SPL, tmp

ldi tmp, 0b11111111
out DDRB, tmp ;PortB als Ausgang
ldi statusAmpel, 0b00000010
out PORTB, statusAmpel ;Am Anfang LEDrot an

;Timer Register für halbe Sekunde werden belegt, hier Timer1
ldi tmp, (1<<CS12) | (1<<CS10) ;Prescaler ist 1024
out TCCR1B, tmp
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1/2 speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
ldi tmp, (1<<TOIE1) ;Hier werden Interrupts nach Timer1 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

;Z-Register mit DB "timerwerte" füllen
ldi ZH, HIGH(timerwerte * 2)
ldi ZL, LOW(timerwerte * 2)

sei ;Interrupts zulassen

main:

rjmp main ;Immer wieder die main durchlaufen

zeitum:
push tmp ;tmp auf Stack sichern
in tmp, SREG
push tmp ;SREG auf Stack sichern
cpi statusAmpel, 0b00000010 ;Ist rot an?
breq rotgelban ;JA -> gelb dazuschalten
cpi statusAmpel, 0b00000110 ;Ist rot UND gelb an?
breq gruenan ;JA -> grün anmachen
cpi statusAmpel, 0b00001000 ;Ist grün an?
breq gelban ;JA -> gelb anmachen

;Wenn alle drei Rechnungen != 0 ergeben, dann ist gelb an
rotan:
sbiw ZL, 1 ;nimm den vorherigen Wert aus der db (lange Zeit)
ldi statusAmpel, 0b00000010 ;rot an
rjmp ende

rotgelban:
adiw ZL, 1 ;nimm den nächsten Wert aus der db (kurze Zeit)
ldi statusAmpel, 0b00000110 ;rot und gelb an
rjmp ende

gruenan:
sbiw ZL, 1 ;nimm den vorherigen Wert aus der db (lange Zeit)
ldi statusAmpel, 0b00001000 ;grün an
rjmp ende

gelban:
adiw ZL, 1 ;nimm den nächsten Wert aus der db (kurze Zeit)
ldi statusAmpel, 0b00000100 ;gelb an

ende:
lpm ;Wert aus der db in r0 schreiben
mov zeitwert, lpm_reg ;den aktuellen Zeitwert aus r0 lesen
;TIMER1 wird neu geladen
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1 Sekunde speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"

out PORTB, statusAmpel ;Ampel richtig umschalten
pop tmp ;SREG vom Stack holen
out SREG, tmp
pop tmp ;tmp vom Stack holen
reti ;spring dahin zurück, wo du hergekommen bist

timerwerte:
.db 65536-7200, 65536-1800
Diese beide Zeilen:

ldi tmp, HIGH(time1)
ldi tmp, LOW(time1)
muss ich noch ändern. Anstelle von time1 muss zeitwert rein, aber da zeitwert kein 16Bit Register ist, gibts Probleme.

Bitte nicht hauen :( *gg*
Ich weiß ja, dass ich für ein 16 Bit Register 2 8 Bit Register brauche, weiß aber leider nicht, wie ich die miteinander verbinde und vor allem, wie ich dann dort eine 16 Bit Zahl einlese. Geht sicher nicht so einfach wie mit dem Timer1, oder?

Gruß
Thomas

michaelb
15.08.2005, 20:15
Hi Leute,
bin bald auf dem aktuellen Stand *freu* wisst ihr dass dieser Thread bei der Statistik auf Platz 1 ist!!
Glückwunsch
bis jetzt hab ich alles verstanden!! *freu*
Gruß Michi

izaseba
15.08.2005, 20:23
Hallo Thomas,
ich weiß daß es schwer ist Tutorials zu lesen, aber Du hast Dir den Tutorial von
www.avr-asm-tutorial.net nicht angetan!

Lese Dir Aufmerksam Kapitel Register/Pointerregister durch.
Und gib acht was dort zu .db und .dw steht.

Gruß Sebastian

Edit:
@Michi, Ich freue mich, daß Du auch gut vorankommst!
Wenn ich eben fragen darf, welches Board benutzt Du ?

toeoe
15.08.2005, 20:48
Ich krieg das nicht hin :(
Ich muss doch die Register r24 und r25 benutzen, oder?

Anwendbar sind die beiden Befehle auf die Registerpaare X, Y und Z sowie auf das Doppelregister R24/R25, das keinen eigenen Namen hat und auch keinen Zugriff auf RAM- oder sonstige Speicher ermöglicht. Es kann als 16-Bit-Wert optimal verwendet werden.
Aber ich weiß nicht, wie ich das verwenden soll.

.def zeitwertL = r24
.def zeitwertH = r25
[....]
lpm ;Wert aus der db in r0 schreiben
mov zeitwertL, lpm_reg ;den aktuellen Zeitwert aus r0 lesen
mov zeitwertH, lpm_reg
;TIMER1 wird neu geladen
ldi tmp, HIGH(zeitwertH) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(zeitwertL) ;für die 1 Sekunde speichern ->
out TCNT1L, tmp
Das funktioniert ja nicht.

Es ist doch richtig, dass lpm_reg, also r0, ein 16 Bit Register ist, oder? Also das in r0 eine 16 Bit-Zahl gespeichert ist. Mit meinen zwei "mov"-Befehlen bewirke ich da relativ wenig befürchte ich. Das kann doch nicht so schwer sein ](*,)

izaseba
15.08.2005, 21:08
Thomas #-o



Ich muss doch die Register r24 und r25 benutzen, oder?




Doppelregister R24/R25, das keinen eigenen Namen hat und auch keinen Zugriff auf RAM- oder sonstige Speicher ermöglicht.

Muß ich noch was dazusagen ?



Es ist doch richtig, dass lpm_reg, also r0, ein 16 Bit Register ist, oder

#-o #-o

Lese Tutorial Register/was?



Also das in r0 eine 16 Bit-Zahl gespeichert ist

#-o #-o

Wer hat Dir das gesagt ?



Das kann doch nicht so schwer sein


:-k :-k ich habe es gehofft, aber scheinbar ist es so.

toeoe
15.08.2005, 21:19
Also das in r0 eine 16 Bit-Zahl gespeichert ist
#-o #-o
Wer hat Dir das gesagt ?

Ich dachte, weil in r0 immer der aktuelle Wert aus der db steht, und da stehen ja nun 16 Bit Werte drinne.
:(
Das heißt, ich brauche einen zweiten Pointer-Register? Aber denn kann ich ja dann auch nicht mit r0 füllen, wenn in r0 anscheinend schon das falsche steht :(

izaseba
15.08.2005, 21:36
und da stehen ja nun 16 Bit Werte drinne.

Und wer hat Dir das gesagt ?



Das heißt, ich brauche einen zweiten Pointer-Register?
Wozu einen Zweiten Pointerregister ?

zu Deinem Vorhaben, ich habe Dir schon gesagt, Schaue im tutorial nach .db und .dw
vergess den Quatsch mit R24 u. R25 und zweitem Pointerregister,
Ich helfe Dir diesmal nicht, da kommst Du selber drauf, sonst lernst Du das nie...

Das Tutorial beinhaltet Antworten auf alle Deine Fragen.

Gruß Sebastian

toeoe
15.08.2005, 22:49
:( Ich schaffs leider wirklich nicht.
Nehmen wir mal das hier:

lpm
mov zeitwert, lpm_reg
In zeitwert steht dann 227, obwohl da eigentlich 61936 (65536-3600) stehen müsste.

timerwerte:
.dw 65536-7200, 65536-3600
Hab jetzt rausgefunden, dass ich anstelle von db dw nehmen muss. Denn nur mit dw kann man Werte über 8Bit speichern (stimmt das denn wenigstens?)
Aber wie ich diese eben auslesen kann, wird aus dem Tutorial nicht wirklich klar.
Ich kann anstelle von dem hier:

ldi tmp, HIGH(HIGH (timerwerte * 2))
out TCNT1H, tmp
ldi tmp, LOW(LOW (timerwerte * 2))
out TCNT1L, tmp
hinschreiben, was ich will, nichts geht.
Ich weiß, dass ich "HIGH(timerwerte * 2))" in beiden Zeilen durch etwas ersetzen muss. Eigentlich durch einen 16 Bit Register, oder? Aber ich hab ja keinen außer dem Z-Register.
Und das hier geht ja nicht:

ldi tmp, HIGH(ZH)

Mir wäre es doch auch lieber, wenn du mir dabei nicht hilfst, aber ich weiß nimmer, was ich da noch ändern soll.

Gruß
Thomas

izaseba
15.08.2005, 23:18
Thomas, Du tust mir wirlich leid, ich weiß nicht, wie ich dir das sonst erklären kann :frown:

Schade, daß Du soweit wohnst sonst würde ich Dich einladen und wir könnten das in ruhe durchgehen O:)

Schau mal hier

In zeitwert steht dann 227, obwohl da eigentlich 61936 (65536-3600) stehen müsste.

Es kann in einem Register nicht 61936 stehen!, das habe ich Dir doch gestern gesagt, ALLE
Register beinhalten 8 Bits

16 Bit stehen ja in zwei Registern!

wenn Du Deine 61936 auslesen willst mußt Du dein ZPointer auf die erste adresse einstellen also z.B.
daten:
.dw 61936

ldi ZH,HIGH(daten *2)
ldi ZL,LOW(daten *2)
jetzt kommt ein lpm

und was steht im Tutorial ?



.DW 12345,6789 ; zwei Worte
......
Beim Lesen per LPM erscheint übrigens das niedrigere Byte der 16-Bit-Zahl zuerst!


und was steht im R0 ?
das niedrigere Byte der 16-Bit-Zahl

also das low Byte von daten (hier von 61936 weil der ZPointer auf sie zeigt)
wenn Du sie jetzt verschiebst nach z.B. zahlLOW(von mir unbenanter Register)
hast Du die hälfte schon drin.
jetzt adiw ZL,1
hiermit gehen wir im .dw ein Byte weiter... jetzt zeigt der Zpointer auf die andere hälfte der Zahl
und was steht dann im R0 nach einem lpm ? das HighByte von daten also von 61936

das kannst Du auch von mir aus nach zahlHIGH schieben und dann hast Du Deine 16Bit Zahl aus dem Speicher ausgelesen.... sollte im .dw noch ein Komma stehen und eine zweite Zahl wirst Du wohl nach einem erneutem adiw ZL,1 wohl bei dieser Zahl landen und der Vorgang widerholt sich , genau so bei einer dritten fünften... 1000 Zahl .

Du mußt Dir in klarem sein, daß es keine 16 Bit Register gibt!! auch der ZPointer ist kein 16 Bit Register, es ist ein Registergespann ZL und ZH also 2 Register.

Edit:
WICHTIG !

Auszug aus dem Dattenblatt Seite 77:




To do a 16-bit write, the High Byte must be written before the Low Byte.
For a 16-bit read, the Low Byte must be read before the High Byte


Muß man unbedingt beachten, sonst kommt dabei nur mißt raus!

Ich hoffe, daß es jetzt verstanden wurde :-k
Spiel das im sim durch, gucke, wo der Zeiger hinzeigt, was er ausliest, und soweiter,
es wird schwer sein die 16 Bits zu zerschlagen, mach das genauso wie ich dir das gestern gesagt habe, wandle eine 16Bit zahl (die Du im .dw stehen hast)in binär um mache schnitt damit Du 2x 8 Bit hast rechne aus was das für dezimale werte sind,
und beobachte, was er Dir mit lpm in den R0 lädt, aber bedenke Low zuerst!

Gute Nacht[/quote]

toeoe
16.08.2005, 00:44
Schade, daß Du soweit wohnst sonst würde ich Dich einladen und wir könnten das in ruhe durchgehen
Das wär natürlich das Highlight :)
Aber ich denk, nun hab ich das verstanden. Mit ein wenig Routine noch bekomm ich das schon auf die Reihe *zuversichtlich sei*
Nun liest er die Werte wenigstens schonmal aus (glaub ich zumindest).
Aber die Zeitabstände stimmen noch nicht so ganz, muss ich dann nochml am Tage schauen, ich werd nu auch erstmal ins Bett gehen.

Danke schonmal :)
Hätts sonst wirklich nicht hinbekommen :(

Gruß und Gute Nacht
Thomas

michaelb
16.08.2005, 09:14
Guten Morgen!
Jemand schon wach? :D
hab ne Frage was bringt das eigentlich:


push tmp ;tmp sichern
in tmp, SREG
push tmp ;SREG sichern

und das:


pop tmp ;SREG wiederholen
out SREG, tmp
pop tmp ;tmp wiederholen

ich weiß schon dass mit den Befehlen das Statusregister und das Universalregister r16(tmp) auf dem Stack abgelgt wird und dem Befehl pop wieder vom Stack geholt wird aber was ich nicht kapier wozu das ganze? Im Statusregister werden doch die ganzen Flags gesetzt?
Gruß Michi

toeoe
16.08.2005, 09:41
Hi,

stell dir mal vor im Register tmp hast du wichtige Werte, die du später (nach der Interruptroutine) noch brauchst. Aber das Register tmp brauchst du auch in der Interruptroutine. Dann sichert man das alles lieber erstmal auf dem Stack und kann dann die Register benutzen wie man will und mit pop holt man sich die alten Werte wieder zurück.

Hoffe, das ist verständlich :)
Gruß
Thomas

michaelb
16.08.2005, 09:50
Hi,
ok danke hab es verstanden! Ihr sichert die Register also immer kurz auf dem Stack da ihr sie kurzzeitig für nen anderen Zweck verwenden wollt!
Was mir auffällt das es nicht immer ne Interruptroutine ist! Oft ist es ein ganz normaler abschnitt der dazwischen kommt! Aber woher weißt du wann du die Dateien speichern sollt, denn der Interrupt kommt ja ohne Vorwarnung und verändert die Register!
Gruß Michi

toeoe
16.08.2005, 10:21
Der Interrupt kommt ohne "Vorwarnung", ja, aber du weißt do, wo er hinspringt, wenn der Interrupt aufgerufen wird. Also kann du dort dann auch die Register sichern.

Was mir auffällt das es nicht immer ne Interruptroutine ist! Oft ist es ein ganz normaler abschnitt der dazwischen kommt!
Da isses ja genauso, da weißte ja auch, wann er da und da ist und kannst deine Register sichern. Obwohl ich das mittem im Programm für unsinnig halte *denk* Außer man ist ganz knapp mit den Registern, weiß ja nicht.

Gruß
Thomas

michaelb
16.08.2005, 10:36
Ok dass hab verstanden! Hab noch ne Frage wegen des Simulators!:
Wenn ich mich mit Step into(F11) durch den ode klicke komme ich nie in die Interrupts! Ich bleibe immer in der Mainschleife hängen und kann so nie die Interrupts mit den ganzen Timersachen kontrollieren!! Wie macht ihr das?
Gruß Michi

toeoe
16.08.2005, 10:58
Timer im Simulator zu simulieren ist sehr zeitaufwendig.
Wenn du im Simulator bist, dann schau mal in deinem Workspace unter Processor -> Stop Watch und drücke dann wieder F11, da siehst, wie langsam sich die Zeit dort ändert. Ich empfehle dir, bevor du simulierst, deine ganzen Timer auf weniger als 1 Sekunde einzustellen, sonst sitzt du ziemlich lange davor.
Also:
1. Simulieren mit Timer, nur, wenn sie < 1 Sekunde sind
2. Dann "Start Debugging", kennste ja schon
3. Dann klickst du mit der Maus auf die Zeile, wo er nach dem Timerüberlauf hinspringen soll, also z.b hier:

.org OVF0addr
rjmp zeitum ;Interruptvektor für Timer0 Überlauf, hier springt
;das Programm hin, wenn der Timer überläuft
Da blinkt dann also der Cursor.
4. Dann klickst du auf "Run to Cursor. Entweder dieses Symbol ->{} oder Strg + F10 oder unter Debug -> Run to Cursor. Wenn du 1 Sekunde eingestellt hast, siehst du schon, wie lange dein Rechner braucht, bis er dort angekommen ist.
Ich stelle meine Timer meistens auf 256-1, sodass er gleich dort hinspringt, wenn ich auf Run to Cursor klicke.

Hoffe, dir geholfen zu haben
Thomas

toeoe
16.08.2005, 11:49
So, hab die Ampel hinbekommen, aber leider nicht mit der Nutzung von:

timerwerte:
.dw 65536-7200, 65536-1800

Ich zeig hier erstmal den Code, der funktioniert. Also die rote und grüne Phase sollen logischerweise länger dauern als das Umstellen von gelb auf grün oder von gelb auf rot. Ihr wisst ja, wie eine Ampel funktioniert ;)

.equ time1 = 65536-7200
.equ time12 = 65536-1800
Meine 2 Timer, wie man sieht, der erste ist der lange Timer (für rote und rüne Phase) und der zweite Timer ist der kurze (für das Umschalten von gelb auf grün/rot)

ldi tmp, HIGH(time1)
out TCNT1H, tmp
ldi tmp, LOW(time1)
out TCNT1L, tmp
Timer wird bei "reset:" vorgeladen, also am Anfang ist die Ampel rot, deswegen der lange Timer. Bei der Interruptroutine (zeitum), also wenn der Timer überläuft, wird dann abgefragt:

zeitum:
push tmp ;tmp auf Stack sichern
in tmp, SREG
push tmp ;SREG auf Stack sichern
cpi statusAmpel, 0b00000010 ;Ist rot an?
breq rotgelban ;JA -> gelb dazuschalten
cpi statusAmpel, 0b00000110 ;Ist rot UND gelb an?
breq gruenan ;JA -> grün anmachen
cpi statusAmpel, 0b00001000 ;Ist grün an?
breq gelban ;JA -> gelb anmachen

rotan:
ldi tmp, HIGH(time1)
out TCNT1H, tmp
ldi tmp, LOW(time1)
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
ldi statusAmpel, 0b00000010 ;rot an
rjmp ende

rotgelban:
ldi tmp, HIGH(time12)
out TCNT1H, tmp
ldi tmp, LOW(time12)
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
adiw ZL, 1
ldi statusAmpel, 0b00000110 ;rot und gelb an
rjmp ende

gruenan:
ldi tmp, HIGH(time1)
out TCNT1H, tmp
ldi tmp, LOW(time1)
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
sbiw ZL, 3
ldi statusAmpel, 0b00001000 ;grün an
rjmp ende

gelban:
ldi tmp, HIGH(time12)
out TCNT1H, tmp
ldi tmp, LOW(time12)
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
adiw ZL, 1
ldi statusAmpel, 0b00000100 ;gelb an
Folgende Info erstmal:
0b00000010 <-- rot
0b00000110 <-- rot und gelb
0b00001000 <-- grün
0b00000100 <-- gelb
Also je nachdem was an ist, wechselt er die LEDs und lädt den Timer neu. Dieser Code funktioniert auch einwandfrei, nur nun wollt ich ja eigentlich mit dem Speichern arbeiten (wie oben schon erwähnt), aber irgendwie hab ich da einen logischen Fehler drinne, der sich einfach nicht finden lässt, also folgendes:
Ich brauche dann oben nur noch eine Konstante für den Timer:

.equ time1 = 65536-7200
[...]
ldi tmp, HIGH(time1)
out TCNT1H, tmp
ldi tmp, LOW(time1)
out TCNT1L, tmp
Dieser Timer wird wieder wie gewohnt im "reset:" vorgeladen (rot ist von Anfang an an.
Und hier der Code der Interruptroutine:

zeitum:
push tmp ;tmp auf Stack sichern
in tmp, SREG
push tmp ;SREG auf Stack sichern
cpi statusAmpel, 0b00000010 ;Ist rot an?
breq rotgelban ;JA -> gelb dazuschalten
cpi statusAmpel, 0b00000110 ;Ist rot UND gelb an?
breq gruenan ;JA -> grün anmachen
cpi statusAmpel, 0b00001000 ;Ist grün an?
breq gelban ;JA -> gelb anmachen

;Wenn alle drei Rechnungen != 0 ergeben, dann ist gelb an
rotan:
sbiw ZL, 3
ldi statusAmpel, 0b00000010 ;rot an
rjmp ende

rotgelban:
adiw ZL, 2
ldi statusAmpel, 0b00000110 ;rot und gelb an
rjmp ende

gruenan:
sbiw ZL, 3
ldi statusAmpel, 0b00001000 ;grün an
rjmp ende

gelban:
adiw ZL, 1
ldi statusAmpel, 0b00000100 ;gelb an

ende:
lpm
;TIMER1 wird neu geladen
mov tmp, lpm_reg
out TCNT1L, tmp
adiw ZL, 1
mov tmp, lpm_reg
out TCNT1H, tmp

out PORTB, statusAmpel ;Ampel richtig umschalten
pop tmp
out SREG, tmp
pop tmp
reti

Nun zur Erklärung, warum ich wie Z hoch oder runterzähle.
Meine Werte sind ja wie folgt beschrieben:

timerwerte:
.dw 65536-7200, 65536-1800
Am Anfang ist ja rot an, diese Lampe bleibt solange an, bis der Timer1 überläuft. Z zeigt zu diesem Zeitpunkt noch auf das LOW-Byte von 65536-7200 (oder?). Dann springt er zu "rotgelban:" dort addiert er den Zeiger mit 2, sodass er dann auf das LOW-Byte von 65536-1800 (der kurzen Zeit) zeigt. Wenn ich das nur mit 1 addieren würde, dann würde er ja auf das HIGH-Byte von der langen Zeit zeigen, also quatsch.
So, er schreibt als LOW (kurze Zeit) in TCNT1L, addiert Z mit 1 und schreibt dann HIGH (kurze zeit) in TCNT1H.
Jetzt zeigt Z auf das HIGH-Byte von der kurzen Zeit.
Beim nächsten Überlauf springt er zu "gruenan", dort subtrahiert er mit 3.
Also müsste er wieder auf das LOW-Byte von 65536-7200, also von der langen Zeit zeigen. Schreibt das dann in TCNT1L, addiert Z mit 1 und schreib das dann in TCNT1H usw. bis wieder rot ist.
Problem ist aber, dass die Zeit anscheinend nicht stimmt. Rot bleibt richtig an, da liegt noch nicht das Problem (ist ja auch oben bei "reset:" vorgeladen). Doch RotGelb bleibt genauso lange an, obwohl ich den Timer eigentlich mit der kurzen Zeit vorgeladen habe. Und das geht dann so weiter, ein totales Durcheinander.

Ich hoffe, ich hab das einißgermaßen verständlich rübergebracht, ist leider ein wenig lang geworden mein Beitrag :(
Aber ich hoffe trotzem, dass mir einer bei meinem (kleinen?) Problem helfen kann.

Gruß
Thomas

Tekeli
16.08.2005, 14:54
Moin moin,

ich habe auch ein paar Fragen.

16-bit Timer:
Wofür ist TCCR1A gut? Wieso haben wir TCCR1B benutzt?

Interrupts:
Wird eine Interruptbehandlugsroutine von einem anderen
Interrupt unterbrochen oder nicht?

Sonst kann es ja passieren, dass beim Laden des 16-Bit Counters wir korrupte Werte in dem Counter haben. :)

Best wishes

toeoe
16.08.2005, 15:39
Hi,

ich kann dir leider nur eine deiner Fragen beantworten.

Interrupts:
Wird eine Interruptbehandlugsroutine von einem anderen
Interrupt unterbrochen oder nicht?
Nein, soweit ich weiß nicht, deshalb sollten die Interruptroutinen so kurz wie möglich gehalten werden.

Gruß
Thomas

izaseba
16.08.2005, 18:19
Hallo Leute, wie ich sehe ist Thomas wieder O:) und nicht mehr :-s und hilft sogar
anderen Leuten,

also zu den Fragen mit den push und pops von tmp, es geht auch anders!

wenn man genug Register zu Verfügung hat, kann man sich ein extra Register nur für die
Interruptsroutinen deklarieren, z.B tmpi oder z.B. meinInterruptarbeitsregister .
und damit nur in den Interrupts arbeiten, womit man dann den tmp nicht benutzen und sichern muß.

@Tekeli,
Der Timer 1 und Timer 2 haben mehrere Modes, man kann auch andere Sachen damit machen, als nur Zeit zu Zählen (sehe DattenBlatt seite 97 ) und Suche mal nach PWM , falls es Dir,Euch nichts sagt.
Bei Deinem Asuro wird z.B. der Timer 1 in 8 bit PWM mode benutzt um die Geschwindigkeit der Motoren einzustellen ich will jetzt nicht näher daraf eingehen, weiß nicht was wir mit PWM hier Programmieren könnten (Außer bei Deinem Asuro)
sehe Dir mal asuro.c an. In der Init u. in der Speed Funktion sieht man wie man das benutzen kann.
Ich hoffe , daß Dir das reicht, am sonsten zieh Dir dashier (http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Die_PWM-Betriebsart)
rein, da ist es gut beschrieben

@Thomas,

Ich tippe der Fehler liegt hier irgendwo :

rotan:
sbiw ZL, 3
ldi statusAmpel, 0b00000010 ;rot an
rjmp ende

rotgelban:
adiw ZL, 2
ldi statusAmpel, 0b00000110 ;rot und gelb an
rjmp ende

gruenan:
sbiw ZL, 3
ldi statusAmpel, 0b00001000 ;grün an
rjmp ende

gelban:
adiw ZL, 1
ldi statusAmpel, 0b00000100 ;gelb an

Bedenke, es steht zwar z.B. so:
.dw 12345,12121

drin aber in der Wirklichkeit sind das 4 Bytes
z.B. (es Stimmt nicht mit den Werten!)

10,20,30,40
Die ersten zwei gehören zu 12345 die letzten zwei zu 12121

Wenn Dein Z Zeiger auf der Low Byte von 12345 (hier 10 ) zeigt,
Du aber den zweiten wert (12121) einlesen willst mußt Du vor lpm adiw ZL,2 machen
(dann Z -> 30) einlesen, adiw ZL,1 (dann Z -> 40) und mit lpm einlesen.
Willst Du irgendwann den zweiten Wert nochmal haben sbiw ZL,1 und für den ersten sbiw ZL,3

Ich denke gelban:
adiw ZL, 1
wird falsch sein , damit landest Du "dazwischen" und jetzt kommt alles durcheinander,
verstehst Du wie ich das meine ?

Gruß Sebastian

toeoe
16.08.2005, 18:50
Klar, brauch ja dann zweimal den Befehl lpm ](*,)


Ich denke gelban:
adiw ZL, 1
wird falsch sein , damit landest Du "dazwischen" und jetzt kommt alles durcheinander,
verstehst Du wie ich das meine ?

Zwischen was genau? Zwischen einem LOW und einem HIGH-Byte?

Nehmen wir mal dein Bsp:
10,20,30,40

10 und 20 sind jeweils mein LOW- und HIGH Byte meiner langen Zeit und 30 40 die meiner kurzen Zeit, ok?

Wenn grün an ist, dann zeigt der Zeiger doch auf 20, oder? Wenn er nun die Interruptroutine aufruft und auf gelb springen will, muss er doch nur 1 addieren, damit Z auf 30 zeigt. Dann lpm - einlesen und nochmal adiw ZL, 1 und wieder lpm - einlesen.

Also ich denk das das adiw ZL, 1 bei gelban: richtig ist. Kannst mich ja noch vom Gegenteil überzeugen ;)
Hab auch beides getestet. Problem ist: Rot bleibt richtig an, dann macht er rot und gelb an und das bleibt nochmal genauso lange an, obwohl er da schon die kurze Zeit nehmen müsste. Ich kann addieren oder subtrahieren oder umstellen wie ich will, kommt nie die richtige Zeit dabei raus...komisch...

So hab ich das nun:

zeitum:
push tmp ;tmp auf Stack sichern
in tmp, SREG
push tmp ;SREG auf Stack sichern
cpi statusAmpel, 0b00000010 ;Ist rot an?
breq rotgelban ;JA -> gelb dazuschalten
cpi statusAmpel, 0b00000110 ;Ist rot UND gelb an?
breq gruenan ;JA -> grün anmachen
cpi statusAmpel, 0b00001000 ;Ist grün an?
breq gelban ;JA -> gelb anmachen

;Wenn alle drei Rechnungen != 0 ergeben, dann ist gelb an
rotan:
sbiw ZL, 3
ldi statusAmpel, 0b00000010 ;rot an
rjmp ende

rotgelban:
adiw ZL, 2
ldi statusAmpel, 0b00000110 ;rot und gelb an
rjmp ende

gruenan:
sbiw ZL, 3
ldi statusAmpel, 0b00001000 ;grün an
rjmp ende

gelban:
adiw ZL, 1
ldi statusAmpel, 0b00000100 ;gelb an

ende:
;TIMER1 wird neu geladen
lpm ;Wert aus der dw in r0 schreiben
mov tmp, lpm_reg
out TCNT1L, tmp
adiw ZL, 1
lpm ;Wert aus der dw in r0 schreiben
mov tmp, lpm_reg
out TCNT1H, tmp

out PORTB, statusAmpel ;Ampel richtig umschalten
pop tmp ;SREG vom Stack holen
out SREG, tmp
pop tmp ;tmp vom Stack holen
reti ;spring dahin zurück, wo du hergekommen bist

timerwerte:
.dw 65536-7200, 65536-1800

michaelb
16.08.2005, 20:25
Hallo Zusammen,
jetzt bin ich auf dem aktuellen Stand von euch!! *freu* hab alle Beiträge hier gelesen! Ich freu mich das ihr jetzt ein Lauflicht proggt! Denn die Sachen mit dem Summer konnte ich nicht mitmachen denn ich habe gerade keinen Summer da! Ich musste alles also theoretisch behandeln was nicht wirklich toll ist! Werde die ganzen Sachen aber nachholen! Jetzt zu ner anderen Fragen: Izaseba hat mich vor einigen Beiträgen gefragt was ich für Boards habe! Also ich drei Boards:
1.Board -Atmega16
-8 LEDs
-8 Taster

2.Board -Atmega16
-Steckplätze für GP2D12s
-RS232 Kommunikation
-4 LEDs
-2 Taster
-TWI Master

3.Board -Atmega16
-LCD Anschluss
-Servoanschluss
-2 LEDs
-TWI Slave

Auf allen Boards ist ne eigene Spannungsversorgung mit LED (die anzeigt ob Strom da ist oder nicht!) Auf allen Boards sind 16Mhz Quarze! Ich hoffe das ich das alles später mit Assembler hinbekomme! Bin da ganz zuversichtlich!
Gruß Michi

toeoe
16.08.2005, 20:31
Darf man nachfragen, wieso du so viele Boards hast, obwohl du erst anfängst? Oder sind die "vom Laster gefallen"?

michaelb
16.08.2005, 20:35
Hallo,
die Boards hab ich scho länger da ich zuerst mit C geproggt hab! C gefällt mir irgendwie net so isch halt eine Mischung auf Assembler und Basic!
Gruß Michi

izaseba
16.08.2005, 20:44
He, der Michi ist gut ausgestattet !

Es hat sich ein Fehler eingeschlichen (ich geb es zu es war meine Schuld) in Sachen 16 Bit Zahlen beschreiben und auslesen,
Ich habe es eine Seite zurück berichtigt Nochmal, falls es übersehen worden ist

16 Bit schreiben, zuerst HIGH übertragen dann LOW
16 Bit lesen, zuerst LOW dann HIGH

(das ist auch der Fehler beim Thomas Programm sehe Neubelegung des Timers
, zum Teil schon berichtigt)

Gruß Sebastian

michaelb
16.08.2005, 20:46
Hi,
warum das?
Gruß Michi

michaelb
16.08.2005, 20:51
Hallo,
nochmal zu meinen Boards hab ich alle selber geätzt! Halbes Euroformat!
Was mich aufregt das die 2 Taster des 2.Boards nicht funktionieren da ich sie falsch angeschlossen habe! Bald mach ich mir aner ein neues großes Board mit allen Sachen drauf! Am Samstag geht's erstmal in den Urlaub! So ein Mist dann kann ich gar nich mehr das Tutorial verfolgen!!!!! *ärger*
Gruß Michi

izaseba
16.08.2005, 21:45
Hi,
warum das?

Dann lese Seite 77 im Datenblatt von M8 bei M16 müßte das auch stehen...



Was mich aufregt das die 2 Taster des 2.Boards nicht funktionieren da ich sie falsch angeschlossen habe!

Deswegen habe ich auch ein Board mit kaum was drauf, aber dafür alle Pins ausgeführt, der Rest wird auf Steckboard gemacht, fertig.

P.S. Wobei eine schön geätzte Platine, mit allem drauf wäre auch toll....

Gruß Sebastian

toeoe
16.08.2005, 21:55
So, hier ist nun die funktionierende Version einer Ampel.
Vielen Dank hier auch nochmal an Sebastian, der hier mitgetüftelt hat.

;Dieses Programm zeigt eine Ampel auf drei LEDs.
;Es wird mit dem Timer1 realisiert und im Flash sind die Timerwerte gespeichert.
;Je nachdem, welcher Wert gebraucht wird, wird aus "timerwerte:" gelesen
.include "m8def.inc"

.equ time1 = 65536-7200 ;Damit wird der Timer1 vorgeladen, für die 2 Sekunden

.def tmp = r16 ;Mein Universallregister
.def statusAmpel = r17 ;Hier wird gespeichert, welche Lampen grad leuchten
.def lpm_reg = r0 ;Mein lpm-Register

.org 0x000
rjmp reset ;Interruptvektor "reset:"

.org OVF1addr
rjmp zeitum ;Interruptvektor für Timer1 Überlauf, hier springt
;das Programm hin, wenn der Timer überläuft

reset:
;Stack einrichten
ldi tmp, HIGH(RAMEND) ;HIGH-Byte der obersten RAM-Adresse
out SPH, tmp
ldi tmp, LOW(RAMEND) ;LOW-Byte der obersten RAM-Adresse
out SPL, tmp

ldi tmp, 0b11111111
out DDRB, tmp ;PortB als Ausgang
ldi statusAmpel, 0b00000010
out PORTB, statusAmpel ;Am Anfang LEDrot an

;Timer Register für halbe Sekunde werden belegt, hier Timer1
ldi tmp, (1<<CS12) | (1<<CS10) ;Prescaler ist 1024
out TCCR1B, tmp
ldi tmp, HIGH(time1) ;Für den Timer1 (16Bit) benötigen
out TCNT1H, tmp ;wir 2 Register, in denen wir den Wert
ldi tmp, LOW(time1) ;für die 1/2 speichern ->
out TCNT1L, tmp ;"TCNT1H" und TCNT2L"
ldi tmp, (1<<TOIE1) ;Hier werden Interrupts nach Timer1 Überlauf eingeschaltet
out TIMSK, tmp ;Register TIMSK ist dafür zuständig

;Z-Register mit DB "timerwerte" füllen
ldi ZH, HIGH(timerwerte * 2)
ldi ZL, LOW(timerwerte * 2)

sei ;Interrupts zulassen

main:
rjmp main ;Immer wieder die main durchlaufen

zeitum:
push tmp ;tmp auf Stack sichern
in tmp, SREG
push tmp ;SREG auf Stack sichern
cpi statusAmpel, 0b00000010 ;Ist rot an?
breq rotgelban ;JA -> gelb dazuschalten
cpi statusAmpel, 0b00000110 ;Ist rot UND gelb an?
breq gruenan ;JA -> grün anmachen
cpi statusAmpel, 0b00001000 ;Ist grün an?
breq gelban ;JA -> gelb anmachen

;Wenn alle drei Rechnungen != 0 ergeben, dann ist gelb an
rotan:
ldi statusAmpel, 0b00000010 ;rot an
rjmp ende

rotgelban:
adiw ZL, 2
ldi statusAmpel, 0b00000110 ;rot und gelb an
rjmp ende

gruenan:
ldi statusAmpel, 0b00001000 ;grün an
rjmp ende

gelban:
adiw ZL, 2
ldi statusAmpel, 0b00000100 ;gelb an

ende:
;TIMER1 wird neu geladen
lpm ;Wert aus dw in r0 schreiben
mov tmp, r0
push tmp ;LOW-Byte speichern
adiw ZL, 1
lpm
mov tmp, r0
out TCNT1H, tmp ;HIGH-Byte in Timer1 laden
pop tmp ;LOW-Byte wiederholen
out TCNT1L, tmp ;LOW-Byte in Timer1 laden

ldi ZH, HIGH(timerwerte * 2) ;Zeiger wieder auf den ersten Wert setzen
ldi ZL, LOW(timerwerte * 2)

out PORTB, statusAmpel ;Ampel richtig umschalten
pop tmp ;SREG vom Stack holen
out SREG, tmp
pop tmp ;tmp vom Stack holen
reti ;spring dahin zurück, wo du hergekommen bist

timerwerte:
.dw 65536-7200, 65536-1800
Wer Fragen zum Code hat, kann sie natürlich jederzeit stellen. Am besten vielleicht per PN, damit der Thread hier übersichtlicht bleibt. Dies gilt natürlich auch für die ganzen anderen Codes, die wir hier reingestellt haben. :)

Gruß
Thomas

izaseba
16.08.2005, 22:01
Die Aufgabe mit der Ampel schien am Anfang leicht, aber es ist nicht so, zuerst hatten wir Probleme mit laden der Werte in den Timer.
Also an dieser Stelle merken High zuerst, Low zum Schluß leider kommt beim lesen
aus .dw das LOW Byte zuerst, deswegen muß man sich den erstmal zwischenspeichern,
dazu haben wir den Stack benutzt. danach gab es Probleme mit dem Z-Pointer, der nach
einer Ampelperiode irgendwo in den Flash gewandert ist und die Ampel hat sich
wirklich seltsam verhalten, gut daß Sie an keiner Kreuzung stand...


Gruß Sebastian