[TUT] Modularizing C Code: Managing large projects

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

For an updated version of this tutorial in PDF format, please see this page of my website.

Modularizing C Code: Managing large projects

Hi Freaks! Time for another half-baked tutorial! This one will focus on how to split up your C projects into manageable files (modules). It's by no means definitive, and serves as only a guide.

There comes a time when a project has outgrown the scope of a single file. Perhaps it's the point at with you need to add more RAM to your computer just to open your project, or when you realize you've just spent 45 minutes tracking down the timeout function you swore you wrote two weeks ago. Whatever the reason, it's time to split your project up into several manageable files, which you can then keep better track of. This tutorial will give several pointers about the intricacies of how to accomplish such a task.

Step one - identify common routines

First of all, it makes sense to group all the common routines into their own files - eg. one for USART management, one for your protocol layer handler, one for your main code. You should determine which functions can be moved into a separate files while still retaining some connection with one another. Let's take the following example of routines:

GetUSARTByte()
TurnOnStatusLed()
SetupSpeaker()
TurnOffStatusLed()
SetupUSART()
main()
CheckTempSensorVal()
ProcessByte()
Beep()
EnableUSART()
SleepMode()
CheckADCChanel()

We can split these routines up into several small files - one for the USART, one for the main (and misc) routines, one for the speaker and one for the ADC (and related processing).

Let's take a look at our proposed new structure:

USART.c

SetupUSART()
EnableUSART()
GetUSARTByte()
ProcessByte()

Speaker.c

SetupSpeaker()
Beep()

ADC.c

CheckADCChanel()
CheckTempSensorVal()

Main.c

main()
SleepMode()
TurnOnStatusLed()
TurnOffStatusLed()

Ok, looks pretty good! We'll cut and paste those routines into the appropriate files in the same project directory, so now we have a bunch of C files containing related functions.

Adding the new files to your makefile

Even though you've made no functional changes to your code outside moving certain routines to a different file, you still need to tell the compiler, linker and associated GCC tools where everything is now located. Open up your makefile, and you should find some lines similar to the following (if your makefile is based off the WinAVR template):

# List C source files here. (C dependencies are automatically generated.)

SRC = $(TARGET).c

What we now need to do is add the file names of the newly created files. We'll take our above example here again for consistency. Our new extra files are called "ADC.c", "Speaker.c" and "USART.c", so we need to add those to the SRC line of the makefile.

# List C source files here. (C dependencies are automatically generated.)

SRC = $(TARGET).c ADC.c USART.c Speaker.c

Now, that'll work, but it's a real pain for future expansion. To make our life easier, we'll place the filenames on their own line in alphabetical order. To indicate a line continuation in a makefile, we need to place a "\" at the end of the continued lines:

# List C source files here. (C dependencies are automatically generated.)

SRC = $(TARGET).c \
      ADC.c       \
      Speaker.c   \
      USART.c

NB: Watch the file case! To GCC and its related utilities, "MAIN.C" is not the same as "Main.c". Make sure you put down the file names in the exact same case as they appear inside explorer, or you'll get build errors!

Naming the routines

One problem with our multi-file setup remains; routines are still hard to find across the files. One might easily identify the file location of our mythical "CheckADCChanel()" routine, but what about "SleepMode()"? Obviously a naming convention becomes important.

Now, this is where it gets tricky. There's really no set standard for function names, and every one prefers something different. It is important to choose which one you prefer, but it is ten times more important to remain consistent in your scheme across your entire project.

The first word, symbol or acronym in your function name should indicate the file it is located in. For example, all the ADC functions will have "ADC" at their start, and all the speaker-related functions in Speaker.c would have "Speaker" at their start. You can eliminate repetition in a name as it becomes self-explanatory what the function is referring to due to the prefix - thus "CheckADCChanel()" would become "ADCCheckChannel()" (or similar), rather than the superfluous "ADCCheckADCChanel()".

I'll use our example again and put here a possible function naming scheme. Note that the "main()" function's name remains unchanged as it is mandated by the C standard:

USART.c

USARTSetup()
USARTEnable()
USARTGetByte()
USARTProcessByte()

Speaker.c

SpeakerSetup()
SpeakerBeep()

ADC.c

ADCCheckChanel()
ADCCheckTempSensorVal()

Main.c

main()
MainSleepMode()
MainTurnOnStatusLed()
MainTurnOffStatusLed()

This new scheme makes finding the location of a routine quick and easy, without the need for a multi-file search utility. Don't forget to change the function prototypes in your header file to the match your new function names!

Making functions static

Static functions is possibly something you've never heard of up to now. The process of declaring a function to be static indicates to the compiler that its scope is limited to the source file in which it is contained - in essence it makes the function private, only accessible by other function in the same C file. This is good practice from two standpoints; one, it prevents outside code from calling module-internal functions (and reduces the number of functions exposed by the file) and two, it gives the compiler a better chance to optimize the function in a better way that if it was assumed to be able to be called by any other source file.

Identify which of your functions are module-internal, and add in the static keyword. In our example, let's say that the "USARTEnable()" function was only called by "USARTSetup()", and never by any other source file. Assume that the USARTEnable function is declared as the following:

void USARTSetup(void)

We'll make the function static to reduce its scope to inside USART.c:

static void USARTEnable(void)

Note that the static keyword should be added to both the prototype in the header file, as well as to the function in the C source file.

Global variables

Your project probably has quite a few global variables declared at the top of your main source file. You need to cut-and-paste them each into the most appropriate source file. Don't worry if it's referenced by more than one file; dealing with that case will be handled later in the tutorial. For now, just put it in the C file you deem to be the best for it.

Just in case you've done this, remember the following golden rule: header files should declare, not define. A header file should not itself result directly in generated code - it is there to help the compiler and yourself link together your C code, located in C files. If you've put any globals inside your header file, move them out now.

Splitting the header file

This is where the hard part begins. So far we've successfully split up the contents of our project into several C files, but we're still stuck with one large header file containing all our definitions. We need to break our header file up into separate files for each of our C modules.

First, we'll create a bunch of .h header files of the same names as our .c files. Next, we'll move the obvious things from the master header file, the function prototypes. Copy each of the prototypes into the respective header files.

Next, the macros. Again, move the macros into the header file where they are used the most. If you have any generic macros that are used in the majority of your source files, create a new "GlobalDefinitions.h" header file and place them there. Do the same for any typedefs you may have in your header file.

Let's assume our above example has the following original header file:

#define StatusLedRed     (1 << 3)
#define StatusLedGreen     (1 << 2)
#define ADCTempSensChannel 5
#define BitMask(x) (1 << x)

typedef unsigned char USARTByte;

USARTByte GetUSARTByte(void);
void      TurnOnStatusLed(void);
void      SetupSpeaker(void);
void      TurnOffStatusLed(void);
void      SetupUSART(void);
int       main(void);
int       CheckTempSensorVal(void);
void      ProcessByte(USARTByte);
void      Beep(void);
void      EnableUSART(void);
void      SleepMode(void);
int       CheckADCChanel(char);

We can split this into our new header files like thus:

USART.h

typedef unsigned char USARTByte;

void        USARTSetup(void)
static void USARTEnable(void)
USARTByte   USARTGetByte(void)
void        USARTProcessByte(USARTByte)

Speaker.h

void SpeakerSetup(void)
void SpeakerBeep(void)

ADC.h

#define ADCTempSensChannel 5

int ADCCheckChanel(char)
int ADCCheckTempSensorVal(void)

Main.h

#define StatusLedRed     (1 << 3)
#define StatusLedGreen     (1 << 2)

int main(void)
void MainSleepMode(void)
void MainTurnOnStatusLed(void)
void MainTurnOffStatusLed(void)

Pretty straightforward - using our renamed functions, the job becomes easy. The only issue is the orphaned macro "BitMask", which we'll assume is used by all the files. As discussed, we'll place that into a separate header file called "GlobalDefinitions.h".

Renaming the macros

As with the function names, the macros should also be renamed to indicate where they are located. Again, use whatever convention you prefer.

Global variables revisited

Now comes the time to fix up those global variable references from before. You're faced with a problem; your global variable is declared in one file, but it's used in two or more. How do you tell the other files to use the declaration you already made?

The answer is the use of the "extern" keyword. This keyword indicates to the compiler that a global of the specified name and type has been declared elsewhere, and so the references are fixed up by the linker when the project is built. Inside the header file of the C module containing the global, add the same line of code (sans any initialization value) but add the extern keyword. Say we have the following:

USART.c

unsigned char LastCommand = 0x00;

We can add a reference to the global in USART.h to make it visible in our other C modules:

USART.h

extern unsigned char LastCommand;

Do this for each of your global variables that are accessed in C files other than the one in which it is contained.

Including the header files

Our final task is to link our header files to the relevant C files. Firstly, each .c file should include its own header file. In the C language, for header files located in the project's current directory, we use quotes around the file names:

USART.c

#include "USART.h"

Speaker.c

#include "Speaker.h"

ADC.c

#include "ADC.h"

Main.c

#include "Main.h"

Next, we need to include the library header files relevant to each C module. For example, our "Main.c" and "Speaker.c" files both make use of the "avr/io.h" header files. System headers should have angled brackets instead of quotes:

Speaker.h

#include 

Main.h

#include 

Rinse and repeat for each of the required system header files inside the header of each C module.

The last task of this step is to include the header files used by each module. For example, if a function in Main.c calls a function in ADC.c, we need our Main.h header file to include the ADC.h header file.

Final step - header file protection

The last thing to do before our conversion is complete, is to protect our header files from multiple inclusion. Take the following example. Say that Main and ADC both refer to each other:

Main.h

#include "ADC.h"

ADC.h

#include "Main.h"

What happens when this is compiled? The preprocessor will look at Main.h, then include ADC.h. However, ADC.h includes Main.h, which again includes ADC.h, etc...

To guard against this problem, we can use preprocessor defines. The following code snippet is the basic protection setup:

#ifndef MAIN_H
#define MAIN_H

// Header file contents

#endif

This construct, when applied to each of your header files, will protect against multiple inclusions. As each C file is compiled, the associated header file is included, as well as any other referenced header files (via includes in the C file's header file). As each header is included, a check is performed to see if the header's unique token is already defined, and if so the inclusion halts to prevent recursion. If the token is not already defined, the preprocessor defines it and looks at the remainder of the header file's contents. By giving each header file a different token (typically the header's filename in ALL CAPS, and the period replaced by an underscore), this system will prevent any preprocessor troubles.

Let's take a final look at how our mythical C file's headers might look like:

USART.h

#ifndef USART_H
#define USART_H

typedef unsigned char USARTByte;

void        USARTSetup(void)
static void USARTEnable(void)
USARTByte   USARTGetByte(void)
void        USARTProcessByte(USARTByte)

extern unsigned char LastCommand;

#endif

Speaker.h

#ifndef SPEAKER_H
#define SPEAKER_H

void SpeakerSetup(void)
void SpeakerBeep(void)

#endif

ADC.h

#ifndef ADC_H
#define ADC_H

#define ADC_TEMPSENSORCHANNEL 5

int ADCCheckChanel(char)
int ADCCheckTempSensorVal(void)

#endif

Main.h

#ifndef MAIN_H
#define MAIN_H

