Forum: Mikrocontroller und Digitale Elektronik Sinus Cosinus mit AVR (C) und PWM


von Ernst (Gast)


Lesenswert?

Hallo,


ich möchte gerne mit einem AVR in C einen Sinus / Cosinus mittels PWM 
erzeugen.
Die Forensuche und Google habe ich schon fleißig genutzt. Leider fehlt 
mir da das Verständnis wie ich soetwas realisiere.

Folgendes habe ich herausgelesen:

Ich brauche zwei Timer, ein PWM Timer, ein durchlaufender Timer.
Dem PWM Timer übergebe ich im Überlauf Interrupt des anderen Timers die 
entsprechenden Werte aus einer Tabelle in seine Vergleichsregister.

Wie ermittel ich denn nun die PWM Werte für den Sinus / Cosinus?
Und vor allem: Mit welchem Timing sollten die Timer laufen?

Ich möchte, wie erwähnt, einen Sinus und einen Cosinus mit einer 
Frequenz von ~40...70Hz erzeugen, wobei die Frequenz und Phasenversatz 
einstallbar sein soll.

Vielleicht bekomme ich ja hier die nötigen Tipps (in Form von Beiträgen 
oder Verweise auf Erklärungen)?

Mir geht es hier nicht um das Handling mit Tabellen, C oder der 
Registereinstellungen für die Timer.


Danke

Ernst

von Dirac-Impuls (Gast)


Lesenswert?

Falls ich dich richtig verstanden habe:


Sin und Cos hat Werte zwischen -1 und 1. Die PWM Werte zwischen 0 und 1.

Somit musst du den Mittelpunkt auf 0,5 setzen.


Wenn also sin(0°)=0 dann ist v_T = 0,5.

sin(30°)=0,5 -> v_T = 0,75

sin(90°)=1 -> v_T =1

....

Formel: v_T= (sin(phi)+1)/2



Ich würde es folgendermaßen probieren:

Die eine PWM hat eine Periodenlänge von T_1=1/f

Die andere PWM probierst du mit z.B T_2=1/100*T_1

Jetzt lässt du die 2. PWM jeweils mit den vorausberechneten v_T laufen.

von Alexander L. (lippi2000)


Lesenswert?

Ganz einfach, mit der PWM erzeugst du dir durch einen anschließenden 
Tiefpass ne Gleichspannung. Diese Gleichsspannung kannst du von 0-Umax 
durch setzen des Tastgrades einstellen. Das heist, dein Sinus hat einen 
Offset von seiner Amplitude. Also Ausgang soll 0-5V sein. Bedeutet du 
kannst ihn mit 8-Bit PWM zwischen 0 und 5V in 256 Schritten einstellen. 
Dein Sinus darf nicht negativ sein und erhält einen Offset auf die halbe 
Ausgangsspannung. In dem Fall 2,5V. Dies bedeutet für dich, eine 8-Bit 
PWM wird um den Wert 127 herum verändert.

Also Sinus hat eine Amplitude von maximal 2,5V, denn dann geht er von 
0-5V.

Du musst dir also ne Wertetabelle anlegen und die Sinuswerte vorher 
ausrechnen.

Deine PWM-Frequenz sollte ein ganzes Stück höher liegen als deine 
Sinusfrequenz. Mal angenommen deine 8-Bit-PWM läuft mit 64kHz und du 
möchtest einen Sinus mit 50Hz erzeugen, hat dieser 256 Zustände die 
aller 5 PWM-Perioden aktualisiert werden.

f = 64kHZ/(50Hz*256*x) mit x=5 folgt f=50Hz

Wenn du nicht die Perioden der PWM zählen willst, dann nimm einfach nen 
zweiten Timer der dir aller 78,125µs (1/(50Hz*256)) den nächsten Wert 
läd.

von Oliver (Gast)


Lesenswert?

>Mir geht es hier nicht um das Handling mit Tabellen, C oder der
>Registereinstellungen für die Timer.

Sondern?

>Die Forensuche und Google habe ich schon fleißig genutzt.

???

Beitrag "DDS Sinus Tabelle wie berechnen ?"
Beitrag "3 Phasen 0-80Hz Sinusgenerator"

Oliver

von Ernst (Gast)


Lesenswert?

O.K.

Ich brauche also eine Tabelle mit 256 Werten.

Berechnung: for (i=0; i<255; i++)
wert[i]=128+127*sin((double)i/256*2*pi);


Ich verstehe nun leider nicht, wann, welche Werte davon genau in die 
entsprechenden Vergleichsregister vom PWM Timer geladen werden müssen.


Ich möchte ja Frequenz und Phasenversatz einstellen können.

