Forum: Compiler & IDEs #define fuer _delay_loop_2


von Tom M. (tomm) Benutzerseite


Lesenswert?

Hallo

Ich steh auf dem Schlauch, oder so... ;)

Wenn ich in meinem Source Code meine Wartefunktion so definiere, stimmt 
die Wartezeit:
1
void delay_1ms( uint8_t ms ) {
2
   while (ms--)
3
      _delay_loop_2( 4000 );    // 4 cycles per iteration
4
}

Für meinen atmega168 mit 16Mhz hab ich überschlagen:
4 Mio Iterationen - 1 s
4 k   Iterationen - 1 ms

Gut. Um ein bisschen portabler zu schreiben, packe ich die Formel in ein 
#define statement und ... meine Funktion tut nicht mehr, die Wartezeit 
wird viel zu lang:
1
// delay loop iterations for 1ms
2
#define ONEMS 1 / 4 * F_CPU / 1000
und
1
void delay_1ms( uint8_t ms ) { 
2
   while (ms--)
3
      _delay_loop_2( (uint16_t) ONEMS );    // 4 cycles per iteration
4
}


Was mach ich falsch?

von Sven P. (Gast)


Lesenswert?

Tom M. schrieb:
> Was mach ich falsch?

Du benutzt nicht die fix-und-fertige Funktion aus der AVR-Libc...

von Rupert (Gast)


Lesenswert?

Ganz davon ab resultiert 1/4 bei Integer-Werten in 0, wodurch ONEMS 0 
wird...

von Tom M. (tomm) Benutzerseite


Lesenswert?

> Du benutzt nicht die fix-und-fertige Funktion aus der AVR-Libc...

Oooch ;) -- ist ja immerhin util/delay_basic.h aus der arv-libc.
F_CPU ist mit 16e6 definiert.

von Sven P. (Gast)


Lesenswert?

Und warum nicht <util/delay.h>?
Ansonsten wird 1/4 tatsächlich 0.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Was spricht dagegen?
1
void delay_1ms( uint8_t ms ) { 
2
   while (ms--)
3
      _delay_ms(1);
4
}

_delay_ms ist weiter nichts als ein Wrapper um _delay_loop_2, der
aus F_CPU und dem angegebenen Wert die für _delay_loop_2 notwendige
Zyklenanzahl berechnet -- also letztlich exakt das, was du willst
(wenn ich dich richtig verstanden habe).

von Tom M. (tomm) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:

> _delay_ms ist weiter nichts als ein Wrapper um _delay_loop_2, der
> aus F_CPU und dem angegebenen Wert die für _delay_loop_2 notwendige
> Zyklenanzahl berechnet -- also letztlich exakt das, was du willst

Das ist richtig. Da _delay_ms aber mit float Zahlen rumhantiert, will 
ich  diesen "teuren" Aufruf/Code wegoptimieren.

Das Resultat meiner Rechnerei im #define sei also Null, versteh ich noch 
nicht, weshalb das so ist.

von Sven P. (Gast)


Lesenswert?

_delay_ms hantiert nicht mit Floats herum und ist auch nicht teuer.

Dein #define ist null, weil 1/4 nunmal null ist.

von Rupert (Gast)


Lesenswert?

Es entscheidet sich erst bei dem Aufruf der Funktion, von welchem Typ 
ONEMS ist. In deinem Fall ist es ein Integer, kann somit nur ganzzahlig 
werden. 1/4 ergibt nach Adam Riese 0.25, somit also 0.

von Sven P. (Gast)


Lesenswert?

Ne, 1/4 ist 0, egal wo es benutzt wird.

von Rupert (Gast)


Lesenswert?

Nicht wenn es ein Float oder Double ist ;)

von Sven P. (Gast)


Lesenswert?

1
#include <stdio.h>
2
3
#define ONEMS \
4
  (1 / 4)
5
6
void print_float(float f) {
7
  printf("== %f\n", f);
8
}
9
10
int main() {
11
  print_float(ONEMS);
12
}

Ausgabe:
1
== 0.000000

von Rupert (Gast)


Lesenswert?

1
#include <stdio.h>
2
3
#define ONEMS 1/4*16000000/1000
4
int main() {
5
    printf("%f\n",(float)ONEMS);
6
    printf("%d\n",(int)ONEMS);
7
}

