PIC Base C 2
PIC Base C 2
www.gooligum.com.au
XC8 is available as a free download from www.microchip.com, and CCS PCB is bundled for free with MPLAB 8
Page 1
www.gooligum.com.au
As an initial example, the pushbutton input was copied to the LED output, so that the LED was on, whenever
the pushbutton is pressed.
In pseudo-code, the operation is:
do forever
if button down
turn on LED
else
turn off LED
end
The assembly code we used to implement this, using a shadow register, was:
start
movlw
tris
b'111101'
GPIO
clrf
btfss
bsf
sGPIO
GPIO,3
sGPIO,1
movf
movwf
sGPIO,w
GPIO
goto
loop
; repeat forever
loop
XC8
To copy a value from one bit to another, e.g. GP3 to GP1, using XC8, can be done as simply as:
GPIObits.GP1 = GPIObits.GP3;
But that wont do quite what we want; given that GP3 goes low when the button is pressed, simply copying
GP3 to GP1 would lead to the LED being on when the button is up, and on when it is pressed the opposite
of the required behaviour.
We can address that by inverting the logic:
GPIObits.GP1 = !GPIObits.GP3;
GPIObits.GP1 = GPIObits.GP3 ? 0 : 1;
or
This works well in practice, but to allow a valid comparison with the assembly source above, which uses a
shadow register, we should not use statements which modify individual bits in GPIO. Instead we should
write an entire byte to GPIO at once.
For example, we could write:
if (GPIObits.GP3 == 0)
GPIO = 0b000010;
else
GPIO = 0;
// if button pressed
//
turn on LED
// else turn off LED
Page 2
www.gooligum.com.au
However, this can be written much more concisely using Cs conditional expression:
GPIO = GPIObits.GP3 ? 0 : 0b000010; // if GP3 high, clear GP1, else set GP1
It may seem a little obscure, but this is exactly the type of situation the conditional expression is intended for.
Complete program
Here is the complete XC8 code to turn on an LED when a pushbutton is pressed:
/************************************************************************
*
Description:
Lesson 2, example 1
*
*
*
*
Demonstrates reading a switch
*
*
*
*
Turns on LED when pushbutton is pressed
*
*
*
*************************************************************************
*
Pin assignments:
*
*
GP1 = indicator LED
*
*
GP3 = pushbutton switch (active low)
*
*
*
************************************************************************/
#include <xc.h>
/***** CONFIGURATION *****/
// int reset, no code protect, no watchdog, int RC clock
__CONFIG(MCLRE_OFF & CP_OFF & WDT_OFF & OSC_IntRC);
/***** MAIN PROGRAM *****/
void main()
{
// Initialisation
TRIS = 0b111101;
// Main loop
for (;;)
{
// turn on LED only if button pressed
GPIO = GPIObits.GP3 ? 0 : 0b000010;
}
}
Note that the processor configuration has been changed to disable the external
use GP3 as an input.
MCLR
reset, to allow us to
CCS PCB
Reading a digital input pin with CCS PCB is done through the input() built-in function, which returns
the state of the specified pin as a 0 or 1.
To output a single bit, we could use the output_bit() function. For example:
output_bit(GP1, ~input(GP3));
This would set GP1 to the inverse of the value on GP3, which is exactly what we want.
Page 3
www.gooligum.com.au
But once again, statements like this, which change only one bit in a port, are potentially subject to readmodify-write issues. We should instead use code which writes an entire byte to GPIO (or, as CCS would
have it, port B) at once:
output_b(input(GP3) ? 0 : 0b000010);
Again, using the ?: conditional expression makes this seem a little obscure, but this is very concise and,
when you are familiar with these expressions, clear.
Complete program
Here is the complete CCS PCB code to turn on an LED when a pushbutton is pressed:
/************************************************************************
*
*
*
Description:
Lesson 2, example 1
*
*
*
*
Demonstrates reading a switch
*
*
*
*
Turns on LED when pushbutton is pressed
*
*
*
*************************************************************************
*
*
*
Pin assignments:
*
*
GP1 = indicator LED
*
*
GP3 = pushbutton switch (active low)
*
*
*
************************************************************************/
#include <12F509.h>
#define
#define
#define
#define
#define
#define
GP0
GP1
GP2
GP3
GP4
GP5
PIN_B0
PIN_B1
PIN_B2
PIN_B3
PIN_B4
PIN_B5
// define GP pins
// repeat forever
Note again that the processor configuration has been changed to disable the external
GP3 is available as an input.
MCLR
reset, so that
Page 4
www.gooligum.com.au
Comparisons
Here is the resource usage summary for the Turn on LED when pushbutton pressed programs:
PB_LED
Source code
(lines)
Program memory
(words)
Data memory
(bytes)
Microchip MPASM
18
13
29
CCS PCB
22
Assembler / Compiler
At only 5 or 6 lines, the C source code is amazingly succinct thanks mainly to the use of Cs conditional
expression (?:).
This code waits for the button to be pressed (GP3 being pulled low), by sampling GP3 every 768 s and
waiting until it has been low for 13 times in succession approximately 10 ms in total.
Page 5
www.gooligum.com.au
XC8
To implement the counting debounce algorithm (above) using XC8, the pseudo-code can be translated
almost directly into C:
db_cnt = 0;
while (db_cnt < 10)
{
__delay_ms(1);
if (GPIObits.GP3 == 0)
db_cnt++;
else
db_cnt = 0;
}
db_cnt;
// debounce counter
Note that, because this variable is only used locally (other functions would never need to access it), it should
be declared within main().
Whether you modify this code to make it shorter is largely a question of personal style. Compressed C code,
using a lot of clever tricks can be difficult to follow.
But note that the while loop above is equivalent to the following for loop:
for (db_cnt = 0; db_cnt < 10;)
{
__delay_ms(1);
if (GPIObits.GP3 == 0)
db_cnt++;
else
db_cnt = 0;
}
That suggests restructuring the code into a traditional for loop, as follows:
for (db_cnt = 0; db_cnt <= 10; db_cnt++)
{
__delay_ms(1);
if (GPIObits.GP3 == 1)
db_cnt = 0;
}
In this case, the debounce counter is incremented every time around the loop, regardless of whether it has
been reset to zero within the loop body. For that reason, the end of loop test has to be changed from < to
<=, so that the number of iterations remains the same.
Page 6
www.gooligum.com.au
Complete program
Here is the complete XC8 code to toggle an LED when a pushbutton is pressed, including the debounce
routines for button-up and button-down:
/************************************************************************
*
*
*
Description:
Lesson 2, example 2
*
*
*
*
Demonstrates use of counting algorithm for debouncing
*
*
*
*
Toggles LED when pushbutton is pressed then released,
*
*
using a counting algorithm to debounce switch
*
*
*
*************************************************************************
*
*
*
Pin assignments:
*
*
GP1 = indicator LED
*
*
GP3 = pushbutton switch
*
*
*
************************************************************************/
#include <xc.h>
#include <stdint.h>
#define _XTAL_FREQ
4000000
// debounce counter
// Initialisation
GPIO = 0;
sGPIO = 0;
TRIS = 0b111101;
// Main loop
for (;;)
{
// wait for button press, debounce by counting:
for (db_cnt = 0; db_cnt <= 10; db_cnt++)
{
__delay_ms(1);
// sample every 1 ms
if (GPIObits.GP3 == 1) // if button up (GP3 high)
db_cnt = 0;
//
restart count
}
// until button down for 10 successive reads
// toggle LED on GP1
sGPIO ^= 0b000010;
GPIO = sGPIO;
Page 7
www.gooligum.com.au
CCS PCB
To adapt the debounce routine to CCS PCB, the only change needed is to use the input() function to read
GP3, and to use the delay_ms() delay function:
for (db_cnt = 0; db_cnt <= 10; db_cnt++)
{
delay_ms(1);
if (input(GP3) == 1)
db_cnt = 0;
}
db_cnt;
// debounce counter
Once again, because this variable is only used locally, it should be declared within main().
Complete program
This debounce routine fits into the toggle an LED when a pushbutton is pressed program, as follows:
/************************************************************************
*
*
*
Description:
Lesson 2, example 2
*
*
*
*
Demonstrates use of counting algorithm for debouncing
*
*
*
*
Toggles LED when pushbutton is pressed then released,
*
*
using a counting algorithm to debounce switch
*
*
*
*************************************************************************
*
*
*
Pin assignments:
*
*
GP1 = indicator LED
*
*
GP3 = pushbutton switch
*
*
*
************************************************************************/
#include <12F509.h>
#define
#define
#define
#define
#define
#define
GP0
GP1
GP2
GP3
GP4
GP5
PIN_B0
PIN_B1
PIN_B2
PIN_B3
PIN_B4
PIN_B5
// define GP pins
Page 8
www.gooligum.com.au
// debounce counter
// Initialisation
output_b(0);
sGPIO = 0;
// Main loop
while (TRUE)
{
// wait for button press, debounce by counting:
for (db_cnt = 0; db_cnt <= 10; db_cnt++)
{
delay_ms(1);
// sample every 1 ms
if (input(GP3) == 1)
// if button up (GP3 high)
db_cnt = 0;
//
restart count
}
// until button down for 10 successive reads
// toggle LED on GP1
sGPIO ^= 0b000010;
output_b(sGPIO);
// repeat forever
As before, the processor configuration in both the XC8 and CCS programs has been changed to disable the
external MCLR reset, so that GP3 is available as an input.
Page 9
www.gooligum.com.au
In the baseline (12-bit) PICs, such as the 12F509, the weak pull-ups are not individually selectable; they are
either all on, or all off.
To enable the weak pull-ups, clear the GPPU bit in the OPTION register.
In the example assembler program from baseline lesson 4, this was done by:
movlw
b'10111111'
; -0------
option
XC8
To load the OPTION register in XC8, simply assign a value to the variable OPTION.
For example:
OPTION = 0b10111111;
//-0------
Note that this is commented in a similar way to the assembler version, with -0------ making it clear we
are concerned with the value of bit 6 ( GPPU ), and that clearing it enables pull-ups.
However, if we use the symbols for register bits, defined in the header files, we can write instead:
OPTION = ~nGPPU;
To enable weak pull-ups in the toggle an LED program from the previous example, simply add this
OPTION = line into the initialisation routine.
The new initialisation code becomes:
// Initialisation
OPTION = ~nGPPU;
GPIO = 0;
sGPIO = 0;
TRIS = 0b111101;
Page 10
www.gooligum.com.au
CCS PCB
Enabling the internal weak pull-ups using CCS PCB is a little obscure, and not well documented.
The CCS compiler provides a built-in function for enabling pull-ups, PORT_x_PULLUPS(), but the
documentation (in the online help) for this function states that it is only available for 14-bit (midrange) and
16-bit (18F) PICs. For baseline PICs, we are told:
Note: use SETUP_COUNTERS on PCB parts
However, the documentation for the built-in SETUP_COUNTERS() function makes does not mention the
weak pull-ups at all.
To figure this out, we need to go digging in the header files. 12F509.h includes the following lines:
// Timer 0 (AKA RTCC)Functions: SETUP_COUNTERS() or SETUP_TIMER_0(),
#define RTCC_INTERNAL
0
#define RTCC_DIV_1
8
#define RTCC_DIV_2
0
#define WDT_18MS
0x8008
#define DISABLE_PULLUPS
0x40 // for 508 and 509 only
#define DISABLE_WAKEUP_ON_CHANGE
0x80 // for 508 and 509 only
To setup the timer without enabling the pull-ups, you explicitly disable them by ORing the
DISABLE_PULLUPS symbol with the second parameter.
For example:
setup_counters(RTCC_INTERNAL,RTCC_DIV_1|DISABLE_PULLUPS);
Page 11
www.gooligum.com.au
To enable weak pull-ups in the toggle an LED program from the last example, add this
SETUP_COUNTERS() line to the initialisation routine.
Our new initialisation code is:
// Initialisation
setup_counters(RTCC_INTERNAL,RTCC_DIV_1);
// enable weak pull-ups
output_b(0);
// start with LED off
sGPIO = 0;
//
update shadow
Comparisons
Here is the resource usage summary for the toggle an LED using weak pull-ups programs:
Toggle_LED+WPU
Source code
(lines)
Program memory
(words)
Data memory
(bytes)
Microchip MPASM
43
36
21
94
CCS PCB
20
82
Assembler / Compiler
The C programs are less than half as long as the assembler versions, but even the CCS compiler, which has
optimisations enabled (unlike the XC8 compiler in Free mode), generates code more than twice the size of
the hand-written assembler version.
Summary
This lesson has shown that basic digital input operations can readily be performed in C, using either the XC8
or CCS compiler, despite their quite different approaches.
However, we also saw, in example 3, that the use of CCSs built-in functions does not necessarily make the
code easier to follow; the operation of a built-in function may not always be clear, or well-documented.
Sometimes, the XC8 approach of directly accessing the PIC registers is actually easier to follow.
In the next lesson well see how to use these C compilers to configure and access Timer0.
Page 12