Author Topic: Multiple file C projects  (Read 5133 times)

0 Members and 1 Guest are viewing this topic.

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17816
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Multiple file C projects
« on: September 10, 2015, 04:10:13 pm »
Although generically this is with particular reference to Atmel studio.

I'm trying to create my own basic project template so that I have a separate C file full of functions and other code relative to each peripheral that I can call on when needed. Sort of Arduino style but with more control and manually setting things up.

So from what I gather I need a separate C file and a header file for each library if perhaps I should call it that? So I add the see file to my project and include the header file in the main C files header file and then include that main header file in the main C file if that is not confusing enough?

So going the other way I would have a main C project file with it header file which is included in that C file. All the content of that header file will appear in the main C file when it is compiled. Therefore if I include all of the other header files in the main program header file that should work? And then I need to add each C file to my project in atmel studio.

I ask because I created a nice set of functions in a separate C file invoked them and absolutely nothing happened. So I copied and pasted the operational code from each function that I was using into my main C file and compiled it. Everything worked.

This is a righteous pain in the backside as it happens. My first attempt at using more than one file for a project was to put everything in header files as I was apparently misguided at the time. But it worked! As soon as I tried to do it properly it becomes a nightmare.
 

Online IanB

  • Super Contributor
  • ***
  • Posts: 11888
  • Country: us
Re: Multiple file C projects
« Reply #1 on: September 10, 2015, 04:21:17 pm »
Basically if you have a source file (*.c) full of functions, then you should have a companion header file (*.h) of the same name that declares the prototypes for each of the functions in the .c file. You include the .h file at the top of the .c file to make sure your functions match their prototypes.

Then you include the header file at the top of any other source file that will be using your functions and make sure the library .c file is included when you build the project.

You can create a "master header file" that includes a whole lot of individual header files in one go, but that is probably not worth the trouble. Just include each individual header file as you need it.
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17816
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: Multiple file C projects
« Reply #2 on: September 10, 2015, 04:24:52 pm »
I see. Well as it happens I end up using a main header file for things like definitions that allow me to manipulate bits on ports more easily than doing complicated maths that I will forget the meaning of later. For example

#define bit1(p,m) ((p) |= (BIT(m)))

Which then allows me to go further in simplifying code so that I can understand it later and write

#define A0_output bit1(DDRA,PA0)

Keep all this crap out of the way in a main header file.
 

Offline helius

  • Super Contributor
  • ***
  • Posts: 3642
  • Country: us
Re: Multiple file C projects
« Reply #3 on: September 10, 2015, 04:29:29 pm »
Header files are nowhere required, but they make life easier.
If you had a source file usart.c, that defined a function: void sendbyte(char d, port p) {}, then it would be compiled into an object and loaded when other code needed it. But in order for the other source files to use it correctly, the function's prototype should be declared:
extern void sendbyte(char,port); in each file that uses it.
The .h file just collects all those declarations in one place so they don't need to be typed over.
 

Online IanB

  • Super Contributor
  • ***
  • Posts: 11888
  • Country: us
Re: Multiple file C projects
« Reply #4 on: September 10, 2015, 04:52:18 pm »
I see. Well as it happens I end up using a main header file for things like definitions that allow me to manipulate bits on ports more easily than doing complicated maths that I will forget the meaning of later.

It's fine to do that. What I mean by "main header file" is a header file that includes other header files. I find it more convenient to include each header file I need one by one individually than to have a master "include everything" file.

So you could have a header file for "hardware definitions", another for "this utility library", one more for "that utility library", and so on.
 

Offline bookaboo

  • Frequent Contributor
  • **
  • Posts: 729
  • Country: ie
Re: Multiple file C projects
« Reply #5 on: September 10, 2015, 05:00:35 pm »
I see. Well as it happens I end up using a main header file for things like definitions that allow me to manipulate bits on ports more easily than doing complicated maths that I will forget the meaning of later.

It's fine to do that. What I mean by "main header file" is a header file that includes other header files. I find it more convenient to include each header file I need one by one individually than to have a master "include everything" file.

