Hallo,
angehängt ist der Beispielcode aus der AVR221 Application Note.
Kann mir jemand sagen warum die Variable pidTimer aus struct
GLOBAL_FLAGS hier nicht volatile sein muss bzw. ist??
1
00001/*This file has been prepared for Doxygen automatic documentation generation.*/
Desweitern verstehe ich im nachfolgend angehängten Code nicht warum bei
pid.maxError und bei pid.maxSumError dem Divisor jeweils noch 1 addiert
wird.
Hätte da vielleicht jemand eine Idee?
1
00001/*This file has been prepared for Doxygen automatic documentation generation.*/
Meier schrieb:> Kann mir jemand sagen warum die Variable pidTimer aus struct> GLOBAL_FLAGS hier nicht volatile sein muss bzw. ist??
Weil es der IAR zufälligerweise auch so richtig compiliert und
daher keiner gemerkt hat, dass sie volatile sein sollte?
> Desweitern verstehe ich im nachfolgend angehängten Code nicht warum bei> pid.maxError und bei pid.maxSumError dem Divisor jeweils noch 1 addiert> wird.
Die entsprechenden Paramter sind erklärt als "Tuning constants for PID
loop". Damit ist es wohl eine reine Definitionsfrage, wie man diese
genau festlegt.
Jörg Wunsch schrieb:> Meier schrieb:>>> Kann mir jemand sagen warum die Variable pidTimer aus struct>> GLOBAL_FLAGS hier nicht volatile sein muss bzw. ist??>> Weil es der IAR zufälligerweise auch so richtig compiliert und> daher keiner gemerkt hat, dass sie volatile sein sollte?
Nur ergänzend: ist mir bei der Portierung von Code für IAR EWAVR und
EWARM und bei dem Compiler von ARM (Realview-Tools) für ARM nach GNU
schon mehrfach aufgefallen, dass volatiles gefehlt haben. Möglicherweise
behandeln diese Compiler implizit alle globalen Variablen oder zumindest
solche, die auch in ISRs verwendet werden, als wären sie mit volatile
deklariert oder es wird einfach nicht mit eingeschalteter Optimierung
getestet.
Hallo,
> Kann mir jemand sagen warum die Variable pidTimer aus struct> GLOBAL_FLAGS hier nicht volatile sein muss bzw. ist??
Es gilt per Definition:
Das Schlüsselwort volatile teilt dem Compiler mit, daß die mit name
bezeichnete Variable mit dem Datentyp typ durch Ereignisse außerhalb der
Kontrolle des Programms verändert werden kann.
Wichtig hierbei ist "außerhalb der Kontrolle des Programms". Wird die
Variable innerhalb des Programms (eines Source-Files) verändert ist
"volatile" nicht nötig denn dann weiss es der Compiler schon!
Jörg Wunsch schrieb:> Weil es der IAR zufälligerweise auch so richtig compiliert und> daher keiner gemerkt hat, dass sie volatile sein sollte?
Könnte es daran liegen:
in der main wird zweimal auf gFlags.pidTimer zugegriffen. Dazwischen ist
ein Funktionsaufruf. Kann der Compiler/Optimiser sicher sein, daß
Registerinhalte über diesen Funktionsaufruf, der ja nun weitere Aufrufe
oder auch Librarycalls enthalten kann, erhalten bleiben? Der Code dieser
Funktionen kann ja unbekannt sein, da sie ja erst der Linker (oder bei
großen Systemen erst der Loader) einbindet.
MfG Klaus
Klaus schrieb:> in der main wird zweimal auf gFlags.pidTimer zugegriffen. Dazwischen ist> ein Funktionsaufruf.
Jain. Der Funktionsaufruf ist da nur dazwischen, wenn die Variable
true ist. Wenn sie false ist, ist keiner drin, und der Compiler
könnte das erneute Abfragen des Variablenwerts aus dem Speicher
wegoptimieren.
> Kann der Compiler/Optimiser sicher sein, daß> Registerinhalte über diesen Funktionsaufruf, der ja nun weitere Aufrufe> oder auch Librarycalls enthalten kann, erhalten bleiben?
Hängt davon ab, ob er diese Funktion selbst mit auf Seiteneffekte
analysieren kann. Ich glaube, auch IAR hat derartige Möglichkeiten.
..ganz klar... bei Benutzung des GNU.C Compilers ( WinAVR )ist "jede",
nur in der Interruptfunktion gesetzte Variable unbedingt volatile zu
deklarieren !!
Die in der AVR221 Appnote gezeigten definerte struct-Variable
"gFlags.pidTimer" funktioniert in der main()while(1){} schleife NICHT!
Diese Variable muß volatile deklariert sein!!
Besser so machen: ( wen man es so machen will ...)
typedef struct GLOBAL_FLAGS {
//! True when PID control loop should run one time
uint8_t pidTimer :1;
uint8_t :7; // dummy Alignment {0}; nicht {0, 0} ;
}GLOBAL_PID_FLAG_TYPE; // Nur Type definieren)
// dann erst globale struct anlegen, aber volatile
volatile GLOBAL_PID_FLAG_TYPE gFlags = {0}; //volatile
dann kann in main().....
if( gFlags.pidTimer == TRUE )// gefragt werden
// oder natürlich nur
if( gFlags.pidTimer )
Auch nicht zu vergessen.
Das volatile ist auch nur in bestimmten Situationen notwendig (im Sinne
von: wirkt sich tatsächlich aus). So wie hier, wenn die Hauptschleife
ausser der PID Sache nichts anderes mehr macht. Allerdings baut man ja
eher selten eine µC-Schaltung, die nur einen PID Regler in sich enthält
und sonst nichts.
D.h. in einem realen Programm, bei dem die Hauptschleife ein wenig mehr
macht, als nur PID-Regeln, ist es gar nicht so unwahrscheinlich, dass
das Versäumnis gar nicht auffällt, weil der COmpiler schlicht und
ergreifend gar keine Möglichkeit hat, dieses Versäumnis auszunutzen,
weil er die Register innerhalb der Hauptschleife auch noch für andere
Dinge braucht. Erst wenn man dann aus einem realen Beispiel nur diesen
Teil isoliert und nur mehr oberflächlich testet, dann würde das Problem
sichtbar, wird aber durch den oberflächlichen Test vor der
Veröffentlichung nicht bemerkt - denn schliesslich hat der Regler, sogar
4 PID-Regler gleichzeitig, im viel komplexeren Quadrokopter ja auch
wunderbar funktioniert.
Meier schrieb:> Desweitern verstehe ich im nachfolgend angehängten Code nicht warum bei> pid.maxError und bei pid.maxSumError dem Divisor jeweils noch 1 addiert> wird.>> Hätte da vielleicht jemand eine Idee?
Klopf mal den Code darauf hin ab, was passiert, wenn die entsprechenden
Faktoren auf 0 gesetzt werden. Ist nur so ein Bauchgefühl.
Stefan++ schrieb:> Wichtig hierbei ist "außerhalb der Kontrolle des Programms". Wird die> Variable innerhalb des Programms (eines Source-Files) verändert ist> "volatile" nicht nötig denn dann weiss es der Compiler schon!
Das hat mit innerhalb oder ausserhalb eines source-Files gar nichts zu
tun.
C als Sprache kennt das Konzept "Interrupt" nicht, und gcc als
C-Sprach-konformer Compiler daher auch nicht. Für den gcc wird eine ISR
nie aufgerufen, weil kein Programmpfad die jemals aufruft. Daher erkennt
der gcc auch nicht, daß die ISR Variablen verändert, egal, ob die ISR
innerhalb oder ausserhalb des Files mit dem Hauptprogramm steht.
Oliver
Aber müsste nicht eigentlich jede Variable die nicht explizit als
"static" gekennzeichnet ist, und damit exportiert wird, als jederzeit
veränderbar angenommen werden?
Denn jede solche Variable kann ja per "extern" überall bekannt gemacht
werden und damit, außerhalb der momentanen "Sicht" des Compilers,
beschrieben werden.
Der Compiler anaylisert den Progammablauf nach den Regeln des C-Stadards
und erkennt, an welchen Stellen sich eine Variable ändert oder ändern
kann. Ein Funktionsaufruf einer Funktion, die nicht im aktuellen
sourcefile steht, ist so ein Fall. Danach sind alle globalen Variablen
potentiell verändert, und würden für danachfolgende Zugriffe neu
gelesen.
Der Unterschied zur ISR ist der, daß die ISR eine Variable auch an
Stellen ändern kann, die das nach dem C-Standard nicht erwarten lassen.
Klassiker ist z.B
>int flag = 0;> while (!flag);
Oliver
bal schrieb:> Aber müsste nicht eigentlich jede Variable die nicht explizit als> "static" gekennzeichnet ist, und damit exportiert wird, als jederzeit> veränderbar angenommen werden?
Nein. Warum sollte sie
Welche Möglichkeit gibt es für i in
extern int i;
void foo()
{
int k = 2 * i;
// <----
int j = 3 * i;
}
an der markierten Stelle seinen Wert zu ändern? Richtig. Es gibt keine
Möglichkeit. Da wird keine Funktion aufgerufen und auch sonst nichts
getan, wodurch sich i verändern könnte. Nachdem der Compiler Code
eingebaut hat um für die Berechnung von k den momentanen Wert von i
festzustellen, kann er denselben Wert auch für die Berechnung von j
benutzen, denn es muss sich an dieser Stelle ja um denselben Wert
handeln.
Die einzige Ausnahme sind Interrupts bzw. memory mapped Variablen oder
Multithreading. Aber das sind Konzepte, die es in C schlicht und
ergreifend nicht gibt. Daher muss man als Programmierer nachhelfen und
dem Compiler mitteilen: du kannst dich bei i auf nichts verlassen - die
Variable verändert sich, ohne dass du das detektieren könntest:
extern volatile int i;
> Denn jede solche Variable kann ja per "extern" überall bekannt gemacht> werden und damit, außerhalb der momentanen "Sicht" des Compilers,> beschrieben werden.
Aber damit sie das tun könnte, muss an der betreffenden Stelle im Code
etwas geschehen. Zb eine Funktion aufgerufen werden, die die Variable
verändern könnte. Gibt es keinen Funktionsaufruf, und wird auch sonst
nicht an der Variablen rumgefummelt - wodurch sollte sie sich verändern?