Newbie to C. Compiler question

Thread Starter

ke5nnt

Joined Mar 1, 2009
384
I am very new to C programming and I'm working on running through a very simple program to get the general idea. I'm using MPLAB IDE with Hi-Tech C compiler for 12/14/16MCUs.

The book I'm using specifies 2 file types that I can't seem to find, which I'm supposed to add to the project window. The first is a .h file, which is a header file. I have all the .inc files, but I don't seem to have any .h files. Are they the same thing? Instead of using #include <16fxxx.h> can I use #include <16fxxx.inc>?

The second file type is some kind of "linker script" which is a .gld file. I don't have any .gld files either, and in fact, in the project window, I don't even have a folder to add a linker file to. It only has: Source files, Header files, Object files, library files, and other files.

Can someone shed some light on these? Thanks!
 

thatoneguy

Joined Feb 19, 2009
6,359
Those files are in the compiler's link path, or should be.

When you include using "arrows", such as #include <16F627.h>, that tells the compiler to search in it's Own directories for the header file. If you include using quotes, such as #include "16f627.h", that tells the compiler to look in your PROJECT Directory for the header file. Compilers come with a different header file for every supported uC, even different header files for 16F627.h and 16F627A.h, so be sure to grab the right one. Some compilers will automatically change the header to the correct target when you simply #include <system.h>. At compile time, system.h includes the correct header for your target uC.

The linker scripts should be generated by the project wizard.

Try MikroC or BoostC out, if you get into programming a lot, they are far more affordable than Hi-Tech, and offer more lenient demo versions. There is a tad more "user frienlyness" to them, especially with lots and lots of example projects.

You can install all the compilers you'd like, and they will also work from within MPLAB. I do most stuff in BoostC, though I have MikroC installed for helping out in problems with it. I can't afford to get all the other versions. :)

--ETA:
Library files are essentially pre-compiled routines that you use often, and can include in your program. Most simpler apps you won't be creating them, mostly using the ones provided by the compiler, such as LCD routine, 7 segment routine, etc. Most often, you'll be using the actual code snippets from "known good" code from other projects.

Header files define the addresses of PORTB, TRISA, and other uC specific addresses, as well as the function prototypes for the compiler provided libraries, such as LCD routines.

object files are what are "linked together" between your source code and the compilers libraries to produce the final .hex file that is programmed to the uC. The reason they are separate is sometimes, functions are better written in assembly, so you have a .c source file and an .asm source file. At compile time, all the funcitons are "linked" together. If that makes sense.
 
Last edited:

Thread Starter

ke5nnt

Joined Mar 1, 2009
384
Ok, I will try out some other compilers.

As for .inc and .h files, are they essentially the same thing? Can I use either? I can locate the 16f628a.inc file but even doing a system search for a 16f628a.h file results in nothing. The funny thing is, when I right click on "header files" in my project window and select add file, then add the file P16F628A.INC, it adds it under "other files" instead of under the header files. I get build fail on everything I try.

Here are 3 scenarios I get when building:

With the P16F628A.INC file added under "other files" since I can't get it under header files:
Rich (BB code):
__config 0b10000100000000
#include "P16F628A.INC"

main()
{
    TRISA = 0xFF00;
    while(1)
    {
        PORTA = 0xFF;
        PORTA = 0;
    }
}
Error [141] C:\electronics\firstC.c; 6.23 can't open include file "P16F628A.INC": No such file or directory

********** Build failed! **********

Second try using arrows instead:
Rich (BB code):
 #include <p16f628a.inc>
Error [141] C:\electronics\firstC.c; 6.23 can't open include file "P16F628A.INC": No such file or directory

********** Build failed! **********

When trying to use a .h file with arrows:
Rich (BB code):
 #include <p16f628a.h>
Error [141] C:\electronics\firstC.c; 6.21 can't open include file "p16f628a.h": No such file or directory

********** Build failed! **********

Now I got some help from a helpful guy, and changed it to:
Rich (BB code):
#include <htc.h>
Now I get a fail message I don't understand at all really:
Error [317] C:\electronics\firstC.c; 5.10 "(" expected
Error [318] C:\electronics\firstC.c; 5.10 string expected
Error [194] C:\electronics\firstC.c; 5.10 ")" expected
Error [312] C:\electronics\firstC.c; 5.10 ";" expected