So you could have a header file for "hardware definitions", another for "this utility library", one more for "that utility library", and so on.

Yep, makes it easy so when you do have nice working functions and libaries you can just copy the .c and the .h file into a new project without a load of copy/paste mess.
 

Online Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: Multiple file C projects
« Reply #6 on: September 10, 2015, 05:09:40 pm »
The split between compiler and preprocessor is important here. Notice how #include starts with an #, that is a preprocessor directive.

If you are a messy programmer, you can even use #include "library.c" at the top of the header to merge in the entire C file, at that point, before going to the compiler.
There are already too many messy programmers around, so please don't do that. (except when actually useful, as with compile-time script generated lookup tables)

Instead, create functional modules that can be tested as a module, which have one header and 1 or more C files, instead of one C file with 10k lines.
I know you're just waiting for an example, so here is an header as I often use them, from memory:
Code: [Select]
#ifndef HEADER_H
#define HEADER_H
// https://en.wikipedia.org/wiki/Include_guard

#include <inttypes.h>

// Dependencies
#include "otherModule.h"

// Typedefs and such...

// Publics
extern int hdr_publicParameter1;

// Api, prefixed with hdr, since there can't be a duplicate object name in the linker stage.
void hdr_moduleInit();
void hdr_transmit(const char *stuff);
uint8_t hdr_isReady();
void hdr_deinit();

// Defines
#define hdr_doInlineThingy(x)   (x+6)

#endif //HEADER_H

C files look like this, using static to prevent being messy with globals.
Code: [Select]
#include <stdstuff.h>
#include "header.h"

// Constants
const int settingA = 6;    /// Set A feature speed.
const int settingB = 9;    /// Set B feature count.

// Private routines
static void hdr_processParam();
static void hdr_clearMem();

// Private variables (private to the module that is)
static int elapsedTime;     /// Time elapsed during transmit.
static int eventCounter;   /// Counter for events.

// publics
extern int hdr_publicParameter1;  /// Doxygen brief.

// Private routines
/** @brief processes the parameters
 * Processes of the parameters according y=ax+b
 */
void hdr_processParam(){
   static int counter; // Persistent variable, but not public.

}
...

// Public routines
/** @brief processes the parameters
 * Processes of the parameters according y=ax+b
 */
void hdr_moduleInit(){

}
...
Yes, I know they are not strictly structured. This I only see when there is teaming with more than two people.
This all navigates fine with an IDE that has right-click->GoTo features. Doxygen is also nice in generating live popups of the function brief when autocompleting and such.

When done, you need to add the C files to your makefile. And the include paths of the headers.
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17816
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: Multiple file C projects
« Reply #7 on: September 10, 2015, 05:22:24 pm »
ok so maybe my problem is elsehwere as I'm still not getting results although code compiles fine:

Attached is my project.

The stuff that is there works and is the code called by the functions, the functions are commented out
« Last Edit: September 10, 2015, 05:28:01 pm by Simon »
 

Offline Mechatrommer

  • Super Contributor
  • ***
  • Posts: 11639
  • Country: my
  • reassessing directives...
Re: Multiple file C projects
« Reply #8 on: September 10, 2015, 05:40:17 pm »
Header files are nowhere required, but they make life easier.
If you had a source file usart.c, that defined a function: void sendbyte(char d, port p) {}, then it would be compiled into an object and loaded when other code needed it. But in order for the other source files to use it correctly, the function's prototype should be declared:
extern void sendbyte(char,port); in each file that uses it.
The .h file just collects all those declarations in one place so they don't need to be typed over.
your example is too simplistic of one or two functions only. when the library grows to hundreds of functions, you are going to say.... "Header files are must be required"... for several reasons other than just protoyping functions....
Nature: Evolution and the Illusion of Randomness (Stephen L. Talbott): Its now indisputable that... organisms “expertise” contextualizes its genome, and its nonsense to say that these powers are under the control of the genome being contextualized - Barbara McClintock
 

