PIC16F84 Fundamentals

Keywords: PIC16F84, Hello World, LED, DIP, timer, serial port, RS232, C programming

The photo shows a PIC16F84 microprocessor board tethered, by ribbon cable, to a circuit having 8 LEDs and an 8-position DIP switch. Learning a microprocessor's capabilities often demands such a setup where LEDs turn on/off, blink at desired rates and respond to switches. This tutorial serves to quickly accelerate your PIC16F84 development ambitions.




Motivation and Audience

An embedded micro like the PIC16F84 is a 18-pin chip that can serve as the "brains" behind your project; the PIC can be outfitted with sensors and decide whether devices like motor and relays should be activated.

Like other micros, the PIC16F84 has a large and loyal following. There are many articles written on it both in print and on-line as well as several books. So why this tutorial? Much of the literature either provides too much or too little information. This tutorial is very focused because its purpose is to rapidly acquaint you with the fundamentals needed to develop PIC16F84-based applications. This is achieved with focused hands-on exercises exploring:

Port I/O, timers and serial communications are the building blocks to construct more complex systems. Supplementing these exercises are links to additional information which can leap frog your future development endeavors. This tutorial makes the following assumptions:

Additionally, it helps to have:

The tutorial breakdown is as follows:

PIC16F84 Development Cycle Overview

The PIC16f84 is an 18-pin 14-bit embedded micro featuring electronically erasable programmable read-only memory (EEPROM). The essential steps are:

  Step 1: On a PC, type the program, successfully compile it and
          then generate the HEX file.
  Step 2: Using a PIC16F84 device programmer, upload 
          the HEX file into the PIC16F84.  This step is often called 
          "burning".
  Step 3: Insert your PIC16F84 into your circuit, power up 
          and verify the program works as expected.  This step
          is often called "dropping" the chip.  If it isn't, 
          you must go to Step 1 and debug your program and 
          repeat burning and dropping.

Embedded micros having EPROM versus those with EEPROM require a fourth step - the program must be erased using ultraviolet light before starting again at Step 1. However the PIC16F84 uses EEPROM and is what makes it popular - the device programmer erases the program without ultraviolet light.

This tutorial assumes you have a PIC16F84 device programmer for Step 2 above. The author personally uses Microchip's (the company that manufactures the PIC16F84) device programmer called PICStart ($199 US or educational discount price of $149). A web search reveals many 3rd party PIC16F84 device programmers ranging in prices from $50 to $200 US). They essentially are similar. PICStart can be purchased at Digikey Part Number: DV003001-ND. For the $149 educational discount phone your order 1-800-DIGIKEY.

PIC16F84 Port Input/Output

The PIC16F84 features two ports named A and B having five and eight digital lines respectively. Any line can be configured to be an input or output. Port I/O exercises is best accomplished by constructing a PIC16F84-based circuit which features a ZIF socket and 0.1 inch headers. The parts list, schematic and construction details follow.

Parts List

US-based vendors include Jameco, Digikey, JDR and Radio Shack. Note: Boondog has no association with these vendors. Attempts were acquire all parts from a single vendor. Part numbers for common resistors are not given.

TABLE 1: PIC16F84 LED/DIP CIRCUIT
PART DESCRIPTIONVENDORPARTPRICE (2002)QTY
PIC16F84-04/PJAMECO1451115.951
40-PIN ZIF SOCKETJAMECO10402910.951
PUSHBUTTON SWITCHJAMECO716421.491
8-POSITION DIP SWITCHJAMECO388420.791
4 MHZ CRYSTAL CLOCK OSCILLATORJAMECO279671.891
0.1 UF CAPJAMECO1511161.00 FOR BAG OF 101
0.1 INCH HEADERSJAMECO1608810.391
SIPP 30-PIN WIREWRAP SOCKETJAMECO1040531.951
T1-3/4 GREEN LEDJAMECO1042560.291
100 OHM RESISTOR1
10 KILO OHM RESISTOR1
220 OHM RESISTOR9
3.3 KILO OHM RESISTOR8
6 INCH PROTOTYPING CIRCUIT BOARDRADIO SHACK276-1702.991
2-3/4 X 3-3/4 PROTOTYPING CIRCUIT BOARDRADIO SHACK276-1582.391