********** Build failed! **********

I'm lost, lol. Thanks for the help.
 

thatoneguy

Joined Feb 19, 2009
6,359
Now I got some help from a helpful guy, and changed it to:
Rich (BB code):
#include <htc.h>
Now I get a fail message I don't understand at all really:
Error [317] C:\electronics\firstC.c; 5.10 "(" expected
Error [318] C:\electronics\firstC.c; 5.10 string expected
Error [194] C:\electronics\firstC.c; 5.10 ")" expected
Error [312] C:\electronics\firstC.c; 5.10 ";" expected

********** Build failed! **********

I'm lost, lol. Thanks for the help.
Now you are on the right track. the htc.h automatically includes the correct target headers, so that's taken care of.

The problem now is only syntax errors. Missing paren somewhere, such as void main()

Each line needs a semicolon ; to terminate it.

Post your firstC.c file as text with code /code tags around it and the problem will be pretty straightforward to fix.

There should be a samples or examples folder for a working skeleton of a program that you can fill in the details on as well, but we can make either one work.

In short, #include <htc.h> is the correct line, but your code has a few typos is all.
 

Thread Starter

ke5nnt

Joined Mar 1, 2009
384
My code is extremely simple right now, not really even a program, but you gotta start somewhere.

Rich (BB code):
//
//    First C Project
//
//
__config 0b10000100000000
#include <htc.h>
//
main()
{
}
my original code included a simple function for turning on and off PORTA, but I get the same build error message either way. That code included:

Rich (BB code):
main()
{
    PORTA = 0xFF;
    PORTA = 0;
}
 

nerdegutta

Joined Dec 15, 2009
2,689
If I'm not mistaken. This code will blink a LED connected to BIT 0 in PORTB forever...

Rich (BB code):
/* 
 
Program:     
Description: 
PIC:         
IDE:         
Compiler:     
Date:         
Author:         
web:         
*/ 
 
#include <htc.h> 
#define _XTAL_FREQ 4000000 
 
/* Configuration */ 
 
__CONFIG    (WDTDIS & 
            PWRTEN & 
            MCLREN & 
            BOREN & 
            LVPDIS & 
            DATUNPROT & 
            UNPROTECT & 
            XT); 
 
/* Prototyping the functions */ 
/*  For future references */ 
 
/* Global variables */ 
/*  For future references */
 
/* Main program */ 
void main() 
{ 
TRISA = 0b11111111;    // Setting all bits on port a to input 
TRISB = 0b00000000;    // Setting all bits on port b to output 
 
PORTA = 0b00000000; // Setting all bits on port a to LOW 
PORTB = 0b00000000;    // Setting all bits on port b to LOW 
 
CMCON = 0x07;    // Disabling the analogue comparators 
 
while (1) 
{ 
 
    PORTB = 0b00000001; // Setting bit 0 to HIGH 
    __delay_ms(1000); 
 
    PORTB = 0b00000000; // Setting all bits to LOW
    __delay_ms(1000); 
 
} // end while 
 
}  //end main
I use this as a skeleton, when I start new project/programs.
 

thatoneguy

Joined Feb 19, 2009
6,359
That should compile with basically nothing for output, but shouldn't give errors.

You need to change Port A to outputs, changing the Tris A register to 0.

There are also analog functions on PortA to disable, cmcon and adc, rather than working around that, use PortB instead.

Make sure PORTA is defined, it may be porta or PortA, C is case sensitive.

Below is a very simple program (written in BoostC) that does work to give you an idea, it simply toggles LEDs on Port B.

You will need to find what case was used to define your ports and change them accordingly, the header file would be htc.h instead of system.h, but this gives you an idea of what a working program looks like.

The PortA stuff is in there from my "standard skeleton" file, and can be ignored. Also, the #pragma may be __config in your compiler. (These are reasons I try to stick with just one or two compilers ;))

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

#pragma DATA _CONFIG & _PWRTE_ON & _LVP_OFF & _BODEN_ON & _PWRTE_ON & _WDT_OFF