Gehen wir davon aus, dass ich einen PWM Timer nutze (für sin & cos) und
einen freilaufenden Timer der einen Interrupt bei Überlauf ausführt.

In der Überlauf ISR, was muss ich da genau machen. Hier sind die 
tatsächlichen Frequenzen der Timer doch dann relevant???

Es wäre toll, wenn mir das jemand schrittweise erläutern kann!

Gruß
Ernst

von Ernst (Gast)


Lesenswert?

BTW: Ist es nicht besser eine möglichst große Tabelle anzulegen um einen 
sauberen Sinus zu erzeugen? Also >255?

Kennt jemand eine Quelle, wo die ganze Thematik "für dumme" erklärt 
wird?
Irgendwie scheint hier allen klar zu sein wie so etwas funktioniert, 
bloß mir nicht ;-)

Gruß
Ernst

von Karl H. (kbuchegg)


Lesenswert?

Ernst wrote:

> Ich möchte ja Frequenz und Phasenversatz einstellen können.

Sieh erst mal zu, dass du überhaupt einen Sinus rauskriegst.
Wenn du dann soweit bist, kannst du ja mal darüber nachdenken, wie man 
die Frequenz einstellen kann. Wenn du den Sinus erst mal am Ausgangspin 
hast, hast du auch das Wissen, wie man Frequenz bzw. Phasenlage (Phase 
in Bezug worauf eigentlich?) einstellen kann.

> Irgendwie scheint hier allen klar zu sein wie so etwas funktioniert,
> bloß mir nicht ;-)


Weil es im Grunde watscheneinfach ist.

Können wir mal davon ausgehen, dass du eine PWM hast.
Sprich: Du stellst einen Wert in einem Register ein und am Ausgangspin 
(+nachfolgender Elektronik) erscheint die zugehörige Spannung.
Das nehmen wir jetzt einfach mal als gegeben an. Das ist so und 
funktioniert (und das sollte dein erster Teilschritt zur Lösung des 
kompletten Problems sein)

Was ist dann so eine Sinusschwingung?
Ganz einfach: Über die Zeit gesehen, 'schwingt' die Spannung an besagtem 
Ausgangspin in Form einer Sinusschwingung.

Willst du zb haben, dass eine Sinusschwingung 1Hz hat, muss die 
Ausgangsspannung in einer Sekunde einmal von 0 nach +1 nach -1 und 
wieder zurück nach 0 kommen. (von 2.5 Volt nach +5V nach 0V und zurück 
nach 2.5V)

Ist deine Tabelle mit den Stützwerten jetzt 256 Einträge lang, heist 
das, dass in besagter 1 Sekunde alle 256 Werte nacheinander an den 
Ausgangspin 'geschickt' werden müssen. Wenn möglich in gleichmässigen 
Zeitabständen :-)
Daraus folgt: Du musst alle 1/256 Sekunde den jeweils nächsten Wert 
ausgeben.
Daraus folgt weiters: Du brauchst einen Mechanismus, der alle 1/256 
Sekunde getriggert wird, um jeweils den nächsten Wert auszugeben. Das 
ist dein Zeitimpuls, der die Ausgabe vorantreibt.

Wie ist das bei einer Wunschfrequenz von 2 Hz?
Na offensichtlich, muss dann alles doppelt so schnell ablaufen.
Du brauchst einen Mechanismus der dir alle 1/512 Sekunde auslöst. Bei 
jedem Auslösevorgang wird der jeweils nächste Tabellenwert an den 
Ausgang gelegt (wenn die Tabelle zu Ende ist, fängst du wieder mit dem 
ersten Wert an). Da deine Tabelle 256 Einträge lang ist, wird also in 1 
Sekunde die Tabelle 2 mal ausgegeben. Und da deine Tabelle genau eine 
Schwingung enthält, bedeutet das, dass in 1 Sekunde 2 komplette 
Schwingungen ausgegeben werden. Das wiederrum sind 2Hz

Wenn nun dein Zeitimpuls alle 1/512 Sekunde kommt, kannst du dann immer 
noch eine Schwingung mit 1Hz erzeugen?
Aber sicher doch! Du ignorierst einfach jeden 2-ten Zeitimpuls. Das 
Signal kommt zwar 512 mal in der Sekunde, aber wenn du nur jedes 2-te 
mal reagierst, gibst du die komplette Tabelle in einer Sekunde nur 
einmal aus -> du hast wieder 1Hz