An effort was made to find a single source supplier of all parts. Jameco has every part cited in the table - Neighborhoold Radio Shacks typically have the proto boards in stock and are comparatively cheaper than similiar boards from Jameco.

Construction

Part connection methods and part locations are not critical. The left photo below shows a part layout that works well. The right photo shows a combination of wire wrapping and soldering can be used to construct the PIC16F84 LED/DIP circuit.

Schematic

ledDip042802a.pdf is the Acrobat file of the same schematic. You will need Adobe's free Acrobat reader to view it.

The schematic and constructing the circuit are relatively straight-forward. Some highlights and clarifications towards circuit construction are given next.

ZIF Socket

Repeated burn-and-drops during development often requires removing and re-inserting the PIC16F84. A zero-insertion-force (ZIF) socket eliminates the risk of accidentally bending the PIC's pins with an IC pulling tool. By releasing the ZIF socket's lever, the chip is easily removed by hand. Closing the lever securely fixes the chip in the socket. chip.

LEDs and DIP Switch Circuit

The PIC16F84 has two ports. Port A and B have five and eight digital lines respectively. The PIC circuit features 0.1" headers to rapidly attach or remove connections to these ports. Eight LEDs hook up to Port B and an eight position DIP switch is interfaced to Port A (3 of the DIP's switches aren't used). Ribbon cable is used to tether the PIC and LED/DIP circuit as shown in the photo below with a close up on the right.

Part placement for the eight LEDs, 220 ohm resistors and the 8-position DIP and eight 3.3 KOhm resistors aren't critical. Although only 5 DIP positions are used (because Port A has only five digital lines), all eight were wired. This is in case you'd like to use the DIP to interface with Port B in the future. The following photo shows the layout used

Example 1: helloLed.asm - Turning on LEDs

The following PIC16F84 assembly program works with the PIC and LED/DIP circuits. Eight LEDs are interfaced to the PIC Port B. This simple program turns the LEDs connected to B1, B3, B5 and B7 on while those connected to B0, B2, B4 and B6 remain off.


PIC ASM code
Note: download helloLed.asm rather than cutting and pasting from below. The resulting HEX file helloLed.hex can be burned into the PIC16F84.

; FILE: helloLed.asm
; AUTH: P.Oh
; DATE: 1.0 - 04/13/02 15:09
; DESC: 1.0 - Makes B0,B2,B4,B6 LO and B1,B3,B5,B7 HI
; NOTE: Tested on PIC16F84-04/P.  
;       Page numbers in code are in Easy Pic'n book
; REFs: Easy Pic'n p. 23 (Predko p. 173 is bogus?)

	list	p=16F84
	radix	hex

;----------------------------------------------------------------------
;	cpu equates (memory map)
myPortB	equ	0x06		; (p. 10 defines port address)
;----------------------------------------------------------------------

	org	0x000

start	movlw	0x00		; load W with 0x00 make port B output (p. 45)
	tris	myPortB		; copy W tristate, port B outputs (p. 58)

	movlw   b'10101010'	; load W with bit pattern (p. 45)
	movwf	myPortB		; load myPortB with contents of W (p. 45)

circle	goto	circle	; done

	end

;----------------------------------------------------------------------
; at blast time, select:
;	memory uprotected
;	watchdog timer disabled
;	standard crystal (4 MHz)
;	power-up timer on


The page numbers refer to Easy PIC'n, a book written by David Benson. At first glance, the book can appear difficult to read but it really isn't and I totally recommend it for understanding the assembly language statements given in the above program.

PICStart comes with the PC program MPLAB which can compile the above ASM file, generate the HEX file and allow you to burn the PIC16F84. Once you've burned the PIC16F84, you drop it into the ZIF socket of your PIC circuit, connect the LEDs and turn on the +5V power source. You should see the appropriate LEDs light up.

Example 2: count.asm - Lights up LEDS while counting from 0 to 255

An 8-bit binary number can range from 0 to 255 decimal. A loop can be implemented to increment a counter. The counter's decimal value, if sent to Port B, will light up the binary equivalent on the LEDs. The PIC16F84 assembly code for this follows.


PIC ASM code
Note: download count.asm rather than cutting and pasting from below. The resulting HEX file count.hex can be burned into the PIC16F84.