#pragma CLOCK_FREQ 4000000   

void main()
{

    trisb=0;  // portb all output

    
    adcon1=0; // Disable porta ADC
    cmcon=7; // Disable porta Comparators
    
    
    
    portb=0xAA; // 10101010

while (1)
{
     delay_ms(100);
     portb^=0xFF;  // Invert 8 bits  
}
}
 

Thread Starter

ke5nnt

Joined Mar 1, 2009
384
Ok, I'm confusing myself going over my assembly codes and trying to figure out how I'd write them in C.

I'm having trouble figuring out how to set up the top of the program. I'm going to use examples from my Assembly program, which uses a 16F628A MCU, DATASHEET HERE.

In assembly, to setup the top of the program, I write:
Rich (BB code):
;
;program description here
;************************************
;CONFIGURATION
   LIST         P=PIC16F628A ;SPECIFY MCU USED
   #INCLUDE  P16F628A.INC ;HEADER FILE SPECIFIED
   __CONFIG B'10000100000000' ;NO CODE PROTECT, NO LVP, NO BOR,                                                 ; MCLR TIED TO VDD, PWRT ENABLED,
                              ;NO WDT, LP OSC (32KHZ)
   ERRORLEVEL -302 ; DESTROY ANNOYING BUILD ERROR
;
   ORG     01
   GOTO   INIT    ;GOTO START OF PROGRAM
