Interruption et Serial --> besoin d'explication

Bonsoir !

J'ai lu dans la référence Arduino que déclencher des interruptions pouvaient engendrer des pertes de données dans le buffer serial. Or pour mon projet, j'ai besoin d'utiliser les deux en même temps, c'est à dire pouvoir déclencher une interruption à tout moment dans le programme tout en récupérant les données dans le buffer quand il y en a.

J'aimerais bien comprendre dans un premier temps pourquoi il y a des pertes et puis ensuite, comment faire en sorte d'utiliser les deux fonctions en même temps sans pertes de données ?

D'après ce que j'ai déjà compris, la lecture du serial déclencherait en fait déjà une interruption, et le fait de déclencher une autre interruption interrompt celle-ci. Je sais pas si c'est juste...

J'espère avoir été clair :stuck_out_tongue:

En général avec les interruptions il faut que le temps de traitement soit le plus court possible.
En effet si une autre interruption arrive est qu’elle est plus prioritaire que celle en cours elle va l’interromptre.
En soit c’est pas gênant la première finira son code après… sauf si entre temps une autre est arrivée et la tu en perds une

Donc ça confirme bien ce que j’ai lu :slight_smile:

" The serial data is captured using an interrupt to put each character into the serial input buffer. If you add any additional processing to that interrupt or trigger another interrupt then you may lose incoming characters because the arduino disables interrupts when it is in an interrupt handler. "

Alors comment faire en sorte de ne pas perdre de données quand on enclenche une interruption ? Utiliser un autre µC pour gérer exclusivement les interruptions ?

Dans toutes mes recherches sur internet, les gens ne parlent que du fait de pouvoir déclencher une interruption à chaque fois que des données arrivent dans le buffer. Mais personne ne parle du problème de pertes de données… Peut-être que ce n’est pas une contrainte si importante ?

Je ne sais pas comment gérer les priorités d'interruptions, mais j'utilise moi-même dans un de mes projets plusieurs interruptions :

  • Mes interruptions "personnelles" (7 toutes les 20ms), avec un code "léger" et des timestamp hard
  • La liaison série "DEBUG" qui utilise ces propres interruptions
  • Un liaison I2C qui utilise aussi ces propres interruptions

Voila comment je les utilisent :
Mes interruptions tournent continuellement en arrière-plan & utilise L'ICP1 de l'Atmega328P, donc un timestamp hard, si mon interruption est interrompue, ça ne change donc rien pour elle :wink:

La liaison série et la lisaison I2C ne sont jamais utilisées de façon concurrentielle, elle ne sont donc jamais lancées à l'interrieur des interruptions mais toujours directement dans la loop().

Je ne sais pas si ces orientations peuvent t'aider.

Bonjour,

kevo:
Alors comment faire en sorte de ne pas perdre de données quand on enclenche une interruption ? Utiliser un autre µC pour gérer exclusivement les interruptions ?

Dans toutes mes recherches sur internet, les gens ne parlent que du fait de pouvoir déclencher une interruption à chaque fois que des données arrivent dans le buffer. Mais personne ne parle du problème de pertes de données... Peut-être que ce n'est pas une contrainte si importante ?

Pas la peine de paniquer :wink:

A moins que ton interruption "externe" soit vraiment très gourmande en cpu (mais dans ce cas tu devrais envisager de revoir ton code) tu n'auras jamais aucune perte de données.
Le port série d'un ATmega328p est double bufferizé, il peut donc recevoir 2 octets avant de saturer.

Je ne sais pas ce que tu fais dans ton interruption mais pour tout ce qui est temps réel, la règle c'est d'en faire le moins possible dans l'interruption (ou dans le thread temps réel en cas de multitaches mais je sors du sujet arduino), et de faire les calcul lourds dans la loop.

Pour cela si tu as des calculs lourds à faire, il faut créer un buffer mémoire.
Dans l'interruption tu stokes simplement tes données acquises (en partant du principe que tu fais de l'acquisition), c'est très rapide.
Dans la loop tu lis les données dispo dans le buffer, et tu fais les calculs nécessaires.
S'ils prennent du temps c'est pas grave ils peuvent être interrompus sans soucis.
Il faut juste arriver à dimensionner le buffer correctement.

Bon ca c'est le principe, il n'y a pas un modèle unique mais il faut l'adapter à ce que tu veux faire.

Mon interruption consiste uniquement à changer 1 seule variable.. Rien de plus :grin:

Donc inutile de m'inquiéter alors, je me pose peut-être trop de questions :stuck_out_tongue:

Bonjour,

Idéalement il faudrait regarder le code compilé en assembleur pour mesurer le nombre de cycles des tes
différentes interruptions, ainsi tu peux t'assurer qu'il n'y a pas de risque.

Une technique un peu plus simple est de mesurer à l'oscilloscope, tu change l'état d'une pin avant et après le
code à mesurer.

Si il y a des risques tu peux peut-être réduire le débit de la communication série.

Mon interruption consiste uniquement à changer 1 seule variable.. Rien de plus

Si tu partage cette variable entre ton programme principal et l'interruption il faut être prudent. Particulièrement
si la variable fait plusieurs octets. Par exemple, le traitement de cette variable dans le programme principal peut se faire interrompre au moment ou un seul octet de celle-ci a été chargé dans un registre lors d'une opération. Dans
ces cas il faut couper brièvement les interruptions faire le traitement sur la variable puis remettre les interruptions.

marcha:
Idéalement il faudrait regarder le code compilé en assembleur pour mesurer le nombre de cycles des tes
différentes interruptions, ainsi tu peux t'assurer qu'il n'y a pas de risque.

Idéalement il faudrait faire des ISR(xxxxx_vect, ISR_NAKED) et mettre ton code assembleur fait à la main dedans :wink:
Mais ce serait pousser le truc dans des extrêmes injustifiés (sauf dans certain cas bien précis).

marcha:
Si tu partage cette variable entre ton programme principal et l'interruption il faut être prudent. Particulièrement
si la variable fait plusieurs octets. Par exemple, le traitement de cette variable dans le programme principal peut se faire interrompre au moment ou un seul octet de celle-ci a été chargé dans un registre lors d'une opération. Dans
ces cas il faut couper brièvement les interruptions faire le traitement sur la variable puis remettre les interruptions.

Solution :
attribut "volatile" sur la variable (empêche la "corruption" de la valeur par optimisation du compilateur) + accès atomiques en cas de variables sur plus d'un octet :

register uint8_t s = SREG;
cli();
maVariableLong = trucMachin;
SREG = s;