La soustraction de deux unsigned est un peu déroutante au début, mais une fois que l'on a compris que le résultat est un unsigned, c'est plus clair
J'ai déjà utilisé un truc du style:
const word MAX = 1000; // Inter active une fois sur 1000
word compteur; // Pas besoin de volatile si il n'est pas utilisé en dehors
ISR(TIMER?_?????_vect)
{
if (++compteur >= MAX) // Entre dans la suite au bour de 1000 appels
{
compteur = 0;
// Traitement de l'interruption, fait donc une fois sur 1000
...
}
}
Tant que le compteur n'atteint pas 1000, le traitement n'est pas fait.
Notes:
- Comme de toutes façon, il y a une division soft, il est même possible d'utiliser un timer 8 bits pour une durée quelconque.
- Pour économiser un timer, on peut utiliser le timer 0 qui en général ne peut pas servir à grand chose si on souhaite garder delay(), millis()... On ne peut pas utiliser TIMER0_OVF_vect qui est pris par l'horloge interne, mais TIMER0_COMPA_vect et TIMER0_COMPB_vect sont utilisables. Si on ne veut pas dérégler l'horloge interne, on a une interruption toutes les 1024µs, pour avoir une heure, il suffit de faire une division par 3515625 qui tient dans un long
- Rien n'empêche de déclarer compteur volatile et de s'en servir pour avoir les autres temps:
-- si le compteur est à 0, c'est que l'on est sur l'heure juste.
-- une minute après l'heure juste, le compteur atteint 58593 (à peu près)
-- deux minutes après c'est 117187....
Avec ce seul timer 0, on peut donc par interruption ou pas, en regardant la valeur de compteur, faire un traitement pour t=0, t=1mn, t=2mn... toutes les heures.
Attention cependant, le traitement se faisant dans la 1000ème interruption, vous êtes dans un contexte un peu particulier où les interruptions sont désactivées par défaut, millis() ne tourne plus etc... Tout cela reste gérable suivant les cas mais il faut le savoir et ça complexifie un peu tout
Dans le cas présent, déclenchement toutes les heures, l'approche avec millis() (ou si vous avez une RTC, genre DS3231, en utilisant les alarmes) sera sans doute largement suffisant
Dont si je comprends bien la réponse de vileroi : Pour conserver les fonctions delay(), millis()…Je peux utiliser le TIMER0 en comparateur A et B.
Je n'ai pas compris non plus (vraiment désolé de mon niveau) c'est 16Mhz l'horloge de l'Arduino Leonardo si on ne touche pas aux prédiviseur cela fait une interruption toutes les 62,5 ns il me semble non ?
Oui, on peut avoir trois fonctions d'interruptions en même temps. Bien sûr, sauf bricolage, toutes les trois fonctionnent avec la même périodicité. Mais cela permet de rajouter du code que l'on ne peut pas faire avec la fonction en place sans modifier la bibliothèque en place.
Si on ne veut pas dérégler l’horloge interne, on a une interruption toutes les 1024µs,
Lors de la mise en place de l'horloge système, il y a un choix sur le prédiviseur. Si on ne change pas, à la mise sous tension, il n'y a pas de prédiviseur et l'horloge des timers sont désactivées. Le choix du prédiviseur pour l'horloge système est de fournir une horloge à 4µs (division par 64). Et comme c'est un timer 8 bits, il va déborder au bout de 4µs x 256 soit 1024µs. C'est vrai pour une carte Uno, il se peut qu'il y ait pour la léonardo un multiple de 2 quelque part, mais le principe est le même.
De toute façons, on ne pourrait pas avoir une interruption toutes les 62ns car lors d'une interruption, il faut sauvegarder et restituer le contexte. Cela prend environ 4µs. Cela veut dire qu'une interruption qui ne ferait rien du tout et qui serait appelée toutes les 4µs ne laisserait plus de temps au programme principal. Si on écrit une fonction d'interruption très très courte, on ne peut guère l'appeler qu'au plus toutes les 10µs.
Merci vileroi pour l'explication je commence à mieux comprendre d'où ça sort les 4µs. Pour Arduino Leonardo on peut diviser par 8 soit 0,5µs c'est quand même rapide. Il y aussi clk/(no prescaling) mais je n'ai pas compris car je ne sais pas quel est la fréquence des Timer.
Dont si je comprends bien le raisonnement de vileroi la variable 'compteur' s'incrémente toutes les 1024µs et au bout de 1000 fois on fait quelque chose ici dans mon cas je vais effectuer l'acquisition des mesures via le C.A.N. Mais alors cela fait 1024µs.1000 = 1,024s je fais une acquisition (cela n'atteint quand même pas toutes les heures). Dont en admettant on choisit les coefficient qui vont bien, et un Timer sur 16 bits je vais atteindre toutes les heures, mais alors cela me dit toujours pas comment je vais acquérir en continue pendant 5 minutes via les C.A.N ? Je pense probablement utiliser un autre Timer pour cadencer le CAN à 5 minutes. Est ce que mon raisonnement est correct ? Ou bien j'utilise millis() ?
Si je choisi le Timer 1 qui est sur 16 bits
ISR(TIMER1_COMPA_vect)
Et pour 1 heure = 3 600 000 ms dont le coefficient à chercher est 3 600 000 000 µs/ 4µs = 900 000 000 fois = 60 000x15 000. Dont on peut charger dans le 'OCR1A' la valeur de 5536 au départ à l'initialisation. Dans ce cas on peut choisir un unsigned int pour la variable 'compteur'. Et la condition if (++compteur >= 15 000) {C.A.N}.
21:55:43.901 -> 4294963296
21:55:44.916 -> A
21:55:44.916 -> 4294963296
21:55:45.897 -> A
21:55:45.897 -> 4294963296
21:55:46.914 -> A
Effectivement le résultat n'est pas négatif mais alors on ne peut rien exploiter non plus du coup il me semble bien qu'il y a un overflow :
4294963296 = 1 0000 0000 0000 0000 0000 0000 0000 0000
Normalement sur un 32 bits on doit avoir au maximum = 4294967295
Vous utilisez millis pour déclencher l’acquisition toutes les heures et encore millis quand vous êtes en mode acquisition pour mesurer les 5 minutes
la structure du code serait comme cela (par exemple):
enum t_mode : uint8_t {DEMARRAGE, NOMINAL, ACQUISITION} etat = DEMARRAGE;
uint32_t chronoMode;
const uint32_t perdiodeAcquisition = 3600000UL; // 1h en ms
uint32_t chronoAcquisition;
const uint32_t dureeAcquisition = 300000UL; // 5 minutes en ms
void gestionMode()
{
switch (etat) {
case DEMARRAGE:
// écrire ici ce que vous voulez faire comme réglages au démarrage
// ...
Serial.println(F("SYSTEME PRET"));
// pour commencer par une phase d'acquisition
chronoAcquisition = millis();
etat = ACQUISITION;
// pour commencer par une phase nominale
// chronoMode = millis();
// etat = NOMINAL;
break;
case NOMINAL:
if (millis() - chronoMode >= perdiodeAcquisition) { // si le temps d'acquisition commence
Serial.println(F("DEBUT MODE ACQUISITION"));
chronoAcquisition = millis();
etat = ACQUISITION;
}
break;
case ACQUISITION:
if (millis() - chronoAcquisition >= dureeAcquisition) { // si le temps d'acquisition est terminé
Serial.println(F("FIN MODE ACQUISITION"));
chronoMode = millis();
etat = NOMINAL;
}
break;
}
}
void setup() {
Serial.begin(115200);
// .... votre setup hors réglages initiaux
}
void loop() {
gestionMode();
if (etat == ACQUISITION) {
// faire ici ce que vous voulez pendant la phase d'acquisition
}
// faire ici le reste de votre code qui fonctionne tout le temps
}
Bonjour Jackson,
Merci pour l'exemple de code que je vais regarder ce soir car je ne savais comment le faire.
En fait pas forcément besoin d'un timer 16 bits. Si pour 1s il faut le coefficient environ 1000, il suffit d'un coefficient 3600000 qui tient alors dans un long.
Par contre en utilisant millis() cela peut éviter pas mal de problèmes. Du plus simple au plus compliqué:
- delay()
- millis()
- les interruptions
Si delay() ne convient pas, utilise millis(). Si millis() ne convenait pas utilise alors les timers.
Dans ce cas on peut utiliser une base de temps de 64µs. 3 600 000 000µs / 64µs = 56 000 000
soit 865 fois 65000. OCR1A=65000, et compteur atteint 865.
Quand on calcule en info il y a très souvent des overflow. Mais cela ne veut pas dire que le calcul est faux ou inexploitable. Quand on fait
if (millis() - chronoMode >= perdiodeAcquisition)
Si millis() déborde, il est faux de 232 (en moins car il devrait valoir plus de 40000... et il vaut presque 0), mais le calcul de millis()-chronoMode est aussi faux (il devrait être négatif) déborde aussi. Il est faux de 232 (en plus car au lieu d'être négatif il est positif) et ces deux erreurs se compensent. De ce fait la comparaison est exacte.
Jackson c'est un titre d'honneur (comme une image que l'on donne à l’école primaire) pour ceux qui ont bien répondu. Comme ton image c'est "Basic"
D'abord je remercie beaucoup JML pour son temps. Mais ce que je ne comprends pas c'est dans le Main c'est du séquentiel du coup lorsque 1 heure est passée je rentre en mode acquisition qui doit durer (par exemple 5 minutes), je vais dans le C.A.N pour acquérir les mesures, cela prend du temps (du temps du conversion du CAN et le nombre de voies à acquérir) à chaque tour de boucle on rajoute du temps, en effet :
case ACQUISITION:
if (millis() - chronoAcquisition >= dureeAcquisition) { // si le temps d'acquisition est terminé
millis() s'incrémente uniquement quand on entre dans le prototype void gestionMode()
Peut-être que je me trompe complétement.
Ok, je n'avais pas pigé désolé JML.
Pas de souci, Jackson ça fait moderne
(sinon c’est Jean-Marc)
Comme on n’a qu’un seul processeur c’est toujours du séquentiel pour le code
Millis continue de s’incrémenter en tâche de fond (c’est justement géré par un timer et interruptions) donc du moment que vous en bloquez pas la boucle ça devrait le faire
Avant de clôturer ce topic, je tiens à vous remercier pour votre participations puisque maintenant j'ai un exemple de JML, je vais pouvoir méditer ce code.
This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.