Can I write my own .c and .h files and use them with .pde main project?

Hello,

I'm new on Arduino.
can I write my own .c and .h files and use them with main.pde project?

Here is what I did:
1/ I wrote funcion void tonTimer(T_TMR *t) and store it to file FC060.c
2/ I wrote line extern void tonTimer(T_TMR t); and store it to file FC060.h
3/ I included #include "FC060.h" in file main.pde and
I called this function tonTimer(&tmr1); but I got compiling error
undefined reference to 'tonTimer(T_TMR
)'
What's wrong ? Files FC060.c, FC060.h, main.pde are compiled OK when I delete line tonTimer(&tmr1);

/*
file:      FC060.c
project:   
device:    
date:      
desc.:    software timer 
*/

#include "FC060.h"
#include <avr/io.h>

T_TMR   tmr1;
T_TMR   tmr2;

void tonTimer(T_TMR *t)
{
  unsigned char sb   ; 
  unsigned char value; 
  unsigned char preset;
  
  sb     =  t-> sb;
  value  =  t-> value;
  preset =  t-> preset;
  
  if (sb==_BV(EN)){
     sb = (_BV(EN) || BV(RUN));
     value  = preset; 
  }
  
  if (bit_is_set(sb,RUN)){
    if (TMR_BIT){
      value--;
      if (value==0){
         sb = _BV(CPL);
      }
    }
  } 
  
  t-> sb = sb;
  t-> value = value;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*                                                             
file:      FC060.h                                             
project:                                                       
device:                                                        
date:                                                          
desc.:     header file for software timer                      
*/                                                             
                                                               
                                                               
#ifndef _FC060_H                                               
#define _FC060_H                                               
                                                               
#include <avr/io.h>                                            
                                                               
#define EN                     (0)                             
#define RUN                    (1)                             
#define CPL                    (2)                             
                                                               
#define TMR_BIT               (bit_is_set(ip,0))               
// uint8_t ip is defined in main skatch                        
                                                               
                                                               
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
                                                               
                                                               
// software timer data                                         
typedef struct {                                               
  unsigned char sb   ;  // holds flags                         
  unsigned char value;  // is value                            
  unsigned char preset;  // init value                         
} T_TMR;                                                       
                                                               
                                                               
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
                                                               
extern T_TMR   tmr1;                                           
extern T_TMR   tmr2;                                           
extern uint8_t ip  ;                                           
                                                               
extern void tonTimer(T_TMR *t);                                
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#endif _FC060_H
// file main.pde
//

#include <avr/io.h>
#include "FC060.h"

uint8_t ip= 0;

//  void setup()
//
//
void setup()
{ 
  tmr1.sb    = _BV(EN);
  tmr1.preset = 20;
}

void loop()
{
   tonTimer( &tmr1);
}

Please read the library tutorial - http://www.arduino.cc/en/Hacking/LibraryTutorial - as a library is often the best place to store reusable code.

robtillaart:
Please read the library tutorial - http://www.arduino.cc/en/Hacking/LibraryTutorial - as a library is often the best place to store reusable code.

Exactly what I would have put. Libraries are convenient to use, as other people can easily use them (should you choose to release them), and the functions that you can add are used in the same way as the standard ones are, so you do not need to add in lots of extra (non-hidden) code into the main sketch.

Onions.

I have found myself with the same requirement. I have wound up with 1,000s of lines of code crammed into a single sketch merely because it is inconvenient in Arduino to build code that is divided into logical modules. Libraries appear to have to be C++ classes, and that is not what I want. The modules I want to make are bog-standard C code and I know well how to build and link these in a conventional C environment. I know that Arduino libraries are preferable in some cases, but that is not what I am trying to do.

Is there any provision in Arduino for conventional C modules, or is it necessary to go directly to the AVR toolchain to make this work?

frpr666:
What's wrong ?

Rename FC060.c to FC060.cpp

Is there any provision in Arduino for conventional C modules, or is it necessary to go directly to the AVR toolchain to make this work?

That is what the separate code tabs are for. There is a arrow button pointing right, near top of the IDE edit window that allows you to write or copy separate functions that will then be compiled along with your main sketch. You do have to use the proper file extension name.

Lefty

Vanyamba:
Rename FC060.c to FC060.cpp

I feel so dumb. Thanks, and thanks Lefty.

I do a lot of debugging in a real C development environment, and establishing a system of CPP macros and such that allow the same file to be built in Arduino and UNIX has been a challenge. This is an excellent step. Thank you.

In order to support overloading, C++ performs an operation called name mangling. The result is that a function's real name is defined by the user-supplied function name, and the types of all the arguments.

C doesn't support overloading, so it doesn't play well when the C++ compiler has mangled the names.

The C++ compiler CAN compile C code without name mangling, so that the c extension can be used, if some compiler directives are added to the header file.

#ifdef __cplusplus
extern "C" {
#endif

// The C function declarations go here

#ifdef __cplusplus
}
#endif

The __cplusplus symbol is defined by the C++ preprocessor, but not by the C preprocessor, so, when the C++ compiler runs, it sees the extern "C" wrapper, but the C compiler does not.

The extern "C" directive tells the C++ compiler not to perform name mangling, since the functions in the block are to be called by C code, too.

Since the code in the .c file is compiled by the C compiler, which does not perform name mangling, the un-mangled names are what the C++ compiler needs to add to the symbol table for the linker to use.

Without the extern "C" directive, the mangled names are added to the symbol table by the C++ compiler, but not by the C compiler, so the linker can't find the function since the name in the symbol table (of the compiled function) doesn't match the name of the called function.

Hello,
the problem is solved. :slight_smile: It works after renaming *.c to *.cpp and after few mistakes correction. Now I know how to split large project.
@Vanyamba Thank you, that was what helped.
Edit1: @PaulS Thank you for the explanation, I try also your way, it works too. I will use it in the future, it seems to be clearer.
Here is the final working project

/*
file:      FC060.c
project:   
device:    
date:      
desc.:    software timer 
*/

#include "FC060.h"
#include <avr/io.h>


T_TMR   tmr1;
T_TMR   tmr2;

void tonTimer(T_TMR *t)
{
  unsigned char sb   ; 
  unsigned char value; 
  unsigned char preset;
  
  sb     =  t->sb;
  value  =  t->value;
  preset =  t->preset;
  
  if (sb==_BV(EN)){
     sb = (_BV(EN) | _BV(RUN));
     value  = preset; 
  }
  if ((TMR_BIT)&&
      (bit_is_set(sb,RUN))){
       value--;
       if (value==0){
         sb = _BV(CPL);
       }
  }

  t->sb = sb;
  t->value = value;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
file:      FC060.h
project:   
device:    
date:      
desc.:     header file for software timer
*/


#ifndef _FC060_H
#define _FC060_H

#ifdef __cplusplus
extern "C" {
#endif

#include <avr/io.h>

#define EN                     (0)
#define RUN                    (1)
#define CPL                    (2)

#define TMR_BIT               (bit_is_set(ip,7))
// uint8_t ip is defined in main skatch


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


// software timer data
typedef struct {
  unsigned char sb   ;  // holds flags
  unsigned char value;  // is value
  unsigned char preset;  // init value
} T_TMR;


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

extern T_TMR   tmr1;
extern T_TMR   tmr2;
extern uint8_t ip  ;

extern void tonTimer(T_TMR *t);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#ifdef __cplusplus
}
#endif

#endif _FC060_H
// file main.pde
//

#include <avr/io.h>
#include "FC060.h"

#define Q13 13          // here comes the output
#define Q13_1   (PORTB = 0b00100000)
#define Q13_0   (PORTB = 0b00000000)

#define INIT_TIMER_COUNT (6)
#define RESET_TIMER2 (TCNT2 = INIT_TIMER_COUNT)

volatile uint8_t int_counter = 0;
uint8_t ip, hf = 0;
uint8_t tmp;

//  void setup()
//
//
void setup()
{ 
  setupTMR2();
  Serial.begin(19200);      // open the serial port at 19200 bps:    
  Serial.flush();
  
  tmr1.sb    = _BV(EN);
  tmr1.value  = 0;
  tmr1.preset = 4;
  
  tmr2.sb    =  0;
  tmr2.value  = 0;
  tmr2.preset = 2;
}

void loop()
{
    // check interrupt activity
    tmp = int_counter;
    ip =  tmp & (~hf);
    hf =  tmp; 
   
   tonTimer( &tmr1);
   tonTimer( &tmr2);
   
       if (bit_is_set(tmr1.sb, RUN)){
             Q13_1; 
       }
       else {
             Q13_0;
       }

   if (bit_is_set(tmr1.sb, CPL)){
      tmr1.sb = 0;
      tmr2.sb = _BV(EN);
   }  

   if (bit_is_set(tmr2.sb, CPL)){
      tmr2.sb = 0;
      tmr1.sb = _BV(EN);
   }  

   

   if (TMR_BIT){
   Serial.print(0x00, BYTE);
   Serial.print(0x00, BYTE);
   Serial.print(tmr1.sb, BYTE); 
   Serial.print(tmr1.value, BYTE);
   Serial.print(tmr1.preset, BYTE);  
   Serial.print(tmr2.sb, BYTE); 
   Serial.print(tmr2.value, BYTE);
   Serial.print(tmr2.preset, BYTE);
   }
}

ISR(TIMER2_OVF_vect) {
  RESET_TIMER2;
  int_counter++;
};

void setupTMR2(){
  //   =   76543210
  DDRB = 0b00100000;
  
  // TIMER 2
  
  //Timer2 Settings: Timer Prescaler /64,
  TCCR2B |= ((1<<CS22) | (0<<CS21) | (0<<CS20));
  
  // Use normal mode
  TCCR2A |= (0<<WGM21) | (0<<WGM20);

  // Use internal clock - external clock not used in Arduino
  ASSR |= (0<<AS2);

  TIMSK2 |= (1<<TOIE2);  //Timer2 Overflow Interrupt Enable
  RESET_TIMER2;
  sei();
}