Forum: Mikrocontroller und Digitale Elektronik #define Frage


von Brocken Sei (Gast)


Lesenswert?

1
#define ultraschall_StarteMessungSRF04(PORT, TriggerPulse_PIN) {PORT |= (1<<TriggerPulse_PIN); _delay_us(12); PORT &=~ (1<<TriggerPulse_PIN);}
Wenn ich die {} weglasse dann sagt der Compiler nichts. Jedoch wird in 
einem CBuch geschrieben, dass die {} notwendig sind bei mehreren 
Anweisungen.
Ich kanns leider nicht ausprobieren, da ich nicht zuhause bin.

Gruß Bro

von Karl H. (kbuchegg)


Lesenswert?

Frag dich einfach mal, was wohl rauskommt, wenn du
1
  if( irgendeine_Bedingung )
2
     ultraschall_StarteMessungSRF04;

programmierst und die Klammern weglässt.


Eines der größten Misverständnisse in C ist wohl, dass dem Präprozessor 
zuviel Intelligenz zugeschrieben wird.

Der macht Textersetzung! Sonst nichts! Den interessiert keine C-Syntax.
1
#define A B
Text A wird an allen vorkommenden Stellen durch Text B ersetzt. Und erst 
dann gehts ab durch den Compiler. (OK. Makro Argumentsubstitution 
erfolgt noch. Aber dann ist schon Ende der Fahnenstange)

Sieht das Programm so aus
1
#definer TEST   i = 5; j = 8
2
3
int main()
4
{
5
  int k = 2;
6
  int j = 0;
7
  int i = 0;
8
9
  if( k == 2 )
10
    TEST;
11
}

dann bleibt nach der Textersetzung übrig
1
int main()
2
{
3
  int k = 2;
4
  int j = 0;
5
  int i = 0;
6
7
  if( k == 2 )
8
    i = 5; j = 8;
9
}

Und wenn man das jetzt nach den C-Regeln umbricht und richtig einrückt, 
dann steht da
1
int main()
2
{
3
  int k = 2;
4
  int j = 0;
5
  int i = 0;
6
7
  if( k == 2 )
8
    i = 5;
9
10
  j = 8;
11
}

und das dürfte dann nicht wirklich die ursprüngliche Absicht gewesen 
sein. Die ursprüngliche Absicht war es wohl, dass auch die Zuweisung an 
j von der if-Bedingung abhängt.

von Floh (Gast)


Lesenswert?

pfui. Aus.
Für sowas gibts Funktionen.
:-)

von (prx) A. K. (prx)


Lesenswert?

Perfektionisten reichen die Klammern noch nicht, weil man sich sonst bei
  if (was)
    ultraschall_StarteMessungSRF04(x,y);
  else
    abort();
über die Syntaxfehlermeldung des Compilers wundert. Die schreiben dann
  #define macro(x) do{ ... }while(0)
weil nur so das Semikolon dahinter nicht stört.

von Brocken Sei (Gast)


Lesenswert?

Floh schrieb:
> pfui. Aus.
> Für sowas gibts Funktionen.

Aber dauert es nicht einbisschen länger wenn ich für diese Aufgabe ne 
Funktion schreibe?
Das sind genau 3 Befehle, mit 3 Parameter.

Also
statt eine Definitionszeile

sowas:
1
void(uint8_t PORT, uint8_t TriggerPulse_PIN, uint8_t delayTime_tillSTART_us)
2
{
3
.
4
.
5
.
6
}

Außerdem kriege ich dann probleme wenn ich einen String PORTA als 
Argument bekomme, dann muss ich wieder vergleichen, oder ich arbeite nur 
mit Zahlen was noch unübersichtlicher ist.
Also ich finde Textersetzung ist in diesem Fall definitiv besser, meine 
Meinung halt.

Gruß Bro

von Brocken Sei (Gast)


Lesenswert?

A. K. schrieb:
> Perfektionisten reichen die Klammern noch nicht, weil man sich sonst bei
>   if (was)
>     ultraschall_StarteMessungSRF04(x,y);
>   else
>     abort();
> über die Syntaxfehlermeldung des Compilers wundert. Die schreiben dann
>   #define macro(x) do{ ... }while(0)
> weil nur so das Semikolon dahinter nicht stört.


versteh ich nicht^^

von Brocken Sei (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> und das dürfte dann nicht wirklich die ursprüngliche Absicht gewesen
> sein. Die ursprüngliche Absicht war es wohl, dass auch die Zuweisung an
> j von der if-Bedingung abhängt.

Weiß was du meinst. Danke

Gruß Bro

von Karl H. (kbuchegg)


Lesenswert?

Brocken Sei schrieb:
> Floh schrieb:
>> pfui. Aus.
>> Für sowas gibts Funktionen.
>
> Aber dauert es nicht einbisschen länger wenn ich für diese Aufgabe ne
> Funktion schreibe?
> Das sind genau 3 Befehle, mit 3 Parameter.

Was ist dir lieber:
20 Sekunden mehr aufwand beim Tippen oder 3 Stunden Fehlersuche?

> Außerdem kriege ich dann probleme wenn ich einen String PORTA

du kriegst sinnvollerweise keinen String.

Aber im Prinzip hast du dann schon recht:
So ist nun mal das Leben. Man kann nicht alles haben.
Manchmal muss man Kompromisse eingehen.

> Also ich finde Textersetzung ist in diesem Fall definitiv besser, meine
> Meinung halt.

Kommt immer drauf an.

1
#define TWICE(x)    2*x

Gut oder schlecht?

Gut: man spart ein wenig Arbeit

Schlecht: Das Ergebnis von TWICE(2+1) ist nicht identisch zum Ergebnis 
von TWICE(3) und mag einen überraschen.

-> Das Fehlerpotential mit Makros ist viel höher. Wenn man das 
weitgehend ausschliessen kann und sich sicher ist, was man tut: Es 
spricht nichts gegen Makros.
Aber eben nicht in allen Fällen. Sag dann nicht, wir hätten dich nicht 
gewarnt.

von Karl H. (kbuchegg)


Lesenswert?

Brocken Sei schrieb:
> A. K. schrieb:
>> Perfektionisten reichen die Klammern noch nicht, weil man sich sonst bei
>>   if (was)
>>     ultraschall_StarteMessungSRF04(x,y);
>>   else
>>     abort();
>> über die Syntaxfehlermeldung des Compilers wundert. Die schreiben dann
>>   #define macro(x) do{ ... }while(0)
>> weil nur so das Semikolon dahinter nicht stört.
>
>
> versteh ich nicht^^

Und wieder:
mach die Textsubstitution.

Makros und ihre potentiellen Fallen kann man nur auf die Art verstehen:
Man spielt selbst Präprozessor und ersetzt probehalber das Makro mit dem 
zu ersetzenden Text und sieht nach, was da rauskommt.

von Floh (Gast)


Lesenswert?

Brocken Sei schrieb:
> Aber dauert es nicht einbisschen länger wenn ich für diese Aufgabe ne
> Funktion schreibe?
> Das sind genau 3 Befehle, mit 3 Parameter.

Dafür kann man das Stichwort inline verwenden, um dem Compiler zu sagen, 
dass er die Funktion auch auflösen darf.

Ich sehe defines deshalb so kritisch, da
- keine Überprüfung der Typen, gibt sehr kriptische Fehlermeldungen
- mögliche Probleme mit anderen defines

inline void ultraschall_StarteMessungSRF04(uint8_t* trigport, uint8_t 
trigpin)
{
  *trigport |= (1<<trigpin);
  _delay_us(12);
  *trigport &=~ (1<<trigpin);
}

Aufgerufen wirds dann so:
ultraschall_StarteMessungSRF04(& PORTA, 4);

:-) Hoffe ich hab keinen Wurm drinnen.