Online tggzzz

  • Super Contributor
  • ***
  • Posts: 19506
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: Multiple file C projects
« Reply #9 on: September 10, 2015, 05:43:57 pm »
Here's a touchy-feely way to think about the .c/.h hack. (BTW, it was a very useful hack back when machines had 64kb memory).

The definitions in a header file are a promise to the external world that the functions will be made available at the right time (i.e. during linking).

Including a header file (either in a main.c file or in a master.h file) specifies that the main.c file requires those facilities (i.e. depends upon or is coupled with), but the implementations are not necessary for compilation of main.c. Hence a single "master" header file #defining all header files rather defeats the advantages of minimising coupling - and can sometimes cause unnecessarily long compilation times.

A lib.h file would contain the API defining what is provided by the library, but the implementation of the API is in the corresponding lib.c file. There is no requirement that there is only one lib.c file. It usually makes sense to split the library into lib-part-a.c lib-part-b.c lib-part-c.c files. Normally all lib*.c files will be compiled together into a single object .o file or set of .o files.

In general the contents of a .h file should only change much less frequently than the .c files.

Note that many compiler optimisations are not possible unless the compiler is compiling all the .c files in one fell swoop. Compiling lib.c has to be pessimal since the compiler cannot predict how the functions in it will be used in main.c, whereas compiling main.c cannot change what has already been compiled in lib*.c. The linker can only knit everything together.
There are lies, damned lies, statistics - and ADC/DAC specs.
Glider pilot's aphorism: "there is no substitute for span". Retort: "There is a substitute: skill+imagination. But you can buy span".
Having fun doing more, with less
 

Offline donotdespisethesnake

  • Super Contributor
  • ***
  • Posts: 1093
  • Country: gb
  • Embedded stuff
Re: Multiple file C projects
« Reply #10 on: September 10, 2015, 06:16:52 pm »
I tried building the project, it seemed ok to me after I uncommented the code in main().