Was ist, wenn dein Zeitimpuls nicht 512 mal sondern 5120 mal in der 
Sekunde kommt. Gleiches Spielchen: Bei einer Tabellengrösse von 256 und 
5120 Ausgabetriggern in der Sekunde, wird die Tabelle 20 mal ausgegeben 
und da jede komplette Tabellenausgabe ein kompletter Wellenzug ist, hast 
du 20 Hz. Kleinere Frequenzen kannst du nach wie vor erreichen, indem du 
nicht bei jedem Zeitimpuls eine Ausgabe machst, sondern zb nur bei jedem 
2-ten, jedem 3-ten etc.

Und das wars dann eigentlich schon. Alles was du brauchst, ist eine 
Idee, wie du den Zeitimpuls realisieren kannst. Und da kommt jetzt der 
2-te Timer ins Spiel. Mit seinem Overflow Interrupt kannst du dir zb. 
diesen Zeitimpuls erzeugen.


In deiner Vorstellungswelt, musst du die PWM vom Ausgeben der Schwingung 
trennen! Das erste hat mit dem zweiten nichts zu tun!
DIe PWM sorgt dafür, dass ein Wert in einem Register in eine Spannung 
übersetzt wird. Die PWM interessiert sich aber nicht dafür, wo dieser 
Wert herkommt.
Der andere Timer hat eine ganz andere Aufgabe. Seine Aufgabe ist es, 
Werte aus einem Array in regelmässigen Zeitabständen zur Verfügung zu 
stellen. Ob dieser Wert an einen Port ausgegeben wird um dort 
irgendwelche Lämpchen einzuschalten (und so vielleicht ein Lauflicht zu 
realisieren) oder ob diese Werte über eine Serielle Schnittstelle 
verschickt werden oder ob diese Wert zufällig in besagtes Register zu 
laden ist, aus dem sich die PWM versorgt, ist dem Timer erst mal egal. 
Der liefert aus dem Array einen Wert nach dem anderen. Und das in 
regelmässigen Zeitabständen.

PWM                 - ist dafür zuständig eine bestimmte Spannung
                      zu erzeugen

2.te Timer Overflow - sagt der PWM in regelmässigen Zeitabständen
                      welche Spannung zu erzeugen ist.


Fang mal mit deinem ersten Teilziel an:
Eine PWM laufen zu kriegen, die dir an einem Ausgangspin eine bestimmte 
Spannung realisiert.

von Alexander L. (lippi2000)


Lesenswert?

Grundsätzlich solltest du dich erst einmal mit der Gleichstromerzeugung 
mittels PWM befassen.

1.Durch den TP, auf den du deine PWM gibst, wird diese integriert. Das 
bedeutet du brauchst einige Perioden damit dein Signal steht. Je weiter 
weg die PWM-Frequenz von der Grenzfrequenz des Tiefpass liegt, desto 
weniger Ripple hast du.

2. Die einstellbaren Spannungswerte hängen von deiner PWM ab. Bei 8-bit 
ergeben sich nun mal nur 256 Tastgrade und somit auch nur 256 
Spannungswerte. Also musst du auch nur 256 Werte berechnen.

3. Deine Sinusfrequenz hängt von dem TP-Filter und der Anzahl der 
verwendeten Spannungswerte.


Mal angenommen du willst einen 100Hz Sinus mit 256 Spannungswerten 
erzeugen. Dann muss du mit einer Frequenz von 100Hz*256 = 25,6kHz die 
Spannungswerte (Vergleichswerte) an den PWM-Generator schreiben. Der TP 
muss nun jeden Wert integrieren. Mal angenommen deine PWM-Frequenz soll 
um den Faktor 10 höher liegen als der TP, dann bedeutet es doch, dass 
der TP auf 25,6kHz gelegt wird und deine PWM mit 10*25,6kHz = 256kHz 
durchgeführt werden muss.

Wenn du statt 8-Bit PWM eine 10-Bit-PWM nehmen willst, dann ergeben sich 
die Werte wie folgt:

Wertübergabe an PWM-Generator mit f = 102,4kHz
Grenzfrequenz Tiefpass: 102,4kHz
PWM-Frequenz: 1,024MHz

von Ernst (Gast)


Lesenswert?

Hallo,

erst einmal ein riesen Dank an Karl Heinz!!!
Mit deinem Text, Stift und Zettel ist mir nun einiges klar geworden.

Ich habe mir gerade ein kleines C# Programm geschrieben um eine 
Sinustabelle zu erzeugen.
1
Console.Write((int)(128+127 * Math.Sin ( (2 * Math.PI * (double)i)/256)));

Verstehe ich also richtig, wenn ich folgende Rechnung aufstelle

(CPU_Clock / 256)/ 256 = Basisfrequenz ???

Also z.B.
(16MHz / 256) / 256 = 244,141Hz

