IRQ non générées par débordement du TIMER1

Travaillant sur un projet de multimètre polyvalent à base d’Arduino, je bute depuis pas mal d’heures sur un problème épineux concernant l’utilisation du TIMER1. Aucun problème pour le faire compter avec l’entrée binaire A5, et ce sur front montant ou descendant avec un programme « brut » où il recycle à zéro chaque fois qu’il dépasse 65535 impulsions de plus.
Mais quand je veux utiliser une plage plus importante, la technique expliquée une kyrielle de fois sur Internet ne fonctionne pas parfaitement.
Cette technique consiste à créer un compteur de débordement. Ce compteur est ensuite utilisé pour avoir la valeur totale d’impulsions détectées.
Ce Compteur est incrémenté par une routine d’interruption générée par TIMER1.
Ben pas moyen. Il y a bien de temps en temps une interruptions, mais pas systématiquement. Pour tester mon programme je peux injecter sur A5 des signaux lents de type 500Hz jusqu’à des fréquences de 4MHz.
Voici mon programme de test :

(#)
/* Programme de comptage avec prise en compte des débordements.
   Dans cette version le compteur est forcé à zéro à 9.999.999.
   Cette valeur anticipe un futur affichage sur LCD formaté avec points décimaux. */

#define Entree_Compteur 5 // Entrée D5 pour servira aussi pour mesurer les fréquences.

// Variables du programme.
byte Nb_IRQ; // Le COMPTEUR sera bloqué à 152 Interruptions.
unsigned long Nb_debordements; // Nombre de débordements du compteur durant le comptage.
unsigned long COMPTEUR, Ancienne_Valeur;

ISR(TIMER1_OVF_vect) {Nb_IRQ++;} //Routine d'interruption sur débordement du TIMER1.

void setup() {
  Serial.begin(115200); pinMode(Entree_Compteur, INPUT);}

void loop() {Serial.println(); Serial.println("FONCTION COMPTEUR.");
  RAZ_COMPTEUR();
  TCCR1A = 0;   // Le Timer 1 est configuré en simple compteur.
  // 111 pour Source externe sur D5 et comptage sur le front montant :
  TCCR1B = (1<<CS12) | (1<<CS11) | (1<<CS10);
  // ============== Autoriser les interruptions sur débordement =================
  TIMSK1 |= (1<<TOIE1); NON OK
//  TIMSK1 = TIMSK1 |(1<<TOIE1); NON OK
//  TIMSK1 = 0x04; // Autre test également NON OK
//  TIMSK1 = 36; // Autre test également NON OK
//  TCCR1B = (1<<5) | (1<<TOIE1); // Autre test également NON OK  
  Boucle_INFINIE:
  //========================= Traiter le COMPTEUR ===============================
  // Ci-dessous une première lecture fait passer TCNT1H dans le TAMPON.
  COMPTEUR = TCNT1L;
  // Lecture des 16 bits dans la variable VALEUR.
  //================ Quatre écritures pour tester le "bug" à 32767 ==============
  COMPTEUR = (uint32_t)((COMPTEUR * 256) + TCNT1L); // Comptage > 32767 OK
//  COMPTEUR = (TCNT1H << 8)|TCNT1L; // NON OK 
//  COMPTEUR = (uint32_t)(TCNT1H << 8)|TCNT1L; // NON OK
//  COMPTEUR = (uint32_t)(TCNT1H << 8) + TCNT1L; // NON OK
  // ================= Ajouter le débordement au COMPTEUR =======================
  Nb_debordements = 65536 * Nb_IRQ;
  COMPTEUR = COMPTEUR + Nb_debordements; // Ajoute le DEBORDEMENT à VALEUR.
  if (COMPTEUR > 9999999) COMPTEUR = 9999999;
  // ============================================================================
  if(COMPTEUR != Ancienne_Valeur) {
  Serial.print("Nb IRQ = "); Serial.print(Nb_IRQ);
  Serial.print(" CMPT = "); Serial.println(COMPTEUR); Ancienne_Valeur = COMPTEUR;}
  goto Boucle_INFINIE; }
  
void RAZ_COMPTEUR() {
  COMPTEUR = 0; Ancienne_Valeur = 1; // Pour forcer l'affichage; 
  // Replacer le compteur à zéro pour une nouvelle mesure.
  // Attention en écriture il faut commencer par les poids forts pour charger le TAMPON.
  TCNT1 = 0x0000; // Autre forme d'écriture pour remettre le TIMER1 à zéro
                  // TCNT1 = 0; équivalent.
  Nb_debordements = 0; Nb_IRQ=0;}

Merci d’avance pour votre aide les copains : Nulentout.

Déjà, il faut modifier la déclaration de Nb_IRQ. Il faut la déclarer comme volatil.

Dans cette ligne:

Nb_debordements = 65536 * Nb_IRQ

Nb_IRQ est un byte, 65536 (contrairement aux apparences) est un entier donc le résultat sera sur 16 bits ce qui n'est sans doute pas ce que tu souhaitais.

Il faudrait l'écrire comme ça:

Nb_debordements = 65536L * (unsigned long)Nb_IRQ

Merci pour cette réponse. Pour le calcul avec Nb_IRQ ça n'a jamais posé de problème, j'avais même effectué "un cast". Pour être certain, j'ai apporté les deux modifications à mon programme. Le problème de l'interruption n'est pas résolu, les débordement ne sont pas tous détectés.

A première vue :

1) Je pense que NbIRQ doit être volatile étant donné qu'il est écrit dans l'interruption et lu dans la boucle...

