0

I have a simple sketch here that should just blink a led once a second or so (approximately):

boolean onOff=true;
int count=0;

void setup() {
  pinMode(13, OUTPUT);
 }

void loop() {
    count++;
    delay(1);

    if (count == 316) { 
    onOff=!onOff;
    digitalWrite(13, onOff);
    count=0;
    }
}

So, if count is 316 it changes the state of led. It works right now, but it does not without delay() function or some other complex ones like digitalWrite() being in the loop() too. If i am deleting delay(), than led is just always on. I can feel it is smth about interrupts, but i am not sure.


What am i trying to do here is to make an alternative to delay() and millis() for my other project. One of the most needed thing for me is to make ASAP (as soon as possible) "timer", so that some code could run every 2(3-4...) iterations of loop(). delay() is bad always, and millis() uses long variable (which is bad for cpu time and ram), and while having problems with ram and cpu time in my project, i want smth lighter for ASAP and other not very precise timing. So, the questions are:

  1. Why does my sketch works only with delay(), but does not work without it?
  2. Why does blinking sketches work with millis(), while they do not use any delay() just like my sketch.
  3. Am i a bad person for trying to make smth like an ASAP timer?:) Is saved ram and cpu time worth of it? Any other possible problems?
  4. Can this sketch be workable without delay() or other complex or ram and cpu costly functions?

Thank you.

So, the sketch up there works indeed, but here is the version that you can can observe with your eyes:

boolean onOff=1;
unsigned int counter=0;
unsigned long time=0;

void setup() {
  pinMode(13, OUTPUT);
  Serial.begin(57600);
}

void loop() {
   if (counter==0)  time=micros();
    counter++;
    if (counter == 0) {
        onOff= !onOff;
    digitalWrite(13, onOff);
    Serial.println(micros()-time);
    }
}

With Serial i was checking delay between blinks, and it is 140164 mcs = 140 ms = 0.14s. But to be honest it feels slower, so please correct me if i can benchmark it in a better way.

  • 1
    Without `delay()` it is doing exactly what you tell it. It is blinking the LED - just far too fast for you to see. Try using values in the hundreds of millions instead of just a few hundred for your counter. – Majenko Aug 20 '16 at 11:58
  • 1
    The typical design pattern used to perform a delay without delay() is described here: https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay – Dirk Grappendorf Aug 20 '16 at 12:02
  • “_millis() uses long variable (which is bad for cpu time and ram)_” If you never need to compare timestamps more than 65 seconds apart, then you can do the timekeeping with 16-bit unsigned integers. – Edgar Bonet Aug 20 '16 at 13:16
  • @Edgar Bonet , i was thinking about it, but what happens, when previous millis() value is 60000, and than it overflowed and current millis() is 3000? 3000-60000=-57000, and who knows what will arduino do next... Or did i miss smth? Tell me please if so, that would a nice solution. – user2882440 Aug 20 '16 at 20:41
  • @ Dirk Grappendorf , thank you for that, but it also uses millis(), and it is too costly in my current project. – user2882440 Aug 20 '16 at 20:45
  • @Majenko , indeed it is!) Thank you! – user2882440 Aug 20 '16 at 20:46
  • With unsigned ints, 3000 − 60000 = 8536. Unsigned numbers follow the rules of [modular arithmetic](https://en.wikipedia.org/wiki/Modular_arithmetic), which make the overflow irrelevant as long as you [compare durations instead of timestamps](http://arduino.stackexchange.com/questions/12587). – Edgar Bonet Aug 21 '16 at 07:47

2 Answers2

1

As majenko answered in the comments, the blinking rate will be so fast you can not see it.
Also it is possible that without the delay the optimizer removes the whole counting. So increasing the number as proposed by majenko may not even work due to compiler optimisations.
Using a method based on counters is a very unreliable way to use timings.

Look at the blinkwithout delay example as proposed by Dirk.
It boils down to using millis() for timing. millis tells you how many millis the arduino is running.

void loop() {
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the
  // difference between the current time and last time you blinked
  // the LED is bigger than the interval at which you want to
  // blink the LED.
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
  }
}
jantje
  • 1,372
  • 1
  • 8
  • 16
  • There is absolutely no reason for the optimizer to remove the counting. If it did that sort of thing then nothing would ever work. The compiler has no clue what delay() does. – Majenko Aug 20 '16 at 12:56
  • You are right when delay is there. I mean when delay is gone. – jantje Aug 20 '16 at 13:38
  • Why do you think that removing a function that has no relationship at all with the counting would affect how the compiler optimizes the counting? – Majenko Aug 20 '16 at 13:39
  • because loop() is actually in a while(true) loop. So without the delay you get while(true){count++;if(count==316){[something]}}. Which can get optimized to while(true){[something]}. I have had that happen to me. – jantje Aug 20 '16 at 13:44
  • What makes you think the optimizer would ever do something as idiotic as that? (By the way: try it for your self. Compile the code without delay() and look at the assembly output. You'll see the count never gets optimized out). The only time the optimizer would remove the counting is if the *result* of the counting does nothing (i.e., if the `if` is empty). – Majenko Aug 20 '16 at 13:50
  • As I said, It happened to me. I never stated it would happen. As Arduino now is plenty of toolchains and cores I think it is save to say: I could happen. Maybe you should reread my answer. – jantje Aug 20 '16 at 14:13
  • As long as the *result* of the count is used somewhere (and that "somewhere" doesn't get optimized out) the count will *never* be optimized, under *any* circumstances. Such behaviour would break massive amounts of code all over the place. I reiterate: it cannot happen. Ever. Full stop. I suspect your "it happened to me" was something completely different. You may have seen it optimized out as a casualty of something else being optimised out (such as an empty if that gets optimized out) but as long as there is something using the count it CANNOT be optimized out. Counting is perfectly reliable. – Majenko Aug 20 '16 at 14:17
0

This sketch blinks an LED on D10 (on a Uno) at one second intervals. It does not use delay, micros, millis or any unsigned long variables (apart from a constant).

const int STROBE_FREQ = 1000;     // sets the period in milliseconds 
const unsigned long countTo = ((float) F_CPU / 1024.0) / (1000.0 / STROBE_FREQ); 

void setup ()
  {
  // D10 to output
  bitSet (DDRB, 2);

  // Fast PWM top at OCR1A
  TCCR1A = bit (WGM10) | bit (WGM11); // fast PWM
  TCCR1B = bit (WGM12) | bit (WGM13) | bit (CS12) | bit (CS10);   // fast PWM, prescaler of 1024
  OCR1A = countTo - 1;                 // zero relative 
  OCR1B = (countTo / 2) - 1;           // 50% duty cycle
  bitSet (TCCR1A, COM1B1);   // clear OC1B on compare
  }  // end of setup

 void loop ()
   {
    // do something useful
   }

It frees up the main loop to do something useful without worrying about time taken to do the blinking.

For more details see my page about timers.

Nick Gammon
  • 35,792
  • 12
  • 63
  • 121
  • Wow, thank you for that! But that is so much more than i know for know, i do not really understand that sketch. Can you please provide some more info or some links, so that i could learn what is going on there? Do i understand correctly that some hardware timer is used here? Also i have no clue how to make a second timer for another pin in that sketch, would love extended example. Thank you again! – user2882440 Aug 21 '16 at 00:38
  • See amended reply. Yes, it uses a hardware timer. – Nick Gammon Aug 21 '16 at 01:01