Moving from assembly to C PIC microprocessors

Thread Starter

portreathbeach

Joined Mar 7, 2010
143
I just noticed that in the file Test.c and i2c_C.c I #include "i2c_C.h", but nowhere does i2c_C.h reference i2c_C.c. So surely when I call a function from Test.c that is in i2c_C.c, it looks at the i2c_C.h file to see the declaration of the function, but how does it know when the function actually is?


Edit:

OK, I just got it working, is this how it should be done?

- Original i2c_C.h and i2c_C.c files compiled and working
- Now to be able to use the functions I created in i2c_C.c, I copied the code into i2c_c.h
- Now Test.h and Test.c have been created and #include i2c_C.h
- I can now call the function 'wait()' from Test.c

Is that correct. I need to create some working code, and then when happy with it, put it all in the header file to be able to use it from another project?

Sorry to sound so stupid, but people can only learn from wiser and more experienced people like yourselves.




Also, what is all this about in the h files that XC8 creates:

Rich (BB code):
#ifndef TEST_H
#define    TEST_H


...........I am currently putting all my defines, pragmas etc. here, is this the correct place to put them? It seem to work if I put them here, or right at the top.



#ifdef    __cplusplus
extern "C" {
#endif




#ifdef    __cplusplus
}
#endif

#endif    /* TEST_H */
 
Last edited:

sirch2

Joined Jan 21, 2013
1,075
In C your code is compiled and linked as two separate operations. The header file - .h - is just a promise that you or some library will implement the functions declared in the .h with the arguments specified in the header. The compiler accepts the header and compiles the individual C files. The linker then goes and finds the implementations of what ever is in the headers by searching in the build path.

Upshot, your .c file does not need to reference it's own ,h
 

Thread Starter

portreathbeach

Joined Mar 7, 2010
143
Hi sirch2, how does the compiler know what .c file is associated with the .h file? Or does it assume that the files have the same name means they are to be linked?

If I do not put #include "i2c_C.h" in my .c file, it fails to compile as it doesn't recognise any of the variables I have defined in the .h file.


Another thing I just did was to put everything in the C file, all the code, the config #pragmas, the #includes and the #defines etc. and saved it. If I rename this to i2c_C.h and put it into my test project folder, when I #include "i2c_C.h" everything works, all the function calls I put in Test.c that are in i2c_C.h all work.
 

MrChips

Joined Oct 2, 2009
35,159
You are discovering things by trial and error.

There is nothing special about a .c file vs a .h file. They are both generic text files.

The compiler makes no association between .c files and .h files of the same name.

When you #include a file in another file, all you are doing is appending the text of the included file, i.e. you are making one bigger file during the compilation process. Thus you can call it a .x file and the compiler doesn't care. We use .h to remind us it is a "header" file. If we want we can call it .i for "info" file.

The use of the .h file is simply an organizational technique for reusing the same code, usually kept in a library. Hence .h files are used to contain things like:

constants
data types
enumerations
structures
function prototypes

The purpose of the function prototype is to tell the compiler that the function exists with the defined parameters. It is up to the linker to find where the function resides.

If you happen to update your library code the .h file doesn't have to change. All you would need to do is update the library and relink your object code.
 

ErnieM

Joined Apr 24, 2011
8,415
If I followed this correctly you have a dot C file that seems to be missing from your project.

So put it there, literally. In the project pane (top left box on my PC) look for the source folder: it should already contain the dot C file with your main() function. Add any new dot C files right there.

Then they will be sent to the compiler and linked during the build process.

You don't need to do that with dot H files as you explicitly #include them individually. You may want to do that for convince when writing the program: you can click and open a dot H to edit it.
 

Thread Starter

portreathbeach

Joined Mar 7, 2010
143
Oh my God, is that all I was missing ErnieM.

I went to my side bar, copied the i2c_c.c file and put it into the source section for the project Test.

