Forum: Compiler & IDEs Bits im Timer-Register durch Präprozessor / Compiler ausrechnen


von Karsten W. (lsmod)


Lesenswert?

Der folgende Thread steht leider im falschen Forum:
Beitrag "Re: Bits im Timer-Register durch Präprozessor / Compiler ausrechnen lassen?"

Gibt es tatsächlich noch keine fertige Lösung für dieses Problem?

Bislang habe ich nur bei roboternetz.de folgenden Ansatz gefunden:
1
#define F_CPU 1475600 // im makefile definiert
2
#define Systemtakt 50
3
#define VorteilerTimer0 1024
4
#define LadewertTimer0 (256-((F_CPU/VorteilerTimer0)/(Systemtakt*2)))
5
6
#if (LadewertTimer0 == 112)
7
#warning "Alles klar!"
8
#endif
9
10
....
11
12
ISR (TIMER0_OVF_vect)
13
{
14
TCNT0 = LadewertTimer0;

Das ist natürlich sehr unvollständig.
Ich habe auch schon eine Idee wie man dies weiter vervollständigen kann, 
aber ich kann mir nicht vorstellen das hierfür nicht schon eine fertige 
Lösung existiert?

: Bearbeitet durch User
von Uwe S. (de0508)


Lesenswert?

Hallo Karsten,

doch das mache ich schon seit Anfang der "Zeit", ich gebe die F_CPU, die 
Timerfrequenz vor und einen TimerN, als Ergebnis erhalten ich den 
passenden Prescaler und den besten Vorteiler für diesen TimerN.

Das rechnet mir natürlich der Preprozessor aus, der C Compiler hat also 
nur mit Konstanten zu tun und erzeugt somit auch minimalen Code.

Die Formel steht im Datenblatt, in Abhängigkeit vom Timermode.

Alles andere ist Dreisatz.

: Bearbeitet durch User
von Karsten W. (lsmod)


Lesenswert?

Mir ist schon klar das dies möglich ist - es hat bisher nur anscheinend 
noch keiner eine Lösung dafür veröffentlicht!

Ich versuche mich gerade ebenfalls an einer Lösung und finde das dies 
nicht so trivial ist, da ja unterschiedliche Teiler möglich sind.

Für fast jedes Problem das "defined" werden kann wurde dies bereits 
getan.
Nur nicht für das Timer Problem. :-(

von Uwe S. (de0508)


Angehängte Dateien:

Lesenswert?

Hallo Karsten,

dann bin ich mal auf deine Analyse dieses Codes gespannt.

einige wenige Defines und Macros und schon wird es sehr übersichtlich, 
oder ?

Die Macros sind für die Codegenerierung für ein atmega32 ausgelegt.

von Karsten W. (lsmod)


Angehängte Dateien:

Lesenswert?

Ich Danke Dir!

Also gut - dann versuche ich Mal den Code zu erklären.
1
// --------------------------------------------------------
2
// 16-Bit Timer - Timer1
3
// --------------------------------------------------------
4
#define T1_CS12_0(b2,b1,b0)  ( (b2)<<CS12 ^ (b1)<<CS11 ^ (b0)<<CS10 )
5
#define T1_PRESCALE_STOP  T1_CS12_0(0,0,0)
6
#define T1_PRESCALE_1    T1_CS12_0(0,0,1)
7
#define T1_PRESCALE_8    T1_CS12_0(0,1,0)
8
#define T1_PRESCALE_64    T1_CS12_0(0,1,1)
9
#define T1_PRESCALE_256    T1_CS12_0(1,0,0)
10
#define T1_PRESCALE_1024  T1_CS12_0(1,0,1)

Das erste Define scheint ein Makro zu sein welches für die nachfolgenden 
Defines das Register mit dem richtigen Bitmuster lädt.

Danach wird der richtige Vorteiler ermittelt.
1
// prescaler :1
2
#if (F_CPU  / T1_FREQUENCE) < T1_MAX_COUNTER
3
# define T1_PRESCALE    1U
4
# define T1_PRESCALE_VALUE  T1_PRESCALE_1
5
6
// prescaler :8
7
#elif (F_CPU  / (T1_FREQUENCE * 8 )) < T1_MAX_COUNTER
8
...

Der Ladewert für den Counter wird dann hier berechnet
1
#define T1_COUNTER    (uint16_t)(1.0 * F_CPU / T1_PRESCALE / T1_FREQUENCE +0.5)

Dies habe ich zum transparenten ausprobieren in ein Calc (Excel-Tabelle) 
übertragen.

Danach folgen weitere Makros, die die jeweiligen Register mit den 
ermittelten Werten laden.

Und nun schaue ich Mal ob das auch mit den Werten passt ...

: Bearbeitet durch User
von Uwe S. (de0508)


Lesenswert?

Super,

nun kannst Du deine eigene Anpasssungen für andere Timer und auch 
weitere AVR µC nach diesem Schema machen.

viel Erfolg!

von Karsten W. (lsmod)


Angehängte Dateien:

Lesenswert?

Ich befürchte die Berechnungen führen noch nicht so wirklich zum Ziel.

Beispiel:
CPU-Frequenz = 8 MHz
Soll-Frequenz = 1Hz

Wie man sieht ist die maximale Zeit die der Timer erreichen kann 8388 
ms.
Es würde schon ein Vorteiler von 256 reichen, damit erreicht man 2097 
ms.

Deine Teilerermittlung versagt hier aber bereits und gibt einen Error 
aus!

Oder ist T1_FREQUENCE nicht die Soll-Frequenz die mit dem Timer erzielt 
werden soll?

: Bearbeitet durch User
von Uwe S. (de0508)


Lesenswert?

Karsten,

sehr eigenartig, dass mein Code in vielen Programm immer die richtige 
Zeiten generiert.

Du musst meinen Code und die Marcos auf dem Papier durch spielen oder
den Assembleroutput ansehen !
1
#define T1_MAX_COUNTER 65536
2
#define T1_FREQUENCE 1 // Hz
3
4
#if (F_CPU  / (T1_FREQUENCE * 256UL )) < T1_MAX_COUNTER
5
# define T1_PRESCALE    256U
6
# define T1_PRESCALE_VALUE  T1_PRESCALE_256
7
#endif

liefert doch die erste gültige Bedingung,
damit ergeben sich die passenden Werte:
1
#define T1_COUNTER (uint16_t)(1.0 * F_CPU / T1_PRESCALE / T1_FREQUENCE +0.5)

Das Kommt hier wohl raus ?

T1_COUNTER = (uint16_t)(1.0 * F_CPU /T1_PRESCALE /T1_FREQUENCE +0.5)
T1_COUNTER = (uint16_t)(1.0 * 8*10^6Hz /256 /1Hz + 0.5 )
T1_COUNTER = 31250

Zurückgerechnet:
T1_PRESCALE = 256
T1_COUNTER = 31250
Frequenz = (F_CPU /T1_PRESCALE /T1_COUNTER)
Frequenz = (8*10^6Hz /256 /31250)
Frequenz = (31250Hz / 31250) = 1Hz

hehe - alles gut..

Da muss die Betrachtung deines Sheets fehlerhaft sein.

: Bearbeitet durch User
von Uwe S. (de0508)


Angehängte Dateien:

Lesenswert?

Hallo Karsten,

anbei das korrigierte Sheet.

Da muss man schon meine Formeln genau so eintragen, wie ich sie 
angegeben habe.

Wenn man das nicht macht, ist das Ergebnis nicht zu gebrauchen.

: Bearbeitet durch User
von Karsten W. (lsmod)


Angehängte Dateien:

Lesenswert?

Oh ja - natürlich hast Du Recht!

Ich habe (in der Eile) in der Excel-Berechnung
F_CPU/(T1_FREQUENCE*B9) < T1_FREQUENCE
statt
F_CPU/(T1_FREQUENCE*B9) < T1_MAX_COUNTER
verglichen!
Das konnte dann natürlich auch nicht funktionieren.

Hier nun das korrigierte Excel-Sheet.

Zum Vergleich habe ich Mal die Rechnung über die Periodendauer gemacht 
(grüner Bereich).
Deine Auswertung ist der gelbe Bereich.

In beiden Fällen kommt man auf einen Vorteiler von 256 und einen Compare 
Match Load von 31250.

Ist ja genial.
Nun muss ich mir noch die Makros im Detail anschauen und evtl. auch noch 
für einen ATMega8 anpassen.
Das dürfte aber sehr überschaubar sein.

Nochmal Danke dafür das Du Deinen Code mir und der Welt zur Verfügung 
gestellt hast.
Ganz so einfach und schnell kommt man als C-Anfänger doch nicht zu so 
einer komfortablen Lösung.

: Bearbeitet durch User
von Uwe S. (de0508)


Lesenswert?

Super, Du hast es verstanden.

Und auch die Abstraktion zum Preloadwert, bei einem Overflow Interrupt, 
geschafft.

Aber immer wenn ein neuer AVR µC verwendet wird, kann man den Timer0 
anstatt im Normal Mode (Overflow) zu betreiben, auch den CTC Mode 
(Compare Match) verwenden.

Dadurch entstehen keine Zeitfehler durch das neu Laden des Timers mit 
einem neuen Zählerwert.

Unter neuer verstehe ich z.B. die Familie atmega48, atmega88, atmega168 
und atmega328p.

: Bearbeitet durch User
von Karsten W. (lsmod)


Angehängte Dateien:

Lesenswert?

Hallo Uwe,

inzwischen habe ich Deine Vorlage ein wenig mit Dokumentation und 
Debug-Ausgaben erweitert.
So sieht man direkt welcher Prescaler eingestellt wird und ob die 
gewünschte Frequenz auch tatsächlich genau erreicht wird.

Nun müsste man die Routine nur noch universeller für die verschiedenen 
Timer ausgestalten ...

In dem Excel-Blatt waren auch noch Fehler, die jetzt hoffentlich alle 
ausgebügelt sind.

: Bearbeitet durch User
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.