von (prx) A. K. (prx)


Lesenswert?

Brocken Sei schrieb:

> versteh ich nicht^^

Was geht:
  if (cond) statement; else ...
  if (cond) {...} else ...
Was nicht geht:
  if (cond) {...}; else ...
Aber was wieder geht:
  if (cond) do{...}while(0); else ...

von Ne Möglichkeit (Gast)


Lesenswert?

Es gäbe sonnst noch inline-Funktionen, bei denen auch eine art 
Textersetzung durchgeführt wird. Sollten seit dem C99-Standard auch in C 
verfügbar sein.

von Karl H. (kbuchegg)


Lesenswert?

Floh schrieb:

> inline void ultraschall_StarteMessungSRF04(uint8_t* trigport, uint8_t
> trigpin)
> {
>   *trigport |= (1<<trigpin);
>   _delay_us(12);
>   *trigport &=~ (1<<trigpin);
> }
>
> Aufgerufen wirds dann so:
> ultraschall_StarteMessungSRF04(& PORTA, 4);
>
> :-) Hoffe ich hab keinen Wurm drinnen.

Nur eine klitzekleine Warnung. Ist aber nicht weiter schlimm. Du hast 
einen 'Modifier' vergessen :-)
1
inline void ultraschall_StarteMessungSRF04( volatile uint8_t* trigport, uint8_t trigpin)
2
{
3
  *trigport |= (1<<trigpin);
4
  _delay_us(12);
5
  *trigport &=~ (1<<trigpin);
6
}

von (prx) A. K. (prx)


Lesenswert?

Wundert sich noch jemand, weshalb es Leute wie Stroustrup (C++ Schöpfer) 
gibt, die den Präprozessor zum Teufel wünschen?

von Karl H. (kbuchegg)


Lesenswert?

Nicht wirklich.

Da muss jeder C Jünger durch.
Entdeckt man Makros, sind sie plötzlich Allheilmittel für alles. Nach 
dem 3ten Fehler, den man sich durch die Verwendung von Makros 
leichtsinnig eingehandelt hat und den man stundenlang gesucht hat, kehrt 
dann meist Ernüchterung ein.

von Floh (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Da muss jeder C Jünger durch.
> Entdeckt man Makros, sind sie plötzlich Allheilmittel für alles. Nach
> dem 3ten Fehler, den man sich durch die Verwendung von Makros
> leichtsinnig eingehandelt hat und den man stundenlang gesucht hat, kehrt
> dann meist Ernüchterung ein.

Naja da hab ich schlechte Erfahrungen mit lernresistenten Kindern. 
Teilweise komplette Programmteile bis ins unleserliche in defines 
gesteckt und trotzdem ist die Software natürlich perfekt und die 
Hardware (Roboter) schuld daran, dass nix funktioniert.
-.-

von Brocken Sei (Gast)


Lesenswert?

Ok,

Karl heinz Buchegger schrieb:
> inline void ultraschall_StarteMessungSRF04( volatile uint8_t* trigport, uint8_t 
trigpin)
> {
>   *trigport |= (1<<trigpin);
>   _delay_us(12);
>   *trigport &=~ (1<<trigpin);
> }

Aber wie soll denn das funktionieren?
Wenn also trigport A gewählt ist, dann ist PORTA, odr nur A das ein 
String und kein uint8_t, dh im Endeffekt steht dann
zahl |= (1<<zahl)
die linke Zahl wird nie einen Port darstellen.

Gruß bro

von Karl H. (kbuchegg)


Lesenswert?

Brocken Sei schrieb:

> Aber wie soll denn das funktionieren?

Indem man der Funktion die Adresse des Ports gibt, an der die Ausgabe 
erfolgen soll. Steht doch dort

> Wenn also trigport A gewählt ist, dann ist PORTA, odr nur A das ein
> String

Da ist gar nichts ein String.

> und kein uint8_t,

Es wird auch kein uint8_t übergeben sondern ein uint8_t*. Genau genommen 
sogar ein volatile uint8_t*

> dh im Endeffekt steht dann

Nö.
Das steht nicht da.

PORTA ist keine Zahl (ist es schon, aber anders als du jetzt denkst). 
Für dich als C-Programmierer ist PORTA einfach nur eine uint8_t 
Variable, die irgendwie magisch mit dem physikalischen Port verknüpft 
ist. Wie diese Verknüpfung genau realisiert ist, ist erst mal 
uninteressant. Für alle praktischen Zwecke kann man sich PORTA einfach 
nur als Variable, so wie i, j, k, HamstiBamsti, AnzahlTicks, ... 
vorstellen. Nur das diese Variable vordefiniert ist und einfach so 
existiert (wenn man avr/io.h inkludiert)

Und: So wie jede andere Variable, hat auch PORTA eine Adresse im 
Speicher, die man ermitteln kann und die man an Funktionen übergeben 
kann.

von Floh (Gast)


Lesenswert?

Brocken Sei schrieb:
> Aber wie soll denn das funktionieren?
> Wenn also trigport A gewählt ist, dann ist PORTA, odr nur A das ein
> String und kein uint8_t, dh im Endeffekt steht dann
> zahl |= (1<<zahl)
> die linke Zahl wird nie einen Port darstellen.

PORTA ist auch nur ein define und steht für eine Speicherstelle. Daher 
funktioniert das.
:-)

von Brocken Sei (Gast)


Lesenswert?

@Karl && Floh:

Verstehe jetzt, danke euch. Mich hat nur das &PORTA irritiert, jetzt 
weiß ich was damit gemeint ist.

Theoretisch könnte ich aber auch die Zeiger weglassen und nur mit die 
Variablen arbeiten, so:
1
//Starte Ultraschall
2
extern inline void ultraschall_StarteMessungSRF05(uint8_t PORT, uint8_t TriggerPulse_PIN, uint8_t delayTime_tillSTART_us)
3
{
4
  PORT |= (1<<TriggerPulse_PIN);
5
  _delay_us(delayTime_tillSTART_us);
6
  PORT &=~ (1<<TriggerPulse_PIN);
7
}

