Question:
Quel est le temps maximum qu'une routine de service d'interruption peut prendre pour s'exécuter sur ATmega328P?
thebear8
2019-07-11 17:44:36 UTC
view on stackexchange narkive permalink

J'ai un ATmega328P qui vérifie si un bouton a été enfoncé via des interruptions de changement de broche.Maintenant, je veux allumer une LED pendant 200 ms. Puis-je simplement allumer la LED, attendre 200 ms et la désactiver dans l'ISR comme dans le code suivant?

  ISR (PCINT1_vect)
{
    si (PINB & 0b1)
    {
         PORT = 0b10;
         _delay_ms (200);
         PORT = 0;
    }
}
 

Dans quelques messages de forum sur AVR Freaks, j'ai lu que vous ne devriez pas passer beaucoup de temps dans un ISR, mais je n'ai jamais vu de chiffres exacts.Je ne trouve malheureusement plus ces messages, je ne peux donc pas les lier.Pour autant que je me souvienne, ils ont tous dit que si vous passiez trop de temps dans l'ISR, le microcontrôleur pourrait planter. Est-ce vrai?Et si tel est le cas, y a-t-il une limite de temps exacte après cela?

Cinq réponses:
Neil_UK
2019-07-11 17:52:02 UTC
view on stackexchange narkive permalink

Si rien d'autre n'est en cours d'exécution dans le MCU, alors vous êtes libre de prendre autant de temps que vous le souhaitez dans l'ISR. Mais , c'est une mauvaise habitude à prendre, et cela signifie que si vous voulez faire autre chose, vous devrez probablement retravailler le code.

Un cas particulier est si le MCU utilise une bibliothèque série, qui s'attend à ce que les interruptions fonctionnent suffisamment souvent pour traiter les caractères individuels reçus.À 115 200 bauds (vitesse série élevée souvent utilisée pour minimiser le temps de téléchargement), il y a moins de 100 µs entre les caractères.Si vous bloquez les interruptions plus longtemps que cela, vous risquez de perdre les caractères d'entrée.

En règle générale, faites le minimum absolu dans un ISR.Dans votre application, une conception raisonnable serait d'avoir une interruption toutes les ms, qui incrémente et vérifie une valeur de compteur.Je suis sûr que vous pouvez trouver une logique appropriée pour définir et tester le compteur pour obtenir 200 ms entre les événements d'activation et de désactivation.

Dave Tweed
2019-07-11 17:49:14 UTC
view on stackexchange narkive permalink

Dans le pire des cas, un ISR peut s'exécuter jusqu'à ce que la prochaine interruption du même type se reproduise.

Mais en général, passer plus de temps dans un ISR que nécessaire, car cela empêche tout autre code de s'exécuter.C'est un gros problème pour tout autre chose que les programmes triviaux.

Je dirais que dans le pire des cas, un ISR peut fonctionner indéfiniment, empêchant la prochaine IRQ de se reproduire.
@DmitryGrigoryev: Dans ce cas, je voulais dire "le pire des cas" en termes de NE PAS faire échouer le système immédiatement.
Brethlosze
2019-07-11 17:51:50 UTC
view on stackexchange narkive permalink

Bien que la pratique consiste à allouer le minimum de cycles d'exécution possibles à l'intérieur d'une interruption, et à côté d'autres spécifications matérielles générales, il n'y a pas de limitations techniques pour les augmenter, s'il n'y a pas d'autre interruption à exécuter.

Dans attachInterrupt () Arduino Reference:

En général, un ISR doit être aussi court et rapide que possible. Si ton sketch utilise plusieurs ISR, un seul peut être exécuté à la fois, l'autre les interruptions seront exécutées après la fin de celle en cours dans un ordre cela dépend de la priorité qu'ils ont. millis () repose sur les interruptions compter, donc il ne s'incrémentera jamais à l'intérieur d'un ISR. Depuis delay () nécessite des interruptions pour fonctionner, il ne fonctionnera pas s'il est appelé à l'intérieur d'un ISR. micros () fonctionne initialement mais commencera à se comporter de manière erratique après 1-2 SP. delayMicroseconds () n'utilise aucun compteur, donc il fonctionnera comme normal.

Ayant 25 interruptions possibles dans cette famille de processeurs, il est conseillé de les traiter comme des événements ponctuels, pour permettre à d'autres interruptions de se produire.

