Program description

What does it do?

The Output program shows you how to light up the CHRP board LEDs using a basic set of C source files. Once you know how to turn on lights, it's just as easy to turn on motors and other output devices.

New concepts

Programming 8-bit microcontrollers in C is a bit different than creating C programs for typical desktop computers. Microchip's XC compilers introduce changes from their previous C compilers as well. Here are a few things to keep in mind when programming PICmicros in C:

  • Using the XC compilers and creating ANSI and CCI compliant programs enables the easiest migration of your programs to new microcontrollers and the best future compatibility.
  • A configuration program file is needed to set up the programmable hardware features of the microcontroller.
  • The xc.h general include file must be included in every C project source code file.
  • The main function must be present in all programs and is the first function called by the compiler at startup.
  • Reset code is added by the C compiler at the end of the main function, so main should consist of a loop that never terminates.

Output programming activity

Let's start CHRP programming flashing the Port B LEDs on and off. If you've assembled your CHRP from scratch, this is a good place to start because you can use the Output program to test the functionality of your circuit as well as your LEDs.

Program requirements

To use this program you will need:

• An assembled CHRP board.

• A computer with the MPLAB X IDE (Integrated Development Environment).

• A Microchip PIC-kit 2 or PIC-kit 3 USB programmer or a stand-alone PIC programmer

• A 6-12V wall adapter, or a power supply, or batteries to power the CHRP.

Before you continue, you will need to know how to create a C project and add source files to it (see the MPLAB X - C language tutorial).

What you should know before starting

CHRP related information

This program controls the LEDs connected to PORTB of the PIC microcontroller (see the CHRP 3.01 schematic to examine the PORTB circuitry).

Microcontroller related information

PORTB is an 8-bit RAM register (known as a file register Microchip terminology) that connects the processing unit core to external I/O (input/output) pins. PORTB is an 8-bit port, so it can control 8 individual circuits. Each of the PORTB pins can be set to be either an input or an output, depending on the value of an associated bit in the TRISB register. See the simplified PIC16F886 block diagram for more details on how the I/O circuits are linked to the processor core.

TRISB (TRIState for port B) is, like PORTB, also an 8-bit file register (or, RAM memory location). Each bit of the TRISB register controls the input/output status of its associated PORTB I/O pin. When a value of 0 is written into a TRISB bit, it makes the corresponding PORTB pin an output. Conversely, when a value of 1 is written into a TRISB bit, it makes the corresponding PORTB pin an input. For example, writing the 8-bit number 00000111 into TRISB would cause the upper five PORTB pins (referring to the left-most bits, or highest digits as the 'upper' pins) to become outputs, and the lower three PORTB pins to become inputs.

Create the PIC16F886config.c source file

The configuration.c file was generated by the Configuration Bits tool in the Window/PIC Memory Views menu of MPLAB X. If you haven't already done so (following the steps in the MPLAB X - C language tutorial), create and save the following code into the PIC16F886config.c source code file.


/*==============================================================================
	CHRP 3.1 (PIC16F886) configuration bit settings.

	Processor configuration bits determine processor operating characteristics
	as well as processor-specific features. Refer to the processor datasheet
	for details on specific configuration bit settings.

	MPLAB X contains a 'Configuration Bits' settings tool accessed under:
	Window menu -> PIC Memory Views. This tool generated the settings, below.
==============================================================================*/

// TODO - Verify or modify configuration bits using the Configuration Bits tool.

// PIC16F886 Configuration Bit settings generated by MPLABX for CHRP3 Level 1.

#include    "xc.h"              // XC compiler general include file

// CONFIG1
#pragma config FOSC = INTRC_NOCLKOUT    // Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled and can be enabled by SWDTEN bit of the WDTCON register)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = ON       // RE3/MCLR pin function select bit (RE3/MCLR pin function is MCLR)
#pragma config CP = OFF         // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = OFF      // Brown Out Reset Selection bits (BOR disabled)
#pragma config IESO = OFF       // Internal External Switchover bit (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled)
#pragma config LVP = OFF        // Low Voltage Programming Enable bit (RB3 pin has digital I/O, HV on MCLR must be used for programming)

// CONFIG2
#pragma config BOR4V = BOR40V   // Brown-out Reset Selection bit (Brown-out Reset set to 4.0V)
#pragma config WRT = OFF        // Flash Program Memory Self Write Enable bits (Write protection off)
		

This file sets the microcontroller's configuration bits. Configuration bits enable/disable or select hardware features of the microcontroller at programming time. Including the settings in a file eliminates the need to set the bits in the programming software every time the microcontroller is programmed.

Create the Output.c program file

To keep things simple, our first program will include all of the user-generated code in one, main source file. Subsequent programs will separate our code into multiple files to differentiate the functions and to simplify the main code. If you haven't already done so (following the steps in the MPLAB X - C language tutorial), create and save the following code into the Output.c source code file.


