Forum: Offtopic Abweichung minimieren (mathematisches Problem)


von Bernhard B. (schluchti)


Lesenswert?

Hallo Leute,

ich würde mir gerne eine Funktion für einen Mikrocontroller schreiben, 
der ich eine beliebige Frequenz (zwischen 1Hz und 10kHz) übergebe und 
die mir dann daraus ein PWM Signal mit eben dieser Frequenz generiert. 
Nun stehe ich vor dem Problem, dass die Frequenz möglichst genau (auf 1% 
genau wäre super) sein sollte. Ich glaub es ist am besten, wenn ich das 
mit ein, zwei Beispielen konkretisiere:

Zur Information:
Der Mikrocontroller wird mit einem 8MHz Quarz betrieben. Weiters besitzt 
dieser die Möglichkeit über ein internes Prescale-Register (16bit breit) 
die Frequenz entsprechend vorzuteilen. Der Wert des Auto-Reload 
Registers wird nach folgender Formel berechnet:
CPU_FREQ/PRESCALER/gewünschte_Frequenz

Angenommen ich verwende einen Prescale Wert von 800 und hätte gerne eine 
gewünschte Frequenz von 200Hz.

Dann würde ich rechnen: 8MHz/800/200 = 50
Hier ist noch alles in Butter.

Würde ich den gleichen Prescale Wert verwenden um eine gewünschte 
Frequenz von 7000Hz erzeugen zu wollen, dann würde ich aber folgendes 
bekommen:

8MHz/800/7000 = 1,428
Der Wert ist aber in zweierlei Hinsicht schlecht. Erstens habe ich, wenn 
ich den Wert auf 1 abrunde einen Fehler von gut 40% und zweitens sollte 
der Auto-Reload Wert zumindest zweistellig sein, damit man über ein 
Capture/Compare Register das Pulsbreitenverhältnis (75%, 50%, 25%...etc) 
verändern kann.

Ich hoffe man sieht bereits auf was ich hinaus möchte. Meine Idee wäre 
nun folgende: Ich berechne mir möglichst 'gute' Prescale Werte und lege 
diese in einer Tabelle im Mikrocontroller ab. Wenn ich nun eine Frequenz 
erzeugen möchte, dann durchsuche ich die Tabelle und vergleiche die 
Fehler, die durch die einzelnen Prescale Werte entstehen. Derjenige, der 
am besten abschneidet, den nehm ich dann.
Die Frage ist nur, wie berechne ich mir die 'guten' Prescale-Werte? Zu 
beachten ist, dass das Prescale Register und das Auto-Reload Register 
jeweils nur 16bit breit sind und das der Auto-Reload Wert zumindest 
zweistellig sein sollte (wobei man letzteres ziemlich einfach 
softwaretechnisch lösen könnte). Welche Genauigkeit kann man damit 
erzielen? Ist 1% Genauigkeit aus der Luft gegriffen oder realistisch? 
Hat jemand einen besseren Vorschlag?

von Karl H. (kbuchegg)


Lesenswert?

Weiter rechnen

8MHz/800/7000 = 1,428

-> du wählst einen Prescaler von 1

Wie groß muss dann der Auto-Reload sein
1
8Mhz / 1 / 7000 = 1142
Also Vorteiler: 1, Auto-Reload 1142

Das ergibt dann nach deiner Formel eine Frequenz von
1
8Mhz / 1 / 1142 = 7005 Hz

Aus dem Bauch heraus würde ich schätzen, dass du einen möglichst kleinen 
Prescaler benutzen willst. 1 wird als Wert immer gut sein, es sei denn 
dass dadurch der Reload Wert die zulässige Grenze verlässt, dann musst 
du mit dem Prescaler höher gehen.

Worum gehts denn:
du suchst ein x, sodas

  8Mhz / x  = gesuchte Frequenz

dieses x setzt sich wiederrum zusammen aus

   x = prescaler * reload

da sowohl prescaler als auch reload ganze Zahlen sein müssen, kann dein 
kompletter Teilerfaktor x sowieso auch nur eine ganze Zahl sein.