; FILE: count.asm
; AUTH: P.Oh
; DATE: 04/13/02 17:00
; DESC: Count to 255 at 1 second intervals and display on LED in 
;	binary. pause subroutine takes apx. 0.2 sec, pause repeated 5 times
; NOTE: Tested on PIC16F84-04/P.  
;       Page numbers in code are in Easy Pic'n book
; REFs: Easy Pic'n p. 81

	list	p=16F84
	radix	hex

;----------------------------------------------------------------------
;	cpu equates (memory map)
portB	equ	0x06		; (p. 10 defines port address)
count	equ	0x0c
nCount	equ	0x0d
mCount	equ	0x0e
;----------------------------------------------------------------------

	org	0x000

start	movlw	0x00		; load W with 0x00 make port B output (p. 45)
	tris	portB		; copy W tristate, port B outputs (p. 58)
	clrf	portB		; clear all lines low
	clrf	count		; initialize counter to 0
get_cnt	movf	count, w	; move count to W
	movwf	portB		; move W to port B
	call	pause		; delay by subroutine
	call	pause		
	call	pause
	call	pause
	call	pause		; five pause executions equals ~ 1 second
	incf	count, f	; increment counter
	goto	get_cnt		; repeat forever

pause	movlw	0xff		; set w = 255 decimal
	movwf	mCount		; mCount = w
loadN	movlw	0xff		; set w = 255 decimal
	movwf	nCount		; nCount = w
decN	decfsz	nCount, f	; nCount--
	goto	decN		; if nCount != 0 then repeat nCount--
	decfsz	mCount, f	; else decrement Count
	goto	loadN		; if mCount != 0 then 
				;   reload nCount to 255 and decrement
	return			; else exit subroutine

	end

;----------------------------------------------------------------------
; at blast time, select:
;	memory uprotected
;	watchdog timer disabled
;	standard crystal (4 MHz)
;	power-up timer on


Example 3: syLed.c - Uses C to light up LEDS while counting from 0 to 255

CCS is a company that sells a C compiler for the PIC. The following C equivalent for count.asm is given below:


CCS C code
Note: download syLed.c rather than cutting and pasting from below. The resulting HEX file syLed.hex can be burned into the PIC16F84.

#include <16F84.H>

#use delay(clock=4000000) // 4 MHz OSC

main() {
  int i;
  for (i=0;i<256;i++) {
	output_b(i);    // Send i to Port B thus lighting LEDs.
	delay_ms(100);  // 100 msec delay
  }
}


To compile type at the DOS prompt: CCSC -fm syLed.c. Successful compiles will generate syLed.hex. Under MPLAB select FILE - IMPORT - Download to Memory. You can then enable your device programmer and burn the HEX file into your PIC16F84.

Example 4: helloDip.asm - reads DIP switch to light appropriate LEDs

Using Port A, a decimal number ranging from 0 to 31 can be represented in binary with the DIP switch. The programming objective is to read the DIP position and then light up the binary equivalent on the LEDs. For example setting the first five DIP positions high places 11111 in binary (31 in decimal) on Port A. Your program reads this and lights up LEDs 0, 1, 2, 3 and 4 leaving LEDs 5, 6 and 7 off. The PIC Assembly program that does this follows.


PIC ASM code
Note: download hellodip.asm rather than cutting and pasting from below. The resulting HEX file hellodip.hex can be burned into the PIC16F84.

; FILE: helloDip.asm - WORKS!
; AUTH: P.Oh
; DATE: 1.0 - 04/13/02 16:30
; DESC: Read Port A DIP switch and display on Port B LEDs
; NOTE: Tested on PIC16F84-04/P.  
;       Page numbers in code are in Easy Pic'n book
; REFs: Easy Pic'n p. 60

	list	p=16F84
	radix	hex

;----------------------------------------------------------------------
;	cpu equates (memory map)
myPortA	equ	0x05
myPortB	equ	0x06		; (p. 10 defines port address)
;----------------------------------------------------------------------

	org	0x000
start	movlw	0xFF		; load W with 0xFF make port A input (p. 45)
	tris	myPortA		; copy W tristate to port A outputs (p. 58)

	movlw	0x00		; load W with 0x00 make port B output
	tris	myPortB		; copy W tristate to port B

	movf	myPortA, w	; read port A DIP and store in W
	movwf	myPortB		; write W value to port B LEDs