/*==============================================================================
	Project: Output
	Version: 3.1 				Date: November 24, 2014
	Target: CHRP3				Processor: PIC16F886

	A simple output program that flashes the LEDs on PORTB of the CHRP3 board
	using the C compiler's built-in millisecond time delay macro.
==============================================================================*/

#include	"xc.h"				// XC general include file

#include	"stdint.h"			// Include integer definitions
#include	"stdbool.h"			// Include Boolean (true/false) definitions

#define	_XTAL_FREQ	4000000		// Set clock frequency for time delays

/*==============================================================================
	Initialize ports. This function is called from within main().
==============================================================================*/

void initPorts(void)
{
	// Initialize user ports and peripherals.

	ANSEL = 0b00000000;			// Make all PORTA pins digital I/O
	ANSELH = 0b00000000;		// Make all PORTB pins digital I/O
	OPTION_REG = 0b01010111;	// PORTB pull-ups on, TMR0 internal div 256

	// Set port directions for I/O pins: 0 = Output, 1 = Input

	TRISA = 0b00101111;			// Set runLED, IR LEDs as outputs in PORTA
	TRISB = 0b00000000;			// Set all PORTB pins for LED output

	// Set initial port outputs.

	PORTA = 0;					// Turn all PORTA outputs off, turn Run LED on
}

/*==============================================================================
	Main program loop. The main() function is called first by the compiler.
==============================================================================*/

int main(void)
{
	// Initialize I/O and peripherals for PORTB output
	initPorts();

	// Make the main program loop repeat continuously
	while(1)
	{
		// Turn all LEDs on and wait for a time delay
		PORTB = 0b11111111;
		__delay_ms(100);

		// Turn all LEDs off and wait for a time delay
		PORTB = 0b00000000;
		__delay_ms(100);
	}
}
		

Compile the program and, if there are any errors, review the steps in the MPLAB tutorial. After programming Output into your CHRP board, the eight LEDs on Port B should be flashing on and off.

How the program works

You might want to refer to the simplified PIC16F886 block diagram and the CHRP 3.01 schematic to help visualize what happens in both the microcontroller and the CHRP circuitry during the following explanations.

Comments

This program, like most programs, starts with some comments. Comments are included in programs to provide additional information for us humans, the programmers.

MPLAB X's XC compilers support both C-style /* commented text */ blocks and C++ style // commented text lines.


/*==============================================================================
	Project: Output
	Version: 3.1 				Date: February 22, 2014
	Target: CHRP3				Processor: PIC16F886

	A simple output program that flashes the LEDs on PORTB of the CHRP3 board
	using the C compiler's built-in millisecond time delay macro.
==============================================================================*/

#include	"xc.h"				// XC general include file

#include	"stdint.h"			// Include integer definitions
#include	"stdbool.h"			// Include Boolean (true/false) definitions

#define	_XTAL_FREQ	4000000		// Set clock frequency for time delays

		

The #include "xc.h" statement is required in all C source files and prepares MPLAB X to support the processor chosen in the project settings by inserting, or including, header (.h) code containing information about the processor into the program.

The next #include statements reference header files that define how MPLAB should handle integer and boolean data. In future programs, we will include our own header or function files in this area.

The #define statement is used to define and assign a value to a symbolic constant. In this case, the C compiler uses the _XTAL_FREQ value to calculate the amount of delay needed when we use one of the compiler's time delay macros.

C functions

Functions are blocks of C-code instructions (and the inevitable curly-brace and semi-colon punctuation required by C) that accomplish a specific task. Functions typically take the form:

return-type function_Name(parameter declarations, if needed)
{
    function code;
    	.
    	.
    return expression;
}
		

The return-type specifies the type of value optionally returned by a function.

The function_Name is self-explanatory, and should logically be a name that provides a reader of the code some insight into what the function accomplishes.

The parameter declarations specify the type(s) of values that are passed to the function.

If the function is to return a value, that value is typically assigned by the return expression, often the last statement in the function.

Processor initialization

initPorts is the first function the C compiler comes across, and serves to prepare the microcontroller's I/O ports to run the program. Even though it comes before the main function in the program, the main function is always the first to execute.

If initPorts executes after main, why put it first? C compilers create code in a single pass and need to be made aware of all of the functions they will encounter. We could include the initPorts function after main, but we would need to add a function prototype here—essentially an empty function declaration line—before the compiler encounters the main function. A function prototype defines all of the returned variables and parameters needed by the function so that the compiler can prepare to compile its code. To simplify our first programs, we'll just add our entire user functions before the main function.


/*==============================================================================
	Initialize ports. This function is called from within main().
==============================================================================*/

