You are on page 1of 7

Program Structure

Program Structure • #include is a directive which allows the pro-


grammer to include header files, which typically
#includes Preamble give the compiler some information regarding the
#fuses type of PIC and in some cases library functions,
#uses such as the mathematics library.
#bytes • #fuses is a directive which inserts code to set
#bits up the configuration fuses at programming time.
function declarations; • #use is a directive which allows the programmer
global variable definitions; to give the compiler some information regarding
the hardware (e.g. clock speed, or pins used for
Main (primary) function serial I/O) or how a peripheral device is to be set
void main(void) up.
{ • #byte is a directive allowing the programmer to
local variable definitions; give a fixed File location a name; e.g.
setup PIC peripherals; #byte PORTA = 5 to define Port A.
do this;
• #bit is a directive allowing the programmer to
call that;
do the other; give a bit in a fixed File location a name; e.g. a bit
etc; in a port connected to a LED; e.g.
} #bit LED = 5.2.
• Any function, besides main() should be named
Secondary functions here, together with the type of variable returned
function1() and the size and names of variables to be passed
{ to the function.
local variable definitions; • Variable defined here are known everywhere in
do this; the program.
call that; • All C programs must have a main() function.
etc; • Variables defined here are only known within the
} main() function.
• Any built-in functions to set up PIC peripherals
function2()
must follow the variable definitions.
{
• Statements and function calls make up the body
local variable definitions;
of the function.
do this;
call that; • Any other functions are defined outside the main
etc; function. They have the same structure as
} main() and can be called from main() or from
themselves. Any variables defined inside these
functions are known only within the function itself.

For example to make PORTD count forever incrementing once per second:
#include <16f877.h> Tells compiler that it is a 16f877 PIC.
#fuses NOWDT,NOPROTECT,PUT,NOLVP No WD Timer, NO code PROTECT, Power-Up Timer, NO Low-Voltage Programming
#use delay(clock = 2000000) Tells the compiler that a 2MHz clock is in use (needed for delay_ms() below)
#byte PORTD = 8 Tells the compiler that File 8 is to be called PORTD
main() The main function
{ Begin
int count = 0; Define an integer variable of size 8 bits, and initialize it to zero
while(TRUE) TRUE is defined as 1 in the header file: Do everything below forever
{ Begin the while loop
PORTD = count++; Send out the 8-bit value of count to PORTD & increment count
delay_ms(1000); delay_ms(n) is a CCS built-in function giving a fixed delay of n milliseconds
} End of while loop
} End of main() function

eejits_guide_to_PIC_C.doc Page 1 of 7 Wednesday, 13 December 2006


Data Definitions

Integers are whole numbers. By


default in the CCS compiler they
Integer Data Types are unsigned. The qualifier
signed may be used to deal with
negative numbers.
short int 1-bit number
int1 0 or 1 The keywords int8 (same as
int 8-bit unsigned number
int8
char
0 – 255 (0 – 0xFF)
Defines an 8-bit character
} int), int16 (same as long) and
int32 are specific to the CCS
compiler.
long int 16-bit unsigned number
int16
int32
0 – 65,536 (0 – 0xFFFF)
16-bit unsigned number
} The keyword const tells the
compiler not to subsequently
0 – 4,294,967,295 ( 0 – 0xFFFFFFFF) change the object
signed int 8-bit signed number }
signed int8 -128 – +127 (0x80 – 0x7F) Objects should normally be de-
signed long 16-bit signed number } fined at the start of a function, be-
fore any statements. Such local
signed int16 -32,788 – +32,787 (0x8000 – 0x7FFF)
signed int32 32-bit signed number variables are only known (are in
-2,147,483,648 – +2,147,483,647 scope) inside their function. If they
(0x80000000 – 0x7FFFFFFF) are static, they will remember
their values between calls

Objects defined outside a function


Floating Point Data Types are globally known to all functions

float 32-bit floating-point number A floating-point variable is made


±1.18 × 10-38 to up of two parts; for example
±3.40 × 1038 300,000 is stored as 0.3×106
where 3 is the mantissa and 6 the
exponent. This gives a large
range of decimal numbers, but
needs a lot of program space to
implement.
For example:
int FRED, JIM; Two unsigned global 8-bit variables called FRED and JIM
long NOREEN; One 16-bit unsigned global variable called NOREEN
int32 NUMBER1 = 0; One 32 bit unsigned global variable called NUMBER1 initial value 0

