[résolu] PCINT qui se déclenche toute seule en continu

Salut la communauté!

Ce post était au départ une question, mais en cours de route, j'ai résolu la chose. C'est donc devenu une info à prendre pour ceux qui auront ce genre de souci de PCINT.

Alors voilà, je parlais de gérer un clavier matriciel par les PCINT...
en gros, on a 8 fils du clavier que j'ai relié au port K du 2560 (entrées analogiques A8 à A16). la méthode est simple : les 4 lignes sur K3:0 et les 4 colonnes sur K7:4. on initialise le port K pour avoir les colonnes en sorties à 0, et les lignes en entrée avec pull-up. on active PCINT2 sur les lignes, il ne doit donc rien se passer tant qu'on n'appuie pas sur une touche, et on doit lire PINK = 0x0F. si on appuie sur une touche, cela mettra la ligne correspondante à 0, donc déclenchement de PCINT2.
Dans l'ISR (routine appelée par PCINT2), on relève donc les lignes, puis on inverse le sens entrées / sorties du port K ainsi que les pull-up, puis on relève les colonnes et on remet le port K comme à l'initialisation. on obtient alors un couple colonnes / ligne contenant un 0 sur la ligne et la colonne de la touche appuyée, des 1 partout ailleurs. Lorsqu'on relâche la touche, même interruption, mais on ne lit que des 1, puisqu'aucune touche n'est en contact.

Super!

Ben non, car ma PCINT2 se déclenche en continu, même en débranchant le clavier (c'est pour dire!). Vous remarquerez que j'utilise également les PCINT0 du port B pour détecter 5 boutons poussoirs, et là, ça marche impeccable!

volatile kb_key, btn_key;  // variables contenant l'état des clavier / boutons

void keyB_init(){
  DDRB &= 0xE0;  // B4:0 input : 5 boutons (pin 50, 51, 52, 53 et 10)
  DDRK = 0xF0;   // clavier : lignes en entrée, colonnes en sortie
  PORTB |= 0x1F;  // pull-up sur boutons
  PORTK = 0x0F;  // colonnes à 0 et pull-up clavier lignes
  PCMSK2 = 0x0F;  // active PCINT19:16 (lignes clavier)
  PCMSK0 = 0x1F;  // active PCINT4:0 (boutons poussoirs)
  PCICR = 0x05;  // active PCI2 et PCI0;
}

ISR(PCINT0_vect){  // interruption boutons
  btn_key = PINB & 0x1F;  // lecture des 5 boutons
  PORTA ^= 0x04;  // led rouge témoin de déclenchement de PCINT2
}

ISR(PCINT2_vect){  // interruption clavier
  PCICR &=0xFB; // désactive PCINT2
  kb_key = PINK & 0x0F;  // lecture lignes
  DDRK = 0x0F;  // inverse les I/O clavier : lignes en sortie et colonnes en entrée
  PORTK = 0xF0;  // lignes à 0 et pull-up sur colonnes 
  delay(1);
  kb_key += PINK & 0xF0;  // lecture colonnes
  DDRK = 0xF0;  // remet les lignes en entrée, colonnes en sortie
  PORTK = 0x0F;  // colonnes = 0 et pull-up sur lignes
  PCICR |=0x04; // réactive PCINT2

  PORTA ^= 0x02;  // led verte témoin de déclenchement de PCINT2
}

Il semblerait que mes déclenchements intempestifs et continus de PCINT2 soient dus à mes manips sur le port K dans l'ISR, pourtant, je désactive bien l'interruption PCINT2 avant de toucher à quoi que ce soit (sinon, l'interruption est prise en compte et sera exécutée dès la sortie de l'ISR)! je vais essayer en désactivant aussi les masques d'interruption dans PCMSK2.

L'ajout de la ligne

  PCMSK2 = 0;  // désactive toutes les PCINT23:16
(traitement isr)
  PCMSK2 = 0x0F;  // résactive les PCINT19:16

dans l'ISR ne change rien... ma led verte clignote toujours comme une tarée. pareil si juste avant de sortir de l'ISR je fais un "clear" du flag de PCINT2, ça ne change rien.

finalement, juste avant de réactiver les PCINT, j'ai ajouté un "delay(10);", et là, miracle, ça marche! en fait, la solution est bien là : il y a un délai entre un PORTK=qqchose et l'apparition de ce qqchose sur la pin du composant, ce qui fait que si on réactive trop tôt les INT, alors les pins du port apparaîtront après la réactivation du PCINT, donc la redéclenche... ma led verte s'allume au démarrage, car dans l'initialisation du port K, je n'attends pas assez avant d'activer la PCINT, elle est donc déclenchée une fois...

j'ai résolu mon pb...

Yep!

Normalement le delai d'ouverture/fermeture d'un port est d'un cycle avant lecture/utilisation sûre.

As-tu essayé de réduire ce temps de 10 ms ?

__asm__("nop\n\t"); // 1 cycle vierge.

@+

Zoroastre.

En effet, un nop pourrait prendre place pour remplacer le delay(1), mais j'ai mis un delay(100) à la fin pour jouer l'anti-rebond, car de temps en temps, l'int était appelée 3 fois à suivre (d'autant plus que j'ai fait un système de buffer avec un keyb_read(), kb_available... comme pour Serial, voir le topic sur la "librairie maison" où j'en ai mis un bout de code)