Ich hab da ein wohl recht einfaches Problem, aber irgendwie sitz ich
aufn Schlauch...
Im Prinzip möchte ich einen Timer eines ATmegas flexibel umkonfigurieren
können.
Also einfach eine Funktion aufrufen
1
timer_set_frequency(10000);
um den Timer mit einem 10kHz Takt den Interrupt feuern zu lassen.
Nun hat ein Timer bekanntlich 2 Variablen, einmal den Vorteiler und
einmal Das Compareregister.
Sind diese bekannt ist es recht leicht, daraus die aktuelle Freuqenz zu
errechnen.
Wie aber gehe ich vor wenn ich zu einer gegebenen Freuqenz diese beiden
Unbekannten bestimmen will?
Ich denke ich muss erst mal irgendwie einen geeigneten Vorteiler finden,
und zwar am besten so einen dass ich mit diesem Vorteiler die gewünschte
Frequenz gerade noch so erreichen kann.
Die Rechnung ist natürlich auch davon abhängig ob der Timer 8 oder 16
Bit breit ist.
Kann mir da einer nen Denkanstoss geben, wie man diesen Ansatz in einen
Algorithmus umsetzt?
Josef schrieb:> Wie aber gehe ich vor wenn ich zu einer gegebenen Freuqenz diese beiden> Unbekannten bestimmen will?
Wie machst du es denn mit der Hand unter Verwendung von Papier und
Bleistift?
> Ich denke ich muss erst mal irgendwie einen geeigneten Vorteiler finden,> und zwar am besten so einen dass ich mit diesem Vorteiler die gewünschte> Frequenz gerade noch so erreichen kann.
klingt gut. Definiere 'gerade noch so erreichen' noch etwas genauer. Was
heisst das in Zahlenform?
> Kann mir da einer nen Denkanstoss geben, wie man diesen Ansatz in einen> Algorithmus umsetzt?
Wie gesagt: Wie gehst du vor, wenn du dasselbe Problem mit Papier und
Bleistift zu lösen hast? Was sind deine Regeln, die dich zum Ergebnis
führen?
Wenn du dir darüber erst mal klar bist, hast du auch deinen Algorithmus.
Der wird dann noch um Datentypen angereichert (wegen möglicher
Überläufe) und im Prinzip hast du dann schon deine Funktion, die du nur
noch eingeben und testen musst.
Hinweis: Eine mögliche legitime Strategie ist es auch auf dem Papier,
ganz einfach alle möglichen Vorteiler durchzuprobieren. So viele sind es
ja nicht. Woher weisst du dann auf dem Papier, ob ein Vorteiler geeignet
ist oder nicht, bzw. mit welcher Reihenfolge der Vorteiler (von klein
nach gross oder von gross nach klein) ist der erste passende Vorteiler
dann auch der für dich beste?
Sorry, aber Programmentwicklung ist nun mal zu 90% Zusammenhänge zu
erkennen und zu sehen. Wenn man das nicht alleine durch hinsehen
hinkriegt, dann ist es oft eine gute Strategie, sich selbst zu
beobachten, wie man exakt dieses Problem löst. Du kannst nämlich viel
mehr, als dir bewusst ist. Und irgendwann muss man mal anfangen,
Algorithmen zu finden. Also warum nicht hier?
Ok, dann hab ich da mal was probiert.
Funktioniert momentan für 8 Bit Timer.
Mangels Hardware hab ichs nur im Kopf durchprobiert.
Wäre nett wenn jemand drüber schauen kann, ob da noch ein Denkfehler
drin ist...
1
voidtimer_set_frequency(uint32_tfreq)
2
{
3
uint16_tprescaler;
4
uint8_tcomp;
5
uint32_tfreal;
6
7
/* calculate necessary prescaler */
8
prescaler=F_CPU/(freq*256);
9
10
/* get next-best prescaler */
11
if(prescaler<=1)
12
prescaler=1;
13
elseif(prescaler<=8)
14
prescaler=8;
15
elseif(prescaler<=64)
16
prescaler=64;
17
elseif(prescaler<=256)
18
prescaler=256;
19
else
20
prescaler=1024;
21
22
/* calculate compare value */
23
comp=(F_CPU/(freq*prescaler))-1;
24
25
/* calculate real frequency */
26
freal=(F_CPU/comp*prescaler);
27
}
Im ersten Schritt rechne ich aus welchen Prescaler ich brauche um bei
maximaler Ausnutzung des Compare-Registers die Frequenz zu erreichen.
Da hier wohl meist eine Kommazahl rauskommt runde ich auf den nächsten
realen Prescaler auf.
Danach berechne ich das Compare-Register, um mit dem realen Prescaler
die Frequenz zu erreichen.
Zu Infozwecken berechne ich noch die reale Frequenz, die sich aufgrund
von Rundungsfehlern bei der Division ergibt.
Kann man da noch was verbessern, grade im Hinblick auf
Rechenungenauigkeiten?
muss es logischerweise (comp+1) lauten.
Ich hab mir mal deinen Code in ein PC-Programm genommen, 8Mhz als F_CPU
angenommen und dort eine Testroutine drüber gelegt, die alle Frequenzen
von 10Hz bis 100Khz im 10Hz Raster durchprobiert und die Ergebnisse
ausgibt. Es wird auch die Abweichung in tatschlichen Hz sowie die
prozentuale Abweichung ausgegeben.
Die Ergebnisse sehen nicht so schlecht aus. Bei den ganz kleinen
Frequenzen gibt es noch ein Problem. Offenbar geht es sich dort selbst
mit dem größten Vorteiler nicht mehr aus.
Edit:
Ah, ich seh grad beim Durchscrollen der Ergebnisse, dass es da offenbar
noch mehr Problemstellen gibt. Rund um 3600Hz ist auch wieder so eine
Phase, an der ein Vorteiler von 64 anscheinend schon zu gross, ein
Vorteiler von 8 aber zu klein ist. (Disclaimer: ich hab jetzt nichts
davon händisch durchgerechnet oder überprüft)
Bei 15630 ist auch ein seltsamer Sprung drinn.
Da wird vom Vorteiler 8 auf Vorteiler 1 gewechselt, der Comparewert
liegt aber erst bei 63 und könnte eigentlich noch kleiner werden. Dem
sollte man auch mal nachgehen, warum das so ist.
Wow, danke Karl-Heinz!
Der Sprung bei 3480 erscheint mir falsch.
Da müsste der Prescaler 22,x betragen, also auf 64 "gerundet" werden.
Dein Log spuckt aber 8 aus. Sehr seltsam. Evtl ein Problem mit einem
Datentyp. Wobei die Multiplikation in 32 Bit nie überlaufen dürfte :/
Ok, ist wohl noch etwas feintunning notwendig, aber danke dir fürs
überprüfen!
Josef schrieb:> Wow, danke Karl-Heinz!>> Der Sprung bei 3480 erscheint mir falsch.> Da müsste der Prescaler 22,x betragen, also auf 64 "gerundet" werden.> Dein Log spuckt aber 8 aus. Sehr seltsam. Evtl ein Problem mit einem> Datentyp. Wobei die Multiplikation in 32 Bit nie überlaufen dürfte :/>> Ok, ist wohl noch etwas feintunning notwendig, aber danke dir fürs> überprüfen!
Installier dir auf dem PC eine der freien Entwicklungsumgebungen. Wie du
siehst, braucht man sowas immer wieder mal, um sich auf dem PC
'Zuliefer-Programme' zu schreiben. Und sei es nur, dass man eine Routine
überprüfen will.
Auf dem PC hast du den Vorteil einer Konsole, auf die du Text ausgeben
kannst und zur genaueren Analyse ist es auch kein Problem, die Ausgabe
mittels Redirection von der Konsole in ein File zu dirigieren. Das geht
auf dem PC alles um Größenordnungen einfacher als auf einem AVR.
Josef schrieb:> Wow, danke Karl-Heinz!>> Der Sprung bei 3480 erscheint mir falsch.> Da müsste der Prescaler 22,x betragen, also auf 64 "gerundet" werden.
wie kommst du darauf?
3480 * 256 -> 890880
8000000 / 890880 -> 8.979, als integer also 8
(und ich denke, hier sieht man auch schon wo das Problem liegt. 8.9 ist
definitiv größer als 8. Durch die Integer Rechnung wird aber 8 draus.
Die kleiner/gleich sind das Problem. Gleich darf nur dann zum Zug
kommen, wenn die Division mit Rest 0 aufgeht, ein gewünschter Vorteiler
von zb 8 also haarscharf passt)
Karl Heinz schrieb:> (und ich denke, hier sieht man auch schon wo das Problem liegt. 8.9 ist> definitiv größer als 8. Durch die Integer Rechnung wird aber 8 draus.> Die kleiner/gleich sind das Problem. Gleich darf nur dann zum Zug> kommen, wenn die Division mit Rest 0 aufgeht, ein gewünschter Vorteiler> von zb 8 also haarscharf passt)
Ich hab mal in (fast) diesem Sinne die Routine abgewandelt und die
kleiner/gleich durch kleiner ersetzt (und nur noch im 100Hz Raster
getestet). Das sieht schon viel besser aus.
Karl Heinz schrieb:> wie kommst du darauf?>> 3480 * 256 -> 890880>> 8000000 / 890880 -> 8.979, als integer also 8
Da hast du natürlich recht.
Mein Controller läuft mit 20Mhz, ich hab sämtliche Trockenübungen auf
dem Papier mit 20Mhz gemacht.
Das hatte ich wohl noch so drinnen...
Ok aber ich sehe das Problem jetzt. 8.9 ist natürlich 8 als Integer.
Ich schau mir dann mal deine Lösung an ;-)