Hallo Leute,
will grad nen sinus per PWM erzeugen. Hab dazu nen viertel Sinuns als
Array in den Flash gelegt und will nun diese Werte per for-schleife als
Zählgrenzen in den Timer schieben. Leider funktioniert das nicht
richtig. Hier erst mal der Code:
Jede for-schleife soll hier ein Viertel eines Sinus erzeugen.
Leider steht nach durchlauf des ersten Viertels immer 1000 statt 2000 in
CCR1.
Bei den anderen drei Vierteln stehen die richtigen Werte in den
Registern.
Interessanterweise geht es wenn ich:
statt:
1
CCR1=SINE[i]*FREQUENCY+FREQUENCY;
2
3
CCR1=SINE[i]*FREQUENCY+1000.0;
schreibe,
oder den kompletten Interrupt auskommentiere und stattdessen
1
i++
in die for-schleife setze.
Ich hatte drüber nachgedacht dass das Ganze so ne Art Timingproblem sein
könnte. Evtl. wird die for-schleife ja mehrmals durchlaufen bevor der
Zähler bei CCR0 ankommt und damit werden die Werte ja immerwieder neu in
die Register geschrieben.
Lt. Manual allerdings soll das im Fall von CCR0 nix machen, da der Wert
2000 ja immer größer als der aktuelle Zählwert ist.
Was da im Falle von CCR1 passiert, dazu hab ich im Manual nichts
gefunden.
Evtl. sollte ich das ganz anders machen und die Wertzuweisung mit in den
Interrupt nehmen. Das werde ich mal als nächstes versuchen.
LG
Ich kann zwar nicht erkennen für welchen µC der Code ist, aber man
sollte wenn möglich Fließkommazahlen vermeiden - vor allem wenn der µC
die nicht in HW unterstützt.
Der Compiler könnte mit einem nicht volatilen i ggf. zu viel
wegoptimieren. Die Lösung mit Interrupt zum Hochzählen der Indexvariable
und nutzen im Hauptprogramm ist auch so nicht gelungen.
A. H. schrieb:> for(i=1;i<256;)> {> CCR0 = FREQUENCY; // PWM Period> CCR1 = SINE[i]*FREQUENCY; // CCR1 PWM duty cycle> }
Ich weiß zwar nicht, warum das Addieren nicht reichtig funktioniert,
aber mir kommt etwas anderes komisch vor. Im letzen Viertel müsste die
Tabelle ja wider rückwärts durchlaufen werden, der Zugriff auf die
Tabeller erfolgt aber wider in Vorwärtsrichtung.
Ok, schonmal danke.
Also erstmal - richtig, ich vergaß: MSP430G2452 im Launchpad
Das mit den Kommazahlen wollte ich ursprünglich so auch nicht machen, da
das Register CCR1 ja eh so erstmal Integer speichert und die
Nachkommastellen abschneidet.
Die Sache ist, die Fequenz des Sinus soll sich im Laufe des Programms
noch ändern. Das wollte ich über die Variable Frequanz realisieren, die
im nächsten Schritt nicht mehr fest sein soll sondern entsprechend
berechnet wird.
Das ganze soll so ne Art Sweep werden. Die Schaltung soll ne
Alarmanlage werden und das hier die Tonausgabe.
Da schien mir den Sinus einfach zwischen -1 und +1 zu definieren und
dann ensprechend umzurechnen das einfachste.
LG
>Im letzen Viertel müsste die>Tabelle ja wider rückwärts durchlaufen werden, der Zugriff auf die>Tabeller erfolgt aber wider in Vorwärtsrichtung.
Von Null bis 1000. Der Sinus ist um seinen Peak bei 1000 nach oben
verschoben, damit er nicht ins negative geht.
>Und noch ein kleiner Fehler: Arrays beginnen bei Null.
Im ersten der vier Teile hab ich das so gemacht. Die anderen Drei müssen
doch aber bei 1 beginnen damit ich nicht denselben Wert zweimal
hintereinander schreibe. Oder hab ich da was falsch verstanden?
Okay,
hab das Programm nochmal geändert und die ganze Zählgrenzensetzung in
die Interruptroutine gelegt. Nun werden die Zählgrenzen nur noch bei
erreichen des CCR0 geändert.
Allerdings ändert das nichts am Problem. Im ersten viertel werden immer
genau im letzten Schritt, also bei i=255 1000 statt 2000 nach CCR1
geschrieben.
Hier der neue Code:
A. H. schrieb:> Die Sache ist, die Fequenz des Sinus soll sich im Laufe des Programms> noch ändern.
Schön.
Aber deswegen brauchst du die Amplitudenwerte ja nicht als Flisskomma
ausführen.
Du scheinst Amplitude mit Frequenz zu verwechseln.
Die Frequenz ist, einfach gesagt, wie schnell die Werte abgearbeitet
werden. Die Amplitude ist, wie weit die Schwingung aus der 0-Lage
herausschwingt.
Ne,
das ist schon klar. Das Problem ist ja dass ich die Frequenz über den
CCR0 einstellen muss. Dadurch muss ich ja den Zählbereich zwischen Null
und CCR0 sinusförmig aufteilen. Wenn ich die Werte nun beispielsweise
mit Faktor Tausend multiplizieren würde, so hätte ich ja so liegen ja
die letzten Werte nur noch knapp drunter. Für CCR0 =400 für 400kHz
beispielsweise würden die Werte des Arrays zu groß werden.
Die Frequenz soll ja aber später so zwischen 400Hz und 2Khz variabel
werden. Da müsste ich das Array ja immer anpassen. Daher schien es mir
das flexibelste zu sein es zwischen Null und Eins zu halten und dann
immer den CCR0 für die Setzung von CCR1 einzurechnen.
Der Witz ist ja auch dass es eigentlich funktioniert, bis auf den
letzten Wert des ersten Viertels
CCR1 = SINE[255]*FREQUENCY+FREQUENCY
liefert 100 statt 2000. SINE[255] ist aber genau 1.0.
A. H. schrieb:> Die Schaltung soll ne> Alarmanlage werden und das hier die Tonausgabe.
Wofür dann der Aufwand? Hast Du da auch hochwertige Lautsprecher und
eine Endstufe die nicht rauscht ohne Klirrfaktor hinter?
Was wir noch garnicht wissen, ist, auf welche Weise im Detail, Du
feststellst, das das Produkt 2000 ist. Beschreib bitte wie Du das
erkennst.
Auch wäre es interessant zu wissen, was für einen Prozessor Du benutzt.
Ich kenne keinen Prozessor, der etwas mit Fließkommazahlen anfangen
kann. Zumindest im Zusammenhang mit der PWM-Ausgabe/-Programmierung.
Rechne das Ganze in Ganzzahlen um und spar' Dir somit die heißen
Fingerkuppen und den unnötigen Speicherverbrauch.
Übrigens: Die endlose und immer gleiche Umrechnung von float in integer
kostet auch einiges an unnötiger Rechenzeit.
HertzFrequenz schrieb:> Auch wäre es interessant zu wissen, was für einen Prozessor Du benutzt.
Und die alles entscheidende Frage: wieviele Stufen kann denn die PWM des
benutzten Prozessors ueberhaupt?
wendelsberg
HertzFrequenz schrieb:> Was wir noch garnicht wissen, ist, auf welche Weise im Detail, Du> feststellst, das das Produkt 2000 ist. Beschreib bitte wie Du das> erkennst.>> Auch wäre es interessant zu wissen, was für einen Prozessor Du benutzt.
Ich schau im Debugger ins Register.
Der Chip ist ein MSP430G2452.
Du hast hier aus meiner Sicht mehrere Ansatzpunkte.
1. Float-Multiplikation ist sehr teuer, auf einem Prozessor, der keine
Fliesskomma-Einheit hat. Wird zwar nicht Dein eigentliches Problem sein,
aber kann zu unerwarteten Ergebnissen führen, da Du mit dem Code nicht
eigentlich Kontrolle über den Zeitablauf beim Debuggen hast.
2. Die Konstantenbenennung ist falsch, da Du mit dem Produkt
1
CCR1=SINE[255]*FREQUENCY+FREQUENCY
eigentlich erstmal einen Wert proportional zur Amplitude ausrechnest,
der dann aber einer Zeit der PWM gleich ist. Muss man nicht unbedingt
ändern, wäre aber vielleicht klarer. Mir leuchtet eigentlich auch der
letzte Summand FREQUENCY nicht eigentlich ein, aber das kann die
Vorfreude auf den Weihnachtsmann sein, die mein Urteilsvermögen trübt.
Ich würde vorschlagen, das Du zunächst einmal die Berechnung des
PWM-Verlaufes völlig aus dem Interrupt herauslöst und schlicht einmal
ganz den Sinusverlauf in einer Schleife berechnest.
Es gibt nämlich durchaus Stellen an denen Du als Ergebnis 1000 erhälst.
Vielleicht erwischst Du die aus Versehen.
Also ungefähr so:
wobei Du CCR0 und CCR1 durch Variablen ersetzt.
Dann schaust Du Dir Schritt für Schritt, vor allem aber an den
Übergängen zwischen den Sinusabschnitten die Ergebnisse an.
Was ändert das Register 'CCR0' eigentlich? Ich nehme jetzt an, dass der
Wert darin die PWM-Frequenz ändert. Du willst aber - so verstanden - die
Sinusfrequenz ändern. Diese steht jedoch nahezu in keierlei Zusammenhang
mit der PWM-Frequenz. Du musst - wie Karl Heinz schon erwähnt - die
Zählgeschwindigkeit deines Tabellenzeigers ändern (i). Alternativ kannst
Du auch Tabellwerte überspringen und das Ganze im Stil der direkten
digitalen Synthese machen.
http://de.wikipedia.org/wiki/Direct_Digital_Synthesis
A. H. schrieb:> FREQUENCY = 1000.0;
und
A. H. schrieb:> CCR0=FREQUENCY*2;
Warum nimmst du float um ein int Register zu beschreiben?
Du weisst, wie der Compiler float nach uint16_t wandelt? Er läßt die
Nachkommastellen weg! Das ist kein Runden.
Ist die Frequenz variabel? Wenn nicht gibt man sie konstant vor
1
#define FREQUENCY 1000
Du kürzt die float auf uint16_t. Warum rechnest du nicht gleich in
Ganzzahlen? Wenn die Frequenz mindestens 1kHz beträgt, kannst du in der
Tabelle den Faktor 1000 ansetzen und selber vernünftig runden. Für die
Tabelle reichen dann 16Bit Werte anstatt 32Bit.
HertzFrequenz schrieb:> CCR1 = SINE[i]*FREQUENCY+FREQUENCY;
Diese Berechung braucht nicht zur Laufzeit erfolgen. Der berechnete Wert
kann direkt in der Tabelle hinterlegt werden. (bei konstanter f)
HertzFrequenz schrieb:> for(i=1;i<256;)> {> CCR0 = FREQUENCY*2; // PWM Period
Da sich der Wert in der Schleife nicht ändert, kann man ihn einmalig vor
der Schleife berechnen und zuweisen.
A. H. schrieb:> for(i=1;i<256;)
Wie kommt eine kontrollierte Zeit in die Ausgabe? Es hängt nur von der
CPU Geschwindigkeit ab. Der Interrupt ist völlig deplatziert.
Ein Ansatz:
Die als - uint16_t berechnete -Tabelle sollte per Timer-Interrupt
ausgegeben werden.
Oookay - nun zurück aus dem Weihnachtsurlaub seh ich grad die vielen
Posts von euch. Erstmal riesen Dankeschön für das Engagement.
Nun - fangen wir mal damit an:
Ich hab nun grad den Crystal auf das Launchpad gelötet und auf einmal
gehts. Am Code hab ich nichts geändert, allerdings hab ich nach dem
Auflöten noch ein Testprogramm für den Crystal laufen lassen (Ich habs
mal angehängt für die, die es interessiert). Das hab ich im Netz
gefunden und einfach mal reingeschoben. Danach hab ich wieder mein
Sinusprogramm geladen (das aus dem zweiten Post) und plötzlich machts
was es soll. Das ist schon merkwürdig, denn der Code nutzt den Crystal
ja gar nicht. Mal sehen ob das nun so bleibt oder ob das Programm dann
plötzlich doch wieder Fehler produziert.
Nun zu euren diversen Antworten:
>Und die alles entscheidende Frage: wieviele Stufen kann denn die PWM des>benutzten Prozessors ueberhaupt?
Keine Anhung, da steht nix im Datenblatt. Wie ist die Frage denn
gemeint?
>1. Float-Multiplikation ist sehr teuer
Ich möchte ja CCR1 sinusförmig erhöhen. Da ich die Frequenz später zw.
ca. 400 und 1000 Hz variieren will müssen die 256 Schritte für die
Viertelperiode je nach Frequenz entsprechend verteilt werden. Daher
schien mir eine Tabelle mit den ungewichteten Werten zwischen 0 und PI/2
als einfachste Lösung um sie mit der jeweiligen Frequenz zu
multiplizieren und so die Verteilung zu erzeugen.
>Mir leuchtet eigentlich auch der letzte Summand FREQUENCY nicht >eigentlich ein
Der Sinus geht ja von-1 bis +1.
Einen negativen CCR1 macht ja aber keinen Sinn, da der Chip ja auch nur
Spannungen von 0 bis Vcc ausgibt. D.h. ich muss den Sinus ja ins
positive nach oben schieben. Daher die Addition der Frequenz als
Amplitudenwert.
>Ich würde vorschlagen, das Du zunächst einmal die Berechnung des>PWM-Verlaufes völlig aus dem Interrupt herauslöst und schlicht einmal>ganz den Sinusverlauf in einer Schleife berechnest.
Das hatte ich ja im ersten Beispiel ganz zu Anfang des Threads. Gab
dasselbe Fehlerbild.
>Diese steht jedoch nahezu in keierlei Zusammenhang>mit der PWM-Frequenz.
Wieso nicht? Der Sinus entsteht doch durch die sinusförmige Änderung von
CCR1.
>Du weisst, wie der Compiler float nach uint16_t wandelt? Er läßt die>Nachkommastellen weg!
Ja, ist aber egal, da die Frequenz dann ja noch hochmultipliziert wird
und die Nachkommastellen dann nicht mehr ins Gewicht fallen.
>Wie kommt eine kontrollierte Zeit in die Ausgabe? Es hängt nur von der>CPU Geschwindigkeit ab.
Das ist richtig. Das sollte dann der nächste Schritt werden dafür zu
sorgen dass aus CCR0= 1000 auch 1000Hz werden. Mein Denkansatz kam aus
folgendem Link:
[http://www.mikrocontroller.net/articles/MSP430_Codebeispiele#PWM]
Konkret aus der Formel und dem Beispiel zur PWM.
timing_0 = 1.0e6 8 125 = 1000
Das funktioniert so einfach für mein Beispiel aber nicht, da ja eine
Viertelperiode schon in 256 Schrite unterteilt ist.
>Der Interrupt ist völlig deplatziert
Wieso? Ich kenn mich mit Interrupts noch nicht so aus. Das ist eines der
ersten µC Programme das ich schreibe.
>Die als - uint16_t berechnete -Tabelle sollte per Timer-Interrupt>ausgegeben werden.
Ich wollte die Werte urprünglich direkt im Code berechnen lassen, aber
der Compiler streikte. Es kam immer eine Fehlermeldung von wegen der
Stack sei zu klein.
Abschließend noch folgendes:
Bei meinen ersten Versuchen den Flash zu beschreiben hab ich den Bereich
in dem die Interrupt Vektoren stehen gelöscht. Daa wurde mir aber in
einem anderen Post bereits gesagt dass die sowieso immer wieder neu
geschrieben würden. Siehe hierzu meinen anderen Post:
[Beitrag "Re: MSP430 Gleitkommazahl in Flash speichern"]
Allerdings erhalte ich nun beim Kompilieren immer Warnungen für die
diversen Interrupts seihen keine Vektoren vorhanden. Der Timerinterrupt
ist in den Warnungen wiederum aber nicht vorhenden. Die Warnungen hab
ich mal als Screenshot angehängt.
Gruß
Eddie