;
   ORG     04
   GOTO   INIT    ;GOTO START OF PROGRAM (NO SEPARATE INTERRUPT CODE
;
   COUNT1   EQU 22
   COUNT2   EQU 23   ;SPECIFY GLOBAL VARIABLE ADDRESSES
;*************************************************
In C, I would write
//
//program description here
//
//CONFIGURATION
#include <htc.h>
__CONFIG 0b10000100000000 (although for the C program, I want to use the internal oscillator of the 628A @ 4MHz), not sure how to set that up in C at all. Then I'd need to specify the clock frequency at 4000000, though I'm not sure of the exact way to do that. Maybe using:
#use delay(clock=4000000) Is there a space between "delay" and (clock...)?
Then including the 2 global variables for the counters, I believe I do that using:
char c1
char c2 //2 8-bit integers for delay counters. Is "char" a signed or unsigned 8-bit integer?

In the "INIT" portion of my assembly code that is the start of the program, where I initialize ports and check a couple of pins for a high state I switch to bank1 of the register, specify all 0's for TRISB to set all PORTB to output, specify RA0-RA1 as input, all other A pins output, turn off comparators, then test RA0 and RA1 for a high state which if detected takes the program to a separate part of the program. If not, the program continues into normal operation. I wont post that part of the code here since I'm focused really on just the configuration part of C right now, but I'm sure I'll be asking questions on it soon enough.

I'm really sorry to sound like an idiot here, but you know how it goes when you're learning new skills. Without clarification, you're liable to really mess something up, so I really appreciate the feedback.

Best,
Ryan
 

thatoneguy

Joined Feb 19, 2009
6,359
The initialization is done with __config or #pragma statements.

Program execution starts in two spots:

main() -> on boot, this is the entry point, initialization is done here, then calls to other functions, or a loop at the end of the initializations for the actual "meat" of the program.

interrupt() -> Code that is executed when an interrupt occurs.

To define variables, in main(), simply add:

int integer1; This will set side memory for an integer, you can also define bits for flags, or char/single bytes. Check your compiler help section on variables for more info.

Then you can use integer1++; to increment it, you can multiply, divide, etc. You mostly don't need to worry about HOW those instructions will actually be compiled, they typically just will. If your code creates a glitch (very rare), you can look at the generated .asm listing to find the problem.

Not many variables are used, due to the tiny amount of RAM available. The compiler will give a warning if you are hitting the uC's or free compiler's limitations, so it won't be a surprise. I use several flags, usually 8 bytes of memory worth, and a couple loop counters for software timers. Otherwise, most manipulation is done with the PIC's registers, as shown in samples above.

You should look for a Samples or Examples directory under the Hi-Tech installation directory, it should have plenty of code you can peruse to get a very good idea of how things work.

--ETA: What you are running into is a reason I think learning C before assembly is the best route, because assembly tends to make better sense when you use "sort of normal language" statements to make a program, then look at the generated assembly. Learning assembly first with PICs makes most of your work focused on memory management, bank use, register save/restore, etc. All of that is done by the compiler with C, and if you still want to use assembly, you can link an .asm function as a separate file, or use inline assembly within the C Source code (see compiler help for details on yours).

Finally, you ARE NOT sounding like an idiot or asking stupid questions! You are doing extremely well compared to others just starting out that I've worked with! It IS confusing, especially if you've worked with assembly on a PIC and have no knowledge of the C language. You are basically trying to get away from habits of micro-managing everything that you learned with assembly, and at the same time, learn the syntax and abilities of an entirely new programming language, C. This is NOT a "Simple Task" for ANYBODY. Keep the faith!
 
Last edited:

Thread Starter

ke5nnt

Joined Mar 1, 2009
384
Well thanks for the help and encouragement. I've been mushing up brain matter too long at this point so it's time for some rest and back at it again in the morning. I'll go over some of the example code in the compiler and follow some other suggestions as well. And I'll check the compiler help section to see what else I can learn from it.

Thanks again for your patience and input.
 

Thread Starter

ke5nnt

Joined Mar 1, 2009
384
ke5nnt said:
one of the things that i'm struggling with right now is how to set up the top of the program in regards to the internal oscillator. In assembly programs, if i want to use the internal 4mhz oscillator, i would have to set the configuration word to use intosc with the osc pins set for digital i/o's. Additionally, i specify the prescaler to the wdt at 1:128 through the option register. I have no idea if i need to do that in c, and if i do, how to do it.

After i write:
//
//program
//
__config 0b...

I know i need to specify something like:
#define _xtal_freq 4000000

though the examples for hi-c use:
#ifndef _xtal_freq
#define _xtal_freq 4000000
#endif (not sure what that's all about)

to make a long question short, how do you set up the program to use the internal clock set for a specific frequency?

Thanks
1234567890
thatoneguy said:
well, you do need to pay attention to some things, but what i meant is you don't need to pay attention to bank switching to change trsia, or paging for memory, etc.

What you've mentioned should be in an example file, the #pragma in sourceboost has the definitions such as wdt_off and hs_osc defined as bit patterns, so you simply and them all together for the configuration word. The same is true for hi-tech, if you look at the program above the one i posted, they are slight variations on the same. I'd suggest grabbing his "template", as it is basically the hi-tech c version of my template (which is for boostc).
There are a lot of little things to pick up and learn, and a good deal of things about assembly that need to be forgotten (just for a bit, until you get the gist of c down). Then you can combine the two and come up with amazing stuff!

The help/hi-tech user's guide and samples should be the best resource you can find. Hit up google for them. Though with the over $500 cost of hi-tech, i went with boostc, which was free or $5, or $150 for the full version, they have in between pricing, but the free version will cover most of what you want. The other with excellent documentation and a sub $500 pricetag is mikro c from mikroelectronica. Those are very common compilers for the pic12-pic18 series, so source code can be "savlaged/stripped" from other projects you may find on the net.
 

thatoneguy

Joined Feb 19, 2009
6,359
Nerguetta's post above is a nice "Skeleton Program" for Hi-Tech C that you should be able to compile with no errors, and should result in a blinking LED on portb.

For the clock, the method is:
#define _XTAL_FREQ 4000000
 

Thread Starter

ke5nnt

Joined Mar 1, 2009
384
Ok, I am making progress, I just wrote this looong response about how I kept getting build error messages and failures whenever I'd add a __config line to my program. Then in the last line of that post, which I just wasted about 20 minutes of my life on, lol... I decide, what happens if I capitalize CONFIG? How about fixing the problem, that's what. lol.

Rich (BB code):
//
//    First C Project
//
/****************************************************************/
//
#include <htc.h>
__CONFIG(INTIO & WDTDIS & PWRTEN & MCLRDIS & BORDIS & LVPDIS & UNPROTECT & UNPROTECT);
main()
{
}
Compiler said:
********** Build successful! **********
On to more things to tackle.
 

Thread Starter

ke5nnt

Joined Mar 1, 2009
384
Ok new question...

If you define in the configuration word that you're using the internal oscillator, or any clock source for that matter I guess, and you specify in your code:

_XTAL_FREQ 4000000 or some other value that matches your clock speed, will the C compiler automatically configure TMRx / prescaler bits for you or what (maybe they don't need anything? I've seen code in an example from thatoneguy in this thread for a time delay, which is delay_ms(100); I've also seen the example for clock speed like #use delay (clock=4000000). Not sure which is correct for my compiler just yet.

Is that all that's needed? Like cycling on and off a port that controls an LED for instance so that you can see it flashing would be written like:
(code snippet only). Is this correct to generate a proper time delay (as in will this be enough to actually get a 1/2 second delay between the port changing from high to low?

Rich (BB code):
config word specifying internal oscillator
#define _XTAL_FREQ 4000000

void main()
{
   trisa=0;
   cmcon=0x07; //comparators off
} (does this brace go here or at the very end?)

while(1)
{
   portb=0xFF;
   delay_ms(500);
   portb=0;
   delay_ms(500);
}
} (maybe this one here from comment above?)
 

thatoneguy

Joined Feb 19, 2009
6,359
The delays are determined by what you have the frequency defined as in the code file XTAL, or whatever the name is (changes with compilers), which is a reason you need to make sure it is correct.
 

Thread Starter

ke5nnt

Joined Mar 1, 2009
384
Ok, from the compiler's info:

Notice in the main function there is a call to what looks like a function called _delay (note the leading underscore character), however you will not find the definition of this function in the program we have just compiled. This is a special identifier for an in-line function that is expanded by the compiler. The argument to this function is the number of instruction cycles that will be executed, thus forming a delay. So in the code we have just executed, a delay of 10000 instruction cycles is placed in the counting loop.

2 questions come from this: First, how do you determine how many instruction cycles it takes for a given period of time? I think there are 4 instructions per clock cycle, so Fosc/4 = instruction time? Then delay desired (d) divided by instruction time (i) for instruction cycles needed =d/i?

Second question, is there a way to change having to use the number of instruction cycles to being able to use the delay_ms(xxxx) type statement? EDIT: Found the answer to my own question. Seems one underscore: _delay(10000) for delay in instruction cycles, two underscores for time delays __delay_ms(500). Heh...the quirks of compilers. I'd still like to know how to do the math behind instruction/clock cycles though, if anyone can provide that.
 
Last edited:

AlexR

Joined Jan 16, 2008
732
Actually it's 4 clock (Fosc) cycles per 1 machine cycle so 1 machine cycle takes 4/Fosc seconds. Using a 4MHz clock 1 machine cycle takes 4/4e6 seconds or 1 usec.
Most instructions take 1 machine cycle but instructions involving jumps or branches take 2 machine cycle. The instruction set description in data sheet will tell you how many machine cycles each instruction takes.
Some instructions e.g DECFSZ show 1(2) machine cycles. These usually involve a test and skip if test=true in which case if the test fails the instruction take 1 cycle and if the test is true and the skip is executed then the instruction takes 2 machine cycles.
 

MMcLaren

Joined Feb 14, 2010
861
First question, how do you determine how many instruction cycles it takes for a given period of time? I think there are 4 instructions per clock cycle, so Fosc/4 = instruction time? Then delay desired (d) divided by instruction time (i) for instruction cycles needed =d/i?

Tcy (instruction cycle time) = Tosc (oscillator time) * 4, but here's a shortcut I use to setup a general purpose assembly language fixed delay subsystem;

Rich (BB code):
        radix   dec
clock   equ     8               ; 4,8,12,16, or 20 (MHz)
usecs   equ     clock/4         ; cycles/usec multiplier
msecs   equ     clock/4*1000    ; cycles/msec multiplier
After you set the 'clock' equate you can specify fixed delays in cycles, usecs, or msecs. Using the usecs and msecs multipliers in the delay operand obviously ties you to the clock speed;

Rich (BB code):
        radix   dec

        DelayCy(1*msecs)        ; delay 1-msec
        DelayCy(1000*usecs)     ; delay 1-msec
        DelayCy(1*msecs-1)      ; delay 1-msec minus 1 cycle
Sometimes you need "cycle accurate" delays. That is, the ability to subtract 'n' number of cycles from the specified delay to account for the number of cycles in a loop. For example, if we used a 1-msec delay to toggle a piezo speaker in the following beep routine, without accounting for the number of cycles in the loop, we'd get slightly different tone frequencies with different clock speeds. If we account for the number of cycles in the loop we can produce a precise 500-Hz tone at almost any clock (4, 8, 12, 16, or 20-MHz);

Rich (BB code):
;
;  key press beep
;
;  DelayCy(1*msecs) produces         DelayCy(1*msecs-6) produces
;  497.018 Hz tone @  4 MHz clock    500 Hz tone @ any clock
;  498.504 Hz tone @  8 MHz clock
;  499.004 Hz tone @ 12 MHz clock
;  499.251 Hz tone @ 16 MHz clock
;  499.400 Hz tone @ 20 MHz clock
;
        bsf     Beep,5          ; do 32 msec "new press" beep     |B0
DoBeep  movf    PORTA,W         ; read port A                     |B0
        xorlw   1<<Spkr         ; toggle speaker bit              |B0
        movwf   PORTA           ; toggle speaker pin              |B0
        DelayCy(1*msecs-6)      ; delay 1 msec minus 6 cycles     |B0
        decfsz  Beep,F          ; done?  yes, skip, else          |B0
        goto    DoBeep          ; loop (toggle Spkr pin again)    |B0
If you're interested, here's an example fixed delay subsystem that you can throw into a project, assemble, and test using the MPLAB Simulator and Stopwatch. Remember to set the Stopwatch clock to match your 'clock' equate.

Please note that this is a "fixed" delay subsystem. It uses constants in the delay operand, not variables. A "variable" delay subsystem, like those used in HLL (high level language) compilers, are a bit different and have a bit more overhead (a higher minimum delay value).

Cheerful regards, Mike McLaren, K8LH

Rich (BB code):
delayHi equ     0x20            ; delay subsystem variable
;******************************************************************
;  K8LH DelayCy() subsystem macro generates four instructions     *
;******************************************************************
        radix   dec
clock   equ     4               ; 4, 8, 12, 16, 20 (MHz), etc.
usecs   equ     clock/4         ; cycles/microsecond multiplier
msecs   equ     clock/4*1000    ; cycles/millisecond multiplier

DelayCy macro   delay           ; 11..327690 cycle range
        movlw   high((delay-11)/5)+1
        movwf   delayhi
        movlw   low ((delay-11)/5)
        call    uDelay-((delay-11)%5)
        endm
;******************************************************************
;  example code for simulation testing                            *
;******************************************************************
        org     0x000
SimTest
        DelayCy(200*usecs)      ; <- put simulator PC here
        goto    $               ; <- put simulator break point here
;******************************************************************
;  K8LH DelayCy() fixed delay subsystem uDelay subroutine         *
;******************************************************************
        nop                     ; entry for (delay-11)%5 == 4     |B0
        nop                     ; entry for (delay-11)%5 == 3     |B0
        nop                     ; entry for (delay-11)%5 == 2     |B0
        nop                     ; entry for (delay-11)%5 == 1     |B0
uDelay  addlw   -1              ; subtract 5 cycle loop time      |B0
        skpc                    ; borrow? no, skip, else          |B0
        decfsz  delayhi,F       ; done?  yes, skip, else          |B0
        goto    uDelay          ; do another loop                 |B0
        return                  ;                                 |B0
;******************************************************************

 
Last edited:

Thread Starter

ke5nnt

Joined Mar 1, 2009
384
Thanks Mike, and everyone else for the very helpful input. It never ceases to amaze me what you can accomplish when you have a group of people like we've got here at AAC, and of course, the desire to learn something and not just have it done for you.

Thanks again, I think I'm making great progress. I only started learning C just 1 week ago and I feel like I've really gotten somewhere, but couldn't have done it without everyone's help!
 
Top