0
votes

Interruption de front montant se déclenchant plusieurs fois sur STM32 Nucleo

J'utilise la carte microcontrôleur STM32 NUCLEO-F401RE.

J'ai un haut-parleur programmé pour changer la fréquence d'une quantité définie lorsque le joystick est poussé vers le haut / bas. Mon problème est que parfois (le plus souvent), lorsque le joystick est poussé vers le haut / bas, la fréquence augmente / diminue plusieurs fois, ce qui implique que l'ISR s'exécute plusieurs fois. En outre, l'objet InterruptIn est configuré pour se déclencher sur le front montant, mais parfois il s'exécute également sur le front descendant (lorsque le joystick revient au neutre après avoir poussé vers le haut / vers le bas). Une aide pour surmonter ça?

int main()
{
    InterruptIn up(A2);
    InterruptIn down(A3);
    InterruptIn fire(D4);

    up.rise(&upISR);
    down.rise(&downISR);
    fire.rise(&toggleISR);

    redLED.on();

    while (1){}
}

'

void upISR()
{
    if (greenLED.getStatus())
    {
        myTicker.detach();
        frequency+=200;   
        myTicker.attach(callback(&spkr, &Speaker::toggle), 0.5/frequency); 
    }
}


9 commentaires

C'est un problème classique, lisez à propos de "Debouncing" (debouncing switch, bouton debouncing)


@BenVoigt Une grande partie de ce que j'ai lu décrit parfaitement le problème, pour ne présenter que des solutions logicielles terriblement mauvaises. Rares sont les commutateurs qui déclenchent directement des interruptions, avec des solutions inappropriées pour une utilisation dans un contexte d'interruption. Comment le PO est-il censé trier le bon du mauvais conseil (ou ce qui peut ou non être applicable à cette situation)? Mieux vaut peut-être poster une réponse ici et laisser sa qualité être jauchée par la communauté - en ne comptant que les votes légaux bien sûr ;-).


@Clifford: Parce que si "terriblement mauvais" n'est bon pour aucune situation, une "bonne" solution dans une situation ne résout pas toutes les situations. Je ne pourrais pas donner une réponse complète sur la base uniquement des informations fournies par OP dans la question ... il serait également utile d'avoir des traces d'oscilloscope et un schéma de la connexion entre le commutateur et le MCU.


vous ne voulez généralement pas interrompre les changements d'état du commutateur, cela fonctionne rarement. une option est une interruption basée sur une minuterie et échantillonnez périodiquement le commutateur et prenez la valeur que vous obtenez et utilisez-la. Un autre est un filtre qui, fondamentalement, sous une forme ou une autre, vous suréchantillonnez l'état du commutateur et recherchez X dans une rangée à un niveau ou appliquez-y des mathématiques pour supprimer les problèmes, 15 sur 16 sont au même niveau, etc. mis au milieu à chaque fois que vous recommencez à échantillonner s'il atteint un seuil, il y avait assez de uns ou de zéros, etc ...


c'est simple car vous avez un état normal (non poussé) et un état poussé donc vous recherchez qu'il soit dans l'état poussé mais pas tous les problèmes, alors vous devez décider de forcer un état normal ou ai-je une fonction de pression et de maintien et si oui, comment définir / échantillonner pour cela ...


