Auteur Sujet: Interruption récalcitrante  (Lu 161 fois)

Tony04

  • Newbie
  • *
  • Messages: 15
    • Voir le profil
Interruption récalcitrante
« le: mai 11, 2018, 05:57:26 pm »
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

  • Global Moderator
  • Sr. Member
  • *****
  • Messages: 380
    • Voir le profil
Re : Interruption récalcitrante
« Réponse #1 le: mai 11, 2018, 08:34:49 pm »
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 :

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

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

  • Newbie
  • *
  • Messages: 18
    • Voir le profil
Re : Interruption récalcitrante
« Réponse #2 le: mai 11, 2018, 08:55:11 pm »
Ne faut-il pas déclarer la variable comme volatil pour l'utiliser sous interruption ?

Roland

Thierry

  • Global Moderator
  • Sr. Member
  • *****
  • Messages: 380
    • Voir le profil
Re : Interruption récalcitrante
« Réponse #3 le: mai 11, 2018, 09:10:09 pm »
Oui, c'est aussi une possibilité.

Jean-Luc

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 1085
    • Voir le profil
Re : Interruption récalcitrante
« Réponse #4 le: mai 12, 2018, 10:50:00 am »
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 :

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

De cette manière la copie locale est une copie correcte.
« Modifié: mai 12, 2018, 10:52:00 am par Jean-Luc »

Tony04

  • Newbie
  • *
  • Messages: 15
    • Voir le profil
Re : Interruption récalcitrante
« Réponse #5 le: mai 12, 2018, 12:24:56 pm »
Ouahou, quelle réactivité sur ce forum.
Je savais bien que j'allais y trouver des pros  :D

Pour Thierry; bien sûr cette boucle telle qu'elle est n'est pas terrible mais dans mon programme elle fait plein d'autres choses.
Pour Roland; c'était bien l'instruction volatile qui manquait.
Et pour Jean-Luc; le second problème même si je n'ai pas réussi à le provoquer reste effectivement un problème et il faut donc le contourner avec la solution (très élégante) proposée.
En tous cas merci à vous trois même si le problème ne traitait pas du modélisme ferroviaire.
Bon WE à tous
Antoine

Tony04

  • Newbie
  • *
  • Messages: 15
    • Voir le profil
Re : Interruption récalcitrante
« Réponse #6 le: mai 12, 2018, 12:34:00 pm »
Un petit oubli,
du coup, dans mon programme d'origine, le fait de faire une Serial.println de la variable cpt_M220V dans la boucle while obligait, je suppose, le compilateur à relire la variable en mémoire et résolvait provisoirement mon problème.
Ai-je bien compris :-\ ?
Antoine

Jean-Luc

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 1085
    • Voir le profil
Re : Interruption récalcitrante
« Réponse #7 le: mai 12, 2018, 01:00:43 pm »
Probablement. Il faudrait regarder le code assembleur engendré pour comprendre.