Bei deinem Beispiel mit den 7Khz bräuchtest du einen kompletten 
Teilerfaktor von   80000000 / 7000 = 1142.85

Die 0.85 wirst du nie erreichen können. Du suchst also 2 Zahlen p und r 
(prescaler und reload), sodass

   p * r = 1142  (oder 1143 wenn du rundest)

Jetzt kannst du zb probieren: prescaler 1, 2, 3, 4, 5
(oder aber einfach mal einen reload Wert in der Mitte des angepeilten 
Bereichs annehmen, daraus den prescaler berechnen und mit dem jetzt 
festgelegten prescaler den reload Wert korrigieren

Du kannst natürlich auch mit "Bruth Force" an die Sache rangehen.
Aus
   p * r = 1142
lässt du deinen Rechner einfach alle Kombinationen durchspielen und 
lässt ihn die Kombination von p und r finden (du weisst ja, dass weder p 
noch r größer als der Zielwert sein können, d.h. du hast eine Obergrenze 
für beide Werte), die am nähesten beim Zielwert liegt. Wenn Computer 
etwas wirklich gut können, dann ist das Rechnen.

von Bernhard B. (schluchti)


Lesenswert?

Hallo Karl Heinz,

ich hab deine Idee mit den möglichst niedrigen Prescale Werten mal 
aufgegriffen und ein paar Testberechnungen durchgeführt. Ich hab das 
Gefühl, dass dadurch die Genauigkeit zunimmt. Ich bin mir noch nicht 
ganz sicher ob das Zufall ist, oder nicht. Das werd ich mir morgen noch 
genauer ansehen.

Karl Heinz Buchegger schrieb:
> Du kannst natürlich auch mit "Bruth Force" an die Sache rangehen.
> Aus
>    p * r = 1142
> lässt du deinen Rechner einfach alle Kombinationen durchspielen und
> lässt ihn die Kombination von p und r finden (du weisst ja, dass weder p
> noch r größer als der Zielwert sein können, d.h. du hast eine Obergrenze
> für beide Werte), die am nähesten beim Zielwert liegt. Wenn Computer
> etwas wirklich gut können, dann ist das Rechnen.

Das hab ich mir auch schon überlegt. Nur die Frage ist: Wie verfahre ich 
dann weiter? Ich hab dann einen optimalen Wert für p und r der für eben 
diese Frequenz optimal ist. Wenn sich die Frequenz ändert, dann geht die 
Rechnerei wieder von Neuem los. Prinzipiell stellt das für den PC 
natürlich kein Problem dar, für den sind solche Berechnungen ja Pipifax. 
Nur wär es halt schön, wenn man ohne diesen Zwischenschritt, dem PC 
Programm/Excel-Sheet..etc auskommen könnte. Also das man einfach eine 
Funktion aufruft, dieser einen Frequenzwert übergibt und die dann die 
alles weitere vornimmt. Also mal wieder eine eierlegende Wollmilchsau, 
ich weiß ;)

von Karl H. (kbuchegg)


Lesenswert?

Bernhard B. schrieb:

> diese Frequenz optimal ist. Wenn sich die Frequenz ändert, dann geht die
> Rechnerei wieder von Neuem los.

So viele sinds ja auch wieder nicht.

  aus

  p * r = Zielwert

folgt

  p = Zielwert / q

du lässt für q eine Schleife von 1 bis Zielwert-1 laufen und siehst nach 
ob die Division einen Rest von 0 ergibt. Theoretisch würde es sogar 
reichen, wenn man für q nur alle Primzahlen im Bereich 1 bis Zielwert 
untersucht. Wenn da nicht die Forderung wäre, dass man (falls es keinen 
Rest 0 gibt), dasjenige q sucht, für das abs(Zielwert - p*q) minimal 
ist. Da bin ich mir jetzt nicht sicher, ob man das dann auch finden 
würde, wenn man sich nur auf Primzahlen beschränkt. Ich denke: ja. Aber 
dann gibt es ja auch noch die Nebenbedingung, dass p kleiner als eine 
obere Schranken sein muss.