void initPorts(void)
{
	// Initialize user ports and peripherals.

	ANSEL = 0b00000000;			// Make all PORTA pins digital I/O
	ANSELH = 0b00000000;		// Make all PORTB pins digital I/O
	OPTION_REG = 0b01010111;	// PORTB pull-ups on, TMR0 internal div 256

	// Set port directions for I/O pins: 0 = Output, 1 = Input

	TRISA = 0b00101111;			// Set runLED, IR LEDs as outputs in PORTA
	TRISB = 0b00000000;			// Set all PORTB pins for LED output

	// Set initial port outputs.

	PORTA = 0;					// Turn all PORTA outputs off, turn Run LED on
}
		

The initPorts function is an entirely self-contained block of code. The void parameter declaration and return-types indicate no information is required by, or returned from this function.

At power-up, the PICmicro hardware initialization leaves all I/O pins in input mode (for safety), with some pins configured for analogue input. The ANSEL and ANSELH registers control which pins are analogue, and which are digital. Writing zeros into these registers disables the analogue circuitry on a bit-by-bit basis (as described in the PIC16F886 data sheet).

The function of the OPTION_REG register includes pull-up resistor, WDT (Watch Dog Timer), and TMR0 (Timer 0) configuration. We'll look at its function more closely in some of the following activities.

As mentioned earlier, the TRISA and TRISB registers control the data direction for each of the Port A and Port B I/O pins. Writing zeros into all of the TRISB bits makes all of the PORTB pins outputs—which is exactly what we want since the PORTB pins connect to the LEDs. Conversely, writing ones into TRIS bits would have made those I/O pins into inputs. Any combination of zeros and ones can be used to set some register pins as inputs and some as outputs. But, for this program, we want all of the PORTB pins to be outputs.

Finally, the PORTA = 0; statement clears (sets the output potential to 0 V) the output pins of Port A.

The main function


/*==============================================================================
	Main program loop. The main() function is called first by the compiler.
==============================================================================*/

int main(void)
{
	// Initialize I/O and peripherals for PORTB output
	initPorts();

	// Make the main program loop repeat continuously
	while(1)
	{
		// Turn all LEDs on and wait for a time delay
		PORTB = 0b11111111;
		__delay_ms(100);

		// Turn all LEDs off and wait for a time delay
		PORTB = 0b00000000;
		__delay_ms(100);
	}
}
		

The main function contains the program code that will be executed first. While it is defined with an int return-type, the main function is expected to be a self-contained, endless loop without any calling parameters.

The first line of main is a function call to the initPorts function and causes it to execute. The empty brackets at the end of the initPorts(); function call indicate that no parameters will be passed to the function.

The while(1) statement follows the initialization function and sets up an infinite loop. It's important for the main code not to exit — if it does, the processor resets and restarts the program. Building the main code into a while loop with an always-true condition makes the code within it execute forever.

Next, curly-brace ({) is important as it is used to contain and group the code within the while loop. PORTB = 0b11111111; sets the Port B outputs (makes all of the pins high, or 5V) using the binary pattern shown. Values can be specified in binary using the 0b prefix as shown, or in decimal (no prefix), hexadecimal (0x prefix), or octal (0 prefix).

__delay_ms(100); is a built-in compiler macro that uses the _XTAL_FREQ definition described above to generate time delay code. It may be shown as an error in the MPLAB X IDE, since it's not a valid C command, but during compile time the XC compiler substitutes the appropriate code to generate the requested delay.

The closing curly braces end the while loop, and the main function.

Program simulation

MPLAB X has a built-in program simulator. The simulator can be used to verify the operation of the Output program before downloading it into your microcontroller. The simulator gives you a virtual peek inside the processor, so you can visually watch what your program code is doing—or, sometimes, not doing! Simulators are also able to time the execution of your program, which becomes very important in many microcontroller applications.

If you have a programmer, connect it and test this program. If not, try to simulate the operation of the program in the MPLAB X simulator.

Test your knowledge

  1. You may want to refer to the simplified PIC16F886 block diagram. The PIC16F886 microcontroller contains more than one type of memory. What type of memory are the file registers made of? What special features do the PORTA, PORTB, and PORTC file registers have?
  2. We refer to registers by name, but the microcontroller refers to registers by their numeric address. What are the addresses of the TRISB and PORTB registers?
  3. What is the function of the TRISA, TRISB, and TRISC registers?
  4. What value would need to be written into the TRISB register to make the bottom six LEDs of the CHRP board into outputs, and the other PORTB pins into inputs?

Apply your skills

  1. Modify the Output program to repeatedly display two or more different light patterns on the LEDs and program it into your CHRP.
  2. The CHRP's motor driver IC can control two DC motors based on the data into the low nybble (the four least significant bits) of PORTB. Use the schematic diagram to help you determine the bit patterns required to make each motor turn forward, reverse, or stop. Connect motors to your CHRP board and verify their operation.