Preprocessor trick to convert F_CPU to string in unit MHz

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

Hello Freaks,

is there a simple way to make the following code valid for any clock frequency:

#if   F_CPU ==  8000000UL
#   define F_CPU_STR "8MHz"
#elif F_CPU == 12000000UL
#   define F_CPU_STR "12MHz"
#elif F_CPU == XY000000UL
#	define F_CPU_STR "XYMHz"
#else
#   warning "unknown frequency"
#   define XSTR(_s_) STR(_s_)
#   define STR(_s_)  #_s_
#	define F_CPU_STR  XSTR(F_CPU) "Hz" // output may contain a lot of zeros and 'UL' of constant F_CPU
#endif
printfP(PSTR("CrystalFrq: " F_CPU_STR "\n"));

As you can see I want to put the frequency in MHz units as string into the binary.
I could use the compiler, but I want to verify with a simple glance on the binary file for what it was compiled.

A division by 1000000UL with a later stringifictaion seems not wo work.

I was wondering, if I already got the last out of the preprocessor. There are some discussions, if the preprocessor can perform integer divisions or not, for example at http://stackoverflow.com/questions/1560357/can-the-c-preprocessor-perform-integer-arithmetic.
And there is also the preprocessor boost library, which can also perform integer arithmetics.

I also studied this thread:
https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=100515&start=0&postdays=0&postorder=asc&highlight=preprocessor+division
h
Is there a easy way to avoid the if/elif/else preprocessor clause with a closed expression?

BTW: Maybe the GNU make tool can already calculate F_CPU_MHZ = F_CPU/1000000UL somehow?!

A solution for this is not essential, but it would make my code more generic...

Best regards,
michaeL

In the beginning was the Word, and the Word was with God, and the Word was God.

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

Well I was going to say "stringification" but when I look up a link to it I find:

http://gcc.gnu.org/onlinedocs/cp...

the code from which looks awfully familiar ;-)

But given #define F_CPU 11059200UL if you stringify it to be be "11059200UL" then in run time C code you chop any trailing non-digits then work 6 digits back from the end and insert a '.' does that not get you where you want to be? That is "11.059200".

Or do you want to do everything at compile time with no run-time overhead?

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

There are no more than 15 common frequencies:
128kHz, 1MHz, 1.832MHz, 2MHz, 3.684MHz, 4MHz, 7.3728MHz, 8MHz, 10MHz, 11.0592MHz, 12MHz, 14.374MHz, 16MHz, 18.432MHz, 20MHz.
You can do Maths in the preprocessor conditionals.
So one simple header file can issue the relevant string value with a #warning statement.
And pass the stringised result to the rest of your program.

You can add any extra 'common' frequency that you might like.

As an included' header file it involves no extra typing or extra text in your source code.

David.

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

Thank you, Clawson and David for your ideas.
Yes, on one hand I want to have the number as an ASCII string in my binary, to see F_CPU; on the other hand I want to output it in a 'nice' format within the application.
A header file with the 'common' frequencies would do it and hold the C file clear.
But maybe I will strike a compromise: in the binary I will just take the stringified F_CPU value (incl. zeros and UL), at runtime I will parse the string and split off the last 6 digits. Alternatively I could 'scanf' the string and print it in any format...

But if anybody has a more elegant method, I would appreciate to know it...

It is even more complicated, if I want to do it with a prescaled timer value:
Getting a string like "15.625 kHz" at compile time(!), instead of "16000000UL/1024 Hz" is a challenge... ;-)

Regards, michaeL

In the beginning was the Word, and the Word was with God, and the Word was God.

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

why you are using macros?
i am new to this world.
but i think this will not function correctly, because the preprocessor will process the text, and it will substitute F_CPU with the right values of frequency, and compile de code after that.
so F_CPU will have all the time the same value, and the value is the value when you have compiled it.
Because you code above will not make part of the code that will be loaded do microcontroller (it is a macro, and it will be substituted with the F_CPU value at de moment of compilation i think).
Maybe I'm wrong, its the best probability lol

regards

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

Write a simple program on PC, which takes the "number" as input parameter then creates e.g. a header file with defining a macro with the resulting string. Then call this program from your makefile.

Some would probably suggest some perl/shell/awk/sed script - I don't like these.

JW

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

See COG python module by Ned Batchelder

Install cog (and python if you have not it yet)

Add rule to makefile

#...
# May be some dances with path to python.exe and/or cog.py will be needed in Windows
COG=cog.py

