Co powoduje, że mój program LED mikrokontrolera przestaje działać?

11

Jestem więc ZUPEŁNYM i kompletnym nowicjuszem w programowaniu. Zrobiłem kilka podstawowych rzeczy na Arduinos (dosłownie przełączając diody LED i wyświetlając coś na wyświetlaczu LCD) i staram się samemu nauczyć się programowania w C. Z zawodu jestem inżynierem sprzętu, ale przeszkadza mi, że nie mogę zrobić jakąkolwiek stronę oprogramowania / oprogramowania i nie ma wieczornych kursów, aby tego nauczyć, i chciałbym rozwinąć opcje kariery. Próbuję zrozumieć, w jaki sposób niektóre z tych poleceń idą w parze i napotkałem problem, którego po prostu nie mogę zrozumieć, dlaczego to nie działa.

Mam więc wejście i wyjście. Moje wyjście to przełączanie bramki FET, która włącza diodę LED. Dane wejściowe pochodzą z bramki AND. Tak więc moja dioda LED jest zawsze włączona, a gdy otrzymam sygnał wejściowy z bramki AND (spełnione są 2 warunki), chcę, aby wyjście (przełączanie diody LED) było NISKIE (wyłącz diodę. Ponieważ wyjście jest również podłączone do jedno z wejść AND, spowoduje to również zmianę sygnału wejściowego na NISKI.

Co chcę zrobić: chcę tylko odczytać wejście jako „spełnione warunki” i wyłączyć diodę LED. Następnie należy go wyłączyć na 1 sekundę i włączyć ponownie. Jeśli sygnał wejściowy ponownie stanie się WYSOKI, proces się powtarza. Używam prostego przycisku, aby zrobić przełącznik jako inne wejście bramki AND i zmierzyłem, że wyjście (wejście MCU) idzie wysoko po naciśnięciu przycisku, ale przełącznik LED (wyjście) nie wyłącza się. Mój kod jest (myślę) dość cholernie prosty, ale najwyraźniej nie rozumiem czegoś poprawnie, ponieważ to po prostu nie działa.

Oto kod, którego używam:

#include "mbed.h"

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

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

I wydaje mi się to logiczne. W zwykłym stanie sygnał wyjściowy jest WYSOKI. Jeśli wejście otrzyma sygnał z bramki AND, dioda LED zgaśnie na 1 sekundę, a następnie ponownie się zaświeci.

Co zrobiłem źle, ponieważ wygląda to na logiczny sposób i po prostu nie rozumiem, dlaczego to nie działa?

Jeśli to pomoże, używam Nucleo F103RB. Kiedy używam kodu „mrugania” i po prostu włączam i wyłączam diodę LED w ten sposób, działa to dobrze, to tylko wtedy, gdy dodam instrukcję „if”, że idzie źle.

To jest uproszczony obwód:

schematyczny

symulacja tego obwodu - Schemat utworzony przy użyciu CircuitLab

PS Wiem, że nie dodałem ich do schematu, ale bramki AND mają rozwijane rezystory na wejściach i wyjściach.

Ciekawy
źródło
Czy to działa, jeśli umieścisz „warunki spełnione” bezpośrednio w IN?
Tranzystor
To nie. Wcisnąłem przycisk prosto do IN i nadal nie
działałem
1
Dobrym pomysłem jest oznaczenie zmiennych wejściowych jako niestabilne, w przeciwnym razie kompilator może dokonać dziwnych optymalizacji, zakładając, że nie jest zmieniany spoza kodu.
Dirk Bruere
3
@DirkBruere: Miałbyś nadzieję, że definicja DigitalInzawiera już volatile.
MSalters,
3
Tylko podpowiedź na następny raz: Spróbuj przytrzymać przycisk po włączeniu (lub zresetowaniu) procesora (lub mikrokontrolera). Co się teraz stanie?
CVn

Odpowiedzi:

26

Myślałem, że potrzebujesz pętli wokół swojego kodu -

while(1)
{

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

Zanim będziesz mógł nacisnąć przycisk, kod zostanie zakończony i zakończony. Potrzebujesz czasu, aby wielokrotnie uruchamiać instrukcję if.

HandyHowie
źródło
Co sprawia, że ​​różni się od mojego? Widzę „chwilę”, ale co to robi? Przepraszamy za wszystkie pytania, ale tak naprawdę zaczynam od zerowej wiedzy!
Ciekawy
1
@curious Zanim będzie można nacisnąć przycisk, kod zostanie zakończony i zakończony. Potrzebujesz czasu, aby wielokrotnie uruchamiać instrukcję if. Tak jest zwykle, chyba że programowany mikrokontroler ma coś innego.
HandyHowie,
9
„Czy możesz wyjaśnić, dlaczego to zadziałało” - Wszystko w pętli chwilowej powtarza się, aż warunek zniknie do zera. Jaki jest warunek, możesz zapytać; to jest część w nawiasach po słowie kluczowym „while” i jak widać warunek jest ustawiony na 1, więc nigdy nie jest równy zero, a zatem jest powtarzany w nieskończoność. Bez pętli while kod jest wykonywany tylko raz, a następnie oprogramowanie się kończy, ale w pętli while kod jest wykonywany wielokrotnie, aż do momentu wyłączenia sprzętu.
Jurgy
14
Twój błąd prawdopodobnie wynikał z przejścia do Arduino na mbed. W Arduino zwykle umieszczasz kod aplikacji loop(), ale framework Arduino dodaje kod, który z grubsza się zachowuje int main() { setup(); while(1) { loop(); } }.
ris8_allo_zen0
1
@Curious Yours zadziałało. Niestety uruchomił się dokładnie raz, natychmiast po włączeniu. Uruchomienie trwało może jedną mikrosekundę i tyle. Jeśli chcesz, aby ciągle sprawdzał dane wejściowe i ustawiał dane wyjściowe, musisz powiedzieć mu, aby nadal to robił. „while (some_condition)” działa tak długo, jak długo „some_condition” jest prawdziwe, co w języku C oznacza niezerowe. Zatem „while (1)” sprawdza dane wejściowe na zawsze, a przynajmniej tak długo, jak długo jest włączone.
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?
}

Procesor wykonuje instrukcje sekwencyjnie . Zaczyna się od skoku do main()z kodu inicjalizacji biblioteki mbed DigitalIni DigitalOut.
Następnie wykonuje porównanie ip == 0, uruchamia instrukcję wewnątrz, {}a następnie main()kończy ... nie więcej instrukcji ... Co to robi?

Może zostać zresetowany z powodu znalezienia niedozwolonych operandów w pustej pamięci flash. Lub może zawiesić się w module obsługi błędów i mrugać SOS, tak jak robią to mbeds. Zależy to od tego, w jaki sposób jest to realizowane, i prawdopodobnie wykroczy teraz poza ciebie.
Ale jeśli jesteś ciekawy, możesz zbadać obsługę błędów ARM lub dowiedzieć się, skąd main()tak naprawdę pochodzi.

Jak to naprawić?

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
źródło
Dziękuję bardzo za wyjaśnienie. Pętla while umożliwiła jej działanie. Niestety, nie mogę jeszcze dać +1, ponieważ mój przedstawiciel jest zbyt niski, ale bardzo doceniam odpowiedź i wyjaśnienie
Ciekawe
Aha! Trzeci głos w sprawie mojego pytania pozwolił mi głosować na twoją odpowiedź! Jeszcze raz dziękuję
Ciekawy
1
@Curious Jeśli chcesz, aby to było dla Ciebie jaśniejsze, programista, możesz napisać coś takiego while(1 == 1)zamiast po prostu while(1). Ten drugi to idiomatyczny C, ale ten pierwszy jest bardziej oczywisty dla człowieka, ponieważ „zawsze oceni się jako prawdziwy”. Każdy przyzwoity kompilator powinien wygenerować ten sam kod binarny dla obu wariantów.
CVn
2
@ MichaelKjörling Nie zgadzam się, że dla człowieka jest to bardziej oczywiste. Podobnie jak twój mózg odczytuje słowa po ich kształcie, a nie po znaku, tak dla doświadczonego programisty te idiomy przekładają się bezpośrednio na pojęcia, a nie na interpretację tego, co robi każda instrukcja. Odchodząc od konstruktów idiomatycznych, zmuszasz ludzi do angażowania się w Twój kod na poziomie niższym niż jest to konieczne do zrozumienia; co w przypadku dużej bazy kodu powoduje wiele niepotrzebnej pracy umysłowej.
Chuu,
1
@Chuu „przez człowieka [który nie jest doświadczonym programistą]”
user253751,
2

Jak słusznie wspominają inni, pętla pozwala na wielokrotne uruchamianie kodu. Istnieje jednak wbudowany sposób, aby to zrobić dla Arduino bez potrzeby używania whilepętli. Odbywa się to przez loopfunkcję - jej zastosowanie do twojego problemu zależy od tego, czy korzystasz z Arduino IDE.

Powinno to wyglądać mniej więcej tak:

#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;
    }
}

