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.
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:
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.
PART DESCRIPTION | VENDOR | PART | PRICE (2002) | QTY | |
PIC16F84-04/P | JAMECO | 145111 | 5.95 | 1 | |
40-PIN ZIF SOCKET | JAMECO | 104029 | 10.95 | 1 | |
PUSHBUTTON SWITCH | JAMECO | 71642 | 1.49 | 1 | |
8-POSITION DIP SWITCH | JAMECO | 38842 | 0.79 | 1 | |
4 MHZ CRYSTAL CLOCK OSCILLATOR | JAMECO | 27967 | 1.89 | 1 | |
0.1 UF CAP | JAMECO | 151116 | 1.00 FOR BAG OF 10 | 1 | |
0.1 INCH HEADERS | JAMECO | 160881 | 0.39 | 1 | |
SIPP 30-PIN WIREWRAP SOCKET | JAMECO | 104053 | 1.95 | 1 | |
T1-3/4 GREEN LED | JAMECO | 104256 | 0.29 | 1 | |
100 OHM RESISTOR | 1 | ||||
10 KILO OHM RESISTOR | 1 | ||||
220 OHM RESISTOR | 9 | ||||
3.3 KILO OHM RESISTOR | 8 | ||||
6 INCH PROTOTYPING CIRCUIT BOARD | RADIO SHACK | 276-170 | 2.99 | 1 | |
2-3/4 X 3-3/4 PROTOTYPING CIRCUIT BOARD | RADIO SHACK | 276-158 | 2.39 | 1 | |
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.
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.
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
; 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.
; 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
#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.
; 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
; 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
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.
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).
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).
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).
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:
PART DESCRIPTION VENDOR PART PRICE (2002) QTY DB9 RIGHT ANGLE FEMALE CONNECTOR JAMECO 104951 0.55 1 SERIAL CABLE MALE/FEMALE DB9 RADIO SHACK 26-117B 9.99 1 MAX233CPP RS-232 DRIVER/RECEIVER JAMECO 106163 4.95 1 20-PIN WIRE WRAP SOCKET JAMECO 169148 1.09 1 0.1 INCH HEADERS JAMECO 160881 0.39 1 419 HOLE PROTOTYPING CIRCUIT BOARD RADIO SHACK 276-150 1.19 1
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.
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
;=======================================================================
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.
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
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