circle	goto	start		; loop forever

	end

;----------------------------------------------------------------------
; at blast time, select:
;	memory uprotected
;	watchdog timer disabled
;	standard crystal (4 MHz)
;	power-up timer on


Example 5: timer1_0 - blink an LED at a desired rate

Example 1's LED/DIP circuit can be used to experiment with timing routines. The programming objective is to blink the LED attached to line RB0 on Port B every 32.8 milliseconds. The ASM code follows:


PIC ASM code
Note: download timer1_0.asm rather than cutting and pasting from below. The resulting HEX file timer1_0.hex can be burned into the PIC16F84.

; FILE: timer1_0.asm - WORKS!
; AUTH: P.Oh
; DATE: 1.0 - 04/14/02 16:00
; DESC: 1.0 - Internal timer, blink LED every 32.8 msec
; NOTE: Tested on PIC16F84-04/P.  
;       Page numbers in code are in Easy Pic'n book.
;	4 MHz crystal yields 1 MHz internal clock frequency.
;	"option" is set to divide internal clock by 256.
;	This results in 1 MHz/256 = 3906.25 Hz or 256 usec.
;	tmr0 bit 7 (128 decimal) is checked, thus yielding
;	128*256 usec = 32.8 msec delay loop
; REFs: Easy Pic'n p. 113

	list	p=16F84
	radix	hex

;----------------------------------------------------------------------
;	cpu equates (memory map)
portB	equ	0x06		; (p. 10 defines port address)
count	equ	0x0c
tmr0	equ	0x01
;----------------------------------------------------------------------

	org	0x000
start	clrwdt			; clear watchdog timer
	movlw	b'11010111'	; assign prescaler, internal clock
				; and divide by 256 see p. 106
	option
	movlw	0x00		; set w = 0
	tris	portB		; port B is output
	clrf	portB		; port B all low
go	bsf	portB, 0	; RB0 = 1, thus LED on p. 28
	call	delay
	bcf	portB, 0	; RB0 = 0, thus LED off
	call	delay
	goto	go		; repeat forever

delay	clrf	tmr0		; clear TMR0, start counting
again	btfss	tmr0, 7		; if bit 7 = 1
	goto	again		; no, then check again
	return			; else exit delay

	end
;----------------------------------------------------------------------
; at blast time, select:
;	memory uprotected
;	watchdog timer disabled
;	standard crystal (4 MHz)
;	power-up timer on


Serial Communications

The PIC16F84 has no serial port but with some hardware and programming, PIC-to-PC serial communication can be established. Typically the PC's serial port has a DB9 male connector. The photo shows a MAX233 chip, serial cable, PIC and LED/DIP circuits. The PC run a terminal program like Windows' Hyperterminal. The PIC can send or receive 8-bit values at prescribed intervals (baud rate). The PIC programs rcv1_1.asm and ser2_0.c demonstrate the PIC receiving and sending bytes serially.


Hardware Circuit

The additional parts needed for establishing PIC16F84-to-PC serial communications are given in Table 2 below:

TABLE 2: PIC16F84-to-PC SERIAL CIRCUIT
PART DESCRIPTIONVENDORPARTPRICE (2002)QTY
DB9 RIGHT ANGLE FEMALE CONNECTORJAMECO1049510.551
SERIAL CABLE MALE/FEMALE DB9RADIO SHACK26-117B9.991
MAX233CPP RS-232 DRIVER/RECEIVERJAMECO1061634.951
20-PIN WIRE WRAP SOCKETJAMECO1691481.091
0.1 INCH HEADERSJAMECO1608810.391
419 HOLE PROTOTYPING CIRCUIT BOARDRADIO SHACK276-1501.191

serial042802a.pdf is the Acrobat file of the same schematic. You will need Adobe's free Acrobat reader to view it.

