Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Moving on to C

Status
Not open for further replies.

Overclocked

Member
Hi Everyone!

Ive been using Some form of PIC basic for years (Swordfish SE, Great Cow Basic). One of the reasons was because it was easy to use, easy to read and most of the time there are plentiful libraries (like ADC,LCD,etc). However, with my latest project, even though its simple, I realized I could use it as a great learning experience and teach myself C.

Hopefully Im on the right path. I chose MPLAB X and XC8 complier to go along with it. I went with XC8 because..well its free! (Im open to others-I know of another by mikroC). However, I cant figure out how to tell the compiler the Frequency I want, Or how to set OSCCON.

PIC used: PIC12F1840. Why not something bigger? Well because my application doesnt need anything more than 8 pins (Its for a smart charger)

Now in BASIC compliers, Its pretty easy, something like:

Code:
Clock = 8
Config OSC = INTIO67

But in C, I cant figure out how to do this. Ive searched around the web to no Avail. Ive been studying C, but I havent come across this "int main" function yet. Most of the time I see void main

Code:
#pragma config FOSC = INTOSC, CLKOUTEN = ON, PLLEN = OFF, WDTE = OFF, LVP = OFF
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv) {

    return (EXIT_SUCCESS);
}

(theres not much here, Im just putting it here for completeness)

Also, Is C the same everywhere? This doesnt seem to be the case between different compilers and how they set up the chips
 
Last edited:
How about just this OSCCON=0b01100000; // 8meg

may be a'lil different for your pic :)

Also I've been trying to learn the new XC8. I'm still stuck on the interrupts as of lately.........
 
Last edited:
Same here, I chose XC8 & MPLABX to learn PIC C on. It's great but MPLAB or XC8 will often flag correct syntax as an error even though it's correct and will compile just fine. Always add
Code:
#include<xc.h>
I also put the pragma after the includes
Code:
#pragma config FOSC = INTRCIO, WDTE = OFF
 
Also, Is C the same everywhere? This doesnt seem to be the case between different compilers and how they set up the chips

C language should be the same everywhere. However, using C to program microcontrollers requires some non-standard features.. and these things are compiler dependent. Things like setting up the clock frequency, declaring interrupt routines etc. are almost always done differently between compilers. You need to read the compiler documentation to learn how to handle these special microcontroller-things.

I always write main functions like this:
Code:
void main(void){
   
}
That is also non-standard, but because microcontrollers do not return from main-function and they can't accept parameters in them, most compilers do not complain about this.
And of course "the standard C library" is anything but standard..
 
Last edited:
Also, Is C the same everywhere? This doesnt seem to be the case between different compilers and how they set up the chips

C is pretty much the same everywhere except when you're setting up a microcontroller. Unfortunately, until things like the config are setup the chip won't do anything.

The other major place that they differ is in the setting of individual bits. The standard way in C to set bit 5 of PORTB is,
Code:
    PORTB |= %00100000;
However, as this is a bit clumsy, most have opted for a shorter version, eg,
Code:
    bitset (PORTB,5);
    PORTB.5 = 1;
    PORTBbits.RB5 = 1;
    B5=1;
Again, check your compiler manual.

Mike.
 
Code:
    bitset (PORTB,5);
    PORTB.5 = 1;
    PORTBbits.RB5 = 1;
    B5=1;

All of the above are standard (ansi) C.
The first one is a macro that probably translates to "PORTB |= 0b00100000;"
Second and third one uses bitfields.
The last one is probably a macro to a bitfield.
 
I didn't know that, I've always used the |= version. Oh well, I've learn't something to save me some typing in the future.