Is jetzt halt die Frage ob ich mit Adressen oder Variablen arbeite, 
natürlich ist die Variante mit Adressen besser, ich werds auch so 
machen.

Gruß Bro

von Klaus W. (mfgkw)


Lesenswert?

SO geht es ja gar nicht.
Da wird beim Aufruf eine Kopie des aktuellen Werts von PORTA
oder was auch imemr übergeben, und diese Kopie wird in der Funktion 
verändert.
Davon merkt der Port aber nichts.
So etwas geht erst in C++ mit Referenzen.

von ... (Gast)


Lesenswert?

Nein so gehts nicht!
Du solltest definitiv ein gutes C-Buch lesen!
Karl Heinz hat da nicht umsonst ein "volatile uint8_t*" drin stehen, der 
weis nämlich was er tut.

von Brocken Sei (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> Da wird beim Aufruf eine Kopie des aktuellen Werts von PORTA
> oder was auch imemr übergeben, und diese Kopie wird in der Funktion
> verändert.

Stimmt da hast du wohl recht, deswegen auch mit Adressen.

OK, danke

Gruß Bro

... schrieb:
> Karl Heinz hat da nicht umsonst ein "volatile uint8_t*" drin stehen, der
> weis nämlich was er tut.

Ist das nicht eher Frage der Zeiger, denn indem man *PORT schreibt wird 
sofort PORTA eingesetzt da ja die Adresse von PORTA übertragen wurde.

Gruß Bro

von Klaus W. (mfgkw)


Lesenswert?

Ich habe schon ein paar C-Bücher gelesen, ehrlich!

von Klaus W. (mfgkw)


Lesenswert?

Brocken Sei schrieb:
> ... schrieb:
>> Karl Heinz hat da nicht umsonst ein "volatile uint8_t*" drin stehen, der
>> weis nämlich was er tut.
>
> Ist das nicht eher Frage der Zeiger, denn indem man *PORT schreibt wird
> sofort PORTA eingesetzt da ja die Adresse von PORTA übertragen wurde.

ja und?
Deswegen muß trotzdem *PORT als volatile deklariert werden, weil
es sich ja dauernd ändern kann und der Compiler sonst nix davon weiß.

von Brocken Sei (Gast)


Lesenswert?

Dann müsste es so funktionieren:
1
extern inline void ultraschall_StarteMessungSRF05(uint8_t *PORT, uint8_t TriggerPulse_PIN, uint8_t delayTime_tillSTART_us)
2
{
3
  *PORT |= (1<<TriggerPulse_PIN);
4
  _delay_us(delayTime_tillSTART_us);
5
  *PORT &=~ (1<<TriggerPulse_PIN);
6
}
7
8
int main()
9
{
10
    ultraschal...(&PORTA, 7, 15);
11
}

Ich sehe nicht was das volatile hier zu suchen hat, denn dieses wird nur 
benötigt wenn der Wert der Variable immer bei jeder Verwendung der 
Variable neu aus dem Hauptspeicher geladen wird.

Gruß Bro

von Klaus W. (mfgkw)


Lesenswert?

Es steht dir frei, das volatile wegzulassen, wenn du dir sicher bist.

Kann gut sein, daß es ohne klappt.
Jeder wie er will.

von Karl H. (kbuchegg)


Lesenswert?

Brocken Sei schrieb:

> Ist das nicht eher Frage der Zeiger, denn indem man *PORT schreibt wird
> sofort PORTA eingesetzt

Du tust dir nichts gutes, wenn du hier 'ersetzen' denkst.

In der Funktion hast du eine Adresse im Speicher an der der Wert zu 
finden ist. Ob das jetzt tatsächlich ein Port ist, oder ob das eine 
normale Variable ist, spielt keine Rolle. In der Funktion existiert nur 
die Adresse im Speicher.

Denk in erster Linie nicht daran, was dir der Compiler hier alles 
optimieren kann, sondern denk in C Einheiten. Alles andere 
weiterführende ist Sache des Compilers.

von Brocken Sei (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> weil
> es sich ja dauernd ändern kann und der Compiler sonst nix davon weiß.

Hmm, was kann sich dauernd ändern?
Ich rufe die Funktion immer neu auf und gebe ihm immer die selben 
Parameter.
Was meinst du jetz?

Gruß Bro

von Brocken Sei (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> In der Funktion hast du eine Adresse im Speicher an der der Wert zu
> finden ist. Ob das jetzt tatsächlich ein Port ist, oder ob das eine
> normale Variable ist, spielt keine Rolle. In der Funktion existiert nur
> die Adresse im Speicher.

Stimmmmmtt, genau, und da kann es passieren, dass sich die Adresse 
ändern, is mir grad eingefallen, und deswegen immer neu laden.

Danke Karl.

@Klaus:
Hast recht kappt, wusste nur nicht dass du mit ändern die Adresse meinst

Gruß Bro

von Karl H. (kbuchegg)


Lesenswert?

Brocken Sei schrieb:

> Ich sehe nicht was das volatile hier zu suchen hat,

Weil du den Compiler an der Optimierung hindern musst.
1
extern inline void ultraschall_StarteMessungSRF05(uint8_t *PORT, uint8_t TriggerPulse_PIN, uint8_t delayTime_tillSTART_us)
2
{
3
  *PORT |= (1<<TriggerPulse_PIN);
4
  _delay_us(delayTime_tillSTART_us);
5
  *PORT &=~ (1<<TriggerPulse_PIN);
6
}


Der Tragödie erster Teil
1
  *PORT |= (1<<TriggerPulse_PIN);

(übrigens solltest du dir angewöhnen Namen die ausschliesslich in 
Grossbuchstaben geschrieben sind, für Makros zu reservieren. Und auch 
umgekehrt: Was kein Makro ist, wird auch nicht komplett gross 
geschrieben. Das hier ist eine normale Pointervariable. Also wird sie 
nicht komplett gross geschrieben)

hier wird also *PORT verändert. Soweit so gut
1
  _delay_us(delayTime_tillSTART_us);
der Compiler findet hier (durch inlining der _delay_us Funktion) raus, 
dass dieses *PORT nicht vreändert.
1
  *PORT &=~ (1<<TriggerPulse_PIN);
und dann wird *PORT noch einmal verändert.

Wenn der COmpiler jetzt clever ist, dann findet er raus, dass beide 
Veränderungen von *PORT jedesmal dasselbe Bit betreffen.

Wozu dann die erste Veränderung, wenn die sowieso dann noch einmal 
geändert wird?

Eben. Braucht es nicht. Die erste Manipulation fliegt raus!

von Karl H. (kbuchegg)


Lesenswert?

Brocken Sei schrieb:
> Karl heinz Buchegger schrieb:
>> In der Funktion hast du eine Adresse im Speicher an der der Wert zu
>> finden ist. Ob das jetzt tatsächlich ein Port ist, oder ob das eine
>> normale Variable ist, spielt keine Rolle. In der Funktion existiert nur
>> die Adresse im Speicher.
>
> Stimmmmmtt, genau, und da kann es passieren, dass sich die Adresse
> ändern,

Nein.
Die Adresse kann sich innerhalb der Funktion nicht ändern. Aber der 
Wert, der sich an dieser Adresse im Speicher befindet.

  volatile uint8_t *

ist etwas anderes als

  uint8_t * volatile

von Brocken Sei (Gast)


Lesenswert?

Brocken Sei schrieb:
> Stimmmmmtt, genau, und da kann es passieren, dass sich die Adresse
> ändern, is mir grad eingefallen, und deswegen immer neu laden.
>
> Danke Karl.

Blödsinn!

Moment zum nachdenken bitte.

Gruß Bro

von Klaus W. (mfgkw)


Lesenswert?

Brocken Sei schrieb:
> Moment zum nachdenken bitte.

Denken schadet nicht.
Du wirst aber nicht für den Rest deines Lebens ergründen können, was ein 
Compiler beim Übersetzen aus deinem Programm macht.

Deshalb einfach der gute Rat:
Wenn eine Variable nicht alleine unter der Obhut des Compilers ist
und/oder mehrere Threads/Interruptroutinen/... auf eine Variable
zugreifen, dann als volatile deklarieren.

Denken hin oder her, es geht sonst irgendwann schief.

von ... (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> Ich habe schon ein paar C-Bücher gelesen, ehrlich!

Das glaub ich Dir aufs Wort und es lag mir fern das zu bezweifeln. Ich 
war einfach nur zu langsam beim Tippen.
Mein Post bezog sich eigentlich auf den Post über Deinen:
Beitrag "Re: #define Frage"

von Brocken Sei (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> der Compiler findet hier (durch inlining der _delay_us Funktion) raus,
> dass dieses *PORT nicht vreändert.

ok, und?

Karl heinz Buchegger schrieb:
> und dann wird *PORT noch einmal verändert.

Ja, und das passt dann auch, denn *PORT = PORTA in dem Fall, die Adresse 
bleibt nämlich gleich

Karl heinz Buchegger schrieb:
> Wenn der COmpiler jetzt clever ist, dann findet er raus, dass beide
> Veränderungen von *PORT jedesmal dasselbe Bit betreffen.

Ja wieso denn?
Steht das nicht schon fest?

Adresse von PORTA ändert sich nicht.
Mit *PORT (PORT = Adresse(PORTA)) spreche ich die Wertigkeit der 
"Variable" PORTA an, diese wird dann verändert.
Wieso hat das jetzt was mit den Bits zu tun?

Karl heinz Buchegger schrieb:
> Aber der
> Wert, der sich an dieser Adresse im Speicher befindet.

Der ändert sich hier 2 mal, zwar genau das selbe Bit aber was solls?

Karl heinz Buchegger schrieb:
> volatile uint8_t *
>
> ist etwas anderes als
>
>   uint8_t * volatile

Gehört das dazu oder ist das eine Bemerkung nebenbei?
Ich glaube mal du meinst damit, dass wenn mehrere Variablen definiert 
werden beim ersten alle als volatile definiert werden und beim 2ten nur 
das erste?

Gruß Bro

von Karl H. (kbuchegg)


Lesenswert?

Brocken Sei schrieb:

>> und dann wird *PORT noch einmal verändert.
>
> Ja, und das passt dann auch, denn *PORT = PORTA in dem Fall, die Adresse
> bleibt nämlich gleich

PORTA ist in dieser Funktion schon uninteressant.
Interessiert keinen mehr.

Die Funktion hat den Auftrag den Wert an Port (welcher *Port ist) zu 
verändern. Nicht mehr und nicht weniger.

Für die Funktion macht es keinen Unterschied ob du schreibst

    i = 5
    _delay_us( 200 );
    i = 8;

die zweite Zuweisung an i überschreibt die erste. Und da _delay_us das i 
nicht verändert und auch nicht darauf angewiesen ist, das i einen 
speziellen Wert hat (weil sie i gar nicht benutzt), gibt es keinen Grund 
die erste Zuweisung überhaupt auszuführen.

Es sei denn, man macht i volatile.

Vergiss endlich einmal dass innerhalb der Funktion *Port identisch ist 
mit PORTA. Interessiert keine Sau. Das ist nur "zufällig" so, weil du 
die Funktion mit &PORTA aufrufst. Das ist aber völlig irrelevant, wenn 
sich der Compiler die Funktion selbst vornimmt. Innerhalb der Funktion 
ist das ein ganz normaler Pointer auf einen stink normalen uint8_t. 
Nicht mehr und nicht weniger.

Und das darf er eben nicht sein. Wir wollen, dass jede Manipulation von 
*Port tatsächlich so ausgeführt wird, wie wir sie hingeschrieben haben. 
Daher das volatile.

von Brocken Sei (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Und da _delay_us das i
> nicht verändert und auch nicht darauf angewiesen ist, das i einen
> speziellen Wert hat (weil sie i gar nicht benutzt), gibt es keinen Grund
> die erste Zuweisung überhaupt auszuführen.

Ok ich muss leider sagen das ergibt keinen Sinn für mich. Was hat i mit 
delay_us zu tun??

Gruß Bro

von Brocken Sei (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Das ist nur "zufällig" so, weil du
> die Funktion mit &PORTA aufrufst.

Ist das nicht Grundvoraussetzung dass das ganze funktioniert?

Gruß Bro

von Karl H. (kbuchegg)


Lesenswert?

Brocken Sei schrieb:
> Karl heinz Buchegger schrieb:
>> Und da _delay_us das i
>> nicht verändert und auch nicht darauf angewiesen ist, das i einen
>> speziellen Wert hat (weil sie i gar nicht benutzt), gibt es keinen Grund
>> die erste Zuweisung überhaupt auszuführen.
>
> Ok ich muss leider sagen das ergibt keinen Sinn für mich. Was hat i mit
> delay_us zu tun??

Eben. Nichts.

Wozu einen Gartenzaun grün streichen, wenn er 5 Minuten später sowieso 
rot gestrichen wird?

Ist alles dasselbe.
Es geht darum, dass das für den Compiler ohne das volatile ansonsten 
eine sinnlose Aktion ist, die er straffrei rauswerfen darf.


   i = 5;
   _delay_us( 200 );
   i = 8;


   *Port = 27;
   _delay_us( 200 );
   *Port = 32;

   *Port |= ( 1 << 5 );
   _delay_us( 200 );
   *Port &= ~( 1 << 5 );

All diese 3 Sequenzen sind von genau derselben Machart. Eine Aktion wird 
von einer anderen Aktion 'überschrieben'. Überschrieben in dem Sinne, 
dass nach Abarbeitung dieser 3 Anweisungen, das Ergebnis 
ununterschiedbar davon ist, wie wenn die erste Aktion nie gemacht worden 
wäre.

(Wobei ich zugeben muss, dass es für den Compiler im letzten Fall schon 
erheblich schwieriger ist, dies tatsächlich festzustellen)

genau darum gehst.

Wenn du

   i = 5;
   _delay_us( 200 );
   i = 8;

ausführst, dann ist danach das Ergbnis nicht zu unterscheiden von

   _delay_us( 200 );
   i = 8;


Wenn du

   *Port = 27;
   _delay_us( 200 );
   *Port = 32;

programmierst, dann ist das Ergebnis nach der letzten Anweisung nicht zu 
unterscheiden von

   _delay_us( 200 );
   *Port = 32;


Wenn du

   *Port |= ( 1 << 5 );
   _delay_us( 200 );
   *Port &= ~( 1 << 5 );

programmierst, dann ist nach Ausführung dieser 3 Anweisungen das 
Ergebnis nicht zu unterscheiden von

   _delay_us( 200 );
   *Port &= ~( 1 << 5 );

Daher könnte der COmpiler auf die Idee kommen: Ja wenn das so ist, dann 
brauch ich doch jeweils die erste Anweisung gar nicht ausführen lassen. 
Kommt ja sowieso hinten nach immer dasselbe raus.

Das und nur das, ist genau der Grund warum ein PORTA eine 'volatile 
uint8_t' Variable ist und keine gewöhnliche Variable. Und das und nur 
das ist auch der Grund warum ein &PORTA den Datentyp volatile uint8_t* 
hat. Und diesen Datentyp sollte die Funktion besser auch aufgreifen und 
nicht das volatile wegwerfen, den ansonsten kann es innerhalb der 
Funktion genau wieder zu dem 'Problem' kommen, dass der Compiler anfängt 
mit *Port zu optimieren.

von Brocken Sei (Gast)


Lesenswert?

Ok, vielleicht versteh ich was falsch.

Wir übergeben der inline Funktion (die den Vorteil hat, dass sie keine 
Sprünge benötigt und dass Datentyp-Übergaben überprüft werden) die 
Adresse von PORTA mit &PORTA, das ist klar.
In der Funktion lesen wir diese Adresse mit Port aus, indem wir einen 
Zeiger definieren. uint8_t *Port. Dieser Zeiger hat die Adresse von 
PORTA.
Mit *Port sagen wir im Prinzip PORTA = ... . Und das eben zwei mal. Was 
ist das Problem?
_delay_us hat nichts mit *Port zu tun, oder versteh ich da was falsch?

Gruß Bro

von Karl H. (kbuchegg)


Lesenswert?

Brocken Sei schrieb:
> Karl heinz Buchegger schrieb:
>> Das ist nur "zufällig" so, weil du
>> die Funktion mit &PORTA aufrufst.
>
> Ist das nicht Grundvoraussetzung dass das ganze funktioniert?

Der Funktion ist es völlig Wurscht, mit welchem Pointer sie aufgerufen 
wird. Die weiß nichts von einem PORTA. Die soll das auch gar nicht 
wissen.

von Karl H. (kbuchegg)


Lesenswert?

Brocken Sei schrieb:
> Ok, vielleicht versteh ich was falsch.
>
> Wir übergeben der inline Funktion (die den Vorteil hat, dass sie keine
> Sprünge benötigt und dass Datentyp-Übergaben überprüft werden) die
> Adresse von PORTA mit &PORTA, das ist klar.

Ja.
Aber innerhalb der Funktion verliert &PORTA seinen Sonderstatus, wenn du 
das volatile weglässt.

> In der Funktion lesen wir diese Adresse mit Port aus, indem wir einen
> Zeiger definieren. uint8_t *Port. Dieser Zeiger hat die Adresse von
> PORTA.

Zum Beispiel.
Er könnte auch die Adresse von i haben.
Der Funktion ist das egal.

> Mit *Port sagen wir im Prinzip PORTA = ... . Und das eben zwei mal. Was
> ist das Problem?

Weißt du was.
Nimm ein Makro.
Mir wird das jetzt zu böd.

von Karl H. (kbuchegg)


Lesenswert?

Ich wiederhols noch mal, damit ich mich nicht dem Vorwurf aussetzen 
muss, ich hätte es nicht versucht, nur weil du es nicht gesehen hast:


Brocken Sei schrieb:
> Karl heinz Buchegger schrieb:
>> Und da _delay_us das i
>> nicht verändert und auch nicht darauf angewiesen ist, das i einen
>> speziellen Wert hat (weil sie i gar nicht benutzt), gibt es keinen Grund
>> die erste Zuweisung überhaupt auszuführen.
>
> Ok ich muss leider sagen das ergibt keinen Sinn für mich. Was hat i mit
> delay_us zu tun??

Eben. Nichts.

Wozu einen Gartenzaun grün streichen, wenn er 5 Minuten später sowieso 
rot gestrichen wird?

Ist alles dasselbe.
Es geht darum, dass das für den Compiler ohne das volatile ansonsten 
eine sinnlose Aktion ist, die er straffrei rauswerfen darf.


   i = 5;
   _delay_us( 200 );
   i = 8;


   *Port = 27;
   _delay_us( 200 );
   *Port = 32;


   *Port |= ( 1 << 5 );
   _delay_us( 200 );
   *Port &= ~( 1 << 5 );

All diese 3 Sequenzen sind von genau derselben Machart: Eine Aktion wird 
von einer anderen Aktion 'überschrieben'. Überschrieben in dem Sinne, 
dass nach Abarbeitung dieser 3 Anweisungen, das Ergebnis 
ununterschiedbar davon ist, wie wenn die erste Aktion nie gemacht worden 
wäre.

(Wobei ich zugeben muss, dass es für den Compiler im letzten Fall schon 
erheblich schwieriger ist, dies tatsächlich festzustellen)

genau darum gehst.

Wenn du

   i = 5;
   _delay_us( 200 );
   i = 8;

ausführst, dann ist danach das Ergbnis nicht zu unterscheiden von

   _delay_us( 200 );
   i = 8;


Wenn du

   *Port = 27;
   _delay_us( 200 );
   *Port = 32;

programmierst, dann ist das Ergebnis nach der letzten Anweisung nicht zu 
unterscheiden von

   _delay_us( 200 );
   *Port = 32;


Wenn du

   *Port |= ( 1 << 5 );
   _delay_us( 200 );
   *Port &= ~( 1 << 5 );

programmierst, dann ist nach Ausführung dieser 3 Anweisungen das 
Ergebnis nicht zu unterscheiden von

   _delay_us( 200 );
   *Port &= ~( 1 << 5 );

Daher könnte der COmpiler auf die Idee kommen: Ja wenn das so ist, dann 
brauch ich doch jeweils die erste Anweisung gar nicht ausführen lassen. 
Kommt ja sowieso hinten nach immer dasselbe raus.

Das und nur das, ist genau der Grund warum ein PORTA eine 'volatile 
uint8_t' Variable ist und keine gewöhnliche Variable. Und das und nur 
das ist auch der Grund warum ein &PORTA den Datentyp volatile uint8_t* 
hat. Und diesen Datentyp sollte die Funktion besser auch aufgreifen und 
nicht das volatile wegwerfen, den ansonsten kann es innerhalb der 
Funktion genau wieder zu dem 'Problem' kommen, dass der Compiler anfängt 
mit *Port zu optimieren.

von Brocken Sei (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Wozu einen Gartenzaun grün streichen, wenn er 5 Minuten später sowieso
> rot gestrichen wird?

Mhm, ziemlich verständlich ausgedrückt und jetzt verstehe ich was du 
meinst. Ich dachte das hätte was mit der inline Funktion zu tun, stimmt 
aber nicht. In den Büchern die ich lese ist volatile kaum verwendet 
worden, nur mal erwähnt.
Also im Großen und ganzen läuft das so ab:
Wenn ich Operationen habe, die sich immer wieder überschreiben dann muss 
ich diese mit volatile ausstatten?

Habe ich nicht gedacht, denn wenn ich eine Rechnung mit mehreren 
Schritten brauche dann definiere ich auch nicht volatile.

ABer ok, wenn das Tatsache ist.

Danke für die HIlfe.

Gruß Bro

von Klaus W. (mfgkw)


Lesenswert?

... schrieb:
> Klaus Wachtler schrieb:
>> Ich habe schon ein paar C-Bücher gelesen, ehrlich!
>
> Das glaub ich Dir aufs Wort und es lag mir fern das zu bezweifeln. Ich
> war einfach nur zu langsam beim Tippen.
> Mein Post bezog sich eigentlich auf den Post über Deinen:
> Beitrag "Re: #define Frage"

(dachte ich mir, konnte mir die Antwort aber leider nicht verkneifen :-)

von Karl H. (kbuchegg)


Lesenswert?

Brocken Sei schrieb:
> Ok, vielleicht versteh ich was falsch.
>
> Wir übergeben der inline Funktion (die den Vorteil hat, dass sie keine
> Sprünge benötigt und dass Datentyp-Übergaben überprüft werden) die
> Adresse von PORTA mit &PORTA, das ist klar.

Auch eine inline Funktion ist zuallererst einfach nur eine Funktion.

Du scheinst du mehr hineinzulesen als dahintersteckt.
Funktion ist Funktion. Ob inline oder nicht.
Vor allen Dingen findet da keine irgendwie geartete Textsubstitution 
statt. Zumindest nicht konzeptionell.

von Brocken Sei (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Aber innerhalb der Funktion verliert &PORTA seinen Sonderstatus, wenn du
> das volatile weglässt.

Was heißt das?

Gruß Bro

von Klaus W. (mfgkw)


Lesenswert?

Brocken Sei schrieb:
> In den Büchern die ich lese ist volatile kaum verwendet
> worden, nur mal erwähnt.

Das liegt einfach daran, daß man es selten braucht - außer bei
dem embedded-Kram.

Ab PCs aufwärts braucht man volatile entweder
- bei hardwarenaher Programmierung iinerhalb des OS oder
  Treiebrn
- bei Multithreading (auf das viele Bücher gar nicht
  oder nur am Rande eingehen)

Bei Controllern ist aber aber erstens viel mahr HW-naher Kram
dabei, zweitens hat man keine Unterstützung durch ein
ausgefeiltes OS, sondern muß stattdessen mit Interrupts etc.
selber hantieren.

Daß in deinen Büchern kaum volatile vorkommt, heißt also nicht,
daß du es hier nicht brauchst.
volatile ist kein Steckenpferd von komischen Leuten hier,
sondern einfach Alltag.

von Brocken Sei (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Vor allen Dingen findet da keine irgendwie geartete Textsubstitution

Das ist mir schon bewusst, es ging mir um die Adresse von PORTA, die 
übergeben wird.
Wieso verschwindet die?
Ich übergebe sie doch mit &PORTA, ich könnte auch &PORTB schreiben und 
es würde PORTB.7 verwendet, ich weiß dass ich da irgendeinen PORT 
verwenden kann, falls du das meinst.

Aber nach deinem letzten Post ist es offensichtlich, dass nicht nur das 
überschreiben von Variablen in Rechenoperationen das Schlüsselwort 
volatile verwendet wird.

Karl heinz Buchegger schrieb:
> Er könnte auch die Adresse von i haben.

Ja, aber ich gebe der Funktion &PORTA, also hat es auch die Adresse von 
PORTA. Aber wie sich diese ändert ohne volatile verstehe ich nicht.

Gruß Bro

von Karl H. (kbuchegg)


Lesenswert?

Brocken Sei schrieb:

> Also im Großen und ganzen läuft das so ab:
> Wenn ich Operationen habe, die sich immer wieder überschreiben dann muss
> ich diese mit volatile ausstatten?


Wenn du haben willst, dass jede Operation auf einer Variablen so 
ausgeführt wird, wie du sie hinschreibst, dann muss die Variable 
volatile sein.

Und bei Ports möchte man das im Allgemeinen. Man möchte haben, dass ein 
Port Pin auf 0 geht, selbst wenn er 5 Taktzyklen später wieder auf 1 
gesetzt wird.
Bei Portpins ist eben nicht nur das Endergebnis interessant, sondern 
auch alle Zwischenschritte. Zwischenschritte die bei normalen Variablen 
völlig uninteressant sind.

 ob das i in

   i = 5;
   i = 8;
   j = 2 * i;

tatsächlich irgendwann einmal 5 war, interessiert niemanden. 
Insbesondere interessiert es bei der Berechnung von 2*i nicht, weil dort 
beweisbar i nicht 5 sein kann, sondern 8 ist. Daher muss man diese 
Zuweisung mit 5 nicht machen und spart so etwas Rechenzeit.

Bei PortPins wollen wir das aber nicht so haben. Wenn ich mache

   PORTA = 5;
   PORTA = 8;

dann will ich haben, dass das Port-Register zwischendurch (und sei es 
noch so kurz) den Wert 5 angenommen hat. Ich will das deswegen haben, 
weil diese Zuweisung einen Nebeneffekt hat: Ein externer Pin, der eine 
Elektronik ansteuert, geht kurzfristig auf 1 und dann wieder auf 0.
Das kann aber der Compiler nicht wissen. Der handelt nach seiner 
obersten Optimizer-Direktive: Für meinen User an Laufzeit rausholen was 
ich kann.
Daher muss man ihm mit einem volatile sagen: Vergiss alles. Ich will, 
dass PORTA jeden Wert annimmt, den ich als Programmierer dafür vorsehe.

von Brocken Sei (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> Bei Controllern ist aber aber erstens viel mahr HW-naher Kram
> dabei, zweitens hat man keine Unterstützung durch ein
> ausgefeiltes OS, sondern muß stattdessen mit Interrupts etc.
> selber hantieren.

Vielleicht sollte ich doch lieber auf 32bit Controllern umsteigen, da 
brauch ich mir keine Sorgen um Schnelligkeit und bösen volatiles zu 
machen.

na, Spaß^^

Gruß Bro

von Karl H. (kbuchegg)


Lesenswert?

Brocken Sei schrieb:

> Vielleicht sollte ich doch lieber auf 32bit Controllern umsteigen, da
> brauch ich mir keine Sorgen um Schnelligkeit und bösen volatiles zu
> machen.

Falscher Schluss.
Dort musst du dir um volatile genauso Gedanken machen.

Und noch um 100 andere Kleinigkeiten mehr.

von Klaus W. (mfgkw)


Lesenswert?

Auch da wirst du entweder Interrupts oder Threads haben, und schon
brauchst du volatile.

Es sei denn, du hast etwas mit Linux drauf und die Threads
kommunizieren nicht über gemeinsame Variablen, sondern ipes
oder sowas.
Dann kannst in der Tat darauf verzichten, und bist wieder bei
dem, was in deinen Büchern steht. Ich fürchte jetzt nur, daß
in denen auch nichts über Pipes steht...

von Karl H. (kbuchegg)


Lesenswert?

Brocken Sei schrieb:
> Karl heinz Buchegger schrieb:
>> Vor allen Dingen findet da keine irgendwie geartete Textsubstitution
>
> Das ist mir schon bewusst, es ging mir um die Adresse von PORTA, die
> übergeben wird.
> Wieso verschwindet die?

Die Adresse verscheindet nicht.
Aber wenn du das volatile weglässt

void foo( uint8_t * Addr )
{
}

dann ist der POinter innerhalb der Funktion kein Pointer mehr auf einen 
volatile Wert

> Ich übergebe sie doch mit &PORTA

Ja

int main()
{
  foo( &PORTA );   // hier ist PORTA noch volatile.
                   // Aber innerhalb der Funktion foo wäre er es nicht
                   // mehr.
}

>> Er könnte auch die Adresse von i haben.
>
> Ja, aber ich gebe der Funktion &PORTA,

Das ist aber der Funktion wurscht!

Du kannst ja auch in eine Funktion

void bar( long i )
{
  ...
}


int main()
{
   bar( 5 );
}

einen int hineinstecken und innerhalb der Funktion ist er dann ein long.

Genauso kannst du ausserhalb einer Funktion einen volatile Wert haben, 
den in eine Funktion hineinschieben und innerhalb der Funktion ist er es 
nicht mehr.

Und ganz genauso kannst du ausserhalb einer Funktionen einen Pointer auf 
einen volatile Wert haben und innerhalb der Funkion ist er esnicht mehr.

In

void batz( uint8_t * Addr )
{
  *Addr = ...
}

ist Addr ein Pointer auf eine stink normale uint8_t Variable.
Selbst wenn du diese Funktion mit &PORTA aufrufst! Der Pointer von 
ausserhalb der Funktion verliert sein volatile, wenn du diese Funktion 
aufrufst.

von Brocken Sei (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Wenn du haben willst, dass jede Operation auf einer Variablen so
> ausgeführt wird, wie du sie hinschreibst, dann muss die Variable
> volatile sein.

Aber dann gilt das nicht für das oder, weil i einmal von j und einmal 
von sich abhängt?:
1
int main()
2
{
3
  int i;
4
  int j = 5;
5
6
  i = 2*j;
7
  i = 4*i;
8
  i = 5+i;
9
}


Dh. nur Variablen mit direkter Zuweisung werden weg optimiert.

Ansonsten ist jetzt alles klar, sehr schön formuliert.

Danke Karl.

Gruß Bro

von Brocken Sei (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> foo( &PORTA );   // hier ist PORTA noch volatile.

Hmm, hmm.

Karl heinz Buchegger schrieb:
>>> Er könnte auch die Adresse von i haben.
>>
>> Ja, aber ich gebe der Funktion &PORTA,
>
> Das ist aber der Funktion wurscht!

Wie kann es der Funktion wurscht sein, wenn sie die Adresse doch 
bekommt, zwar nicht als volatile weil sie einen uint8_t zeiger erwartet, 
aber die adresse würde stimmtn,
ich habe volatile ben so verstanden dass bei direkten Zuweisungen 
optimiert wird, was auch verständlich ist. Der Compiler hält es für 
überflüssig. Die Adresse von PORTA ist aber trotzdem noch in Port 
drinnen, mit oder ohne volatile.

Gruß Bro

von Karl H. (kbuchegg)


Lesenswert?

Brocken Sei schrieb:
> Karl heinz Buchegger schrieb:
>> Wenn du haben willst, dass jede Operation auf einer Variablen so
>> ausgeführt wird, wie du sie hinschreibst, dann muss die Variable
>> volatile sein.
>
> Aber dann gilt das nicht für das oder, weil i einmal von j und einmal
> von sich abhängt?:

genau

> Dh. nur Variablen mit direkter Zuweisung werden weg optimiert.

Wenns nur das wäre.

Meistens kommt man auf umgekehrtem Wege zum volatile


   i = 5;
   while( i != 8 )
     mach irgendwas, in dem kein i vorkommt.

Hier kann der Compiler SChlussfolgern dass i niemals 8 werden kann


   while( PINA != 5 )
     ...

hier ist das aber tödliche, wenn der Compiler annimmt, dass er den Wert 
von PINA kennen würde. PINA kann sich ändern, ohne dass der Compiler das 
im Code sehen könnte.

Dassselbe mit Variablen, die sowohl in so einer Abfrage als auch in 
einer ISR benutzt werden: Für den Compiler ist das nicht ersichtlich, 
dass diese Variable ihren Wert ändern kann, denn die ISR Funktion wird 
ja nirgends explizit aufgerufen.

von Klaus W. (mfgkw)


Lesenswert?

Um die Diskussion mal zu drehen:
Die PORT... sind ja letztlich so definiert (vereinfacht):
1
#define PORTA    (*(volatile uint8_t *)(0x32))

Wenn ich jetzt &PORTA an eine solche Funktion übergebe:
1
void blabla( uint8_t *p_port )...
dann ist das einfach deshalb ein Fehler, weil es das volatile
wegcasten würde.
Ohne wissen zu müssen, warum Atmel das mit volatile definiert
hat, hat man einfach &PORTA als volatile uint8_t* zu behandeln,
basta.

von Karl H. (kbuchegg)


Lesenswert?

Brocken Sei schrieb:
> Karl heinz Buchegger schrieb:
>> foo( &PORTA );   // hier ist PORTA noch volatile.
>
> Hmm, hmm.
>
> Karl heinz Buchegger schrieb:
>>>> Er könnte auch die Adresse von i haben.
>>>
>>> Ja, aber ich gebe der Funktion &PORTA,
>>
>> Das ist aber der Funktion wurscht!
>
> Wie kann es der Funktion wurscht sein, wenn sie die Adresse doch
> bekommt,

der Funktion ist es wurscht, ob die Adresse die sie bekommt ausserhalb 
der Funktion auf einen volatile Wert gezeigt hat oder nicht

void foo( uint8_t * Addr )
{
}

> zwar nicht als volatile weil sie einen uint8_t zeiger erwartet,
> aber die adresse würde stimmtn,

Der reine Zahlenwert der Adresse: ja.
Aber volatile ist ja eine Zusatzinformation zu diesem reinen Zahlenwert. 
Zusatzinformation die wichtig ist, damit der Compiler weiß, was er alles 
anstellen darf mit dem Wert, den er an der angegebenen Adresse findet.
Und diese Zusatzinformation würde ja sconst beim Funktionsaufruf 
verloren gehen, wenn du das volatile weglässt.

von Brocken Sei (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> ist Addr ein Pointer auf eine stink normale uint8_t Variable.
> Selbst wenn du diese Funktion mit &PORTA aufrufst! Der Pointer von
> ausserhalb der Funktion verliert sein volatile, wenn du diese Funktion
> aufrufst.

Hm, ok sowie ich deinen Text verstehe geht es hier nicht darum ob die 
Adresse ankommt sondern mehr dass die Variable sein Volatile verliert.
PORTA ist als ein volatile, stimmr muss es ja, denn sonst wären meine 
ganzen PORT-Zuweisungen nicht mit rein genommen, sondern immer nur das 
letzte.
Verständlich.

Vielen dank, ich glaube ich habe volatile verstanden.

Hmm aber das heißt volatile Vairbalen sind nicht unbedingt notwendig bei 
Interrupt routinen, wenn sie nur einmal im Zuweisungsprozess vorkommen 
oder?

Gruß Bro

von Klaus W. (mfgkw)


Lesenswert?

Vergiß doch mal deine blöden Zuweisungen!
volatile ist dann nötig, wenn auf einer Variablen oder
sonstigen Speicherstelle mehr passiert, als der Compiler anhand der 
aktuellen Funktion sehen kann.

von Brocken Sei (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Der reine Zahlenwert der Adresse: ja.
> Aber volatile ist ja eine Zusatzinformation zu diesem reinen Zahlenwert.
> Zusatzinformation die wichtig ist, damit der Compiler weiß, was er alles
> anstellen darf mit dem Wert, den er an der angegebenen Adresse findet.
> Und diese Zusatzinformation würde ja sconst beim Funktionsaufruf
> verloren gehen, wenn du das volatile weglässt.

Ok, sehr gut, habe ich mir gedacht dass das so ist.

Klaus Wachtler schrieb:
> dann ist das einfach deshalb ein Fehler, weil es das volatile
> wegcasten würde.

Nun ja das muss aber nicht bedeuten, oder besser gesagt es bedeutet 
nicht dass es nicht funktionieren würde.
Jetzt weiß ich aber warum hier volatile auch wichtig ist, und auch warum 
Atmel dies so definiert haben, und zwar damit man Zuweisungen machen 
kann ohne sich sorgen zu machen, dass was optimiert wird.

Gruß Bro

von Brocken Sei (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> Ohne wissen zu müssen, warum Atmel das mit volatile definiert
> hat, hat man einfach &PORTA als volatile uint8_t* zu behandeln,
> basta.

Prinzipiell hast du recht, aber ich konnte nicht wissen, dass PORTA als 
volatile deklariert ist.

Aber im Großen unf Ganze habe ich volatile jetzt verstanden.
Danke euch beiden, echt feine Sache.

Gruß Bro

von Matthias K. (mkeller)


Lesenswert?

Karl heinz Buchegger schrieb:
> volatile uint8_t *
>
> ist etwas anderes als
>
>   uint8_t * volatile
>
>


Erstmal Respekt für soviel Durchhaltevermögen beim Erklären.

Im ersten Fall ist der Zeiger volatile und zeigt auf non-volatile Daten 
und im 2. Beispiel anders herum oder? Wie sieht das Verhalten des 
Compilers aus?

Im 1. Fall wird beim Zugriff auf den Zeiger (also pointer1++ ....) der 
Pointer-wert, aufgrund des volatile modifieres, auf jeden Fall neu aus 
dem Hauptspeicher geladen. Die Daten der Speicheradresse könnten aber 
aktuell auch in einem Register liegen oder?

Im 2. Fall wird beim Zugriff auf die Adresse (also *pointer2++ ...) der 
Wert der Variable neu aus dem Hauptspeicher geladen

Soweit Richtig?

von Klaus W. (mfgkw)


Lesenswert?

Genau falsch rum geraten :-)

volatile uint8_t * ist ein normaler Zeiger auf eine flüchtige, 
dampfartige uint8_t.

uint8_t * volatile ist ein dampfender Zeiger auf eine solide uint8_t.

volatile uint8_t * volatile ist ein volatiler Zeiger auf eine volatile 
uint8_t.

von Brocken Sei (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> Genau falsch rum geraten :-)
>
> volatile uint8_t * ist ein normaler Zeiger auf eine flüchtige,
> dampfartige uint8_t.
>
> uint8_t * volatile ist ein dampfender Zeiger auf eine solide uint8_t.
>
> volatile uint8_t * volatile ist ein volatiler Zeiger auf eine volatile
> uint8_t.

Was bedeutet das?
Hab den Unterscheid immer noch nicht.

Kannst du das bitte mit einfachen Worten erklären?

Gruß Bro

von Matthias K. (mkeller)


Lesenswert?

Danke.... Ich sollte besser nach 10h programmieren/dokumentieren nicht 
auch noch in Foren posten ;)

von Klaus W. (mfgkw)


Lesenswert?

Was gibt es da zu erklären?
Es gibt Variablen, die können const sein oder nicht, und sie können 
volatile sein oder nicht:
1
    uint8_t          i;      // normale uint8_t
2
    volatile uint8_t PORTA;  // volatile uint8_t

Halt je nachdem, ob sie sich heimlich ändern können oder nicht.

Bei Zeigern muß man jetzt etwas mehr überlegen: soll der Zeiger selbst 
volatile sein? ja oder nein
Soll der Wert, auf den der Zeiger zeigt, volatile sein? ja oder nein.

Mehr ist nicht zu verstehen, man muß es dem Compiler nur noch sagen,
und da muß man es halt so schreiben, daß er es versteht.
Du musst dich nach ihm richten und nicht umgekehrt:
1
uint8_t          *p1; // normaler Zeiger auf normale uint8_t
2
volatile uint8_t *p2: // normaler Zeiger auf volatile uint8_t
3
uint8_t          *volatile p3: // volatile Zeiger auf normale uint8_t
4
volatile uint8_t *volatile p4: // volatile Zeiger auf volatile uint8_t

Selbiges gilt für const, falls du das mal brauchst (braucht man oft).

von Brocken Sei (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> Selbiges gilt für const, falls du das mal brauchst (braucht man oft).

Ok, danke!

Gruß Bro

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.