#define MAIN_STATLED_RED     (1 << 3)
#define MAIN_STATLED_GREEN   (1 << 2)

int  main(void)
void MainSleepMode(void)
void MainTurnOnStatusLed(void)
void MainTurnOffStatusLed(void)

#endif

Voila, we now have a separated, maintainable project!

I hope this tutorial is of some use. Feel free to post your corrections, questions and comments as always. Cheers all!

For an updated version of this tutorial in PDF format, please see this page of my website.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

Last Edited: Sat. Feb 4, 2012 - 02:19 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Great tutorial. Just have to nitpick a little

abcminiuser wrote:
Just in case you've done this, remember the following golden rule: header files should define, not declare.

This should be the other way round. Hence
//Function Definition

return-type function-name(argument definitions)
{
    local variable definitions
    statements
}
//Goes in source file


//Function Declaration

return-type function-name(argument declarations); 

//Goes in header file 

Also

abcminiuser wrote:
Identify which of your functions are module-internal, and add in the static keyword. In our example, let's say that the "USARTEnable()" function was only called by "USARTSetup()", an never by any other source file. Assume that the USARTEnable function is declared as the following:
and then in the codesegments you declare USARTSetup to be static.

Again great tutorial.

Regards
-Andri Mar

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

Thanks Andri, fixed both of those issues. Glad to hear you liked it!

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Anyone who doesn’t know C will greatly benefit form your tutorial and the rest of us wish we had a tut like this when we first started learning C.
Very well written and concise, great job!
Keep up the good work.
Cheers
John

Resistance is futile…… You will be compiled!

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

I have to second John's statement. I'm going back through my code with tutorial in-hand to see if there are areas where I can clean things up and make it more readable.

I like the idea of using the .c filename as the prefix for each of the routines inside that file. I know I don't do that 100% of the time, but it's a nice convention and I plan on using it from now on.

Thanks for the hard work, Dean! It's appreciated.

Tom

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

Thanks for the comments John and Tom. This is why I write these tutorials!

Tom: The function naming convention is up to you, but I DO strongly suggest you follow one. Use whatever you think is best, but be consistent and do make some reference to where the function is stored.

In my project, I use {ABBR}_{FNAME}, where "{ABBR}" is an abbreviation - actually acronym - of the file name. This means that a function "DoStuff" inside "SomeAmazingCFunctions.c" would be named "SACF_DoStuff". Use whatever works for you.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Dean,

Our coding standard contains an extension of your function naming suggestion that others may find useful. Here's an excerpt from our standard:

http://www.ourcottage.plus.com/cs.html

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

i place all my functions in my header files.only a main.c;then many header files which contain the real codes.i think this is much more convenient,cleaner and easier than your method.

Our Martians are beginning to learn AVR

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

Header files should NEVER directly output code when included - it's a bad idea all round. You might end up with naming conflicts, duplicated functions, etc and it's just a bad practice all round, just like including C files. Follow my guide here and you'll have a manageable large project which follows basic standards about multiple files.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

i can not see your meaning clearly.

Quote:

Header files should NEVER directly output code when included

my project is so well now,just fine,working so smooth,doing the thing what i want.

Our Martians are beginning to learn AVR

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

What happens when you want to #include the same .h file into two different .c files?

Remember that, in general, each .c file in a project is passed to the compiler and compiled in isolation. So if file1.c includes shared.h and that shared.h actually generates any code or data then file1.o will include a copy. Now you compile file2.c which also includes shared.h and the output file2.o ALSO now has copies of the generated data and functions. Hopefully the linker will recognise things as being identical but the chaces are you are going to get "xxxx is mutiply defined" errors during the link.

Cliff

PS I was about to point you towards an excellent tutorial about code modularity in the Tutorial Forum that explains how to do this properly - THEN I realised I was actually typing on the end of it! (obviously more caffeine needed here)

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

yyw794 - just because it works, doesn't mean it is good! Feel free to do it your way, but if anyone else has to work with your code, expect to hear some complaints.

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

last night i made it the same way as you mentioned above,but now the big problem appears! avrstudio4.13 tell me that "gcc plug-in:Error:Object file not found on the expected location ..."i have repaired my gcc and avrstudio4,but there is no help.what the hell is happening???

Our Martians are beginning to learn AVR

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

i make it ! i make it!! all of that is the nasty global variables.i fixed it ,and then ok

Our Martians are beginning to learn AVR

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

For offline reference I've made a PDF file of Dean's tutorial and wanted to share it, you can find it below.

Ingo

Attachment(s): 

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

Thanks for the comments John and Tom. This is why I write these tutorials!

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

Great tutorial, although I have a few newb questions:

dean: I may be wrong, but in all of the .h codes, in definitions like

void        USARTSetup(void) 
static void USARTEnable(void) 
USARTByte   USARTGetByte(void) 
void        USARTProcessByte(USARTByte)

you haven't used any ; dots. Is it supposed to be like that?

Also, now I'm getting some error that there's a multiple definition of one variable, although there's no multiple definition of the variable.

adc.o: In function `__vector_21':
../adc.c:4: multiple definition of `adc_channel_being_read'
main.o:../main.c:4: first defined here
make: *** [Sbot.elf] Error 1

and there's no definition of it outside that .h file
[edit] Nothing. solved the problem. Thanks for a lovely tutorial

[edit] xingpingli: WTF?

There are pointy haired bald people.
Time flies when you have a bad prescaler selected.

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

Quote:

dean: I may be wrong, but in all of the .h codes, in definitions like
Code:
void USARTSetup(void)
static void USARTEnable(void)
USARTByte USARTGetByte(void)
void USARTProcessByte(USARTByte)

you haven't used any ; dots. Is it supposed to be like that?

Intentional. I wanted to improve clarity by eliminating everything but the basics. I'm hoping the end reader realizes that they should use real, legal C code in their projects.

Seconding the WTF on xingpingli. Did your reply get chopped off?

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

David,

Sorry to be a pedant but you mean you are DECLARing and not DEFINing adc_channel_being_read in the .h don't you?

IOW just ONE of your .c files should contain the definition:

type adc_channel_being_read;

while the shared .h file should have the declaration:

extern type adc_channel_being_read;

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

cliff:
It's OK now. I fortunately found it out. Never used this kind of program structuring before. I've also had a problem, that the ADC part will be trying to use some of the USART code. It gave me an unknown reference. Is including the "usart.h" everywhere where it is needed the right way, or should there be one file with all of the function prototypes?

There are pointy haired bald people.
Time flies when you have a bad prescaler selected.

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

Yes, the "right" way is to include all the required header files. If you've structured your project as this tutorial teaches, including the header files should do nothing more than expose typedefs, enums, macros, prototypes and variable declarations - no code size penalty. If you *really* want, you can make public and private headers for each file, with only the items that need to be globally exposed in the public headers.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Quote:

If you *really* want, you can make public and private headers for each file, with only the items that need to be globally exposed in the public headers.

I've sometimes wondered if I should be doing this. But how would one keep the private header private? Simply not mention it in documentation? Or add a big comment "Should only be included in foo.c"? Splitting things that are only needed in one file to another file 'feels' wrong even though it would follow the rule that declarations go to header files.

So I usually put declarations of static functions and constants only needed in one place to the top of the corresponding .c file. I would appreciate any advice or comments others may have on this.

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

In our large projects a module will generally have a three letter acronym (e.g. SPI) and in the \spi directory there will be various spi???.c files, an spi.h that is the "public" header file that may be #include'd by other modules or main code in the project then any .h stuff that other modules don't need to see will go into spii.h (the added i for "internal"). While the tla???.c files will probably include tla.h and tlai.h, outside of that directory the C files will only include tla.h (they may know that tlai.h exists but would never use it directly)

Cliff

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

Hi All,
Again a great tutorial here.
Where should I best store these new source and header files in Winavr ?
Where to configure the path to these files ?
Do I have to configere the path ones ?
Or do I have to add Header and Source files in the left
column named "AVR GCC" in AvrStudio for each new project that needs these files ?
Thanks,

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

Hi Go4It,

Thanks for the compliment.

Quote:
Where should I best store these new source and header files in Winavr ?
Where to configure the path to these files ?
Do I have to configere the path ones ?
Or do I have to add Header and Source files in the left
column named "AVR GCC" in AvrStudio for each new project that needs these files ?

I don't have much experience in using AVRStudio as a GCC frontend, so the following is only what I believe to be the case. I'm sure someone else will correct me if I get things wrong.

You should put your source files in the same directory as your main project source file, to keep things neat and ordered. You should then add them to the left AVRStudio source file pane, to tell AVRStudio to compile and link them in with your main source file.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

abcminiuser wrote:

Quote:
Where should I best store these new source and header files in Winavr ?
Where to configure the path to these files ?
Do I have to configere the path ones ?
Or do I have to add Header and Source files in the left
column named "AVR GCC" in AvrStudio for each new project that needs these files ?

[...]You should put your source files in the same directory as your main project source file, to keep things neat and ordered. You should then add them to the left AVRStudio source file pane, to tell AVRStudio to compile and link them in with your main source file.


Right click on 'Source files' -> 'Add existing source file(s)' to do this and add all .c files.

Then add the #include statements for the corresponding .h files on the top of your main source file.

Ingo

Attachment(s): 

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

clawson wrote:
Dean,

Our coding standard contains an extension of your function naming suggestion that others may find useful. Here's an excerpt from our standard:

http://www.ourcottage.plus.com/cs.html

quote from your link:

Quote:

Note: C doesn’t support the concept of a module global, this naming convention allows the
distinction between a system global which would be used by other modules from a module
global which should not be accessed by other modules

Isnt it so that making a module level variable static means its global to module level and inaccessible from other modules?

For that matter, not declaring the variable in the modules header file will generate an 'undeclared ...' warning when it is used from outside the module. And we always want zero warnings right? :)

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

abcminiuser wrote:
If you *really* want, you can make public and private headers for each file, with only the items that need to be globally exposed in the public headers.

- Dean :twisted:

Private declarations belong in the source file itself, not in a header. This is the only way of ensuring that no other module knows about the private declarations.

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

Hi!

Can I make one big Common.h for all .c files?
Common.h would contain:

-#include
-all function's prototype,
-extern.. global vars (these are defined in the beginning of main.c)

Common.h would be included in every *.c file in the project.
So there will be multiple prototypes, global var declarations.
But is this a problem?

Thx

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

You can do whatever you like and I can't do anything to stop you ;).

However, I'd recommend against it. Generally, it's considered good practice to go with the normal one header per source file route. That way each source file only includes what is needed, preventing multiple definition problems and speeding up compilation.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

mrx23dot wrote:

But is this a problem?

Dean wrote:

[...]go with the normal one header per source file route. That way each source file only includes what is needed, preventing multiple definition problems and speeding up compilation.

And apart fromthose two (valid) points, ponder this: What happends when you want to take one piece of the developed source code and use in another project. Example: Some code to handle a "ring buffer" "on top" of the UART, or maybe some code to handle a LCD display. If all your prototypes are in one big header file you now have to copy the parts you want and paste them into another header file for the new project. If you had a separate RingBuffer.h or LCD.h (to accompany your RingBuffer.c or LCD.c) you'd just make a copy of the files to the new project.

