11
\$\begingroup\$

So, I am a COMPLETE and utter novice at programming. I have done some basic stuff on Arduinos (literally toggling LEDs and displaying something on an LCD) and I am trying to self teach myself how to program in C. I am a hardware engineer by trade, but it bothers me that I can't do any of the firmware/software side and there are no evening courses to teach it, and I'd like to further my career options. I am struggling to understand how some of these commands go together and have run into an issue that I just can't get my head around why it isn't working.

So, I have an input and an output. My output is toggling the gate of a FET which turns an LED on. The input is coming from an AND gate. So, my LED is always on, and when I get an input signal from the AND gate (2 conditions have been met) I want the output (LED toggle) to go LOW (turn off the LED. As the output is also connected to one of the AND inputs, this will also turn the input signal LOW.

What I want to do: I just want to read the input as 'conditions met' and turn the LED off. It should then be off for 1 second, and turn back on. If the input goes HIGH again, the process repeats. I am using a simple push to make switch as the other AND gate input and have measured that the output (MCU input) goes high when the button is pressed, yet the LED toggle (output) will not turn off. My code is (I think) pretty damn simple, but clearly I don't understand something correctly as it just isn't working.

So this is the code I am using:

#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 to me, that seems logical. In the usual state, the output is HIGH. If the input gets the signal from the AND gate, the LED will turn off for 1 second, then turn on again.

What is it I've done wrong as that looks like the logical way to do it and I just can't understand why that doesn't work?

If it helps, I am using the Nucleo F103RB. When I use the 'blink' code and just toggle the LED on and off like that, it works fine, it's just when I add the 'if' statement that it goes wrong.

This is the simplified circuit:

schematic

simulate this circuit – Schematic created using CircuitLab

PS I know I didn't add them in the schematic, but the AND gates do have pulldown resistors on the inputs and output.

\$\endgroup\$
8
  • \$\begingroup\$ Does it work if you put "conditions met" directly into IN? \$\endgroup\$
    – Transistor
    Oct 19, 2017 at 11:38
  • \$\begingroup\$ It does not. I stuck the button straight to IN and still didn't work \$\endgroup\$
    – Curious
    Oct 19, 2017 at 11:40
  • 1
    \$\begingroup\$ It's a good idea to mark input variables as volatile, or the compiler might do some weird optimizations by assuming that it is not being changed from outside the code. \$\endgroup\$ Oct 19, 2017 at 12:01
  • 3
    \$\begingroup\$ @DirkBruere: You'd hope the definition of DigitalInalready includes volatile. \$\endgroup\$
    – MSalters
    Oct 19, 2017 at 13:10
  • 3
    \$\begingroup\$ Just a hint for next time: Try holding the button down when you turn on (or reset) the CPU (or microcontroller). Now what happens? \$\endgroup\$
    – user
    Oct 19, 2017 at 13:57

3 Answers 3

26
\$\begingroup\$

I would have thought that you would need a loop around your code -

while(1)
{

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

Before you have chance to press the button you code will have finished and exited. You need the while to keep the if statement repeatedly running.

\$\endgroup\$
7
  • \$\begingroup\$ What makes that different to mine? I can see the 'while' but what does that do? Apologies for all the questions but I really am starting with zero knowledge! \$\endgroup\$
    – Curious
    Oct 19, 2017 at 11:42
  • 1
    \$\begingroup\$ @curious Before you have chance to press the button you code will have finished and exited. You need the while to keep the if statement repeatedly running. This is normally the case, unless there is something different about the microcontroller you are programming. \$\endgroup\$
    – HandyHowie
    Oct 19, 2017 at 11:44
  • 9
    \$\begingroup\$ "Could you explain why that worked" - Everything in a while loop get's repeated until the condition resolves to zero. What is the condition, you might ask; that is the part in the parentheses after the "while"-keyword and as you can see, the condition is set to 1, so it's never zero and thus is repeated indefinetly. Without the while loop the code is executed just once and after which the software terminates, but with the while loop the code is executed repeatedly until you turn of the hardware. \$\endgroup\$
    – Jurgy
    Oct 19, 2017 at 13:19
  • 14
    \$\begingroup\$ Your error probably stemmed from going to Arduino to mbed. In Arduino you usually put your application code in loop(), but the Arduino framework adds code that roughly behaves like int main() { setup(); while(1) { loop(); } }. \$\endgroup\$ Oct 19, 2017 at 13:26
  • 1
    \$\begingroup\$ @Curious Yours did work. Unfortunately it ran precisely once, immediately when you powered it on. It took maybe one microsecond to run, and that was it. If you want it to keep checking the input and setting the output, you need to tell it to keep doing it. "while (some_condition)" runs for as long as "some_condition" is true, which in the C language means non-zero. So "while (1)" keeps checking the input forever, or at least for as long as it's powered on anyway. \$\endgroup\$
    – Graham
    Oct 20, 2017 at 13:02
21
\$\begingroup\$
#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?
}

The processor executes the instructions sequentially. It starts with a jump to main() from within the mbed library initialisation code of DigitalIn and DigitalOut.
Then performs the comparison ip == 0, runs the instruction within the {} and then main() ends... no more instructions... What does it do?

It could reset due to finding illegal operands in the empty flash memory. Or it will could hang in a fault handler and blink SOS like mbeds do. This depends on how this is implemented, and will probably go beyond you right now.
But if you're curious you can research ARM Fault Handling, or find out where main() is actually called from.

Now, how to fix this?

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
}
\$\endgroup\$
6
  • \$\begingroup\$ Thank you very much for the explanation. The while loop enabled it to work. Unfortunately I can't give you a +1 just yet as my rep is too low but I very much appreciate the response and explanation \$\endgroup\$
    – Curious
    Oct 19, 2017 at 11:52
  • \$\begingroup\$ Aha! That third upvote on my question has let me vote your answer up! Thanks again \$\endgroup\$
    – Curious
    Oct 19, 2017 at 11:54
  • 1
    \$\begingroup\$ @Curious If you want this to be clearer to you, the programmer, you could write something like while(1 == 1) instead of just while(1). The latter is idiomatic C, but the former is more obvious to a human being as "will always evaluate to true". Any decent compiler should produce the same binary code for both variants. \$\endgroup\$
    – user
    Oct 19, 2017 at 13:55
  • 2
    \$\begingroup\$ @MichaelKjörling I would disagree it's more obvious to a human. Just like your brain reads words by their shape instead of by character, to an experienced programmer these idioms translate directly to concepts rather than interpreting what each individual statement is doing. By moving away from idiomatic constructs you force people to engage with your code a lower level than is necessary for comprehension; which over a large code base adds up to a lot of needless mental work. \$\endgroup\$
    – Chuu
    Oct 19, 2017 at 22:07
  • 1
    \$\begingroup\$ @Chuu "by a human being [who is not an experienced programmer]" \$\endgroup\$
    – user253751
    Oct 19, 2017 at 23:50
2
\$\begingroup\$

As correctly mentioned by others, a loop would allow your code to run repeatedly. However, there is a built-in way to do this for Arduino without the need for a while loop. This is done by the loop function - its applicability to your problem is dependent on whether you use the Arduino IDE.

It should look something like this:

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

Your main function is now hidden and is only added to your program when compiled. Here is a good discussion on this: http://forum.arduino.cc/index.php?topic=379368.0

\$\endgroup\$
2
  • \$\begingroup\$ Yeah. I originally done things on an arduino, including this so when switching to the nucleo and the mbed IDE I couldn't understand why it didn't work! \$\endgroup\$
    – Curious
    Oct 19, 2017 at 15:53
  • 1
    \$\begingroup\$ This answer relies on using the Arduino system. mbed is a different system / set of libraries and the loop() and setup() functions from Arduino are not used in most systems. For reference, Arduino simply defines a main() something like this: void setup(); void loop(); int main() { setup(); while (true) loop(); } \$\endgroup\$ Oct 19, 2017 at 20:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.