When I compile Test it gave an error referring to 'void main' in the i2c_C file, which is obviously correct as Test.c contains main() and you can't have two mains. So I remove the main loop from the i2c_C.c file which is now in the Test project and it all compiles correctly.

Thanks everyone. I'll have a bit more of a play with it when I get back from work later.
 
Last edited:

Thread Starter

portreathbeach

Joined Mar 7, 2010
143
Having another play with C and getting very confused with header files.....

I have working code in i2c_C.c, in this file I #include "i2c_C.h". The h file contains various defines for the SLA and SDA tris registers etc.

I start another project called i2c_LCD. I create a source file called i2c_LCD.c which I have a line of code saying #include "i2c_LCD.h". I also create a file called i2c_LCD.h which I #include "i2c_C.h". I also put my various defines etc. into this h file which are used in i2c_LCD.

All the 4 files are in the same directory.

The problem I seem to have is, in the i2c_C.h file, the SLA and SDA tris registers are set to certain pins and also the _XTAL_FREQ is set. These need to be set here so that the code in i2c_C.c can work. If in my new project 'i2c_LCD' I want to use the code contained in i2c_C.c I can call the function, but I may not want the SLA and SDA tris registers set to what they are in the i2c_C.h file. Also, I may want to use a different frequency clock in my new project, but the _XTAL_FREQ is already defined in the i2c_C.h file.

Is it a case of making code that works and then when copying it into a new project going back and editing bits of it to work with a new project, or can I go to i2c_C.h file and #include "i2c_LCD.h" and then remove all the defines and put them into the i2c_LCD.h file.

Hopefully someone can make sense of this, as I can get it to work if I keep changing stuff around, but I want to start off doing stuff the correct way and keep everything I do organised.

Thanks in advance and I really appreciate your help :)
 

sirch2

Joined Jan 21, 2013
1,075
You can put the defines in the .c file rather than the header, they only really need to go in the header if you want to share them across .c files.

Alternatively you can use #ifndef to check if the defines already exist. That way if you include your i2c_LCD header first it defines your SLC/SDA pins and the i2c_C.c checks and does not define them if they are already defined. Hope that makes sense, it's easier to do than explain - Google #ifndef
 

Thread Starter

portreathbeach

Joined Mar 7, 2010
143
Thanks for the quick reply.

Obviously the _XTAL_FREQ needs to be shared with all the c files as they will probably all have a __delay_us or __delay_ms command in them. So I should define this in the i2c_LCD.h files and make sure that the i2c_C.h files #includes the i2c_LCD.h file?

Also, do I have to declare all of my functions in the header files, they seem to work either way?
 

Thread Starter

portreathbeach

Joined Mar 7, 2010
143
Had a bit more of a play around with this and it at last compiles and seems to be OK. Please can someone take a quick look at the 4 code files I have posted to see if this is the correct way to do things:

i2c_C.h
Rich (BB code):
#include <xc.h>


void short_delay(void);                         // small wait
void i2c_start(void);                // generates an I2C start condition
void i2c_stop(void);                // generates an I2C stop condition
void bit_out(unsigned char data);               // outputs a bit to I2C bus
void bit_in(unsigned char *data);               // inputs a bit from the I2C bus
unsigned char i2c_wr(unsigned char i2c_data);   // writes a byte to the I2C bus
unsigned char i2c_rd(unsigned char ack);    // reads a byte from the I2C bus
void ack_poll(unsigned char cont);



#ifndef I2C_C_H
#define    I2C_C_H


#ifdef    __cplusplus
extern "C" {
#endif


#ifdef    __cplusplus
}
#endif

#endif    /* I2C_C_H */

i2c_C.c
Rich (BB code):
#include <stdio.h>
#include <stdlib.h>
#include <pic16f690.h>
#include <xc.h>
#include "i2c_LCD.h"
#include "i2c_C.h"


//....................................................................
// This function waits for 10uS
//....................................................................
void short_delay()
{
    __delay_us(10);
}


