EEVblog Electronics Community Forum

Products => Computers => Programming => Topic started by: PerranOak on September 25, 2019, 03:14:19 pm

Title: While {you're down there ...}
Post by: PerranOak on September 25, 2019, 03:14:19 pm
In one of my first exercises learning C, I had to: light a LED, wait with a while loop for a button press then go on.

I can't think what syntax to use for a completely empty while.

Is it just:

while(button!=0) {}


BTW is this for "Beginners" or "Programming"?
Title: Re: While {you're down there ...}
Post by: dave j on September 25, 2019, 03:46:05 pm
Code: [Select]
while(condition){}
will work, as will
Code: [Select]
while(condition);

The former is preferable as it is more obvious that you meant to have an empty loop.

Don't forget, in your code you'll need to make sure button gets updated otherwise you'll be stuck in an infinite loop.
Title: Re: While {you're down there ...}
Post by: dcbrown73 on September 25, 2019, 03:52:52 pm
This is not an electronics question, it's a programming question.

As for your question, button is not defined.    How is button defined?  Is it a variable that is Boolean true or 1 if the button has been pushed? 

If it's a boolean, you do not even need to test if it's != 0.

EDIT:  I'm guessing this is C programming you're referencing?

// Button not True (or 0)
while  !(button) {
    // do whatever
};

// Button is true (or anything other than 0)
while (button) {
    // do whatever
};
   

Title: Re: While {you're down there ...}
Post by: PerranOak on September 25, 2019, 04:28:11 pm
Cheers all.

So next time should I post a dopey noob question re C in "Programming"?
Title: Re: While {you're down there ...}
Post by: rstofer on September 25, 2019, 04:36:34 pm
In one of my first exercises learning C, I had to: light a LED, wait with a while loop for a button press then go on.

I can't think what syntax to use for a completely empty while.

Is it just:

while(button!=0) {}


I would probably do something like

while( button != 0 ) {
}

And somebody won't like it...

The problem I have is what exactly is 'button'.  Is it a variable that is changed in an interrupt routine?  If so, it needs to be declared as 'volatile'.  If it is a function that returns the state of the switch then the statement is wrong and should probably be:

while( button() != 0 ) {
}

if button is actually a MACRO like

#define button ( PORTB & 0x0001 )

it would probably work but macros should be all caps like

#define BUTTON ( PORTB & 0x0001 )

and

while( BUTTON != 0 ) {
}
Title: Re: While {you're down there ...}
Post by: magic on September 25, 2019, 05:08:33 pm
Firstly, button != 0 is same as simply button. That's actually a reasonable change to make.

And secondly, for is less keystrokes than while :-DD

for(;;button);
Title: Re: While {you're down there ...}
Post by: Kilrah on September 25, 2019, 06:33:31 pm
I count the same number of keystrokes actually ;)
Title: Re: While {you're down there ...}
Post by: SiliconWizard on September 25, 2019, 06:38:40 pm
Very much a matter of style. To each their own pretty much.

I usually do the following:
while (condition) ;

The advice to use braces instead of a semicolon makes sense; it avoids wreaking havoc if you ever forget the ';' or inadvertently delete it. I just don't do it. I'm not afraid. Go figure.

And most often, I use
Code: [Select]
for (;;)
instead of while (1) or while (true) for infinite loops. My rationale is that to me it looks different enough from any other loop that it's immediately obvious to the eye it's an infinite loop. Again, just a matter of style.

As a side note, waiting for conditions like this is very common in embedded dev, but make sure you know what you do - they may get your software stuck forever.
So when you do this, make sure that the condition IS guaranteed to occur in a timely fashion whatever happens, OR that you have enabled a watchdog. Sometimes a preferred approach is to implement a time-out, that you would add as a condition to get out of the loop.
Title: Re: While {you're down there ...}
Post by: SiliconWizard on September 25, 2019, 06:39:46 pm
And secondly, for is less keystrokes than while :-DD

for(;;button);

Yeah, but the condition would have to be placed second, not third. Otherwise it will never get out.
Code: [Select]
for (; button; ) ;

Fewer keystrokes may be good, but beware of typos... :P
Title: Re: While {you're down there ...}
Post by: maginnovision on September 26, 2019, 04:54:08 am
I like while for this type of stuff. This is how I'd usually do it, depending on polarity.

Code: [Select]
while (!(PORT & MASK)) {};
Title: Re: While {you're down there ...}
Post by: PerranOak on September 26, 2019, 10:03:40 am
Wow! I can't believe there are so many ways to do such a simple thing! It's traffic lights and is supposed to wait on red until the button is pressed then go through the (UK) sequence pausing on green a little longer.

BTW the whole programme is (and I don't get the "static bit button" part either!) as below:

       #include <xc.h>

       static bit button @ (unsigned) &PORTA*8+1;
       
       void main(void){
         unsigned int count;
         ANSELA = 0;
         ANSELB = 0;
         TRISB = 0;

         while(1==1)
         {
           while(button!=0)                                             //wait for button to be pressed
           {
           PORTB = 0b00000001;                                    //red
           }
           PORTB = 0b00000011;                                   //red+yellow
           for( count=40000; count>0; count=count-1 );
           PORTB = 0b00000100;                                   //green
           for( count=65000; count>0; count=count-1 );
           PORTB = 0b00000010;                                   //yellow
           for( count=40000; count>0; count=count-1 );
         }
         return;
       }
Title: Re: While {you're down there ...}
Post by: sleemanj on September 26, 2019, 10:26:06 am
Code: [Select]
while(1==1)

Is usually written

Code: [Select]
while(true)

(but will optimise to the same thing anyway).

Code: [Select]
  for( count=40000; count>0; count=count-1 );  

Unless you have a really really slow clock that will be done in an instant.  You probably want to insert some delaying function in there, looks like you are targetting an AVR, so ....

https://www.nongnu.org/avr-libc/user-manual/group__util__delay.html (https://www.nongnu.org/avr-libc/user-manual/group__util__delay.html)
Title: Re: While {you're down there ...}
Post by: Kilrah on September 26, 2019, 11:05:33 am
That looks like PIC, not AVR. But the XC compiler certainly has built in delay functions.

The compiler might optimize out the entire empty loop too.
Title: Re: While {you're down there ...}
Post by: magic on September 26, 2019, 07:20:10 pm
Yeah, these days it's safer to make the dummy count variable a volatile int or use some delay function which does more or less the same (in the case of MCUs).

Code: [Select]
while(1==1)

Is usually written

Code: [Select]
while(true)
Won't work in C without including stdbool.h.
Just while(1), that's exactly what true boils down to.
Title: Re: While {you're down there ...}
Post by: SiliconWizard on September 26, 2019, 08:00:37 pm
On modern MCUs with caches and branch prediction and whatnot, those delay loops are virtually useless, even when you make sure they don't get optimized out (which is still a common trap for young players). Just adapting them to the clock freq won't work to get any sensible idea of the real delay.

On ARM Cortex ones, you can use special counter registers for that instead. I think PIC32 also have CPU counters which you can use for that as well.
Title: Re: While {you're down there ...}
Post by: westfw on September 26, 2019, 08:08:26 pm
Empty loops make me a bit nervous, especially from a readability point of view.
(and see also: https://stackoverflow.com/questions/3592557/optimizing-away-a-while1-in-c0x (which I think is not directly applicable, but adds to the nervousness.)

Code: [Select]
while (!button);
is particularly bad.  "Is that semicolon really supposed to be there?"
Comments are good, like the "// Fall through" comment in case statements.
Code: [Select]
while (!button)  ; /* spin, waiting */
while (!button)
    ; // spin
while (!button) {
   // wait
}
are all better.
And I'll put in a new suggestion
Code: [Select]
while (1) {
   if (button)
      break;
}
which I like for indeterminate and long loops.  (if you're checking something like a UART status flag, it's a loop that you expect to terminate "soon."  Whereas with a button, it could be a very long time indeed.  The less likely the event is to occur, the more I like to emphasize the "infinite-ness" of the loop.  Or something like that.  YMMV, local styles override, etc...)
Title: Re: While {you're down there ...}
Post by: rstofer on September 26, 2019, 10:56:17 pm

On modern MCUs with caches and branch prediction and whatnot, those delay loops are virtually useless, even when you make sure they don't get optimized out (which is still a common trap for young players)
. Just adapting them to the clock freq won't work to get any sensible idea of the real delay.

On ARM Cortex ones, you can use special counter registers for that instead. I think PIC32 also have CPU counters which you can use for that as well.

This is a HUGE trap when optimization is turned on.  The program ran great, everything is ready to ship, let's optimize the code and go out for Pizza.

In theory, and I wouldn't want to guarantee it, if the variable in the while statement is declared volatile, the loop shouldn't be optimized away.  But it also has to be a global variable, reachable from the world.  The idea is that an interrupt routine, in another module, could change the value so the compiler can't just assume it never changes and, therefore, it leaves the code alone.

Spin loops are problematic!

Title: Re: While {you're down there ...}
Post by: westfw on September 27, 2019, 08:34:27 am
Here's an improved delay mechanism for Cortex-M4, using the 32bit cycle counter that is part of the "Debug, Watchpoint, and Trace" (DWT) unit that is present in (most?) CM4 chips...

https://github.com/adafruit/ArduinoCore-samd/pull/77/commits/faf28a90ca4c97229736bd5fbbbbba5b1bbcc808 (https://github.com/adafruit/ArduinoCore-samd/pull/77/commits/faf28a90ca4c97229736bd5fbbbbba5b1bbcc808)

(replaces the instruction-counting loop in the Adafruit SAMD51 Arduino core that (surprise!) didn't work right with the cache enabled.)


All ARM Cortext chips have Systick, which can be used similarly (but it's only 24 bits, and is usually set to reload periodically, and counts down, which makes it more awkward to use.  https://github.com/WestfW/Duino-hacks/blob/master/systick_delay/systick_delay.ino (https://github.com/WestfW/Duino-hacks/blob/master/systick_delay/systick_delay.ino) )
Title: Re: While {you're down there ...}
Post by: SiliconWizard on September 27, 2019, 06:06:05 pm
Yup, I actually gave some code for STM32 (HAL, but the HAL part only to get the clock frequency) here:
https://www.eevblog.com/forum/microcontrollers/ad7685-reading-data-problem/msg2687904/#msg2687904 (https://www.eevblog.com/forum/microcontrollers/ad7685-reading-data-problem/msg2687904/#msg2687904)
Title: Re: While {you're down there ...}
Post by: Siwastaja on September 30, 2019, 10:18:31 am
On modern MCUs with caches and branch prediction and whatnot, those delay loops are virtually useless...

I don't understand this. I have done quite some work with modern MCUs with caches, and they mostly come with core-coupled instruction scratchpad, and I just simply put all even remotely timing-critical code there. It has the same performance than having everything 100% in cache with no misses, and is predictable, including the first iteration.

Branch prediction in a simple delay loop should be predictable as well.

IMO, caches are there to increase the average performance of large routines when you run out of small core-coupled RAM and have to run "directly" out of FLASH, or, worse, out of external SD card or similar. But this doesn't matter much for small timing critical routines (which the delay loops obviously are) - just run them out of instruction RAM.

I have never turned caches on in an MCU project; turns out, I can always fit all timing-critical processing in tightly coupled RAM, and the rest can be "slow" from flash.

But even if you "have" to run it from cache - it's going to be a small offset at the start, depending on whether it produces a miss or not in the first cycle. Assuming you still run from the internal flash, the difference is a flash cycle or two, or maybe about 20ns.

And I'm using delay loops extensively. Of course they aren't good for precision timing in presence of interrupts, but neither are timer-based busy loops, or interrupt handlers. Doing it accurately in a system with existing interrupts requires a big picture understanding, i.e., setting your interrupt priorities right while making sure nothing breaks in edge cases.

Using a simple delay loop to implement something at least won't risk the existing interrupts, and it's very obvious that the delays are going to be longer than specified depending on interrupt load level. This is much easier than to add a new interrupt handler, configure the pre-emptive priorities right, and still get jitter to the less important task.