Part connection methods and part locations are not critical. The schematic shows the Maxim chip (MAX233) and a female DB9 connector in relation to the PIC16F84 circuit you've constructed previously. The MAX233 chip and female DB9 can be placed on a separate protoboard. This board connects to your PC via a serial cable. Because a PC has a male DB9 connector, the cable must have one end female and the other male (e.g. Radio Shack Part #26-117B). This cable is a straight-through cable and is not null-modem styled. In other words the cable's transmit (TD) and receive (RD) pins are the same at both ends. Note: Here's a schematic using the MAX-232 (Jameco # 24811 $1.95 16-pin part) which is a MAX233 but requires four capacitors.

The photos below show the serial cable's male end plugging into the MAX233 circuit's female connector. The cable's other end would plug into your PC's serial port.

You'll note that a female DB9 connector has two rows of pins that are off-centered; it can't be inserted in a standard 0.1 inch protoboard. To overcome this, you can cut a slot in the protoboard with a Drexel tool The photo belows shows an inserted DB9 connector (seen inside the red circle) which is secured to the protoboard with two screws.

Example 6: PC-to-PIC serial communication

Each alphanumeric key on a PC is uniquely represented by an 8-bit number. For example, upper case "A" ( the SHIFT and "a" keys) is represented by the number 65 (01000001 binary). 97 represents "a", the lower case key. An ASCII table defines keys and their values.

In this example, each key you type on the PC will be sent to the PIC16F84 serially. The PIC16F84 then displays the binary equivalent of the ASCII value on the 8 LEDs. The photo below shows the hardware setup (PC not shown) with the PIC assembly code following. Note: PIC16F84 pin RA0 receives (RD) serial data. This pin connects to the MAX233's R1OUT (pin 3). Because of the straight-through serial cable, this ultimately connects the PC's TD pin 3 of the DB9 (male or female).


PIC ASM code
Note: download rcv1_1.asm rather than cutting and pasting from below. The resulting HEX file rcv1_1.hex can be burned into the PIC16F84.

; FILE: rcv1_1.asm
; AUTH: P.Oh
; DATE: 04/27/02 18:00 1.0 - WORKS
;	04/27/02 18:35 1.1
; DESC: 1.0: PC-to-PIC serial communications.  LEDs display binary equivalent
;            of key typed on PC
;       1.1: Same as 1.0 but eliminates need for switch
; REFS: rcv4800.asm in PIC'n Techniques p. 219

;--------------------------------------------------------------------------
	list	p=16f84
	radix	hex
;--------------------------------------------------------------------------
; 	CPU EQUATES

tmr0	equ	0x01	; Timer/counter register
status	equ	0x03	; Status word register. See Easy PIC'n p. 145
portA	equ	0x05	; Port A register
portB	equ	0x06	; Port B register
intCon	equ	0x0b	; Interrupt control register
rcvReg	equ	0x0c	; General purpose register
count	equ	0x0d	; General purpose register
temp	equ	0x0e	; General purpose register
optReg	equ	0x81	; File register in Bank 1
trisA	equ	0x85	; File register in Bank 1. See Easy PIC'n p. 145
trisB	equ	0x86	; File register in Bank 1. See Easy PIC'n p. 145
;--------------------------------------------------------------------------
; 	BIT EQUATES
rp0	equ	5
;--------------------------------------------------------------------------
	org	0x000

start	bsf 	status, rp0	; Switch to Bank 1. See Easy PIC'n p. 145
	movlw	b'00000101'	; A0, A2 are input and the rest are output
	movwf	trisA
	movlw	b'00000000'	; Port B: all output
	movwf	trisB
	bcf	status, rp0	; Switch back to Bank 0
	clrf	portB
	clrf	rcvReg
; switch	btfsc	portA, 2	; Is A2 equal 0? i.e. is switch closed?
; 	goto	switch		; No, so keep checking
doThis	call	rcv4800		; Yes, to serial in subroutine
	movf	rcvReg,  w	; Get byte received
	movwf	portB		; Display byte on the 8 LEDs
circle	goto	doThis		; Done
;--------------------------------------------------------------------------
rcv4800	bcf	intCon, 5	; Disable tmr0 interrupts
	bcf	intCon,	7	; Disable global interrupts
	clrf	tmr0		; Clear timer/counter
	clrwdt			; Clear wdt prep prescaler assign
	bsf	status, rp0	; to page 1	
	movlw	b'11011000'	; set up timer/counter
	movwf	optReg
	bcf	status, rp0	; Back to page 1
	movlw	0x08		; Init shift counter
	movwf	count