And after doing that for a while you get problems with those copies drifting apart leading to eg. bugs fixed in one project being un-fixed in another. You realise that you need those files stored in one master place. Every time you fix a bug or make an enhancement you'd update the master files, test them and then distribute them to all projects that are using them.

(And after doing that for a while you'd stop distributing the sources to the different projects and instead build librarys of precompiled code to distribute. Or you might set up your own version control system, eg. Subversion and store the files there...)

Anyhow, my point is - it's not only about compiler efficiency and avoiding build errors. Its just as much about making code reuse easy, efficient and less error-prone.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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

I don't have a .h file for every .c file but one .h file for every "module" that may actually contain a number of .c files. So I might have ADC support in a single adc.c with a corresponding adc.h but for something like a flash filing system there could be 10 or more .c files and a single ffs.h

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

Awesome tutorial. Just what the doctor ordered.

Thanks Dean.

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

Guys,

I've been working on a project for uni and have been putting all my code into the one .c file and the one .h file.

However, it was all getting a bit messy, so I decided to try and break it all up into seperate .c and .h files for ease of documentation.

However, I'm now getting an issue when I try and compile.
I get messages such as

../ISR.c:13: In function '__vector_6':
../ISR.c:31: error: 'distance_string' undeclared (first use in this function)

'distance_string' is declared in another header file (distance_measure.h) with the 'extern' keyword, so I don't understand why AVR Studio isn't linking it correctly. Do I have to include distance_measure.h in isr.h? Through reading Dean's tutorial it makes it sound like I only do this if I am calling a function from another file.

Can anyone help me out with some ideas to try?
If I need to post the code I will, it's just that it's all a bit messy at the moment :wink:

Thanks!

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

You've got distance_measure.h included in ISR.c?

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

I would like to add a tip that will save you a lot of time in Programmers Notepad. There is an easy way to prepend extern to long lists of declarations:

1)Make a vertical list of the word "extern " (note the space character) on the left margin using copy and paste
2)Hold down the Alt key and click and drag a box around the list you just made. The list should be selected now.
3)Cut
4)Place the cursor at the beginning of the list of declarations
5)Paste

If you do this, PN should have prepended your declarations with "extern ". I hope this is helpful to someone.

edit: I just figured out that it works in AVR Studio also, only you have to continue holding Alt until after you cut. And also you have to issue the cut command with the mouse not the keyboard.

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

Quote:

Do I have to include distance_measure.h in isr.h?

Include it in isr.c, as hinted at by davef.

We see questions similar to this on and off here at 'freaks, and what you really need to know to analyze the situation yourself (rather than us just giving a cook-book recipe for resolving it) is that all includes are handled by the preprocessor before the compiler proper starts looking at the source. Wherever an #include is done, in that place the whole header file is inserted into the source code (and nested includes work that way too). So when the compiler proper starts working there are no #includes left in the "compilation unit".

Now, in your ISR.c, on line 31, the declaration of distance_string must aready have been seen by the compiler.

If you are really curious there is a switch to the gcc compiler to produce a listing file which shows what the compiler proper sees after the preprocessor has "mangled" the file. I don't recall the syntax of the switch - go to the (avr-)gcc documentation if you really want to try it out.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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

Thanks JohanEkdahl and davef for your replies.

However, now I have another problem. :(

I started getting alot more error messages saying things like:

ISR.c: (.text+0x1a): undefined reference to 'distance_string'

and for other global variables.

I went and removed the 'extern' keyword from all the global variables declared in the different header files just to see what happened, and the error messages changed to

 ... multiple definition of...

Now I don't know why this is occurring either, because the variables are only declared in ONE header file, and I have put

 #ifndef DISTANCE_MEASURE_H
#define DISTANCE_MEASURE_H
...
#endif

in each of the header files.

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

The "undefined reference to X" is probably because the source file that actually defines (implements) X is not included in the project. While compiling, the compiler only sees

extern type X

and goes "Oh, allright it is somewhere, but I don't know where. Another instance of me the compiler will see it while compiling some other source file, and we both will leave it to the linker to resolve this." As that other file never is compiled the linker will not see it and goes "Darn, here's a reference that I cannot resolve".

When you remove all those extern from the declarations they all turn into definitions (implementations) of X. Now every time the compiler sees one of those it will emit code for it into the object file. And when the linker gets hold a hold of thyose object files it has to emit an error becuse it sees the same variable implemented several times.

The solution to the first problem is not to delete all "extern" thingies, but to locate the source file that actually implements X and see to it that it gets compiled and linked in.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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

Sorry when I say I've put

#ifndef DISTANCE_MEASURE_H
...

I mean I've named them differently depending on the name of the header file (eg. ISR_H for isr.h, etc etc etc)

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

Thanks to your help JohanEkdahl, and a bit of fiddling... I managed to get it to all compile (and probably more important learned something along the way :) ).

Now to start playing with this doxygen to get some some nice documentation out of it...

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

Glad to be back here... :)

Im not sure if this may help but, one may also use function table in declaring functions, specially those that almost have the same characteristics (ie communication protocols that sends, receives data).

For example:

From Protocol.h
#define INIT   0
#define TXDATA 1
#define RXDATA 2


From Protocol.c
void (*Protocol[][Idx])() =
{
#if defined (UART)
    ProtocolInit[Idx] = UartInit();
    ProtocolTxData[Idx] = UartTxData();
    ProtocolRxData[Idx] = UartRxData();

#elif
    ProtocolInit[Idx] = SpiInit();
    ProtocolTxData[Idx] = SpiTxData();
    ProtocolRxData[Idx] = SpiRxData();
#endif
}

The Idx refers to the UART channel.

From the calling function, one may only need to call

Protocol[INIT][Idx]

if the UART or SPI needs to be initialized, depending on the channel (Idx). If you are using 2 UART, you set Idx to either 0 or 1, depending on which UART you want to initialize.

Protocol[TXDATA][Idx]

to transmit data,
and

Protocol[RXDATA][Idx]

to get the data.

I haven't compiled this code yet so there might be some missing or erroneous syntax. Please feel free to correct me.

Thanks. :)

Nice tutorial, dean.

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

Quote:

Header files should NEVER directly output code when included

Quote:

What happends when you want to take one piece of the developed source code and use in another project.

Indeed. How does a statement like #include allow you to use functions like printf()?

Thanks,

-Stephen

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

Thanks for this very good tutorial! :D

I'm just re-organising a large project, but I've got some problems with global vars.

this is my situation:
I've got several source files with their headers
main(.c .h) UART(.c.h) EEPROM(.c .h) ...
I've got a Constants.h with some defines that are used in the whole project and need to be included everywhere, as th GlobalHeader.h where i put some type defs and library includes.

1) those headers that go everywhere stay better in c files or in every header? (i.e. changes something if I put Constants.h in main.c or main.h etc...)

2) I've got a compilation error (undeclared variable) on global vars. I've got some global (whole project) vars which I declared at the beginning of the main.c as

uint8_t checkSend;

and in the main.h as

extern uint8_t checkSend;

now I got errors compiling UART.c whic uses those global vars. Do I need to put the extern uint8_t checkSend; in headers of every file wich uses this var?

Thanks,
-bo.

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

You should have a vars.h (or something) that documents the globally accessible variables in main.c and #include this wherever one or more of those variables need to be accessed.

It should only be necessary to have the extern declaration of any particular variable definition in a .c file made in one .h file - then #include that file where visibility is required.

If it were me I think I'd #include main.h, uart.h and eeprom.h (perhaps also etc) in globalheaders.h and then just #include that one file in each .c file.

Cliff

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

ok, thanks!
I'll include those vars as a sub section of the GlobalHeader.h, which goes everywhere...
bye,
bo.

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

A few other little hints along the same lines.

* Include as FEW header files in other files as far as is possible for several reasons:

(1) You can quickly tell what modules are **really** dependant on other modules.

(2) Compile time. OK, AVR compile time is relatively short, but wait until you do a complex C++ project using lots and lots of template functions. You'll definitely appreciate this rule then!

* Include the header files in order of custom.h through to the standard libraries. You'll have less dependency problems when porting the sources to something else.

When separating out functionality, I usually group related files in a directory. It then makes it very easy to branch this directory into another project and track/merge changes between the two of them. See comments on a source control system below :)

And, not directly related to C programming (so I expect to get flamed), but I strongly recommend the following:

(a) Learn C++, or a reasonably close-to-metal Object-oriented Language, even if it's for doing basic host-side work. My C code (and even assembly!) is cleaner, more portable and far more maintainable after learning an OO language and putting various practices in place.

(b) There is no excuse whatsoever (and I really, really mean that) for not using a version control system. Even if you're an only-programmer or hobbyist on a project. One day, you'll break something and wonder what you did to break it.

Subversion is free, open source and cross-platform, both command line and graphical for Windows and *nix.

(c) C99 includes nice features like inline, restrict and const that, when used correctly, will make code safer and faster in many scenarios.

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

Quote:

Subversion is free, open source and cross-platform, both command line and graphical for Windows and *nix.

Agree and if using Windows then Tortoise is a nice interface. But if you are in Linux also consider Git which is the new pretender to the SVN crown (possibly because it was written by Linus Torvalds with the needs of version managing the Linux kernel in mind)

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

Thanks for the great tutorial Dean!!

Very useful and clear!!

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

Quote:
But if you are in Linux also consider Git

AFAIK Git is available on Windows also.

Quote:

consider Git which is the new pretender to the SVN crown

I'd say that there is a troika of contenders: Bazaar, Git and Mercurial. There are Windows Explorer-integrated GUIs available for all three (TortoiseBzr, TortoiseGit and TortoiseHg).

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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:

I'd say that there is a troika of contenders: Bazaar, Git and Mercurial. There are Windows Explorer-integrated GUIs available for all three (TortoiseBzr, TortoiseGit and TortoiseHg).

We'll leave the revision control wars for another thread :P

In the meantime, a good list can be found at http://en.wikipedia.org/wiki/Lis...

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

Is agreeing with the positive comments on this thread

-Jd from the land of the Pacman

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

Hi there guys

I have used this tutorial as a basis for splitting up my code for larger projects, however my compiler (GCC) is grumpy and coming up with the following warnings regarding the static function declarations:

Quote:
../SDCard.h:108: warning: 'SendCommand_SDC' declared 'static' but never defined

//In .h file
static BOOL SendCommand_SDC(uint8_t uchCommand, uint32_t uiArgument, uint8_t *puchResponse);
//In .c file
static BOOL SendCommand_SDC(uint8_t uchCommand, uint32_t uiArgument, uint8_t *puchResponse)
{
...code 
}

The function definition is definitely in the .c file with a declaration in the .h file.

If need be I can include the full code, however I get this with all of my static functions and do not think it is necessary.

Does anyone know what this warning is about? I have searched for a list of warnings, but have had little success. I obviously prefer to not have any warnings, even if they are not hazardous to the codes execution.

I would like to find out more about the compiler warnings, for future reference, as in a complete list of warnings and there meanings. As well as a work around for this problem.

Thank you in advance.

Regards,

Derek

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

I suppose you are including the header file into more than one source file, but only define (implement) the static function in one of them.