Aber eine 'Optimierung' gibt es. Es reicht, aus Symetriegründen, die 
Schleife von 1 bis sqrt(Zielwert-1) laufen zu lassen, denn eine der 
beiden Zahlen muss kleiner/gleich sqrt(Z-1) sein. sqrt kann jetzt eine 
teure Operation sein, eventuell durch (zielwert-1)/2 ersetzen.

Ist nun mal so:
Du hast 1 Gleichung in 2 Unbekannten und zwei Nebenbedingungen. So der 
Mathecrack bin ich nun auch wieder nicht, dass ich wüsste wie man sowas 
löst bzw. ob das überhaupt geschlossen lösbar ist.

von Karl H. (kbuchegg)


Lesenswert?

Bernhard B. schrieb:
> Hallo Karl Heinz,
>
> ich hab deine Idee mit den möglichst niedrigen Prescale Werten mal
> aufgegriffen und ein paar Testberechnungen durchgeführt. Ich hab das
> Gefühl, dass dadurch die Genauigkeit zunimmt.

Logisch.
Wenn der Prescaler 1 ist, dann kannst du deinen Gesamtteiler rein durch 
den Reload Wert in 1-er Schritten variieren.
Ist dein Presscaler 2, dann kannst du den Gesamtteiler auch nur noch in 
2-er Schritten variieren. Erhöhst du den Reload-Wert um 1, dann wird der 
komplette Teilerfaktor um 2 größer.
Machst du einen Prescaler von 3, dann kann dein kompletter Teilerfaktor 
auch nur noch in 3-er Schritten variieren.
etc. etc.

Mit einem Prescaler von 1 hast du daher die Möglichkeit, den kompletten 
Teiler am feinsten einzustellen.

Als Ausgleich dafür kriegst du aber mit einem höheren Prescaler Wert 
auch höhere Gesamtteiler-Werte. Das ist jetzt insofern wichtig, als du 
dir zb ausgerechnet hast, dass du einen Gesamtteiler von 70000 brauchen 
würdest. Mit einem Prescaler von 1 würde das einen Reload Wert von 70000 
ausmachen. Das geht aber nicht, weil Reload nicht größer als 65535 sein 
kann.
Aber mit einem Prescaler von 2 geht es. Denn dann ist der Preload Wert 
35000, und das geht dann wieder mit 16 Bit. Allerdings ist der nächste 
Gesamtteiler nach 70000 dann erst wieder 70002. Ein Teiler von 70001 ist 
so mit diesem Prescaler nicht erreichbar.

von Peter D. (peda)


Lesenswert?

1
Prescaler = (uint16_t)(f_Quarz / PWM_max / f_out + 1.0);

Peter

von Bernhard B. (schluchti)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Logisch.
> Wenn der Prescaler 1 ist, dann kannst du deinen Gesamtteiler rein durch
> den Reload Wert in 1-er Schritten variieren.

Jetzt wo ich (im ausgeschlafenen Zustand) drüber nachdenke ist das 
natürlich logisch.

Durch deinen letzten Beitrag angespornt hab ich mir jetzt folgende 
Vorgehensweise überlegt:
Ich werd ein mir ein C-Programm für den PC schreiben, welches mir für 
jede Frequenz zwischen 1Hz und 10kHz die Abweichung vom Sollwert 
berechnet. Den Prescale-Wert lass ich dabei von 1 bis 50 laufen. Ein 
höherer Prescale-Wert wird denke ich nicht so viel Sinn haben. So kann 
ich mal überprüfen welche Genauigkeiten überhaupt erreicht werden 
können.

Sollte sich die Abweichung in Grenzen halten, dann gehts daran das im 
Mikrocontroller zu realisieren. Wenn ich nun, so wie oben beschrieben, 
den Prescaler von 1 bis 50 laufen lasse, dann würde das 50 Divisionen 
bedeuten. Ich hab ehrlich gesagt nicht wirklich ein Gespür wie lang ein 
32bit-Mikrocontroller, getaktet mit 8MHz ohne FPU für 50 Divisionen 
braucht.
Aber ich denke ich werd das einfach mal ausprobieren.

Danke!

@Peter: Könntest du kurz erklären was PWM_max und f_out ist?
f_out = gewünschte Frequenz?

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.