One thing I noticed  in timer0.c
Quote
void counter0_mode (uint8_t mode)
{
   switch (mode)
   {
      case (0):
      bit0(TCCR0B, WGM02);
      bit0(TCCR0A, WGM01);
      bit0(TCCR0A, WGM00);
      
      case (1):
      bit0(TCCR0B, WGM02);
      bit0(TCCR0A, WGM01);
      bit1(TCCR0A, WGM00);
      

Should there be a break in there? Although the compiler didn't generate a warning, so maybe I missed something.

You will find sooner or later that include files can get included twice, so will need a way to avoid that. In gcc, you can use "#pragma  once" at the top of include files.

You may already know it, but the compiler generates a listing file called ATtiny24A.lss which contains the source code and assembler output, which can be really handy.
Bob
"All you said is just a bunch of opinions."
 

Offline Wim_L

  • Regular Contributor
  • *
  • Posts: 212
  • Country: be
Re: Multiple file C projects
« Reply #11 on: September 10, 2015, 09:05:06 pm »
A break may or may not be required. Switch statements in C fall through, which means that everything after the matching case statement gets executed until a break or the end of the switch statement is encountered. In this case, because there is no break, if mode == 0, then the code after case (0): will get executed, and because there's no break the code after case (1): will also be executed.
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17816
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: Multiple file C projects
« Reply #12 on: September 10, 2015, 09:54:35 pm »
Ah that might be it. yes the baffling thing was that it compiles ok, I was kind of getting round to thinking that there may be problems there.

I'm actually thingking of converting the lot to defines.
 

Offline linux-works

  • Super Contributor
  • ***
  • Posts: 1999
  • Country: us
    • netstuff
Re: Multiple file C projects
« Reply #13 on: September 10, 2015, 10:30:22 pm »
if you are starting out in C, I'd recommend you go light (easy) on the defines and header magic.  stuck with regular functions in C and the only header 'magic' you should use is to repeat the definition of the function which is known as a 'prototype'.  such as:

main1.c file has:

#include "a.h"

int main()
{
  int b=abc();
}


a.h file has:

int abc();



a.c file has:

int abc()
{
 return 0;
}


(just as a stupid-simple example).

the a.c and a.h files 'implement' the abc() function and the header (a.h) is what you look at to see how to call abc().  in theory, you should even get used to NOT seeing what is inside a.c since it may be compiled and given to you in binary, only.

if you get a binary library or .o file and a header file, the header is your only 'contract' with the api routines inside the binary object that you link against and make calls into.

that is the MAIN use for header files.

I would really avoid tricky macros and defines if you can avoid it.  first, its not portable across other languages (the concept) and its also overused and abused too much.

you can get 99% of what you want done if you play it simple.  plus, the reader will thank you for keeping things simple.

stick with C functions and let the header be the function -prototypes- and little else.  my suggestion, fwiw (writing C code since the mid 80's).

Offline linux-works

  • Super Contributor
  • ***
  • Posts: 1999
  • Country: us
    • netstuff
Re: Multiple file C projects
« Reply #14 on: September 10, 2015, 10:33:34 pm »
Ah that might be it. yes the baffling thing was that it compiles ok, I was kind of getting round to thinking that there may be problems there.

'it builds!  ship it!' is a long-standing joke in software development.

char i;
for (i='a'; i<'c'; i++) ;
  putchar(i);

what will this do?  it builds.  can we ship it?

;)

Offline Hideki

  • Frequent Contributor
  • **
  • Posts: 256
  • Country: no
Re: Multiple file C projects
« Reply #15 on: September 11, 2015, 12:27:40 am »
case (1): looks really odd. Why not use case 1: like "everyone" else?

It's similar to the weird feeling I get when I see things like: return (1);

But of course... it will build, so you can ship it! :)
 

Offline true

  • Frequent Contributor
  • **
  • Posts: 329
  • Country: us
  • INTERNET
Re: Multiple file C projects
« Reply #16 on: September 11, 2015, 01:16:48 am »
case (1): looks really odd. Why not use case 1: like "everyone" else?

It's similar to the weird feeling I get when I see things like: return (1);

But of course... it will build, so you can ship it! :)

That's just syntactic preference (in modern times anyway). Some old C compilers did require this. One language I deal with regularly, which has its compiler heavily modified from a gcc2.95 codebase, requires this otherwise errors will be thrown and it will not compile.
 

Offline FreddyVictor

  • Regular Contributor
  • *
  • Posts: 164
  • Country: gb
Re: Multiple file C projects
« Reply #17 on: September 11, 2015, 08:55:08 am »
Ah that might be it. yes the baffling thing was that it compiles ok, I was kind of getting round to thinking that there may be problems there.

I'm actually thingking of converting the lot to defines.

using defines does have an advantage since they are typeless and avoid potential bugs like:
Code: [Select]
uint8_t timer1_read ()
{
return TCNT1;
}

I can see where you are going, but to me all that timer code is a bit OTT
would have thought defining the bit masks for each timer mode would be a better way than all those case statements

we're all learning tho'  :)
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17816
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: Multiple file C projects
« Reply #18 on: September 11, 2015, 11:52:42 am »
Ah that might be it. yes the baffling thing was that it compiles ok, I was kind of getting round to thinking that there may be problems there.

I'm actually thingking of converting the lot to defines.

using defines does have an advantage since they are typeless and avoid potential bugs like:
Code: [Select]
uint8_t timer1_read ()
{
return TCNT1;
}

I can see where you are going, but to me all that timer code is a bit OTT
would have thought defining the bit masks for each timer mode would be a better way than all those case statements

we're all learning tho'  :)

the timer1 code is derived from the timer0 code and I've not finished updating it. The idea is that I can use a custom name to define my mode and that is the end of that and it means something when i read it back later. Or I cam use the mode number as displayed on the datasheet. Far quicker.

I'll probably look at converting themto defines to lighten the code. Of i find i get lost in setup routines and every change means rereading the datasheet so this will hopefully make life simpler for me.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf