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 Thomas :)
Auf einem 8051 programmierte ich schon mal Melodien, aber ganz einfach,
wie in Spielautomaten der 1980-er Jahre.
Im Grunde hat man im Hauptprogramm eine Liste mit aneinander gereihten
Parametersätzen wie Zeitdauer und Frequenz, und ruft dann damit den
Timer auf. Ein Flag signalisiert für das Hauptprogramm immer das Ende,
wo es wieder weiter geht.
Kann sein, daß ich beide Timer benutzte, das weiß ich nicht mehr so
genau. Einen für die Zeit, den anderen für den Ton.
Das macht nur mit 2 Timern Sinn. Einer für die Tonhöhe, einer für die
Dauer. Wenn Du da perioden zählen wolltes, wird das nie was.
Ich hatte das in C so erledigt:
/* Melodieerzeugung mit 2 Timern
Timer 0 erzeugt einen Grundtakt von 25ms für die Notendauer
Timer 1 erzeugt die Ausgabefrequenz
Im Array Noten[] steht die gewünschte Melodie
Im Array Dauer[] steht die Notendauer (1, 2, 4, 8, 16 möglich)
Steht für ganze, halbe, viertel, achtel und sechzehntel Note.
(punktiertes Viertel mit 3 und Punktierte Achtel mit 6 geht auch)
Timer 1 erzeugt die Frequenz aus dem Wert Noten[aNote]
Reload-Wert für Timer: halbe Periodendauer in us
1000000[us/s]/(2*Frequenz) = 500000/Frequenz
==> bei 24MHz dann 2000000[us/s]
Timer 0 erzeugt die Tondauer aus dem Wert Dauer[aNote]
Notendauer: 120 Viertel/Minute => Viertel Note dauert 0,5s
Halbe Note dauert 1s, ganze Note dauert 2s, achtel Note dauert 0,25s
Grundzeit ist 25ms, für ein Achtel muss dann 80/8 mal gewartet werden
zwischen 2 Noten ist 25ms Pause => Timer 1 wird dabei ausgeschaltet!
*/
Statt den Arrays definierst Du halt Const-Listen in Assembler.
Danke für die Antworten!
Aber leider kann ich weder mit const-Listen noch mit der C-Theorie was
anfangen :(. Sorry Leute.
Ich bin voll der Anfänger im Asm programmieren. Da ist das erste mal
aufm PC mit Timer/Counter.
Und darum will/muss/soll ich es so machen wie ich oben beschrieben habe,
vielleicht kleine Verbesserungen/Abänderungen.
Ich hoffe jemand kann mir trotzdem helfen.
Na ja, meine Beschreibung enthält keinen einzigen Befehl. Weder C noch
Assembler. Die Listen für die Melodie definierst Du in ASM mit db.
Zum Beispiel eine Tonleiter - die Tonzuordnungen zu Zählerständen
erfolgen an anderer Stelle, bei Dir in notesdefine.h (was aber eher nach
C aussieht und funktionell eher unvollständig ist)
Also die Notwendigen Byte-Werte noch definieren
C4 equ 42 ; was auch immer
A4 equ 114 ; einziger nutzbarer Wert aus der .h-Datei...
Tonleiter: db C4, D4, E4, F4, G4, A4, H4, C5
Der Zugriff auf die Tabelle erfolgt in ASM über movec und den
Data-Pointer
mov DPTR, #Tonleiter ; Datapointer auf Tabellenanfang
mov A, #0 ; ersten Wert lesen
movc A, @A+DPTR ; jetzt steht der Wert von C4 im Akku
Wenn Du Tabellen mit 16-Bit Werten machen willst, dann dw als Kürzel.
Wird in ASM aber ziemlich trickreich.
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^^.
Du solltest halt am Ende der Note ein Flag setzen, das dem Hauptprogramm
sagt, das es nun die nächste Note spielen darf (also die Werte in die
Timer schreibt) und das Flag löscht. Solange das Flag nicht gesetzt
ist, muss das Hauptprogramm Däumchen drehen, oder sich mit
Tastaturabfrage, Lämpchen, UART oder sonst was beschäftigen.
Flags sind gut aufgehoben im B Register oder in der 'bit addressable
area'
thomas schrieb:> 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.
Doch, du musst lediglich den Akku pushen am Anfang der ISR und poppen
am Ende. Es ist gute Praxis, ISRs immer in einer anderen Registerbank
laufen zu lassen und diejenigen anderen Register am Anfang der ISR zu
retten und am Ende zu restaurieren, die sonst noch benutzt werden.
Danke!
Also soll ich in Register B irgendnen Wert hineinschreiben? Ja und wie
stoppe ich dann das main. Das verstehe ich überhaupt nicht.
Das mit dem ISR ist mir jedoch klar.
Mh ich glaube ich habs ich setze einfach ein Bit vor Call Playtone und
mache dann zwischen zeile 308 und 309 "JB bit,$".
Das bit resette ich dann in der Zeile 430(statt SETB TR0, TR1 gehört da
CLR).
So müsst es eigentlich funktionieren? Und welches bit sollte ich nehmen?
im PSW das Flag 0?
Bernhard Spitzer schrieb:> Ich hatte das in C so erledigt:> /* Melodieerzeugung mit 2 Timern> Timer 0 erzeugt einen Grundtakt von 25ms für die Notendauer> Timer 1 erzeugt die Ausgabefrequenz> [...]> Halbe Note dauert 1s, ganze Note dauert 2s, achtel Note dauert 0,25s> Grundzeit ist 25ms, für ein Achtel muss dann 80/8 mal gewartet werden> zwischen 2 Noten ist 25ms Pause => Timer 1 wird dabei ausgeschaltet!> */
Und das hat dann funktioniert?
Ich hab es kompiliert und der Code hat garnichts gemacht muahaha :D
SCNR^^
thomas schrieb:> Und welches bit sollte ich nehmen?> im PSW das Flag 0?
Kannst du machen. Der Bitbereich im internen RAM hat aber auch 128
Flaggen, im Adreßbereich zwischen 0x20 und 0x2F.
Versuchs mal so, und gestalte die Hauptprogrammschleife aus einer
Aneinanderreihung von Tönen, zwischen denen immer wieder ein
Abspielprogramm aufgerufen wird. Z.B.:
Lade Timer 0 mit dem Ton
Lade Timer 1 mit der Tondauer
Rufe das Abspielprogramm
Lade Timer 0 mit dem Ton
Lade Timer 1 mit der Tondauer
Rufe das Abspielprogramm
Lade Timer 0 mit dem Ton
Lade Timer 1 mit der Tondauer
Rufe das Abspielprogramm
Lade Timer 0 mit dem Ton
Lade Timer 1 mit der Tondauer
Rufe das Abspielprogramm
usw. usf.
Im Abspielprogramm werden beide Timer gestartet, und nach abgelaufener
Zeit gestoppt, Rückkehr ins Hauptprogramm.
Der Teufel steckt natürlich noch im Detail: Die Programmierarbeit. Das
ist schon noch etwas mehr, als nur die 2 Parameter laden. Z.B. auch
Timerüberläufe berücksichtigen.
Wie man die Töne ausrechnet, weißt du sicher. Sonst kannst du danach
auch noch mal fragen.
Ob der µC noch was anderes macht, sonstige Aufgaben, solltest du erst
mal ganz außer acht lassen. Das kann man später überlegen, wenn mal was
funktioniert.
/* */...
> Und das hat dann funktioniert?>> Ich hab es kompiliert und der Code hat garnichts gemacht muahaha :D>
Beinahe lustig... Wieso meldest Du dich nicht ordentlich an, dann kann
ich Deine Beiträge leichter ignorieren.
Der Fragende wollte halt Assembler und kein C-Code. Statt PAP oder
Struktogramm zu zeichnen habe ich halt meinen Kommentar aus dem Programm
rauskopiert.
Wilhelm schrieb:
> Kannst du machen. Der Bitbereich im internen RAM hat aber auch 128> Flaggen, im Adreßbereich zwischen 0x20 und 0x2F.
Das sind dann die Bitadressen 00h bis 7Fh.
Festlegung z.B. mit Merker bit 00h
Wenn der Stack durch viele Unterprogramm-Aufrufe und push größer werden
kann, sollte man die Bits liebe an den oberen Rand des Bitadressierbaren
Speichers legen, z.B. Merker bit 7Fh
Wenn der Speicher knapp werden sollte, gibt es häufig noch die internen
Ports P4 und P5, die zwar auf dem Chip, aber an vielen Gehäusen nicht
vorhanden sind. setb und clr funktioniert damit auch problemlos (aber
aufpassen, wenn mal jemand das Programm auf einem 80535 laufen lässt.)
Ok gut danke^^.
Aber das mit den Pausen verstehe ich nicht ganz, was soll ich da machen
genau?
Hat wer eventuell Tipps etc. bitte?
hier der code: bissl habe ich schon was zwecks pausen gemacht, also das
er timer nicht gesetzt werden, aber wie mache ich weiter dann im main?
http://pastebin.com/aCZTaGZg
Pause = kein Ton, also Bit nicht toggeln sondern nur warten (in meinem
Beispiel ist jan immer 25ms Pause zwischen 2 Noten, weil sonst 2
Viertel wie eine hlabe Note klingen würden). Ob jetzt 0 oder 1
ausgegeben wird ist dem Lautsprecher da ziemlich egal (bei größeren
Verstärkern schaltet man halt einen Elko dazwischen, damit kein
Gleichstrom durch den Lautsprecher fleißt).