Forum: Mikrocontroller und Digitale Elektronik AVR: OCRx1 Werte automatisch berechnen


von Mitleser (Gast)


Lesenswert?

Hi

ich wollte gerne die OCR Werte automatisch berechnen lassen, weil ich in 
der Entwicklungsphase mehrere Zeiten ausprobieren möchte. (Und in 
Zukunft generell die Zeiten mit der Formel berechnen)

Kann ich das einfach so machen, oder gibt es bei zu großen Werten einen 
Überlauf bzw. bei vielen "Teil"-aufgaben, abgeschnittene Werte?

Habe mir das so vorgestellt:
1
#define F_CPU       4000000UL
2
#define Prescaler       256
3
4
#define time             50 //(in ms)
5
6
//mit Taschenrechner gerechnet: 50ms bei 4MHz OCR1A = 781
7
8
OCR1A = (F_CPU * 50) / Prescaler / 1000;
9
//bzw.
10
OCR1A = (F_CPU / Prescaler / 1000) * 50;

Geht das so?
weil
1.: 4.000.000 * 50 = 200.000.000 (Überlauf?)
2.: 4.000.000  256  1000 = 15,625  (15 oder 16?)

Wäre cool wenn ihr die Antwort allgemein haltet, vielleicht brauche ich 
irgendwann mal andere Werte. z.B. F_CPU 1MHz, Prescaler 1024, etc.

Schönen Gruß
 fortgeschrittener Anfänger

von Oliver (Gast)


Lesenswert?

Mitleser schrieb:
> 1.: 4.000.000 * 50 = 200.000.000 (Überlauf?)

Warum Überlauf? F_CPU mit 4.000.000 läuft ja auch nicht über. Wenn du 
rausfindest, warum nicht, dann weisst du auch, warum das 50-fache davon 
auch nicht überläuft.

> 2.: 4.000.000  256  1000 = 15,625  (15 oder 16?)

15.
Mit der 2. Formel bekommst du OCR1-Werte in 50-er Schritten. Ist nicht, 
was du willst.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Mitleser schrieb:

> 1.: 4.000.000 * 50 = 200.000.000 (Überlauf?)

Das UL als Postfix bei den 4Mio
#define F_CPU       4000000UL
kennzeichnet die Zahl als unsigned long.

Damit steht da in den Datentypen betrachtet

    unsigned long * int

und diese Berechnung wird im höchsten Datentyp durchgeführt, also 
unsigned long. Das Ergebnis davon ist dann ebenfalls ein unsigned long.

Die Preisfrage lautet daher: wie groß kann eigentlich ein unsigned long 
maximal werden, ehe er überläuft.
Glücklicherweise weißt du aber, dass ein unsigned long auf deinem System 
aus 32 Bits besteht. D.h. du musst dir nur ausrechnen, welches die 
größte Zahl ist, die man unsigned mit 32 Bits darstellen kann.

8 Bit:     (2 hoch 8) - 1)     ->  255
16 Bit:    (2 hoch 16) - 1)    ->  65535
32 Bits:   (2 hoch 32) - 1)    ->   ????????

Und wenn deine Berechnung mit den für dich relevanten Zahlen darunter 
bleibt (Achtung: bei einer komplexeren Formel musst du jede Operation 
für sich betrachten!), dann hast du auch keinen Überlauf.