2) Ton paramétrage de registre devrait être dans le setup et pas dans la loop...

3) As-tu essayé le paramétrage suivant : TIMSK1 = 1;

Merci pout on aide UniseV.
J’ai passé NbIRQ en volatile et ça ne change rien au problème rencontré.
Oui, bien que non listé dans les essais donné plus haut, j’avais tenté TIMSK1 qui n’a pas
résolu non plus le problème.
Passer les paramétrages dans le SETUP n’est pas une option pour mon programme.
En effet, ce dernier présente plusieurs options pour lesquelles je n’utilise pas les TIMERS et IRQ de la
même façon.
Donc pour le moment j’ai toujours ce petit problème épineux qui be bloque. :fearful:
mais il y aura bien quelcun qui va trouver, je n’en doute pas. :wink:

Ok, je viens de comprendre le GOTO... mais je n'aime pas trop les GOTO.

Voici des bouts de code extraits d'une de mes librairie :

void PPMread::begin(unsigned char ecart)
{
  // Timer1 setup
  TCCR1A=B00000000; // Timer: normal mode
  TCCR1B=B00000010; // CAPTUREPIN Interruption sur front déscendant, Timer:normal mode, Prescaler=clk/8 
  TIMSK1=B00100001; // Interruption sur CAPTUREPIN & timer1 Overflow
  _ecart=ecart;
}

ISR(TIMER1_OVF_vect) // Le compteur a attends sa limite de 32ms (overflow)...
{
  endTrame=2;  // ...ce paramètre servira a la fonction read.
}

Cette librairie fonctionne bien et son overflow aussi...

EDIT : Dans ton code d'exemple, tu lis 2 fois TCNT1L et pas TCNT1H... volontaire ?

EDIT 2 : Grillé par haifger !

Bonjour,

nulentout: J'ai passé NbIRQ en volatile et ça ne change rien au problème rencontré.

Ça n'a rien changé parce que ce n'est pas le seul soucis, mais il n'en reste pas moins que dans ce cas précis il faut que NbIRQ soit déclarée volatile, cela fait partie de la solution.

Une autre partie du problème se trouve là :

  // Ci-dessous une première lecture fait passer TCNT1H dans le TAMPON.
  COMPTEUR = TCNT1L;
  // Lecture des 16 bits dans la variable VALEUR.
  //================ Quatre écritures pour tester le "bug" à 32767 ==============
  COMPTEUR = (uint32_t)((COMPTEUR * 256) + TCNT1L); // Comptage > 32767 OK

Ceci n'est pas logique, tu lis 2 fois de suite l'octet bas du compteur, mais jamais l'octet haut, le résultat final ne peut pas être correct. Ce que la datasheet dit concernant la lecture des registres 16 bits, c'est qu'il faut lire l'octet bas avant l'octet haut, mais pas lire le même 2 fois de suite. De plus ceci n'a de réel intérêt quasiment que lorsque l'on fait de l'assembleur ; en C c'est le compilateur qui fait le sale boulot, il suffit donc d'écrire :

COMPTEUR = TCNT1;

Merci Unisev, Merci haifger, Bon, j'ai donc deux pistes sur lesquelles je vais me focaliser.

Oui, le GOTO a été pas mal décrié ... at souvent à raison. Comme il permet au programmeur de faire n'importe quoi, c'est la raison pour laquelle il était exclus de langage comme PASCAL par exemple. Personnellement je ne m'en sert que dans des cas similaires à celui-ci et uniquement POUR AMELIORER LA LISIBILITE. En effet, l'étiquette INFINI est pour moi plus parlante qu'un while(true), mais c'est une question totalement personnelle. heureux seront les programmeurs qui n'utiliseront jamais le GOTO. :grinning:

OK pour déclarer la variable en volatile. Même si en apparence ça ne change rien dans ce cas précis, ça présente l'avantage de la classer dans les données manipulées par interruption, c'est autant de gagné pour la lisibilité. J'avais donc inséré ce complément dans toutes mes versions en cours d'expérimentation.

Pour ce qui est de n'additionner que les poids faibles, c'est effectivement pas très logique. Je vais donc revoir ma copie. je vous tiens au courant pour les résultats obtenus.

Encore merci pour vos aides, c'est toujours très instructif.

nulentout:
ça présente l’avantage de la classer dans les données manipulées par interruption

Ce n’est pas le sens de volatil en C.
Ce modificateur de type indique simplement au compilateur que la variable en question peut changer à n’importe quel moment ce qui a pour conséquence t’interdire les optimisations sur cette variable et de la relire systématiquement à chaque manipulation au lieu de la laisser dans un registre entre 2 accès consécutifs lorsque le programme ne modifie pas celle-ci explicitement.
Ce n’est pas trop le cas avec Arduino qui n’a pas de bus externe mais, par exemple, on utilise volatil lorsqu’on accède à des zones mémoire partagées, on lorsqu’on relit des registres I/O sur un bus.

Rebonjour les copains, Merci fdufnews pour cette précision. C'est bon, mon programme fonctionne correctement. En réalité, le problème venais du manque de logique quand je ne chargeait pas le poids fort. Ceci dit, que le comptage soit faux c'est assez compréhensible, mais en revanche je ne vois pas en quoi cette erreur provoquait des pertes d'IRQ. Je reste donc un peu dubitatif. Quoi qu'il en soit, je suis positivement ravi que ce problème soit résolu car ça fait des heures que je tournicotait sans trouver. Donc ENCORE MERCI les copains, je vais en profiter pour vous augmenter vos Karmas. ;) Amicalement : Nulentout.