Wenn ich nun den PWM Timer (8Bit) und den durchlaufenden Timer (8Bit) 
mit
vollem Takt (16MHz) laufen lasse und in jedem Timer Überlauf einem nach 
dem anderen Tabbelenwert ausgebe, habe ich eine Frequenz von 244,141Hz.

Erhöhe ich den Zeiger auf den Tabellenwert nur jeden
-zweiten Interrupt =>
244,141 / 2 = 122.0705Hz
- dritten Interrupt =>
244,141 / 3 = 81,380 Hz
usw.

Vielleicht ist das Quatsch was ich gerade geschrieben habe???

Wie kann ich denn die Frequenz jetzt auf 1/10 Hz genau einstellen ?



PWM läuft. Am Wochenende werde ich mal den Sinus mit voller Frequenz 
ausgeben.


Vielen Dank für die Unterstützung!

Gruß
Ernst

von hans (Gast)


Lesenswert?

Wenn die PWM läuft fehlt nur die DDS.

Hatte ich im Forum schonmal erklärt:

Hallo,

du must das System on DDS richtig kapieren.

Deine Tabelle hat 256 Einträge (hier Sinuswerte für eine Periode).

Dein Timer macht ein festes Zeitfenster (CTC mit reload).

Nehmen wir an, das Zeitfenster ist 39,0625 µS (Nachladewert 144 TZ)
und du gibst jedesmal denn nächsten Tabellenwert aus bedeutet das
für eine Periode (Tabelle 0-255) 10 ms (256*39,0625 µs) => eine
Frequenz von 100 Hz.

Das Bedeutet du hast eine Grundfrequenz von 100 Hz.

Du willst jetzt aber 300 Hz ausgeben. Dann mußt du bei
jedem Interrupt nicht eine sondern 3 Positionen weiterspringen.
Dein Additionswert ist jetzt 3.

->   Tabellenposition += Additionswert im Interrupt
     mit Additionswert = Ausgabefrequenz / Gundfquenz

Was ist aber bei 25 HZ? Wir brauchen Nachkommastellen.

Additionswert = 25 / 100 = 0,25

Ein UINT16 besteht aus 16 Bit. Wir denken uns jetzt ein Komma
zwischen HighByte und LowByte. Für die 3 von oben steht dann
binär  0000 0011 (,) 0000 0000 und für unsere 0,25
binär  0000 0000 (,) 0100 0000

Unser Tabellenzähler ist jetzt natürlich auch UINT16, das Highbyte
ist dabei unsere Tabellenposition für den Sinuswert.

Wenn man das sauber weiterrechnet kommt man auf die einfache
Endformel (bei 256 Werten):

Additionswert = Ausgabefrequenz  Aulösung_max  Nachladewert_Timer/Fosc

hier Auflösung_max = 2^16  (16Bit Zählerbreite)

Je höher die Auflösung umso feiner kann abgestuft werden, ein schneller
Fosc sorgt für saubere Signale bei größerer Ausgabefrequenz.


Ich hoffe, so ist es verständlich erklärt.


Gruß Hansl

von Ernst (Gast)


Lesenswert?

Hallo Hansl,

jetzt komme ich total durcheinander.

Also ich rechne:

- In der ISR: Tabellenposition += Additionswert im Interrupt

  mit Additionswert = Ausgabefrequenz / Gundfquenz


Z.B.
- Additionswert = 25 Hz / 100 = 0,25

Das High Byte ist dann meine Tabellenposition.


Hier die von dir angegebene Endformel:

Additionswert = Ausgabefrequenz  Aulösung_max  Nachladewert_Timer/Fosc

Was kommt dazwischen? * , / ,... ?


Danke

von hans (Gast)


Lesenswert?

Das mal! (x, * )

Dann hast du in der Interruptroutine die Tabellenposition berechnet,
das High-Byte ist der ganszahlige Anteil für die Tabelle, holst den
Wert und gibst ihn an deine PWM weiter.

gruß hans

von Ernst (Gast)


Lesenswert?

Hallo,

Additionswert = Ausgabefrequenz  Aulösung_max  
(Nachladewert_Timer/Fosc) ???

Wofür steht denn jetzt Auflösung_max ?

Bis zum Posting von dir habe ich noch (fast) alles Verstanden ;-)

Jetzt werden die Stützpunkte doch weniger, wenn ich nur jeden 3. oder 4. 
oder ... aus der Tabelle lese?
Um einen sauberen Sinus zu bekommen sollten es doch nicht zu wenig 
Stützpunkte werden, verstehe ich das richtig?

Viell. wäre dann eine >10 Bit PWM sinnvoll.

