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
Druckbare Version
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
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 :)
@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
@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:
Weiß nicht, wie ich das sonst anders lösen sollte, also ohne Hilfsvariable.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
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
:-k Habt Ihr das übersehen, oder war das zu einfach? :-kZitat:
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...
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 ;)
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.Code:;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
Gruß
Thomas
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
Hallo,
ich hab me Frage zu diesem hier:
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?:Code:push tmp ;Rette Universallregister
in tmp, SREG ;Rette Statusregister
push tmp
Gruß MichiCode:pop tmp ;stelle SREG wieder her
out SREG, tmp
pop tmp ;stelle Universalregister wieder her
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!