Créer 2 interruptions par Timer

Bonjour,
Je souhaiterai faire un deuxième interruption par Timer avec un ARDUINO UNO. Par exemple le premier Timer me permet d'acquérir les mesures toutes les secondes (1s) et par exemple toutes les 2 secondes j'enregistre ces valeurs d'acquisition (après conversion et éventuellement des calculs de spectres) dans une carte mémoire par exemple. C'est pour un nouveau projet pour l'instant rien n'est figé au point de vue matériel (µP ou DSP), comme j'ai un Arduino sous la main et je voulais savoir s'il est possible de créer 2 interruptions par Timer. Je cherche des infos (documentations, des conseils ou mieux des exemples concrets) Merci par avance de votre aide.

PS : Il y avait longtemps je faisais du µP avec le HC12 et je débute sur l'Arduino

Voilà un lien qui explique à peu près tout sur les timers: https://www.gammon.com.au/timers

Mais pourquoi ne pas simplement incrémenter une variable, et quand cette variable atteint la valeur désirée, la remettre à 0 et sauvegarder dans la carte mémoire..? :slight_smile:

Il existe aussi une méthode plus simple que d'utiliser un timer, c'est la fonction millis()...

D’accord avec @guix, pour des périodes de l’ordre de la seconde, le timer c’est vraiment l’artillerie lourde et se compliquer la vie à gérer des interruptions et sections critiques. Utilise millis() dans la loop est largement suffisant si la loop est non bloquante.

Bonjour,
Merci pour votre réponses j'ai parcouru sur ce lien 'https://www.gammon.com.au/timers' je ne trouve pas le fonctionnement avec 2 timers simultanés (ou peut-être que j'a mal vu). Je cherche à un fonctionnement avec 2 Timers avec la fonction millis() peut-être que cela fonctionne mais ce qui m'intéresse c'est vraiment les interruptions avec 2 Timers. Merci par avance de votre conseils.

Avec une Uno, seul le timer1 permet une interruption jusqu'à 1 ou 2 secondes.

Il est possible d'utiliser un seul timer pour avoir plusieurs fonctions d'interruptions, mais cela se complique un peu car il n'y a qu'un seul compteur. Dans le cas ou l'on veut deux interruption, l'une à 1s et l'autre à 2s, la solution est plus simple, il suffit de demander ue interruption toutes les secondes, de traiter la première action et de traiter la deuxième une fois sur deux.

J'ai l'intention d'utiliser le timer 0 pour

  • faire une interruption toutes les 1024µs qui est utilisé par le système
  • faire une interruption toutes les 1000µs pour avoir un comptage à ma ms près (ma base de temps)
  • faire ue interruption toutes les Xms pour gérer des boutons ou des pas à pas.
    Quand j'ai la deuxième, je n'exécute le code de la troisième qu'une fois sur X.

Utilise millis() dans la loop est largement suffisant si la loop est non bloquante.

C'est le SI qui complique les choses. Il faut se compliquer pas mal la vie pour que:

  • le code principal ne soit pas bloquant
  • il faut appeler une fonction genre run() le plus souvent possible.

Je pense que quel que soit la durée passer par une interruption permet de simplifier largement le code principal. Par exemple dans mes codes, j'utilise un écran qui peut afficher une image, et cela dure quelques secondes. Je me vois mal réécrire la fonction d'affichage pour la scinder en bout de 10ms. Quand bien même je le ferai, les 1s seront alors justes à 10ms près.

DOY38:
Merci pour votre réponses j’ai parcouru sur ce lien ‘https://www.gammon.com.au/timers’ je ne trouve pas le fonctionnement avec 2 timers simultanés (ou peut-être que j’a mal vu). Je cherche à un fonctionnement avec 2 Timers avec la fonction millis() peut-être que cela fonctionne mais ce qui m’intéresse c’est vraiment les interruptions avec 2 Timers. Merci par avance de votre conseils.

Dans le lien cité tu as un exemple pour le timer2. En s’inspirant de cet exemple et de la datasheet de l’atmega328, tu peux faire la même chose pour le timer 1. Comme le dit vileroi, avec le timer2 tu ne peux pas atteindre 1 ou 2 secondes. Il faut donc un diviseur soft.
Ça donne ceci par exemple:

const byte pin1 = 8;
const byte pin2 = 9;

void setup()
{
  Serial.begin(115200);
  startTimer1();
  startTimer2();
}

void loop()
{
}

const unsigned periode2 = 10; // ms
void startTimer2() {
  pinMode(pin2, OUTPUT);

  cli();//stop interrupts

  // init interruption timer
  TCNT2  = 0;
  TCCR2A = (1 << WGM21);  // CTC mode
  TCCR2B = (1 << CS22) | (1 << CS21) | (1 << CS20);  // 1024 prescaler
  TIMSK2 |= (1 << OCIE2A);  // enable timer compare
  OCR2A = (F_CPU / 1000 / 1024) * periode2;
//  Serial.println((F_CPU / 1000 / 1024) * periode2);
//  Serial.println(OCR2A);

  sei();//allow interrupts
}

