Program description

What does it do?

The Count program demonstrates the use of Port B as a RAM register as well as the use of included, user-created code and header files.

New concepts

At the risk of complicating the understanding of its code, the Count program has been split into four files based on the following logical separation of functions:

  • PIC16F886config.c - Contains chip hardware-related configuration information, exactly as it did in the output program.
  • CHRP3.h - Defines symbolic constants for the CHRP3 family of circuit boards.
  • CHRP3.c - Contains user code functions for CHRP3 port initialization and configuration.
  • Count.c - The home of the main function, similar to the output program.

More complex programs might include even more source code files which could contain functions of specific algorithms (eg. complex math ), or functions to support specialized hardware (eg. LCDs).

Count programming activity

The output program consisted of two files: PIC16F886config.c for processor configuration, and Output.c for the code to flash the LEDs. The Count program code has been broken up into four files for the purpose of simplifying the code by separating it into logical pieces. While creating four source code files complicates the size and structure of the program, the reality is that most of the program files will rarely change. By moving fairly static code out of the Count.c file, the code in Count.c becomes simpler because it only focuses on its main function.

What you should know before starting

Microcontroller related information

In the output programming activity, you learned how to change the output to the Port B LEDs. What may not be apparent when beginning microcontroller programming is that Port B (and its LEDs) is just another RAM register in the chip. And, as RAM, it can be used to hold and manipulate variables just as any other RAM register could.

C coders less familiar with microcontrollers might be tempted to modify Port B as a 'shadow' of an internal register, like this:

		
    while(1)
	{
   		counter ++;
   		PORTB = counter;
	}
		

Due to the limited RAM in microcontrollers, creating a separate counter register is both an extra step, and takes up one or more (depending on the counter variable's type definition) RAM registers. Since PORTB is a RAM register, it's ok to simply increment it.

Create the PIC16F886config.c program

The PIC16F886config.c file is the same as the one used in the output project. 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)
		

Create the CHRP3.h header file

As you did before, follow the steps to create a new file in MPLAB X. This time, make a header file called CHRP3.h, and make sure it is saved into the Header Files section of the project.

		
/*==============================================================================
	CHRP 3.1 (PIC16F886) symbolic constants for user and main functions.
==============================================================================*/

// TODO - Add user constant definitions for user.c and main.c here. Constant
// definitions provide names for all CHRP 3 I/O devices and program constants.

// PORTA I/O device definitions
#define Q1			RA0			// Phototransistor Q1 input
#define Q2			RA1			// Phototransistor Q2 input
#define VR1			RA2			// Potentiometer VR1 input
#define T1			RA3			// Temperature sensor T1 input
#define LED2		RA4			// Run LED, LED2 output
#define RUNLED      RA4         // Run LED, LED2 output
#define LCDBACKLIGHT	RA4		// LCD LED backlight cathode output
#define LCDLED		RA4			// LCD LED backlight cathode output
#define VM			RA5			// Motor supply voltage divider input
#define BEEPER		RA6			// Piezo beeper P1 output
#define P1			RA6			// Piezo beeper P1 output
#define LED12		RA7			// Floor/phototransistor LED (LED12) output

// PORTB I/O device definitions
#define LED10		RB0			// LED10 output (LSB)
#define M1A			RB0			// Motor 1 A output
#define S2			RB0			// Switch S2 input
#define	LED9		RB1			// LED9 output
#define M1B			RB1			// Motor 1 B output
#define S3			RB1			// Switch S3 input
#define LED8		RB2			// LED8 output
#define M2A			RB2			// Motor 2 A output
#define	S4			RB2			// Switch S4 input
#define LED7		RB3			// LED7 output
#define M2B			RB3			// Motor 2 B output
#define S5			RB3			// Switch S5 input
#define LED6		RB4			// LED6 output
#define	S6			RB4			// Switch S6 input
#define LED5		RB5			// LED5 output
#define S7			RB5			// Switch S7 input
#define LED4		RB6			// LED4 output
#define LED3		RB7			// LED3 output (MSB)

// PORTC I/O device definitions
#define H2			RC0			// External I/O header H2
#define LCDE		RC0			// LCD Enable output
#define SERVO1		RC0			// Servo 1 output (external header H2)
#define H3			RC1			// External I/O header H3
#define LCDRW		RC1			// LCD Read/!Write output
#define	SERVO2		RC1			// Servo 2 output (external header H3)
#define	H4			RC2			// External I/O header H4
#define LCDRS		RC2			// LCD Register Select output
#define	SERVO3		RC2			// Servo 3 output (external header H4)
#define H5			RC3			// External I/) header H5
#define SERVO4		RC3			// Servo 4 output (external header H5)
#define LED11		RC6			// Communication/sensor LED, LED11 output
#define IR			RC7			// IR demodulator U5 input
#define U5			RC7			// IR demodulator U5 input

