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\$
  • \$\begingroup\$ Does it work if you put "conditions met" directly into IN? \$\endgroup\$ – Transistor Oct 19 '17 at 11:38
  • \$\begingroup\$ It does not. I stuck the button straight to IN and still didn't work \$\endgroup\$ – Curious Oct 19 '17 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\$ – Dirk Bruere Oct 19 '17 at 12:01
  • 3
    \$\begingroup\$ @DirkBruere: You'd hope the definition of DigitalInalready includes volatile. \$\endgroup\$ – MSalters Oct 19 '17 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\$ – a CVn Oct 19 '17 at 13:57
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\$
  • \$\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 '17 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 '17 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 '17 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\$ – ris8_allo_zen0 Oct 19 '17 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 '17 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\$
  • \$\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 '17 at 11:52
  • \$\begingroup\$ Aha! That third upvote on my question has let me vote your answer up! Thanks again \$\endgroup\$ – Curious Oct 19 '17 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\$ – a CVn Oct 19 '17 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 '17 at 22:07
  • 1
    \$\begingroup\$ @Chuu "by a human being [who is not an experienced programmer]" \$\endgroup\$ – immibis Oct 19 '17 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\$
  • \$\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 '17 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\$ – Cameron Tacklind Oct 19 '17 at 20:05
0
\$\begingroup\$

If you are fimiliar with assembly, this could be a little more in your comfort zone too:

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

}

\$\endgroup\$
  • 3
    \$\begingroup\$ Please do not use goto in any language above assembly. \$\endgroup\$ – Jeroen3 Oct 19 '17 at 19:50
  • \$\begingroup\$ I am not familiar at all with assembly I'm afraid! \$\endgroup\$ – Curious Oct 19 '17 at 20:08
  • \$\begingroup\$ I know of it but thats about it! \$\endgroup\$ – Curious Oct 19 '17 at 20:09
  • \$\begingroup\$ @Jeroen3 The question you link to is answered with "goto's are appropriate in a few places", "Nothing is wrong with goto if it is used properly" and "There is nothing wrong with goto in itself". I agree that in languages having Exceptions, goto is superfluous, but especially in C, it has its uses. \$\endgroup\$ – glglgl Oct 20 '17 at 12:24
  • \$\begingroup\$ @glglgl: As Chuu mentioned above, code should be readable. goto**strongly** suggests "magic going on here", possibly with the exception of goto cleanup;. In the example here, the reader will be left with the puzzling question "what's so special that you didn't use while(1) { } here???". \$\endgroup\$ – MSalters Oct 20 '17 at 15:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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