[8051] Nur eine Note abspielen((noch)kein Lied!)



  • Hallo zusammen,

    ich versuche mich gerade am ASM 8051 programmieren und über gerade eine
    einzelne Note(z.B. ein Viertel mit 440Hz) auszugeben, jedoch gibts da
    ein paar Probleme.

    Also einen Dauerton habe ich auf jedenfall schon geschafft, dies
    funktioniert einwandfrei.

    Hier das bis jetzige Programm: http://pastebin.com/jiZP43uc

    Ich soll wie gesagt einen Ton mit 440Hz 600ms lang spielen(entspricht
    einer Viertelnote).

    Der verwendete 8051er hat 24MHz/12=2MHz Taktfrequenz

    Mit dem Timer1 will ich zählen bis 600ms vorbei sind und mit Timer0 wird
    der Takt des Tones erzeugt.

    Timer1 wird im Mode 1 betrieben, also 16-Bit-Timer. Es gibt 2 Register
    TH1 und TL1. Wenn TL1 Overflow, dann wird TH1 inkrementiert, d.h. man
    kommt am ende auf 256*256 verschiedene Werte. Wenn TH1 Overflow ist dann
    springt der auf die Interruptadresse(flash: 001BH und beim Monitor:
    801BH).

    Timer0 soll gestoppt werden wenn Timer1 zur Interruptadresse springt.

    Hier die Formel zur Berechnung des Start-Values von Timer1:
    f(x) = 65536 - (x*10^3*2); x=600ms; 256*256=65536; --> f(x)=startwert =
    negativer wert

    Im T1_ISR stoppe ich den Timer0. Mehr brauche ich eigentlich hier nicht
    oder?

    Mein Problem ist es, das ich nicht ganz weiß wie ich das umsetzen soll.
    Ich haben wir aufgeschrieben:

    PlayTone(R0) --> PlayTone aufrufen und R0 übergeben
    T0_INIT Reload --> T0 auf autoreload Mode 2 setzen
    T0 starten --> SETB TR0
    RET --> zurück zum Main
    PlayNote(R0,R1) --> PlayNote aufrufen und R0,R1 überegeben
    Timer1 laden/starten
    CALL PlayTone --> wieder aufrufen? ich glaube das gehört weg, oder seht
    ihr einen Sinn darin?

    R1mal warten auf TF=1(Überlauf) --> das erklärt das 4*150 - R1=4 in dem
    Fall
    T0 stoppen --> das mach ich ja im T1_ISR(CLR TR0)

    Wenn ich mir jetzt die Reihenfolge der Befehle hir so ansehe, dann
    heißst das das ich im PlayTone das INIT_T0 aufrufen soll, aber das wie
    ich es jetzt gemachdt habe passt auch oder?
    ___

    Zum PlayNote:
    Warum bitte ins PlayNote auch R0 übergeben?
    (In R0 steht der Wert, von dem Timer0 zu zählen starten muss, also muss
    er 142mal inkrementieren, bis er einen Zustand(0 oder 1) "gezeichnet"
    hat / Berechnung: Ton hat 440Hz --> T = 2272,72us --> T/2/16=72us(/16,
    weil es sonst nicht mit dem zählen ausgeht) --> 256-(71us/0,5us)=114 -->
    R0=114 )

    Ich komme nicht darauf was im PlayNote stehn sollte? Vielleicht das
    selbe wie im PayTone, also das Timer1 starten und so?

    Zum Timer1 laden/starten:

    Ich bekomme ja mit der Formel oben einen negativen Wert? Wie kann ich
    das denn umgehen?

    Ich hoffe ich habe alles plausibel erklärt und ihr kennt euch sofort aus
    :D. Wenn ihr mehr Infos braucht, dann sagt es bitte.

    mfg anonym



  • Ohje, wer soll das alles lesen?
    Kenne mich zwar mit dem 8051 nicht aus, aber mal versuchen:

    anonym111 schrieb:

    Im T1_ISR stoppe ich den Timer0. Mehr brauche ich eigentlich hier nicht
    oder?

    Jo, und den Timer 1, falls das nicht automatisch passiert...

    anonym111 schrieb:

    Ich haben wir aufgeschrieben:

    PlayTone(R0) --> PlayTone aufrufen und R0 übergeben
    T0_INIT Reload --> T0 auf autoreload Mode 2 setzen
    T0 starten --> SETB TR0
    RET --> zurück zum Main
    PlayNote(R0,R1) --> PlayNote aufrufen und R0,R1 überegeben
    Timer1 laden/starten
    CALL PlayTone --> wieder aufrufen? ich glaube das gehört weg, oder seht
    ihr einen Sinn darin?

    R1mal warten auf TF=1(Überlauf) --> das erklärt das 4*150 - R1=4 in dem
    Fall
    T0 stoppen --> das mach ich ja im T1_ISR(CLR TR0)

    Was soll mir der Pseudocode sagen? Steht das in eurer Main-Funktion? Reichlich unuebersichtlich!
    In "PlayTone" soll offensichtlich ein Ton bestimmter Frequenz auf dem Lautsprecher einfach gestartet werden. Dazu muss nach deiner Beschreibung Timer 0 initialisiert und gestartet werden.
    "PlayNote" soll dagegen - so habe ich das verstanden - bei Aufruf die Ausgabe eines Tones gegebener Frequenz und Dauer anstossen. Dazu muss sowohl Timer 1 zur Zeitbegrenzung gesetzt als auch die Tonausgabe mittels der Funktion "PlayTone" gestartet werden. Der Aufruf von "PlayTone" in "PlayNote" macht so gesehen also durchaus Sinn.

    anonym111 schrieb:

    Wenn ich mir jetzt die Reihenfolge der Befehle hir so ansehe, dann
    heißst das das ich im PlayTone das INIT_T0 aufrufen soll, aber das wie
    ich es jetzt gemachdt habe passt auch oder?

    Da ich mich weder mit den Details der Hardware auskenne, noch genau entraetseln kann, auf welche Loesungen du dich oben jeweils beziehst, kann ich das nicht beantworten. Folgendes sollte vor jedem Starten der Timer jedoch sichergestellt sein:

    • timer-modi muessen korrekt gesetzt sein (sollte reichen, dies einmal zu Programmstart zu tun).
    • der Einfachheit halber sollten Timer wieder bei 0 anfangen zu zaehlen
    • der Spitzenwert (TOP - wo der Overflow auftritt) muss gesetzt sein
    • für T1 muss der Interrupt nach Starten des Timers freigeschaltet werden

    anonym111 schrieb:

    Zum PlayNote:
    Warum bitte ins PlayNote auch R0 übergeben?
    (In R0 steht der Wert, von dem Timer0 zu zählen starten muss, also muss
    er 142mal inkrementieren, bis er einen Zustand(0 oder 1) "gezeichnet"
    hat / Berechnung: Ton hat 440Hz --> T = 2272,72us --> T/2/16=72us(/16,
    weil es sonst nicht mit dem zählen ausgeht) --> 256-(71us/0,5us)=114 -->
    R0=114 )

    Ich komme nicht darauf was im PlayNote stehn sollte? Vielleicht das
    selbe wie im PayTone, also das Timer1 starten und so?

    Siehe oben:
    PlayTone startet unmittelbar Tonausgabe bestimmter Frequenz. Braucht als Parameter die Note (/Frequenz) und setzt damit Timer 0.
    PlayNote startet Tonausgabe bestimmter Frequenz fuer eine bestimmte Zeit. Braucht als Parameter neben Tonfrequenz also auch die Dauer des Tons. Intern werden sowohl Timer 0 (Ton) als auch Timer 1 (Dauer) gesetzt und gestartet. So gesehen kannst du dir "PlayNote" also auch als Wrapper, bzw. Hülle für die Funktionen "PlayTone" gefolgt von Init-Funktionen zum Setzen von Timer 1 vorstellen.

    anonym111 schrieb:

    Zum Timer1 laden/starten:

    Ich bekomme ja mit der Formel oben einen negativen Wert? Wie kann ich
    das denn umgehen?

    Deine Formel ist offensichtlich falsch. Nochmal scharf nachdenken... Dreisatz:
    Du hast einen Timer, der mit Periodendauer (Kehrwert Frequenz) T [s = Einheit Sekunden] laeuft.
    Du willst x [s] warten, dh. x/T = y Zaehler-Takte.
    Wenn du deinen Quellcode genau anschaust, siehst du, dass die Notendauern immer Vielfache von 150ms sind. D.h. es wuerde sich anbieten, Timer 1 beim Initialisieren so einzustellen, dass nach 150ms ein Overflow und damit ein Interrupt ausgeloest wird. Um das zu tun, musst du nur die Periodendauer von T1 bestimmen...
    Mit jedem Interrupt dekrementierst du dann einen Zaehler, der in PlayNote initialisiert werden muss, und wenn der Zaehler 0 wird, schaltest den Ton aus.

    hth.



  • Danke dir, ich habe mir jetzt das ganze durchdacht und soweit gekommen:

    http://pastebin.com/LRzNeCfv

    Mein Problem:

    Das Main-Programm wartet nicht bis die note fertiggespielt wurde,
    sondern geht immer weiter durch. Ich hab mir das überlegt mit einer
    zweiten schleife vielleicht, aber ich kanns net umsetzen irgendwie^^.

    Mein 2tes Problem:

    Im ISR verwenden ich ja den Akku, aber das darf man ja im Interrupt
    nicht, wenn ich da R2 bzw. R3 verwende kann ich das cjne nicht
    verwenden, da #t0count bzw. #t1count nicht funktionieren.

    Könnt ihr mir bitte auf die Sprünge helfen bzw. wie kann ich das machen,
    dass es passt.

    Es gibt sicher leichtere Möglichkeit das zu programmieren, aber ich muss
    das so machen, also bitte so fortsetzen, außer es geht überhaupt nicht,
    was ich da fabriziere^^.



  • anonym11 schrieb:

    Das Main-Programm wartet nicht bis die note fertiggespielt wurde,
    sondern geht immer weiter durch. Ich hab mir das überlegt mit einer
    zweiten schleife vielleicht, aber ich kanns net umsetzen irgendwie^^.

    Du machst das mit einer globalen Variablen, nennen wir sie mal BUSY:

    BUSY wird ganz am Anfang mit 0 belegt. Wenn MAIN einen Ton ausgesucht und initialisiert hat (Tonhöhe, Dauer, Interrupts starten), dann setzt es BUSY auf 1. Der Tonhöhen-Interrupt fängt an zu dudeln. Der Tonlängen-Interrupt fängt an zu zählen. Die MAIN-Funktion überspringt die Tonauswahl immer dann, wenn BUSY==1 ist. Wenn der Tonlängen-Interrupt zu Ende gezählt hat, setzt er BUSY auf 0. Damit kann die MAIN-Funktion den nächsten Ton auswählen.

    Mein 2tes Problem:

    Im ISR verwenden ich ja den Akku, aber das darf man ja im Interrupt
    nicht, wenn ich da R2 bzw. R3 verwende kann ich das cjne nicht
    verwenden, da #t0count bzw. #t1count nicht funktionieren.

    Wenn Du im Interrupt ein Register (auch der Akkumulator ist ein Register) verwenden willst, das auch woanders gebraucht wird, dann musst Du es sichern (PUSH) und vor Beendigung des Interrupts (RETI) wiederherstellen (POP). Wenn ich es richtig behalten habe, dann kann im 8051 ein Interrupt nicht durch einen weiteren Interrupt unterbrochen werden. Das heißt, was Du im Interrupt mit den Registern anstellst, ist völlig egal, wenn sie am Ende wieder auf den Ursprungswert gesetzt werden.

    viele grüße
    ralph


Anmelden zum Antworten