Parlons Arduino > Composants

Interruption récalcitrante

(1/2) > >>

Tony04:
Bonjour à tous,
suivant ce forum depuis plusieurs mois et ayant réalisé pas mal de montages proposés (DCC, bus CAN, félicitations à leurs auteurs), j'espère trouver un membre qui pourrait m'expliquer un comportement très étrange d'un arduino Méga.
Dans un programme assez complexe je me retrouve devant un problème d'interruption qui refuse de fonctionner sauf en rajoutant une ligne comprenant un "Serial.println". Le programme est en PJ avec tous les commentaires qui pourraient être utiles.
Le câblage est le suivant:
1 led sur pin 8 vers +5V
1 bouton sur pin 5 vers masse
1 bouton pour simuler l'interruption sur pin 3 vers masse (avec résistance de rappel vers +5V)
Le but est de faire une action quelconque lorsque le compteur atteint une certaine valeur.
Il se trouve que l'interruption refuse de fonctionner (INT0 ou INT1) sauf si je rajoute la ligne 32 qui est:
Serial.println(cpt_M220V);   // ne fonctionne qu'avec cette ligne
Qu'ai loupé dans les explications sur les interruptions de Jean-Luc de décembre 2014 ?
Merci à tous ceux qui pourraient me trouver la solution.
Cordialement
Antoine

Thierry:
Bonsoir

Il y a quelque chose qui me choque dans ton code, c'est de mettre un While qui attend que la valeur soit arrivée à un seuil. Je pense que le mieux serait de te débrouiller pour que loop() soit relancé en permanence et juste avoir un test :


--- Code: --- ...
if (cpt_M220V < 105) { // attente incrément du compteur
//Serial.println(cpt_M220V); // ne fonctionne qu'avec cette ligne
monter();
}
...
--- Fin du code ---

Dans ce genre de situation, une machine à état est une bonne façon de simplifier le problème. Voir pour ça le dernier article de Jean-Luc (http://www.locoduino.org/spip.php?article25)...

Rob1:
Ne faut-il pas déclarer la variable comme volatil pour l'utiliser sous interruption ?

Roland

Thierry:
Oui, c'est aussi une possibilité.

Jean-Luc:
Bonjour,

Après examen rapide il y a deux problèmes :

L'absence de volatile sur une variable partagée entre le programme principal et l'ISR alors que le programme principal (loop) effectue un while (...) sur cette variable comme le souligne Rob1. Rien ne dit que le compilateur la relit de la mémoire et prend donc connaissance de la valeur mise à jour par l'ISR

Un problème plus subtil de concurrence sur l'accès à la variable. En effet, l'Arduino AVR est une machine 8 bits, donc qui effectue les calculs sur un octet alors qu'un int occupe deux octets. De plus l'AVR 8 bits est une machine RISC : les calculs et comparaisons se font dans des registres internes du microcontrôleur. Par conséquent, additionner 1 à une variable globale se fait en plusieurs instructions : copie de la mémoire vers un registre, ajout de 1 au registre, copie du registre vers la mémoire. Pour un int, on a : copie du premier octet dans un premier registre, copie du 2e octet dans un second registre, ajout de 1 avec retenue sur les deux registres, copie du premier registre dans le premier octet de la mémoire, copie du second registre dans le second octet de la mémoire. Maintenant que se passe-il si l'interruption survient alors que dans le programme principal, le premier octet a été copié mais pas encore le second ? Et bien la valeur lue dans le programme principal sera formée de la moitié de la donnée avant l'ajout de 1 et de la moitié de la donnée après l'ajout de 1. La donnée aura donc une valeur non prévue.

Il faut donc copier cette variable globale dans une variable locale avec les interruptions désactivées :


--- Code: ---while(1)
{
  noInterrupts();
  int localCopy = cpt_M220V;
  interrupts();
  if (localCopy >= 105) break;
  monter();
}

--- Fin du code ---

De cette manière la copie locale est une copie correcte.

Navigation

[0] Index des messages

[#] Page suivante

Utiliser la version classique