// A-D Converter input channel definitions
#define ADCH0		0b00000000	// A-D channel 0 input
#define	ADQ1		0b00000000	// Q1 phototransistor A-D input channel (Ch0)
#define ADCH1		0b00000100	// A-D channel 1 input
#define	ADQ2		0b00000100	// Q2 phototransistor A-D input channel (Ch1)
#define ADCH2		0b00001000	// A-D channel 2 input
#define	ADVR1		0b00001000	// Potentiometer VR1 A-D input channel (Ch2)
#define ADCH3		0b00001100	// A-D channel 3 input
#define	ADT1		0b00001100	// Temperature sensor T1 A-D input channel (Ch3)
#define ADCH4		0b00010000	// A-D channel 4 input
#define	ADVM		0b00010000	// Motor voltage divider A-D input channel (Ch4)

// Clock frequency for delay macros and simulation
#define _XTAL_FREQ	4000000			// Set clock frequency for time delays
#define FCY			_XTAL_FREQ/4	// Set processor cycle time

void initPorts(void);    // Port configuration function prototype declaration
		

The CHRP3.h file is full of symbolic constants for use in CHRP3.c and Count.c. Symbolic constants are typically defined in UPPER CASE, and in this file constants have been created to represent the names of the components on the CHRP 3 board. The component names can now be used directly in a program, making it easy to check an input, or output to an LED by name, without having to refer to the pin name used on the schematic diagram.

The void initPorts(void); statement at the bottom of CHRP3.h is a function prototype — a statement that defines a function before the compiler read it — for the initPorts function in the CHRP3.c file. Although the CHRP3.h header file does not explicitly include CHRP3.c, MPLAB reads the CHRP3.c file and includes the initPorts function into the Count program.

Create the CHRP3.c program file

Create CHRP3.c in the Source Files section of the project. As you have likely noticed, all .c files are source files, and .h files are header files.

		
/*==============================================================================
	CHRP 3.1 (PIC16F886) hardware initialization and user functions.
==============================================================================*/

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

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

#include	"CHRP3.h"			// Include user-created constants and functions

void initPorts(void)
{
	// TODO - Initialize oscillator, ports, and add (optional) user functions.

	// 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
	TRISC = 0b10110000;			// Set up receive and transmit lines for IR
								// demodulator (U5) and LED11, servo outputs

	// Set starting I/O conditions.

	PORTA = 0;					// Turn off all PORTA outputs, turn on run LED
	PORTB = 0;					// Turn off all PORTB LEDs
	PORTC = 0;					// Turn off all PORTC outputs

	// Enable interrupts, if needed.
}
		

The CHRP3.c file contains the initPorts initialization function. The reason it has been moved to its own file, from the main code that contained it in the output project, is that its code will only be run once, at power-up, to set up the processor up for the rest of our user code in Count. As you'll see next, doing this keeps the Count.c file very compact and easy to read and debug.

Create the Count.c program

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

	A simple binary counting program that demonstrates the use of Port B as a
	register as well as the use of an included header file.
==============================================================================*/

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

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

#include    "CHRP3.h"            // Include user-created constants and functions

/*==============================================================================
	Main program code
==============================================================================*/

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

    while(1)
    {
		// Increment PORTB by one each time through the loop, changing the LEDs.
		PORTB ++;
		__delay_ms(100);
	}
}
		

Compile the Count program and try it in your CHRP board. You should see a binary count cycling through the Port B LEDs.

How the program works

Other than having the code split into multiple files, there are few differences between this program and the previous output project.

When main runs, the first function call is to initPorts, in the CHRP3.c file. After the initPorts initialization code runs, the endless while loop in main increments the Port B register in between time delays. Rather than setting the Port B value using an instruction (as the output code did), the Count program modifies the Port B value each time through the loop.

In C, the statement PORTB ++; simply adds one to a variable register — it's the equivalent of: PORTB = PORTB + 1;. Similarly, PORTB --; could be used to subtract one from a variable register.

Test your knowledge

  1. What do each of the four files that make up the Count program do?
  2. RB0 is defined three times in the CHRP3.h file. Why do you think this is?
  3. What is the maximum count you'll see on the LEDs? How does this relate to the size of the Port B RAM register?
  4. How could you make a counter program count to a larger number than Port B displays?

Apply your skills

  1. Set Port B to the starting number 11110000 before the while loop and let it count up from there. What happens after the count reaches 11111111?
  2. Change the program so that it counts down instead of up. What happens when the count reaches zero?
  3. PORTB is an 8-bit register–with a maximum of 256 states–that can count up to 255. What do you think will happen if numbers larger than one are added to PORTB? Modify the count program to successively add 153 to PORTB and program it into your board. What is the result?