If the function is static, this means that it is only visible inside the source file where it is defined, and thus it's function prototype needs not be in the header file at all. For me the primary reason for header files is tha they are "contracts" between proucers and consumers of eg functions. In your case the producer and the consumer is the same source file, so the contract can be in that file itself.

You can of-course place such a prototype in a header file, but you will need a separate such heaer file for each source file that declares static functions.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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

What we do for any given module TLA is to have both a tla.h which is Johan's public "contract" and also a tlai.h (i=internal) which is a private header file for information that only needs to be used amongst the components of the module. However we wouldn't put a static function declaration into even the tlai.h. The thole point of "static" is that they are private to the one .c file where they are defined so if the function needs to be announced before it is used a simple declaration would be made within the early sections of the .c

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

Thanks - sounds good to me. The project now compiles and links with out any errors.

Thank you again.
D

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

spliting is very nice idea, as one said you'll especially benefit when using OO languages. The disadvantage is that with separate compilations compiler has much less oportunities to optimize program. For example (AFAIK) it doesn't inline code compiled in other module, it pushes more registers then needed or just leaves unused functions in generated header. Therefore after such spliting user usually should be more compilator-aware - for example ISR that call function should be in same module - function we'll be inlined, or at least only needed registers we'll go to the stack.

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

This is the makefile I'm using with CrossPack for OS X:

# Name: Makefile
# Author: 
# Copyright: 
# License: 

# This is a prototype Makefile. Modify it according to your needs.
# You should at least check the settings for
# DEVICE ....... The AVR device you compile for
# CLOCK ........ Target AVR clock rate in Hertz
# OBJECTS ...... The object files created from your source files. This list is
#                usually the same as the list of source files with suffix ".o".
# PROGRAMMER ... Options to avrdude which define the hardware you use for
#                uploading to the AVR and the interface where this hardware
#                is connected.
# FUSES ........ Parameters for avrdude to flash the fuses appropriately.

DEVICE     = atmega8
CLOCK      = 8000000
PROGRAMMER = -c stk500v2 -P /dev/tty.usbserial-A3000HDB
OBJECTS    = main.o
FUSES      = -U hfuse:w:0xd9:m -U lfuse:w:0x24:m
# ATMega8 fuse bits (fuse bits for other devices are different!):
# Example for 8 MHz internal oscillator
# Fuse high byte:
# 0xd9 = 1 1 0 1   1 0 0 1 <-- BOOTRST (boot reset vector at 0x0000)
#        ^ ^ ^ ^   ^ ^ ^------ BOOTSZ0
#        | | | |   | +-------- BOOTSZ1
#        | | | |   +---------- EESAVE (set to 0 to preserve EEPROM over chip erase)
#        | | | +-------------- CKOPT (clock option, depends on oscillator type)
#        | | +---------------- SPIEN (if set to 1, serial programming is disabled)
#        | +------------------ WDTON (if set to 0, watchdog is always on)
#        +-------------------- RSTDISBL (if set to 0, RESET pin is disabled)
# Fuse low byte:
# 0x24 = 0 0 1 0   0 1 0 0
#        ^ ^ \ /   \--+--/
#        | |  |       +------- CKSEL 3..0 (8M internal RC)
#        | |  +--------------- SUT 1..0 (slowly rising power)
#        | +------------------ BODEN (if 0, brown-out detector is enabled)
#        +-------------------- BODLEVEL (if 0: 4V, if 1: 2.7V)

# Example for 12 MHz external crystal:
# Fuse high byte:
# 0xc9 = 1 1 0 0   1 0 0 1 <-- BOOTRST (boot reset vector at 0x0000)
#        ^ ^ ^ ^   ^ ^ ^------ BOOTSZ0
#        | | | |   | +-------- BOOTSZ1
#        | | | |   +---------- EESAVE (set to 0 to preserve EEPROM over chip erase)
#        | | | +-------------- CKOPT (clock option, depends on oscillator type)
#        | | +---------------- SPIEN (if set to 1, serial programming is disabled)
#        | +------------------ WDTON (if set to 0, watchdog is always on)
#        +-------------------- RSTDISBL (if set to 0, RESET pin is disabled)
# Fuse low byte:
# 0x9f = 1 0 0 1   1 1 1 1
#        ^ ^ \ /   \--+--/
#        | |  |       +------- CKSEL 3..0 (external >8M crystal)
#        | |  +--------------- SUT 1..0 (crystal osc, BOD enabled)
#        | +------------------ BODEN (if 0, brown-out detector is enabled)
#        +-------------------- BODLEVEL (if 0: 4V, if 1: 2.7V)


# Tune the lines below only if you know what you are doing:

AVRDUDE = avrdude $(PROGRAMMER) -p $(DEVICE)
COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE)

# symbolic targets:
all:	main.hex

.c.o:
	$(COMPILE) -c $< -o $@

.S.o:
	$(COMPILE) -x assembler-with-cpp -c $< -o $@
# "-x assembler-with-cpp" should not be necessary since this is the default
# file type for the .S (with capital S) extension. However, upper case
# characters are not always preserved on Windows. To ensure WinAVR
# compatibility define the file type manually.

.c.s:
	$(COMPILE) -S $< -o $@

flash:	all
	$(AVRDUDE) -U flash:w:main.hex:i

fuse:
	$(AVRDUDE) $(FUSES)

# Xcode uses the Makefile targets "", "clean" and "install"
install: flash fuse

# if you use a bootloader, change the command below appropriately:
load: all
	bootloadHID main.hex

clean:
	rm -f main.hex main.elf $(OBJECTS)

# file targets:
main.elf: $(OBJECTS)
	$(COMPILE) -o main.elf $(OBJECTS)

main.hex: main.elf
	rm -f main.hex
	avr-objcopy -j .text -j .data -O ihex main.elf main.hex
# If you have an EEPROM section, you must also create a hex file for the
# EEPROM and add it to the "flash" target.

# Targets for code debugging and analysis:
disasm:	main.elf
	avr-objdump -d main.elf

cpp:
	$(COMPILE) -E main.c

How can I edit this to allow adding additional .c files? Right now, I have to do #include "uart.c" to get it to work; which simply embeds said code in main.c rather than having the compiler link it in.

Tried to see if the makefile could first make uart.o from uart.c and then link uart.o and main.o to yield main.hex; not making much progress in this as aspect though.

Thanks in advance for your advice :)

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

The clue seems to be here

Quote:

# OBJECTS ...... The object files created from your source files. This list is
#                usually the same as the list of source files with suffix ".o".


It's late, so I wont test it but you could try to add the names of other object to be built to this variable. Eg if your project consists of main.c and uart.c then

OBJECTS    = main.o uart.o

You will of-course also have to edit up a uart.h with the things (eg function prototypes, extern declarations of "shared" variables etc) that needs to be seen from both main.c and uart.c and #include that file in both main.c and uart.c .

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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:
...

You will of-course also have to edit up a uart.h with the things (eg function prototypes, extern declarations of "shared" variables etc) that needs to be seen from both main.c and uart.c and #include that file in both main.c and uart.c .

Thanks Johan! I did try having uart.o in the OBJECT definition but I was getting this strange error:

expected '=', ',', ';', 'asm' or '__attribute__' before

The reason is that the 'uart.h' header needed the avr/io.h header to be included. For the ref of others, here are my two files as they stand:

uart.h

#ifndef USART_H
#define USART_H 

#include 
#include 

uint8_t uart_putchar (char data);
uint8_t uart_getchar (void);
void uart_putstring(char *s);

#endif

uart.c

#include "uart.h"

uint8_t uart_putchar (char data)
{
	while ( !( UCSRA & (1<<UDRE)) ); // Wait for empty transmit buffer           
	UDR = data;  // Putting data into the buffer, forces transmission                               
	return 0;
}

uint8_t uart_getchar (void)
{
	while ( !(UCSRA & (1<<RXC)) );  // Wait for data to be received           
	return (UDR);        // Return Data                         
}

void uart_putstring(char *s)
{
	while(*s) uart_putchar(*s++);
	
} 

and the beginning of my main.c. It is possible that I could leave out some of them.

#include 
#include 
#include 
#include 
#include 
#include  
#include "uart.h"
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Personally, when there's a lot of common system headers included in each .c I'd put the #includes in a syshdr.h and then just #include "syshdr.h" in each .c to reduce the "noise". On the occasion that the interrupt header file changes from to I'd then just need to make the change in one place - not edit every .c where it is included.

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

clawson wrote:
Personally, when there's a lot of common system headers included in each .c I'd put the #includes in a syshdr.h and then just #include "syshdr.h" in each .c to reduce the "noise".

You might want to rename it to syshdr.inc, perhaps, to stress the fact that it is *not* a *header* (i.e. "interface description of a *.c") as such.

JW

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

Quote:

You might want to rename it to syshdr.inc, perhaps, to stress the fact that it is *not* a *header* (i.e. "interface description of a *.c") as such.

Perhaps. Perhaps not. The use of ".h" as the extension even for nested includes is quite wide-spread.

AFAICR this is the first time I see the notion of making the super-include-files have a filetype of ".inc". Is this a cultural thing, where eg U*ix people do it, and others (eg MS) don't?

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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

In our coding standard it would most definitely be a .h, we do have the concept of ".i" to but that's for the very specific purpose of a block of externally generated const data that MUST only be #include'd in one .c file - the .i rather than .h makes this clear to the casual reader.

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

wek wrote:
clawson wrote:
Personally, when there's a lot of common system headers included in each .c I'd put the #includes in a syshdr.h and then just #include "syshdr.h" in each .c to reduce the "noise".

You might want to rename it to syshdr.inc, perhaps, to stress the fact that it is *not* a *header* (i.e. "interface description of a *.c") as such.

JW

During my C/C++ classes at Uni and even most books I have always seen the use of ".h" and never the use of ".inc".

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

Quote:

During my C/C++ classes at Uni and even most books I have always seen the use of ".h" and never the use of ".inc".

Doesn't matter what extension you use on a file #include'd in your code -- the preprocessor will process it just the same. That said, personally I think it extremely poor form to use anything other than the .h unless it's something very unusual, like a giant array of PCM audio data which needs to be embedded. Even then, I'd make the embedding of resource files a compile step, and not part of the actual code.

Each to his own, but don't be surprised if you get fired for naming all your C header files "Includes.jpg".

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

JohanEkdahl wrote:
Quote:

You might want to rename it to syshdr.inc, perhaps, to stress the fact that it is *not* a *header* (i.e. "interface description of a *.c") as such.

Perhaps. Perhaps not. The use of ".h" as the extension even for nested includes is quite wide-spread.

AFAICR this is the first time I see the notion of making the super-include-files have a filetype of ".inc". Is this a cultural thing, where eg U*ix people do it, and others (eg MS) don't?

It most definitely isn't a *nix thing. A quick journey through the system headers in a linux install will reveal that many are simply includes of other headers, usually architecture specific. I too have never seen the suggestion to use an extension other than .h for this purpose. Just because a file is used to consolidate common includes, does not make it "not a header".

.inc from my experience is usually reserved for includes in assembly projects.

But as Dean stated, the C compiler does not care what the extension is. #include is simply a text insertion.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

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

Johann wrote:
The use of ".h" as the extension even for nested includes is quite wide-spread.

