Qu'est-ce qui fait que mon programme LED de microcontrôleur cesse de fonctionner?

11

Donc, je suis un novice COMPLET et total en programmation. J'ai fait quelques trucs de base sur Arduinos (basculer littéralement les LED et afficher quelque chose sur un écran LCD) et j'essaie de m'auto-apprendre à programmer en C. Je suis ingénieur matériel de métier, mais cela me dérange que je ne puisse pas faire n'importe quel côté du firmware / logiciel et il n'y a pas de cours du soir pour l'enseigner, et j'aimerais approfondir mes options de carrière. J'ai du mal à comprendre comment certaines de ces commandes vont ensemble et ont rencontré un problème que je n'arrive pas à comprendre pourquoi cela ne fonctionne pas.

Donc, j'ai une entrée et une sortie. Ma sortie bascule la porte d'un FET qui allume une LED. L'entrée provient d'une porte ET. Donc, ma LED est toujours allumée, et quand je reçois un signal d'entrée de la porte ET (2 conditions sont remplies), je veux que la sortie (bascule LED) passe à LOW (éteignez la LED. Comme la sortie est également connectée à l'une des entrées ET, cela fera également baisser le signal d'entrée.

Ce que je veux faire: je veux juste lire l'entrée comme «conditions remplies» et éteindre la LED. Il devrait ensuite être éteint pendant 1 seconde, puis rallumé. Si l'entrée revient à HAUT, le processus se répète. J'utilise une simple pression pour commuter comme l'autre entrée de porte ET et j'ai mesuré que la sortie (entrée MCU) monte haut lorsque le bouton est enfoncé, mais la bascule LED (sortie) ne s'éteint pas. Mon code est (je pense) sacrément simple, mais clairement je ne comprends pas quelque chose correctement car il ne fonctionne tout simplement pas.

Voici donc le code que j'utilise:

#include "mbed.h"

DigitalIn ip(D7);
DigitalOut op(D8);

int main() {
    if (ip == 1){
        op = 0;
        wait (1.0);
        op = 1;
    }else{
        op = 1;
    }
}

Et pour moi, cela semble logique. Dans l'état habituel, la sortie est HAUTE. Si l'entrée reçoit le signal de la porte ET, la LED s'éteindra pendant 1 seconde, puis se rallumera.

Qu'est-ce que j'ai fait de mal car cela ressemble à la façon logique de le faire et je ne comprends tout simplement pas pourquoi cela ne fonctionne pas?

Si cela aide, j'utilise le Nucleo F103RB. Lorsque j'utilise le code «clignotant» et que je active et désactive simplement la LED comme ça, cela fonctionne bien, c'est juste lorsque j'ajoute la déclaration «si» que ça va mal.

Voici le circuit simplifié:

schématique

simuler ce circuit - Schéma créé à l'aide de CircuitLab

PS Je sais que je ne les ai pas ajoutés dans le schéma, mais les portes ET ont des résistances déroulantes sur les entrées et les sorties.

Curieuse
la source
Cela fonctionne-t-il si vous mettez des "conditions remplies" directement dans IN?
Transistor
Ce ne est pas. J'ai collé le bouton directement sur IN et je n'ai toujours pas fonctionné
Curieux
1
C'est une bonne idée de marquer les variables d'entrée comme volatiles, ou le compilateur pourrait faire des optimisations étranges en supposant qu'elles ne sont pas modifiées depuis l'extérieur du code.
Dirk Bruere
3
@DirkBruere: Vous espérez que la définition de DigitalIncomprend déjà volatile.
MSalters
3
Juste un indice pour la prochaine fois: essayez de maintenir le bouton enfoncé lorsque vous allumez (ou réinitialisez) le CPU (ou le microcontrôleur). Maintenant, que se passe-t-il?
un CVn

Réponses:

26

J'aurais pensé que vous auriez besoin d'une boucle autour de votre code -

while(1)
{

    if (ip == 1){
       op = 0;
       wait (1.0);
       op = 1;}
    else {
       op = 1;}
}

