comptage d'impultions, précision, traitement caché...

Bonjour à tous!

J'ai finalement décidé de m'y mettre, j'ai acheté 2 arduinos UNO et quelque accessoires pour bien débuter.

Je me suis donc lancé dans un projet qui m'a amené à jouer avec un arduino : un régulateur de vitesse pour ma R11 (ben quoi? elle monte très facilement au dessus de 150, et sur la route, c'est fatigant de gérer tout le temps l'accélérateur...) :~ .

Côté hardware :

un capteur "PMH" sur volant cranté du moteur adapté qui me délivre 40 impulsions par tour de moteur, remplacé par un GBF pour la phase mise-en-route.
un LCD 16 x 2 pour IHM
un clavier matriciel 5 x 4 (un clavier 16 touches + 4 poussoirs),
un servo pour actionner l'accélérateur.

Voilà mon montage :

Ne rentrons pas dans les détails de sécurité, pour l'instant, je souhaite juste gérer un asservissement de vitesse du moteur de la voiture. Les sécurités viendront après.

Mon gros problème :

1 - j'utilise les librairies "servo", "keypad", "liquidCrystal" et "MsTimer2", comptage par interruption externe 0 (broche 2) :
saisie au clavier d'un angle pour le servo et affichage du nombre d'impultions toutes les 202ms (meilleur rapport pour appliquer les facteurs de vitesse et tours moteur). Youpi, tout marche tout seul, mais :

  • les timers servo et MsTimer2 se bouffent, et le comptage d'impulsion n'est pas du tout stable et le servo a des ratés.
  • on est très loin de 202ms, plutôt dans les 180ms.

