#define problem in IAR - Problem solved

Go To Last Post
12 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi!

I'm working on a test program for an application using a mega128, and I just wanted to test the I2C (TWI) interface. I did the following frequency definitions in a header file:

#define Xtal    16000000    // uC speed = 16Mhz
#define TWI_FREQUENCY    400000    // 400 kHz Prescaler = 0x00
#define TWI_BIT_RATE    (Xtal/(2*TWI_FREQUENCY)-8)    // Bit Rate if prescaler = 0x00

and the following in the TWI initialization:

TWBR = TWI_BIT_RATE;

Everything works fine.
The problem appears when I want to try 20kHz with these definitions:

#define Xtal    16000000    // uC speed = 16Mhz
#define TWI_FREQUENCY    20000    // 20 kHz Prescaler = 0x01
#define TWI_BIT_RATE    (Xtal/(8*TWI_FREQUENCY)-2)    // Bit Rate if prescaler = 0x01

Then I get the following warning when trying to write the TWI_BIT_RATE to the TWBR register:
C:\Prosjekter\Sintef\SDF\twi.c (93) : Warning[Pe061]: integer operation result is out of range
I've tried several combinations, and it seems it is the value of TWI_FREQUENCY that is the problem. If it is less than 33kHz I get the "out of range"-error regardless of which TWI_BIT_RATE-definition I use.

Anybody have any suggestions to what might be the problem?

Regards
Jan Egil

Last Edited: Thu. Apr 14, 2005 - 07:22 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You might want to try

Quote:

#define Xtal 16000000L // uC speed = 16Mhz

to force 32-bit calculations in the preprocessor, and then cast the result to another size when used if needed.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey,

Note that there is a possible problem with your comments:

#define Xtal    16000000    // uC speed = 16Mhz

So you tell it to replace Xtal with everything following... while that means "16000000 // uC speed = 16Mhz". Then all of a sudden when you try to use the value it will blank out whatever follows, since your comment tells it that the rest of the line is a comment. Instead do this:

#define Xtal    16000000L    /* uC speed = 16Mhz */

Since it will end the comment. I'm not sure if it is related, but just a note (and use the L as Lee points out).

-Colin

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is a good point, Colin. In practice with CodeVision I've never had a problem using the construct as Jan presented it.

Do you know for a fact that the pre-processor will not throw away the rest of the line following the // start-of-comment? Maybe some C guru with the standard handy can verify. All my C reference books [being a grumpy old guy] are too old to even have the C++ style // comment even listed.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey,

The pre-processor may throw away the rest of the line, but AFAIK it isn't supposed to:

Quote:
A preprocessing directive of the form
# define identifier replacement-list new-line
defines an object-like macro that causes each subsequent instance of the macro name to be replaced by the replacement list of preprocessing tokens that constitute the remainder of the directive.

Here is some examples later on - they only use the /* */ style comments in the valid sequences, and use // style in the invalid sequences. However the comments are not what is mentioned as making the directive invalid, so not sure what to make of it.

Quote:
EXAMPLE 6 To demonstrate the redefinition rules, the following sequence is valid.
#define OBJ_LIKE (1-1)
#define OBJ_LIKE /* white space */ (1-1) /* other */
#define FUNC_LIKE(a) ( a )
#define FUNC_LIKE( a )( /* note the white space */ \
a /* other stuff on this line
*/ )

But the following redefinitions are invalid:
#define OBJ_LIKE (0) // different token sequence
#define OBJ_LIKE (1 - 1) // different white space
#define FUNC_LIKE(b) ( a ) // different parameter usage
#define FUNC_LIKE(b) ( b ) // different parameter spelling

BTW - looking over the preprocessing section, I remebered a handy old friend I don't see used too much that I'd all but forgotten about. It is the ## operator, which you can do this with:

#define LED_CONNECTION B

DDR ## LED_CONNECTION |= 1<<3;
PORT ## LED_CONNECTION |= 1<<3;

Useful if you want to define the connection pin that a device is on, but this way you only need to define the A/B/C/D in one spot, and not PORTB, PINB, etc.

Regards,

-Colin

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think the problem (as hinted above) is that "//" comments aren't (last I heard)
part of the C language, but rather a "standard" extension, which means they get
implemented as the compiler author chooses.