//....................................................................
// This function generates an I2C Start Condition
//....................................................................
void i2c_start(void)
{
    SDA_TRIS = 1;                   // ensure SDA & SCL are high
    SCL = 1;
    SDA_TRIS = 0;                   // SDA = output
    SDA = 0;                        // pull SDA low
    short_delay();
    SCL = 0;                        // pull SCL low
}


........there's actually a lot more code here......

i2c_LCD.h
Rich (BB code):
#include "i2c_C.h"


// CONFIG
#pragma config FOSC = INTRCIO   // Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA4/OSC2/CLKOUT pin, I/O function on RA5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled and can be enabled by SWDTEN bit of the WDTCON register)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select bit (MCLR pin function is digital input, MCLR internally tied to VDD)
#pragma config CP = OFF         // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = OFF      // Brown-out Reset Selection bits (BOR disabled)
#pragma config IESO = ON        // Internal External Switchover bit (Internal External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is enabled)


#define _XTAL_FREQ 4000000

#define SDA_TRIS  TRISAbits.TRISA5
#define SCL_TRIS  TRISAbits.TRISA4
#define SDA       PORTAbits.RA5
#define SCL       PORTAbits.RA4

#define ACK       0x00
#define NACK      0x80

#define DS3231Add 0b01101000    // the address of the DS3231 RTC module


// testing and setting individual bits in a
#define testbit(var, bit) ((var) & (1 <<(bit)))
#define setbit(var, bit) ((var) |= (1 << (bit)))
#define clrbit(var, bit) ((var) &= ~(1 << (bit)))



#ifndef I2C_LCD_H
#define    I2C_LCD_H

#ifdef    __cplusplus
extern "C" {
#endif




#ifdef    __cplusplus
}
#endif

#endif    /* I2C_LCD_H */

And finally the main LCD code i2c_LCD.c (obviously no where near finished yet)
Rich (BB code):
#include <stdio.h>
#include <stdlib.h>
#include <pic16f690.h>
#include <xc.h>
#include "i2c_LCD.h"
#include "i2c_C.h"

//.............................................................................
//  Writes to the LCD
//.............................................................................
void lcd_wr(unsigned char data)
{
    i2c_start();

 
}


//.............................................................................
//  Initiates LCD in
//.............................................................................
void lcd_init()
{
//    __delay_ms(150);
                                                // we need to send 0x3 3 times and then 0x2 for 4 bit mode
                                                // we, will send 0x33 and then 0x32
    lcd_wr(0x33);
    __delay_ms(10);
    lcd_wr(0x32);
    __delay_ms(10);
        lcd_wr(0x28);                           // 2 lines 5x7 characters
    __delay_ms(10);
        lcd_wr(0x01);                           // clear and home display
    __delay_ms(10);
        lcd_wr(0x06);                           // eantry mode, increment cursor position after character entered
    __delay_ms(10);
        lcd_wr(0x0C);                           // turn on display
    __delay_ms(10);
}


int main(void) {

    lcd_init();

    while (1){


    }




    return 0;
}

Does this look like I'm getting the idea of what I'm meant to be doing?
 

Thread Starter

portreathbeach

Joined Mar 7, 2010
143
Thanks ErnieM. I'll do that.

Is the layout and structure of the 4 posted files ok. I've only just started programming in C and don't want to get into any bad habits.
 

Thread Starter

portreathbeach

Joined Mar 7, 2010
143
Oh. Ok. Well, it seems to be working anyway. I'll just keep tapping away at it. I'm sure I'll get the hang if it in the end.


So this is what I have learnt so far (so far).....

- Every C source code has a header file associated with it which hold the defines and function declarations
- Every C source file must #include it's header file
- Only one C source file can have a 'main' in it
- Every header file must #include any other header files if the C source file associated with it uses functions from another source file
- You never #include a .c file, only .h files

Is this right?
 
Last edited:
Top