sbit	btfsc	portA, 0	; Look for start bit
	goto 	sbit		; For Mark
	movlw	0x98		; 
	movwf	tmr0		; Load and start timer/counter
	bcf	intCon, 2	; Clear tmr0 overflow flag
time1	btfss	intCon, 2	; Has the timer (bit 2) overflowed?  Skip next line if 1
	goto	time1		; No
	btfsc	portA, 0	; Start bit still low?
	goto 	sbit		; False start, go back
	movlw	0x30		; real, define N for timer
	movwf	tmr0		; start timer/counter - bit time
	bcf	intCon, 2	; Clear tmr0 overflow flag
time2	btfss	intCon, 2	; Timer overflow?
	goto	time2		; No
	movlw	0x30		; Yes, define N for timer
	movwf	tmr0		; Start timer/counter
	bcf	intCon, 2;	; Clear tmr0 overflow flah
	movf	portA, w	; Read port A
	movwf	temp		; Store
	rrf	temp, f		; Rotate bit 0 into carry flag
	rrf	rcvReg, f	; Rotate carry into rcvReg bit 7
	decfsz	count, f	; Shifted 8?
	goto	time2		; No
time3	btfss	intCon, 2	; Timer overflow?
	goto	time3		; No
	return			; Yes, byte received
;-----------------------------------------------------------------------
	end
;-----------------------------------------------------------------------
; At blast time, select:
; 	memory unprotected
; 	watchdog timer disabled
;	standard crystal (4 MHz)
; 	power-up timer on
;=======================================================================


To code makes the PIC16F84 receive serially at 4800 baud, 8 bits, no parity and one stop bit (8N1). With your hardware setup and the PIC16F84 circuit powered up, launch the Windows Hyperterminal program on your PC. Configure Hyperterminal for the desired COM port at 4800 baud and 8N1. If you type the SHIFT and "A" keys, then only two LEDs should light. Recall that uppercase "A" is 65 decimal (01000001 binary).

Example 7: PIC-to-PC serial communication

In this example, the PIC16F84 is programmed to continuously send ASCII values ranging from 65 to 122 serially. This will display characters "A" to "z" on your Windows Hyperterminal. The PIC16F84 code below is written in CCS C. This compiler features functions to handle serial I/O, enabling must shorter programs to be written.


CCS C code
Note: download ser2_0.c rather than cutting and pasting from below. The resulting HEX file ser2_0.hex can be burned into the PIC16F84.

// FILE: SER2_0.C
// AUTH: P.OH
// DATE: 04/27/02 22:45 - 1.0 works
//       04/28/02 23:50 - 2.0 started 
// VERS: 1.0 - Attempts serial com between PC and PIC
//       2.0 - PIC sends message to PC serially

#include <16F84.H>

#define RS232_XMIT	PIN_A1
#define RS232_RCV	PIN_A0	// PIC line which receives PC transmission

#use delay(clock=4000000)	// 4 MHz OSC
#use rs232(baud=9600, xmit=RS232_XMIT, rcv=RS232_RCV)

main() {

  int i;
  
  while(1) {
     i = 65;  // Recall ASCII 'A' is 65
     do {
         putc(i);  
         delay_ms(1000); // send characters every 1 sec
         i++;
     } while (i<=122); 	 // Recall ASCII 'z' is 122
  }
} // end of main


The above CCS C program operates at 9600 baud. Powering up the PIC16F84 circuit and launching Hyperterminal at 8N1 9600 baud, you'll see the alphabet being displayed. Note: The PIC16F84's RA1 line serially transmits (TD) and connects to the MAX233's T1IN (pin 2). Because of the straight-through serial cable, this ultimately connects to the PC's RD (pin 2 on DB9 male or female).

Where To Go From Here

The PIC16F84 features EEPROM and two I/O ports. Using either PIC Assembly or a C compiler like that sold by CCS, enables you to develop embedded micro applications. This tutorial illustrated fundamental by focusing on port I/O, timer and serial examples. Such examples are building blocks towards more interesting applications like constructing robots or interfacing GPS units. Towards such efforts an annotated list of references follows. Happy building!

References

In print Web

PIC Models