Avant de pouvoir appuyer sur le bouton, votre code sera terminé et terminé. Vous avez besoin de temps pour que l'instruction if soit exécutée à plusieurs reprises.

HandyHowie
la source
Qu'est-ce qui rend cela différent du mien? Je peux voir le «moment», mais qu'est-ce que cela fait? Toutes mes excuses pour toutes les questions mais je commence vraiment avec zéro connaissance!
Curieux
1
@curious Avant de pouvoir appuyer sur le bouton, vous aurez terminé et quitté le code. Vous avez besoin de temps pour que l'instruction if soit exécutée à plusieurs reprises. C'est normalement le cas, sauf s'il y a quelque chose de différent dans le microcontrôleur que vous programmez.
HandyHowie
9
"Pourriez-vous expliquer pourquoi cela a fonctionné" - Tout dans une boucle de temps est répété jusqu'à ce que la condition se résout à zéro. Quelle est la condition, vous pourriez demander; c'est la partie entre parenthèses après le mot-clé "while" et comme vous pouvez le voir, la condition est définie sur 1, elle n'est donc jamais nulle et est donc répétée indéfiniment. Sans la boucle while, le code est exécuté une seule fois et après quoi le logiciel se termine, mais avec la boucle while le code est exécuté à plusieurs reprises jusqu'à ce que vous éteigniez le matériel.
Jurgy
14
Votre erreur provient probablement de votre passage à Arduino pour mbed. Dans Arduino, vous mettez généralement votre code d'application loop(), mais le cadre Arduino ajoute du code qui se comporte à peu près comme int main() { setup(); while(1) { loop(); } }.
ris8_allo_zen0
1
@Curious Yours a fonctionné. Malheureusement, il n'a fonctionné qu'une seule fois, immédiatement après sa mise sous tension. Il a fallu peut-être une microseconde pour fonctionner, et c'est tout. Si vous voulez qu'il continue à vérifier l'entrée et à définir la sortie, vous devez lui dire de continuer à le faire. "while (some_condition)" s'exécute aussi longtemps que "some_condition" est vrai, ce qui dans le langage C signifie non nul. Donc "while (1)" continue de vérifier l'entrée pour toujours, ou du moins aussi longtemps qu'elle est sous tension.
Graham
21
#include "mbed.h"

DigitalIn ip(D7);
DigitalOut op(D8);

int main() {
    if (ip == 1){
        op = 0;
        wait (1.0);
        op = 1;
    }else{
        op = 1;
    }
    // and now the program ends? What to do?
}

Le processeur exécute les instructions séquentiellement . Il commence par un saut à main()partir du code d'initialisation de la bibliothèque mbed de DigitalInet DigitalOut.
Effectue ensuite la comparaison ip == 0, exécute l'instruction dans le {}puis main()se termine ... plus d'instructions ... Que fait-il?

Il pourrait se réinitialiser en raison de la découverte d'opérandes illégaux dans la mémoire flash vide. Ou il pourrait se bloquer dans un gestionnaire de fautes et clignoter SOS comme le font les mbeds. Cela dépend de la façon dont cela est mis en œuvre et vous dépassera probablement en ce moment.
Mais si vous êtes curieux, vous pouvez rechercher ARM Fault Handling, ou savoir d'où main()est réellement appelé.

Maintenant, comment y remédier?