// it timer 2
ISR(TIMER2_COMPA_vect) {
  static int cpt=0;
  if (++cpt==2000/periode2)
  {
    // clignotement led 1/2 période 2s
    digitalWrite(pin2, !digitalRead(pin2));
    cpt=0;
  }
}



void startTimer1() {
  const unsigned periode1 = 1000; // ms

  pinMode (pin1, OUTPUT);
  cli();
  TCCR1A = 0;
  TCCR1B = _BV(WGM12) | _BV(CS12) | _BV(CS10); // horloge interne divisée par 1024
  TIMSK1 = _BV(OCIE1A); // it sur comparateur
  TCNT1 = 0;
  OCR1A = (F_CPU / 1000 / 1024) * periode1;
  sei();
}

ISR(TIMER1_COMPA_vect)
{
  // fait clignoter la led à 1/2 periode de 1s
  digitalWrite (pin1, !digitalRead (pin1)); // LED Toggle
}

Cependant je ne comprend pas ton obsession à vouloir utiliser les interruptions (sauf pour la beauté de la chose), d’autant plus que vouloir écrire sur une carte SD en interruption va à mon avis causer problème(s).

Bonjour Kamill,
Merci pour ce code, je vais l'essayer et regarder de près ce soir ou demain sur une carte Arduino Uno.
Kamill a dit :
'Cependant je ne comprend pas ton obsession à vouloir utiliser les interruptions (sauf pour la beauté de la chose)...'
D'abord c'est pour apprendre (ou revenir sur la programmation des µP) d'autres part peut-être que dans mon exemple il est mal situé mais dans le passé (17 ans) j'avais déjà travaillé sur plusieurs interruptions avec le HC12 et je sais que cela me sera utile un jour ou l'autre. Et pour finir la beauté de la chose… Bien sûr sinon ce n'est pas la peine de faire l'électronique.
Merci

d'autant plus que vouloir écrire sur une carte SD en interruption va à mon avis causer problème(s).

Oui mais on peut facilement les résoudre.
Si on a une interruption par seconde et que l'écriture ne dure pas une seconde, en réactivant les interruptions avant d'écrire sur la SD, il n'y a plus de problèmes.

hello
code à tester
une contrainte: OCR1B doit etre inférieur à OCR1A

void setup()
{
  Serial.begin(115200);
  startTimer1();
}

void loop()
{
}

void startTimer1() {
  const unsigned periodeA = 2000; // ms  OCR1A doit etre sup à OCR1B
  const unsigned periodeB = 1000; // ms OCR1B doit etre inf à OCR1A
  cli();
  TCCR1A = 0;
  TCCR1B = _BV(WGM12) | _BV(CS12) | _BV(CS10);
  TIMSK1 = _BV(OCIE1A) | _BV(OCIE1B);
  TCNT1 = 0;
  OCR1A = (((F_CPU / 1000 / 1024)) * periodeA);
  OCR1B = ((F_CPU / 1000 / 1024) * periodeB);
  sei();
}

ISR(TIMER1_COMPA_vect)
{
  Serial.println("A");
}

ISR(TIMER1_COMPB_vect)
{
  Serial.print("B");
}

C'était une bonne idée, mais ça ne fonctionnera pas.
La période B va être égale à la période A, simplement B sera décalé par rapport à A.

hello Kamill
pas exactement, avec A=4000 et B=500
effectivement nous n'avons pas 8 interruptions B pour 1 interruption A

mais nous avons une B, puis 4 secondes après une A puis 0.5 seconde après B puis apres 4 secondes, à nouveau une A
voir copie d'écran jointe

je ne croyais pas cela possible, c'est en testant quee je suis tombé sur ce cas de figure

void setup()
{
  Serial.begin(115200);
  startTimer1();
}

void loop()
{
}

void startTimer1() {
  const unsigned periodeA = 4000; // ms  OCR1A doit etre sup à OCR1B
  const unsigned periodeB = 500; // ms OCR1B doit etre inf à OCR1A
  cli();
  TCCR1A = 0;
  TCCR1B = _BV(WGM12) | _BV(CS12) | _BV(CS10);
  TIMSK1 = _BV(OCIE1A) | _BV(OCIE1B);
  TCNT1 = 0;
  OCR1A = (((F_CPU / 1000 / 1024)) * periodeA);
  OCR1B = ((F_CPU / 1000 / 1024) * periodeB);
  sei();
}

ISR(TIMER1_COMPA_vect)
{
  Serial.println("A");
}