main()
{
int i; One unsigned local 8-bit variable called i
signed int number2; One signed local 8-bit variable called number2
const int Weeks = 52; One 8-bit variable called Weeks that is not to be changed from 52 later on
const int Months[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
An array of 12 8-bit variables called Months[] that are not be changed

/* Statements and function calls */

/* More functions with their own local variables */

eejits_guide_to_PIC_C.doc Page 2 of 7 Wednesday, 13 December 2006


Simple Selection Structures
To implement a decision (a branch): For instance,
The if-else Structure to make number1 count 0..1..2..3..4..5..0…..

if(something is true) if(number1 >= 5)


{do this;} {number1 = 0;}
else else {number1++;}
{do that;}
It is possible to use the if part without the else
part; for example:
or
if(temperature < 5) {alarm = 1;}

if(something is true) If more than one statement following the if or


{ else then group them together between braces
do this; {…}. These are called compound statements or a
do that; block. For example the variable alarm is made
} zero and the variable lamp made one:
else if(something else is true)
{ if(fuel < 20)
do the other; {
} alarm = 0;
else {do the default;} lamp = 1;
Optional }

Multiple-way decision based on a series of com-


The switch-case Statement parisons on the value of a variable.
For example:
switch (something)
{ switch(temperature)
case value1: {
{do this; break;} case 0:
case value2: value3: (heater2 = 1; fan = 1; break;}
{do that; break;} case 1:case 2:case 3:case 4:
default: {heater 2 = 1; fan = 0; break;}
{optional default action;} case 5:case 6:case 7:case 8: case 9:
} {heater 1 = 1; fan = 0; break;}
default:
{heater1 = heater2 = fan = 0;}

eejits_guide_to_PIC_C.doc Page 3 of 7 Wednesday, 13 December 2006


Iterative Statements
To implement a loop to do something a number of
The while loop times, or even forever.
For example to find the integer square root of a
number:
while(true)
i = 1;
{
while(i < number)
do this;
{
do that; L
o number = number - i;
do the other; o i = i + 2;
} p
}
root = i >> 1; /* Divide by 2 */

To implement a loop test for exit after the


The do-while loop statement has been executed at least once.
For example to forever pulse pin RA0 (as-
do { suming a directive #bit pin_RA0 = 5.0)
this; L high and low once per second:
o
that; o
do {
and the other; p pin_RA0 = 0;
}while(something is true); delay_ms(500);
pin_RA0 = 1;
delay_ms(500);
} while(1);

An enhanced while loop, which allows an


The for Statement initialisation, and an action at the end of
each loop.
for(initial;while;loop) For example, repeat the while example
{ above:
do this; L for(i=1; i<number; i=i+2)
do that; o
o
{
do the other; p number = number – i;
} }
root = i >> 1;
Fields can have several entries separated
by commas; e.g.
for(i=1,j=52;i<number;i++,j-=4).

1. The break keyword allows the programmer to break out of a loop prematurely; for example
the light emitting diode connected to pin RA0 (#bit LED = 5.0) will pulse continually until
SWITCH1 (say #bit SWITCH 5.3; i.e. pin RA3) goes low, and then the loop breaks:
while(1)
{
LED = 0; delay_ms(500);
LED = 1; delay_ms(500);
if(SWITCH1) {break;} Exit the loop if SWITCH1 is low (false)
}
2. The continue keyword allows the execution to go back to the top of the loop; for example
the LED will pulse only as long as SWITCH2 is high:
while(1)
{ Short-circuit the loop if SWITCH2 is low
if(SWITCH2==0) {continue;}
LED = 0; delay_ms(500);
LED = 1; delay_ms(500);
}

eejits_guide_to_PIC_C.doc Page 4 of 7 Wednesday, 13 December 2006


Parallel Ports1: Bit Banging
set_tris_X(nn); • set_tris_X(nn); is a special function to allow the pro-
grammer to put a constant into the TRISX register; e.g.:
set_tris_c(0x0F); /* TRISC <- b’00001111’*/
makes the lower four Port C pins Inputs and upper four
Port C pins Outputs
#byte PORTX = kk • #byte is a directive which allows the programmer to give
a fixed File location a name; e.g.:
#byte FUEL = 6
which gives File 6 (i.e. Port B) the name FUEL
#bit pin_name = PORTX.n • #bit is a directive allowing the programmer to give a bit in
a fixed File a name; e.g.:
#bit STROBE = 5.0
which gives bit 0 (i.e. pin RA0) the name STROBE
PORTX = jj;
• Writing to a port is by assignment; e.g., assuming that
Port C pins are all set as Output:
PORTC = 0X03;
which brings pins RC[1:0] high and the rest low
value = PORTX; • Reading from a port is by assignment; e.g.:
value = FUEL;
Boolean = pin_name; • Reading from a pin (setup as an Input) is by assignment;
either 0 or 1; e.g.:
if(SWITCH == 0)
is true if the value of the bit named SWITCH is 0
PIN_NAME = 0|1; • Writing to a pin (setup as an Output) is by assignment; ei-
ther 0 or 1; e.g.:
STROBE = 1;
brings the pin labelled STROBE high
For example, to cause pin RA0 (connected to a LED) to pulse at a rate of twice a second as long
as the voltage on pin RB7 is low (switch open) and then pulse at a rate of four per second for two
seconds (included header file not shown):
#use delay(clock = 8000000) Tells compiler that an 8MHz clock is in use (for delay() ftns)
#bit LED = 5.0 Names pin RA0 LED (assume that an LED is connected)
#bit SWITCH=6.7 Names pin RB7 SWITCH (assume that a switch is connected)
void main(void) The main() function
{ Begin main()
int i = 0; Define an integer variable of size 8 bits & initialize it to zero
set_tris_a(0xFE); Make RA0 an Output, rest of Port A Inputs (TRISA = 111110)
set_tris_b(0xFF); Make all Port B pins inputs (TRISB = 11111111)
while(SWITCH == 0) As long as pin RB7 remains low DO:
{ Begin the while loop
LED = 1; Bring pin RA0 high
delay_ms(250); and hang around for 250ms
LED = 0; Bring pin RA0 low
delay_ms(250); and hang around for 250ms
} End of while loop
while(i++ < 8) DO eight times (i starts at zero & is incremented each pass)
{ Begin the while loop
LED = 1; Bring pin RA0 high
delay_ms(125); and hang around for 125ms
LED = 0; Bring pin RA0 low
delay_ms(125); and hang around for 125ms
} End of while loop
} End of main() function
eejits_guide_to_PIC_C.doc Page 5 of 7 Wednesday, 13 December 2006
Asynchronous Serial Ports Output
E.g. to define a single 4800 baud serial
Preliminary directives port using the integral USART (pins RC6 &
#use delay(clock speed)
RC7) for an 8MHz device:
#use delay(clock= 8000000)
Tells the compiler what the PIC MCU’s master clock rate #use rs232(BAUD=4800,XMIT=PIN_C6,RCV=PIN_C7)
is.
E.g. to define a two serial ports for the
#use rs232(BAUD=X,XMIT=Y,RCV=Z,stream=name) same device; one at 4800 baud called pc
and one at 1200 baud called gps:
Tells the compiler what baud rate to use, what pins to use
#use delay(clock= 8000000)
to ReCeiVe and transmit and optionally for multiple serial #use rs232
ports to give each stream a name for use with (BAUD=4800,XMIT=PIN_C6,RCV=PIN_C7,STREAM=pc)
fprintf() etc functions. #use rs232
(BAUD=1200,XMIT=PIN_A0,RVC=PIN_A1,STREAM=gps)

E,g, to send out ten stars followed by the


putc(char) and codes 0x0a (Line Feed) and 0x0d (Car-
fputc(char,stream_name) functions riage return (that is a new line):
int count = 10;
while(i<10) {putc(‘*’);}
Sends a specified character over the RS232 XMIT pin. putc(0x0a);
putc() (or putchar()) is used if there is only one se- putc(0x0d);
rial stream and fputc() is used for a named stream.
The char is an 8-bit constant or variable. To transmit the single character ‘Z’ to the
pc stream:
fputc(‘Z’,pc);

E.g. to send out the following 2-line mes-


puts(string) and sage:
fputs(string,stream_name) functions puts(“Hello world”);
puts(“Hello world once more”);
Sends out a specified character string (or a null-
terminated character array) over the RS232 XMIT pin. To send out a message to the pc stream:
A new line sequence (Line Feed – Carriage Return) is
fputs(“Hello world”,pc);
automatically send after the string.
E.g. to transmit forever the mes-
sage “For the xxth time: Hello
printf(string,values…) and world”, where xx is the value of a
fprintf(stream_name,string,values…) long int variable i; displayed
as an unsigned long decimal
functions value: Some token values are:
%u Unsigned intger
Sends out a specified character string (or a null- %lu Long Unsigned integer
terminated character array) over the RS232 XMIT pin %d signed Decimal
and specified variables. Values is a list of comma %ld signed Long Decimal
separated variables whose form and position is indi- %x/%X heX integer (lower/upper case letters)
cated by tokens embedded in the string. %lx/%lX Long heX integer
%4X Four digits only
%03u 3-digit unsigned integer with leading 0s
Token
while(TRUE) Variable
{
printf(“For the %luth time, Hello world \n\r”,i);
i++; New line/Return
}
eejits_guide_to_PIC_C.doc Page 6 of 7 Wednesday, 13 December 2006
Asynchronous Serial Ports Input
E.g. to continually poll the serial port waiting for the
getc() and fgetc(stream_name) terminal to send a ‘G’ or ‘g’ character:
functions char character;
…..
…..
Waits for a character to come in from the
while(TRUE)
RS232 RCV pin. getc() (or getch() or {
getchar()) is used if there is only one serial character = getc();
stream and fgetc() is used for a named if((character==‘G’)|(character==’g’))
stream. The char is an 8-bit constant or vari- {break;}
able. }

If you do not want to wait forever for a charac- If there is more than one serial port (see previous
ter use the function kbhit() to first test for a page); for instance, a stream called pc, then
character available. substuitute for getc()above:

character = fgetc(pc);

eejits_guide_to_PIC_C.doc Page 7 of 7 Wednesday, 13 December 2006