A poor practice, however widespread, is still a poor practice. Replace "sub-optimal" for "poor" in the above if you find it offensive.
glitch wrote:
.inc from my experience is usually reserved for includes in assembly projects.

The choice of extension is arbitrary, but I agree that defacto standards have to be respected. As you might have noticed, I don't have that much of an experience with projects invoving C (I try to avoid them, in fact, being a dedicated C-hater ;-) ). Any other extension would do - fancy .hh?

glitch wrote:
But as Dean stated, the C compiler does not care what the extension is. #include is simply a text insertion.
And that's the problem. The C/*nix folks are not known for being meticulous to the detail, are they. And, if they have chance to take a shortcut, they take it. So it's upon the poor programmer to maintain a discipline, so that the gun won't fire in the direction of the toes.

While it's very easy to implement literal inclusion, it's hard to do it in the proper way. So there's no enforcement of binding the header to the actual source, nor of rejecting irrelevant information from it. There is no syntactical distinction between "include your own header for making sure it matches the body of module", "include a header of the module to be able to link to its functions and variables", and "include an arbitrary text - possibly a collection of headers".

And this is why it is hard to explain to the novices the concept of headers. This thread is in a tutorial section after all, isn't it.

Jan Waclawek

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

Jan,

You may want to take a look at in AVR-LibC ;-)

Even \Program Files\Microsoft Visual Studio\VC98\include\windows.h is little more than a bunch of #include's

And, as has already been stated, the technique is used a LOT in the Linux kernel tree.

It may not be "good practice" but it's what C programmers have actually been doing in large projects since time immemorial. In fact that includes me in large in-house projects (one with 57,000 .c files) - some are "meta headers" that just group the inclusion of a bunch of other headers.

Cliff

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

clawson wrote:
You may want to take a look at in AVR-LibC ;-)
Well, that might be a collection, but of what are not headers either. I would assign them a .def :-) OK, this is stretching the things a bit further than you traditionalists would be willing to accept... :-)

clawson wrote:
Even \Program Files\Microsoft Visual Studio\VC98\include\windows.h is little more than a bunch of #include's
Oh, are you trying to tell us that M$ sets the benchmark in good programming practices? :-P

Jan

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

Quote:

Oh, are you trying to tell us that M$ sets the benchmark in good programming practices?

Well, yes, actually I do believe that. While people decry Microsoft their own source code is very good quality. The reason people call Windows "buggy" is that it does not isolate driver code enough and when Tom, Dick or Harry get a copy of the DDK and write their own webcam or scanner driver it has the ability to crash the kernel (in fact the same is true of anything that is insmod/modprobe'd into the Linux kernel too). The end user's experience is that "Windows has crashed" but more often than not it's 3rd party driver code that has crashed.

In fact a lot of the coding standard we adhere too is based on the writings of Steve Maguire in Writing Solid Code:

http://www.amazon.com/Writing-So...

He outlines the techniques that Microsoft use internally to avoid the more obvious code faults. If you like it's a bit like a cut-down, less stringent MISRA rule book. That is a very very good book and I highly recommend it to anyone programming in C. While I've had office "purges" over the years and had to discard a lot of books to make room, this remains one of the core books (like K&R, the Borland C reference and Charles Petzold on programming Windows) that I'll retain for all time.

Cliff

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

I have never seen anyone who uses a .inc extension for included files in C. In fact, the only variation I have ever really seen other than .h is to use no extension at all (an older practice that has gone out of favor). So I'm not sure how you can say in one breath that you should follow the de facto standard, but in another say to throw the de facto standard out the window only to advocate something else that virtually no one else does.

Regards,
Steve A.

The Board helps those that help themselves.

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

Koshchi wrote:
So I'm not sure how you can say in one breath that you should follow the de facto standard, but in another say to throw the de facto standard out the window only to advocate something else that virtually no one else does.

Well, using an already "assigned" extension for a different purpose would cause confusion; while using an already "unassigned" extension for the non-interface headers would cause at worst a mild attention.

But of course there's no point to push it beyond reason. There are many controversial items in various coding standards to add one more. The thinking programmer develops his own little variations anyway.

I rest the case.

JW

PS. I googled for "file name extension .i" and learned that it is likely to be an INTERCALC file... :-P

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

wek wrote:
There are many controversial items in various coding standards to add one more.
This is not a controversial item, you are just trying to make it into one, pulling arguments out of thin air.

You seriously underestimate the value of established conventions and traditions. The existence of a convention is a value as such, even if the convention is (in your imagination) not optimal. You forgot that there isn't black and white only, but that engineering is a string of compromises and most results are grey. You forgot that it is usually a huge waste of time and money to do things perfect (the 80/20 rule of thumb). Engineering is not about perfection.

Stealing Proteus doesn't make you an engineer.

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

ArnoldB wrote:
You forgot that there isn't black and white only, but that engineering is a string of compromises and most results are grey. You forgot that it is usually a huge waste of time and money to do things perfect (the 80/20 rule of thumb). Engineering is not about perfection.

[wek, standing ashamed in the corner of the room, nodding quietly while concentrating on his yet unshot toes, letting out a sob now and then...]

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

Quote:
Well, using an already "assigned" extension for a different purpose would cause confusion;

How is it a different purpose? #include is used to include other files, and that is what is being done. There is nothing in the designation ".h" that implies what the content of the file is other than that it is intended to be included by other files.

Regards,
Steve A.

The Board helps those that help themselves.

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

There is a simple cure for case problems: Don't Capitalize!

main(), printf(), int, static, avr/interrupt.h, _asm_, _delay_us... See the pattern? Just say no. And in case you feel like a JavaRebel, ok, but at least be consistent.

The exceptions are things already mentioned, constants, macros, classes, header top ifdef's. F_CPU, DEBUG, DDRA, //FIXME, M_PI, RAND_MAX...

A few outstanding conventions exist, like Makefile with a capital M so it stands out from code and README(.txt) in all caps because that's the first thing that should stand out at a glance.

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

I am following this tutorial in order to manage my code...but I did not find the make file? where should I find it??

Thanks,

Ashish

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

Quote:

but I did not find the make file?

There isn't one. But what's the problem? You take the Mfile template. Edit the names of all the C (.c) source files onto the SRC= line, the name of Asm (.S) source files into the ASRC= line and the name of all the C++ (.C) files onto the CPPSRC= line and that's it. Job done.

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

I have numbers of global variables in my projects. Is it possible to put all the global variables in one .h file and include this file in all the .c file ?

I am defining the global variables using extern word.

If I am doing this way then it is giving me error
"

(.text.avr-libc.fplib+0x0): multiple definition of `__floatunsisf'
"

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

I do this but not how you suggested. As this article hopefully explained there should never be any definitions in a .h file, only declarations. So create a new .c file called something like:
globals.c

int n;
char c;
long l;

and add it to the list of source files to be compiled. Then also create:
globals.h

extern int n;
extern char c;
extern long l;

and #include this in any file that needs access to 'n', 'c' or 'l'

In fact I often take this one stage further and group all the globals in a struct. So I have in the .h file:

typedef struct
{ 
	volatile uint8_t	*recv_ptr;
	volatile uint32_t	spi_byte_count;
	volatile uint16_t	df_page;
	volatile uint16_t	buf_ptr;
	volatile uint16_t	samples_per_motor;
	volatile uint16_t	uart_bit_len;
	volatile uint16_t	start_len;
	volatile uint16_t 	sample1;
	volatile uint16_t 	sample2;
	volatile uint16_t 	motor_count;
	volatile uint8_t	last_flash_byte;
	volatile uint8_t	t1_timed_out; //false or 0x37 ??
	volatile uint8_t	packet_number;
	volatile uint8_t	nak_count;
	volatile uint8_t	use_of_timer;
	volatile uint8_t	sample_count;
	volatile uint8_t	sample_limit;
	volatile uint8_t	sample_shift;
	volatile uint8_t	orig_OSCCAL;
	volatile uint16_t	tim0_count;
	volatile uint8_t	ignore_button;
	volatile uint8_t  	adpcm; // 0 or 1
	volatile uint8_t  	debug; // 0 and !0
	volatile uint8_t	play_one;
	volatile uint8_t	play_two;
} global;

extern global gl;

then in the global.c I have:

#include "global.h"

global gl;

and I access the globals as:

gl.packet_number = 30;
if (gl.ignore_button) {
   // something
}

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

Quote:

I have numbers of global variables in my projects. Is it possible to put all the global variables in one .h file and include this file in all the .c file ?

No, this will not work. As I'm sure has been explained earlier in this thread you will get duplicate definitions of those variables, and the linker generating errors.

What you should do has also most likely been described earlier in this thread:
- Place definitions of your global variables in one .c source file.
- Add that .c source file to the project so that it gets compiled.
- Put extern declarations of the ame variables in a .h header file.
- Include that .h header file into all .c source files that needs to see the variables.

Example:

// globals.c
#include "globals.h"
int aGlobalInt;
// globals.h
extern int aGlobalInt;
// userOfGlobals.c
#include "globals.h"
.
.
.
   aGlobalInt = 42;
.
.
.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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

By the way I just looked back at your post and the __floatunsisf error is actually because you are using FP functions without linking with libm.a, nothing to do with your own variables (in the case of that particular error message)

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

I follow the same.. but still I am getting the same error..

(.text.avr-libc.fplib+0x0): multiple definition of `__floatunsisf'

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

Quote:

By the way I just looked back at your post and the __floatunsisf error is actually because you are using FP functions without linking with libm.a, nothing to do with your own variables (in the case of that particular error message)

Could you please explain how to eliminate this error?

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

Quote:

So add a "-lm" to your LDFLAGS or, if using AVR Studio go into project config and under "Libraries" copy libm.a from the left to the right pane.

I followed this solution given by clawson sir in some other thread and now it is not giving me that error.

Thank you so much....

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

Quote:

I have numbers of global variables in my projects. Is it possible to put all the global variables in one .h file and include this file in all the .c file ?

Yes, you can have all your "extern" (publically visible) globals listed in one header file. However, you will still need to place them in one of your .c files as well without the "extern" modifier, otherwise you will receive undefined reference errors.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

I am trying to write a header file and put up everything related to TWI in it, leaving only the main processing to the main.c and functions being called as and when required. I want the TWI ISRs to be contained by this header. The program compiles fine, but somehow the ISR just isn't executing. Could you please help me out with this?

And, should I be using a .c file instead of the .h file.

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

An ISR is effectively just a fancy regular function - and anything that results in actual executable code in the binary should be a .c file rather than a .h file. Try creating a TWI.c and TWI.h pair, with the ISRs in the C file.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Quote:

The program compiles fine, but somehow the ISR just isn't executing. Could you please advise me something on this?

The snide remark is that you are yet another one that has a problem on line 42 of your source code.

Seriously: We can't say much unless you post your code. It helps boost interest in helping you if you reduce the code to the smallest possible that still builds and executes and displays the problem.

Quote:
And, should I be using a .c file instead of the .h file.

The usual case is a .c file and a .h file. Go read the tutorial here on "Managing large projects".

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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:

Go read the tutorial here on "Managing large projects".

Irony. I love it!

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

Blimey! Didn't notice we where already there. :roll:

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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

Thank you for the quick response :) Got it working.

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