ISR(TIMER1_COMPB_vect)
{
  Serial.println("B");
}

dfgh:
mais nous avons une B, puis 4 secondes après une A puis 0.5 seconde après B puis apres 4 secondes, à nouveau une A

C'est exactement ce que j'ai dit

La période B va être égale à la période A, simplement B sera décalé par rapport à A.

Sur ton enregistrement on voit bien que A et B ont la même période
Période A: 48.059-44.212=3.847
Période B: 48.561-44.710=3.851

allez, un dernier post, les interruptions comme je pense, notre ami les souhaitait

volatile byte compteur=0;
void setup()
{
  Serial.begin(115200);
  startTimer1();
}

void loop()
{
}

void startTimer1() 
{
  const unsigned periodeA = 1000; // ms  OCR1A doit etre sup à OCR1B
  const unsigned periodeB = 1000; // ms OCR1B doit etre inf à OCR1A
  cli();
  TCCR1A = 0;
  TCCR1B = _BV(WGM12) | _BV(CS12) | _BV(CS10);
  TIMSK1 = _BV(OCIE1A) | _BV(OCIE1B);
  TCNT1 = 0;
  OCR1A = (((F_CPU / 1000 / 1024)) * periodeA);
  OCR1B = ((F_CPU / 1000 / 1024) * periodeB);
  sei();
}

ISR(TIMER1_COMPA_vect)
{
  Serial.println("A");
}

ISR(TIMER1_COMPB_vect)
{
  if (compteur++%2){Serial.println("B");compteur=0;}
}

timer_2inter.png

timer_2inter.png

Oui, mais là tu triches. :slight_smile: On peut simplement faire

ISR(TIMER1_COMPA_vect)
{
  Serial.println("A");
  if (compteur++%2){Serial.println("B");compteur=0;}
}

De plus 1s et 2s c'est simplement un exemple. Les périodes ne sont pas forcément multiples.

Bonsoir,
Merci pour vos réponses mais là je suis un peu largué….C’est un peu trop compliqué pour moi (pour l’instant). Finalement j’ai une Arduino leonardo et non Uno comme j’avais annoncé au début. Je pense que cela ne change pas au niveau des Timers. Je peux fermer ce topic car j’ai suffisamment d’éléments pour travailler. Merci à vous.

Ce ne sont pas les mêmes timers sur la leonardo (ATmega32u4)

Du coup cela ne vous dérange pas de me faire le code comme 'dfgh' l'avait pour les 2 Timers mais pour une LEONARDO (je ne sais même pas s'il possède 2 Tmers ce µP ?)

je viens de jeter un oeilsur l data sheet du 32u4 ICI

il semble bien que le prog soit compatible

tu peux faire un essai ?

Bonjour dfgh,

Bien sur, je vais faire un essai (pas maintenant) et je vous dirai. C'est vrai que j'abuse de votre gentillesse il faut que je travail aussi un peu, tout ne tombe pas comme ça !

Bonjour,
J'ai copier le code et compiler le code pas d'erreurs et quand je voulais téléchargé sur la cible je rencontre pas mal d'erreurs :
Arduino : 1.8.9 (Windows 10), Carte : "Arduino Leonardo"
Le croquis utilise 3908 octets (13%) de l'espace de stockage de programmes. Le maximum est de 28672 octets.
Les variables globales utilisent 155 octets (6%) de mémoire dynamique, ce qui laisse 2405 octets pour les variables locales. Le maximum est de 2560 octets.
processing.app.debug.RunnerException
at cc.arduino.packages.uploaders.SerialUploader.uploadUsingPreferences(SerialUploader.java:152)
at cc.arduino.UploaderUtils.upload(UploaderUtils.java:77)
at processing.app.SketchController.upload(SketchController.java:732)
at processing.app.SketchController.exportApplet(SketchController.java:703)
at processing.app.Editor$UploadHandler.run(Editor.java:2070)
at java.lang.Thread.run(Thread.java:748)
Caused by: processing.app.SerialException: Erreur de la commande « touch » sur le port série « COM5 ».
at processing.app.Serial.touchForCDCReset(Serial.java:107)
at cc.arduino.packages.uploaders.SerialUploader.uploadUsingPreferences(SerialUploader.java:136)
... 5 more
Caused by: jssc.SerialPortException: Port name - COM5; Method name - openPort(); Exception type - Port busy.
at jssc.SerialPort.openPort(SerialPort.java:164)
at processing.app.Serial.touchForCDCReset(Serial.java:101)
... 6 more
Une erreur est survenue lors du transfert du croquis
Ce rapport pourrait être plus détaillé avec
l'option "Afficher les résultats détaillés de la compilation"
activée dans Fichier -> Préférences.

Avez vous une idée d'où cela peut venir ? Merci par avance