Forum: Mikrocontroller und Digitale Elektronik Timer flexibel konfigurieren


von Josef (Gast)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

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?

: Bearbeitet durch User
von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

So hab ich das mal gemacht:
1
// Main frequency variable
2
volatile uint16_t Divisor;
3
volatile uint8_t Prescale;
4
// set the generator
5
// toggler on compare match
6
static void init_generator(void)
7
{
8
// TCNT1 = 0;
9
 TCCR1B = (1 << WGM12)| Prescale;
10
 OCR1A = Divisor;
11
 TCCR1A = (0 << COM1A1)|(1 << COM1A0);
12
}
13
/* input a frequency and output divider and prescaler
14
 * 
15
 */
16
void setdivider(uint32_t freq) 
17
{
18
  if (freq == 0) freq = 1; 
19
  if (freq > 168) {
20
    Prescale = (1 << CS10);
21
  Divisor = 1 + (F_CPU / freq);
22
  }
23
  if ((freq < 169) && (freq > 22)) {
24
    Prescale = (1 << CS11);
25
  Divisor = 1 + ((F_CPU/8) / freq);
26
  }
27
  if (freq < 23) {
28
    Prescale = (1 << CS12);
29
  Divisor = 1 + ((F_CPU/256) / freq);
30
  }
31
  init_generator();  // Timer Register schreiben
32
}
Das ist für einen Mega8515 mit 16 bit Timer, sollte aber anpassbar sein.

: Bearbeitet durch User
von Josef (Gast)


Lesenswert?

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
void timer_set_frequency(uint32_t freq)
2
{
3
  uint16_t prescaler;
4
  uint8_t comp;
5
  uint32_t freal;
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
  else if (prescaler <= 8)
14
    prescaler = 8;
15
  else if (prescaler <= 64)
16
    prescaler = 64;
17
  else if (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?

von Karl H. (kbuchegg)


Angehängte Dateien:

Lesenswert?

Nitpicking

Hier in der Rückrechnung
1
  freal = (F_CPU / comp * prescaler);
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)

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

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.

von Josef (Gast)


Lesenswert?

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!

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

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)

: Bearbeitet durch User
von Karl H. (kbuchegg)


Angehängte Dateien:

Lesenswert?

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.

von Josef (Gast)


Lesenswert?

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

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.