Dean,

You Did not mention where to define ISR routines when splitting a large project into different .c and .h files. Is it in the same file where main() function defined?

-Partha

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

I'd put it inside the module where it is most relevant, i.e. the module that sets up the ISR.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

One key thing is if the ISR() calls any functions then either make them static inline - in which case they could actually be located in a .h file - otherwise put them in the same .c file where the ISR() is located so the compiler has some chance of determining register usage and will not be forced to save/restore the entire context.

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

Quote:
I'd put it inside the module where it is most relevant, i.e. the module that sets up the ISR.
To be more specific, it is up to you where you put it, the compiler doesn't care.

Regards,
Steve A.

The Board helps those that help themselves.

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

Hey Guys,
Quick question. With custom data types how does the extern tag work? For something like the following:

In the .c file:

typedef struct
{
	uint8_t one;
	uint8_t two;
	uint8_t three;
} MyStructure;

MyStructure myStructureArray [12];

How would you declare the .h file? I have tried different extern syntax and can't seem to get right.
I have tried the following:

.h file:

extern typedef struct MyStructure;
extern MyStructure myStructureArray [];

I get the following errors:

Following warning on the structure call:
warning: useless keyword or type name in empty declaration

Following error on the custom MyStructure call:
error: syntax error before "outputsArray"

Thanks in advance!

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

Try:

typedef struct 
{ 
   uint8_t one; 
   uint8_t two; 
   uint8_t three; 
} MyStructure; 

extern MyStructure myStructureArray [12];

You want the variable definition of that type to be extern, not the declaration of the type.

- Dean :twisted:

(EDIT: Fixed wrong definition/declaration terminology - always get it round the wrong way!)

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

Last Edited: Mon. Feb 21, 2011 - 09:48 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To add to what Dean says. A typedef does not create any storage. A definition of the array of struct does. The only things you want to avoid in a header file are anything that leads to storage being allocated. So it's the variable definition that needs to be turned into a declaration only. You do this by adding 'extern'.

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

Sorry if this was already asked.
Not explicitly said this tutorial seems to indicate that say "usart.h" does not need to include "usart.c" is that true?

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

Suggest you re-read the thread. The whole point is that you (almost) never name a .c file in a #include

You add the .c that contains routines to be used to the list of files to be compiled/linked. Then you #include the .h that documents the interface to those routines in the other .c files that want to make use of the functions offered.

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

Quote:

"usart.h" does not need to include "usart.c" is that true?

Technically, that is correct. You can have the prototypes in uart.h, the implementations in uart.c w/o #include'ing uart.h, and then #include uart.h in main.c and call the funxtions. The compiler/linker will not complain.

But for me the header files should be seen as specifications of interfaces that both implementors and callers adhere to. Thus they should both include the interface specification.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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

Dear clawson,
can you please explain bit more about Declaration and Definition, difference b/w them.

As for as my understanding is that memory is allocated in the definition only.

Thanks,
Prabu

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

Quote:

can you please explain bit more about Declaration and Definition, difference b/w them.

You have read the entire tutorial / thread haven't you?

But in short:

Declaration: usually found in a .h file - opportunity to make the compiler aware of a function/variable's existence and provide details about the types involved. In the case of a function you can tell it's a declaration because it ends with a semi-colon rather than braces containing statements. In the case of a variable you can tell it's a declaration as it starts with the word 'extern' (you can put the word 'extern' on a function declaration too if you like but don't need to as it's obvious from the fact there's no function body). Declarations (as long as they agree) may occur multiple times. Declarations do not lead to any storage being allocated, they are only to let the compiler (and reader) know about types involved.

Definition: Usually found in a .c file. This is the thing that actually allocates storage and creates something in memory - whether it be the storage to hold a variable or the opcodes that implement a function. A variable definition is obvious by the fact that it does not contain the word 'extern' and a function definition is obvious by the fact that the function header ("prototype") is followed by a statement block contained in braces ({}) which hold the C statements that implement the operation of the function. Definitions must only appear once within all the files passed to the linker and are therefore usually contained in a single .c file.

Example:

//foo.h - declaration of a variable and function
extern int n;
long my_function(int number, char c);
//foo.c - definition of a variable and function
int n;
long my_function(int number, char c) {
 return c * number;
}

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

Ya i had gone trough the tutorial. It is too good and informative.

Quote:

In the case of a function you can tell it's a declaration because it ends with a semi-colon rather than braces containing statements. In the case of a variable you can tell it's a declaration as it starts with the word 'extern' (you can put the word 'extern' on a function declaration too if you like but don't need to as it's obvious from the fact there's no function body)

what about in the the below code if foo.h is not used?

//foo.c - definition of a variable and function
int n; //will be definition as well as declaration,right?

long my_function(int number, char c) {
return c * number;
}

Thanks,
Prabu

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

Both n and my_function are definitions as well as declarations.

Think of definition as "implementation". Where a variable is actually allocated, or the body of a function is given.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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

Personally I wouldn't confuse myself by calling them both definitions and declarations. Both lead to memory being allocated so, in my book, they are definitions. But clearly, once the compiler has encountered a definition it knows the types involved so I guess in that sense you might also use the term declaration but this seems to be trying to over-complicate something that is really very simple:

Is storage allocated?

yes) it's a definition
no) it's a declaration

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

I see your point, Cliff.

For me it is rather like:

A definition is implementing something (and it might lead to memory allocation).

A declaration is about "making the existence of something known" (leading to an entry in the compiler symbol table if the thing is not already there, could lead to an update even if it was already there, and could lead to an entry in the table of unresolved references left to the linker).

A Definition is inherently a declaration.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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:

A Definition is inherently a declaration.

Agreed but I think it actually helps the learner to think of them as two distinct things. Declaration=type-info/header(usually), Definition=allocation/C file. Anything more over-complicates and begins to lose the point about where they should be located and why which is the key thing.

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

Awesome tutorial. I'm now using the guidelines you explained to manage my project with 11 .c/.h files! Better than having everything in main.c, huh?

Thanks a lot.

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

I have a different method of using headers which works out well for me so I thought it might be worth a mention.

Any #define's which are only accessed within the modules .c file, are simply placed at the top of the .c file. Only parts of the module which must be accessed externally go into the .h file. Then all .h files get included into one file called includes.h which is included at the start of every .c file.
I have done some large projects like this, and have never needed to use #ifndef.

Telling yourself something is too hard, or too complicated, is not a determination. It is a decision.

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

Quote:
Only parts of the module which must be accessed externally go into the .h file.

This boils down to your header files being the interface to the module and nothing else, which in my view is a beautiful thing.

Wether the defines etc that are needed only inside the volume is placed in the source 'per se' or in another header file is another matter, where my opinions are less strong. But note that a header file will likely be needed for that too, if the 'module' becomes large enough to warrant splitting it's implementation into several .c source files.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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

I wish to be able to use fprintf() or fprintf_P() from many different c files in project to send data out via a USART. The problem that arises is the first argument (i.e., FILE *stream) in AVRs w/ multiple USARTS. As a cheat, I would do something like:

#define comExt (&com0_str)
extern FILE com0_str;

in some "global" projDef.h file. com0_str would be declared elsewhere in a "*Msg.c" as

FILE com0_str = FDEV_SETUP_STREAM(usart0_putchar, usart0_getchar, _FDEV_SETUP_RW);

Functions then call like:

fprintf(comExt, "blah blah blah\n");

The problem is that any file that contains a function that wants to use fprintf() needs to include this projDef.h header file to get to extCom.

I suppose that I could create a header/c file pair that only has one (two) function(s) in it, i.e.,

uint8_t myPrintFxn(const char* _fmt, uint8_t portNum){}
uint8_t myPrintFxn_P(const char* _fmt, uint8_t portNum){}

This c file then would be the only one that reaches out and touches the "com ports". However, this still leads to the problem that every module that includes this submodule would need to know what the two com ports (in a given AVR w/ two ports) are. What is the best solution to this problem?

Science is not consensus. Science is numbers.

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

Quote:

As a cheat, I would do something like:

How is that a "cheat". Defining it in one C file and declaring it extern for the others that need to use IS the right way to do this?!? Not sure the point of your macro though?

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

It is a cheat because my modules are not independent. Meaning, if I have a display.h and display.c file pair whose purpose is to provide an interface to a commercial display, I would like that module to be portable to many projects. However, as soon as I include the fprintf statements, it needs some overarching project definition header file to glue the project together. If I use comExt in one project and comInt in another, I would need to change the code in the display module.

The macro is just to make things neater, to abstract the pass by reference nature of the FILE argument. If I remove the macro, I could put multiple extern definitions of the port in all of my module header files, still only declaring it once (I think). However, I think the naming issue for portability remains.

Science is not consensus. Science is numbers.

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

I still don't get your problem? What's wrong with simply doing the equivalent of:

Quote:

As a cheat, I would do something like:

How is that a "cheat". Defining it in one C file and declaring it extern for the others that need to use IS the right way to do this?!? Not sure the point of your macro though?

Here's an example:

// first.c
#include 
#include 

void foo(void);

int uart_putchar(char c, FILE *stream)
{
	// blah blah
	UDR = c;
	return 0;
}

FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE); 

int main(void) {
	fprintf(&uart_str, "Hello");
	foo();
	while(1);
}  
// second.c
#include 

extern FILE uart_str;

void foo(void) {
	  fprintf(&uart_str, "Hi from foo");
}

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

The only problem is that second.c relies on the variable being declared as uart_str in some other file. If second.c gets reused in another project that defined it as uart0_str or uart1_str, second.c would need to be changed. Maybe this is unavoidable.

Science is not consensus. Science is numbers.

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

Quote:

Maybe this is unavoidable.

I cannot see a way to avoid it - the object has to be instantiated somewhere. Perhaps the "client" should be told to own it? They actually define it in their main.c (or whatever).

Having said that surely the FILE used for uart_str would be located with UART_init, UART_putchar() and others in uart.c and a lcd_str would be created with LCD_init(), LCD_putchar() and so on in lcd.c ?

That is:

// uart.c
#include 
#include 
#include "uart.h"

FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

int uart_putchar(char c, FILE *stream)
{
   // blah blah
   UDR = c;
   return 0;
}
// uart.h
extern FILE uart_str;

int uart_putchar(char c, FILE *stream);
// main.c
#include 
#include "uart.h"

void foo(void);

int main(void) {
   fprintf(&uart_str, "Hello");
   foo();
   while(1);
} 
// second.c
#include 
#include "uart.h"

void foo(void) {
     fprintf(&uart_str, "Hi from foo");
} 

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

Thanks for the advice. My setup is currently as in your example.

I think there was a miscommunication regarding the "str" for the LCD. The display module (used as an example here, really I meant any module) does not itself communicate via a UART. It uses some other interface (i.e., I2C or SPI). I want the display module to be able to print informational messages out the "external" uart to be monitored by some third party -- there is only one uart_str.

Science is not consensus. Science is numbers.

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

If you want the output of a module to be more universal, then the module could contain a FILE* which either gets loaded directly by main, or via a function.

// uart.c 
#include  
#include  
#include "uart.h" 

int uart_putchar(char c, FILE *stream);

FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE); 

