Salut Gérard,
Alors justement non, la variable
i n'appartient pas à la fonction
func, elle est globale car elle est déclarer et créer en dehors de toutes fonctions.
Ci dessous la variable
i est globale et sa portée atteint tout le fichier .c dans le quel elle est. Cette variable sera créée dans ce qu'on appelle le "tas" dans la RAM.
1 2 3 4 5 6 7
| int i;
void func(void) // fonction attendre
{
i = 0;
while (i == 0);
} |
Ci dessous la variable
i est locale et sa portée est limitée à la fonction
func. Cette variable sera créée dans la "pile" qui se trouve dans la RAM, au moment où on entre dans la fonction. Puis elle est détruite lorsqu'on sort de la fonction.
1 2 3 4 5
| void func(void) // fonction attendre
{
int i = 0;
while (i == 0);
} |
Ce qui se passe dans le code ci dessous est très subtile et on comprend le problème en regardant l'assembleur généré par le compilateur.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| int i; //variable globale
interrupt (TIMERA0_VECTOR) irq_routine(void) // a chaque débordement du TIMERA, disons toutes les secondes
{
i = 42;
}
void func(void) // fonction attendre
{
i = 0;
while (i == 0); // tant que i == 0 alors on tourne en rond
}
int main(void) // programme principal qui ne fait pas grand chose
{
func();
printf("si j'arrive ici, c'est que i ne vaut plus 0 et que je suis sortie de la function func()\n");
return 0;
} |
Si on le déroule on voit que le
main lance la fonction
func qui initialise la variable
i à 0 puis tant que cette dernière est à 0 on tourne en rond. Sans routine d'interruption on a simplement réussi a planter le micro ou plutôt à le faire entrer une boucle infinie. Pour être plus précis la variable
i va d'abord être mis dans un registre de travail pour être mis à jour avec la valeur 0 dans la RAM puis pour manipulation, donc ce registre sera mis à 0 (valeur de la variable
i) puis testé en boucle avec la valeur 0 dans une boucle via l'ALU (l'unité arithmétique et logique).
Ensuite arrive l'interruption du TIMERA, une seconde après le démarrage du micro. Le micro va donc empiler (stocker dans la pile qui se trouve dans la RAM) les registres de travail, le status register et le pointeur de pile, pour retrouver ces petits et recommencer à travailler là où il était une fois que le routine d'interruption sera terminée.
Le programme d'interruption va affecter la valeur 42 à la variable
i qui se trouve en RAM
Sortie de la fonction d'interruption, le micro va dépiler tout ce qu'il avait empiler avant d'être interrompu et le CPU va être rechargé avec les valeurs d'avant l'interruption pour reprendre où il était.
Malheureusement il était dans une boucle infinie qui ne fait que comparer un registre qui vaut 0 avec la valeur 0. Malgré que la variable
i vaut bien 42 en RAM pourquoi diable est ce que le CPU irait relire la variable
i en RAM ? Il ne fait que reprendre ce qu'il faisait. Et ce qu'il faisait c'est juste une comparaison dans le
while (i == 0);. D'ailleurs il n'y a pas d'affectation dans cette instruction donc pas d'excuse pour aller causer avec la RAM ou recharger un registre de travail.
Note qu'avec des variables locales, ce phénomène bien tordu n'arrive pas. Si on a besoin impérativement d'une variable globale, il faut spécifier avec GCC (compilateur de Arduino) :
volatile int i; pour forcer le compilateur a ajouter des instructions qui consistent justement à aller recharger le registre de travail avec la valeur dans la variable globale
i, à chaque manipilation de celle ci, moyennant une petite perte de temps d'accès à la RAM à chaque fois.
Je ne sais pas si c'était plus clair comme ça ?
A+
2 |
0 |