Ich brauche wohl erst einmal ein wenig Zeit um einen Sinus mit fester 
Frequenz auszugeben.
Am Wochenende schnappe ich mir mal mein Board und Messe mal was sich 
dann nach dem Ausgangsfilter tut.


Gruß
Ernst

von Ernst (Gast)


Lesenswert?

Irgendwie schlägt hier die autom. Formatierung zu:
1
Additionswert = Ausgabefrequenz * Aulösung_max * (Nachladewert_Timer/Fosc)

Ist die Formel so richtig?

von Karl H. (kbuchegg)


Lesenswert?

Ernst wrote:

> Jetzt werden die Stützpunkte doch weniger, wenn ich nur jeden 3. oder 4.
> oder ... aus der Tabelle lese?

Ja klar.
Wenn du in einer Sekunde angenommen maximal 100 Werte ausgeben kannst, 
aber 300 ausgeben müsstest, dann muss irgendwo etwas auf der Strecke 
bleiben.

> Um einen sauberen Sinus zu bekommen sollten es doch nicht zu wenig
> Stützpunkte werden, verstehe ich das richtig?

Richtig - Shannonsches Abtasttheorem.

>
> Viell. wäre dann eine >10 Bit PWM sinnvoll.

Das hat damit nichts zu tun.
Hier geht es lediglich um die Zeitauflösung deines Sinus. Oder mit 
anderen Worten: Wenn man sich eine komplette Schwingung ansieht, aus 
wievielen Punkten besteht sie. Die Feinheit der PWM regelt, wie fein du 
die Amplitude jedes einzelnen Stützpunktes einstellen kannst. Das hat 
aber nichts damit zu tun, wieviele Werte du in eine Schwingung packen 
kannst. Ganz im Gegenteil: Je mehr Bits du in die PWM steckst, umso mehr 
sinkt die Grenzfrequenz bis zu der die PWM deinen Vorgabewerten noch 
folgen kann. Und damit sinkt dann auch die maximale Frequenz, die dein 
DDS noch sauber generieren kann. Das folgt unmittelbar daraus, wie eine 
PWM funktioniert. Je mehr Bits - desto langsamer reagiert die PWM (weil 
ja der Zähler einen größeren Umfang zählen muss)

(In diesem Sinne habe ich bisher einen Zusammenhang ignoriert, um dich 
nicht zu verwirren. Ich hab weiter oben mal gesagt, dass der 
Abtasttimter mit der PWM nichts zu tun hat. Das stimmt so nicht ganz. 
Die PWM setzt dir prinzipielle Grenzen, wie schnell dein Abtasttimer 
maximal sein kann/darf. Es ist ja völlig sinnlos, wenn dein Abtasttimer 
sagen wir mal 400 Werte in der Sekunde zur PWM schickt, wenn die PWM 
selber nur maximal 100 Werte in der Sekunde realisieren kann. PWM ist ja 
auch nicht gratis, die braucht ja auch Zeit für sich [folgt aus dem 
Funkionsprinzip]: Je mehr Bits in der PWM realsiert werden - desto 
langsamer)

von Simon K. (simon) Benutzerseite


Lesenswert?

http://www.google.de/search?q=direkte+digitale+synthese

Nur mal als Tipp. Für das Thema braucht man nicht Leute in einem Forum 
zu nerven.

von Ernst (Gast)


Lesenswert?

Ist jeder Beitrag !direkt! an dich persönlich gerichtet?

Ja: Ich bitte um Entschuldigung das ich nicht so schlau bin wie du es 
bist.

Nein: Dann ignorier es doch einfach!



Liebe Grüße
Ernst

von Gerhard (Gast)


Lesenswert?

Hi Ernst,

bin gerade über folgendes Tutorial gestolpert. Könnte hilfreich für dich 
sein:
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=68302

insbesondere der Abschnitt zur Phasenkorrektur könnten dich 
interessieren.

Gerhard

von Gast (Gast)


Lesenswert?

"Nur mal als Tipp. Für das Thema braucht man nicht Leute in einem Forum
zu nerven."

Hast du auf der Uni gelernt, so freundlich gegenüber Leuten zu sein, die 
nicht ihr halbes Leben Zeit hatten abi zu machen und zu studieren (von 
Mama und Papa gesponsored)???

von Simon K. (simon) Benutzerseite


Lesenswert?

Gast wrote:
> "Nur mal als Tipp. Für das Thema braucht man nicht Leute in einem Forum
> zu nerven."
>
> Hast du auf der Uni gelernt, so freundlich gegenüber Leuten zu sein, die
> nicht ihr halbes Leben Zeit hatten abi zu machen und zu studieren (von
> Mama und Papa gesponsored)???

Quatsch, so alt bin ich noch gar nicht.

von Ernst (Gast)