Actually when I look more closely I think this one is non-standard:
PORTB.5 = 1;
Because bitfield names can not start with a number. So that one must be a non-standard compiler feature. Quite handy as a compiler feature, because you do not have to define the structures etc. so you can use that notation for any variable (I assume.. I don't know what compiler supports that).
 
Last edited:
All of the above are standard (ansi) C.
The first one is a macro that probably translates to "PORTB |= 0b00100000;"
Second and third one uses bitfields.
The last one is probably a macro to a bitfield.

You know I hate to disagree..... But Mike is correct.. The other definitions are in the processor header files

RB5 = 0 directly assembles to PORTBbits.RB5... They are both a type definition that's all...
 
You know I hate to disagree..... But Mike is correct.. The other definitions are in the processor header files

We are thinking two different ways here.. The header files are ANSI C and you could easily write them yourself on top of the main-file. And they would compile perfectly with any ansi C compiler. The library (header files) is there just to save you the work of doing the typing (of all the register addresses etc.).

RB5 = 0 directly assembles to PORTBbits.RB5... They are both a type definition that's all...

That is a simple:

#define RB5 (PORTBbits.RB5)

which is perfectly good ANSI C. You also need to have the definition of the structure PORTBbits somewhere..

We were talking about non-standard features of C compilers. All of the above, and what I said, is perfectly standard ANSI C that all compilers should be able to handle (provided the proper definitions, which are also ANSI C).. no matter what the platform is.
 
Last edited:
I find it strange that the compiler will sometimes give this error:

Code:
make[2]: *** No rule to make target `x86)/Microchip/xc8/v1.20/include/pic12f1840.h)', needed by `dist/default/production/Test.X.production.hex'.  Stop.
make[2]: Leaving directory `C:/Users/chris/MPLABXProjects/Test.X'
make[1]: *** [.build-conf] Error 2

Yet if I rebuild again, it goes away. :confused:

I havent tried this code, but I found another way to do it. I have to still use the above methods! But its all very good info.

Code:
void SetupClock ()
{
    OSCCON = 0b01110010;

}

Reference: https://singularengineer.com/programming-pic-18-using-xc8-mplab-x-io-ports/

Note: It Compiles but I havent tested it on actual hardware.

Are there any good tutorials out there On how to use this particular Compiler? I have a book on mikroC but as Ive seen, does nothing in terms of setting stuff up. I did take a look at the IDE/Compiler briefly, but doesnt seem as rich, however, there are a lack of libraries But I guess its time I learn how to set up ADC manually as thats the only key thing this project requires.

Update! The below code works! YAY

Code:
#define _xtal_freq_8000000

#include <xc.h>
#include <stdio.h>

#include <stdlib.h>

#pragma config FOSC = INTOSC, CLKOUTEN = ON, PLLEN = OFF, WDTE = OFF, LVP = OFF

;
void SetupClock(void);

void SetupClock ()
{
    OSCCON = 0b01110010;

}

void main(int argc, char** argv) {
    SetupClock();
    
}

I successfully get a 2Mhz clock on Clock out! (8Mhz Internal clock)
 
Last edited:
MikroC is probably the best on the market re PIC 16F/18F and has extremely comprehensive library functions ready to go for just about any peripheral. It also accepts bit manipulation and defines, which compile to good assembler, so
PORTB.F5 = 1;
assembles to a single instruction;
BSF PORTB,5

beware of lesser compilers that will compile a single bit set instruction to slower/fatter/worse assembler like;
MOVLW (mask)
IORWF PORTB

People may have different opinions on that, some believe C is a "pure" concept that can't be deviated, where I believe that a really good embedded C compiler deviates more from standard C to a form of C which compiles and performs better on an embedded application (ie an "embedded refined" form of C).
 
Why don't you look through my tutorials.... They are for a pic16f628a but the code is virtually the same... LCD's, ADC's and more..

The link is in my signature..

The code is written for the HTC compiler, but if you leave the code as it is it should compile on XC8 with no problem...
 
People may have different opinions on that, some believe C is a "pure" concept that can't be deviated, where I believe that a really good embedded C compiler deviates more from standard C to a form of C which compiles and performs better on an embedded application (ie an "embedded refined" form of C).

There is "Embedded C", a standard set of extensions to the C language that include operations and support for fixed-point arithmetic, multiple distinct memory banks, and basic I/O hardware addressing.
Unfortunately I don't see many compilers implementing these things in a standard way. And even then interrupts are always problematic and require some non-standard way to deal with.
 
Hmm The IDE seems buggy. Keeps marking "_delay_us(25)" as wrong, even though I have defined the xtal frequency, but the compiler compiles.

Atleast Im not alone in my pursuit of trying to use this Compiler :).
 
Hmm The IDE seems buggy. Keeps marking "_delay_us(25)" as wrong, even though I have defined the xtal frequency, but the compiler compiles.

Wrong how? Usually the compiler gives out an error message of some sort. Post the message.
Have you turned optimizations on? The delays do not work properly if optimizations are turned off.
 
Hmm The IDE seems buggy. Keeps marking "_delay_us(25)" as wrong, even though I have defined the xtal frequency, but the compiler compiles.

Atleast Im not alone in my pursuit of trying to use this Compiler :).

The delays have TWO underscores... I know ... difficult to see

Code:
__delay_ms(25);

The easiest way is define your own, then call it what you want...
 
Wrong how? Usually the compiler gives out an error message of some sort. Post the message.
Have you turned optimizations on? The delays do not work properly if optimizations are turned off.

IDE says "Unresolved identifier __delay_us(25)" It used to give a error upon start up when I first build it too, but I think I solved that by adding in pic12f1840.h under linker files. I have optimizations off.

Here is the code, maybe someone can see if they get the same error.
Code:
#include <xc.h>
#include <stdio.h>
#include <pic12f1840.h>
#include <stdlib.h>
#include <legacy/../plib/delays.h>

#define _XTAL_FREQ 8000000
#define RA5 (PORTAbits.RA5)

#pragma config FOSC = INTOSC, CLKOUTEN = OFF, PLLEN = OFF, WDTE = OFF, LVP = OFF

void SetupClock(void);

;

void SetupClock ()
{
    OSCCON = 0b01110010;

}
void IntADC (void)
 {
         //** Initalise Ports FOR ADC **//
         PORTA = 0x00;

         TRISA = 0b00001111; //RA4,RA5 outputs, RA0, RA1 Inputs
         //** Set Up ADC Parameters **//
         ANSELA = 0b00000111; //RA4 Output, All others input
         ADRESH = 0x00; //Set the analog high bits to 0
         ADRESL = 0x00;
         ADCON1 = 0b1001000; // Sets ADRESL to contain the first 7 bits of conversion, ADRESH will have the final 3 bits.
 }    // void InitADC(void)

void main(int argc, char** argv) {
    SetupClock();
    IntADC;
    unsigned int ADCresult;
     ADCON0bits.ADON = 1; //Turns on ADC module
     ADCON0bits.CHS = 0; //AN0 as input

     while (1)
     {
       __delay_us(25);
       ADCON0bits.GO = 1; //Starts Conversion
        while (ADCON0bits.GO_nDONE) continue; //wait till ADC conversion is over
            {
            ADCresult = (ADRESH<<8) + ADRESL ; //Merging the MSB and LSB
             if (ADCresult > 512)
                RA5 = 1;
               else 
                 RA5 = 0;
       
       
     }
     }
}
 
MikroC is probably the best on the market re PIC 16F/18F and has extremely comprehensive library functions ready to go for just about any peripheral. It also accepts bit manipulation and defines, which compile to good assembler, so
PORTB.F5 = 1;
assembles to a single instruction;
BSF PORTB,5

beware of lesser compilers that will compile a single bit set instruction to slower/fatter/worse assembler like;
MOVLW (mask)
IORWF PORTB

People may have different opinions on that, some believe C is a "pure" concept that can't be deviated, where I believe that a really good embedded C compiler deviates more from standard C to a form of C which compiles and performs better on an embedded application (ie an "embedded refined" form of C).

I am a little surprised a compiler does that (bolded) for setting a port bit in the 16F/18F series. I am less sure about using IORWF since that is a byte operation, but I could not find a definitive statement, i.e., from Microchip, saying it was safe. *

Microchip recommends:
The LAT registers [18F series] allow direct access to the output. When reading, you should read PORTx, but writing should be done to LATx. The reason is to avoid "read modify write" effects. This is where the chip will change one pin (usually due to a bsf/bcf instruction you did on the port), but the other 7 pins are read. They could be read to be in a different state than they were last outputting. Capacitance on the line, as well as pullups/downs can cause this strange effect, as can process variation.

The difference may not be noticable in many cases because if you write to the whole PORTx at one time, then you will change all 8 bits at once, so it doesn't matter what is read. Also, when using bsf/bcf on a port, the port may not be loaded down enough to read as an incorrect value. However, once it reads as another value, that value gets written to the output (latch) register, and then it will continue outputting that new state.

To avoid this with PIC16 devices, you never bsf/bcf a port, you simply read the port, write it to another register, modify that register, then read that register and write it back to the port. This is known as "shaddowing". The PIC18 is easier, since instead of needing to do that, you write to the LATx register directly, thus skipping the read step in hardware.

John

Edit#2: Here is a PicList link that states andwf, iorwf, and xorwf are not "safe" from r-m-w (as suspected). Since no one challenged it, I assume it is accurate:
 
Last edited:
Here is the code...

I'm listing few observations about your code.

- You should be able to use simple "void main(void)" instead of "void main(int argc, char** argv)".
- You are not calling the "IntADC()" -function properly.. the line "IntADC;" does nothing.
- It would be better style not to omit braces {}. Makes the code easier to read and prevents future bugs (you might add more lines and forget to add the braces).
- Learn to write macros.. makes writing code easier and results in more readable and flexible code..and prevents bugs. Just don't go crazy with macros.

I generally like your style. You could separate the ADC conversion to a function like "int read_adc();" which starts ADC conversion, waits it to finish and returns the value.
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top