Forum: Mikrocontroller und Digitale Elektronik (8051) Dauerton und Note abspielen


von thomas (Gast)


Lesenswert?

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 :)

von Wilhelm F. (Gast)


Lesenswert?

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.

von Bernhard S. (b_spitzer)


Lesenswert?

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.

von Bernhard S. (b_spitzer)


Lesenswert?

Nachtrag: zwischen 2 Noten füge ich noch eine Pause von 25ms (ein 
Überlauf Timer 0) ein, damit wiederholte Noten auch getrennt 
wiedergegeben werden.

von thomas (Gast)


Lesenswert?

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.

von Bernhard S. (b_spitzer)


Lesenswert?

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.

von thomas (Gast)


Lesenswert?

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^^.

von thomas (Gast)


Lesenswert?

Weis den keiner einen Rat bitte?

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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.

von thomas (Gast)


Lesenswert?

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.

von thomas (Gast)


Lesenswert?

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?

von Fetz (Gast)


Lesenswert?

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^^

von Wilhelm F. (Gast)


Lesenswert?

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.

von Bernhard S. (b_spitzer)


Lesenswert?

/* */...
> 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.)

von thomas (Gast)


Lesenswert?

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

von Bernhard S. (b_spitzer)


Lesenswert?

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).

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.