2 - OK, plan B : on oublie la lib "servo" (j'ai regardé le code, c'est bien trop "usine à gaz" pour ne gérer qu'un seul servo).

  • attachInterrupt pour incrémenter un compteur (compteurImpul) via impulsions,
  • Mstimer2 déclenche toutes les 20ms la routine suivante :
void traitement_timer(){  // proc appelée toutes les 20ms par le timer.
timerCount++;                          // on compte le nombre d'appel de cette routine
if (timerCount > 10) {               // on a appelé 10 x la routine (donc 200ms), alors on lit le compteur d'impulsions
  timerCount = 0;                  // RAZ compteur de routine
  nbImpul = compteurImpul;    // lecture compteur d'impulsions
  compteurImpul = 0;             // RAZ compteur d'impulsions
}
digitalWrite(servoPin, HIGH);       // Début imp servo
delayMicroseconds(servoValue);    // temps d’impulsion servo (entre 544 et 2400 µs)
digitalWrite(servoPin, LOW);       // fin imp servo
if (timerCount == 0) {             // on est dans le cas de la lecture compteur d'impulsions
  /*
      ici le code pour le traitement,
      et quelque calculs,
      affichage LCD du nbre d'impulsions etc etc
  */
}
}

C'est beau, relativement stable (+/- 1 imp, normal), je suis content, mais du coup, il ne me reste plus trop de temps pour le reste de mon programme, 10ms environ (la moitié des 20ms de périodicité).

Est-ce que certains d'entre vous on déjà joué dans cette configuration (utilisation de plusieurs timers)? ma solution ne me plait pas, j'aurais voulu continuer à jouer avec la solution 1...

Et grosse question : que se passe-t-il pendant une routine d'interruption timer et pendant un delayMicroseconds ou un delay? est-ce que mon compteur d'impulsions ne serait pas tout simplement désactivé le temps du traitement de la void ci-dessus? car il semble que quelques impulsions ne sont pas prises en compte, alors que pourtant il s'agit d'un beau signal carré 0-5V (de 500 à 5000Hz)???

Je trouve que la doc arduino reste très silencieuse là-dessus, comme si toutes les librairies étaient utilisables les unes avec les autres, sans aucune incompatibilité... je viens de voir que non, et je ne trouve aucune réponse à ce sujet...

Merci d'avoir lu ce... long message et merci à ceux qui auront des idées...

Super_Cinci:
Et grosse question : que se passe-t-il pendant une routine d'interruption timer et pendant un delayMicroseconds ou un delay? est-ce que mon compteur d'impulsions ne serait pas tout simplement désactivé le temps du traitement de la void ci-dessus?

Ça dépend.

Pour delay() et delayMicroseconds() vas voir le code dans ton fichier hardware/arduino/cores/arduino/wiring.c sous ton fichier Arduino de base. Tu y trouveras aussi la fonction d'interruption de Timer0 qui gère millis().

Pour les fonctions d'interruption, le comportement dépend de la définition. Tous les détails sont dans la documentation de avrlibc au sujet de avr/interrupt.h. Voir ici: avr-libc: <avr/interrupt.h>: Interrupts

Avec ces informations, tu pourras choisir le solution qui va mieux avec ton problème.

Korman

pour ma R11 (ben quoi? elle monte très facilement au dessus de 150

HS: c'est une R11 Turbo ?

HS: c'est une R11 Turbo ?

HS : non, un bon 1400 en forme avec encore tous ses 72 chx...

bon, j'ai trouvé, en mettant un mouchard (une impulsion générée par l'arduino pour synchroniser l'oscillo).

En fait, c'est MsTimer2 qui pose souci, car :

  • MsTimer2.cpp est rempli de routines qui s'appellent dans tous les sens alourdissent grave le traitement. Timer2 appelle l'ISR toutes les 1 ms, qui appelle une proc de compteur logiciel qui appelle ma void "traitement_timer".
  • le timer2 ne sert donc qu'à incrémenter un compteur logiciel toutes les 1 ms, et pendant le traitement de ma routine "traitement_timer", timer2 est désactivé, il faut donc compter 20ms + le temps de traitement de ma routine.

Je suis très déçu, car malgré la complexité du calcul du prescaler, on n'a absolument aucune précision. J'ai donc mis le nez dans la doc de AT328, et il va falloir que je crée moi-même mon "driver" de timer, ce qui a l'air assez simple au final, et permettrait d'avoir une fenêtre de comptage avec bien plus de précision que la simple milliseconde de MsTimer2...

Donc pour ceux qui veulent une IRQ timer précise, n'utilisez surtout pas MsTimer2!

D'où une seconde question : où puis-je trouver une doc qui explique le fond du fonctionnement de l'OS arduino et les ressources qu'il utilise, genre qui m'aiderait à savoir si je peux utiliser sans danger les 3 timers pour mon usage perso (dépendance sur les fonctions millis() et microSecond(), delay()...)?

Super_Cinci:
J'ai donc mis le nez dans la doc de AT328, et il va falloir que je crée moi-même mon "driver" de timer, ce qui a l'air assez simple au final, et permettrait d'avoir une fenêtre de comptage avec bien plus de précision que la simple milliseconde de MsTimer2...

Ça c'est la bonne chose à faire et le seul moyen de procéder.

Super_Cinci:
D'où une seconde question : où puis-je trouver une doc qui explique le fond du fonctionnement de l'OS arduino ...

L'Arduino n'a pas de OS, donc pas de doc. C'est qu'une collection de fonctions et librairies plus ou moins utiles de qualité très variable.

Super_Cinci:
Donc pour ceux qui veulent une IRQ timer précise, n'utilisez surtout pas MsTimer2!

Ce ne sera pas la première ni la dernière librairie bâclée. La structure du ATmega328 est telle, que beaucoup de choses sont en fait assez simple lorsqu'on a réussit de trouver les bon registres et le bon chapitre dans la doc de Atmel. Il ne faut seulement pas avoir peur de tripoter les registres en direct. Et comme il n'y a pas vraiment un OS, ton application est la seule chose à prendre en considération.

Super_Cinci:
... et les ressources qu'il utilise, genre qui m'aiderait à savoir si je peux utiliser sans danger les 3 timers pour mon usage perso (dépendance sur les fonctions millis() et microSecond(), delay()...)?

Malheureusement pas. Ça devrait faire partie de la doc, mais ça manque dans les plupart des cas. Il faut vérifier sois-même dans le code. Dans ce cas particulier, je peux te répondre.

L'Arduino n'utilise qu'un seul timer, c'est Timer0 auquel une fonction d'interruption est attachée qui gère le compteur pour millis(). Remplacer cette fonction est une mauvaise idée, car pleins de trucs ne vont plus marcher. AnalogWrite() pour les broches 5 et 6 (elles sont associées avec Timer0) fonctionne sans problèmes mais il faut faire attention de ne pas reprogramme le pre-scaler ou le mode d'une manière qui change la fréquence des interruptions de Timer0.

Timer1, le seul à 16 bit est libre, mais il est souvent utilisé par des librairies comme Servo. AnalogWrite() pour les broches 9 et 10 utilise aussi ce timer, ce qui est un source de problèmes commune, si on essaye d'utiliser les deux en même temps.

Timer2 est aussi libre et associé avec le PWM des broches 3 et 11. Il ne faut que vérifier les librairies qu'on utilise.

Peut-être ça t'aide un peu.

Korman

oui, pardon, dans le terme "OS", il fallait comprendre "boot loader"... et aussi les 450 octets qui sont rajoutés au moindre programme. Ensuite, je crois que les librairies, je vais éviter au maximum si je ne les déchiffre pas avant.

J'ai pris note du fonctionnement du timer2, je vais regarder le timer1 qui semble du même acabit en 8 bits, mais tu sembles dire que l'on ne devrait pas toucher au prescaler (commun au 1 et au 0)... Dommage, à moins de tout faire soi-même, pourtant certaines fonctions d'origine risquent d'être bien pratiques...

je mettrais bien "résolu", mais j'ai encore un petit souci...

Alors mon comptage est enfin extrêmement précis, j'utilise donc le timer1 pour créer une fenêtre de comptage de 202.45ms (autant être précis), fenêtre qui s'ouvre sur une impulsion du compteur d'événement, et à l'oscillo, mes impulsions sont parfaitement synchronisées avec la fenêtre, c'est magnifique. Il restait un flottement de +/- une demi impulsion, résolu par une tolérance qui ne prend en compte la nouvelle mesure que si elle a au moins 2 impulsions de différence avec la précédente. Mon affichage devient donc très fluide et stable!

Ca, c'est fait!

On a donc le timer1 qui gère la fenêtre de comptage avec l'INT0 qui compte, le traitement de base (calcul de la vitesse du moteur, application des facteurs et affichage des mesures sur LCD) ne prend que 2 à 3ms, il m'en reste 200 pour tout le reste de mon programme, ce qui devrait être suffisant!

La gestion du servo, maintenant... j'ai voulu prendre le timer2, mais hélas, je fais un reset du prescaler (reset général des timers en fait) pour synchroniser mon comptage, alors ce timer devient inutilisable s'il n'est pas synchronisé avec les autres, et là, j'ai encore quelques cheveux à m'arracher... dommage, car ça avait l'air de bien se présenter sur le papier... j'envoie au servo un angle égal à la vitesse affichée (de 20 à 180Km/h, parfait), ce qu'il reçoit, mais il a la tremblotte car l'info est grandement perturbée par mes reset de timer...

Je crois qu'au final, c'est le timer1 qui gèrera le tout, puisque je n'utilise pas encore le comparateur B qui lui pourra être dans les 15 à 20ms de la période que demande le servo... Mais c'est pas gagné, ça ne fait que 10 jours que j'ai reçu l'ARDUINO, et ça me rappelle quand on jouait avec un 68HC11 en TS, il y a 15 ans...

Ou alors un simple PIC en comm série qui s'occupe de mon servo? mais j'ai peur des PICs, ça n'existait pas il y a 15 ans...

arduino, ça n'existait pas non plus y'a 15 ans :wink:

EUREKA! ça y est, youpi, cool! j'ai réussi. il suffisait de coller le début de l'impulsion servo avant l'appel de l'ISR timer qui elle, arrête l'impulsion...

en gros :

timer1 en mode double comparateur,
comparateur A à 15ms
comparateur B à 15ms - valeur à envoyer au servo.

ISR (comparateur B) {
  servo = HIGH;
}

ISR (comparateurA) {
  servo = LOW;
  timer_count++;
  if (timer_count > 9) {
    timer_count = 0;
    désactiver le compteur de vitesse;
    désactiver timer1;
    // ici le traitement et calcul et affichage du régulateur
    réactiver et reset compteur de vitesse;
    réactiver et reset timer1;
  }
}

A l'oscillo, j'ai toujours mon impultion toutes les 15ms, sauf que toutes les 10 impulsions, il y a 2.2ms de plus entre les impulsions, le temps de faire le traitement de calcul et affichage, puisque le timer est en pause pendant ce temps-là...

le servo est stable, mais je crois quand même qu'il n'aime pas trop les impulsions pas régulières... (je croyais que ça lui serait égal...). Mais voilà, on ne peut pas intervenir sur les registres de timer si on est dans une ISR, ça fait tout planter, donc je ne peux pas réinitialiser mon timer au début de l'ISR(comparateur A) pour tenir mes 15ms...

résolu parce que je me suis empiffré les pages des timers de la datasheet de l'arduino (85 pages), mais je sais pas si quelqu'un qui tombe sur ce pb comprendra tout... qu'il me contacte par mon profil si besoin!