Ergebnis:
1
4000.000000
2
0

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Tom M. schrieb:

> Das ist richtig. Da _delay_ms aber mit float Zahlen rumhantiert, will
> ich  diesen "teuren" Aufruf/Code wegoptimieren.

"teuer" ist das nur für den Compiler.  Der macht daraus dann den
gewünschten Integerwert.

Die Intention hinter den Gleitkommazahlen war einfach, dass auf
deinem Quarz ja nicht drauf steht: 7372800 Hz, sondern: 7,3728 MHz.
Damit kannst du schreiben:
1
#define F_CPU 7.3728E6

ohne dir erst überlegen zu müssen, wie viele Nullen du jetzt anhängst.
Außerdem kannst du halt auch bei _delay_ms schreiben:
1
_delay_ms(0.8);
2
_delay_ms(2.2);

Das empfinde ich als die durchaus natürliche Denkweise eines
Ingenieurs.  Solche Aufrufe werden oft genug benutzt, um bestimmte
Timing-Werte aus einem Datenblatt zu realisieren, und da stehen sie
auch als Gleitkommazahlen -- weil man das am besten lesen kann.

Die Umrechnung in das, was der Controller kann, sollte man halt dann
dem Computer überlassen können.

von Sven P. (Gast)


Lesenswert?

(a) das hat nix mit dem Funktionsaufruf zu tun, sondern mit dem 
cast-Operator.

(b) dein Define ist falsch, der cast bezieht sich nur auf die 1.

Beispiel:
1
#include <stdio.h>
2
3
#define ONEMS \
4
  (1 / 4)
5
6
void print_float(float f) {
7
  printf("== %f\n", f);
8
}
9
10
int main() {
11
  print_float((int)ONEMS);
12
  print_float((float)ONEMS);
13
}

Ausgabe:
1
== 0.000000
2
== 0.000000

1/4 ist 0, egal wie und wo es verwendet wird.

von Tom M. (tomm) Benutzerseite


Lesenswert?

Hab jetzt auf _delay_ms() umgestellt und siehe da, mein Code a) 
funktioniert b) ist nicht wesentlich grösser. Schön. :)

Ich meine mich gerade zu erinnern, dass man dem Compiler mit einem 
Suffix mitteilen sollte, wenn man mit nicht-integer Zahlen hantiert, 
sowas wie:
1
#define ONEMS 1L / 4 * F_CPU / 1000

1L (long?)... Muss gleich mal K&R rauskramen...

von Sven P. (Gast)


Lesenswert?

Tom M. schrieb:
> Hab jetzt auf _delay_ms() umgestellt und siehe da, mein Code a)
> funktioniert b) ist nicht wesentlich grösser. Schön. :)
Endlich :->

> Ich meine mich gerade zu erinnern, dass man dem Compiler mit einem
> Suffix mitteilen sollte, wenn man mit nicht-integer Zahlen hantiert,
> sowas wie:
Es würde schon helfen, wenn du endlich ein paar verdammte Klammern 
setzen würdest:
1
/* FALSCH!!!!!111einself */
2
#define ONEMS 1L / 4 * F_CPU / 1000
3
4
/* Besser, aber immer noch 0 */
5
#define ONEMS (1L / 4 * F_CPU / 1000)
6
7
/* 'Korrekt': */
8
#define (ONEMS 1.0 / 4.0 * F_CPU / 1000.0)


Probier mal aus:
1
#define X 5 + 9
2
3
printf("== %u\n", 10 * X);
Und? Gibt nicht 14*10=140 aus, gell? Komisch.

von Tom M. (tomm) Benutzerseite


Lesenswert?

> Es würde schon helfen, wenn du endlich ein paar verdammte Klammern
> setzen würdest:

Hast ja recht, die Klammern... :)


> Und? Gibt nicht 14*10=140 aus, gell? Komisch.

Nee, da nicht: Der Präprozessor tauscht den Text aus, und dann greift 
das altbekannte "Punkt-vor-Strich". Mein Cast war allerdings total übel, 
oder die Klammern ham gefehlt, oder beides. ;)


Danke euch allen, schönen Abend noch. :)

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.