[TUT] Modularizing C Code: Managing large projects

Go To Last Post
145 posts / 0 new

Pages

  • 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.

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

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

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

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

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

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

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

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

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

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

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

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

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

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

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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

Pages