Forum: Mikrocontroller und Digitale Elektronik DDS sample-Auswahl Rundungsfehler


von Peter A. (elkopeter)


Lesenswert?

Hallo in die Runde.
Ich generiere mir eine PWM im Phase Correct Modus des Atmega 8A via 
Atmel Studio 6.
Hierzu habe ich ein samples-Array mit 256 Werten von 0-511-0.
Als Phasenakku verwende ich eine uint16_t Variable die dann über ein 
Schieberegister >>8 die Indizes für den jeweiligen Wert ausgibt.
Im Prinzip muss ich beim Programmstart nur noch die gewünschte 
Ausgangsfrequenz meines Signals eingeben und die restlichen Werte 
(Indexschrittweite etc.) werden automatisch berechnet.

Jetzt zur Frage:
Aktuell wird meine Schrittweite durch eine 16Bit Variable bestimmt.
Meine PWM-Frequenz liegt bei 3,6kHz. Für ein Sinussignal mit 20Hz 
brauche ich dann 3600/(2*20) = 90 Werteänderungen je Halbwelle.
Das ergibt dann für meine Indexschrittweite einen Wert von 2,844444444.
In mein Highbyte schreibe ich also die 2 und in das obere nibble des 
Lowbytes die 8 und in das unter nibble die 4.
Jetzt erzeuge ich allerdings immernoch einen Rundungsfehler, da ja meine 
Schrittweite eigentlich 65635/90=728,17777 sein müsste. 0x284 entspricht 
dezimal aber einer Schrittweite von 644.
Kann mir jemand eine Möglichkeit nennen dieser Abweichung 
entgegenzuwirken?

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Guck mal in die Appnote von atmel in sachen DTMF:
http://www.atmel.com/dyn/resources/prod_documents/DOC1982.PDF

Die machen da ja auch nur etwas DDS und verkleinern mit einen kleinen 
TRick den Fehler.
Ganz wech kricht man den nicht.

von Peter A. (elkopeter)


Lesenswert?

Martin Wende schrieb:
> Guck mal in die Appnote von atmel in sachen DTMF:
> http://www.atmel.com/dyn/resources/prod_documents/DOC1982.PDF
>
> Die machen da ja auch nur etwas DDS und verkleinern mit einen kleinen
> TRick den Fehler.
> Ganz wech kricht man den nicht.

Ah ok ich schau mir das mal an.
Notfalls kann ich mit der  Abweichung leben.

von Falk B. (falk)


Lesenswert?

@ Peter A. (elkopeter)

>Hierzu habe ich ein samples-Array mit 256 Werten von 0-511-0.
>Als Phasenakku verwende ich eine uint16_t Variable die dann über ein
>Schieberegister >>8 die Indizes für den jeweiligen Wert ausgibt.

Schiebeoperation. Ein Schieberegister ist was anderes, nämlich ein 
logisches Bauteil.

>Aktuell wird meine Schrittweite durch eine 16Bit Variable bestimmt.
>Meine PWM-Frequenz liegt bei 3,6kHz. Für ein Sinussignal mit 20Hz
>brauche ich dann 3600/(2*20) = 90 Werteänderungen je Halbwelle.

???

Die Berechnung des Frequenzeinstellwortes ist doch einfach.

ftw = fa * 2^n / fs = 20 * 2^16 / 3600 = 364,0888888

wobe n die Bitbreite deines Phasenakkus ist. Das ist klar und deutlich.

Klar gibt es einen Rundungsfehler, weilö man nur ganzzahlige Werte als 
Increment nutzen kann. D.h. deine Frequenz weicht etwas ab, in diesem 
Fall kommen halt 19,9951171875 Hz raus. Den Rundungsfehler kann man 
verkleinern, indem man die Bitbreite des Phasenakkus erhöht. Für deine 
Anwendung sollte das aber akademisch sein. Denn die Frequnenzauflösung 
leigt bei DDS immer bei

fsa = fs / 2^n = 3600 / 2^16 = 0,054931640625 Hz.

>Das ergibt dann für meine Indexschrittweite einen Wert von 2,844444444.

Nö. Siehe oben.

>In mein Highbyte schreibe ich also die 2 und in das obere nibble des
>Lowbytes die 8 und in das unter nibble die 4.

Mann O Mann. Du bist ein echter Künstler im umständlich ausdrücken. 
Normale Leute hätten einfach gesagt, dein Increment is 0x284 oder 644.

>Jetzt erzeuge ich allerdings immernoch einen Rundungsfehler, da ja meine
>Schrittweite eigentlich 65635/90=728,17777 sein müsste. 0x284 entspricht
>dezimal aber einer Schrittweite von 644.

???

>Kann mir jemand eine Möglichkeit nennen dieser Abweichung
>entgegenzuwirken?

Deine Rechung stimmt nicht.

von c-hater (Gast)


Lesenswert?

Peter A. schrieb:

> Das ergibt dann für meine Indexschrittweite einen Wert von 2,844444444.
> In mein Highbyte schreibe ich also die 2 und in das obere nibble des
> Lowbytes die 8 und in das unter nibble die 4.

Das ist schlicht falsch. Es handelt sich um einen Binärbruch, nicht um 
eine BCD-Darstellung. Den korekten Wert berechnest du, indem du einfach 
deinen Dezimalbruch mit 256 multiplizierst. Das Ergebnis ist 0x2D8.

> Jetzt erzeuge ich allerdings immernoch einen Rundungsfehler

Das ist kein Rundungsfehler, sondern einfach ein ganz normaler Fehler.

Der Rundungsfehler ist dann das, was bei korrekter Umsetzung noch 
überbleibt. Im konkreten Fall rund 0.025%

> Kann mir jemand eine Möglichkeit nennen dieser Abweichung
> entgegenzuwirken?

Man versteht, was man da benutzt? Naja, immerhin bist du deutlich weiter 
als die meisten normalen Codeklauer. Du überprüfst und hinterfragst das 
Ergebnis. Sehr guter Ansatz, da helfe ich dann auch gern weiter.

Der nächste Schritt ist dann die Erkenntnis, daß Fehler immer 
zuallererst im eigenen Code zu suchen sind...

von c-hater (Gast)


Lesenswert?

Falk Brunner schrieb:

> Den Rundungsfehler kann man
> verkleinern, indem man die Bitbreite des Phasenakkus erhöht.

Oder ein etwas clevereres Konzept verwendet...

Aber natürlich scheitern auch die clevereren Konzepte irgendwann an den 
Gesetzmäßigkeiten der Mathematik. Nur eben später als triviale Konzepte 
wie der sog. "Phasenakku" (der letzlich nichts anderes als eine 
primitive Festkomma-Addition darstellt)

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.