Forum: Mikrocontroller und Digitale Elektronik DDS, 50Hz Sinus, Schaltfrequenz 16 - 48kHz


von Al3ko -. (al3ko)


Angehängte Dateien:

Lesenswert?

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

von Falk B. (falk)


Lesenswert?

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

von Al3ko -. (al3ko)


Lesenswert?

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?

: Bearbeitet durch User
von Purzel H. (hacky)


Lesenswert?

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.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ 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!

von Al3ko -. (al3ko)


Angehängte Dateien:

Lesenswert?

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ß

von Al3ko -. (al3ko)


Angehängte Dateien:

Lesenswert?

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?

von Falk B. (falk)


Lesenswert?

@ 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 ;-)

von Al3ko -. (al3ko)


Lesenswert?

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

: Bearbeitet durch User
von Purzel H. (hacky)


Lesenswert?

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

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
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.