"True" C comments ("/**/") are defined by the preprocessor language, so they are defined
to disappear (actually replaced by a single space character) before token replacement.
Whether "extended" C comments ("//") are defined by the preprocessor language or
by the (post-preprocessor) C language is I think up to the author.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

From one Web source:

Quote:
...GNU CPP ... transformations correspond roughly to the first three "phases of translation" described in the C standard. ...

1. The input file is read into memory and broken into lines. ...
2. If trigraphs are enabled, they are replaced by their corresponding single characters. ...
3. Continued lines are merged into one long line. ...
4. All comments are replaced with single spaces. ...

If 4. holds, then // in #define is no more dangerous than /* frog */

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey,

I read the rest of that website - and it sounds like // isn't dangerous in #define because of replacing it with whitespace. Since that happens before the token is created and replaced according to the site I'm pretty sure your right.

Regards,

-Colin

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I think the problem (as hinted above) is that "//" comments aren't (last I heard)
part of the C language, ...

From http://www.comeaucomputing.com/t... :

Quote:

Tech Talk About C99
Comeau C99 FAQ
Copyright © 2001-2005 Comeau Computing. All rights reserved. The intent of this page is to address questions about C99. C99 is a new revision to Standard C, ...

What are // comments?
// style comments, ala C++, are now available in C99. A comment of this form starts with // and ends at the end of the line.
...
Note also that // is in addition to, not instead of /* */ comments, so both can be used: ...

Nesting the two different styles of comments is generally ok: ...

Take note also that the end of line in the new comments must account for line splicing: ...

so unless line splicing is used (not an issue with the code shown by OP) then // at the end of simple #define lines should be OK.

So according to this source, it IS part of standard C. Of course, the compiler writers are free to conform or not conform, so YMMV.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mckenney wrote:
I think the problem (as hinted above) is that "//" comments aren't (last I heard)
part of the C language

The // comments are not a part of the "C89" standard, but they are now a part of the "C99" standard, along with some other useful goodies.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for all your help!

The problem is now solved. It wasn't the comments, and it wasn't the L at the end of the Xtal definition (But I'll start using it because it is probably a better way of coding :D ).
No, the solution was an L at the end of the TWI_FREQUENCY definition, like this:

#define TWI_FREQUENCY    20000L    // 20 kHz Prescaler = 0x01

As I said in the first post it all worked fine if the TWI_FREQUENCY was 33000 or above. 32000 didn't work. This all fits mysteriously with the limit of a 16bit signed int which is 32767. It seems to me that the preprosessor could not do the division

#define TWI_BIT_RATE    (Xtal/(8*TWI_FREQUENCY)-2)    // Bit Rate if prescaler = 0x01

if the numerator was a 32 bit long and the denominator was an 16bit int. By forcing the TWI_FREQUENCY to a long it all works fine.
I couldn't have done this without your help. Thanks again.

Jan Egil

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

... An even better solution is to make sure the frequency value is multiplied with a long before the division, like this

#define TWI_BIT_RATE    (Xtal/(8L*TWI_FREQUENCY)-2)    // Bit Rate if prescaler = 0x01

(I've put an L after the 8 )

Here's my final result: an flexible, easy to use I2C bit rate calculation for mega128

#define Xtal    16000000L    // uC speed = 16Mhz

#define TWI_FREQUENCY      20000     // 20 kHz Prescaler = 0x01
//#define TWI_FREQUENCY      33000     // 33 kHz Prescaler = 0x00
//#define TWI_FREQUENCY      400000    // 400 kHz Prescaler = 0x00

#define TWI_PRESCALE_BITS  0x01      // If prescaler is changed the bit rate formular must be divided by 4 raised to the power of the value of the prescaler bits

#if TWI_PRESCALE_BITS == 2
    #define TWI_BIT_RATE   ((Xtal/(2L*TWI_FREQUENCY)-8)/16)   // Bit Rate if prescaler = 0x02
#elif TWI_PRESCALE_BITS == 1
    #define TWI_BIT_RATE   ((Xtal/(2L*TWI_FREQUENCY)-8)/4)    // Bit Rate if prescaler = 0x01
#else
    #define TWI_BIT_RATE   (Xtal/(2L*TWI_FREQUENCY)-8)        // Bit Rate if prescaler = 0x00
#endif