0

I wanted to run the following code as something similar to Tone or pwm. However for some reason that I am unable to seek it doesn't work.

#define WAIT 3200UL

void setup() {
  DDRB |= B00000001;  // set pin8 as output
}

void loop() {
  unsigned long t;
  while (1) {

    PORTB &= B11111110; // clear pin 8
    t = WAIT;
    for (t = 0 ; t < WAIT; t++);

    PORTB |= B00000001; // set pin 8
    t = WAIT;
    for (t = 0; t < WAIT; t++);


    //            PORTB &= B11111110;
    //            delayMicroseconds(WAIT);
    //            PORTB |= B00000001;
    //            delayMicroseconds(WAIT);

  }
}

the code is pretty simple, just toggling a pin. If I use the commented section, it works fine but as is, it doesn't work.

I removed the "while(1)" statement since it was unnecessary for now, it started to work improperly, (changing the WAIT didn't have any effect and I'm not sure I'm getting a full swing).

Pin 8 is connected to an LED + R to ground and a piezzo in parallel to them. the LED seems to be steady on yet the buzzer makes a low noise. I measured a voltage of 3.8v DC and arround 9v AC on the pin, other pins I ran the code for were similar.

As another attempt I put a Serial.print in the beginning of the while(1) but it printed out only once.

Also looked at the assembly code generated for a clue to find out what does delayMicroseconds() do but couldn't figure out what's wrong.

Appreciate your help.

  • Answering your title - there's nothing wrong with an infinite loop within loop, but it's not necessary as loop itself is an infinite loop. Move unsigned long t; into setup and you will have an infinite loop without needing to explicitly code the while(1). – Holmez Feb 21 '18 at 16:20
4

First, I will give you some unsolicited advise about your coding style. Instead of writing:

PORTB &= B11111110;
...
PORTB |= B00000001;

you could write:

PORTB &= ~_BV(PB0);
...
PORTB |= _BV(PB0);

These are standard AVR idioms, and they make it clearer that you are toggling the pin PB0.

Then, to answer your actual question, your delay loop does not work because it got optimized-out by the compiler. This has already been said in Michel Keijzers' answer, but let me elaborate.

All modern compilers are optimizing compilers. This means they can change your code in whatever way they see fit, as long as the visible effects remain the same. The effects considered visible are usually I/O and access to variables declared volatile. Quite importantly, the slowness of the program is not considered a visible effect for the purpose of optimization. And if you think about it, it makes good sense: if the slowness of the program were to be preserved, then any form of optimization would be impossible. Thus, a loop that has no effect other than slowing your program down can legitimately be optimized-out.

The solution to your problem is quite simple: if you want a delay, then call delayMicroseconds(). Or maybe even delay() if you are OK with its low resolution. On the other hand, if you need sub-microsecond resolution, you can call _delay_us(), from the avr-libc. This function (or rather, macro) provides single-cycle resolution. If you write

#include <util/delay.h>

...
_delay_us(1.375);

you get exactly what you ask for, i.e. a delay of 22 CPU cycles (on an Arduino Uno clocked at 16 MHz). The issues with this function are that it is AVR-specific, and its argument must be a compile-time constant.

It has been suggested to you to write your delay loop using a volatile loop counter. I strongly advise you against doing that, for three reasons:

  1. Doing so will force the counter to be allocated to RAM, thus consuming a scarce and valuable resource for no good reason.
  2. Incrementing a RAM-based 32-bit counter takes 20 CPU cycles. Add to this the time required to test the end-of-loop condition and you will realize that your delay resolution is way worse than that of delayMicroseconds().
  3. Your actual delay will depend on the clock frequency, and probably also on the compiler's version, making your code pretty fragile.
  • 1
    Upvoted for the (much extra) explanation – Michel Keijzers Feb 21 '18 at 21:11
  • Re, "effects considered visible are usually I/O and..." There is no I/O in the C++ programming language. The effects that are considered visible are calls to functions with external linkage, and access to volatile functions. When a C++ program does something like std::cout<<"hello world!\n";, that's a call to an external library function. – Solomon Slow Feb 22 '18 at 1:44
  • static loop counter should work just as good in this particular case, saving some of the load/store instructions. – wondra Feb 22 '18 at 6:52
  • @jameslarge: Recent versions of the Arduino IDE pass the -flto option to the compiler, which triggers full-program link-time optimization. In this case, external linkage is irrelevant, and calls to external functions can be optimized out (yes, I tried). – Edgar Bonet Feb 22 '18 at 10:12
  • @wondra: I tried with a static counter, both a global static and a local static. In both cases the loop got optimized-out, although the counter did get written with its end-of-loop value. – Edgar Bonet Feb 22 '18 at 10:12
0

First

t = WAIT;
for (t = 0; t < WAIT; t++);

The first statement is not useful, since t will be assigned to 0 in the for loop.

However, since t is not used, probably it is optimized and removed by the compiler, meaning it will not perform a delay.

You should make t volatile. This prevents the compiler from optimizing it (out).

The only difference with or without the infinite loop is that variable t does not need to be allocated for (and keeps its value from the previous loop), but since the value is overwritten it does not make a difference.

  • 1
    Even t is marked volatile, the loop will execute that quickly you won't even see it flickering. – Kwasmich Feb 21 '18 at 13:53
  • increasing the WAIT value might help ... not sure how many clock cycle are used. I would say 16 MHz / number of clock cycle for one iteration handling. – Michel Keijzers Feb 21 '18 at 13:54
  • And a useful device to analyze this is a logic analyzer, you can buy it for 5 euro from China and is easier to use for low signal checking than a (cheap) oscilloscope. – Michel Keijzers Feb 21 '18 at 13:55
  • 2
    it is not used afterwards and has no side effects, so the compiler can optimize it away. – Kwasmich Feb 21 '18 at 13:59
  • 1
    65535 loop iterations times single digit count of ticks (10 or less) is roughly 600000 (rounded for convinience), yes, 3200 is a pretty low delay. – Kwasmich Feb 21 '18 at 14:08

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.