int uart_putchar(char c, FILE *stream) 
{ 
   // blah blah 
   UDR = c; 
   return 0; 
} 

/ uart.h 
extern FILE uart_str; 


// main.c 
#include  
#include "uart.h" 
#include "second.h"

int main(void) { 
	second_init(&uart_str);
   fprintf(&uart_str, "Hello"); 
   second_foo(); 
   while(1); 
}

// second.c 
#include  
#include "uart.h" 
#include "second.h"

	FILE *second_output_fptr;

void second_init( FILE* output)
{
	second_output_fptr=output;
}

void second_foo(void) { 
     fprintf(second_output_fptr, "Hi from foo"); 
}
// second.h
void second_init( FILE* output);
void second_foo(void);

Telling yourself something is too hard, or too complicated, is not a determination. It is a decision.

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

The tutorial is very good - I used similar modules in a project containing about 40 source files.
A small hint to improve the source readability:
Use a template for your .c and .h files.
The example below is for a smaller project, a controller for a brushless DC motor. It includes comments for Doxygen. The template.h is similar

/*! \file NAME.c
  \brief description
*/

/* Include files */
#include "config.h"
#ifdef SYMBOL
#include 
#include "file"
/********************
	local RAM variables with initialisation as needed
*********************/

/********************
	local PROM variables with initialisation
*********************/

/********************
	local EEPROM variables with initialisation
*********************/

/********************
	external variable and function definitions in order
	RAM, PROM, EEPROM, functions
*********************/
/* **************** AVRDSS ****************/
/* ************** BLDControl **************/
/* ************** commutator **************/
/* *************** dispatch ***************/
/* ***************** PID ******************/
/* ***************** PLL ******************/

/********************
	Functions
*********************/

/********************
	Interrupt service routines
*********************/

/********************
	Event handlers
*********************/

#endif /* SYMBOL */
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Where do you put the module related header files?

Like for example my uart.c needs avr/io.h, avr/pgmspace.h etc. where should I put it? In the uart.h or in the uart.c?

I know that uart.h should be included in uart.c so even if I put the other header files in the uart.h it will be included in uart.c

So which version do you prefer? Putting the includes in the module header file, or in the source file?

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

Try to keep header files as local as possible to what they document. Not everyone who includes your uart.h necessarily wants an inclusion of avr/io.h and especially avr/pgmspace.h forced upon them.

What you can do is use those files own header protection to give warnings about their requirement. Say your uart.h uses types such as uint8_t which are indirectly defined by the inclusion of avr/io.h you could rely on the fact that the io.h has this:

#ifndef _AVR_IO_H_
#define _AVR_IO_H_

So in your own header file you could include:

#ifndef _AVR_IO_H_
 #error "uart.h requires avr/io.h to be included before it"
#endif

If the user then uses:

#include "uart.h"
#include 

they would get that compilation error. While:

#include 
#include "uart.h"

would work just fine.

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

I generally don't use header files, I just declare everything (relating to that module) at the top of each code module. Seems quicker from an editing point of view and reduces the number of files involved.

But I frequently have to put #include et al at the top of each code module. Is there a danger in having the same header file appear multiple times like this at the top of multiple modules.

