Making a call to a function in a different file not working

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

I understand the concept of making a call to a function that is in a different file.  I created a Function_Prototypes.h file where all my prototypes are placed.  Function_Prototypes.h  is #included in all the .c files.  All the files are part of the project.  I assume (I know the danger of that) gcc automatically links all the files in a project.  I am running Atmel Studio 7 Version 7.0.2397.

 

I use init.c to initialize all the different hardware items (USARTs, Timers, Interrupts, GPIO, etc. ).  I ideally I create a file for each hardware item (USART0-3.c, Timers.c, Int.c) and call the initialize functions from each of the hardware files (USART_Initialize(), Timers_Initialize(), Init_Initalize() ).   

 

Some of the init functions will compile when called by init.c - USART_init() works from the USART0-3.c file, yet the tc_init() will not work from the Timer.c file.

 

The interrupt calls for the different hardware do not work from the hardware files and must be placed in init.c for them to work.

 

The error reported is "undefined reference to 'tc_init'  "and for the interrupt "undefined reference to 'usart0_int_handler' ".

 

Any insight into this issue would be appreciated!

David

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

There is a tutorial on modularizing your C project here: https://www.avrfreaks.net/forum/...

You may find it helpful, I did.

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

Hi Jim - thanks for the tip.  I read thru the article, only it refers to makefile, which, as I understand it, gcc-Atmel Studio 7 does not use as a default.  It appears that all files must be added as existing files - which mine have been - still no luck on the undefined errors.

David

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

The overriding idea to keep in mind- you are compiling a single source file (typically a .c file, but compiler doesn't care about names). You normally compile more than 1 source file, but the compiler is only interested in 1 (at a time) and has no concerns about the other 99 (those are 99 separate 'compiles').

 

Since the compiler has no idea about the other 99 source files, it needs some way of being able to use functions/vars from other files, so you tell it someFunc will exist, and it looks like 'void someFunc(void)'. The compiler will believe you and be happy (it knows the name and how to use it).

 

The linker has to put it all together, so if you lied to the compiler and there is no 'someFunc' as you said there would be, the linker will complain (undefined reference...).

 

So, in the end your job is simply to compile a single source file, and ideally the source file is only provided the info it needs. Same for each source file. If you can keep that idea in mind, it goes a long way to understanding how to organize files/headers.

 

Simple example-

 

//timers.h - for those that want to use functions/vars from timers.c, include this file

#pragma once //prevents including this file more that once

#include <stdbool.h> //since we use bool in this header

void Timers_Initialize(); //these are the only things we

extern bool isTimersInit; //allow others to know about what is in timers.c

 

//timers.c

#include "timers.h" //not needed, but makes sure what we do below matches what we tell others

#include <stdbool.h> //although currently provided in timers.h, we also use a bool for a static var

                     //so if we change the header (no longer needs bool), we will still be ok here

bool isTimersInit; //var we told others about

static bool inProgress; //local var, only we know about

void Timers_Initialize(){ inProgress = true; /* init timers */ isTimersInit = true; inProgress = false; }

 

 

 

//init.h - for those that want to use functions/vars from init.c, include this file

#pragma once

void init(); //this is all they need to know

 

//init.c

#include "init.h" //not needed, but makes sure what we do below matches what we tell others

#include "timers.h" //we want to use timers things

void init(){ if( ! isTimersInit ) Timers_Initialize(); }

 

 

 

//main.c

#include "init.h" //this is all we need to know

int main(){

    init();

}

 

 

Notice each source file is only provided what it needs to compile. Timers.c basically needs nothing external, init.c needs to know about a var and function in timers.c, and main.c just needs to know about init in init.c. Each file compiled individually, and when each file compiles without error the linker can 'add' them all together. If you forgot to add init.c to the compile list, you will get an 'undefined reference to init' error since you lied to the compiler in main.c (there is no init function since you didn't compile init.c).

 

Also note that '#include' simply places the file you specified 'inline', so each source file looks like this (I removed comments)-

 

//main.c

void init();

int main(){

    init();

}

 

 

//timers.c

#include <stdbool.h> //this is also inlined here, I'm just not showing it

void Timers_Initialize();

extern bool isTimersInit;

 

#include <stdbool.h> //this would not be inlined here as it would have been 'include guarded'

bool isTimersInit;

static bool inProgress;

void Timers_Initialize(){ inProgress = true; /* init timers */ isTimersInit = true; inProgress = false; }

 

 

//init.c

void init();

#include <stdbool.h> //this is also inlined here, I'm just not showing it

void Timers_Initialize();

extern bool isTimersInit;

 

void init(){ if( ! isTimersInit ) Timers_Initialize(); }

 

 

Now you may be able to see that each source file does not depend on other source files to compile. For example, if we had no init.h header you can see that simply providing a declaration of 'void init()' would be enough to use init in init.c (assuming init was not a static/local symbol that prevented others from using).

 

You can also see any header 'system' is simply to relive you from the tedious work of providing your source file with all the info it needs, but in the end it is still a single source file.

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

dfansler wrote:

The error reported is "undefined reference to 'tc_init'  "and for the interrupt "undefined reference to 'usart0_int_handler' ".

Any insight into this issue would be appreciated!

 

It is hard to provide any insight without seeing the actual code. Your description in the initial post suggests that you've done everything correctly. There's no way to say what the reason for the error could be without more information.

Dessine-moi un mouton

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

I suspect a typo.

Either that, or a file you thought was in the project was not.

I think that you can find a list of all the source files in a project.

If all else fails, look for all the .o file in a directory tree.

Iluvatar is the better part of Valar.

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

Wow Curtvm - an absolutely wonderful reply!  I thank you for the time you spent on the reply.  It explains a lot and I cannot wait to implement it later today!

kind regards,

David