Lesenswert?

Hallo,


jetzt hatte ich ein wenig Zeit die ganze Sache mal in der Hardware zu 
testen.

Ich habe eine Tabelle mit 2048 Werten angelegt. Der Timerinterrupt für 
die Ausgabe der Tabellenwerte auf das PWM Register erfolgt nach 256 
Takten.
Der CPU Takt liegt bei 20MHz.

O.K. die Basisfrequenz liegt jetzt bei 30,05Hz. Das lässt sich auch mit 
dem Oszilloskop bestätigen.

Meine Interrupt Overflow sieht so aus:
1
ISR(TIMER0_OVF_vect) {
2
3
    
4
  
5
   
6
  OCR1BL=pgm_read_byte(&signal[test]);
7
  
8
9
  test+=1;
10
  if (test>=2048)
11
    test=0;
12
}

Wird der Tabellenwert nur jeden zweiten Interrupt erhöht, so messe ich 
knapp 30Hz. Erhöhe ich die Tabellenposition pro Interrupt um 2 
Positionen komme ich auf 61Hz.

Soweit so gut ;-)


Ich verstehe bloß nicht wie ich jetzt auf z.B. 50Hz kommen soll ?

Hansl hatte ja bereits folgendes erklärt:

"Additionswert = Ausgabefrequenz / Gundfquenz

Was ist aber bei 25 HZ? Wir brauchen Nachkommastellen.

Additionswert = 25 / 100 = 0,25

Ein UINT16 besteht aus 16 Bit. Wir denken uns jetzt ein Komma
zwischen HighByte und LowByte. Für die 3 von oben steht dann
binär  0000 0011 (,) 0000 0000 und für unsere 0,25
binär  0000 0000 (,) 0100 0000

Unser Tabellenzähler ist jetzt natürlich auch UINT16, das Highbyte
ist dabei unsere Tabellenposition für den Sinuswert.
"

Rechne ich jetzt Wunschfrequenz / Grundfrequenz:

50Hz / 30,5Hz = 1,64

Also müsste ich die Tabellenposition immer (Overflow ISR) um 1,64 
Stellen erhöhen.
Das kann ich jetzt leider nicht auf die Aussage von Hansl beziehen.

Bei 0,25 gibt er jetzt 0000 0000 (,) 0100 0000 an. Wieso???

Kann mir bitte jemand auf die Sprünge helfen?


Danke!
Ernst*

*(der nicht studiert hat und auch im Job nichts mit Elektronik zu tuen 
hat)

von Ernst (Gast)


Lesenswert?

So, weiter geht es ;-)

Ich berechne nun vorher:
1
FCV = (uint16_t) freq * 65536 * 256/ F_CPU;

Die ISR sieht so aus:
1
STEP+=FCV;
2
//von 16 Bit auf Tabellenlänge 2048 
3
OFFSET= STEP / 32;
4
OCR1BL=pgm_read_byte(&signal[OFFSET]);

bei
1
freq=50;

bekomme ich auch 50Hz heraus :-)))

Ehrlich gesagt, verstehe ich das Prinzip noch nicht ganz.

Wie sieht es denn aus wenn ich genauere Frequenzen möchte, also in einer 
möglichst feinen Auflösung (z.B. in 0,2Hz Schritten).

Ich könnte ja die Wunschfrequenz mit 1000 Multiplizieren. Bei 33Hz dann 
33000 als Int...???



Gruß

von Simon K. (simon) Benutzerseite


Lesenswert?

Ernst wrote:
> Wie sieht es denn aus wenn ich genauere Frequenzen möchte, also in einer
> möglichst feinen Auflösung (z.B. in 0,2Hz Schritten).

Na dann brauchst du vor allem eine größere Tabelle und einen schnelleren 
Takt. Nicht umsonst kriegen DDS Chips wie AD9833 nen Takt von 50MHz.

von Ernst (Gast)


Lesenswert?

Hallo,

ich hätte mich viell. anders ausdrücken sollen. Ich möchte die Frequenz 
verstellen können. Mir schwebt da eine Schrittweite von 0,01Hz vor 
(bitte nicht schlagen ;-) ).

Ist das mit dem Verfahren und meinem Hardwarevorraussetzungen ( CPU 
Takt, 2048 Werte Tabelle, 8Bit ISR Timer) möglich?

Gruß
Ernst

von hans (Gast)


Lesenswert?

Der Schrei nach einer noch größeren Tabelle ist reiner Unsinn!

Die Tabellengröße hat mit der Frequenzauflösung nichts zu tun.

Die jetzige ist schon zu groß für die gewünschte Aufgabe mit
PWM-Ausgabe und 40-70 Hz.