I use:
AS6 (under duress)
WinAvr 20100110
Windows 10
STK 600
AVR ONE!(broken and Atmel won't fix)
JTAGICE3 (only works with AS6)
AVRISP II
So far - '88 '2560
Sometimes my own RTOS

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

Quote:
I generally don't use header files, I just declare everything (relating to that module) at the top of each code module. Seems quicker from an editing point of view and reduces the number of files involved.

But I frequently have to put #include et al at the top of each code module. Is there a danger in having the same header file appear multiple times like this at the top of multiple modules.


Those comments seem to suggest you really haven't got the concept of header files. The #include basically just pastes the included file into the file that has the #include - no magic. Consider the instance where you #include that file into another file that has already #included the file - you have two instances of that file - thus the compiler will complain. Understand the concept, follow the rules and life will be good.

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

I used to use them all the time for larger projects, but I find that with smaller MCU code, I would rather have the function declarations and variable definitions at the top of the .c module than having a separate file open. Just laziness more than anything else.

My question is if I have a program comprised of three .c modules and each of them have a io.h (or any other include) file included at the top, is there any danger? The compiler does not complain. Actually it will not compile if I don't do it. I could make a foo.h to go with my foo.c and put the io.h file inside the foo.h but that seems pointless.

I use:
AS6 (under duress)
WinAvr 20100110
Windows 10
STK 600
AVR ONE!(broken and Atmel won't fix)
JTAGICE3 (only works with AS6)
AVRISP II
So far - '88 '2560
Sometimes my own RTOS

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

Quote:
Consider the instance where you #include that file into another file that has already #included the file - you have two instances of that file - thus the compiler will complain.
No it won't. is set up correctly so that the already knows that it has been included.
Quote:
I would rather have the function declarations and variable definitions at the top of the .c module
That works for that file, but what if you need those variables and functions visible is multiple other files? Then you need to declare those functions and variables multiple times. Put them into a .h file, then you need only declare them once.
Quote:
My question is if I have a program comprised of three .c modules and each of them have a io.h (or any other include) file included at the top, is there any danger?
No, not at all. Separate .c files are just that, separate. None know what files the others include.

Regards,
Steve A.

The Board helps those that help themselves.

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

Quote:

Those comments seem to suggest you really haven't got the concept of header files.

And even more so the concepts of function prototypes, typedefs and extern declarations.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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:

And even more so the concepts of function prototypes, typedefs and extern declarations.

Indeed. Suppose you have an adc.c that contains:

int ADC_init(void) { .. }
uint16_t ADC_read(void) { .. }
void ADC_change_channel(uint8_t chan) { .. }

Now in main.c you want to call those functions. How on earth can you achieve that? How can the compiler know when it encounters a call to:

foo = ADC_Read();
or
ADC_change_channel(5);

what the input or return parameter types are? This is the whole reason you would have adc.h:

// adc.h
int ADC_init(void);
uint16_t ADC_read(void);
void ADC_change_channel(uint8_t chan);

It would be #include'd in main.c so the compiler knows what the interface to each of the routines is. This is pretty fundamental to the use of the C programming language, ignore it at your peril.

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

I believe that it is important for the understanding of this to keep the concepts of prototypes, typedefs and externals apart from the concept of the #include mechanism as such. Together they form a powerful mechanism for structuring projects on the source file level, but taking them apart will reveal what is actually going on and helps understanding what this is all about.

As short as I can manage:

Using Cliffs example, suppose you have functions for handling the ADC in a separate source file adc.c, but you want to call these functions from another source file, say main.c . Now purely technically you might get away without having any prototypes in adc.h but just the definitions (implementations) of the functions. In main.c you definitively must have prototypes for the functions, or else (as Cliff points out) the compiler will not know about parameter and return types etc.

But (and that is a big BUT): If you at some time change the actual definitions (implementations) of the functions in adc.c, but fail to update the prototypes in main.c the result will likely be disastrous. Since the compiler actually compiles each C source file separately it will not spot the mismatch. The linker will then happily resolve the calls from main.c to functions in adc.c – but since the code from main.c will push different parameters from what the code from adc.c expects everything will be a mess. You will not get any build errors, but you application will likelt crash at run time.

The solution is to make sure that the very same prototypes are seen in both adc.c and main.c. I am not speaking about two prototypes that look identical (because, again, with time one of them will change but not the other). I am talking about the two source files actually “seeing” the same prototype. The solution is to place such prototypes in a text file that is then inserted into both source files just before compilation proper takes place. This is what header files and the #include pre-processor directive is all about.

Now when you change an implementation in adc.c but fail to change the corresponding prototype in adc.h, the compiler will emit an error when compiling adc.c. You now change the prototype, but fail to change the actual call (e.g. adding a newly introduced parameter) in main.c – and the compiler will emit an error compiling main.c. When that also is corrected the application builds clean.

Think of an include file as a contract between the supplier (implementation) and consumer (caller) of some functionality. Both ends must “do the same thing” or you will get a build error. Without that contract you will get a runtime error. A build error is always preferable to a runtime error.

I cannot see how you can get around this if you have several source files. If you actually have your prototypes only in main.c and implementations in adc.c, then you need to rethink and take in what I describe above. Effectively, you are satisfying the compilers need for knowing how to call the functions, but lose out on the advantages you can get from a common include file. Ignoring this will lead to disaster sooner or later. Probably sooner..

Not so short after all, but I hope it helps to clear up what this is really about.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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

Guys, my original question was:
"Is there a danger in having the same header file appear multiple times like this at the top of multiple modules." Like io.h.

I do use a .h file if I find I want to put all the prototypes and variable declarations in one place. Sometimes. Most of the time they are at the top of my main source code module. If I want to re-use a bunch of stuff then I create a .h file for the module so I don't have to do much work the next time around.

I do use typedefs when I want to get a bit more portable.

I understand and use extern when needed.

I just asked what was going to happen if I put #include io.h at the top of each c module to make the compiler happy.

I do appreciate the extention of the tutorial but to extrapolate from my original question and statement that I prefer to put everything at the top rather than a separate .h file means I lack a bunch of understanding was a touch pedantic and condecending, don't you think?

I use:
AS6 (under duress)
WinAvr 20100110
Windows 10
STK 600
AVR ONE!(broken and Atmel won't fix)
JTAGICE3 (only works with AS6)
AVRISP II
So far - '88 '2560
Sometimes my own RTOS

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

I may have a module called read_gps.c
I call it and it returns a variable.

My main module would have the prototype for read_gps(xxx) But the read_gps.c module may have 50 functions to do its work. And all 50 functions may have their prototypes at the top of that code module.

Why is that a sin?

I don't and won't ever need to call any of those internal functions from anywhere else. I can make a single read_gps.h file and include the prototype for the read_gps(xxx) but that is just as much work or more than putting the prototype along with the others at the top of the main module.

I can put all the prototypes for the internal read_gps.c file in read_gps.h but then I would have to include it in two places to get the compiler to work.

I can have two .h files, one containing the read_gps(xxx) prototype to include in the main module and another containing all the internal module protypes to include inside the read_gps.c module but that is just for housekeeping. In this case I see no added value of header files.

I use:
AS6 (under duress)
WinAvr 20100110
Windows 10
STK 600
AVR ONE!(broken and Atmel won't fix)
JTAGICE3 (only works with AS6)
AVRISP II
So far - '88 '2560
Sometimes my own RTOS

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

Extrapolationg, you said? For functions that are both defined in and called from one and the same source file you do not need to have prototypes in a separate header file. I never said that.

I wasn't my meaning to be condecending. Every time one answers, one has to make some assumption of the knowledge and skill level of the intended receivers/readers. If I missed the mark widely this time, I did that. Period.

OTOH, when you're saying

Quote:

I can make a single read_gps.h file and include the prototype for the read_gps(xxx) but that is just as much work or more than putting the prototype along with the others at the top of the main module.

it seems to me that you've missed the point. The prototype for read_gps() should be seen in two source files, not only in the main module. If you actually type it into both files then i) this will be un-necessary work, and ii) it will not in any way "guarantee the contract".

Quote:
I just asked what was going to happen if I put #include io.h at the top of each c module to make the compiler happy.

Nothing bad, since that header file is nicely written and following good conventions. The compiler will use the parts of it that it needs, and the rest of the stuf in it will not be used.

Quote:
I do use a .h file if I find I want to put all the prototypes and variable declarations in one place.

I am a bit uncertain what is meant by "variable declarations" above, but if it is something like

int foo;

then this will spell problems when the header file in inluded into more than one source file. The root of the problem isn't the inclusion into two source files, but having the variable definition in the header file. They should go into .c source files just like function implementations.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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

Sorry, very importantly above my example for adc.h SHOULD have said:

// adc.h
#ifndef _ADC_H_
#define _ADC_H_

int ADC_init(void);
uint16_t ADC_read(void);
void ADC_change_channel(uint8_t chan); 

#endif // _ADC_H_

If you look at any "standard" header file such as those within the AVR-LibC collection you will see they all have protection like this. If main.c then said (in error):

#include "adc.h"
#include 
#include "adc.h"

or perhaps more likely

#include 
#include "adc.h"
#include "uart_that_happens_to_include_adc.h"

then on the first inclusion of adc.h the _ADC_H_ define is not made so the bit between #ifndef and #endif is used. This introduces all the types/interfaces and, inportantly now #define's _ADC_H_.

Now when it gets to the second (perhaps indirect) #include "adc.h" the _ADC_H_ symbol is already defined so the entire section between #ifndef and #endif is ignored this and all subsequent times.

THAT is the way you ensure that it doesn't matter if there are multiple inclusions of a .h file.

EDIT: BTW just looked at the universally used and guess what...:

#ifndef _AVR_IO_H_
#define _AVR_IO_H_

PS the annoying thing is that I even realised I'd forgotten to mention the header protection thing when I was sat in the local health centre having an ECG for a suspected heart attack (luckily not it turns out) but I was only just allowed back near a PC just now by she who must be obeyed.

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

JohanEkdahl wrote:

I wasn't my meaning to be condecending. Every time one answers, one has to make some assumption of the knowledge and skill level of the intended receivers/readers. If I missed the mark widely this time, I did that. Period..

Sorry, was not directed at you. The initial reply to my initial question didn't answer my question but did question my understanding.

Quote:
I am a bit uncertain what is meant by "variable declarations" above, but if it is something like
.

int foo;

Quote:

then this will spell problems when the header file in inluded into more than one source file. The root of the problem isn't the inclusion into two source files, but having the variable definition in the header file. They should go into .c source files just like function implementations.

I should have specified, global variables. I have placed global variables in a .h at times. Most of the time my .h files just have function prototypes, defines and typedefs. But now-a-days, I don't use many .h files. I just put it all in the code.

I use:
AS6 (under duress)
WinAvr 20100110
Windows 10
STK 600
AVR ONE!(broken and Atmel won't fix)
JTAGICE3 (only works with AS6)
AVRISP II
So far - '88 '2560
Sometimes my own RTOS

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

Quote:
I should have specified, global variables. I have placed global variables in a .h at times.

And such a header file can not be included into more than one source file or the linker will emit an error about a multiply defined variable. So, since this file is only included into one source file this should fall under your "rule" about unnecessary header files. You could just as well define that variable in the .c source file.

It all boils down to this: Things that are to be shared between several source files needs to be defined in one of the source files (yes, it could be by way of having it in a header file that just one source file includes). In other source files it must be declared (for a variable this is an extern declaration, for a function this is a function prototype) and this the reasonable approach is to have those declarations in a header file so that the compiler can assert that the definition and the usage does not conflict.

You may do as you wish, and live with the risks of your M.O.. But this had to be said so that C beginners are not led astray when reading this thread.

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"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:

And such a header file can not be included into more than one source file or the linker will emit an error about a multiply defined variable.

Just to note that the header protection (#ifndef _ADC_H_) I mentioned above will not protect against this. Imagine this scenario again but with a modified adc.h:

// adc.h
#ifndef _ADC_H_
#define _ADC_H_

int ADC_init(void);
uint16_t ADC_read(void);
void ADC_change_channel(uint8_t chan);

uint16_t adc_reading;

#endif // _ADC_H_ 

That now has an 'adc_reading' variable definition added. Now, sure, if the programmer used this in main.c:

#include "adc.h"
#include 
#include "adc.h" 

then there still would be no problem as the re-include protection comes into play so the definition of 'adc_reading' like everything else in the header file is only seen once even if adc.h is included more than once.

However main.c is just one "compilation unit". At the time it is compiled then sure only one copy of adc_reading is created within main.o

The problem comes when you also have a motor_control.c that also does:

#include 
#include "adc.h"
... 

When this compilation unit is compiled (which could even be the Tuesday after main.c was last compiled) it too will see the contents of adc.h and it too will therefore create an entity called adc_reading in motor_control.o

Finally when the linker comes to join main.o and motor_control.o to make myproject.elf it will find two different adc_reading variables and emit an error about "multiply defined".

That is the reason you should never put anything that generates either variable or function bytes into a .h because it almost inevitably leads to multiple definitions from different compilation units.

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

Quote:

Our coding standard contains an extension of your function naming suggestion that others may find useful. Here's an excerpt from our standard:

http://www.ourcottage.plus.com/c...

I have not yet read through all this whole topic.
And it seems there is nothing further mentioned about the
contents of cs.html.

The http://www.ourcottage.plus.com/c... is closed.
So could anyone give a new link or add an attachment?

Thanks ahead. :D

Andy Liu

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

I never copied it to a new site so that's a dead link. I now work for the company that wrote that standard, not the company where we used it as our own template so it would now likely violate my contract of employment to distribute a copy - so it's probably as well the link is now dead!

If you want a good standard to work by start with MISRA!

Also read "Writing Solid Code" my Steve Maguire.

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

Anywany,thanks cliff. :D

Andy Liu

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

Good explanations. Thanks. It almost works but ..
I'm trying to organize my project this way and realized that I can't externally declare my global register variable :-(
I've tried different combinations of keywords in extern declaration like

extern volatile unsigned char flags;
extern volatile register unsigned char flags;
extern unsigned char flags;

gettin' different error messages. What do I do wrong?

There are few files:

//--------main.h
  /* not important */
//--------/main.h
//--------extern.h
#ifndef EXTERN_H
#define EXTERN_H

/* Global flags and its bit positions */
extern volatile unsigned char flags; // THE SUBJ

#define flags_KeyPressed_bp 3    //keyboard driver related

/* these two ugly funcs are all I could do to access the subj variable :-( */
void SetGlobalFlag( uint8_t bitpos, uint8_t value );
uint8_t GetGlobalFlag( uint8_t bitpos );

#endif //#ifndef EXTERN_H
//--------/extern.h
//--------main.c

#include "main.h"
#include "extern.h"

//Global flags
register volatile unsigned char flags asm("r7"); //THE SUBJ VARIABLE

// my eerie funcs to handle flags. 'cause I can't get it work with externing :(
// Actually I do not want them here!
void SetGlobalFlag( uint8_t bitpos, uint8_t value ) {
    if (value) flags |= (1<<bitpos);
    else flags &= ~(1<<bitpos);
}

uint8_t GetGlobalFlag( uint8_t bitpos ) {
    return ((flags & (1<<bitpos)) != 0);
}


int main() {...}

//--------/main.c
//--------kbd_drv.h

#ifndef KBD_DRV_H
#define KBD_DRV_H

/***********************************************************************
 * here we define macroses to read and set keyboard flag. user can
 * change those to put flag wherever he wants.
 **********************************************************************/

/* Why these two don't work? (../kbd_drv.c:55: undefined reference to `flags')*/
//#define KEYBOARD_SET_FLAG flags |= (1<<flags_KeyPressed_bp)
//#define KEYPRESSED ((flags & (1<<flags_KeyPressed_bp)) == 0 ? 0 : 1)

/* these two work, but it is not an option to use function call here */
#define KEYBOARD_SET_FLAG SetGlobalFlag(flags_KeyPressed_bp, 1)
#define KEYPRESSED GetGlobalFlag(flags_KeyPressed_bp)


/***********************************************************************
  Function definitions
***********************************************************************/
void Keyboard_Driver( void );


#endif //#ifndef KBD_DRV_H
--------/kbd_drv.h
//--------kbd_drv.c

#include "extern.h"
#include "kbd_drv.h"

//perhaps this flag is stored somewhere else. see defines of
// KEYBOARD_SET_FLAG, KEYPRESSED.
//unsigned char kbdPressed = 0;        //"Key pressed" flag

 
void Keyboard_Driver( void ) {
   /* something unrelated */

   /* Here we have error (../kbd_drv.c:55: undefined reference to `flags')*/
   KEYBOARD_SET_FLAG;

   /* something unrelated */
}
//--------/kbd_drv.c

One clever book said that "The register declaration can only be applied to automatic variables and to the
formal parameters of a function". But I think if I could define it as external one, there must be a way to declare it as external too, mustn't it?

Last Edited: Thu. Oct 16, 2014 - 11:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How could you have a global "register"? When foo.c is compiled the compier might choose to assign R7 to the global. When bar.c is compiled it might choose R13.

 

If you really want a fixed register in multiple compilation units use:

volatile unsigned char flags asm("r16");

Then also pass -ffixed-16 (or whatever the register number is) to the compiler.

 

Note however that the library code will NOT have been built with -ffixed-16 so there's every chance it might be using R16 and if you call a library function you may find R16 is corrupted.

 

To be honest I don't know why you'd do this for "flags" - isn't this why the AVr has GPIOR0/1/2 ?? (obviously use GPIOR0 if you can - especially for bit flags!)

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

yyw794 wrote:
i think this is much more convenient,cleaner and easier than your method.

I think you are probably in a minority of one, there!

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

If you really want a fixed register in multiple compilation units use:

volatile unsigned char flags asm("r16");

I use almost the same, but with register keyword. I've tried this very form you shown, and it haven't place var in the register. It uses LDS / STS if there is no register keyword. :(
The compiler really does not use r7 (I use r7 in definition, and use -ffixed-7 in compiler params): I looked for it in listing file - none at all.
But the good thing I've found that if I do not use register keyword in definition then I can declare

/* in extern.h */
extern volatile unsigned char flags asm("r7"); //no errors, no warnings

//extern volatile register unsigned char flags asm("r7");
//this causes "../extern.h:14: error: multiple storage classes in declaration specifiers"

with no errors! :-)

useless, of course, because it's about memory location, not register :(

 

Note however that the library code will NOT have been built with -ffixed-16 so there's every chance it might be using R16 and if you call a library function you may find R16 is corrupted.

it is not a problem. There is no large lib in use, and anyway, I can inspect those libraries for reg usage.

To be honest I don't know why you'd do this for "flags" - isn't this why the AVr has GPIOR0/1/2 ?? (obviously use GPIOR0 if you can - especially for bit flags!)

I asked this mega8 for any GPIORx - no answer.. (and TWI is in use too ).

 

add:
Also found that if I try to pass 5 pointers to some function, it places them to r16 .. r25 even with -ffixed-16.

Last Edited: Sat. Oct 18, 2014 - 05:48 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's an old article, but it has really helped me a lot. I really appreciate you.

AVR, ARM, MFC, .NET