F_CPU = 7372800

#... for avr-gcc - add UL suffix here
CFLAGS += -DF_CPU=$(F_CPU)UL
#...
# f_cpu_str.h depend on makefile because F_CPU can be changed here
f_cpu_str.h : f_cpu_str.cog makefile
	echo ====        make $@ from $< ; $(COG) -D F_CPU=$(F_CPU) $< >$@

Add f_cpu_str.cog file to project

/*[[[cog
import cog
''' convert F_CPU define into human-readable string
'''
freq = float(F_CPU) / 1000000.0
cog.out('#define F_CPU_STR "%.3fMHz"' % freq )
]]]*/
//[[[end]]]
~$ make f_cpu_str.h

Include f_cpu_str.h where you need it.

/*[[[cog
import cog
''' convert F_CPU define in human-readable string
'''
freq = float(F_CPU) / 1000000.0
cog.out('#define F_CPU_STR "%.3fMHz"' % freq )
]]]*/
#define F_CPU_STR "7.373MHz"
//[[[end]]]

p.s. I use cog for jobs like decode table generation

uint8_t decode_address_nibble(uint16_t adc_value)
{
   static const uint16_t level_table[] PROGMEM = {
/*[[[cog
rcommon = 7.5
resistors = (62.0, 30.0, 15.0, 7.5)
nres = len(resistors)
code = 1023

for i in range(1,2**nres) :
    rpar = 0

    for k in range(nres) :
        if i & (1<<k) :
            rpar += 1 / resistors[k]

    prevcode, code = code, 1023.0 - 1024.0 * rcommon / (1.0/rpar + rcommon)

    cog.out( ' %d,' $ round( (prevcode+code)/2 ) )

]]]*/
//[[[end]]]
   };

   return search_level_less( adc_value, level_table, sizeof(level_table)/sizeof(level_table[0]));
}

wbr, ReAl

Last Edited: Tue. Feb 28, 2012 - 01:31 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek wrote:
perl/shell/awk/sed script - I don't like these
You forgot python.

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

bash and dc is enough :lol:

makefile

F_CPU = 14745600

#... for avr-gcc
CFLAGS += -DF_CPU=$(F_CPU)UL
#...

f_cpu_str.h : makefile
	@echo '#define F_CPU_STR "'`dc -e "3k $(F_CPU) 1000000 / p"`'MHz"' >$@
#define F_CPU_STR "14.745MHz"

But python + cog is a programmer swiss knife.

wbr, ReAl

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

Quote:

wek wrote:

perl/shell/awk/sed script - I don't like these

You forgot python.

And Ruby.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

JohanEkdahl wrote:
Quote:

wek wrote:

perl/shell/awk/sed script - I don't like these

You forgot python.

And Ruby.

And C? :?

(as a C programmer I'd be tempted to write a C program if I wanted to pre/post process build files. YMMV)

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

I was kind'of assuming that he was referring to interpreted "scripting languages".

I assume you where thinking of making a complete list of languages that wek dislikes - we all know he hates C (but still seems to sleep with her...).

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Quote:

I was kind'of assuming that he was referring to interpreted "scripting languages".

Personally I "script" quicker in C than anything else. I have a smattering of the usual candidates (awk, sed, perl, python, bash) but if I want a job done quickly and efficiently I write in C. The 5 seconds it takes to compile it to a "script" doesn't matter much to me.

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

Well, the UL at the end of the CPU frequency is redundant, isn't it? All relevant numbers (except 32768 and 128000) are automatically long, and none hit the value where they NEED to be unsigned.

So you have "#define F_CPU 16000000" and you want to generate "char *CPUMHZ = "16";" ??
I'll be there's something you can do with inline assembly language and an assembler macro. Maybe even without the macro.

Alternately, you could pre-process F_CPU in the makefile, where you have all sorts of unix-utils available to create the variables eventually passed to the compiler...

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

If you can go the other way around and start with

#define CPUMHZ_DEF 16

(or the corresponding -d compiler/preprocessor switch) you can use that to generate both the #define of F_CPU and the definition of the variable.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

perl/shell/awk/sed/Ruby/python/C - I don't like these.

;-)

Jan

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
#define F_CPU_MHz 7.32728
#define F_CPU     (F_CPU_MHz * 1000000)
    ...
    printf( #(F_CPU_MHz) "MHz" );
    ...

I use F_CPU. Occasionally I display a MHz value:

    printf("F_CPU is %fMHz", 0.000001 * F_CPU);

David.