Alors que le retard dans un ISR est généralement une mauvaise idée, la question utilise ce qui semble être `_delay_ms () 'd'Apr-gcc qui est basé sur des cycles contrairement à la fonction Arduino` delay () `qui repose sur l'interruption du minuteur.
Dmitry Grigoryev
2019-07-12 13:45:51 UTC
view on stackexchange narkive permalink

Techniquement, il n'est même pas interdit de démarrer une boucle infinie à l'intérieur d'une interruption, il n'y a donc pas de limite supérieure sur le temps d'exécution d'ISR. Cependant, les interruptions sont plus utiles lorsque vous voulez, eh bien, interrompre votre déroulement normal du programme pour effectuer une action courte qui doit être effectuée immédiatement , et les qualificatifs «court» et «immédiatement» sont naturellement liés: si votre ISR le plus long prend 1 ms, alors une interruption entrante de même priorité aura un temps de réponse de 1 ms. Donc, par essence, votre temps d'exécution ISR est limité par le temps de réponse IRQ souhaité.

Si votre programme passe un long moment à attendre dans une interruption, il peut être plus facile de supprimer complètement les interruptions et d'utiliser l'interrogation, ce qui facilite considérablement la programmation.

Dans certains cas, vous pouvez abuser des interruptions pour exécuter du code plus ou moins régulier. Un exemple serait d'implémenter la priorité de tâche: une tâche de faible priorité est lancée à partir de la boucle principale, tandis qu'une tâche de priorité plus élevée est une interruption de temporisation déclenchée périodiquement. Cela se fait généralement sur les MCU avec plusieurs niveaux de priorité IRQ, afin que le système puisse toujours avoir des ISR réguliers en cas de besoin.

AaronD
2019-07-12 03:21:12 UTC
view on stackexchange narkive permalink

Vous disposez d'un processeur à un seul cœur et à un seul thread. Cela signifie qu'à tout moment, il fait exactement une chose. Si votre application l'exige pour faire plusieurs choses, le code doit être conçu pour basculer entre toutes. Cela peut être aussi simple ou complexe que vous le souhaitez.

Normalement, le CPU tourne autour de la boucle principale, en faisant tout ce qu'il y a dedans, et c'est très bien jusqu'à ce qu'une interruption se produise. Le matériel d'interruption force essentiellement un appel de fonction à l'ISR approprié, même s'il n'y a aucune instruction d'appel pour celui-ci dans la boucle principale. Cette imprévisibilité est la source de la plupart des règles d'écriture des ISR.

Quel que soit le temps que vous passez dans un ISR, c'est le temps que la boucle principale est mise en pause, en attendant le retour de l'ISR. Si la boucle principale est la seule à réinitialiser un minuteur de chien de garde actif (très bonne pratique), alors le chien de garde ne sera pas réinitialisé pendant ce temps. Si le chien de garde expire, il vous donne une réinitialisation matérielle. Tout comme la réinitialisation externe, mais avec différents indicateurs que vous pouvez vérifier au démarrage. C'est probablement le "crash" dont vous avez entendu parler.


C'est une très bonne pratique d'utiliser le chien de garde et de ne le réinitialiser qu'une fois à chaque trajet autour de la boucle principale. Cela vous oblige à écrire du code qui reste réactif. Si vous avez besoin d'attendre quelque chose, vous pouvez configurer un événement (minuterie terminée, prochain caractère reçu, etc.) et passer à autre chose. Vérifiez périodiquement cet événement ou définissez une autre interruption pour son achèvement, puis revenez-y. En attendant, vous continuez tout ce que vous faisiez.

Ma structure principale est typiquement quelque chose comme ceci:

  #include "module1.h"
#include "module2.h"

void main (void)
{
    //global
    //puce
    //installer

    mod1_init ();
    mod2_init ();

    // efface les drapeaux d'interruption
    // activation d'interruption globale

    tandis que (1)
    {
        // effacer le chien de garde

        mod1_run ();
        mod2_run ();
    }
}
 

Et mes modules sont comme ceci:

  void modX_init (void)
{
// hardware et variable init pour ce module uniquement
    // n'utilise pas d'interruptions si l'interrogation est assez bonne
}

void modX_run (void)
{
    si (POLLED_INTERRUPT_FLAG)
    {
        POLLED_INTERRUPT_FLAG = 0;

        // code "ISR" non bloquant
    }
}

void ISR modX_ISR (void)
{
    // d'accord, cela nécessite une réponse * immédiate *
    // passe le temps minimum absolu ici et sors
}
 

Les signatures de fonction n'ont pas besoin d'être void , mais la plupart le sont. Parfois, j'aurai un timing large dans un module qui est également utilisé par un autre, et il est pratique d'utiliser la valeur de retour d'un modX_run () et les arguments d'un autre (ou d'une logique de base) pour faire ce lien. Par exemple:

  if (DMX_run ()) // inclut son propre timing, et retourne true au début de chaque intervalle de 30Hz, sinon false
{
    I2C_start (); // Les trames I2C sont synchronisées avec DMX
}
I2C_run (); // une fois démarré, une trame I2C tourne librement jusqu'à la fin
 

Si vous étudiez la fiche technique, vous constaterez peut-être également que les périphériques matériels peuvent être massés pour faire ce que vous voulez sans aucune intervention du processeur.

La génération d'impulsions de sortie, par exemple, est courante. Allumez-le, réglez le périphérique pour qu'il s'éteigne plus tard et oubliez-le. C'est généralement dans le même domaine général que PWM.



Ce Q&R a été automatiquement traduit de la langue anglaise.Le contenu original est disponible sur stackexchange, que nous remercions pour la licence cc by-sa 4.0 sous laquelle il est distribué.
Loading...