int main() {
    // Add a while(1) infinite loop
    while(1){
        if (ip == 1){
            op = 0;
            wait (1.0);
            op = 1;
        }else{
            op = 1;
        }
    }
    // Program never gets here
}
Jeroen3
la source
Merci beaucoup pour l'éxplication. La boucle while lui a permis de fonctionner. Malheureusement, je ne peux pas vous donner de +1 pour le moment car mon représentant est trop faible mais j'apprécie beaucoup la réponse et l'explication
Curieux
Ah! Ce troisième vote positif sur ma question m'a permis de voter pour votre réponse! Merci encore
Curious
1
@Curious Si vous voulez que cela soit plus clair pour vous, le programmeur, vous pouvez écrire quelque chose comme while(1 == 1)au lieu de simplement while(1). Ce dernier est idiomatique C, mais le premier est plus évident pour un être humain car "sera toujours évalué comme vrai". Tout compilateur décent devrait produire le même code binaire pour les deux variantes.
un CVn
2
@ MichaelKjörling Je ne suis pas d'accord, c'est plus évident pour un humain. Tout comme votre cerveau lit les mots par leur forme plutôt que par leur caractère, pour un programmeur expérimenté, ces idiomes se traduisent directement en concepts plutôt qu'en interprétant ce que fait chaque déclaration individuelle. En vous éloignant des constructions idiomatiques, vous forcez les gens à s'engager avec votre code à un niveau inférieur à celui nécessaire à la compréhension; ce qui sur une grande base de code ajoute à beaucoup de travail mental inutile.
Chuu
1
@Chuu "par un être humain [qui n'est pas un programmeur expérimenté]"
user253751
2

Comme correctement mentionné par d'autres, une boucle permettrait à votre code de s'exécuter à plusieurs reprises. Cependant, il existe un moyen intégré de le faire pour Arduino sans avoir besoin d'une whileboucle. Cela se fait par la loopfonction - son applicabilité à votre problème dépend de l'utilisation de l'IDE Arduino.

Ça devrait ressembler a quelque chose comme ca:

#include "mbed.h"

DigitalIn ip(D7);
DigitalOut op(D8);

void setup() {
    // any code before loop is run
}

void loop() {
    if (ip == 1){
        op = 0;
        wait (1.0);
        op = 1;
    }else{
        op = 1;
    }
}

Votre fonction principale est désormais masquée et n'est ajoutée à votre programme qu'une fois compilée. Voici une bonne discussion à ce sujet: http://forum.arduino.cc/index.php?topic=379368.0

OLLEY102
la source
Ouais. À l'origine, j'ai fait des choses sur un arduino, y compris cela, donc lorsque je suis passé au nucléo et à l'IDE mbed, je ne comprenais pas pourquoi cela ne fonctionnait pas!
Curieux
1
Cette réponse repose sur l'utilisation du système Arduino. mbed est un système / ensemble de bibliothèques différent et les fonctions loop()et setup()d'Arduino ne sont pas utilisées dans la plupart des systèmes. Pour référence, Arduino définit simplement main()quelque chose comme ceci:void setup(); void loop(); int main() { setup(); while (true) loop(); }
Cameron Tacklind
0

Si vous êtes familier avec l'assemblage, cela pourrait aussi être un peu plus dans votre zone de confort:

int main() {

//A label or function similar to assembly

label:

    if (ip == 1){

        op = 0;

        wait (1.0);

        op = 1;

    }else{

        op = 1;

    }

// Goto used same as "jmp" in assembly

goto label;

// Program never gets here

}

Susmit Agrawal
la source
3
Veuillez ne pas utiliser goto dans aucune langue au-dessus de l'assemblage.
Jeroen3
Je ne suis pas du tout familier avec le montage, j'ai bien peur!
Curieux
Je le connais mais c'est tout!
Curieux
@ Jeroen3 La question à laquelle vous créez un lien est répondue par "les goto sont appropriés à quelques endroits", "Rien ne va mal avec goto s'il est utilisé correctement" et "Il n'y a rien de mal avec goto en soi". Je suis d'accord que dans les langages ayant des exceptions, goto est superflu, mais surtout en C, il a ses utilisations.
glglgl
@glglgl: Comme Chuu l'a mentionné ci-dessus, le code doit être lisible. goto** fortement ** suggère "la magie qui se passe ici", peut-être à l'exception de goto cleanup;. Dans l'exemple ici, le lecteur se retrouvera avec la question déroutante "qu'est-ce qui est si spécial que vous n'avez pas utilisé while(1) { }ici ???".
MSalters