11

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.

JYelton
  • 32,302
  • 33
  • 134
  • 249
Curious
  • 482
  • 6
  • 19

3 Answers3

26

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.

HandyHowie
  • 4,030
  • 1
  • 15
  • 22
  • 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! – Curious Oct 19 '17 at 11:42
  • 1
    @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. – HandyHowie Oct 19 '17 at 11:44
  • And that's done it. Just tried it and adding the while command worked. Could you explain why that worked and mine didn't in noob terms? And thanks a lot for your answer. It was starting to really frustrate me! I tried to give you a +1 too but not enough rep! – Curious Oct 19 '17 at 11:44
  • Aha! That third upvote on my question has let me vote your answer up! Thanks again – Curious Oct 19 '17 at 11:54
  • 9
    "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. – Jurgy Oct 19 '17 at 13:19
  • 14
    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(); } }`. – ris8_allo_zen0 Oct 19 '17 at 13:26
  • 1
    @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. – Graham Oct 20 '17 at 13:02
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?
}

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
}
Jeroen3
  • 21,976
  • 36
  • 73
  • 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 – Curious Oct 19 '17 at 11:52
  • Aha! That third upvote on my question has let me vote your answer up! Thanks again – Curious Oct 19 '17 at 11:54
  • 1
    @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. – user Oct 19 '17 at 13:55
  • 2
    @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. – Chuu Oct 19 '17 at 22:07
  • 1
    @Chuu "by a human being [who is not an experienced programmer]" – user253751 Oct 19 '17 at 23:50
  • @Chuu immibis pretty much nailed it. There's a number of ways to write C code which are idiomatic, and should be recognizable by someone who knows C, but non-obvious unless you know the language. Generally speaking I'm against "dumbing down" code, but for someone who is just starting out, sometimes writing slightly more verbose code can make a big difference in being able to tell what's going on. It does not appear likely that this code will be worked on by a large team. – user Oct 20 '17 at 07:12
2

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

OLLEY102
  • 21
  • 1
  • 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! – Curious Oct 19 '17 at 15:53
  • 1
    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(); }` – Cameron Tacklind Oct 19 '17 at 20:05