von Mitleser (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Das UL als Postfix bei den 4Mio
> #define F_CPU       4000000UL
> kennzeichnet die Zahl als unsigned long.
>[...]
> und diese Berechnung wird im höchsten Datentyp durchgeführt, also
> unsigned long. Das Ergebnis davon ist dann ebenfalls ein unsigned long.

Das war der Hintergrund!
Danke Karl Heinz!

Das Ergebnis wird in ein Register geschrieben, welches ja nur 16 Bit 
aufnehmen kann, das die Berechnung sich an dem höchstem Datentyp in der 
Formel richtet, war mir nicht bewusst.
Daher danke für die Erklärung!

Oliver schrieb:
> 15.
> Mit der 2. Formel bekommst du OCR1-Werte in 50-er Schritten.

Auch dafür danke, Oliver.
Es richtet sich also auch nach dem Rechenweg bzw. der Reihenfolge 
innerhalb der Formel.

Fazit:
Wieder etwas dazugelernt!!

Einen schönen Tag noch

von Karl H. (kbuchegg)


Lesenswert?

Mitleser schrieb:

> Das Ergebnis wird in ein Register geschrieben, welches ja nur 16 Bit
> aufnehmen kann, das die Berechnung sich an dem höchstem Datentyp in der
> Formel richtet, war mir nicht bewusst.

Nein. Nicht in der Formel.
In der Operation!


   F_CPU * ( 1000 * 1000 )

Die Berechnungsreihenfolge ist

   1000 * 1000

und erst dann wird mit F_CPU multipliziert!

1000 * 1000, das sind aber beides int. Daher wird diese Berechnung als 
int Multiplikation ausgeführt und liefert ein int Ergebnis. Und erst 
dieses int-Ergebnis wird dann mit einem unsigned long multiplizert und 
daher wird der entstandene int zuerst zu einem unsigned long 
hochgehoben, multipliziert und das Ergebnis ist ein unsigned long.

Aber: die int*int Multiplikation 1000*1000 wird auf einem 16-Bit int 
überlaufen. Und daher ist das komplette Ergebnis unbrauchbar, weil es 
zwischendurch einen Overflow gab, obwohl das Endergebnis ein unsigned 
long ist.

von Falk B. (falk)


Lesenswert?

Festkommaarithmetik

Hier steht einiges dazu, auch wenn das eigentliche Thema ein anderes 
ist.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Falk Brunner schrieb:
> Festkommaarithmetik

Übrigens beherrscht avr-gcc Festkommaarithmetik und Typen wie accum oder 
short fract etc.  Wäre bestimmt ein Update von "Festkommaarithmetik" 
wert!

von Falk B. (falk)


Lesenswert?

@ Johann L. (gjlayde) Benutzerseite

>Übrigens beherrscht avr-gcc Festkommaarithmetik und Typen wie accum oder
>short fract etc.

Was bedeutet das praktisch? Welche Vorteile hat das?

>  Wäre bestimmt ein Update von "Festkommaarithmetik" wert!

Na dann mal los!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Falk Brunner schrieb:

> Welche Vorteile hat das?

Man muss nicht immer händisch die Ergebnisse skalieren.  Und es gibt 
optimierte ALgorithmen für (saturierte) Operationen.

Insgesamt gibt es 16 Typen

http://gcc.gnu.org/wiki/avr-gcc#Fixed-Point_Support

von Mitleser (Gast)


Lesenswert?

Wenn ich das alles richtig verstanden habe, sollte folgende Berechnung 
richtig sein:
1
#define F_CPU       4000000UL
2
#define ZEIT_IN_MS  50
3
#define PRESCALER   256
4
5
OCR1A = (uint16_t)( (F_CPU*ZEIT_IN_MS)/(uint32_t)(PRESCALER*1000) )

von Falk B. (falk)


Lesenswert?

@ Mitleser (Gast)

>Wenn ich das alles richtig verstanden habe, sollte folgende Berechnung
>richtig sein:

>#define F_CPU       4000000UL
>#define ZEIT_IN_MS  50
>#define PRESCALER   256

>OCR1A = (uint16_t)( (F_CPU*ZEIT_IN_MS)/(uint32_t)(PRESCALER*1000) )
Für diesen Fall ist das OK, allgemein würde ich liber überall UL hinter 
die Konstanten schreiben, damit ist es sicher und man muss nicht über 
cast nachdenken.

>#define F_CPU       4000000UL
>#define ZEIT_IN_MS  50UL
>#define PRESCALER   256UL

>OCR1A = (uint16_t)( (F_CPU*ZEIT_IN_MS)/(PRESCALER*1000) )

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Fehlen da nicht nich ein L und eine -1?
1
OCR1A = (uint16_t) ((F_CPU*ZEIT_IN_MS)/(PRESCALER*1000L) -1)

von Karl H. (kbuchegg)


Lesenswert?

Mitleser schrieb:
> Wenn ich das alles richtig verstanden habe

anscheinend aber nicht

> #define F_CPU       4000000UL
> #define ZEIT_IN_MS  50
> #define PRESCALER   256
>
> OCR1A = (uint16_t)( (F_CPU*ZEIT_IN_MS)/(uint32_t)(PRESCALER*1000) )


PRESCALER wird durch 256 ersetzt. Die 1000 stehen schon da.
Also steht da nach der Textersetzung (256*1000)

256 ist ein int  (16 Bit)
1000 ist ein int   (16 Bit)

Also wird dies Multiplikation als int Multiplikation durchgeführt. Und 
patsch: das abseits vom µC gefundene Ergebnis 256000 ist zu groß um als 
int-Ergebnis noch korrekt zu sein.

Und nein. Der Cast nach uint32_t hilft dir nichts. Denn hier wird zuerst 
multipliziert und dann gecastet.

von Ralf G. (ralg)


Lesenswert?

Mitleser schrieb:
> OCR1A = (uint16_t)( (F_CPU*ZEIT_IN_MS)/(uint32_t)(PRESCALER*1000) )

Löse einfach mal die Klammern auf und sortiere die Variablen und 
Konstanten so, dass du sicher im Wertebereich von 32Bit bleibst und 
die Genauigkeit nicht darunter leidet. Dann klappt's auch ohne 
Kommazahlen!

von Uwe (de0508)


Lesenswert?

Hallo,

es gibt noch ein weitere Möglichkeit, wir lassen mit "Kommazahlen" 
rechnen:
1
#define F_CPU       4000000UL
2
#define FREQUENZ    20 // 50ms
3
#define PRESCALER   256
4
5
OCR1A = (uint16_t)(1.0 * F_CPU / PRESCALER / FREQUENZ +.5) -1;

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Falk Brunner schrieb:
> @ Johann L. (gjlayde) Benutzerseite
>
>>  Wäre bestimmt ein Update von "Festkommaarithmetik" wert!
>
> Na dann mal los!

Selbst hab ich anwendungsseitig nicht viel mit Festkommaarithmetik zu 
tun; ich hab lediglich die Arithmetik von Sean in den Compiler eingebaut 
und ein paar Optimierungen vorgenommen.

Seans Arithmetik war bereits in einigen Atmel-AVR-Toolchains enthalten, 
IIRC in der 4.6.2.  Der Support in 4.8+ unterscheidet sich allerdings in 
der Darstellung und der Position des Dezimalpunkts bei den Fixed-Werten 
mit Ganzzahl-Anteil.

Für die Ergänzung im Wiki wär's m.E. sinnvoll, wenn das jemand machen 
würde der Festkommaarithmetik tatsächlich in freier Wildbahn einsetzt.

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.