Tuesday, 8 October 2013

Getting started with SourceBoost C compiler

We've been avid users of Oshonsoft's PIC simulator for a long time, here at Nerd Towers - not only one of the best simulators we've seen for Microchip's PIC range, but a great compiler, working with a really easy-to-learn BASIC style language. But as the range of PIC micros has increased, sadly Oshonsoft failed to keep up. Now we're often finding that the best/cheapest PIC from the Microchip product selector can't be programmed using our favourite coding IDE.

A while back we used SourceBoost (C compiler) to write some pretty heavy-duty code (reading SD card data). Since then we've done a few other projects, and each time have leaned pretty heavily on previous examples to set up the fuses on the PIC, just to get a "hello world" blinking LED example.
So here's how to get started coding with Sourceboost C and how to use the datasheets from Microchip to get a simple "hello world" example running...

(we're assuming that SourceBoost is correctly installed, and can compile correct code into a burnable .hex file: there isn't room here to discuss setting up the IDE!)

Firstly, create a new project with source code file

Double click the .c source code file to start editing.
Note that the main system.h file is already included.

At this point, we need to tell the IDE which PIC/microcontroller we're targetting. For this example, we're going to use the 16F722 chip (because some arrived in the post about an hour ago, so why not?)

Behind the scenes, this tells the IDE to include the appropriate header file for the target chip. A quick rummage through the Program Files\SourceBoost\Include folder reveals the .h header file for our 16F722 microcontroller:

Open the header file up in a text editor and you should see a whole load of registers and values that the compiler will recognise as being related to this chip. Often (though be careful of a few gotchas) these match the values given to registers (and bit values) in the datasheet. Quite often. But not always. So it always pays to have the header file as a reference!

Now take a look at the datasheet for the PIC and check out the configuration bits.
Reading microcontroller datasheets is an art in itself. Starting to code from a datasheet is infinitely more difficult than, say, hacking a few lines of Arduino code together, but also gives you complete control of the lower-level workings on the chip. No nice, abstracted libraries here - you're gettingn your hands dirty!

To keep our hardware set-up nice and simple, we're going to run this PIC off it's own internal oscillator, at 16Mhz (so no need for an external crystal or resonator then - just give it some juice and let it run!)

Starting with configuration word number one, we're going to set each bit of the special register CONFIG1 to get the PIC running exactly how we want it. We do this in SourceBoost by using the #prama DATA command. From the header .h file, we can see that SourceBoost has called this register _CONFIG1

We set the _CONFIG1 values by using the bitwise AND operator to join them together.
From the datasheet, we want _DEBUG off, PLL on (to set the clock speed to 16Mhz) brown out reset BOREN disabled, code protection _CP disabled, MCLR tied high (use RE3 as input), power up timer _PWRTE disabled, watch dog timer WDTE disabled, and the fosc (clock source) to be the internal oscillator with no clock out.

This is where the header file comes in really handy.
The SourceBoost C variables and the datasheet names don't always exactly match up. But from our header file, we can see that the combination of config bits we want should read:

 (note that some variable names translate back to the same value/register. So it doesn't really matter whether we use _MCLR_DIS or _MCLR_OFF, both have the same value. Likewise for the oscillator selection, _INTRC_OSC_NOCLKOUT and _INTOSCIO both have the same value 0x3FFC)

Configuration word 2 is much similar for this chip.
We simply want to disable all Vcap activity on all pins

And lastly, tell our compiler the clock speed that the device will be running at (to assist with compiling pause/delay macros later on). Hit the build button and if all has gone well, the last line of the report should read "success" followed by "done!"

Now the PIC should be running under it's own steam when powered up. That's to say, it's using it's own internal oscillator as a clock source, so when it's given power, any code inside the "main" function will get called. So let's write some simple code to get our "hello world" program working:

There we have it - a simple LED blinking program. But we're not quite done just yet.
There are still a few more registers to set up. That's why we have the "initialise" routine. Here at Nerd Towers we just got into the habit of creating an init routine, rather than putting everything inside the main loop. It's just personal preference - that's just how we roll....

Now PICs are awesome devices. They're multi-function and one chip can have hundreds of possible configurations; many have A2D (analogue-to-digital) converters on the input pins, a lot now have capacitive sensing on a lot of the input pins, and almost all can configure their pins as either inputs or outputs (and in some quirky, charlie-plexing programs, sometimes change between the two, or set to high-impedence: neither input nor output!)

So we need to tell the PIC what function we want the pins to take on.
We'll start by setting the input/output direction of the pins, using the TRIS registers:

Back to the datasheet - we've got loads of different ports to configure. Our chip has PORTA, PORTB, PORTC (and even a PORTE) defined on the pins. So we need to set the appropriate registers to define theses.

We're going to make the whole of PORTA (i.e.any pin that is labelled RA.. such as RA0, RA1 etc) an input.
We're going to make the whole of PORTB outputs (it's perfectly acceptable to use a combination of inputs/outputs on a single port - for example, make RB0 an input, RB1 and RB2 an output, RB3 an input and so on)
We're not really bothered about PORTC but for completeness, we'll make it an output too.
A quick check of the header file and we can see the TRIS register variable names:

If we want to make all the pins of PORTA inputs, we need to set the value b11111111 (in the tris register a one represents "input" and a zero represents "output"). To make PORTB outputs, we set the tris register to b00000000. If we wanted to make, say, RB2 an input, and all others outputs, we would use the value b11111011 (or 251 decimal, 0xFB in hex).

Now, it's not really critical for this example, since we're only concerned with outputs, but debugging input pins that don't work properly can be a real nightmare if you've forgotten to turn off the A2D converter module on each bank of input pins.

We just got into the habit of actively disabling analogue on our pins - some chips default to "off" but most chips have the A2D converters "on" at boot-up. To change the analogue settings on a PIC, we need to set the ANSEL registers. Our particular PIC has two sets of A2D, on both PORTA and PORTB.

Back to the header file, and we can see the variable names we need:

So the last thing to do is make our i/o lines all digital and make sure all output pins are turned off:

Build the file, then go digging about in the debug folder (our SourceBoost is set up to create debug versions of the .hex file during testing) and burn the .hex file to your PIC.


No comments:

Post a Comment