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
for (t = 0 ; t < WAIT; t++);
PORTB |= B00000001; // set pin 8
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– Holmez2018年02月21日 16:20:52 +00:00Commented Feb 21, 2018 at 16:20
2 Answers 2
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:
- Doing so will force the counter to be allocated to RAM, thus consuming a scarce and valuable resource for no good reason.
- 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()
. - Your actual delay will depend on the clock frequency, and probably also on the compiler's version, making your code pretty fragile.
-
1Upvoted for the (much extra) explanationMichel Keijzers– Michel Keijzers2018年02月21日 21:11:35 +00:00Commented Feb 21, 2018 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 likestd::cout<<"hello world!\n";
, that's a call to an external library function.Solomon Slow– Solomon Slow2018年02月22日 01:44:43 +00:00Commented Feb 22, 2018 at 1:44 -
static
loop counter should work just as good in this particular case, saving some of the load/store instructions.wondra– wondra2018年02月22日 06:52:03 +00:00Commented Feb 22, 2018 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– Edgar Bonet2018年02月22日 10:12:07 +00:00Commented Feb 22, 2018 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– Edgar Bonet2018年02月22日 10:12:48 +00:00Commented Feb 22, 2018 at 10:12
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.
-
1Even t is marked volatile, the loop will execute that quickly you won't even see it flickering.Kwasmich– Kwasmich2018年02月21日 13:53:22 +00:00Commented Feb 21, 2018 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– Michel Keijzers2018年02月21日 13:54:19 +00:00Commented Feb 21, 2018 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– Michel Keijzers2018年02月21日 13:55:33 +00:00Commented Feb 21, 2018 at 13:55
-
2it is not used afterwards and has no side effects, so the compiler can optimize it away.Kwasmich– Kwasmich2018年02月21日 13:59:18 +00:00Commented Feb 21, 2018 at 13:59
-
165535 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– Kwasmich2018年02月21日 14:08:30 +00:00Commented Feb 21, 2018 at 14:08