Twoja główna funkcja jest teraz ukryta i jest dodawana do programu dopiero po skompilowaniu. Oto dobra dyskusja na ten temat: http://forum.arduino.cc/index.php?topic=379368.0

OLLEY102
źródło
Tak. Początkowo robiłem różne rzeczy na arduino, włączając to, więc po przejściu na jądro i IDE mbed nie mogłem zrozumieć, dlaczego to nie działa!
Ciekawy
1
Ta odpowiedź polega na użyciu systemu Arduino. mbed to inny system / zestaw bibliotek, loop()a setup()funkcje i Arduino nie są używane w większości systemów. Dla porównania, Arduino po prostu definiuje main()coś takiego:void setup(); void loop(); int main() { setup(); while (true) loop(); }
Cameron Tacklind,
0

Jeśli jesteś zaznajomiony z montażem, może to być nieco więcej w twojej strefie komfortu:

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
źródło
3
Proszę nie używać goto w żadnym języku powyżej asemblera.
Jeroen3,
Obawiam się, że w ogóle nie jestem obeznany z montażem!
Ciekawy
Wiem o tym, ale to wszystko!
Ciekawy
@ Jeroen3 Na pytanie, do którego prowadzi link, można odpowiedzieć „goto's są odpowiednie w kilku miejscach”, „Nic nie jest nie tak z goto, jeśli jest właściwie używane” i „Nie ma nic złego w goto samo w sobie”. Zgadzam się, że w językach z wyjątkami goto jest zbędny, ale zwłaszcza w C ma swoje zastosowania.
glglgl,
@glglgl: Jak wspomniano powyżej Chuu, kod powinien być czytelny. goto** zdecydowanie ** sugeruje „magię tu się dzieje”, być może z wyjątkiem goto cleanup;. W tym przykładzie czytelnikowi pozostanie zagadkowe pytanie „co jest takiego specjalnego, że nie while(1) { }użyłeś tutaj ???”.
MSalters