les boutons sont plus compliqués que les interruptions. vous pouvez également appliquer du matériel (analogique ou numérique, mais en pensant à l'analogique ici), un RC ou un autre filtre passe-bas analogique, mais cela entraîne un retard qui convient probablement à la vitesse manuelle humaine. (le suréchantillonnage numérique entraîne également un retard, tout filtrage le fait).


Merci pour l'aide de tout le monde. @old_timer ce programme faisait partie d'un exercice d'interruptions pour lequel nous n'avons pas été autorisés à utiliser le sondage, mais sinon vos suggestions sonnent toutes bien et je les noterai pour la prochaine fois. J'ai commenté la façon dont j'ai corrigé le code de la réponse.


Une meilleure solution implique toujours des interruptions, mais pas en fonction des changements d'état de l'entrée du commutateur. Pour éviter que vous ayez toujours besoin d'informations sur le changement d'état par heure, soit l'échantillonnage d'une minuterie dans l'interruption de changement d'état de la broche ou quelque chose comme ça et le problème qui se pose une fois que le changement d'état a cessé de rebondir, vous n'avez plus d'interruptions pour déclarer qu'il a été stable pendant tant de millisecondes, donc je peux l'appeler une pression sur un bouton ....


Que ce soit la réponse de Clifford ou autre, pensez-y en fonction du temps, une lunette aide vraiment si elle est assez rapide (et ne finit pas par filtrer le rebond). Ensuite, réfléchissez à ce que ma puce peut réellement interrompre à chacun de ces changements d'état (dans certains cas, vous êtes sous-échantillonné simplement en raison du fonctionnement de votre système d'interruption et du temps d'interruption). Même sans portée, vous pouvez visualiser cela. Une fois que le commutateur est assez mauvais, deux est bien pire, d'autant plus que vous voulez contrôler un élément avec deux commutateurs.


3 Réponses :


1
votes

Le rebond mécanique des commutateurs est une caractéristique de tous les commutateurs mécaniques dans une moindre ou plus grande mesure. Il est souvent nécessaire de mettre en œuvre un «anti-rebond» dans le logiciel surtout si le commutateur pilote directement une interruption comme dans ce cas.

Une recherche rapide sur Google des techniques de dénonciation des logiciels donne des techniques plutôt médiocres de l'OMI. Je l'ai vu mal plus que bien malheureusement.

Je suggère que dans le commutateur ISR vous démarrez (ou redémarrez en cas de "rebond") une minuterie matérielle pendant une période de disons 20 ms environ (plus longue que le temps de rebond du commutateur, mais plus courte que le temps que vous pourriez peut-être réellement relâchez l'interrupteur). Ensuite, dans la minuterie ISR, vous testez l'état du commutateur et changez la fréquence en conséquence:

Pseudocode:

void debounceTimerISR()
{
    debounceTimerStop() ;

    static tDirection previous_dir = CENTRE ;
    tDirection dir = getJoystickDir() ;

    // If the state changed...
    if( previous_dir != dir )
    {
        previous_dir = dir ;

        switch( dir )
        {
            case UP :
            {
                increaseFrquency() ;
            }
            break ;

            case DN :
            {
                decreaseFrquency() ;
            }
            break ;
        }
    }
}

Ce que cela fait, c'est déclencher une interruption de la minuterie peu de temps ("temps anti-rebond") après que le commutateur cesse de rebondir. Notez que le chronomètre n'est pas périodique.

Ci-dessous, je présente une amélioration à la suggestion de @ BenVoigt (dans les commentaires). Je le garde séparément pour qu'il soit clair que c'était son travail. Ce qui précède fonctionnera généralement, mais si vous avez un commutateur particulièrement médiocre, ce qui suit résoudra les problèmes, et à peu de frais, vous pouvez donc aussi:

void upISR()
{
    debounceTimerRestart() ;
}

void downISR()
{
    debounceTimerRestart() ;
}

void debounceTimerISR()
{
    debounceTimerStop() ;

    tDirection dir = getJoystickDir() ;
    swithc( dir )
    {
        case UP :
        {
            increaseFrquency() ;
        }
        break ;

        case DN :
        {
            decreaseFrquency() ;
        }
        break ;
    }
}


6 commentaires

Un simple changement permettrait également de rejeter les getJoystickDir() , à savoir, comparer la valeur de getJoystickDir() à la valeur précédente de getJoystickDir() , et augmenter ou diminuer la fréquence uniquement si le manche est actuellement en position active et ne l'était pas auparavant.


@BenVoigt peut-être, mais dans la plupart des cas, inutile, mais une amélioration gratuite néanmoins. Si après l'expiration de la minuterie, le bâton n'est ni en haut ni en bas, rien ne se passe. Tout "pépin positif" dont la durée est inférieure au temps d'anti-rebond est automatiquement rejeté tel quel. Il est vrai qu'un "pépin négatif" où l'interrupteur s'ouvre momentanément tout en étant maintenu fermé serait considéré comme une transition que votre suggestion résoudrait, et peut être nécessaire avec un interrupteur particulièrement pauvre, sale ou usé peut-être, mais la durée de vie du matériel est peut-être limitée d'ici là.


@BenVoigt: Après réflexion, a ajouté votre suggestion. Merci.


@Clifford merci beaucoup pour votre aide et vos explications. J'ai implémenté un anti-rebond dans mon code avec l'utilisation d'un indicateur qui est défini lorsque l'un des ISR est appelé, et effacé après une courte minuterie, les ISR ne pouvant pas s'exécuter si l'indicateur est défini. Vous pensez que c'est une bonne façon de le mettre en œuvre?


@BenVoigt merci pour la suggestion efficace. Le joystick est hors de portée des ISR pour cet exercice, mais je me souviendrai certainement de votre suggestion pour la prochaine fois.


@mvem_ Bien que votre solution fonctionne, elle n'est pas immunisée contre le bruit / les pépins, et n'a aucun avantage réel par rapport à une simple interrogation sur une minuterie comme suggéré par P_J. Il a un point. Si vous souhaitez discuter de votre solution, vous devez soit la publier en tant que réponse ici, soit publier une nouvelle question, SO n'est pas un forum de discussion et ce n'est pas une utilisation appropriée de la fonction de commentaires



0
votes

Nous pensons clairement que c'est le rebond normal et attendu du commutateur. Mécaniquement, un interrupteur est un morceau de métal qui, lorsqu'il est actionné, déplace ce métal d'un pôle à un autre, même s'il ne ressemble pas à un essuie-glace et à deux pôles. Le métal qui bouge va entrer en collision et rebondir, la connexion électrique le montrera. Le rebond est souvent assez lent pour qu'un processeur reçoive plusieurs interruptions, bien que cela puisse être un sous-échantillonnage de tous les rebonds éventuellement vus électriquement. Si vous essayez de le regarder sur une lunette, la lunette elle-même peut ne pas en filtrer intentionnellement une partie (mais votre puce aussi).

Une façon de voir le problème est, comme pour tout, de rechercher d'abord, puis d'écrire l'application plus tard. Ce n'est pas une solution mais un moyen de caractériser le problème de votre système

switch_isr ( void )
{
    ...
    some_global_variable <<= 1;
    some_global_variable |= (pin_state_register>>pin_number)&1;
    ...
}

main ( void )
{
...
some_local_variable = 0;
while(1)
{
   if(some_local_variable != some_global_variable)
   {
       some_local_variable = some_global_variable;
       primitive_hex_print(some_local_variable);
   }
}
}

Aucune raison de vous attendre à voir chaque changement d'état dans la variable décalée, mais vous devriez en voir quelques-uns et avoir une idée du problème. Une autre façon consiste simplement à avoir un incrément de compteur à chaque interruption, à imprimer périodiquement au premier plan et vous verrez qu'une pression sur un bouton peut entraîner plusieurs comptes. Et à partir du temps qu'il faut aux impressions pour arrêter de changer approximativement en temps humain, le temps de stabilisation.

Le filtrage concerne les changements d'état par unité de temps et vous devez avoir une certaine idée du temps, que ce soit une boucle au premier plan qui interroge certaines informations définies par l'interruption (compteurs haut / bas, etc.), ou des changements d'état par rapport à un minuterie / horloge.

Je ne sais pas quelles sont les règles complètes de votre affectation, si vous ne pouvez avoir qu'une interruption pour chaque commutateur et non une minuterie, ou de préférence une minuterie à la place, je ne vois pas de solution propre qui fonctionnera réellement. Vous auriez à filtrer au premier plan, mais tout ce que vous faites est d'interroger une copie de l'état de la broche collecté par l'interruption et est-ce différent de ne pas utiliser l'interruption? Vous ne pouvez pas utiliser la réponse de Clifford si vous ne pouvez pas définir une interruption de minuterie, si vous pouvez utiliser une minuterie et une interruption, vous pouvez simplement échantillonner périodiquement les états du commutateur avec cette interruption ou une copie de l'état de la broche collectée par les interruptions de changement d'état de la broche et le filtre. dans l'interruption de la minuterie. Pas la même chose que celle de Clifford, mais dans tous les cas, vous avez besoin d'un historique des changements d'état par rapport au temps pour voir quand la chose s'installe.

Sans une référence de temps et des états qui ne changent pas par rapport au temps (qu'une interruption de broche ne peut pas afficher puisque l'état n'a pas changé), vous ne pouvez pas filtrer les rebonds. Au lieu de cela, travaillez sur votre dextérité et la façon dont vous actionnez le joystick de haut en bas.


1 commentaires

Veuillez faire attention à votre orthographe ici, en particulier aux mots contenant des apostrophes. Voici quelques orthographes correctes, ainsi que le nombre de fautes d'orthographe dans votre historique de publication: ne pas (765), ne (488), ne sera pas (217), ne peut pas (306). Il y a une certaine tolérance ici pour les personnes qui n'ont pas l'anglais comme langue maternelle, mais une faute d'orthographe stylistique et délibérée va à l'encontre des objectifs du site. Avez-vous un correcteur orthographique disponible dans votre navigateur?



0
votes

Simple, n'utilisez pas EXTI pour les touches et les boutons mécaniques.

Utilisez une interruption régulière (par exemple systick) pour interroger l'état des broches.


0 commentaires