Es geht um den Zähler mit dem die Tabellenadresse berechnet wird.

So wie Ernst es jetzt mit seiner Tabelle macht hat er einen
16 Bit Zähler, die obersten 11 Bit ergeben die Tabellenposition
(0-2047).

D.h. er hat 5 Bit Nachkommastellen (1/2 1/4 1/8 1/16 1/32).

Für eine höhere Auflösung braucht er einen breiteren Zähler um
mehr Nachkommastellen unterzubringen. In Assembler würde man
einfach ein Byte dranhängen, in C bleibt (fast) nur der
Einsatz von Unsigned Long.
Die ersten 11 Bit bleiben die Tabellenadresse (Ganzzahlanteil),
der rest ist Nachkommastellen.

Das bearbeiten von 32 Bit Zahlen kostet Zeit, der Interruptaufruf
ebenfalls. Pass auf, das dein Interrupt nicht zu lang wird.

Sonst die Tabelle auf 8 Bit zurückführen, die Interrupthäufigkeit
hochsetzen (Faktor 32 oder mit neuer Grundfrequenz rechnen) und
dann mit dem 32 Bit-Zähler arbeiten.

gruß hans

von Ernst (Gast)


Lesenswert?

Hallo Hans,


ich habe die Tabelle so "groß" dimensioniert, weil ich ja noch einen 
weiteren phasenverschobenen Sinus ausgeben möchte und der Phasenversatz 
möglichst kleinschrittig einstellbar sein soll.

Bei 360° / 2048 hätte ich ja dann ~ 2 ° Schrittweite.

Verstehe ich dich richtig, dass ich für mein Vorhaben dann komplett mit 
32 Bit Variablen rechnen müsste, d.h. ein 32 Bit Tabellenzähler
1
FCV = (uint32_t) freq * 4294967296 * 256/ F_CPU;

In der ISR dann
1
STEP+=FCV;
2
//von 32 Bit auf Tabellenlänge 2048 
3
OFFSET= STEP / 2097152;
4
OCR1BL=pgm_read_byte(&signal[OFFSET]);

Bloß wie rechne ich jetzt für "krumme" Frequenzen, wie 50,01Hz oder 
50,02Hz , den FCV aus ?

Mit meinem Beispiel oben kann ich ja nur mit ganzen Integer den FCV 
ausrechnen.


Der Controller muss sonst nicht viel können, wenn also ein guter Teil 
der Ressourcen für die Erzeugung vom Sinus + phasenversetztem Sinus 
drauf geht.

Es wäre toll wenn mir da jemand der Ahnung hat helfen kann!

Bis vor einer Woche hätte ich nie gedacht das ich überhaupt einen so 
sauberen Sinus aus meinem ATMega 32 heraus kriege ;-)

Gruß
Ernst

von hans (Gast)


Lesenswert?

Du must halt bei dieser Berechnung auch mit Nachkommastellen arbeiten.
50.5 Hz sind ja das das gleiche wie 505/10 Hz im Dezimalsystem.
Oder 3280/100 in HEX (12928/256=50.5x256/256).
Für die Frequenz in deiner Formel einfach den Wert mit Nachkomma
einsetzen (256 oder 2^x kürtzt sich dann teilweise raus).

Für den Phasenversatz ist ebenfalls nicht die Tabellengröße
wichtig. Hier entscheidet ebenfalls nur der Zähler.

Der eine startet bei 0 der andere bei einem Startwert=Phasenversatz.
0° Phase => Startwert 0
180° Phase => Startwert Tabellengröße/2 (da Tabelle = 360°).
Auch hier kommt es auf die möglichen Nachkommastellen an.

gruß hans

von Simon K. (simon) Benutzerseite


Lesenswert?

hans wrote:
> Der Schrei nach einer noch größeren Tabelle ist reiner Unsinn!
>
> Die Tabellengröße hat mit der Frequenzauflösung nichts zu tun.

Indirekt schon, denn bei ganz langsamen Frequenzen will man ja immer 
noch möglichst wenig "Treppen" im Signal haben. Wobei das mit dem RC 
Tiefpass ja sowieso entschärft wird.

Wobei das natürlich auch nichts mit der Frequenzauflösung direkt zu tun 
hat. Aber wenn die Frequenzauflösung halt 50Hz beträgt (als Beispiel), 
dann ist 50Hz auch die kleinste Frequenz.

Also hat die Tabellengröße quasi zwei mal indirekt was mit der 
Frequenzauflösung zu tun. Najo, hab mich mit dem Thema noch nie so 
wirklich befasst.

von hans (Gast)


Lesenswert?

