Multiple file C projects - Page 1
Forum section: electronics / general

author: Simon
author_rank: Unknown
timestamp: September 10, 2015, 04:10:13 pm
content: 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.

author: IanB
author_rank: Super Contributor
timestamp: September 10, 2015, 04:21:17 pm
content: 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.

author: Simon
author_rank: Unknown
timestamp: September 10, 2015, 04:24:52 pm
content: 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.

author: helius
author_rank: Super Contributor
timestamp: September 10, 2015, 04:29:29 pm
content: 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.

author: IanB
author_rank: Super Contributor
timestamp: September 10, 2015, 04:52:18 pm
content: Quote from: Simon on September 10, 2015, 04:24:52 pmIt'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.

author: bookaboo
author_rank: Frequent Contributor
timestamp: September 10, 2015, 05:00:35 pm
content: Quote from: IanB on September 10, 2015, 04:52:18 pmYep, 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.

author: Jeroen3
author_rank: Super Contributor
timestamp: September 10, 2015, 05:09:40 pm
content: 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:#ifndef HEADER_H#define HEADER_H// https://en.wikipedia.org/wiki/Include_guard#include <inttypes.h>// Dependencies#include "otherModule.h"// Typedefs and such...// Publicsextern 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_HC files look like this, using static to prevent being messy with globals.Code:#include <stdstuff.h>#include "header.h"// Constantsconst int settingA = 6; /// Set A feature speed.const int settingB = 9; /// Set B feature count.// Private routinesstatic 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.// publicsextern 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.

author: Simon
author_rank: Unknown
timestamp: September 10, 2015, 05:22:24 pm
content: 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

author: Mechatrommer
author_rank: Super Contributor
timestamp: September 10, 2015, 05:40:17 pm
content: Quote from: helius on September 10, 2015, 04:29:29 pmyour 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....

author: tggzzz
author_rank: Super Contributor
timestamp: September 10, 2015, 05:43:57 pm
content: 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.

author: donotdespisethesnake
author_rank: Super Contributor
timestamp: September 10, 2015, 06:16:52 pm
content: I tried building the project, it seemed ok to me after I uncommented the code in main().One thing I noticed in timer0.cQuoteShould 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.

author: Wim_L
author_rank: Regular Contributor
timestamp: September 10, 2015, 09:05:06 pm
content: 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.

author: Simon
author_rank: Unknown
timestamp: September 10, 2015, 09:54:35 pm
content: 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.

author: linux-works
author_rank: Super Contributor
timestamp: September 10, 2015, 10:30:22 pm
content: 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).

author: linux-works
author_rank: Super Contributor
timestamp: September 10, 2015, 10:33:34 pm
content: Quote from: Simon on September 10, 2015, 09:54:35 pm'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?

author: Hideki
author_rank: Frequent Contributor
timestamp: September 11, 2015, 12:27:40 am
content: 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!

author: true
author_rank: Frequent Contributor
timestamp: September 11, 2015, 01:16:48 am
content: Quote from: Hideki on September 11, 2015, 12:27:40 amThat'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.

author: FreddyVictor
author_rank: Regular Contributor
timestamp: September 11, 2015, 08:55:08 am
content: Quote from: Simon on September 10, 2015, 09:54:35 pmusing defines does have an advantage since they are typeless and avoid potential bugs like:Code:uint8_t timer1_read (){return TCNT1;}I can see where you are going, but to me all that timer code is a bit OTTwould have thought defining the bit masks for each timer mode would be a better way than all those case statementswe're all learning tho'

author: Simon
author_rank: Unknown
timestamp: September 11, 2015, 11:52:42 am
content: Quote from: FreddyVictor on September 11, 2015, 08:55:08 amthe 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.