Newbie to C. Compiler question

Discussion in 'Programmer's Corner' started by ke5nnt, Jan 10, 2011.

  1. ke5nnt

    Thread Starter Active Member

    Mar 1, 2009
    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 <>?

    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!
  2. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    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. :)

    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: Jan 10, 2011
  3. ke5nnt

    Thread Starter Active Member

    Mar 1, 2009
    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 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:
    Code ( (Unknown Language)):
    2. __config 0b10000100000000
    3. #include "P16F628A.INC"
    5. main()
    6. {
    7.     TRISA = 0xFF00;
    8.     while(1)
    9.     {
    10.         PORTA = 0xFF;
    11.         PORTA = 0;
    12.     }
    13. }
    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:
    Code ( (Unknown Language)):
    1.  #include <>
    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:
    Code ( (Unknown Language)):
    1.  #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:
    Code ( (Unknown Language)):
    1. #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.
  4. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    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.
  5. ke5nnt

    Thread Starter Active Member

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

    Code ( (Unknown Language)):
    2. //
    3. //    First C Project
    4. //
    5. //
    6. __config 0b10000100000000
    7. #include <htc.h>
    8. //
    9. main()
    10. {
    11. }
    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:

    Code ( (Unknown Language)):
    2. main()
    3. {
    4.     PORTA = 0xFF;
    5.     PORTA = 0;
    6. }
  6. nerdegutta


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

    Code ( (Unknown Language)):
    2. /*
    4. Program:    
    5. Description:
    6. PIC:        
    7. IDE:        
    8. Compiler:    
    9. Date:        
    10. Author:        
    11. web:        
    12. */
    14. #include <htc.h>
    15. #define _XTAL_FREQ 4000000
    17. /* Configuration */
    19. __CONFIG    (WDTDIS &
    20.             PWRTEN &
    21.             MCLREN &
    22.             BOREN &
    23.             LVPDIS &
    24.             DATUNPROT &
    25.             UNPROTECT &
    26.             XT);
    28. /* Prototyping the functions */
    29. /*  For future references */
    31. /* Global variables */
    32. /*  For future references */
    34. /* Main program */
    35. void main()
    36. {
    37. TRISA = 0b11111111;    // Setting all bits on port a to input
    38. TRISB = 0b00000000;    // Setting all bits on port b to output
    40. PORTA = 0b00000000; // Setting all bits on port a to LOW
    41. PORTB = 0b00000000;    // Setting all bits on port b to LOW
    43. CMCON = 0x07;    // Disabling the analogue comparators
    45. while (1)
    46. {
    48.     PORTB = 0b00000001; // Setting bit 0 to HIGH
    49.     __delay_ms(1000);
    51.     PORTB = 0b00000000; // Setting all bits to LOW
    52.     __delay_ms(1000);
    54. } // end while
    56. }  //end main
    I use this as a skeleton, when I start new project/programs.
  7. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    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 ;))

    Code ( (Unknown Language)):
    3. #include <system.h>
    7. #pragma CLOCK_FREQ 4000000  
    9. void main()
    10. {
    12.     trisb=0;  // portb all output
    15.     adcon1=0; // Disable porta ADC
    16.     cmcon=7; // Disable porta Comparators
    20.     portb=0xAA; // 10101010
    22. while (1)
    23. {
    24.      delay_ms(100);
    25.      portb^=0xFF;  // Invert 8 bits  
    26. }
    27. }
  8. ke5nnt

    Thread Starter Active Member

    Mar 1, 2009
    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:
    Code ( (Unknown Language)):
    2. ;
    3. ;program description here
    4. ;************************************
    6.    LIST         P=PIC16F628A ;SPECIFY MCU USED
    8.    __CONFIG B'10000100000000' ;NO CODE PROTECT, NO LVP, NO BOR,                                                 ; MCLR TIED TO VDD, PWRT ENABLED,
    9.                               ;NO WDT, LP OSC (32KHZ)
    11. ;
    12.    ORG     01
    14. ;
    15.    ORG     04
    17. ;
    18.    COUNT1   EQU 22
    20. ;*************************************************
    In C, I would write
    //program description here
    #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.

  9. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    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: Jan 11, 2011
  10. ke5nnt

    Thread Starter Active Member

    Mar 1, 2009
    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.
  11. ke5nnt

    Thread Starter Active Member

    Mar 1, 2009
  12. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    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
  13. ke5nnt

    Thread Starter Active Member

    Mar 1, 2009
    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.

    Code ( (Unknown Language)):
    2. //
    3. //    First C Project
    4. //
    5. /****************************************************************/
    6. //
    7. #include <htc.h>
    9. main()
    10. {
    11. }
    On to more things to tackle.
  14. ke5nnt

    Thread Starter Active Member

    Mar 1, 2009
    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?

    Code ( (Unknown Language)):
    2. config word specifying internal oscillator
    3. #define _XTAL_FREQ 4000000
    5. void main()
    6. {
    7.    trisa=0;
    8.    cmcon=0x07; //comparators off
    9. } (does this brace go here or at the very end?)
    11. while(1)
    12. {
    13.    portb=0xFF;
    14.    delay_ms(500);
    15.    portb=0;
    16.    delay_ms(500);
    17. }
    18. } (maybe this one here from comment above?)
  15. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    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.
  16. ke5nnt

    Thread Starter Active Member

    Mar 1, 2009
    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: Jan 14, 2011
  17. AlexR

    Well-Known Member

    Jan 16, 2008
    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.
  18. MMcLaren

    Well-Known Member

    Feb 14, 2010

    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;

    Code ( (Unknown Language)):
    2.         radix   dec
    3. clock   equ     8               ; 4,8,12,16, or 20 (MHz)
    4. usecs   equ     clock/4         ; cycles/usec multiplier
    5. 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;

    Code ( (Unknown Language)):
    2.         radix   dec
    4.         DelayCy(1*msecs)        ; delay 1-msec
    5.         DelayCy(1000*usecs)     ; delay 1-msec
    6.         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);

    Code ( (Unknown Language)):
    1. ;
    2. ;  key press beep
    3. ;
    4. ;  DelayCy(1*msecs) produces         DelayCy(1*msecs-6) produces
    5. ;  497.018 Hz tone @  4 MHz clock    500 Hz tone @ any clock
    6. ;  498.504 Hz tone @  8 MHz clock
    7. ;  499.004 Hz tone @ 12 MHz clock
    8. ;  499.251 Hz tone @ 16 MHz clock
    9. ;  499.400 Hz tone @ 20 MHz clock
    10. ;
    11.         bsf     Beep,5          ; do 32 msec "new press" beep     |B0
    12. DoBeep  movf    PORTA,W         ; read port A                     |B0
    13.         xorlw   1<<Spkr         ; toggle speaker bit              |B0
    14.         movwf   PORTA           ; toggle speaker pin              |B0
    15.         DelayCy(1*msecs-6)      ; delay 1 msec minus 6 cycles     |B0
    16.         decfsz  Beep,F          ; done?  yes, skip, else          |B0
    17.         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

    Code ( (Unknown Language)):
    1. [COLOR=DarkGreen][COLOR=Black]delayHi equ     0x20            ; delay subsystem variable
    2. [/COLOR][/COLOR][COLOR=DarkGreen][COLOR=Black];******************************************************************
    3. ;  K8LH DelayCy() subsystem macro generates four instructions     *
    4. ;******************************************************************
    5.         radix   dec
    6. clock   equ     4               ; 4, 8, 12, 16, 20 (MHz), etc.
    7. usecs   equ     clock/4         ; cycles/microsecond multiplier
    8. msecs   equ     clock/4*1000    ; cycles/millisecond multiplier
    10. DelayCy macro   delay           ; 11..327690 cycle range
    11.         movlw   high((delay-11)/5)+1
    12.         movwf   delayhi
    13.         movlw   low ((delay-11)/5)
    14.         call    uDelay-((delay-11)%5)
    15.         endm
    16. ;******************************************************************
    17. ;  example code for simulation testing                            *
    18. ;******************************************************************
    19.         org     0x000
    20. SimTest
    21.         DelayCy(200*usecs)      ; <- put simulator PC here
    22.         goto    $               ; <- put simulator break point here
    23. ;******************************************************************
    24. ;  K8LH DelayCy() fixed delay subsystem uDelay subroutine         *
    25. ;******************************************************************
    26.         nop                     ; entry for (delay-11)%5 == 4     |B0
    27.         nop                     ; entry for (delay-11)%5 == 3     |B0
    28.         nop                     ; entry for (delay-11)%5 == 2     |B0
    29.         nop                     ; entry for (delay-11)%5 == 1     |B0
    30. uDelay  addlw   -1              ; subtract 5 cycle loop time      |B0
    31.         skpc                    ; borrow? no, skip, else          |B0
    32.         decfsz  delayhi,F       ; done?  yes, skip, else          |B0
    33.         goto    uDelay          ; do another loop                 |B0
    34.         return                  ;                                 |B0
    35. ;******************************************************************[/COLOR][/COLOR]

    Last edited: Jan 15, 2011
  19. ke5nnt

    Thread Starter Active Member

    Mar 1, 2009
    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!