@Simon
Wenn du schon sagst, daß du dich niecht damit beschäftigt hast, dann
sei doch bitte ruhig, bis sich dieser Zustand geändert hat.

Deine Aussage bzgl. der 50 Hz als kleinste Frequenz ist reiner
Schwachsinn!

Wenn die Grundfrequenz bei 50 Hz liegt, d.h. mit einem Additionswert
von 1 (jeder Tabellenwert nacheinander), ist mit der entsprechenden
Anzahl an Nachkommastellen für den Zähler und den Additionswert jede
Frequenz <50 Hz realisierbar. Der Tabellenwert wird dann mehrmals
Nacheinander zum Ausgabewert, bis die Nachkommastellen einen Überlauf 
haben.
Genau das ist das Grunkonzept der DDS!

gruß hans

von Ernst (Gast)


Lesenswert?

Hallo,

ich dachte ich kann nur einen Zähler nutzen und dann auf den ermittelten 
Index der Sinustabelle entsprechend draufrechnen (+1 = 2°, +2 = 4°...).
So dann entsprechend Rechenleistung einsparen.

Hans, ich kann dir leider nicht folgen. Muss ich dann z.B. für 50,05Hz
1
FCV = (uint32_t) (50050 * 4294967296 * 256/ F_CPU)1000
;
rechnen ?

und lasse die ISR wie in meinem letzten Posting?

Wie bestimme bzw. berechne ich denn jetzt die kleinst mögliche 
Schrittweite ?

Irgendwie muss ich ja jetzt herausfinden wie "kleinschrittig" ich im 
Programm arbeiten kann.


Gruß
Ernst

von Ernst (Gast)


Lesenswert?

Ich meine:
1
FCV = (uint32_t) (50050 * 4294967296 * 256/ F_CPU)/1000;

von Simon K. (simon) Benutzerseite


Lesenswert?

hans wrote:
> Wenn die Grundfrequenz bei 50 Hz liegt, d.h. mit einem Additionswert
> von 1 (jeder Tabellenwert nacheinander), ist mit der entsprechenden
> Anzahl an Nachkommastellen für den Zähler und den Additionswert jede
> Frequenz <50 Hz realisierbar. Der Tabellenwert wird dann mehrmals
> Nacheinander zum Ausgabewert, bis die Nachkommastellen einen Überlauf
> haben.
> Genau das ist das Grunkonzept der DDS!

Wenn der Software Interrupt mit 50Hz kommt und den Phasenakkumulator 
hochzählt, wie soll das dann gehen?
Software PLL? ;)
Und da sagte ich ja schon, dass genau das der Grund ist, warum AD9833 
und Co. einen sehr hohen Takt bekommen.

Übrigens darfst du auch locker bleiben, immerhin ist das ein Forum, wo 
jeder schreiben darf, wie er möchte.

von hans (Gast)


Lesenswert?

Ich habe deine Zeile mal angepasst.
1
FCV = (uint32_t) ((50.5*65536) * 16777216/ F_CPU)

Das Teilen durch die 65536 habe ich durch kürzen gemacht.

Du kannst auch mit einem Zähler arbeiten. Dann den Zähler nach der
Ausgabeberechnung für Signal 1 um den Phasenversatz erhöhen (Zähler
für 1 dabei nicht ändern), d.h. deine 32 Bit Variable + Phasenversatz
in 32 Bit berechnen und dann die vordersten 11 Bit für die
Tabellenadresse verwenden => Signal 2.

gruß hans

von Ernst (Gast)


Lesenswert?

Hallo,

wie kommst du von meiner Formel für 50,05HZ auf
1
FCV = (uint32_t) ((50.5*65536) * 16777216/ F_CPU)

?

Ziel ist es, eine Funktion zu schreiben die mir FCV ausrechnet.
Ich möchte der Funktion nur ein integer übergeben und weitestgehend die 
Finger von Fließkomma lassen.

Deswegen hatte ich da an frequenz multipliziert mit 1000 gedacht, also 
50,05Hz x 1000 = 50050.

Passt meine Formel von oben da nicht?

Bevor ich hier weiter mache. Kann mir jemand erläutern wie ich die 
kleinst möglich Schrittgröße ermitteln kann?

Gruß
Ernst

von hans (Gast)


Lesenswert?

@Simon
Die Tabellenadresse kann bei niedrigen Frequenzen in mehreren
Interruptaufrufen gleich bleiben, nur die Nachkommastellen
des Zählers ändern sich.

Da du bei deiner Aussage bleibst, bleibe ich auch bei meiner.

Es werden hier genug Frager mit Halbwissen verstört.


Sch...
es muß 50.05 heisen.

Deine zweite Version mit 1000 stimmt auch.

gruß hans

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.