Serial statements ignored when begin method commented out?

Setting baud rate register (correct my term) is all Serial.begin does:

And it enables the rx, tx and rx interrupt (see above)

From the data sheet:

The USART Transmitter is enabled by setting the Transmit Enable (TXEN) bit in the UCSRnB Register. When the Transmitter is enabled, the normal port operation of the TxDn pin is overridden by the USART and given the function as the Transmitter’s serial output.

Serial.begin does this:

  sbi(*_ucsrb, _rxen);
  sbi(*_ucsrb, _txen);
  sbi(*_ucsrb, _rxcie);

So without calling Serial.begin the hardware has not enabled serial comms.

tim7: The gcc compiler is intelligent enough to remove any code blocks which are guaranteed never to be used. So in the following example, ``` #define DEBUG 0 ``` has exactly the same effect as commenting out the debugging lines. The compiler can also recognise and remove conditional statements which are always true, so writing ``` #define DEBUG 1 ``` will have the effect of deleting the ``` if (DEBUG) ``` check. In either case, the ``` if ``` statements will never actually be compiled into the binary and executed.

Tim7, This is good to know, seems a little simpler to use than IFDEF. Do you know if it is the pre-processor that removes these items based on evaluating directives like #define or does it also do it for the regular code? Like for a function that is never called?

willnue

[quote author=Nick Gammon link=topic=69181.msg512376#msg512376 date=1313182914] So without calling Serial.begin the hardware has not enabled serial comms. [/quote]

So the mystery deepens...

Where do the bits from Serial.print go then when Serial.begin is not called?

willnue

So without calling Serial.begin the hardware has not enabled serial comms.

Does that mean the bootloader changed back the comm setting bits to their power-up default values when it timed out and jumped to the users sketch?

Lefty

willnue: Where do the bits from Serial.print go then when Serial.begin is not called?

The USART is not configured to output them so it ignores them.

However I think you are better off with the "define" approach. But not this way:

if (DEBUG) Serial.begin(19200);

That is generating code:

if (0) Serial.begin(19200);

Now the compiler might be smart enough to optimize that out, but why rely on that? Instead do:

#if DEBUG
  Serial.begin(19200);
#endif

Now the code is not even generated if DEBUG is not true.

And later on:

#if DEBUG
  Serial.println ("some debugging info");
#endif

(or use #ifdef DEBUG, they are subtly different).

You can make a #define that takes the hard work out, like this:

// make true to debug, false to not
#define DEBUG true

// conditional debugging

#if DEBUG
  #define TRACE(x)   Serial.print (x)
  #define TRACELN(x) Serial.println (x)
#else
  #define TRACE(x)   ((void)0)
  #define TRACELN(x) ((void)0)
#endif // DEBUG

long counter;
unsigned long start;

void setup() {
  start = micros ();

  #if DEBUG
    Serial.begin (115200);
  #endif  // DEBUG
}  // end of setup

void loop() 
{

  counter++;
  if (counter == 100000)
  {
    TRACELN ("100000 reached.");
    TRACE ("took ");
    TRACELN (micros () - start);
  }  // end of if

}  // end of loop

Now the macros TRACE and TRACELN only output if DEBUG is true, otherwise not. If it isn't true the compiler generates ((void)0) which is optimized away to nothing.

(edit)

((void)0) isn't exactly optimized away, it is a "do nothing" construct. Thus it isn't relying on optimization, the compiler genuinely cannot generate code for it, although it is valid.

retrolefty: Does that mean the bootloader changed back the comm setting bits to their power-up default values when it timed out and jumped to the users sketch?

No it didn't. However "init" in your sketch did (last instructions in init):

    // here so they can be used as normal digital i/o; they will be
    // reconnected in Serial.begin()
#if defined(UCSRB)
    UCSRB = 0;
#elif defined(UCSR0B)
    UCSR0B = 0;
#endif

If it didn't do that, then you couldn't use D0 and D1 for "normal digital I/O" in case you didn't need Serial comms.

[quote author=Nick Gammon link=topic=69181.msg512555#msg512555 date=1313195615] [Now the macros TRACE and TRACELN only output if DEBUG is true, otherwise not. If it isn't true the compiler generates ((void)0) which is optimized away to nothing. [/quote]

I had no idea you could define macros like that. I really like this approach since the inline debugging statements are much cleaner.

Thanks for all your help!

willnue

However “init” in your sketch did

D’oh! Forgot “init”! I lose my (small) bet.

#if DEBUG
  #define TRACE(x)   Serial.print (x)
  #define TRACELN(x) Serial.println (x)
#else
  #define TRACE(x)   ((void)0)
  #define TRACELN(x) ((void)0)
#endif // DEBUG

The “((void)0)” are not necessary - they can be omitted.

AWOL:
The “((void)0)” are not necessary - they can be omitted.

I was wondering why some of the code examples I saw used them, because on the face of it you are right. But there is a reason …

Consider this case:

// make true to debug, false to not
#define DEBUG false

// conditional debugging

#if DEBUG
  #define TRACE(x)   Serial.print (x)
#else
  #define TRACE(x)
#endif // DEBUG

void setup() {} 

void loop() 
{
long counter;
  counter++;
  if (counter)
    TRACE ("foo");
  else
    TRACE ("bar")
 counter++;
}  // end of loop

That compiles OK. But it shouldn’t! There is a missing semicolon after “TRACE (“bar”)”. The conversion of TRACE(x) to has hidden that. So actually “counter++;” is done conditionally.

Now we’ll put the “((void)0)” back …

// make true to debug, false to not
#define DEBUG false

// conditional debugging

#if DEBUG
  #define TRACE(x)   Serial.print (x)
#else
  #define TRACE(x)   ((void)0)
#endif // DEBUG

void setup() {}  

void loop() 
{
long counter;
  counter++;
  if (counter)
    TRACE ("foo");
  else
    TRACE ("bar")
 counter++;
}  // end of loop

Now I get the error:

sketch_aug12c.cpp: In function 'void loop()':
sketch_aug12c:24: error: expected `;' before 'counter'

It is correctly saying I have forgotten a semicolon after my debugging print.

It’s a subtle point, but leaving out semicolons accidentally is not unknown. :wink:

So actually “counter++;” is done conditionally.

Which is a good reason to never omit braces for conditionals - many coding standards require them to be there.

Good point though - thanks!

willnue: Tim7, This is good to know, seems a little simpler to use than IFDEF. Do you know if it is the pre-processor that removes these items based on evaluating directives like #define or does it also do it for the regular code? Like for a function that is never called?

I don't know at what stage the optimisation takes place, but I suspect it's later in the compilation. Unlike IFDEF you can't totally rely on this kind of simplification happening, as it depends on the capabilities of the compiler and the complexity of the code. But for non-professional (or un-professional :)) programmers like myself it's a handy shortcut.

it’s a handy shortcut

I fail to see the “shortcut” here. #ifdef FOO isn’t exactly much longer than if(FOO) {. In fact they’re the same length, but the former conveys a clear message, the conditional compilation.

Do you know if it is the pre-processor that removes these items based on evaluating directives like #define or does it also do it for the regular code?

The preprocessor only cares about lines beginning with a # sign, and macros outside of strings. It is not responsible for any optimizations (the #ifdef procedure is not an optimization, it’s simple conditional generation of code that will be passed to the compiler).

dominikh: I fail to see the "shortcut" here

Well, it saves you the #endif and allows you to write the whole thing on one line instead of three. However if you want to be lazy and right, then use the macros in Nick Gammon's example.