Hi Leute,
ich möchte einen 50Hz Sinus mittels DDS erzeugen. Der DSP ist ein TI
F28027 Piccolo. Frequenz meiner PWM beträgt 16kHz - 30kHz.
Die 50Hz müssen bei weitem nicht genau sein (49-51Hz sind durchaus
passabel).
Der Code befindet sich im Anhang. Die Sinustabelle beinhaltet 256 Werte
und umfasst eine Halbwelle (0 bis (Pi-1))
Die ISR wird immer aufgerufen, wenn die PWM Periode vorbei ist. In der
ISR wird der Wert der Sinustabelle aktualisiert. Die Schrittweite für
den Phasenakkummulator f_inc für 16kHz habe ich wie folgt berechnet:
f_inc = 2^16 / (256*Schaltfrequenz/(2*Grundfrequenz)) =
2^16/(256*16000Hz/100Hz) = 1.6 = 1*256 + 0.6*256 = 409
Gleiche Berechnung für die anderen PWM Frequenzen.
i ist eine 16bit Variable, die sich jedes mal in der ISR um f_inc
erhöht.
1
i=i+f_inc
Mittels
1
EPwm1Regs.CMPA.half.CMPA=sineLookup[i>>8];
verwende ich lediglich das obere Byte des 16bit Integers.
So habe ich die DDS bzw. den Phasenakkumulator verstanden. Ist das
grundsätzlich korrekt? Der PWM Ausgang sieht schon ganz okay aus, finde
ich (muss bei Gelegnheit noch mal einen RC Tiefpass dran hängen).
Ich habe allerdings 2 Fragen:
1.
Die 16bit Variable i wird maximal (fsw/(2*50Hz)) 16000/100*409=65440
groß, bevor es zum Überlauf kommt. Nach dem Überlauf beträgt der Wert
von i 313 und es wird dann f_inc=409 drauf addiert (313+409=722). Soll
das so sein oder sollte i idealerweise nicht wieder bei 0 anfangen,
sobald ein Überlauf stattgefunden hat?
2.
Wie mache ich, dass ich die Tabelle wieder rückwärts laufe, um die
negative Halbwelle des Sinus zu erhalten? Im Prinzip würde ich
vorschlagen
1
if(i>=65536)
2
{
3
i=i-f_inc;
4
}
Aber:
Sobald es zum Überlauf kommt, ist i doch bereits 313, so dass die
Bedingung niemals erfüllt ist. Oder täusche ich mich da?
Ich hoffe, dass mir jemand helfen kann.
Gruß,
al3ko
@ al3ko -.- (al3ko)
>ich möchte einen 50Hz Sinus mittels DDS erzeugen. Der DSP ist ein TI>F28027 Piccolo. Frequenz meiner PWM beträgt 16kHz - 30kHz.
Klemm mal einen passenden RC-Tiefpass an dein IO-Pin, vieleicht
10k+100nF. Dann sieht man deinen Sinus direkt. Das PWM-Gezappel kann
keiner deuten.
>Die 50Hz müssen bei weitem nicht genau sein (49-51Hz sind durchaus>passabel).
Mit einem uC kann man das problemlos quarzgenau erzeugen. Das kostet
keinerlei Zusatzaufwand.
>den Phasenakkummulator f_inc für 16kHz habe ich wie folgt berechnet:>i ist eine 16bit Variable, die sich jedes mal in der ISR um f_inc>erhöht.>i = i+ f_inc
Der Name ist ungünstig. f ist irgendwas mit Frequenz. Du erhöhst aber
die Phase, schließlich heißt das Ding Phasenakkumulator. Das aber nur
nebenbei.
>So habe ich die DDS bzw. den Phasenakkumulator verstanden. Ist das>grundsätzlich korrekt?
Ja.
> Der PWM Ausgang sieht schon ganz okay aus, finde>ich (muss bei Gelegnheit noch mal einen RC Tiefpass dran hängen).
Tu das.
>Die 16bit Variable i wird maximal (fsw/(2*50Hz)) 16000/100*409=65440>groß, bevor es zum Überlauf kommt. Nach dem Überlauf beträgt der Wert>von i 313 und es wird dann f_inc=409 drauf addiert (313+409=722). Soll>das so sein
Ja.
> oder sollte i idealerweise nicht wieder bei 0 anfangen,
Nein, das passiert nur in wenigen Sonderfällen, wenn das Phaseninkrement
ein ganzzahliger Teiler des Phasenakkus ist.
>Wie mache ich, dass ich die Tabelle wieder rückwärts laufe, um die>negative Halbwelle des Sinus zu erhalten?
Warum? Hast du zu wenig Speicher? Leg einfach die vollen 2*Pi in der
Tabelle ab, fertig. Wenn du es richtig machts (tm), ist deine Tabelle
von Typ const und landet im Flash, nicht im RAM. Und Flash hast du
genug.
Der Rechenaufwand lohnt sich nicht.
Hi Falk,
Die Sinus PWM mittels DDS ist momentan das einzige, das auf dem DSP
läuft. Ergo muss der Code nicht sonderlich auf Ressourcen optimiert
sein.
Allerdings erschließt sich mir der Sinn einer ganzen Sinustabelle noch
nicht ganz. Denn negative Zahlen gibt's doch sowieso nicht. Und den
kompletten Sinus nach oben schieben halte ich für auch keine gute Idee,
denn das ergibt ~50% duty cycle immer wenn der Sinus sehr kleine Werte
(bei 0, Pi und 2*Pi) hat.
Ergo würde mich wirklich interessieren, wie man die Logik in mein
Programm einbinden kann.
Als Anmerkung:
MOSFET 1 soll bei 0 bis Pi (positive Halbwelle) den Sinus modellieren,
während MOSFET 2 komplett ausgeschaltet ist.
MOSFET 2 soll bei Pi bis 2*Pi (negative Halbwelle) den Sinus
modellieren, während MOSFET 1 komplett ausgeschaltet ist.
Ergo muss ich so oder so erkennen, wo ich mich momentan im Sinus
befinde.
Wie kann man das elegant lösen?
Man kann den sinus auch Offset shiften, eine Frage der anwendung. Also
anstelle von -1 .. +1 , von 0 .. 1. Dann muss man eben die PWM anpassen.
die Mitte der Amplitude waere dann zB auf 128, das Minimum waere auf 0,
das Maximum waere auf 255.
Wo man ich im sinus befindet ? Dafuer gibt es ja des phasenindex, die
Phase, die immer weiter zaehlt und per modulo in die sinustabelle zeigt.
Man kann von der sinustabelle auf einen DAC gehen, oder auf den PWM, das
ist dasselbe.
@ al3ko -.- (al3ko)
>läuft. Ergo muss der Code nicht sonderlich auf Ressourcen optimiert>sein.
Schon mal ein Problem weniger.
>Allerdings erschließt sich mir der Sinn einer ganzen Sinustabelle noch>nicht ganz. Denn negative Zahlen gibt's doch sowieso nicht.
Ja.
>Und den>kompletten Sinus nach oben schieben halte ich für auch keine gute Idee,
Doch.
>denn das ergibt ~50% duty cycle immer wenn der Sinus sehr kleine Werte>(bei 0, Pi und 2*Pi) hat.
Ja und? Das ist nur ein Gleichanteil. Den kann man problemlos mit einem
Hochpass oder Addierverstärker wieder entfernen.
>Ergo würde mich wirklich interessieren, wie man die Logik in mein>Programm einbinden kann.
???
Du hast doch schon alles. Mach die Tabelle vollständig und mit Offset,
fertig. Denn deine PWM kann so oder so keine negativen Tastverhälnisse
ausgeben ;-)
>Als Anmerkung:>MOSFET 1 soll bei 0 bis Pi (positive Halbwelle) den Sinus modellieren,
modulieren
>Ergo muss ich so oder so erkennen, wo ich mich momentan im Sinus>befinde.>Wie kann man das elegant lösen?
Mit den Timerfunktionen des Piccolo. Ist sicher etwas komplexer, aber
genau dafür gebaut!
Siebzehn Für Fuenfzehn schrieb:> Man kann den sinus auch Offset shiften, eine Frage der anwendung.> Also> anstelle von -1 .. +1 , von 0 .. 1. Dann muss man eben die PWM anpassen.
Aber genau das ist m.E. nicht der geeignete Weg aus dem o.g. Grund:
Wenn der Sinus geshiftet ist, also mit einem Offset versehen, verzerrt
sich die ganze PWM Folge inklusive der Tastgrade. Ich habe das mal
skizziert. Wenn der Sinus nahe dem Nulldurchgang ist, ist das Duty Cycle
entsprechend groß aufgrund des Offsets. Richtig wäre jedoch ein
minimales Duty Cycle.
> die Mitte der Amplitude waere dann zB auf 128, das Minimum waere auf 0,> das Maximum waere auf 255.> Wo man ich im sinus befindet ? Dafuer gibt es ja des phasenindex, die> Phase, die immer weiter zaehlt und per modulo in die sinustabelle zeigt.
Phasenindex? Quasi ein Counter, der in jeder ISR um einen nach oben
zählt? Aber woran erkenne ich, wo ich mich gerade im Sinus befinde? Denn
ich greife ja nicht auf jedes Element der Tabelle zu. Das ist ja meine
Schwierigkeit:
Tabelle hat 256 Elemente. Bei 16kHz PWM Frequenz erreiche ich allerdings
nur 160 Tabellenzugriffe.
> Man kann von der sinustabelle auf einen DAC gehen, oder auf den PWM, das> ist dasselbe.
Was meinst du damit?
Gruß
Falk Brunner schrieb:>>>Allerdings erschließt sich mir der Sinn einer ganzen Sinustabelle noch>>nicht ganz. Denn negative Zahlen gibt's doch sowieso nicht.>> Ja.>>>Und den>>kompletten Sinus nach oben schieben halte ich für auch keine gute Idee,>> Doch.>>>denn das ergibt ~50% duty cycle immer wenn der Sinus sehr kleine Werte>>(bei 0, Pi und 2*Pi) hat.>> Ja und? Das ist nur ein Gleichanteil. Den kann man problemlos mit einem> Hochpass oder Addierverstärker wieder entfernen.
Am Ende möchte ich gerne einen DC/AC Wechselrichter ansteuern. Und die
Lösung mit dem 50% Duty Cycle ist definitiv keine Option für mich, da
das Spektrum des PWMs dadurch verzerrt wird.
>>>Ergo würde mich wirklich interessieren, wie man die Logik in mein>>Programm einbinden kann.>> ???> Du hast doch schon alles. Mach die Tabelle vollständig und mit Offset,> fertig. Denn deine PWM kann so oder so keine negativen Tastverhälnisse> ausgeben ;-)
Negative Tastverhältnisse sind nicht mein Ziel. Das, was als negative
Tastverhältnisse gedeutet wird, ist die normale PWM für MOSFET 2 (siehe
Bild).
>>Als Anmerkung:>>MOSFET 1 soll bei 0 bis Pi (positive Halbwelle) den Sinus modellieren,>> modulieren
Stimmt :P
>>Ergo muss ich so oder so erkennen, wo ich mich momentan im Sinus>>befinde.>>>Wie kann man das elegant lösen?>> Mit den Timerfunktionen des Piccolo. Ist sicher etwas komplexer, aber> genau dafür gebaut!
Wenn der Timer ins Spiel kommt, wie sähe die DDS dann aus?
Momentan wird die Sinustabelle nach jeder PWM Periode aktualisiert. Die
ISR hängt also von der PWM Frequenz ab. Die Grundfrequenz (50Hz), ist
sowieo konstant.
Wenn ich einen Timer implementiere, könnte ich doch auch die
Sinustabelle in jedem Timer Interrupt aktualisieren. Der Timer sollte
m.E. unabhängig von der PWM Frequenz sein. Ergo kann ich die DDS mit dem
Timer allein realisieren und die PWM für was auch immer benutzen.
Spricht etwas dagegen?
@ al3ko -.- (al3ko)
>> anstelle von -1 .. +1 , von 0 .. 1. Dann muss man eben die PWM anpassen.>Aber genau das ist m.E. nicht der geeignete Weg aus dem o.g. Grund:
So macht es der Rest der Welt.
>Wenn der Sinus geshiftet ist,
Ist er das? Beim mir ist der eher verschoben.
> also mit einem Offset versehen, verzerrt>sich die ganze PWM Folge inklusive der Tastgrade.
Nö.
>Ich habe das mal>skizziert. Wenn der Sinus nahe dem Nulldurchgang ist, ist das Duty Cycle>entsprechend groß aufgrund des Offsets. Richtig wäre jedoch ein>minimales Duty Cycle.
Falsch. Du vergisst den Gleichanteil, aka. Offset. Dein Quasi-analoge
PWm.Erzeugung mittel Dreiekc und Komparator mus natürlich dann auch
einen Offset kriegen.
>Phasenindex? Quasi ein Counter, der in jeder ISR um einen nach oben>zählt?
Denn hast du schon! Dein i!
> Aber woran erkenne ich, wo ich mich gerade im Sinus befinde?
Am i!
> Denn>ich greife ja nicht auf jedes Element der Tabelle zu.
Das ist normale be DDS.
>> Man kann von der sinustabelle auf einen DAC gehen, oder auf den PWM, das>> ist dasselbe.>Was meinst du damit?
Man kann anstatt PWM auch einen "normalen" DAC verwenden, der gibt
direkt die Analogspannung aus.
>Am Ende möchte ich gerne einen DC/AC Wechselrichter ansteuern. Und die>Lösung mit dem 50% Duty Cycle ist definitiv keine Option für mich, da>das Spektrum des PWMs dadurch verzerrt wird.
Du weißt gar nicht wovon du redest.
>Wenn der Timer ins Spiel kommt, wie sähe die DDS dann aus?
Ähnlich, aber die Steuerung ist etwas anders. Der TImer im Piccolo kann
in Hardware Totzeiten generieren und auch Halbbrücken ansteuern. Wie das
im Detail funktioniert musst du selber herausfinden.
>Momentan wird die Sinustabelle nach jeder PWM Periode aktualisiert. Die>ISR hängt also von der PWM Frequenz ab. Die Grundfrequenz (50Hz), ist>sowieo konstant.
An diesem Konzept ändert sich nichts.
>Wenn ich einen Timer implementiere, könnte ich doch auch die>Sinustabelle in jedem Timer Interrupt aktualisieren.
Nein. Es wird vor allem die PWM- und Totzeitfunktion des Timers benutzt.
> Der Timer sollte>m.E. unabhängig von der PWM Frequenz sein.
Nein. genau das Gegenteil.
> Ergo kann ich die DDS mit dem>Timer allein realisieren und die PWM für was auch immer benutzen.>Spricht etwas dagegen?
Alles ;-)
Falk Brunner schrieb:>>Ich habe das mal>>skizziert. Wenn der Sinus nahe dem Nulldurchgang ist, ist das Duty Cycle>>entsprechend groß aufgrund des Offsets. Richtig wäre jedoch ein>>minimales Duty Cycle.>> Falsch. Du vergisst den Gleichanteil, aka. Offset. Dein Quasi-analoge> PWm.Erzeugung mittel Dreiekc und Komparator mus natürlich dann auch> einen Offset kriegen.
Wie geschieht das? Wie packe ich einen Offset auf mein Dreieck? Wenn wir
den Offset da auch mit raufpacken, sieht die Geschichte in der Tat
wieder ganz anders aus.
>>Phasenindex? Quasi ein Counter, der in jeder ISR um einen nach oben>>zählt?>> Denn hast du schon! Dein i!
Mein i gibt mir aber keine zuverlässige Auskunft über meine aktuelle
Position. Wenn i=160*409 ist, dann ist es genau der Wert vor dem
Überlauf. Genau dieser Überlauf gibt mir Auskunft über den Nulldurchgang
meines Sinus. Aber: ich kenne den letzten Wert vor dem Überlauf nicht.
Der ist doch undefiniert. Oder anders ausgedrückt:
Den Überlauf kann ich doch nicht programmtechnisch erfassen, oder habe
ich eine Denkblockade?
Ich stelle mir lediglich folgende Frage:
Wann habe ich die Sinustabelle vollständig durchlaufen, ergo wann habe
ich die positive Halbwelle fertig.
Nach dem Überlauf ist i=313. Wenn ich eine andere PWM Frequenz wähle,
ändern sich alle Werte. Dann ist i nach dem Überlauf möglicherweise 200.
>>Am Ende möchte ich gerne einen DC/AC Wechselrichter ansteuern. Und die>>Lösung mit dem 50% Duty Cycle ist definitiv keine Option für mich, da>>das Spektrum des PWMs dadurch verzerrt wird.>> Du weißt gar nicht wovon du redest.
Okay, das lassen wir im Raum stehen. Ich gehe jede Wette ein, dass man
den Unterschied im PWM Spektrum sieht. Aber das müssen wir nicht hier
und jetzt diskutieren.
EDIT:
Darauf möchte ich gerne hinaus:
http://e2e.ti.com/resized-image.ashx/__size/550x0/__key/CommunityServer-Discussions-Components-Files/171/7317.npc1.JPG
>Aber: ich kenne den letzten Wert vor dem Überlauf nicht. Der ist doch
undefiniert. Oder anders ausgedrückt:
Nein ? Ich kenne alles was ich wissen will, indem ich's in eine globale
variable packe.
Der Ansatz ist eh falsch. Der Ueberlauf ist immer am ende der Tabelle.
Ich habe einen Phasenzaehler, der hat zB 32 bit. Dazu addiere ich pro
timer interrupt zb 1234567, dann nehme ich die jeweils obersten 8 oder
10 bit und gehe damit in die Sinustabelle. Die Sinustabelle muss daher
2^N lang sein. Somit habe ich automatisch den Ueberlauf. Ich bekomme
jeden timerinterrupt einen neuen Wert, der entweder auf einen DAC oder
einen PWM geht.
Der Phasenzaehler ist natuerlich eine globale variable.
@ al3ko -.- (al3ko)
>> Falsch. Du vergisst den Gleichanteil, aka. Offset. Dein Quasi-analoge>> PWm.Erzeugung mittel Dreiekc und Komparator mus natürlich dann auch>> einen Offset kriegen.>Wie geschieht das? Wie packe ich einen Offset auf mein Dreieck? Wenn wir>den Offset da auch mit raufpacken, sieht die Geschichte in der Tat>wieder ganz anders aus.
Ist es wirklich SOOO schwer, sich einen Sinus mit Offset 1 vorzustellen?
y = sin(x) +1
>> Denn hast du schon! Dein i!>Mein i gibt mir aber keine zuverlässige Auskunft über meine aktuelle>Position.
Wie bitte? Aber sicher! Dein i ist der Phasenwinkel des Sinus!
> Wenn i=160*409 ist, dann ist es genau der Wert vor dem>Überlauf. Genau dieser Überlauf gibt mir Auskunft über den Nulldurchgang>meines Sinus.
Viel einfacher. Nimm 2^16 = 2*Pi
Damit wird dein MSB von i zum Indikator, ob du in der 1. oder 2.
Halbwelle bist.
0x7345 -> 1. Halbwelle
0x8234 -> 2. Halbwelle
>Den Überlauf kann ich doch nicht programmtechnisch erfassen, oder habe>ich eine Denkblockade?
Letzteres.
>Wann habe ich die Sinustabelle vollständig durchlaufen, ergo wann habe>ich die positive Halbwelle fertig.
Dein Problem ist deine Sparsamkeit. Nimm nicht eine Halbwelle als
Maximum für deinen Phasenindex sondern die volle Sinusschwingung.
>Nach dem Überlauf ist i=313. Wenn ich eine andere PWM Frequenz wähle,>ändern sich alle Werte.
Vergiss die Werte, die sind vollkommen egal.
>Okay, das lassen wir im Raum stehen. Ich gehe jede Wette ein, dass man>den Unterschied im PWM Spektrum sieht. Aber das müssen wir nicht hier>und jetzt diskutieren.>>Darauf möchte ich gerne hinaus:>>http://e2e.ti.com/resized-image.ashx/__size/550x0/...
Drei Level Wechselrichter. Aber bis dahin musst du erstmal an deinen
Grundlagen arbeiten.
al3ko -.- schrieb:> Wann habe ich die Sinustabelle vollständig durchlaufen, ergo wann habe> ich die positive Halbwelle fertig.
Na, na. So schwer ist das nicht.
Wenn dein Zugriffsindex von 0 bis 512 geht, deine Tabelle aber nur 256
EInträge hat, dann sind die Zugriffsinidzes 256 bis 511 diejenigen der
zweiten Halbwelle.
WEnn du nicht genug Bits für einen 9 Bit Index hast, dann kann man immer
noch mit einer globalen Variablen ein derartiges Bit dazu 'erfinden'.
Praktisch gesehen läuft es dann darauf hinaus, dass man eine Variablen
bei jedem Zähler-0-Durchgang von 0 auf 1 bzw. umgekehrt schaltet und
abhängig von der Variablen dann den Ausgabewert vom Offset aus addiert
oder subtrahiert.
Sei halt ein wenig kreativ!
Simpler ist es aber, ganz einfach die volle Welle abzulegen. Dann
funktioniert die Ausgabe bei allen Wellenformen immer gleich, was
letztendes der Geschwindigkeit zu gute kommt. Gut, bei 50Hz ist das
jetzt nicht soo das Kriterium.