Что приводит к тому, что моя светодиодная программа микроконтроллера перестает работать?

11

Итак, я ПОЛНЫЙ и полный новичок в программировании. Я сделал некоторые базовые вещи для Arduinos (буквально переключая светодиоды и отображая что-то на LCD), и я пытаюсь самостоятельно научиться программировать на C. Я по профессии аппаратный инженер, но меня беспокоит, что я не могу Если у вас есть какие-либо вопросы в отношении прошивки / программного обеспечения, то вечерних курсов для этого нет, и я бы хотел расширить свои карьерные возможности. Я изо всех сил пытаюсь понять, как некоторые из этих команд идут вместе и столкнулись с проблемой, которую я просто не могу понять, почему она не работает.

Итак, у меня есть вход и выход. Мой вывод переключает ворота FET, который включает светодиод. Вход поступает из логического элемента И Итак, мой светодиод всегда включен, и когда я получаю входной сигнал от логического элемента И (2 условия были выполнены), я хочу, чтобы выход (переключение светодиода) стал НИЗКИМ (выключить светодиод. Поскольку выход также подключен к один из входов AND, это также повернет входной сигнал НИЗКИМ.

Что я хочу сделать: я просто хочу прочитать входные данные как «условия выполнены» и выключить светодиод. Затем он должен быть выключен на 1 секунду и снова включен. Если вход снова становится ВЫСОКИМ, процесс повторяется. Я использую простое нажатие для переключения в качестве другого входа логического элемента И и измерил, что выход (вход MCU) становится высоким при нажатии кнопки, но переключение (выход) светодиода не выключается. Мой код (я думаю) чертовски прост, но, очевидно, я что-то не правильно понимаю, потому что он просто не работает.

Вот код, который я использую:

#include "mbed.h"

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

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

И мне это кажется логичным. В обычном состоянии выход ВЫСОКИЙ. Если на вход поступает сигнал от логического элемента И, светодиод выключится на 1 секунду, а затем снова включится.

Что я сделал не так, потому что это выглядит логичным способом сделать это, и я просто не могу понять, почему это не работает?

Если это поможет, я использую Nucleo F103RB. Когда я использую «мигающий» код и просто так включаю и выключаю светодиод, он работает нормально, только когда я добавляю «если», он идет не так.

Это упрощенная схема:

схематический

смоделировать эту схему - схема, созданная с использованием CircuitLab

PS Я знаю, что я не добавил их в схему, но у вентилей AND есть резисторы понижающего напряжения на входах и выходах.

любознательный
источник
Работает ли это, если вы поместите «условия выполнены» непосредственно в IN?
Транзистор
Это не. Я вставил кнопку прямо в IN и все еще не работал
Любопытный
1
Хорошей идеей будет пометить входные переменные как volatile, иначе компилятор может выполнить странные оптимизации, предполагая, что они не изменяются извне кода.
Дирк Брюер
3
@DirkBruere: Вы надеетесь, что определение DigitalInуже включает volatile.
MSalters
3
Просто подсказка для следующего раза: попробуйте удерживать кнопку нажатой при включении (или перезагрузке) процессора (или микроконтроллера). Что теперь происходит?
CVn

Ответы:

26

Я бы подумал, что вам понадобится цикл вокруг вашего кода -

while(1)
{

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

Прежде чем вы сможете нажать кнопку, ваш код будет завершен и завершен. Вам нужно время, чтобы оператор if работал повторно.

HandyHowie
источник
Что отличает это от моего? Я могу видеть «время», но что это делает? Извиняюсь за все вопросы, но я действительно начинаю с нуля знаний!
любопытно
1
@curious Прежде чем вы сможете нажать кнопку, код будет завершен и завершен. Вам нужно время, чтобы оператор if работал повторно. Обычно это так, если в микроконтроллере, который вы программируете, нет ничего другого.
HandyHowie
9
«Не могли бы вы объяснить, почему это сработало» - все во время цикла get повторяется до тех пор, пока условие не станет равным нулю. Каково состояние, спросите вы? это часть в скобках после ключевого слова while, и, как вы можете видеть, условие установлено равным 1, поэтому оно никогда не равно нулю и поэтому повторяется бесконечно. Без цикла while код выполняется только один раз, после чего программное обеспечение завершается, но с циклом while код выполняется повторно до тех пор, пока вы не включите оборудование.
Jurgy
14
Ваша ошибка, вероятно, связана с переходом на Arduino к mbed. В Arduino вы обычно вставляете код своего приложения loop(), но среда Arduino добавляет код, который примерно ведет себя как int main() { setup(); while(1) { loop(); } }.
ris8_allo_zen0
1
@ Curious Yours работал. К сожалению, он работал ровно один раз, сразу после включения. Это заняло, может быть, одну микросекунду, и все. Если вы хотите, чтобы он продолжал проверять вход и устанавливать выход, вам нужно сказать, чтобы он продолжал делать это. «while (some_condition)» выполняется до тех пор, пока «some_condition» имеет значение true, что в языке C означает ненулевое значение. Поэтому «while (1)» постоянно проверяет ввод, или, по крайней мере, до тех пор, пока он все равно включен.
Грэм
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?
}

Процессор выполняет инструкции последовательно . Он начинается с перехода main()изнутри кода инициализации библиотеки mbed DigitalInи DigitalOut.
Затем выполняет сравнение ip == 0, запускает инструкцию внутри {}и затем main()завершает ... больше никаких инструкций ... Что это делает?

Он может быть сброшен из-за обнаружения недопустимых операндов в пустой флэш-памяти. Или он может зависнуть в обработчике ошибок и мигать SOS, как это делает mbeds. Это зависит от того, как это реализовано, и, вероятно, выйдет за вас прямо сейчас.
Но если вам интересно, вы можете изучить ARM Fault Handling или узнать, откуда main()на самом деле вызывается.

Теперь, как это исправить?

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
источник
Большое спасибо за объяснение. Цикл while позволил ему работать. К сожалению, я пока не могу дать вам +1, так как мой представитель слишком низкий, но я очень ценю ответ и объяснение
Любопытно
Ага! Это третье замечание по моему вопросу позволило мне проголосовать за ваш ответ!
Любопытно
1
@Curious Если вы хотите, чтобы это было более понятным для вас, программист, вы могли бы написать что-то вроде while(1 == 1)этого, а не просто while(1). Последнее является идиоматическим C, но первое более очевидно для человека, поскольку «всегда будет оценивать как истинное». Любой достойный компилятор должен создавать одинаковый двоичный код для обоих вариантов.
CVn
2
@ MichaelKjörling Я бы не согласился, что это более очевидно для человека. Подобно тому, как ваш мозг читает слова по форме, а не по характеру, для опытного программиста эти идиомы переводят непосредственно в понятия, а не в интерпретацию того, что делает каждое отдельное утверждение. Отойдя от идиоматических конструкций, вы заставляете людей взаимодействовать с вашим кодом на более низком уровне, чем это необходимо для понимания; который по большой кодовой базе добавляет много ненужной умственной работы.
Чуу
1
@Chuu "человеком [который не является опытным программистом]"
user253751
2

Как правильно упоминали другие, цикл позволит вашему коду запускаться повторно. Тем не менее, есть встроенный способ сделать это для Arduino без необходимости whileцикла. Это делается loopфункцией - ее применимость к вашей проблеме зависит от того, используете ли вы Arduino IDE.

Это должно выглядеть примерно так:

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

Ваша основная функция теперь скрыта и добавляется в вашу программу только после компиляции. Вот хорошее обсуждение этого: http://forum.arduino.cc/index.php?topic=379368.0

OLLEY102
источник
Да уж. Первоначально я делал вещи на Arduino, в том числе это, поэтому при переходе на ядро ​​и IDE mbed я не мог понять, почему это не работает!
любопытно
1
Этот ответ основан на использовании системы Arduino. mbed - это другая система / набор библиотек, loop()а setup()функции и от Arduino не используются в большинстве систем. Для справки, Ардуино просто определяет что- main()то вроде этого:void setup(); void loop(); int main() { setup(); while (true) loop(); }
Кэмерон Таклинд
0

Если вы знакомы со сборкой, это может быть немного больше и в вашей зоне комфорта:

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
источник
3
Пожалуйста , не используйте goto на любом языке выше сборки.
Jeroen3
Боюсь, я совсем не знаком со сборкой!
любопытно
Я знаю об этом, но это все!
Любопытно
@ Jeroen3 На вопрос, на который вы ссылаетесь, есть ответы: «goto's подходят в нескольких местах», «Нет ничего плохого в goto, если оно используется правильно» и «Нет ничего плохого в самом goto». Я согласен, что в языках, имеющих исключения, goto является излишним, но особенно в C, он имеет свое применение.
glglgl
@glglgl: как упоминалось выше, код должен быть читабельным. goto** настоятельно ** предполагает "магию, происходящую здесь", возможно, за исключением goto cleanup;. В приведенном здесь примере читателю останется загадочный вопрос «что такого особенного, что вы while(1) { }здесь не использовали ???».
MSalters