Você está na página 1de 225

USING PIC®

-...r- MICROCONTROLLERS

FROM

David Benson
Tirne'n and Count'n

USING PIC® MICROCONTROLLERS

FROM

DAVID BENSON

VERSION 2.0
NOTICE

The material presented in this book is for the education and


amusement of students, hobbyists, technicians and engineers.
Every effort has been made to assure the accuracy of this infor-
mation and its suitability for this purpose. Square 1
Electronics and the author assume no responsibility for the
suitability of this information for any application nor do we
assume any liability resulting from use of this information. No
patent liability is assumed for use of the information contained
herein.

All rights reserved. No part of this manual shall be reproduced


or transmitted by any means (including photo copying) without
written permission from Square 1 Electronics and the author.

Copyright © 1999 and 2002 David Benson

TRADEMARKS

Registered trademarks of Microchip Technology, Inc :

PIC
MPLAB
MPASM
MPSIM

PUBLISHER
Square 1 Electronics
P.O. Box 501
Kelseyville,CA 95451 U.S.A.

Voice (707)2 79-8881


FAX (70 7)279-8883
EMAIL sqone@pacific .net
http://www.sq-1.com
USING PIC® MICROCONTROLLERS

FROM

PREFACE 1

INTRODUCTION 3

TEST EQUIPMENT FOR TIMING AND COUNTING EXPERIMENTS 4

Test equipment for detecting a short single pulse


and measuring it's width 4
Pulse generator - 32 microseconds 10
Pulse generator - 128 microseconds 11
Pulse generator - 2000 microseconds 11
Pulse generator - 65280 microseconds 14
Test equipment for generating a frequency output 17

USING TIMER 1, TIMER 2 AND THE CAPTUREICOMPARElPWM (CCP) MODULE 20

PIC16F870 test circuit 20


Before we take off 25
TMR2: 8-bit timer 26
TMR2 description 26
How to choose/select mode of operation 27
Interrupts 27
TMR2 applications 28
Free running mode (via TMR2 interrupt) 28
Free running mode (via period register) 32
TMR1: 16-bit timer/counter and capture/compare module 36
TMR1/CCP module simplified 36
TMR1 description 38
CCP module - capture mode 40
CCP module - compare mode 41
CCP module - PWM mode 41
CCP1 pin 42
How to choose/select mode of operation 42
T1CON register
CCP1CON register
Interrupts 43
Reading and writing TMR1 43
Long time intervals 44
Controlling the CCP1 pin in compare mode 44
More than one way to do timing stuff 45
Timing and counting experiments 46
Free running output (via TMR1 interrupt) 46
Event counting (via TMR1) 50
Count events, read TMR1 contents 50
Capture TMR1 count when external event occurs 53
Count events up to predetermined number and
generate an output (compare) 57
Single time interval output (via TMR1 and CCP,
compare mode) 61
Start TMR1, auto end 61
500 milliseconds 61
128 microseconds 65
Free running output (via TMR1 and CCP, compare mode) 67
Clear TMR1 each cycle 67
Free running TMR1, add interval value to
compare register each cycle 71
Time measurement (period/interval/time between events)
(via TMR1 read) (interval input via port pin) 76
(via TMR1 and CCP, capture mode) (interval input
via CCP1 pin, two captures, subtract) 81
Frequency measurement (via TMR1, gate via TMRO) 87
External 32,768 Hz watch crystal-based clock - TMR1 93
Pulse width modulation (PWM) using TMR2 and the CCP module 101
Analog output - increase/decrease buttons - PWM -
8-bit mode 105
Duty cycle - 10-bit mode 110
Analog output - increase/decrease buttons - PWM -
10-bit mode 118

DESIGNING AND BUILDING YOUR OWN TEST EQUIPMENT 124

Keypad/LCD user interface 124


5-digit decimal to 16-bit binary entry program 126
Using the 5-digit decimal to 16-bit binary
entry program 136
Digital pulse generator 148
Digital frequency generator 148
Time interval measurement instrument 160
Frequency measurement instrument 173
Creating your own combination signal generator and
measurement instrument 184

APPENDIX A· Program listings vs. page number 185

APPENDIX B • PIC16F870 control registers 186

APPENDIX C • '84 on a board 193

APPENDIX D • PIC/LCD schematic and code 195

APPENDIX E· Keypad 207

APPENDIX F • Using the ICD 209

APPENDIX G • Sources 217


PREFACE

This material presented in this book is the timing and counting information which previously
appeared in PIC'n Techniques. Unfortunately, because of trademark issues, it has become
mandatory that we change the titles of four of our books . The old and new titles appear on our
web site. Since the titles are changing, we are using this as an opportunity to move some topics
from one book to another to improve the flow from one topic to the next. Again, the details
appear on our web site. We regret any inconvenience and confusion that results from these
changes.

The more current PIC16F870 is substituted here for the PIC16C63 that was used in some of the
examples in PIC'n Techniques. The F870 has in-circuit debugging (ICD) capability which is
very useful. If you choose to use the lCD, the test/demo circuit must be modified slightly to
make use of Microchip's ICD possible (via ports RB7,6) . The code must be modified slightly
for the same reason. Details appear in Appendix F - Using The ICD.

The examples based on the PIC16F84 remain essentially unchanged. The PIC16F84A is the
current version of this device. I will simply refer to it as the F84. The demonstration circuit
module is called the '84 on a board to be compatible with our previous books which use the
same circuit. Other small PIC ® devices may be used instead if you prefer as long as the code is
adapted as necessary.

Finally, "Thank You" to all the readers who have made suggestions over the years which have
contributed to the refinement of this material.

GENERAL INFORMATION

See our web site at http://www.sq-l.com for updates, for errata, and for downloading the code
in this book at no cost.

1
2
INTRODUCTION

'nme'n and Count'n is an intermediate level book. I am assuming you know all the beginner
information included in Easy PIC'n or Easy MlC1'OCOntrol'n (our beginner books) either from
using the book or from other experience. Some of the circuits used in
PIC'n Up The Pace orMicrocontrol'n Apps are used in this book as well. These circuits are
shown in appendices in the back of this book. Complete explanations may be found in PIC'n Up
The Pace or MiCl'OCOntrol'n A~
The programs included in this book are examples to help you learn. My hope is that you will
study the examples in this book and write your own borrowing from what you see here. If you
want to borrow from the code in this book, it is currently available for downloading at the
Square 1 website (no charge) .

For those of you who already know a little bit about PIC microcontrollers, here is my philosophy
about some of the topics to be covered :

Include files are not used in this book because if you use someone else's include file (this
includes those provided by Microchip), you won't know precisely what's in it and will spend a
lot of time scratching your head because your program isn't working because you didn't pay
attention to what the author of the include file had in mind.

If you write the code, you know what's in it and what it does .

Use of macros and most assembler directives is avoided because they confuse people who are
learning more often than not. If you end up doing a lot of PIC microcontroller programming,
you may find them useful.

My objective is to write code that you can understand rather than try to optimize for minimum
number of program steps and/or for fastest possible execution . Where programs used previously
in this book or borrowed from PIC'n Up The Pace or Microcontrol'n Apps are combined for
use in a more exotic application, I did not rewrite the code to achieve double duty (shared) use
of file registers.

The PIC 16F870 test circuit and code examples in this book are presented as they would be when
using a device programmer to program the F870 . The circuit and code examples may be easily
changed for ICD use per the instructions in Appendix F - Using The ICD.

The F870 is used as the example in this book, however the material is device independent.

I think you will particularly enjoy the test equipment projects which combine the techniques you
will learn in functional equipment. The real fun, however, is to take all this a step further on
your own by using these techniques to design and build PIC microcontroller
based systems for your own applications.

3
TEST EQUIPMENT FOR
TIMING AND COUNTING EXPERIMENTS

TEST EQUIPMENT FOR DETECTING A SHORT SINGLE PULSE AND


MEASURING Irs WIDTH

Since I don't have a storage oscilloscope, I built a PIC microcontroller-based pulse detector
which measures the width of a single pulse . This device can be used to test the output of an
example circuit when a short pulse (4 to 255 microseconds range) is produced. To develop this,
I used two '84 on a board circuits, one to generate a pulse of known length and the other to
detect it and measure (verify) the pulse width . The '84 on a board circuit for experimenting is
presented in Appendix C.

128.asm pgenlsl.asm

PIC16F84A PIC16F84A

8 LEDs
.....r---L...
RBO RAO Port B r--
.. ~

-
680

::! !!:
--
For initial test purposes, the sending '84 generates a 128 microsecond pulse and the receiving '84
counts up in microseconds while the pulse is high and displays the result via LEDs at port B.
Simple! Then the detection/measuring device can be used to look for a short pulse generated by
a PIC16F870 using the techniques we will develop later in this book.

4
The pulse generator (sender) looks like this:

ROO
+5VDC

PIC16F84A
10K .. ~

RB1

The program used to generate the 128 microsecond test pulse is:

Setup
Send

No

Test

No
Count = 128?

5
i=======128.ASM=====================================4/17/02==
itimer/counter demo
single time interval
internal clock
128 microsecond pulse out
i----------------------------------------------------- - - - - - - -
list p=16f84a
__config h'3ffl'
radix hex
j----------------------------------------------------- - - - - - - -
cpu equates (memory map)
tmrO equ OxOl
status equ Ox03
portb equ Ox06
intcon equ OxOb
opt reg equ Ox8l
trisb equ Ox86
j--------------------- -------------------------------- - - - - - - -
bit equates
rpO equ 5
j------ --------------- ----------------- --------------- - - - - - - -
org OxOOO

start bsf status,rpO iswitch to bank 1


movlw b'OOOOOOlO' iport Bin/out
movwf trisb
bcf status,rpO iback to bank 0
bcf portb,O ilow
bcf portb,2 ilow
bcf portb,3 ilow
bcf portb,4 ilow
bcf portb,5 ilow
bcf portb,6 ilow
bcf portb,7 ilow
bcf intcon,2 iclear TMRO interrupt flag
bcf intcon,7 idisable global interrupts
bcf intcon,5 idisable TMRO interrupts
clrf tmrO iclear TMRO
clrwdt iclr WDT prep prescale assign
bsf status,rpO ito bank 1
movlw b'llOlllll' iset up timer/counter
movwf opt reg
bcf status,rpO iback to bank 0
send btfss portb,l iswitch open?
goto send inot yet
clrf tmrO istart timer/c, clr prescaler
bsf portb,O istart pulse
test btfss tmrO,7 ilook for count=128
goto test inot yet
bcf portb,O iend pulse
circle goto circle idone

end
j--------------------------------------------- -------- - - - - - - -

6
iat device program time, select:
code protection off
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
;============================================================

The width of the pulse is actually 129 microseconds because an additional microsecond is need-
ed to clear port B, bit 0 after the timer times out.

The pulse detection/measuring device (receiver) looks like this :

Signal RAO

+SVDC PIC16F84A
SLEDs
10K
.-----1 RA1 Port B
680

~'~l -
No

-.
The object is to count pulses from a source of known frequency (internal instruction clock, 1
MHz) while the incoming pulse to be measured is high. The count will be the pulse width in
microseconds.

This is similar in concept to using an AND gate . An unknown time interval may be measured
by using the time interval signal to gate a second signal of known frequency.

Count Pulses Which Arrive During


Unknown TIme Interval
Known Frequency

Unknown TIme
Interval
TIme Interval = Pulses = Pulses
Frequency PulseslTime

The signal of known frequency will appear at the output of the AND gate only while both sig-
nals are high. The pulses appearing at the output of the AND gate are then counted . The num-
ber of pulses divided by the frequency (multiplied by the period) of the reference signal is the
time interval.

7
The flow chart for the program is:

Setup

No

Yes

No
--r- ?

No
~?

DisplayTMRO At Port B
LEOs

8
The code follows:

;=======PGENTST.ASM=================================4/17/02==
list p=16f84a
__config h'3ff1'
radix hex
j----------------------------------------------------- - - - - - - -
cpu equates (memory map)
tmrO equ Ox01
status equ Ox03
porta equ OxOS
portb equ Ox06
opt reg equ Ox81
trisa equ Ox8S
trisb equ Ox86
j----------------------------------------------------- - - - - - - -
bit equates
rpO equ S
,
e _

org OxOOO

start bsf status,rpO ;switch to bank 1


movlw b'OOOOOOl1' ;port A inputs/outputs
movwf trisa
movlw b'OOOOOOOO' ;port B outputs
movwf trisb
bcf status,rpO ;back to bank 0
clrf portb ;LEDs off

clrf tmrO ;clear TMRO before assign wdt


clrwdt ;clr WDT prep prescale assign
bsf status,rpO ;to bank 1
movlw b' 11011111' ;set up timer/counter
movwf opt reg
bcf status,rpO ;back to bank 0

ready btfss porta, 1 ; ready?


goto ready ;not yet
p_hi btfss porta,O ;monitor signal
goto p_hi
clrf tmrO ;clear TMRO
p_lo btfsc porta, 0 ;monitor signal
goto p_lo
movf tmrO,w ;read timer 0
movwf portb ;display TMRO contents
circle goto circle ; done

end
,
e _

;at device program time, select:


code protection off
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
j================================== =================== =======

9
Code for generating pulses of four different width follows. The code will be used in conjunction
with an '84 on a board to test circuits presented later in the book.

PULSE GENERATOR - 32 microseconds

ABO
+5VDC

PIC16F84A
10K "10'

AB1

;=======32.ASM======================================4/17/02==
;timer/counter demo
single time interval
internal clock
32 microsecond pulse out
j -------------- --------------------------------------- - - - - - - -
list p=16f84a
__config h'3ff1'
radix hex
i----------------------------------------------------- - - - - - - -
cpu equates (memory map)
tmrO equ Ox01
status equ Ox03
portb equ Ox06
intcon equ OxOb
opt reg equ Ox81
trisb equ Ox86
i-------------------------------------------------- --- - - - - - - -
bit equates
rpO equ 5
i---------------------- ------ - --- - ------ -------------- - - - - - - -
org OxOOO

start bsf status,rpO ;switch to bank 1


movlw b'00OOOO10' ;port B in/out
movwf trisb
bcf status,rpO ;back to bank 0
bcf portb,O ;low
bcf portb,2 ;low
bcf portb,3 ;low
bcf portb,4 ;low
bcf portb,5 ;low
bcf portb,6 ;low
bcf portb,7 ;low

10
bcf intcon,2 iclear TMRO interrupt flag
bcf intcon,7 idisable global interrupts
bcf intcon,5 idisable TMRO interrupts
clrf tmrO iclear TMRO
clrwdt iclr WDT prep prescale assign
bsf status,rpO ito bank 1
movlw b'l1011111' iset up timer/counter
movwf opt reg
bcf status,rpO iback to bank 0
send btfss portb,l iswitch open?
goto send inot yet
clrf tmrO istart timer/c, clr prescaler
bsf portb,O istart pulse
test btfss tmrO,5 ilook for count=32
goto test inot yet
bcf portb,O iend pulse
circle goto circle idone

end
;------------------------------------------------------------
iat device program time, select:
code protection off
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
j============================================================

The width of the pulse is actually 33 microseconds because an additional microsecond is needed
to clear port B, bit 0 after the timer times out.

The 33 microsecond pulse can be detected and its width measured using a second '84 on a board
and pgentst.asm as was done for thel28 microsecond example.

PULSE GENERATOR -128 microseconds

See 128.asm presented previously.

PULSE GENERATOR - 2000 microseconds

We will use a PIC16F84 to generate a single pulse


of known length, 2000 microseconds in this case. RBO
Timer 0 in the F84 is an 8-bit counter and won't +5VDC
hold 2000. We will use the prescaler to divide the PIC16F84A
internal clock signal by 8 and let TMRO count to 10K .. ~

250 (250x8=2000). Since TMRO counts up, it RB1


will be loaded with 6 and will roll over when the
count reaches 255 plus 1 generating an interrupt.

11
Main
Program

Clear TMRO Interrupt


Flag

Send

No

Start Count

Port B, Bit 0 HI
Start Pulse

Interrupt
ClearTMRO
Service Interrupt Flag
Routine
~
Port B, Bit 0 LO
End Pulse

~
Return From Interrupt

12
i=======2000.ASM====================================4/17/02==
itimer/counter demo
single time interval
internal clock divided by 8
2000 microsecond pulse out
,._-----------------------------------------------------------
list p=16f84a
__config h'3ff1'
radix hex
,
e _

cpu equates (memory map)


tmrO equ Ox01
status equ Ox03
portb equ Ox06
intcon equ OxOb
opt reg equ Ox81
trisb equ Ox86
;------------------------------------------------------------
bit equates
rpO equ 5
;------------------------------------------------------------
org OxOOO
goto start iskip over location pointed
to by interrupt vector
org Ox004
goto iserv

start bsf status,rpO iswitch to bank 1


movlw b'00000010' iport Bin/out
movwf trisb
bcf status,rpO iback to bank 0
bcf portb,O ilow
bcf portb,2 ilow
bcf portb,3 ilow
bcf portb,4 ilow
bcf portb,5 ilow
bcf portb,6 ilow
bcf portb,7 ilow
bcf intcon,2 iclear TMRO interrupt flag
bsf intcon,7 ienable global interrupts
bsf intcon,5 ienable TMRO interrupts
clrwdt iclr WDT prep prescale assign
bsf status,rpO ito bank 1
movlw b'1l010010' iset up time r/counte r
movwf opt reg
bcf status,rpO iback to ban k 0
send btfss portb, l iswitch ope n ?
goto send inot yet
movlw Ox05 iinc 250 times to rollover
movwf tmrO istart timer/c, clr prescaler
bsf portb,O istart pulse
circle goto circle iwait for interrupt

13
iserv bcf intcon,2 iclear TMRO interrupt flag
bcf portb,O iend pulse
retfie idone

end
;------------------------------------------------------------
iat device program time, select:
code protection off
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
j===================================================== =======

As with the previous example, power up with the send switch closed. Open the switch to send
the pulse. The pulse is too wide to be measured with the F84 pulse detector, so an oscilloscope
is needed .

PULSE GENERATOR - 65280 microseconds

Finally, we will need a program to output a pulse 65280 microseconds wide . This program uses
timer 0 and a file register counter called "count" to count the number of times timer 0 rolls over.
The sequence starts with timer 0 clear and the count register loaded with OxFF. When the num-
ber of pulses fed into timer 0 reaches 65,280, timer 0 rolls over for the 255th time and the count
register contains zero . To look at this another way, the program decrements the count register
each time timer 0 overflows . When the contents of count reaches zero, the game is over and the
number of pulses received by timer 0 is 65,280 .

RBO
+SVDC

PIC16F84A
10K ....

RB1

14
Main
Program

Port B Output Lines Low

Disable Global
Interrupts

Load File Register


Counter OxFF
Send

No

Start Count Interrupt


ClearTMRO
Service
Interrupt Flag
Routine

Port B, Bit 0 HI
Start Pulse
DECFSZ

Clear TMRO Interrupt


Flag

Port B, Bit 0 LO
End Pulse

15
i=======65280.ASM=================================== 4/17/02==
itimer/counte r demo
single time interval
internal clock
65280 microsecond pulse out
;-- - - - -- - - ---- -- - - -- - ---- - - - - -- - -- - --- - - --- - - - - -- - - - - - --- - - --
list p=1 6f84a
__config h'3ff1'
radix hex
;------------- ----- -------------- ----------------------------
cpu equates (memory map)
tmrO equ Ox01
status equ Ox03
portb equ Ox06
intcon equ OxOb
count equ OxOc
opt reg equ Ox81
trisb equ Ox86
;----------------------- - - - - ---------- ----- ------------------
bit equates
z equ 2
rpO equ 5
; ---- - -------------- ----- -------- ----------------------------
org OxOOO
goto start iskip over location pointed
to by interrupt vector
org OxOO4
goto iserv

start bsf status,rpO iswitch to bank 1


movlw b'OOOOOO10' iport B in/out
movwf trisb
bcf status,rpO iback to bank 0
bcf portb,O ilow
bcf portb,2 ilow
bcf portb,3 ilow
bcf portb,4 ilow
bcf portb,5 ilow
bcf portb,6 ilow
bcf portb,7 ilow
bcf intcon,7 idisable global interrupts
bcf intcon,5 idisable TMRO interrupts
clrf tmrO iprep assign
clrwdt iclr WDT prep prescale assign
bsf status, rpO ito bank 1
movlw b' 11011 111' i s e t up timer/counter
movwf opt reg
bcf status,rpO iback t o bank 0
movlw Oxff
movwf count iload c ount er
send btfss portb,l iswitch open?
goto send inot yet
clrf tmrO istart timer/c , cl r prescaler
bsf portb,O istart pulse

16
bsf intcon,7 ;enable global interrupts
bcf intcon,2 ;clear TMRO interrupt flag
bsf intcon,5 ;enable TMRO interrupts
circle goto circle ;wait for interrupt

iserv bcf intcon,2 ;clear TMRO interrupt flag


decfsz count,f ;dec count, = zero?
retfie ;no
bcf portb,O ;yes, end pulse
round goto round ;done, circle

end
;------------------------------------------------------------
;at device program time, select:
code protection off
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
;============================================================

As with the previous example, power up with the send switch closed . Open the switch to send
the pulse. The pulse is too wide to be measured with the F84 pulse detector, so an oscilloscope
is needed .

TEST EQUIPMENT FOR GENERATING A FREQUENCY OUTPUT

An '84 on a board can be used easily to generate a 100 KHz square wave for test purposes. A
simple software delay loop does the job.

REO 1------

PIC16F84A

The flow chart and code follow:

17
Initialize
Again

10J,l.Sec
2

10 Cycles Total

i=======FREQOUT.ASM=================================4/17/02==
itimer/counter demo
100 KHz out
internal clock
i-------------------------------- - -------------------- - - - - - - -
list p=16f84a
__config h'3ff1'
radix hex
;------------------------------------------------------------
cpu equates (memory map)
status equ Ox03
portb equ Ox06
opt reg equ Ox81
trisb equ Ox86
j----------------------------------------------- ------ - - - - - - -
bit equates
rpO equ 5
;------------------------------------------------------------
org OxOOO

start bsf status,rpO iswitch to bank 1


movlw b'OOOOOOOO' iport B outputs
movwf trisb
bcf status,rpO iback to bank 0
movwf portb iall low
again bsf portb,O ibit 0 high
nop
nop

18
nop
nop
bcf portb,O ;output low
nop
nop
goto again

end
;------------------------------------------------------------
;at device program time, select:
code protection off
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
;============================================================

19
USING TIMER 1, TIMER 2
AND
THE CAPTURE/COMPARE/PWM MODULE

TIME'n AND COUNT'n TEST CIRCUIT - TC BOARD

Timer 1 (TMRl), timer 2 (TMR2), and capture/compare/PWM (CCP) modules are found in
many PIC parts . A test circuit based on the PIC 16F870 is shown here as an example you may
use for experiments. I elected to use a 16-pin DIP socket as a connector for port B so that the
keypad board described in "PIC' n Up The Pace" or "Microcontrol'n Apps" (see Appendix E)
can be connected via a 16-conductor ribbon cable with DIP plugs. This scheme requires putting
the LEDs for port B on a separate small board with a 16-pin DIP socket used as a connector.
The LED board is connected for all experiments except those utilizing the keypad .

20
Gnd +SV

T r t T T t I() () ,
DIP Socket
6 432 S o 10!1.: 0.1 EL
L..-.,---r-~----r-..-----r----r-.....,.~-.1 Tesl T T
Point

+SV.--t---1t-t---1_h
==- +SV

Keypad ,---,_~_- _ __e +SV


Column +SV
Pull~

RB7 RB6 RBS RB4 RB3 RB2 RB1 RBO Vdd Vss RC7 RCG RCS RC4

PIC16F870
+SV ~ TOCKI ClKI ClKO CCP1
Vpp RAO RA1 RA2 RA3 RM RAS Vss OSC1 OSC2 RCO RC1 RC2 RC3

L
N.C.

~
"
1000,--_-+_t--I_t--I--it4.--f-----1t----+---.-+-_+-__-+----'
Reset 1 . ~ .. ~ . ~
+sv
.- t--

-
cilp Xtal Cap
Serial Output TMRO Input +SV • Sockets
To PIC/lCD,
Input
i TMR1 Input CCP1
4.0 MHz
All Pullup Resistors 10K CIockOsc • Cap = 22 pI (2 reqd'
Xtal =32.768 KHz

1-i.
N.C.
Sockets to allow removal.
Parts used lor external clock
experiment only

TC BOARD

21
LED BOARD

6800

8LEDs
Port B

I like to use SIP resistor networks (10-pin, 9 resistors bussed) and LED bargraph displays. The
bargraphs I have found have 10 LEDs, so I cut two off with a hacksaw and filed the cut end
smooth. The result is a neat, small board.

The TC board has the following I/O:

• Eight port B pins brought out to a 16-pin DIP socket for use with LED board or
keypad board.
• Three DIP switches with pullup resistors on port B for use with the keypad.
• Connections for TMRO (input), TMR1 (input), and the CCP1 module (input/output).
• Two DIP switches with pullup resistors for use as inputs (RAO, RA2).
• Sockets for a 32.768 KHz crystal and two 22 pF capacitors for use with TMR1 .
• Serial output pin for use with a PIC/LCD circuit (RA1)(see PIC'n Up The Pace or
Microcontrol'nAppsz, and Appendi x D for details).
• Pullup resistors on the unused pins .

On my breadboard, I used the following DIP switch and terminal block assignments/layout. To
help avoid confusion, a list of switch settings will be shown for each experiment so you won't
have to think so hard about which switches should be open and which ones should be closed.

22
,.....- RAO
- RA1

- RA2
~ RA4ITOCKI

- RCOIT10S0
,.....- RC1/T10S1
+SVDC
~ RC2ICCP1
T
10K"~ .. ~

100001

- CCP1

.. ~ .....~_ ............
- . 10K - TMR11npul

- TMROlnput

! '-- Serial Output To PICIlCD


+SVDC Input

Pull Up Lines When Not Being Used


Example: Not Using Any Lines.

DIP Switches

1234567
ococccc

I leave the LED board connected to the TC board for all experiments except those involving the
keypad. This way, I know the port B lines are not left floating. The 3 port B DIP switches are
open except when the keypad is connected.

23
The simple program shown may be used to see if your PIC16F870 circuit comes alive. The
LEDs at port B bits 7, 6, 5,4 should be off and the remaining four should be on.

DIP Switches

1 2 345 6 7
o C 0 C C C C

i=======PGMR1.ASM==========================12/20/01==
list p=16f870
__config h'3f71'
radix hex
;----------------------------------------------------
cpu equates (memory map)
status equ Ox03
porta equ Ox05
portb equ Ox06
portc equ Ox07
trisa equ Ox85
trisb equ Ox86
trisc equ Ox87
adconl equ Ox9f
i----------------------------------------------------
bit equates
rpO equ 5
i----------------------------------------------------
org OxOOO
start bsf status,rpO iswitch to bank 1
movlw b' 00000110' iturn off A/D, port A
movwf adconl
movlw b'OOOOOOOO' iinputs/outputs
movwf trisa
movwf trisb
movwf trisc
bcf status,rpO iback to bank 0
movlw OxOf iload w with bit pattern
movwf portb iload port B with contents of W
circle goto circle idone

end
i-----------------------------------------------------
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal (using 4 MHz osc for test) XT
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
;=====================================================

24
BEFORE WE TAKE OFF

There are some details we need to be aware of.

The register addresses used in the examples are for the PIC16F870. If you plan to use a different
device, check the Microchip data book to be sure the addresses are correct for the device you
intend to use.

The PIC16F870 peripheral control register descriptions are in Appendix B called "Control
Registers" in the back of the book so you can find them easily for reference.

TMRl, TMR2 and the CCP modules are "peripherals" . The list of modules considered "periph-
erals" is device (part number) dependent. Note that timer 0 (TMRO) is not called a peripheral
and that the register bits that control it are not in the peripheral control registers (they are in the
status register).

There is a peripheral interrupt enable (pIE) register and a peripheral interrupt (flag) register
(pIR). Note that there is a peripheral interrupt enable bit in the interrupt control (INTCON) reg-
ister which must be set to allow interrupts from any of the peripherals. Remember also that the
global interrupt enable bit in the INTCON register must be set to allow peripheral interrupts.

Remember that an "interrupt" flag may be polled to determine its status instead of using it to
trigger an interrupt.

The INTCON register in the PIC16F870 is not the same as for the PIC16F84. The difference is
bit 6 which is the peripheral interrupt enable bit in the F870 where as in the F84 it is the EE
write complete interrupt.

The PIC16F870 has two CCP modules designated CCPl and CCP2 . We will use only CCPl
(and will ignore the existence of CCP2) to simplify the learning process .

Finally, the output of the timing and counting examples will, in general, be slightly inaccurate
because of things like a program loop looking for a bit to change state catching it a cycle or two
after it actually occurs or because of program steps taking a few extra instruction cycles. In
most cases, the error will be very small and inconsequential. If these errors are significant in
your particular application, you can analyze the code and compensate by changing the value(s)
loaded into the timer to correct the results . Or you can simply change the values used to correct
the output and forget about analysis (assuming your measuring equipment is more accurate than
a crystal-controlled PIC microcontroller).

25
TIMER 2 (TMR2): 8-BIT TIMER

TMR2 has the following features:

• 8-bit timer.
• Source of pulses is the internal instruction clock (timing applications only).
• Prescaler divides the incoming pulse train by 1,4, or 16.
• Postscaler divides the comparator output by 1,2,3 .... 16
• 8-bit period register (PR2) .
• Comparator to look for TMR2/PR2 match .
• TMR2 may be cleared, written to, or read at any time.
• The period register may be written to or read.
• TMR2 may be used as a general purpose timer.
• TMR2 may be used with the CCP module for PWM.
• TMR2 may be used with the SSP module for baud rate generation.

TMR2 Description

A simplified block diagram ofTMR2 follows:

CCP1 Pin High For CCP PWM


SSP Baud Rate

Reset
TMR2
Internal Prescaler Postscaler Interrupt
Clock
Flag
1,4,6 1,2,3,---,16

TMR2 is incremented by the internal instruction clock (fosc/4) (prescaled) from OxOO until it's
contents match the period register. On the next increment, it is reset to OxOO. For each match of
TMR2 and its period register, a pulse is output to a postscaler. The postscaler output sets the
TMR2 interrupt flag in the PIRI register. The output of the TMR2 module may be :

• Read TMR2.
• TMR2 interrupt flag.

The match output from the comparator (directly) is used to reset TMR2 in PWM applications
(see PWM section), and is used in SSP serial applications (baud rate).

26
The interrupt generated by TMR2 is called both the TMR2 interrupt and the TMR2 match inter-
rupt in the Microchip literature. The term match interrupt implies that an interrupt occurs at
every match, which may not be true depending on the postscaler ratio. We will refer to this
interrupt as the TMR2 interrupt.

TMR2 can be turned on or off via a control bit in the TMR2 control register T2CON.

TMR2 is cleared by any device reset (pOR, BOR, MCLR, WDT).

The prescaler and postscaler are cleared by:

• Write to TMR2.
• Write to T2CON, the TMR2 control register.
• Any device reset (pOR, BOR, MCLR, WDT).

PR2 is filled with l's on reset (full count).

A write to the T2CON register does not clear TMR2 .

How To Choose/Select Mode Of Operation

T2CON register:

Bit(s)

Prescaler select 1,0


Postscaler select 6,5,4,3
TMR2 on/off 2

Interrupts

Enable using PIEl register:

Bit

TMR2 interrupt enable 1

Enable using INTCON register:

Bit

Peripheral interrupt enable 6


Global interrupt enable 7

Flag - PIRI register:

Bit

TMR2 interrupt flag 1

27
TMR2 Application· Free Running Mode (via TMR2 interrupt)

The first example is similar to the TMRO free running mode with internal clock example in
Sug Ple'n or Sug Microcontrol'n.
Use internal clock, prescaler 1:16, postscaler 1:16, period 255.
Time interval 65 milliseconds.
Output to port B, bit o.

PIC16F870

RBO
680

-...

28
Main Interrupt
Clear TMR2 Interrupt
Program Service
Flag
Routine

Disable Global
Interrupts

Disable Peripheral
Interrupts

Clear TMR2 Interrupt


Flag

Set Up Prescaler And


Postscaler, TMR2 Off

Load Period Register


PR2

29
Unimplemented

I Postscaler 1:16

T2CON --------- 1 I0
<c-:
DIP Switches

1 2 345 6 7
o C 0 C C C C
POR = OxOO
L Prescaler 1:16
TMR20ff

i=======TMR2.ASM====================================4/28/02==
list p=16f870
__config h'3f71'
radix hex
;------------------------------------------------------------
itimer 2 demo
free running, time interval 65 milliseconds
internal clock, prescaler 1:16, postscaler 1:16,
period 255
;------------------------------------------------------------
cpu equates (memory map)
status equ Ox03
portb equ Ox06
intcon equ OxOb
pir1 equ OxOc
tmr2 equ Ox11
t2con equ Ox12
trisb equ Ox86
pie1 equ Ox8c
pr2 equ Ox92
adcon1 equ Ox9f
;------------------------------------------------------------
bit equates
rpO equ 5
j----------------------------------------------------- - - - - - - -
org OxOOO
goto start iskip over location pointed to by
interrupt vector
org Ox004
goto iserv

start bsf status,rpO iswitch to bank 1


movlw b'OOOOOOOO' iport B outputs
movwf trisb
movlw b' 00000110 I iturn off A/D, port A
movwf adcon1
bcf status,rpO iswitch back to bank 0
movwf portb iport B lines low
bcf intcon,7 idisable global interrupts
bcf i n t c o n, 6 idisable peripheral interrupts
bsf status,rpO i bank 1
bcf pie1,1 idisable tmr2 interrupt

30
bcf status,rpO ibank 0
bcf pir1,1 iclear tmr2 interrupt flag
movlw b' 01111010' iprescaler and postscaler setup,
movwf t2con tmr2 off
clrf tmr2 iclear timer 2
bsf status,rpO ibank 1
movlw b'llllllll' iperiod=255
movwf pr2
bcf status,rpO ibank 0
bsf intcon,7 ienable global interrupts
bsf intcon,6 ienable peripheral interrupts
bsf status,rpO ibank 1
bsf pie1,1 ienable tmr2 interrupt
bcf status,rpO ibank 0
bsf t2con,2 itimer 2 on
circle goto circle idone

iserv bcf pir1,1 iclear TMR2 interrupt flag,


enable further interrupts
btfss portb,O iport B, bit 0 status?
goto setbit ibit is clear
clrbit bcf portb,O iclear port B, bit 0
retfie ireturn from interrupt
setbit bsf portb,O iset port B, bit 0
ret fie ireturn from interrupt

end
;------------------------------------------------------------
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
;============================================================

Run the program and look at the port B, bit 0 LED.

Example:

with a 4 MHz clock oscillator, the internal instruction clock will have
a frequency of 1 MHz and each cycle will be 1 microsecond in
duration (ie. period = 1 microsecond) .
1 microsecond multiplied by 16 = 16 microseconds between pulses out of
the prescaler.
Count to 255 in TMR2 = 4080 microseconds between pulses out of TMR2.
Multiply by 16 in postscaler = 65,280 microseconds = 65 millisecond
output interval.

1 Jlsec x 16 x 255 x 16 65, 280 Jlsec 65 msec

31
65 msec

This is the time between each HI/LO or LO/HI transition at the port
line. The time to execute the interrupt service routine adds to
this slightly .

When thinking in terms of frequency, the prescaler divides the frequency. When thinking in
terms of period, the prescaler multiplies the period .

TMR2 Application· Free Running (via period register)

This time, we will use the period register and poll (watch) the TMR2 interrupt flag (won't use
interrupts). The prescaler will divide the internal instruction clock by 16 giving 16 microsec-
onds per pulse into TMR2 . The period register PR2 will be loaded with decimal 10. The
postscaler will divide by 10.

16 ~ sec x 10 x 10 1600 ~sec = 1.6 msec time interval

1.6 msec

The prescaler output will increment TMR2 until its contents match the contents ofPR2 (match
occurs) sending a pulse to the postscaler and resetting TMR2 .

Use internal clock, prescaler 1:16, postscaler 1:10, period 10.


Time interval 1.6 milliseconds.
Output to port B, bit O.

PIC16F870

RBO
680

32
Disable Global
Interrupts

Disable Peripheral
Interrupts

c1rbit

- - . l------~
Clear TMR2 Interrupt
Flag

Set Up Prescaler And


Postscaler. TMR2 Off

Load Period Register


PR2

Clear TMR2 Interrupt


Flag

33
Unimplemented

I Postscaler 1:10
DIP Switches

T2CON --------- 1 I0
<c-:
1 2 345 6 7
o C 0 C C C C
POR .. OxOO
L Prescaler 1:16
TMR20ff

i=======TSTTMR2.ASM=========================4/28/02==
itimer 2 demo
free running
internal clock divided by 16 (prescaler)
period value = 10
postscaler 1:10
;--------------------------------- -------------------
list p=16f870
__config h'3f71'
radix he x
;----------------------------------------------------
cpu equates (memory map)
tmrO equ Ox01
status equ Ox03
portb equ Ox06
intcon equ OxOb
pir1 equ OxOc
tmr2 equ Ox11
t2con equ Ox12
trisb equ Ox86
pie1 equ Ox8c
pr2 equ Ox92
adcon1 equ Ox9f
;----------------------------------------------------
bit equates
rpO equ 5
;---- ------------------------------- -----------------
org OxOOO

start bsf status,rpO iswitch to bank 1


movlw b'OOOOOOOO' iport B outputs
movwf trisb
movlw b' 00000110' iturn off AID, port A
movwf adcon1
bcf status,rpO iswitch back to bank 0
movlw b'OOOOOOOO' iall outputs low
movwf portb

34
bcf intcon,7 idisable global interrupts
bcf intcon,6 idisable periphe ral inte r rupts
bsf status,rpO ito bank 1
bcf pie1,1 idisable tmr2 interrupt
bcf status,rpO ito bank 0
bcf pir1,1 iclear tmr2 interrupt flag
movlw b'01001010' iprescaler = 1 :16,
postscaler = 1:10
movwf t2con itmr2 off
clrf tmr2 iclear tmr2
bsf status,rpO ibank 1
movlw d' 10' idecimal 10
movwf pr2 iload period register
bcf status,rpO ibank 0
bsf t2con,2 itmr2 on
t20vfl btfss pir1,1 iwatch tmr2 interrupt flag
goto t20vfl inot yet
bcf pir1,1 iflag set, so clear flag
btfss portb,O iport B, bit 0 status?
goto setbit ibit is clear
clrbit bcf portb,O iclear port B, bit 0
goto t20vfl irepeat
setbit bsf portb,O iset port B, bit 0
goto t20vfl irepeat

end
i-----------------------------------------------------
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset e n a b l e d
lvp disabled
debug mode disabled
;=====================================================

Run the program and look at port B with an oscilloscope. The pulse width should be 1.6 mil-
liseconds pulse a hair for software overhead.

You have a design choice of polling the TMR2 interrupt flag or waiting for an interrupt and ser-
vicing it. Either way, the interrupt flag must be cleared in software each time it pops up.

35
TMR1: 16-81T TIMER/COUNTER AND CAPTURE/COMPARE MODULE

TMR1/CCP Simplified

Timer 1 (TMRl) is a 16-bit timer/counter. The resolution it provides is quite useful. A cap-
ture/compare module may be used in conjunction with timer 1 to do some neat tricks. We will
try a few of them .

First, timer 1 may be used in the typical way be reading its contents or looking at the overflow
flag.

TMR1 - Timer/Counter

>----.t TMR1 Input

TMR1

16-bit

The contents of timer 1 may be captured (grabbed) for use triggered by a signal edge from cir-
cuitry external to the PIC microcontroller.

TMR1 • Capture

..J" TMR1/CCP
or
"""l..

Pulses coming into timer 1 increment the counter. When a capture command signal edge
arrives , the counter contents are captured and an interrupt flag is set. The captured value is
available to be read .

36
The contents of timer 1 may be continuously compared to the contents ofa compare register
which has been loaded previously with some value. When the contents of timer 1 are increment-
ed to a value equal to the contents of the compare register, an output occurs which may be a
level change on the capture/compare module's output pin, an interrupt, or both .

TMR1 - Compare

TMR1/CCP
CCP Pin ....r or L....
(output)

Using the CCP module makes it possible to read timer 1 and store its contents for later use when
a signal edge (external) is detected while the PIC microcontroller is busy doing other things
(capture). Using the CCP module also makes it possible to drive a pin high or low when the
contents of timer 1 are incremented to a predetermined value totally independent from other
tasks the PIC microcontroller may be performing at the moment (compare).

The source of pulses feeding timer 1 may be the internal clock for timing applications. This
allows fine resolution (1 microsecond for 4 MHz clock oscillator). Alternatively, the source of
pulses might be an external clock or it could be a circuit which has a pulse output for an applica-
tion based on counting pulses.

37
TMR1 Description

TMRI used in conjunction with the CCP module has the following features:

• 16-bit timer/ counter consisting of two 8-bit registers.


• Source of pulses either internal instruction clock or external via PIC pin.
• Prescaler divides the incoming pulse train by 1,2,4, or 8.
• TMRI may be cleared or read at any time.
• An edge input via a pin can be used to trigger a read ofTMRI (capture) coupled with
setting an interrupt flag so the PIC microcontroller knows the number read
is available.
• TMRI can be incremented via event or clock pulses until its contents matches a
number previously loaded into a comparator register (compare) triggering an
output via a pin, or setting an interrupt flag, or both.
• A single time interval or frequency output may be generated using a compare
function .

TMRI increments from OxOOOO to OxFFFF and rolls over to OxOOOO.

TMRI may be cleared, read, and turned on or off.

A simplified block diagram ofTMRI (without the CCP module) follows:

Input
Circuit TMR1
Prescaler
TMR1L TMR1H Interrupt
cr
I
Flag
1,2,4,8
Internal
Clock
Special Event
Trigger From
CCP Module

First, note that the source of pulses input to TMRl may be the internal instruction clock (fosc/4)
for timing applications or an external source for either timing or counting applications. The
selection of source is determined by a bit in the Tl CON register. Second, notice that a prescaler
is used to divide the incoming pulse train by 1,2,4, or 8 as determined by two bits in the
TICON register. TMRI may be turned on or off using a bit in the TICON register. TMRI itself
consists of a register pair (8 bits each). On TMRl overflow, a flag bit is set in the PIRI register
and an interrupt occurs if enabled by a bit in the PIEI register.

38
External inputs to TMRI may be one of two types. The most common is via the TICKI pin.

.....rL

T1CKI
Pin

TMR1

When pulses are fed to pin TICKI from an external source, TMRI increments on every rising
edge (exception noted later) on the TICKI pin (divided by the prescaler). This mode of opera-
tion is selected by setting the TMRICS bit in the TICON register. When external pulses are fed
into TMRI via the TICKI pin (port C, bit 0), the pin must be configured as an input.

We will use only what is called the synchronous mode for our examples and this is selected by
clearing the TISYNC bit in the TICON register.

You won't need to concern yourself with this now, but there are some external clock input timing
specs (synchronous counter mode) to be aware of.

Prescaler 1:1.

The external input at the T1CKI pin must be high for at least 2 Tos c and
low for at least 2 Tos c where Tos c is the period of the external clock
oscillator.

Prescaler other than 1:1

The external input at the T1CKI pin must be high or low for at least
4 Tos c divided by the prescaler value Also, the minimum pulse width
requirements called out in the "Electrical Specifications" in the
Microchip data book must not be violated.

39
The other circuit possibility is a crystal clock oscillator created by connecting a crystal between
pins TIOSI and TIOSO. The oscillator may operate up to 200 KHz. The primary application is
real-time using a 32.768 KHz crystal (details later).

PIC16F870

nCON Bit3

T10SCI T10SCO

32.768 KHz D
22pl J J 22pl

CCP1 Module· Capture Mode

The CCPI module uses the CCPI pin as its input.

CCPR1 CCPR1H CCPR1L

CCP1 Pin
Edge Detect Prescaler
(Input)

Rising 1,4,16
vs.
Falling TMR1 TMR1H TMR1L

The I6-bit contents ofTMRI is captured when one of the following events occurs on pin CCPI :

• Every falling edge (exception noted later) .


• Every rising edge (exception noted later).
• Every 4th rising edge.
• Every 16th rising edge.

40
When the selected event occurs, the contents ofTMRl are captured in a capture register and the
CCPl interrupt flag is set (flag must be cleared in software). The number captured is available
for whatever use the programmer intends.

CCP1 Module - Compare Mode

The CCPl module uses the CCPl pin as an output.

Special Event
Trigger Clears
Set CCP1 TMR1
Interrupt Flag

CCPR1 CCPR1H CCPR1L

CCP1 Pin
Comparator Logic
(Ouput)

TMR1 TMR1H TMR1L

When the CCPl compare mode is selected :

• TMRI may free-run or count external pulses . TMRI is incremented .


• When a match occurs, the CCPl pin is:

- Driven high.
- Driven low.
- Remains unchanged .

- And the CCPl interrupt flag is set.

• A special event trigger may be used to reset TMRI when a match occurs.
Note that the special event trigger does n.Q1 set the TMRI interrupt flag.

CCP1 Module - PWM Mode

See the PWM section .

41
CCP1 Pin

The CCPl pin must be initialized as an input or output as part of the program. The pin is forced
low when a bit pattern calling for the compare mode is loaded into the CCPICON register.

If the CCPl pin is configured as an output, a write to the port can cause a capture condition.
Therefore, the CCPl interrupt enable bit should be cleared to disable interrupts prior to writing
to the port and the CCPl interrupt flag should be cleared following writing to the port.

CCPR1 LAnd CCPR1 H Register Pair

The CCPRIL and CCPRIH register pair is used as the capture register in the capture mode and
as the compare register in the compare mode. We will refer to this register pair as the capture
register or as the compare register depending on the application.

How To Choose/Select Mode Of Operation

T1CON regi ster:

Bit (s)

Clock source internal/external 1


Oscillator enable control on/off (input circuit) 3
T1SYNC 2
If external source, sync for our applications
Prescaler select 5,4
TMR1 on/off 0

CCP 1CON register:

Bit (s)

CCP1 mode select 3,2,1,0

Module off 0000


Capture, every falling edge 0100
every rising edge 0101
every 4th rising edge 0110
every 16th rising edge 0111
Compare, set output pin on match and 1000
set CCP1 interrupt flag
clear output pin on match and 1001
set CCP1 interrupt flag
software interrupt on match and 1010
set CCP1 interrupt flag
trigger special event on match, 1011
set CCP1 interrupt flag and
clear TMR1
PWM (see PWM section) 11xx

42
Interrupts

Enable using PIEI register:

Bit

TMRl overflow interrupt enable o


CCPl interrupt enable 2

Enable using INTCON register :

Bit

Peripheral interrupt enable 6


Global interrupt enable 7

Flags - PIRI register:

Bit

TMRl overflow interrupt flag o


CCPl interrupt flag 2

Reading And Writing TMR1

IfTMRl can be stopped, reading and writing is simply a mater of reading or writing the high
and low registers (individually). Reading or writing TMRI while it is running is another matter.

Writing To TMRI While It Is Running

First, clear the TMRIL register to ensure that a rollover into the TMRIH register will not occur
soon. Load TMRIH. Then load TMRIL. All interrupts must be disabled while the write opera-
tion takes place and re-enabled afterwards .

clrf tmrll iclear low byte - prevent rollover into high


byte
movlw .... .. idefine value to be loaded into high byte
movwf tmrlh,f iload high byte
movlw ...... idefine value to be loaded into low byte
movwf tmrll,f iload low byte

Reading TMRI While It Is Running

Reading the l6-bit TMRI involves reading two 8-bit values. The danger is that a TMRI over-
flow may occur between reads. If an overflow occurs, it must be detected in software.
Detection of an overflow must cause TMRI to be read a second time to obtain valid data. All
interrupts must be disabled while the read operation takes place and re-enabled afterwards .

43
;subroutine - read timer 1 on-the-fly
movf tmrlh,w ;read high byte
movwf t l hi ;store
movf tmrll,w ;read low byte
movwf tl 10 ;store
movf tmrlh,w ;read high byte again
subwf tl_hi,w ;subtract first read from second read
btfsc status,z ;is result O?
return ;read valid
movf tmrlh,w ;rollover occurred, read high byte again
movwf t l hi ; store
movf tmrll,w ;read low byte
movwf tl 10 ;store
return

Long Time Intervals

Long time intervals may be generated by counting rollovers of timer I .

Controlling The CCP1 Pin In Compare Mode

When using the compare mode, the CCPI pin must be made an output via the TRISC register.
Turning on the CCPI module in the compare mode (by writing to the CCPICON register) forces
the CCPI pin low (no matter what). The version ofMPSIM built in to MPLAB that I am cur-
rently using does not do this, but the hardware does, which is what counts in the end.

This means that if you want to generate a single time interval output via the CCPI pin so as to
take advantage of the "auto off' feature, you will need to use a trick to force the pin high at the
beginning of the time interval. The trick is to load the compare register with the value OxOOOI
and put the CCPI module in the compare mode programmed to force the CCPI pin high on
match which will occur right away (TMRI counts to I) . Then load the compare register with the
value corresponding to the desired time interval and flip the bit in CCPICON which will tell the
CCPI pin to go low on the next match .

This technique will be used in the example programs .

44
More Than One Way To Do Timing Stuff

As usual, there is more than one way to do things. Here are some concepts to stimulate your
thinking about timing applications and the compare mode.

TMRI can be turned on to start a time interval which ends automatically via the CCPI module
operating in the compare mode . This is fine ifTMRI is being used for only the one application
at the moment. The trick for controlling the CCPI pin described earlier must be used .

Compare Mode
J L
Start TMR1 And Auto End
I
Start
I
Auto
TMR1 End

IfTMRI is being shared by more than one function at (the same time), TMRI may be operated
in the free running mode. Generating a time interval begins with reading TMRl, adding the
required time interval to the contents ofTMRl just obtained, and storing the result in the com-
pare register. When TMRI is incremented to that number, an output is automatically generated .

Compare Mode
J L
TMR1 Free Running
1
Read TMR1 .
1
Auto
Add Value To End
Interval And
Store In Compare
Register

The same technique can be used to generate a periodic output.

Compare Mode
TMR1 Free Running
Add To Compare Register To
Create Periodic Waveform
• Use Interrupt Service Routine
• Must Toggle Rise/Fall Bit

The experiments which follow include examples to illustrate these concepts.

45
TIMING AND COUNTING EXPERIMENTS

Free Running Output (via TMR1 Interrupt)

To begin your hands-on familiarization with timer 1, we will start with an example using the
same techniques used previously with timer 0 and timer 2. We will set up TMRI to free run and
use an interrupt service routine to toggle an output pin.

Use internal instruction clock, prescale 1:8, count OxFFFF = 65,536.


Output port B, bit O.
Time interval 8 microseconds x 65,536 = 524 milliseconds.

PIC16F870

RBO
680

-
oN

The code is written in a logical sequence for ease of understanding, especially at the flow chart
level. You can eliminate some bank switching by combining some of the bank 1 steps if you
want to streamline the code .

46
Main Interrupt
Clear TMR1 Interrupt
Program Service
Flag
Routine

Disable Interrupts And


Clear Interrupt Flags

CiearsTMR1
Prescaler

I Unimplemented

I I Prescaler 1:8
DIP Switches
~~
1 234 5 6 7
o C 0 C C C C
TMR10ff
paR = OxOO
Internal Clock
Ignore With
Internal Clock
Oscillator Off

47
;=======TMR1.ASM====================================4/28/02==
list p=16f870
__config h'3f71'
radix hex
j--------------------------------- ------- - --------- --- - - - - - - -
;timer 1 demo
free running, time interval 524 milliseconds
internal clock, prescaler 1:8, count to 65,536
;--------------------------------- ---------------------------
cpu equates (memory map)
status equ Ox03
portb equ Ox06
intcon equ OxOb
pir1 equ OxOc
tmr1l equ OxOe
tmr1h equ OxOf
t1con equ Ox10
trisb equ Ox86
pie1 equ Ox8c
adcon1 equ Ox9f
j----------------------------------------------------- - - - - - - -
bit equates
rpO equ 5
;-------------------------------------- ----------------------
org OxOOO
goto start ;skip over location pointed to by
interrupt vector
org Ox004
goto iserv

start bsf status,rpO ;switch to bank 1


movlw b'OOOOOOOO' ;port B outputs
movwf trisb
movlw b' 00000110 I ;turn off A/D, port A
movwf adcon1
bcf status,rpO ;switch back to bank 0
movwf portb ;port B lines low
bcf intcon,7 ;disable global interrupts
bcf intcon,6 ;disable peripheral interrupts
bsf status,rpO ;bank 1
bcf pie1,0 ;disable tmr1 interrupt
bcf status,rpO ;bank 0
bcf pir1,0 ;clear tmr1 interrupt flag
movlw b' 00110000' ;prescaler and tmr1 setup,
movwf t1con tmr1 off
clrf tmr1h ;clear timer 1 high
clrf tmr1l ;clear timer 1 low, clear prescaler
bsf intcon,7 ;enable g lobal i n t er r up t s
bsf intcon,6 ;enable peripheral inter rupts
bsf status,rpO ;bank 1
bsf pie1,0 ;enable tmr1 interrupt
bcf status,rpO ;bank 0
bsf t1con,0 ;timer 1 on
circle goto circle ; done

48
iserv bcf pi rl,O iclear TMRl interrupt flag,
enable further interrupts
bt fss portb,O iport B, bit 0 status?
goto setbit ibit is clear
clrbit bcf portb,O iclear port B, bit 0
nop iequalize paths
retfie ireturn from interrupt
setbit bsf portb ,O iset port B, bit 0
retfie ireturn from interrupt

end
;----------------------- -------------------------------------
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
i===================================================== =======

PULSER CIRCUIT

..n..

Momentary 3300
Contact

T =300C =161l5ec

+5V
74HC14

Clear ....r "1...


0 1/2 0 Output
74HC74
-lL
Clock

0
Set

+5V

49
Event Counting (via TMR1)

• Count Events, Read TMR1 Contents On-The-Fly

For this example, the contents of timer 1 will be displayed continuously using a program loop.
A pulser is used to inject pulses into TMRI .

T1CKI

PIC16F870
SLEDs
Port B

Timer 1 is supposed to increment on every rising edge. Timer 1 has a "weird" in that what con-
stitutes the first rising edge is determined by the initial logic level of the TICKI pin when timer
1 is turned on. If it is high initially (on reset with this experimental setup), the count indeed will
begin on the first rising edge (increment on every rising edge).

Count = 1

!
U-
On Reset J t
1st Rising
Edge

50
If the level at TICKI is low on reset, the first rising edge won't cause TMRI to increment (i.e.
not every rising edge will be counted).

Count = 1

!
_-----In I
OnResot J i
2nd Rising
Edge

This can prove to be a "gotcha" if you are not aware of it.

.
Port Setup

Disable Interrupts And


Clear Interrupt Flags

~
CCP1 Module Off,
Clear CCP1 Prescaler

~
TMR1 Setup, TMR1 Off

i CiearsTMR1
CiearTMR1
Prescaler
~
TMR10n

......
,r
ReadTMR1
Register Low

+
Display Number
At Port B

51
I Unimplemented

I I Prescaler 1:1
DIP Switches
~~
1234567
T1CON
o C 0 C 0 C C
TMR10ff
POR= OxOO
External Clock
Sync External
Clock Input
Oscillator Off

i=======READ.ASM====================================4/28/02==
list p=16f870
__config h'3f71'
radix hex
i------------------------------------------------------------
itimer 1 - read event count on-the-fly demo
;------------------------------------------------------------
cpu equates (memory map)
status equ Ox03
portb equ Ox06
intcon equ OxOb
pirl equ OxOc
tmrll equ OxOe
tmrlh equ OxOf
tlcon equ OxlO
ccplcon equ Ox17
t r i sb equ Ox86
trisc equ Ox87
piel equ Ox8c
adconl equ Ox9f
;------------------- -----------------------------------------
bit equates
rpO equ 5
i------------------------------------------------------------
org OxOOO

start bsf status,rpO iswitch to bank 1


movlw b'OOOOOOOO' iport B outputs
movwf trisb
bsf trisc,O iport C, bit 0 timer 1 input
movlw b'OOOOOllO' i turn off A/D, port A
movwf adconl
bcf status,rpO iswitch back to bank 0
movlw b'OOOOOOOO' iport B lines low
movwf portb
bcf intcon,7 idisable global interrupts
bcf intcon,6 idisable peripheral interrupts
bsf status,rpO ibank 1
52
bcf piel,O ;disable tmrl inter rupt
bcf p iel,2 ;disable ccpl interrupt
bcf stat us,rpO ;bank 0
clrf ccplcon ;ccpl module o ff, clear ccpl prescaler
movlw b'OOOOO Ol O' ;tmrl prescaler and tmrl setup,
movwf tlcon tmrl off
c lr f t mrlh ;clear timer 1 high
clrf tmr ll ;clear timer 1 low, clear prescaler
bsf tlcon,O ;timer 1 on
read movf t mr l l ,w ;re a d timer 1 l ow
movwf portb ;display count at port B
g oto read

end
i--- - ----- - -- ----- -- -- - - ---- - - -- ---- - - - - - --- - - - --- - - - - - - - - - - -
;at devi c e programmi ng t i me , select :
memo r y unprot ect e d
watch d og t i mer disab led (d ef a ult i s e n a bled)
stand ard c r y s t a l XT (using 4 MH z osc f or test)
power-up time r o n
b ro wn-ou t reset enabled
lvp d i s a ble d
d e b u g mode di sabled
; = = = = = = = = = = = = = = ============= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

• Capture TMR1 Count When An External Event Occurs

T1CKI

+SVDC PIC16F870
8 LEDs
10K
CCP1 Port B

Capture
Count
1 680

-
-
For this experiment, we will use a pulser to inject pulses into timer I via the TICKI pin. The
objective is to increment TMRI (via the prescaler) on every rising edge. The input at TICKI pin
must be high on reset or the result will be one count short. The count will be displayed in binary
via the port B LEDs.

A pullup resistor and switch are used to generate a capture on a rising edge via the CCPI pin
which is configured as an input. Power-up the circuit with the switch closed (LEDs will be oft)
and then open the switch (rising edge) when you want to capture the contents of timer 1.

53
Inject a few pulses at the TICKI pin. Capture the count using the switch on the CCPI pin and
see if the displayed count equals the number of rising edges you injected.

Assumes:
Count .. 1

J
ur---
CnRese' J 1
1st Rising
Edge

Main Interrupt
Clear Capture Interrupt
Program Service
Flag
Routine
Disable Interrupts And
Clear Interrupt Flags
+
Read Capture Register
Low

CCP1 Module Off,


Clear CCP1 Prescaler •
Display Number
At Port B

CiearsTMR1
Prescaler

Return from Interrupt

CCP1 Setup-
Capture Mode
CCP1 ModuleOn

54
I Unimplemented

I I Prescaler 1:1

~~

T1CON

TMR10ff
POR. oxoo
External Clock
Sync External
Clock Input
Oscillator Off

I Unimplemented

I I NotUsed
DIP SwItches
~~
1 2 3 4 5 6 7 [OEGTOlr-0~'-1--'--1-0-.,-
-- 1I
-------
CCP1CON
o C 0 C 0 C C

C losed On Reset
Open To Capture
J POR = axoo
L Capture Mode,
Every Rising Edge

i =======CAPT.ASM====================================4/28/02==
list p=l6f870
__config h'3f7l'
radix hex
j------- --------------------------- ------------------- - - - - - - -
it imer land ccpl module - capture mode demo
j - - - - - - - - --- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
cpu equates (memory map)
status equ Ox03
portb equ Ox06
intcon equ OxOb
pirl equ OxOc
tmrll equ OxOe
tmrlh equ OxOf
tlcon equ OxlO
ccprll equ OxlS
ccprlh equ Oxl6
ccplcon equ Oxl7
trisb equ Ox86
trisc equ Ox87
piel equ Ox8c
adconl equ Ox9f
j----------------------------------------------------- - - - - - - -

55
bit equates
rpO equ 5
ccpl equ 2 ;ccpl bit 2, port C
i--- ---------- ------------ --------------- - - - - --------- - - - - - - -
org OxOOO
goto start ;skip over location pointed to by
interrupt vector
org Ox004
goto iserv

start bsf status,rpO ;switch to bank 1


movlw b' 00000110' ;turn off AID, port A
movwf adconl
movlw b'OOOOOOOO' ;port B outputs
movwf trisb
bsf trisc,O ;port C, bit 0 timer 1 input
bsf trisc,ccpl ;ccpl pin input
bcf status,rpO ;switch back to bank 0
movlw b'OOOOOOOO' ;port B lines low
movwf portb
bcf intcon,7 ;disable global interrupts
bcf intcon,6 ;disable peripheral interrupts
bsf status,rpO ;bank 1
bcf piel,O ;disable tmrl interrupt
bcf piel,2 ;disable ccpl interrupt
bcf status,rpO ;bank 0
bcf pirl,2 ;clear ccpl interrupt flag
clrf ccplcon ;ccpl module off, clear ccpl prescaler
movlw b'OOOOOOlO' ;tmrl prescaler and tmrl setup,
movwf tlcon tmrl off
clrf tmrlh ;clear timer 1 high
clrf tmrll ;clear timer 1 low, clear prescaler
clrf ccprlh ;clear capture register high
clrf ccprll ;clear capture register low
movlw b'OOOOOlOl' ;ccpl prescaler mode,
movwf ccplcon ccpl capture mode, ccpl on
bsf intcon,7 ;enable global interrupts
bsf intcon,6 ;enable peripheral interrupts
bsf status,rpO ;bank 1
bsf piel,2 ; e n a b l e ccpl interrupt
bcf status,rpO ;bank 0
bsf tlcon,O ;timer 1 on
circle goto circle ;done

iserv bcf pirl,2 ;clear ccpl interrupt flag,


enable further interrupts
movf ccprll,w ;read capture register low
movwf portb ;display captured count at port B
retfie ;return from interrupt

end

56
i----------------------------------------------------- - - - - - - -
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
i============================================================

• Count Events Up To Predetermined Number And Generate An Output


(compare)

T1CKI

PIC16F870

CCP1
680

-.....

For the compare mode experiment, we will load the compare register (low byte) with a small
number such as binary 8. The CCPI pin must be configured as an output. We will input 8 rising
edges via the TICKI pin and that should trigger an output at the CCPI pin (goes high), lighting
the LED connected to it via a current limiting resistor. Remember that the TI CKI input pin must
be high on reset. Try some other numbers to get a feel for how this works .

Assumes :
Count = 1

J
J u-
DnRese'
1
1st Rising
Edge

57
Disab le Interrupts And
Clear Interrupt Flags

CCP1 Module Off,


Clear CCP1 Prescaler

CiearsTMR1
Prescaler

Clear Compare Register


High

Clear Compa re Register


Low

CCP1 Setup -
Compare Mode
CCP1 Module On

I Unimplemented I Unimplemented

I I Presca ler 1:1 I I NotUsed

~/'--.... ~~
T1CON CCP1CON ~'-1-'-
1 0-'-
10~10-1
POR = OxOO
TMR10ff

External Clock
POR = OxOO -------- I
Compare Mode,
Sync External
Clock Input Set Output On Match

Oscillator Off

58
DIP Switches

1 2 345 6 7
o C 0 C 0 C 0

i=======CMPR.ASM====================================4/28/02==
list p=16f870
__config h'3f71'
radix hex
;------------------------------------------------------------
itimer 1 and ccpl module - compare mode demo
;------------------------------------------------------------
cpu equates (memory map)
status equ Ox03
portc equ Ox07
intcon equ OxOb
pirl equ OxOc
tmrll equ OxOe
tmrlh equ OxOf
tlcon equ OxlO
ccprll equ Ox15
ccprlh equ Ox16
ccplcon equ Ox17
trisc equ Ox87
piel equ Ox8c
adconl equ Ox9f
;------------------------------------------------------------
bit equates
rpO equ 5
ccpl equ 2 iccpl bit 2, port C
;------------------------------------------------------------
org OxOOO

start bsf status,rpO iswitch to bank 1


bsf trisc,O iport C, bit 0 timer 1 input
bcf trisc,ccpl iccpl pin output
movlw b'OOOOOllO' iturn off A/D, port A
movwf adconl
bcf status,rpO iswitch back to bank 0
bcf portc,ccpl iccpl pin low
bcf intcon,7 idisable global interrupts
bcf intcon,6 idisable peripheral interrupts
bsf status,rpO ibank 1
bcf piel,O idisable timer 1 interrupts
bcf piel,2 idisable ccpl interrupts
bcf status,rpO ibank 0
clrf ccplcon iccpl module off
movlw b'OOOOOOlO' itmrl prescaler and tmrl setup,

59
movwf tlcon tmrl off
clrf tmrlh iclear timer 1 high
clrf tmrll iclear timer 1 low
clrf ccprlh iclear compare register high
movlw d'8 ' iload compare register low with dec 8
movwf ccprll
movlw b'OOOOlOOO' iccpl compare mode, ccpl pin high
on match, ccp module on,
ccpl pin low
movwf ccplcon
bsf tlcon,O itimer 1 on
circle goto circle idone

end
;------ ------------------------------------------------------
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
;================ === = == == ==== ================================

60
Single Time Interval Output (via TMR1 and CCP, compare mode)

• Start TMR1, Auto End

Single Long Pulse - 500 milliseconds

The clock input for TMRI may be the internal instruction clock, an external clock connected to
the Tl CKI pin, or an external crystal (details on external crystal later). We will use the internal
clock for this example. The output is via the CCPI pin and an LED .

+SVDC PIC16F870

~ L
10K

...--1 RAO CCP1.r-L Ie- soo msec-J

Generating a single time interval represented by a positive-going pulse output on the CCPl pin
requires some gymnastics. First, the CCPl pin must be assigned as an output using the TRISC
register. Second, when the CCPl module is turned on (by writing the compare mode bit pattern
to the CCPI CON register), the CCPI pin is forced low (no matter what). To accomplish our
objective, which is to indicate the start of the time interval with a rising edge, we will:

1) Load timer 1 with the value OxOOO I .


2) Enable interrupts .
3) Load the CCPICON register with the bit pattern 1000 (bits3,2, 1,0) which puts the
CCPl module in the compare mode, calls for CCPl pin high on match, turns on
the CCPI module, and causes the CCPI pin to be low initially.
4) Start timer I .

The arrival of the first pulse at the input of timer 1 will result in a match causing a CCPl inter-
rupt. The interrupt service routine will:

1) Clear the CCPI interrupt flag.


2) Flip a bit in the CCPICON register making it so the CCPI pin will go low on
the next match.
3) Load the compare registers.
4) Return from interrupt.

61
Timeout

CCP1 Pin

CCP1
Module On

TMR10n

1st Auto End -


CCP1 Pin Goes High
Under CCP1 Module Control

Turning on the CCPl module forces the CCPl pin low (previously initialized low). Timer 1
starts counting and a match occurs as soon as the first clock pulse is received resulting in a
CCPl interrupt and forcing the CCPl pin high. Timer 1 continues to be incremented. Bit a in
the CCPlCON register is set by the interrupt service routine so that when the next match occurs,
the CCPl pin will be forced low. The interrupt service routine loads the compare registers with
the desired value and then returns control to the main program . Timer 1 increments until a
match occurs and the output pin (CCPl) goes low.

Notice that 3 internal clock cycles are used by the single pulse compare gymnastics. You can
shave the value used for the second compare to compensate.

BCF t1con,o 1 cycle


First pulse into TMR1 1 cycle
CCP1 pin goes high 1 cycle
Total 3 cycles

Timer 1 is incremented until its contents match the value stored in the compare register. This is
how the duration of the time interval is controlled .

The desired time interval for this example is 500 milliseconds.


Use internal instruction clock, prescale 1:8.
8 microseconds x 62,500 = 500 milliseconds.

The hexadecimal number which represents decimal 62,500 is:

OxF424

~
I = 4
L== 2x16 = 32
4x 256 = 1024
15x4096 = .6..l..4.iQ.
62500

62
Disable Interrupts And
Clear Interrupt Flags

CCP1 Module Off,


Clear CCP1 Prescaler

CiearsTMR1
Prescaler
ready

No

I Unimplemented

I I Prescaler 1:8

Load CCP1 Compare


~~
Register Ox0001
T1CON

TMR10ff
CCP1 Compare Mode, paR = OxOO
CCP1 Pin HI On Match, Internal Clock
CCP1 Module On,
CCp1 Pin La Ignore With
Internal Clock
Oscillator Off

go hi
I Unimplemented
No I I NotUsed

~~
CCP1CON ~~1--'--10--'--10-----'-10-\
Load Compare Register
With Real Value
paR = OxOO -------- I
Compare Mode,
Set Output On Match

63
DIP Switches

1 2 3 4 5 6 7
ceo C ceo
L Closed On Reset
Open When Ready

i=======SNGL.ASM====================================4/28/02==
list p=16f870
__config h'3f7l'
radix hex
i------------------------------------- - --------------- - - - - - - -
itimer 1 and ccpl module - single time interval demo
i----------------------------------------------------- - - - - - - -
cpu equates (memory map)
status equ Ox03
porta equ Ox05
portc equ Ox07
intcon equ OxOb
pirl equ OxOc
tmrll equ OxOe
tmrlh equ OxOf
tlcon equ OxlO
ccprll equ Ox15
ccprlh equ Ox16
ccplcon equ Ox17
trisa equ Ox85
trisc equ Ox87
piel equ Ox8c
adconl equ Ox9f
i----------------------------------------------------- - - - - - - -
bit equates
rpO equ 5
ccpl equ 2 iccpl bit 2, port C
.
e

org OxOOO
_

start bsf status,rpO iswitch to bank 1


bcf trisc,ccpl iccpl pin output
movlw b' 00000110' iturn off A/D, port A
movwf adconl
bcf status,rpO iswitch back to bank 0
bcf portc,ccpl iccpl pin low
bcf intcon,7 idisable global interrupts
bcf intcon,6 idisable peripheral interrupts
bsf status,rpO ibank 1
bcf piel,O idisable timer 1 interrupts
bcf piel,2 idisable ccpl interrupts
bcf status,rpO ibank 0

64
bcf pirl,2 iclear ccpl interrupt flag
clrf ccplcon iccpl module off
movlw b' 00110000 I itmrl prescaler and tmrl setup,
movwf tlcon tmrl off
clrf tmrlh iclear timer 1 high
clrf tmrll iclear timer 1 low
ready btfss porta, 0 iready?
goto ready inot yet
trick clrf ccprlh iclear compare register high
movlw OxOl iload compare register low
movwf ccprll
movlw b'OOOOlOOO' iccpl compare mode, ccpl pin high
movwf ccplcon on match, ccpl module on,
ccpl pin low
bsf tlcon,O itimer 1 on
gohi btfss pirl,2 iccpl interrupt flag set?
goto gohi inot yet
movlw Oxf4 iload compare register high
movwf ccprlh
movlw Ox24 iload compare register low
movwf ccprll
bsf ccplcon,O iccpl pin l o w on match
circle goto circle idone

end
i----------------------------------------------------- - - - - - - -
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
;========================================= == = = = = == === = =======

Single Short Pulse - 128 microseconds

Let's go back to testing the F870. This time, we will use a 128 microsecond time interval and
test the output using the '84 on a board running pgentst.asm.

+5VDC

10K .... one128 .asm pgenlst.asm

RAO
PIC16F84A
PIC16F870
8 LEDs
. CCP1
.-.r--L
RAO Port B -
..~ 680

.....
::! ~ .....
- ~

65
I found that the measurement result was always a little bit low. First, the 128 microsecond pulse
generated may not be exactly 128 microseconds long because the program loop which tests
TMRO, bit 7 may not be testing the bit at the very instant it is set. At the receiving end, the
loops looking at the input may not detect the leading and trailing edges of the pulse at exactly
the right time . For the 128 microsecond example, my readings were a mix of 123 and 129
microseconds. This error mayor may not be significant, depending on the application. The
longer the pulse, the less significant the error (few microseconds) becomes .

i=======ONE128.ASM==================================4/28/02==
list p=16f870
__eonfig h'3f71'
radix hex
j------------------------------------------------------------
itimer 1 and eepl module - single time interval demo
;------------------------------------------------------------
cpu equates (memory map)
status equ Ox03
porta equ Ox05
porte equ Ox07
inteon equ OxOb
pirl equ OxOe
tmrll equ OxOe
tmrlh equ OxOf
tleon equ OxlO
eeprll equ Ox15
eeprlh equ Ox16
eepleon equ Ox17
trisa equ Ox85
trise equ Ox87
piel equ Ox8e
adeonl equ Ox9f
j------------------------------------------------------------
bit equates
rpO equ 5
eepl equ 2 ieepl bit 2, port C
;------------------------------------------------------------
org OxOOO

start bsf status,rpO iswiteh to bank 1


movlw b' 00000110' iturn off A/D, port A
movwf adeonl
bsf trisa,O iport A, bit 0 input
bef trise,eep l ieepl pin output
bef status,rpO iswiteh back to bank 0
bef porte,eepl ieepl pin low
bef inteon,7 idisable global interrupts
bef inteon,6 idisable peripheral interrupts
bsf status,rpO ibank 1
bef piel,O idisable timer 1 interrupts
bef piel,2 idisable eepl interrupts
bef status,rpO ibank 0
bef pirl, 2 ielear eepl interrupt flag
elrf eepleon ieepl module off
movlw b'OOOOOOOO' itmrl presealer and tmrl setup,

66
movwf tlcon tmrl off
clrf tmrlh ;clear timer 1 high
clrf tmrll ;clear timer 1 low
ready btfss porta, 0 ; ready?
goto ready ;not yet
trick clrf ccprlh ;clea r compare register high
movlw OxOl ;load compare register low
movwf ccprll
movlw b'OOOOlOOO' ;ccpl compare mode, ccpl pin high
movwf ccplcon on match, ccpl module on,
ccpl pin low
bsf tlcon,O ;timer 1 on
gohi btfss pirl,2 ;ccpl interrupt flag set?
goto gohi ;not yet
movlw OxOO ;load compare register high
movwf ccprlh
movlw Ox40 ;load compare register low
movwf ccprll
bsf ccplcon,O ;ccpl pin low on match
circle goto circle ;done

end
j----------------------------------------------------- - - - - - - -
;at device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
;============================================================

Free Running Output (via TMR1 and CCP, compare mode)

• Clear TMR1 Each Cycle

Generating a free running output works in much the same way as creating the single interval in
the example shown earlier. Here, the state of the output pin (CCPl) is changed every time the
timer value matches the compare register value. Notice that the bit in the CCPICON register
which determines whether the CCPl pin goes high or low on timeout must also be toggled .

Use internal instruction clock, prescale 1:8.


8 microseconds x 62,500 = 500 milliseconds.

PIC16F870

JUl..SL
CCPl JUUL ~l-
680 500 msec

-"'"
67
Main Interrupt
Clear Compare Interrupt
Program Service
Flag
Routine

Disable Interrupts And


Clear Interrupt Flags

CCP1 Module Off, Yes


Clear CCP1 Prescaler

CiearsTMR1
Prescaler

Load CCP1 Compare


Registers

CCP1 Compare Mode,


CCP1 Pin HI On Match,
CCP1 Module On,
CCP1 Pin La

I Unimplemented I Unimplemented

I I Prescaler 1:8 I I NotUsed

~~ ~~
~'----1----'--10----'--10----'--

-------
T1CON CCP1CON 1 0-1

TMR10ff
paR = OxOO paR = oxoo I
Internal Clock
Compare Mode,
Ignore With
Set Output On Match
Internal Clock
Oscillator Off

68
DIP Switches

1 2 3 4 5 6 7
ceo C ceo
L Closed On Reset
Open When Ready

i=======FREE .ASM====================================4/28/02==
list p=16f870
__config h'3f71'
radix hex
;------------------------------------------------------------
itimer 1 and ccpl module - free running compare mode demo
;------------------------------------------------------------
cpu equates (memory map)
status equ Ox03
portc equ Ox07
intcon equ OxOb
pirl equ OxOc
tmrll equ OxOe
tmrlh equ OxOf
tlcon equ OxlO
ccprll equ OxlS
ccprlh equ Ox16
ccplcon equ Ox17
trisc equ Ox87
piel equ Ox8c
adconl equ Ox9f
;------------------------------------------------------------
bit equates
rpO equ S
ccpl equ 2 iccpl bit 2, port C
i------------------------------------------------- ---- - - - - - - -
org OxOOO
goto start iskip over location pointed to by
interrupt v e c t o r
org Ox004
goto iserv

start bsf status,rpO iswitch to bank 1


bcf trisc,ccpl iccpl pin output
movlw b' 00000110' iturn off A/D, port A
movwf adconl
bcf status,rpO iswitch back to bank 0
bcf portc,ccpl iccpl pin low
bcf intcon,7 idisable global interrupts
bcf intcon,6 idisable peripheral interrupts
bsf status,rpO ibank 1
bcf piel,O idisable timer 1 interrupts
bcf piel,2 idisable ccpl interrupts

69
bcf status,rpO ibank 0
bcf pirl,2 iclear ccpl interrupt flag
clrf ccplcon iccpl module off
movlw b' 00110000' itmrl prescaler and tmrl setup,
movwf tlcon tmrl off
clrf tmrlh iclear timer 1 high
clrf tmrll iclear timer 1 low
movlw Oxf4 iload compare register high
movwf ccprlh
movlw Ox24 i load compare register low
movwf ccprll
bsf intcon,7 ienable global interrupts
bsf intcon,6 ienable peripheral interrupts
bsf status,rpO ibank 1
bsf piel,2 ienable ccpl compare interrupt
bcf status,rpO ibank 0
movlw b'OOOOlOOO' iccpl compare mode, ccpl pin high
movwf ccplcon on match, ccpl module on,
ccpl pin low
bsf tlcon,O itimer 1 on
circle goto circle idone

iserv bcf pirl,2 iclear ccpl interrupt flag,


enable further interrupts
clrf tmrlh iclear timer 1 high
clrf tmrll iclear timer 1 low
btfss ccplcon,O icontrol bit status?
goto sbit ibit is clear
cbit bcf ccplcon,O iclear control bit
retfie ireturn from interrupt
sbit bsf ccplcon,O iset control bit
retfie ireturn from interrupt

end
;------------------------------------------------------------
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
;============================================================

70
Free Running Output (via TMR1 and CCP, compare mode)

• Free Running TMR1, Add Interval Value To Compare Register Each Cycle

This example is useful in situations where timer 1 is used for more than one application simulta-
neously. The free running output is initiated by reading TMRl while it is running, adding the
time interval value to the TMRI reading, and storing the result in the compare register. From
then on, the interval value is added to the compare register contents each time a CCPl interrupt
occurs. We will use the subroutine for reading timer 1 while it is running and double precision
addition.

PIC16F870

.ri.n.ri.
CCP1 ..n.n.n. -I l-
680 500 msec

"I"
--
Since you are getting used to this stuffby now, the flow charts will be less detailed .

71
Main Interrupt
Clear CCP1 Interrupt
Program Service
Flag
Routine

Add Time Interval To


Compare Register

Read TMR1 Via Sub -


2 Bytes

Add Time Interval To


TMR1 Reading Via Sub
(Double Precision)

Circle = Do
Something Else

I Unimplemented I Unimplemented

I I Prescaler 1:8 I I NotUsed

~~ ~~

T1CON CCP1CON ~r-1-1'-----0-'-10-'-11 -1


POR = OxOO
TMR10ff

Internal Clock
POR = OxOO ---------
I
Compare Mode,
Ignore With
Clear Output On Match
Internal Clock
Oscillator Off

72
DIP Switches

1 2 3 4 5 6 7
o C 0 C ceo

i=======FREEADD.ASM=================================4/28/02==
list p=16f870
__config h'3f7l'
radix hex
j----------------------------------------------------- - - - - - - -
itimer 1 and ccpl module - free running compare mode demo
read timer 1 on-the-fly
j----------------------------------------------------- - - - - - - -
cpu equates (memory map)
status equ Ox03
portc equ Ox07
intcon equ OxOb
pirl equ OxOc
tmrll equ OxOe
tmrlh equ OxOf
tlcon equ OxlO
ccprll equ Ox15
ccprlh equ Ox16
ccplcon equ Ox17
trisc equ Ox87
piel equ Ox8c
adconl equ Ox9f
t l hi equ Ox20
tl 10 equ Ox2l
lsb2 equ Ox22
msb2 equ Ox23
int hi equ Ox24
int 10 equ Ox25
j----------------------------------------------------- - - - - - - -
bit equates
c equ 0
z equ 2
rpO equ 5
ccpl equ 2 iccpl bit 2, port C
;------------------------------------------------------------
org OxOOO
goto start iskip over location pointed to by
interrupt vector
org Ox004
goto iserv

start bsf status,rpO iswitch to bank 1


bcf trisc,ccpl iccpl pin output
movlw b '00000110 I iturn off A/D, port A

73
movwf adconl
bcf status,rpO ;switch back to bank 0
bcf portc,ccpl ;ccpl pin low
bcf intcon,7 ;disable global interrupts
bcf intcon,6 ;disable peripheral interrupts
bsf status,rpO ;bank 1
bcf piel,O ;disable timer 1 interrupts
bcf piel,2 ;disable ccpl interrupts
bcf status,rpO ;bank 0
bcf pirl,2 ;clear ccpl interrupt flag
clrf ccplcon ;ccpl module off
movlw b' 00110000' ;tmrl prescaler and tmrl setup,
movwf tlcon tmrl off
clrf tmrlh ;clear timer 1 high
clrf tmrll ;clear timer 1 low
bsf tlcon,O ;timer 1 on
nop ;fake do something else
nop
nop ;ready to output
call read ;read timer 1 on-the-fly
movlw Oxf4 ;add interval to timer 1 reading
movwf msb2 ;load interval high
movlw Ox24 ;load interval low
movwf lsb2
dblplus movf tl_lo,w ;fetch timer 1 reading low
addwf lsb2,f ;add low bytes, result in lsb2
btfsc status,c ;carry set?
incf msb2,f ;yes, add 1 to msb result
movf tl_hi,w ;fetch timer 1 reading high
addwf msb2,f ;add high bytes, result in msb2
trick clrf ccprlh ;clear compare register high
movlw OxOl ;load compare register low
movwf ccprll
movlw b'OOOOlOOO' ;ccpl compare mode, ccpl pin high
movwf ccplcon on match, ccpl module on,
ccpl pin low
gohi btfss pirl,2 ;ccpl interrupt flag set?
goto gohi ;not yet
movf msb2,w ;load compare register high
movwf ccprlh
movf lsb2,w ;load compare register low
movwf ccprll
bsf ccplcon,O ;ccpl pin low on match
bsf intcon,7 ;enable global interrupts
bsf intcon,6 ;enable peripheral interrupts
bsf status,rpO ;bank 1
bsf piel,2 ;enable ccpl compare interrupt
bcf status,rpO ;bank 0
circle goto circle ;done
j----------------------------------------------------- - - - - - - -
;note that interrupts must be disabled when using the
read subroutine
read movf tmrlh,w ;read high byte
movwf tl hi ;store

74
movf tmrll,w iread low byte
movwf tl 10 istore
movf tmrlh,w iread high byte again
subwf tl_hi,w isubtract first read from second read
btfsc status,z iis result O?
return iread valid
movf tmrlh,w irollover occurred, read high byte again
movwf t l hi istore
movf tmrll,w iread low byte
movwf tl 10 istore
return
j----------------------------------------------------- - - - - - - -
iserv bcf pirl,2 iclear ccpl interrupt flag,
enable further interrupts
movlw Oxf4 iload interval high
movwf int hi
movlw Ox24 iload interval low
movwf int 10
dbladd movf int_lo,w ifetch interval low
addwf Isb2,f iadd low bytes, result in Isb2
btfsc status,c icarry set?
incf msb2,f iyes, add 1 to msb result
movf int_hi,w ifetch interval high
addwf msb2,f iadd high bytes, result in msb2
movf msb2,w iload compare register high
movwf ccprlh
movf Isb2,w iload compare register low
movwf ccprll
btfss ccplcon,O icontrol bit status?
goto sbit ibit is clear
cbit bcf ccplcon,O iclear control bit
nop idelay 1 cycle to equalize paths
retfie ireturn from interrupt
sbit bsf ccplcon,O iset control bit
retfie ireturn from interrupt
j----------------------------------------------------- - - - - - - -
end
j----------------------------------------------------- - - - - - - -
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
Ivp disabled
debug mode disabled
j===================================================== =======

75
Time Measurement (period/interval/time between events)
• Via TMR1 Read
This is similar in concept to using an AND gate. An unknown time interval may be measured
by using the time interval signal to gate a second signal of known frequency.

Count Pulses Which Arrive During


Unknown Time Interval
Known Frequency

Unknown Time
Interval
Time Interval = Pulses = Pulses
Frequency PulsesITime

The signal of known frequency will appear at the output of the AND gate only while both sig-
nals are high. The pulses appearing at the output of the AND gate are then counted. The num-
ber of pulses divided by the frequency (multiplied by the period) of the reference signal is the
time interval.

A time interval generated by some circuit external to a PIC16F870 may be measured using
timer 1. The time interval might be the time between two pulses or the time a single pulse is
high. For this experiment, we will use a PIC16F84 to generate a single pulse of known length
and use timer 1 in a PIC16F870 to measure the time it is high.

76
+SVDC

10K .. ~
2000.asm

~
RB1 RBO RA1
+SVDC

PIC16F870
.. ~ 10K .. ~
PIC16F84A
"=:=" RAO

"""1 RA2 PortB


8 LEOs

Display
. ~ 680

Low
Byte
1. - '-
-
:!! ~ .....

The selection of numbers used here is arbitrary. We will use a time interval of 2000 microsec-
onds generated by a PICI6F84. The circuit and software are in the chapter Test Equipment For
Timing And Counting Experiments.

The internal clock of the PIC16F870 will be used as the signal of known frequency (1 MHz,
period 1 microsecond). The internal clock pulses will be fed into timer 1 (prescaler 1:1) during
the time the port line connected to the F84 is high. A "ready" switch will be used so that mea-
surement does not begin until everything is stable . The program displays the timer 1 high byte
at the port B LEDs as soon as the measurement is complete. The timer 1 low byte may subse-
quently be displayed by opening the "display low byte" switch .

77
No
Port A, Bit 0

Yes

No
Port A, Bit 1

No
Port A, Bit 1

No
Port A, Bit 2

78
I Unimplemented

I I Prescaler 1:1
DIP Switches
~~
1 2 3 4 5 6 7
T1CON
C 0 C C C C C
POR - OxOO
TMR10ff
Internal Clock L Closed On Reset
Open To Display Low Byte
Ignore With
Internal Clock
Closed On Reset
Oscillator Off Open When Ready

i=======PERIOD.ASM==================================4/28/02==
list p=16f870
__config h'3f71'
radix hex
;------------------------------------------------------------
iperiod measurement demo
internal clock, prescaler 1:1
;------------------------------------------------------------
cpu equates (memory map)
status equ Ox03
porta equ OxOS
portb equ Ox06
intcon equ OxOb
pir1 equ OxOc
tmr1l equ OxOe
tmr1h equ OxOf
t1con equ Ox10
trisa equ Ox8S
trisb equ Ox86
pie1 equ Ox8c
adcon1 equ Ox9f
:------------------------------------------------------------
bit equates
rpO equ S
:------------------------------------------------------------
org OxOOO

start bsf status,rpO iswitch to bank 1


movlw b'OOOOOOOO' iport B outputs
movwf trisb
movlw b' 00000110' iturn off A/D, port A
movwf adcon1
movlw b' 00011111' r po r t A inputs
movwf trisa
bcf status,rpO iswitch back to bank 0
movlw b'OOOOOOOO' iport B lines low
movwf portb
bcf intcon,7 idisable global interrupts
bcf intcon,6 idisable peripheral interrupts
bsf status,rpO ibank 1

79
bcf piel,O idisable tmrl inter rupt
bcf status,rpO ibank 0
bcf pirl,O iclear tmrl interrupt flag
movlw b'OOOOOOOO' iprescaler and tmrl setup,
movwf tlcon tmrl off
clrf tmrlh iclear timer 1 high
clrf tmrll iclear timer 1 low, clear prescaler
ready btfss porta, 0 iready?
goto ready
istart btfss porta,l ;watch for rising edge
goto istart
bsf tlcon,O itimer 1 on
iend btfsc porta,l iwatch for falling edge
goto iend
bcf tlcon,O itimer 1 off
movf tmrlh,w iget timer 1 high
movwf portb idisplay via port B LEDs
disp_lo btfss porta,2 iwatch display low byte switch
goto disp_lo
movf tmrll,w iget timer 1 low
movwf portb idisplay via port B LEDs
circle goto circle idone

end
;------------------------------------------------ ----- -- -- -- -
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
;============================================================

Procedure:

1) In preparation for power-up:


A) "Ready" switch on F870 closed.
B) "Display low byte" switch on F870 closed
C) "Send" switch on F84 closed.
2) Power-up both F84 and F870 .
3) Open the "ready" switch on the F870.
4) Open the "send" switch on F84.
5) The high byte will be displayed on the F870 LEDs .
6) Open the "display low byte" switch on the F870.
7) The low byte will be displayed on the F870 LEDs.

Decoding result:

0000011 1 1 1 0 1 0 000

l ~~'2256 l ~2:' L 16

Total: 2000
80
• TMR1 And CCP, Capture Mode

A time interval may be measured using another technique. Timer 1 may be in the free run mode.
The time interval signal is fed into the CCPl pin and used to capture the time values from
timer 1 at the start and end of the time interval.

J L
1
1st Capture
1
2nd Capture

The first capture is on a rising edge and the second is on a falling edge.

Timer 1 is merrily doing its thing when the captures occur.

TMR1 CONTENTS

TIME

1st Capture 2nd Capture

TMR1 Rolls
Over

Timer 1 will be part of the way to a full count when the first capture occurs. We are really inter-
ested in obtaining the time remaining to rollover. This can be obtained by complementing the
captured value. Complementing a value means changing all the 1s to Os and visa versa . The
result is the complement of the value and is equal to the difference between the value and
OxFFFF for a 16-bit number. There is an instruction COMF which does this .

The complement of the first capture value is added to second capture value . The second value
captured represents the time from rollover to the second capture . The result is the number of
pulses corresponding to the time interval between the two captures .

Two flags are used , one to indicate that the first capture has occurred and the other to indicate
that the task has been completed.

81
The objectives for the interrupt service routine are:

• Read timer 1 on-the-fly - 1st capture and 2nd capture.


• Determine whether or not timer 1 overflowed between captures .
• Clear the timer 1 overflow flag after the first capture.
• When a CCPl interrupt occurs, determine which edge (rising or falling, first or second)
caused the interrupt.
• When the first edge is detected, set the 1st capture flag and store the count value .
• When the second edge is detected, set the 2nd capture flag, store the count value and
do the math = calculate the time interval.

+5VDC

10K . ~
2000 .asm

--r-----1.-
RB1 RBO CCP1
+5VDC

PIC16F870
... 10K .. ~
PIC16F84A
. RAO

A""l. 8 LEOs
RA2 Port B

Display
. ~ 680

Low
Byte
1. ~~
-...
-N

82
Main Interrupt
Program Service
Routine

Yes

Clear CCP1 Interrupt Complement 1st


Flag Reading

CCP1 Module On,


Capture On Rising Edge Change To Capture On Add Result To 2nd
Falling Edge Reading

Clear 1st Capture And


Done Flags

No I Unimplemented

I I Prescaler 1:1

Yes
~~

T1CON
No
TMR10ff
paR = OxOO
Internal Clock

Ignore With
Internal Clock
Oscillator Off

I Unimplemented

I I NotUsed

~~

CCP1CON l -1I
~-o----'-I-1--'-I-o'----

paR = OxOO --------


L Capture Mode,
Every Rising Edge
83
DIP Switches

1 2 345 6 7
C C C C C C c

L Closed On Reset
OpenTo Display Low Byte

Closed On Reset
Open When Ready

;=======PDCCP.ASM===================================4/28/0 2==
list p=16f870
__config h'3f7I'
radix hex
;------------- ---------------------------------------- ---- ---
;time r 1 and ccpI module - period measurement demo
timer 1 free running
capture at start and end of period pulse
;------------------------------------------------------------
cpu equates (memory map)
status equ Ox03
porta equ OxOS
portb equ Ox06
po r tc equ Ox07
intcon equ OxOb
pi rl e qu OxOc
t mrIl equ OxOe
t mr I h e qu OxOf
tIcon equ OxIO
ccprIl equ OxIS
ccprIh equ Ox16
ccpIcon equ Ox17
trisa equ Ox8S
trisb equ Ox86
trisc equ Ox87
piel equ Ox8c
adconl equ Ox9f
one hi equ Ox 20
one 10 e q u Ox2I
I sb2 equ Ox 22
msb2 e qu Ox 23
flags equ Ox2 4 ; Ist captu re flag bit 0
done flag bit 1
;----------- --- -- - - - - - - ---- - ---- - - --- - - --- --- - ------- - - - - -- - -
bit equates
c equ 0
z equ 2
rpO equ S
c cp I equ 2 ; c c p I bit 2, p ort C

84
;------------------------------------ -- --------- - --- ---- --- --
o rg OxOOO
goto start iskip over location point ed to b y
inte rrupt v e c t o r
org Ox004
goto iserv

start bsf status,rpO iswitch to bank 1


movlw b'OOOOOOOO ' iport B outputs
movwf trisb
movlw b' 00000110' iturn off AID, port A
movwf adcon1
movlw b' 00011111' iport A inputs
movwf trisa
bsf trisc,ccp1 iccp1 pin input
bcf status,rpO iswitch back to bank 0
movlw b'OOOOOOOO' iport B lines low
movwf portb
bcf intcon,7 idisable global interrupts
bcf intcon,6 idisable peripheral interrupts
bsf status,rpO ibank 1
bcf pie1,0 idisable timer 1 interrupts
bcf pie1,2 idisable ccp1 interrupts
bcf status,rpO ibank 0
bcf pir1,2 iclear ccp1 interrupt f l a g
clrf ccp1con iccp1 module off
movlw b'OOOOOOOO' itmr1 prescaler and tmr1 setup,
movwf t1con tmr1 off
clrf tmr1h iclear timer 1 high
clrf tmr1l iclear timer 1 low
bsf t1con,0 itimer lon, free running
movlw b'00000101' icapture on rising edge, ccp1
movwf ccp1con module on
clrf flags iclear 1st captu re and done f l a g s
bsf intcon,7 ienable global interrupts
bsf intcon,6 ienable peripheral inter rupts
bsf status,rpO ibank 1
bsf pie1,2 ienable ccp1 interrupts
bcf status,rpO ibank 0
ready btfss porta,O iready?
goto ready
done btfss flags, 1 idone?
goto done
movf msb2,w iget period high
movwf portb idisplay via port B LEDs
disp_lo btfss porta,2 iwatch display low byte switch
goto disp_lo
movf lsb2,w iget period l ow
movwf portb idisplay via port B LEDs
circle goto circle idone
;--------------------------- ------------------ ----- - ---------
iserv btfsc flags, 0 i1st capture flag s et?
goto cap2 iyes, goto 2nd capture
bsf flags,O ino, set 1st capture flag

85
bcf pirl,2 ;clear ccpl interrupt flag,
enable second interrupt
bcf ccplcon,O ;second capture on falling edge
movf ccprlh,w ;get 1st capture high
movwf one hi ;store
movf ccprll,w ;get 1st capture low
movwf one 10 ;store
retfie
cap2 bsf status,rpO ;bank 1
bcf piel,2 ;disable ccp1 interrupts
bcf status,rpO ;bank 0
comf one_hi,f ;complement 1st capture high
comf one_lo,f ;complement 1st capture low
movf ccprlh,w ;get 2nd capture high
movwf msb2 ;store
movf ccpr11,w ;get 2nd capture low
movwf lsb2 ; store
dblplus movf one_lo,w ;fetch complement of 1st low
addwf lsb2,f ;add low bytes, result in lsb2
btfsc status,c ;carry set?
incf msb2,f ;yes, add 1 to msb result
movf one_hi,w ;fetch complement of 1st high
addwf msb2,f ;add high bytes, result in msb2
bsf flags,l ;set "done" flag
retfie
i----------------------------------------------------- - - - - - - -
end
;------------------------------------------------------------
;at device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
;============================================================

Procedure:

1) In preparation for power-up:


A) "Ready" switch on F870 closed.
B) "Display low byte" switch on F870 closed
C) "Send" switch on F84 closed.
2) Power-up both F84 and F870 .
3) Open the "ready" switch on the F870.
4) Open the "send" switch on F84.
5) The high byte will be displayed on the F870 LEDs.
6) Open the "display low byte" switch on the F870.
7) The low byte will be displayed on the F870 LEDs.

The result will be the same as for the previous example.

86
Frequency Measurement (via TMR1 and CCP, gate via TMRO)

Measuring frequency is the flip side of measuring a time interval. This is similar in concept to
using an AND gate. Frequency may be measured by using the known time interval signal to
gate a second signal of unknown frequency .

Count Pulses Which Arrive During


Known Time Interval
Unknown Frequency

Known Time
Interval

The signal of unknown frequency will appear at the output of the AND gate only while both sig-
nals are high . The pulses appearing at the output of the AND gate are then counted . The num-
ber of pulses divided by the time interval is the frequency.

The frequency of a signal generated by some circuit external to a PIC 16F870 may be measured
using timer 1. For this experiment, we will use a PIC 16F84 to generate a signal of "unknown"
frequency and use timer 1 in a PIC16F870 to measure that frequency.

lreqout.asm

..J'UUL.
RBO T1CKI
+SVDC

PIC16F84A
.. ~ 10K .. . PIC16F870

RAO

Ae'~l
8 LEDs

Display
RA2 Port B
. ~ 680

Low
Byte
- I-
-
:!~ .....

87
Again the selection of numbers used here is arbitrary. We will use a frequency of 100 KHz gen-
erated by a PIC 16F84. This is done using a very simple program involving software time delays
(freqout.asm).

In the PIC 16F870, we will use timer 0 to generate a sample time of 0.1 second which should
result in 10,000 pulses being counted by timer 1. Using a timer 1 prescaler value of 1:1 will
result in maximum resolution.

Since timer 0 is an 8-bit counter, it will roll over every 256 counts . We will need a file register
counter to count TMRO rollovers.

100,000 = 390.625 TMRO relievers


256

390.625 - 256 = 134.625 .625 x 256 = 160

The file register counter will count up to 255 and roll over on the next count. We will have the
program watch for this to happen and then look for the file register counter to reach 134 as it
counts up the second time. Next, the program will look to see when the count in TMRO reaches
160. It will do this by watching bit 7 to be set (128), followed by watching for bit 5 to be set
(32). The sum of 128 and 32 is 160.

Bit 7 6 5 4 3 2 1 0

UW
128 64 32 16 8 4 2
I

Watch for 128, then watch for 32


_I

There are, of course, other ways to do this . This method does not require use of interrupts and
uses a minimum of program steps once the final count in TMRO is reached (source of error).

To review, the program will:

• Clear TMRI.
• Start TMRl , count signal pulses coming from the F84.
• Do timing via 256 TMRO rollovers, 134 more TMRO rollovers, 160 TMRO counts.
• Stop TMRI.
• Read TMRI .
• Display TMRI high byte .
• Display TMRI low byte when "display low byte" switch is opened .

The frequency is the number of pulses counted by timer 1 during the 0.1 second sample period
times 10.

88
~NO
ClearTMRoOverflow
Flag

Known IncrementTimer 0
Gate Time Rollover Counter

Clear Timer 0 Rollover No


Counter

No

No

Yes

No
Clear Timer 0 Overflow
Flag

oflow1

No
ReadTimer 1 High
And Display

Clear TMRO Overflow


Flag

No

IncrementTMRO
Rollover Counter

Display Timer 1
No Low Byte

89
I Unimplemented

I I Prescaler 1:1

~~

T1CON

TMR10ff
POR= oxoo
External Clock
Sync External
Clock Input
Oscillator Off

Port B Pullups Disabled (Not Used)

External Interrupt On Rising Edge (Not Used)

Clock Source Internal

Option Register

POR =OxFF --------


L Prescaler Value (Not Used)
Prescaler Assigned To WDT (Not Used)

External Clock - Increment


On Falling Edge
(Not Used)

DIP Switches

1 2 3 4 5 6 7
C C C c o C C

L Closed On Reset
Open To Display Low Byte

Closed On Reset
Open When Ready

90
i=======FREQ.ASM====================================4/28/02==
list p=16f870
__config h'3f71'
radix hex
j----------------------------------------------------- - - - - - - -
ifrequency measurement demo
gate via tmrO, internal clock, prescaler 1:1
j----------------------------------------------------- - - - - - - -
cpu equates (memory map)
tmrO equ Ox01
status equ Ox03
porta equ Oxos
portb equ Ox06
intcon equ OxOb
tmr11 equ OxOe
tmr1h equ OxOf
t1con equ Ox10
opt equ Ox81
trisa equ Ox8S
trisb equ Ox86
trisc equ Ox87
adcon1 equ Ox9f
pie1 equ Ox8c
count equ Ox20
j----------------------------------------------------- - - - - - - -
bit equates
z equ 2
rpO equ S
j----------------------------------------------------- - - - - - - -
org OxOOO

start bsf status,rpO iswitch to bank 1


movlw b'OOOOOOOO' iport B outputs
movwf trisb
movlw b' 00000110' iturn off A/D, port A
movwf adcon1
movlw b' 00011111' iport A inputs
movwf trisa
bsf trisc,O iport C, bit 0 input, t mr 1 input
bcf status,rpO iswitch back to bank 0
movlw b'OOOOOOOO' iport B lines low
movwf portb
bcf intcon,7 idisable global interrupts
bcf intcon,6 idisable peripheral interrupts
bcf intcon,S idisable timer 0 interrupts
bsf status,rpO ibank 1
bcf pie1,0 idisable tmr1 interrupt
bcf status,rpO ibank 0
movlw b'OOOOOO10' iprescaler and tmr1 setup,
movwf t1con tmr1 off
clrf tmr1h iclear timer 1 high
clrf tmr11 iclear t imer 1 low, clear prescaler

91
clrf tmrO iclear timer 0
clrwdt iclr WDT, prep prescaler assign
bsf status,rpO ibank 1
movlw b'11011111' iset up timer 0
movwf opt
bcf status,rpO ibank 0
clrf count iclear tmrO rollover counter
ready btfss porta, 0 iready?
goto ready
bsf t1con,0 iturn on timer 1
clrf tmrO iclear timer 0
bcf intcon,2 iclear timer 0 interrupt flag
oflow1 btfss intcon,2 itimer 0 overflow?
goto oflow1 inot yet
bcf intcon,2 iclear timer 0 interrupt flag
incf count,f iinc timer 0 overflow counter
movf count,w icompare
sublw Oxff idecimal 255
btfss status,z itest z flag, skip next instruction
if flag is set
goto oflow1 iagain
oflow2 btfss intcon,2 itimer 0 overflow?
goto oflow2 inot yet
bcf intcon,2 iclear tmrO interrupt flag
incf count,f iinc timer 0 overflow counter
movf count,w icompare
sublw Ox86 idecimal 134
btfss status,z itest z flag, skip next instruction
i f flag is set
goto oflow2 iagain
clrf tmrO iclear timer 0
tl28 btfss tmrO,7 ilook for timer 1=128
goto tl28
t32 btfss tmrO,5 ilook for timer 1=32 (128+32=160)
goto t32
bcf t1con,0 istop timer 1
movf tmr1h,w iget timer 1 high
movwf portb idisplay via port B LEDs
disp_lo btfss porta,2 iwatch display low byte switch
goto disp_Io
movf tmr11,w iget timer 1 low
movwf portb idisplay via port B LEDs
circle goto circle idone

end
j----------------------------------------------------- - - - - - - -
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
j===================================================== = = = = = = =

92
Procedure:

1) In preparation for power-up:


A) "Ready" switch on F870 closed.
B) "Display low byte" switch on F870 closed
2) Power-up both F84 and F870.
3) Open the "ready" switch on the F870.
4) The high byte will be displayed on the F870 LEDs.
5) Open the "display low byte" switch on the F870.
6) The low byte will be displayed on the F870 LEDs.

External 32,768 Hz Watch Crystal-Based Clock For TMR1

The input of timer 1 can be connected to a crystal oscillator circuit which is on-chip . A crystal
and two capacitors are connected externally. The principal application of this is real-time keep-
ing. Watch crystals are readily available which have a frequency is 32,768 Hz.

PIC16F870

Tl0SCI Tl0SCO

32.768 KHz
n
22 pi I I 22pf

--
When the timer 1 oscillator is enabled (TICON bit 3 set), the TI0S0 and TI0SI pins become
inputs. The corresponding bits in the TRISC register are ignored.

As it turns out, 32,768 is a power of2, which is very convenient. Timer 1 is a 16-bit counter.
The number 32,768 is represented by 15 bits . We can handle this by setting bit 15 (16th bit) in
timer 1 after each rollover so that inputting 32,768 pulses will cause a rollover.

To get this working in a simplistic way, we will write a program to blink an LED connected to
port B, bit 0 at the rate of one blink per second (precisely).

93
Main Interrupt
Clear Timer 1
Program Service
Interrupt Flag
Routine

1
Set Up Timer 1 To Roll
Over At 32,768

+
Togg le LED

~
Return From Interrupt

i=======SECONDS.ASM=================================4/28/02==
list p=16f870
__config h'3f71'
radix hex
;------------------------------------------------------------
iseconds demo
external oscillator, 32768 Hz, timer 1, prescaler 1:1
;------------------------------------------------------------
cpu equates (memory map)
status equ Ox03
portb equ Ox06
intcon equ OxOb
pir1 equ OxOc
tmr1l equ OxOe
tmr 1h equ OxOf
t1con equ Ox10
trisb equ Ox86
pie1 equ Ox8c
adcon1 equ Ox9f
i----------------------------------------------------- - - - - - - -
bit equates
rpO equ 5
i----------------------------------------------------- - - - - - - -
erg OxOOO
goto start iskip over location pointed to by
interrupt vector
org Ox004
goto iserv

start bsf status,rpO iswitch to bank 1


movlw b'OOOOOOOO' iport B outputs
mevwf trisb
movlw b' 00000110 I iturn off A/D, port A
movwf adcon1
bcf status,rpO iswitch back to bank 0
movlw b'OOOOOOOO' iport B lines low

94
movwf portb
bcf intcon,7 idisable global interrupts
bcf intcon,6 idisable peripheral interrupts
bsf status,rpO ibank 1
bcf piel,O idisable tmrl interrupts
bcf status,rpO ibank 0
bcf pirl,O iclear timer 1 interrupt flag
movlw b'OOOOlOlO' iprescaler and tmrl setup,
movwf tlcon tmrl off
clrf tmrlh iclear timer 1 high
clrf tmrll iclear timer 1 low, clear prescaler
bsf intcon,7 ienable global interrupts
bsf intcon,6 ienable peripheral interrupts
bsf status,rpO ibank 1
bsf piel,O ienable tmrl interrupts
bcf status,rpO ibank 0
bsf tlcon,O itimer 1 on
circle goto circle
;------------------------------------------------------------
iserv bcf pirl,O iclear timer 1 interrupt flag
bsf tmrlh,7 iset up timer 1 to rollover at
32,768 counts
btfss portb,O iport B, bit 0 status?
goto setbit ibit is clear
clrbit bcf portb,O iclear port B, bit 0
retfie ireturn from interrupt
setbit bsf portb,O iset port B, bit 0
retfie ireturn from interrupt
j----------------------------------------------------- - - - - - - -
end
j----------------------------------------------------- - - - - - - -
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
j===================================================== = = = = = = =

When the oscillator is operating properly, a 32.768 KHz sine wave with an amplitude of approx-
imately 3.5 volts will be present at the TI0S0 pin (pin 11, PICI6F870). If the example pro-
gram does not run, check pin 11 with an oscilloscope to be sure that the oscillator is functioning
properly. The values for the capacitors called out in Microchip's data books are not consistent
(15 pF and 33 pF). 22 pF caps worked for me.

The user must provide a software delay to ensure proper oscillator startup. This has not been
done in this example and so you will notice that the time from power-up to first blink is more
than one second. This is something to keep in mind when you design your own applications.

To illustrate real-time keeping, we can create a very simple clock. This is not meant for your
office or living room . It will serve as a simple example and may be used as a basis for control or
data logging applications.
95
+5VDC

10K 10K

t----1 RAO

~~l PIC16F870

-. 8LEDs
' - - - - - - - - 1 RA2 Port B 1----,

T10SCI T10SCO 680

32 .768 KHz
o -
N

22 Pf I 22pf
I .
A timer 1 interrupt is generated at precisely 1 second intervals. Software will be used to develop
minutes and hours as illustrated by the following flow chart. The default display is seconds in
binary via the port B LEDs. Opening the minutes switch will cause minutes to be displayed as
long as the switch is open. Similarly, opening the hours switch will result in hours being dis-
played until the switch is closed . The clock runs for 24 hours from the time the F870 is reset
and rolls over meaning all counters are cleared and counting starts over.

Again, note that the most significant bit of timer 1 is set after each rollover so that 32,768 oscil-
lator pulses will cause an overflow.

96
Main
Program Setup

display

No

Interrupt
Clear Timer 1 Interrupt
Service
Flag
Routine

97
I Unimplemented

I I Prescaler 1:1
DIP Switches
~~
1234567
T1CON
C C ceo 0 C
paR = OxOO TMR10ff

External Clock L Closed On Reset


Open • Minutes Displayed
Sync External
Clock Input
Closed On Reset
Osci llator Enab led Open- Hours Displayed

;=======TIME.ASM====================================4/28/02 ==
list p=16f8 70
__config h'3f71'
radix hex
j------------------------------------------------------------
; seconds, minutes, hours demo
exte rnal oscil lator, 32768 Hz, timer 1, prescaler 1 :1
j------ ------------------ ------------------------------------
cpu equates (memory map)
status equ Ox03
porta equ OxOS
portb equ Ox06
i nt c on equ OxOb
pir1 equ OxOc
tmr1l equ OxOe
tmr1h equ OxOf
t1 c on equ Ox10
t r i sa equ Ox 8S
tr isb equ Ox86
pie1 equ Ox8 c
adcon1 equ Ox 9f
sec equ Ox20
min equ Ox21
hr equ Ox22
j------------------------------------------------------------
bit equates
z equ 2
rpO equ S
j------------------------------------------------------------
org OxOOO
goto start ;skip over location pointed to by
interrupt vector
org Ox004
goto iserv

start bsf status,rpO ;switch to bank 1


movlw b'OOOOOOOO' ;port B outputs

98
movwf trisb
movlw b '00000110 I ;turn off A/D, port A
movwf adconl
movlw b '00011111' ;port A inputs
movwf trisa
bcf status,rpO ;switch back to bank 0
movlw b'OOOOOOOO' ;port B lines low
movwf portb
bcf intcon,7 ;disable global interrupts
bcf intcon,6 ;disable periphe ral inter rupts
bsf status,rpO ;bank 1
bcf piel,O ;disable tmrl interrupts
bcf status,rpO ;bank 0
bcf pirl,O ;clear timer 1 interrupt flag
movlw b'OOOOlOlO' ;prescaler and tmrl setup,
movwf tlcon tmrl off
clrf tmrlh ;clear timer 1 high
clrf tmrll ;clear timer 1 low, clear prescaler
clrf sec
clrf min
clrf hr
bsf intcon,7 ;enable global interrupts
bsf intcon,6 ;enable peripheral interrupts
bsf status,rpO ;bank 1
bsf piel,O ;enable tmrl interrupts
bcf status,rpO ;bank 0
bsf tlcon,O ;timer 1 on
display movf sec,w ;get seconds
movwf portb ;display seconds
minutes btfss porta, 0 ;display minutes?
goto hours ;no
movf min,w ;get minutes
movwf portb ;display minutes
goto minutes
hours btfss porta,2 idisplay hours?
goto display ;no
movf hr,w iget hours
movwf portb ;display hours
goto hours
;------------------------------------------------------------
iserv bcf pirl,O iclear timer 1 interrupt flag
bsf tmrlh,7 iset up timer 1 to roll over at
32,768 counts
incf sec,f
movf sec,w icompare
sublw d' 60' iseconds = 60?
btfss status,z itest z flag, skip n ext instruction
i f flag is set
retfie
clrf sec iclear seconds
incf min,f iincrement minutes
movf min,w icompare
sublw d'60' iminutes = 60?
btfss status,z itest z flag, skip next instruction

99
if flag is set
retfie
clrf min ;clear minutes
incf hr,f ;increment hours
movf hr,w ; compare
sublw d'24' ;hours = 24?
btfsc status,z ;test z flag, skip next instruction
if flag is clear
clrf hr ;clear hours
retfie
i----------------------------------------------------- - - - - - - -
end
i------------------------------------------------------ - - - - - -
;at device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
;============================================================

100
PULSE WIDTH MODULATION (PWM) USING TMR2 AND THE CCP MODULE

The fundamentals ofPWM are explained in Ple'n Up The Pace or Microcontrorn Apps along
with techniques for do-it-yourselfPWM using software. Here we will talk about doing PWM
using hardware peripherals built into many of the PIC microcontroller variants.

A PWM output may be generated on the CCP 1 pin by using TMR2, PR2 and CCP 1 module .
We will use 8 bits to represent the duty cycle for now to keep things simple .

A simplified block diagram shows the basic concept:

CCP1
Module

CCP1 Pin Low


Match

PWM Output

CCP1 Pin High,


Match ClearTMR2

PR2

Period

~
JU
TMR2 = PR2 J LL TMR2 - PR2

TMR2 = Duty Cycle

TMR2 increments. A comparator looks for a match between TMR2 and the duty cycle register.
When a match occurs, the CCPl pin is driven low. A second comparator looks for a match
between TMR2 and the period number in the PR2 register. When a match occurs, the CCPl pin
is driven high and TMR2 is cleared to start the next period . This process repeats over and over
giving a PWM output. The duty cycle may be changed on-the-fly to change the analog voltage
output on the CCPl pin by loading a different duty cycle number in the duty cycle register
which is CCPRIL.

101
Notice that the TMR2 postscaler is not involved in PWM applications.

Generating a PWM output via the CCP1 pin (8-bit duty cycle) requires:

• Make CCP1 an output pin.


• Disable the appropriate interrupts.
• Load the period register.
• Clear two bits of the CCP 1CON register not used in the 8-bit mode.
• Load the (initial) duty cycle .
• Set up timer 2.
• Select PWM mode for CCP1 module .
• Start timer 2.

A short program will serve to demonstrate how all this works. The object is to generate a PWM
output on the CCP1 pin. Timer 2 is fed by the internal instruction clock and the prescaler choic-
es are 1:1, 1:4, and 1:16. I arbitrarily chose to use 1:4 as the prescaler value and OxFF (for deci-
mal 256 counts) as the period register value.

period = (lx10-6 seconds)(4)(256) = 1024 microseconds

102
The duty cycle for our demonstration is 50 percent, so Ox7F is loaded into CCPRIL which
serves as the duty cycle register (8 bits) .

Register Duty Cycle


Bits Bits

CCP1L 7-0 9-2


CCP1CON 5,4 1,0

Load Period Register


PR2

Unimplemented

I Postscaler Not Used In


I PWM Applications

TMR2 Prescaler 1:4,


TMR20ff
T2CON ----------
~
POR = OxOO
L Prescaler 1:4

TMR20ff

CCP1 PWM Mode,


CCP1 Module On

I Unimplemented

I I Not Used

~~
,---,---.,.--.,----,

[O]YTOJ?J
-------
CCP1CON 1 11 1 0 1 0 I

POR = OxOO I
PWM Mode (11 xx)

103
DIP Switches

1 2 345 6 7
ceo C ceo

i=== ====HDWPWMX.ASM=================================4/28/02==
list p=16f870
__config h'3f71'
radix hex
j----------------------------------------------------- - - - - - - -
cpu equates (memory map)
status equ Ox03
portc equ Ox07
intcon equ OxOb
tmr2 equ Ox11
t2con equ Ox12
ccprll equ Ox15
ccp lcon equ Ox1 7
trisc equ Ox87
piel equ Ox8c
pr2 equ Ox92
adconl equ Ox9f
j----------------------------------------------------- - - - - - - -
bit equates
rpO equ 5
ccpl equ 2 iccpl bit 2, port C
j----------------------------------------------------- - - - - - - -
org OxOOO

s tart bsf status,rpO iswitch to bank 1


movlw b' 00000110' iturn off A/D, port A
movwf adconl
bcf trisc,ccpl iccpl pin outpu t
bcf status,rpO iswitch back to bank 0
bcf portc,ccpl iccpl pin low
bcf intcon,7 idisable global interrupts
bcf intcon,6 idisable peripheral interrupts
bsf status,rpO ibank 1
bcf piel,l idisable timer 2 interrupts
bcf piel,2 idisable ccp l interrupts
bcf status,rpO ibank 0
clrf ccplcon iccpl module off
bsf status,rpO ibank 1
movlw Oxff idecimal 255
movwf pr2 iload period register
bcf status,rpO ibank 0
bcf ccplcon,5 ic lear bit 1 of duty cycle reg
bcf ccplcon,4 iclear bit 0 of duty cycle reg
movlw Ox7f iduty cycle 50 percent
movwf ccprll ibits 9-2 of duty cycle
movlw b'OOOOOOOl' iprescaler = 1:4,

104
movwf t2con tmr2 off
clrf tmr2 iclear tmr2
movlw b' 00001100 I iccpl pwm mode, ccpl module on
movwf ccplcon
bsf t2con,2 itmr2 on
circle goto circle idone

end
i------- ----------------------------------- ----------- - - - - - - -
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
;================================================== === == = = = = =

An oscilloscope may be used to observe the output signal. The duty cycle will be 50 percent.

ANALOG OUTPUT - INCREASE/DECREASE BUTTONS - PWM - 8-bit Mode

The next example shows how an adjustable analog output on the CCPl pin can be created using
PWM via TMR2, PR2 and the CCPl module. The circuit is controlled by an increase (voltage)
button and a decrease button . Increase/decrease is proportional to the time the button is pressed
(to a maximum of 5 volts at the output or a minimum of 0 volts). The rate of increase/decrease
is proportional to the time interval generated by the time delay code (change it if you wish).

+SVDC

10K . ~ 10K c ~

RAO

Incr ~ Dea~
PIC16F870
l l
- RA2 CCP1 PWMOutpuI

105
OVERVIEW

repeat

Yes

No

Yes

No

Main
Program

repeat

Look For
Key Press-
Inc Or Dec
If Find One

106
Subroutine

pressck

No

decduty

Yes

No

Yes

Unimplemented

------
I Postscaler Not Used In
I PWM Applications

T2CON

~
POR .. OxOO
L Prescaler 1:4

TMR20ff
DIP Switches

1 2 345 6 7
000 C ceo I Unimplemented

I I
L Decrease When
Closed ~~
Not Used

.---.------.-----.-----
CCP1CON [OJ?TOTOJ 1 11 10101
Increase When
Closed
POR .. OxOO -------- I
PWM Mode (11xx)

107
i=======CCPPWM.ASM==================================4/2 8 / 02==
list p=16f870
__eonfig h'3f71'
radix hex
i----------------------------------------------------- - - - - - - -
cpu equates (memory map)
status equ Ox03
porta equ Ox05
porte equ Ox07
inteon equ OxOb
tmr2 equ Ox11
t2eon equ Ox12
eeprll equ Ox15
eepleon equ Ox17
duty equ Ox20
meount equ Ox21
neount equ Ox22
trisa equ Ox85
trise equ Ox87
piel equ Ox8e
pr2 equ Ox92
adeonl equ Ox9f
i----------------------------------------------------- - - - - - - -
bit equates
z equ 2
rpO equ 5
eepl equ 2 ieepl bit 2, port C
i----------------------------------------------------- - - - - - - -
org OxOOO

start bsf status,rpO iswiteh to bank 1


movlw b' 00000110 I iturn off AID, port A
movwf adeonl
movlw b'OOOOOlOl' iinputs/outputs
movwf trisa
bef trise,eepl iccpl pin output
bef status,rpO iswitch back to bank 0
bef portc,ccpl iccpl pin low
bef inteon,7 idisable global interrupts
bef inteon,6 idisable peripheral interrupts
bsf status,rpO ibank 1
bcf piel,l idisable timer 2 interrupts
bcf piel,2 idisable ccpl interrupts
bcf status,rpO ibank 0
elrf ceplcon icepl module off
bsf status,rpO ibank 1
movlw Oxff idecimal 255
movwf pr2 iload period register
bef status,rpO ibank 0
bef ccpleon,5 iclear bit 1 of duty cycle reg
bef ecplcon,4 iclear bit 0 of duty cycle reg
movlw Ox7f iinitial duty cycle 50 percent
movwf duty
movwf ccprll ibits 9-2 of duty cycle

108
movlw b'OOOOOOOl' ipresca1er = 1:4,
movwf t2con tmr2 off
clrf tmr2 ic1ear tmr2
movlw b' 00001100 I iccp1 pwm mode, ccp1 module on
movwf ccp1con
bsf t2con,2 itmr2 on
;------------------------------------------------------------
repeat call pressck ilook for incr/decr button press
movlw Oxff idelay 200 milliseconds
movwf mcount
loadn movlw Oxff
movwf ncount
decn decfsz ncount,f idecrement N
goto decn iagain
decfsz mcount,f idecrement M
goto loadn iagain
goto repeat
:---------------------------------------------------- --------
pressck btfss porta,2 idecrease button pressed?
goto decduty iyes, decrement duty cycle
btfss porta, 0 iincrease button pressed?
goto incduty iyes, increment duty cycle
return
incduty movf duty,w iget duty cycle into W
sublw Oxff iduty cycle=Oxff?
btfsc status,z
return
incf duty,f iincrement duty cycle
movf duty,w iget duty cycle into W
movwf ccpr11 iduty cycle into ccp register
return ipress check done
decduty movf duty,w iget duty cycle into W
sublw OxOO iduty cycle=O?
btfsc status,z
return
decf duty,f idecrement duty cycle
movf duty,w iget duty cycle into W
movwf ccpr11 iduty cycle into ccp register
return ipress check done

end
:------------------------------------------------------------
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
:============================================================

109
An oscilloscope may be used to observe the output signal. The duty cycle will be 50 percent ini-
tially. Close the increase switch and observe the results on the scope.

DUTY CYCLE - 10-blt Mode

The duty cycle register is really 10 bits wide, split 8 and 2.

CCPR1L CCP1CON
7654 321 o I5 4

,--------,--------,------,---,-----,----,--CO
[ITT]
8 Bits (9-2) 2 Bits (1,O)

The high 8 bits (bits 9 - 2) are the CCPRIL register. The low 2 bits are bits 1 and 0 of the
CCPICON register. Resolution up to 10 bits is possible.

The most significant 8 bits and least significant 2 bits must be loaded separately requiring a little
thought in programming.

Many PWM applications involve incrementing or decrementing the duty cycle. To do this, the
current duty cycle value needs to be stored in two file registers and incremented or decremented
there. Then a copy of the contents of those registers is transferred to the CCP module to change
the duty cycle. In the 10-bit mode, doing this is an extra challenge. For the purpose of incre-
menting or decrementing, the low 8 bits need to be in an 8-bit register and the two remaining
(high) bits should be in a second register. The challenge is that the opposite arrangement is used
in the CCP module registers. The high 8 bits are written to the CCPRIL register and the low 2
bits are written to the middle of the CCPICON register.

110
Here is an example of IO-bit hardware PWM. The duty cycle is stored in two registers. The low
8 bits are in the duty Io register. The high 2 bits are in the duty_hi register. A subroutine called
"dutychg" moves the bits to their proper places in the CCP module registers.

dutyJo

[TTJ] I
_ _-,I I II

~\ I II I
I I----!-_ _J
[TTJ],----,------,--O I------L------'
5 4
CCPR1L CCP1CON

PIC16F870

CCP1 PWM Output

111
dutychg

Clear Bit 1 Of Duty


Cycle Reg ister,
CCP1CON,5
Rotate duty-Io Right,
Result In W register
Clear Bit 0 Of Duty
Cycle Register,
Bits 7-2 Of CCP1CON,4
Duty Cyc le

Clear

Rotate temp Right,


Result In W Register Set Duty Register, Bit1
Duty Cycle Bits 7-2 . CCP1CON,5

Bits 1,0 Of
Clear
Duty Cycle

Clear

Bits 9,8 Of Set Duty Register , Bit 0


Duty Cycle CCP1CON ,4

Clear

temp Into W,
Duty Cycle Bits 9-2

DIP Switches

W into Duty Cycle 1 2 3 4 5 6 7


Register Low
C C 0 C C C 0

112
Unimplemented I Unimplemented

~
I
Postscaler Not Used In I I NotUsed

--------
PWM Applications
~~

T2CON CCP1 CON ~-1---'1'-1-'-10---'---1-0I

POR = OxOO
~

L Prescaler 1:4
POR = OxOO --------
I
PWM Mode (11xx)
TMR20ff

;=======HDWPWMY.ASM=================================4/28/02==
list p=16f870
__config h'3f71'
radix hex
;------------------------------------------------------------
cpu equates (memory map)
status equ Ox03
portc equ Ox07
intcon equ OxOb
tmr2 equ Ox11
t2con equ Ox12
ccprll equ Ox15
ccplcon equ Ox17
duty_lo equ Ox20
duty_hi equ Ox21
temp equ Ox22
trisc equ Ox87
piel equ Ox8c
pr2 equ Ox92
adconl equ Ox9f
j----------------------------------------------------- - - - - - - -
bit equates
c equ o
rpO equ 5
ccpl equ 2 ;ccpl bit 2, port C
j----------------------------------------------------- - - - - - - -
org OxOOO

start bsf status,rpO ;switch to bank 1


bcf trisc,ccpl ;ccpl pin output
movlw b' 00000110' ;turn off A/D, port A
movwf adconl
bcf status,rpO ;switch back to bank 0
bcf portc,ccpl ;ccpl pin low
bcf intcon,7 ;disable global interrupts
bcf intcon,6 ;disable peripheral interrupts
bsf stat us,rpO ;bank 1
bcf piel,l ;disable timer 2 interrupts

113
bcf piel,2 ;disable ccpl interrupts
bcf status,rpO ;bank 0
clrf ccplcon ;ccpl module off
bsf status,rpO ;bank 1
movlw Oxff ;decimal 255
movwf pr2 ;load period register
bcf status,rpO ;bank 0
movlw OxOO ;duty cycle 25 percent
movwf duty_lo
movlw b'OOOOOOOl' ;duty cycle hi 2 bits
movwf duty_hi
call dutychg ;change duty cycle via subroutine
movlw b'OOOOOOOl' ;prescaler = 1:4,
movwf t2con tmr2 off
clrf tmr2 ;clear tmr2
movlw b' 00001100 1 ;ccpl pwm mode, ccpl module on
movwf ccplcon
bsf t2con,2 ;tmr2 on
circle goto circle ;done
,-------------------------------------------------------------
dutychg bcf status,c ;clear carry flag
rrf duty_lo,w ;rotate duty right, result in w
movwf temp ;store in temp
bcf status,c ;clear carry flag
rrf temp,f ;bits 7-2 of duty cycle
btfsc duty_hi, 1 ;duty cycle hi bit 1 = l?
bsf temp, 7 ;yes, set duty register bit 9
btfsc duty_hi, 0 ;duty cycle hi bit 0 = l?
bsf temp, 6 ;yes, set duty register bit 8
movf temp,w ;duty cycle bits 9-2
movwf ccprll ;into duty cycle register
bcf ccplcon,5 ;clear bit 1 of duty cycle reg
bcf ccplcon,4 ;clear bit 0 of duty cycle reg
btfsc duty_lo,l ;duty cycle bit 1 = l?
bsf ccplcon,5 ;yes, set duty register bit 1
btfsc duty_lor 0 ;duty cycle bit 0 = l?
bsf ccplcon,4 ;yes, set duty register bit 0
return
i----------------------------------------------------- - - - - - - -
end
i----------------------------------------------------- - - - - - - -
;at device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
Ivp disabled
debug mode disabled
i===================================================== =======

An oscilloscope may be used to observe the output signal.

114
When the program runs , a PWM signal is generated on the CCPl pin. The duty cycle is 25 per-
cent.

The next challenge is to increment and decrement a regist er pair wh ere only the least significa nt
2 bits of the upper register are used . Notice how the increment and decrement instructions affect
the zero flag .

• INCF Z flag set when register contents overflows


• DECF Z flag set when register contents = 0 (not us eful here)

When the lower register is incremented, we need to know when it overflows (OxFF to OxOO) so
that a check can be made to see if the upper register should be incremented. We can use the state
of the ze ro flag to tell us .

When the lower reg ister is decremented, we need to know when it underflows (OxFF to OxOO) so
that a check can be made to see if the upper register should be decremented . The state of the
zero flag won't tell us, so we will have to use a comparison to test for this .

Increment:

FF to 00

L Sets Z-flag, increment 2-bit register if not already full

Decrement:

00 to FF

L Test via compare, decrement 2-bit register if not alread y zero

The flow charts which follow show one method for doing this .

Again, the duty cycle is stored in two registers. The low 8 bits are in the dutyJo register. The
high 2 bits are in the duty_hi register.

The maximum and minimum values are :

Maximum b'OOOOOOll' OxFF


Mi n i mum b'OOO OO OOO' OxOO

The increment/decrement program must keep the values cont rollin g the duty cycl e within these
boundaries.

115
Increment
11111111
Yes

Yes, Rollover Occured

No

Decrement

116
i------------------------------------------------------ - - - - - -
incduty movf duty_lo,w itest for maximum
sublw Oxff
btfss status,z
goto do i inot at maximum
movf duty_hi,w
sublw b' 00000011' icompare
btfsc status,z
return iat maximum
do i incf duty_lo,f iincrement
btfss status,z
goto inc
movf duty_hi,w
sublw b' 00000011' icompare
btfsc status,z
goto inc
incf duty_hi,f
inc call dutychg ichange duty cycle
return ipress check done
i----------------------------------------------------- - - - - - - -
decduty movf duty_lo,w itest for minimum
sublw OxOO
btfss status,z
goto do d inot at minimum
movf duty_hi,w
sublw b'OOOOOOOO'
btfsc status,z
return iat minimum
do d decf duty_lo,f idecrement
movf duty_lo,w
sublw Oxff
btfss status,z
goto dec
movf duty_hi,w
sublw b'OOOOOOOO' icompare
btfsc status,z
goto dec
decf duty_hi,f
dec call dutychg ichange duty cycle
return ipress check done
i-------------------------------------------------- --- - - - - - - -

117
ANALOG OUTPUT - INCREASE/DECREASE BUTTONS· PWM - 1a-bit Mode

The next example shows how to generate a PWM output with increase/decrease capability using
10 bits to represent the duty cycle. The rate of increase/decrease is controlled by a software time
delay loop . When you test this circuit and program, you can change the delay if you wish.

+5VDC

10K .... 10K .. ..


RAO

Dea~
PIC16F870

- l
RA2 CCP1 PNMOuipul

118
OVERVIEW

repeat

Yes

No

Yes

Main
Program

repeat

Look For
Key Press -
Inc Or Dec
If Find One

119
Subroutine

pressck

No

decduty

Yes

No

Yes

DIP Switches

1 2 3 4 5 6 7
a a a c c c a

LDecrease When
Closed

Increase When
Closed

Unimplemented I Unimplemented

I Postscaler Not Used In I I NotUsed

-------
I PWM Applications
~~

T2caN CCP1CaN ~r-1-'--'1-'--'0-'--


' 0-1
paR - OxOO
~

L Prescaler 1:4
paR - OxOO --------
I
PWM Mode (11 xx)
TMR20ff

120
i=======CCPPWMX.ASM=================================4/ 28102==
list p=16f870
__config h'3f7I'
radix hex
i--------------------------- ------- ------------------- - - - - - - -
cpu equates (memory map)
status equ Ox03
porta equ Ox05
portc equ Ox07
intcon equ OxOb
tmr2 equ Ox11
t2con equ Ox12
ccprIl equ Ox15
ccpIcon equ Ox17
duty_l 0 equ Ox20
duty_hi equ Ox2I
temp equ Ox22
mcount equ Ox23
ncount equ Ox24
trisa equ Ox85
trisc equ Ox87
piel equ Ox8c
pr2 equ Ox92
adconl equ Ox9f
;------------------------------------------------------------
bit equates
c equ o
z equ 2
rpO equ 5
ccpI equ 2 iccpI bit 2, port C
i----------------------------------------------------- - - - - - - -
org OxOOO

start bsf status,rpO iswitch to bank 1


movlw b' 00000110 I iturn off AID, port A
movwf adconl
movlw b'OOOOOlOl' iinputs/outputs
movwf trisa
bcf trisc,ccpl iccpl pin output
bcf status,rpO iswitch back to bank 0
bcf portc,ccpl iccpl pin low
bcf intcon,7 idisable global interrupts
bcf intcon,6 idisable peripheral interrupts
bsf status,rpO ibank 1
bcf piel,l idisable timer 2 interrupts
bcf piel,2 idisable ccpl interrupts
bcf status,rpO ibank 0
clrf ccplcon iccpl module off
bsf status,rpO ibank 1
movlw Oxff idecimal 255
movwf pr2 iload period register
bcf status,rpO ibank 0
movlw Oxff iduty cycle 50 perce nt
movwf duty_lo

121
movlw b'OOOOOOOl' iduty cycle hi 2 bits
movwf duty_hi
call dutychg ichange duty cycle via subroutine
movlw b'OOOOOOOl' iprescaler = 1:4,
movwf t2con tmr2 off
clrf tmr2 iclear tmr2
movlw b' 00001100' iccp1 pwm mode, ccp1 module on
movwf ccp1con
bsf t2con,2 itmr2 on
i----------------------------------------------------- - - - - - - -
repeat call pressck ilook for incr/decr button press
movlw Ox7f idelay 100 milliseconds
movwf mcount
loadn movlw Oxff
movwf ncount
decn decfsz ncount,f idecrement N
goto decn iagain
decfsz mcount,f idecrement M
goto loadn iagain
goto repeat
j----------------------------------------------------- - - - - - - -
pressck btfss porta, 2 idecrease button pressed?
call decduty iyes, decrement duty cycle
btfss porta,O iincrease button pressed?
call incduty iyes, increment duty cycle
return
j------------------------- ---------------------------- - - - - - - -
incduty movf duty_lo,w itest for maximum
sublw Oxff
btfss status,z
goto do i inot at maximum
movf duty_hi,w
sublw b' 00000011' icompare
btfsc status,z
return iat maximum
do i incf duty_lo,f iincrement
btfss status,z
goto inc
movf duty_hi,w
sublw b' 00000011' icompare
btfsc status,z
goto inc
incf duty_hi,f
inc call dutychg ichange duty cycle
return ipress check done
j----------------------------------------------------- - - - - - - -
decduty movf duty_lo,w itest for minimum
sublw OxOO
btfss status,z
goto do d inot at minimum
movf duty_hi,w
sublw b'OOOOOOOO'
btfsc status,z
return iat minimum

122
do d decf duty_lo,f ; decrement
movf duty_lo,w
sublw Oxff
btfss status,z
goto dec
movf duty_hi,w
sublw b'OOOOOOOO' ; compare
btfsc status,z
goto dec
decf duty_hi,f
dec call dutychg ;change duty cycle
return ;press check done
;-------------------------------------- ----- - --- -------------
dutychg bcf status,c ;clear carry flag
rrf duty_lo,w ;rotate duty right, result in W
movwf temp ;store in temp
bcf status,c ;clear carry flag
rrf temp,f ;bits 7-2 of duty cycle
movf temp,w
btfsc duty_hi, 1 ;duty cycle hi bit 1 = 1?
bsf temp, 7 ; yes, set duty register bit 9
btfsc duty_hi, a ;duty cycle hi bit a = 1?
bsf temp, 6 ;yes, set duty register bit 8
movf temp,w ;duty cycle bits 9-2
movwf ccpr11 ;into duty cycle register
bcf ccp1con,5 ;clear bit 1 of duty cycle reg
bcf ccp1con,4 ;clear bit a of duty cycle reg
btfsc duty_lo,l ;duty cycle bit 1 = 1?
bsf ccp1con,5 ;yes, set duty register bit 1
btfsc duty_10,0 ;duty cycle bit a = 1?
bsf ccp1con,4 ;yes, set duty register bit a
return

end
;------------------------------------------------------------
;at device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
;============================================================

An oscilloscope may be used to observe the output signal. The duty cycle will be 50 percent ini-
tially.

123
DESIGNING AND BUILDING
YOUR OWN TEST EQUIPMENT

Designing and building your own lab test equipment is a great way to have some fun and learn
more about the applications for microcontrollers in projects of your own choosing. The projects
presented are not intended to be the ultimate device of their type. They are intended as simple
examples which demonstrate PIC microcontroller techniques you can use for your own
amazement.

KEYPAD/LCD USER INTERFACE

To add to the fun and to create more useful experiments, we need a user interface which will
allow us to key in numbers corresponding to pulse width, frequency, etc. Serial communication,
interfacing an alphanumeric LCD, scanning keypads, and double precision arithmetic are
explained in P IC'n Up The Pace or MlC1'OCOntrol'n App!!. An interface using a 3x4 telephone
keypad (switch matrix) and a 1 line by 16 character alphanumeric display along with a 3-digit
decimal to 8-bit binary conversion program are presented there.

The hardware for the remaining timing and counting experiments utilizes the TC board used thus
far plus the keypad and the PICILCD boards described in PIC'n Up The Pace or
MlCl'OCOntrol'nApp!! (see Appendix D and Appendix E in this book).
Thus, there are three modules and a 5 volt power supply involved.

Since we are using a 16-bit timer here, we will need 5-digit decimal to 16-bit binary conversion
capability. The Decimal Interface chapter in PIC'n Up The Pace or Microcontrol'n Apps
describes 3-digit decimal to 8-bit binary conversion . The programming example is decentry.asm
which includes the routines for scanning the keypad, serial output of the information to be dis-
played, and decimal-to-binary conversion . Following will be an adaptation of that program.
The changes are:

• 5 decimal digits instead of3 .


• 16-bit binary result instead of 8-bit.
• Double precision addition to handle the 16-bit result.
• Runs on PIC16F870 instead of PIC 16F84 (general purpose file registers
start at Ox20 plus AID is turned oft).

124
TCBOARD PICILCD

RA1 RAO
10 RD

II
LCD
II
1
PortS

V
'" 7
KEYPAD BOARD

1 2 3

4 5 6 Note: Use 3 keypad pullup


resistors on TC board.
7 8 9

* 0 #

• 'n . •
- - ~

f~- - - _

125
5-digit Decimal To 16-bit Binary Entry Program

The program does some setup and then gets each digit in succession, multiplies by its decimal
weight (in binary) and adds it to a register pair which accumulates the total value of the number.
The first digit entered is the 10,000's place digit so it is multiplied by 10,000 and added to the
addition registers named "numsum ". The second digit entered is multiplied by 1,000, and so on,
until the binary equivalent of the number has been assembled in the numsum locations.

10,000'5

11 11
1 '0~~'~'s
10'5
1'5

n n n n n

I x 10000=
x 1000=
x10 0 ..
x 10=

Binary Sum =

To suppress high order zeros, we will need a most significant digit flag for each digit down to
100's. The first non-zero digit encountered will set the flag . From that point on, a zero should
be displayed rather than being replaced with a blank.

126
BlankTo Display RAM
Ox20

Yes

BlankTo Display RAM


Ox21

Etc.
127
i=======DECENT63.ASM================================4/28/02==
list p=l6f870
__config h'3f7l'
radix hex
j----------------------------------------------- ------- - - - - - -
cpu equates (memory map)
tmrO equ OxOl
status equ Ox03
porta equ Ox05
portb equ Ox06
intcon equ OxOb
sendreg equ Ox20
count equ Ox2l
instr equ Ox22
char equ Ox23
addr equ Ox24
digctr equ Ox25
rowctr equ Ox26
colctr equ Ox27
rowbits equ Ox28
colbits equ Ox29
temp equ Ox2a
ncount equ Ox2b
mcount equ Ox2c
test n equ Ox2d
math equ Ox2e
copy_8xn equ Ox2f
numsuml equ Ox30
numsumh equ Ox3l
tenthou equ Ox32
thou equ Ox33
hund equ Ox34
ten equ Ox35
one equ Ox36
hold equ Ox37
lobyte equ Ox38
hibyte equ Ox39
copylo equ Ox3a
copyhi equ Ox3b
flags equ Ox3c
opt reg equ Ox8l
trisa equ Ox85
trisb equ Ox86
adconl equ Ox9f
j----------------------------------------------------- - - - - - - -
bit equates
c equ o
z equ 2
rpO equ 5
j------------------------------------ ---------- ------- - - - - - - -
org OxOOO

128
start bsf status,rpO ;switch to bank 1
movlw b' 00000110' ;turn off A/D, port A
movwf adcon1
movlw b'OOOOOOOO' ;port A outputs
movwf trisa
movlw b' 01110000' ;port B inputs/outputs
movwf trisb
bcf status,rpO ;switch bac k t o bank 0
bsf porta, 1 ;output mark, bit 1 (serial - LCD)
bsf portb,O ;rows high
bsf portb,l
bsf portb,2
bsf portb,3
bcf portb,? ;unused line low
call debounce
call debounce
movlw OxOO ;blanks to display RAM
movwf instr
call sndstf isend instruction to LCD module
call debounce
movlw Ox01 ;send 16 characters to display
movwf instr
call sndstf ;send instruction to LCD module
call debounce
clrf numsumh ;clean out
clrf numsuml ;clean out
clrf flags
do10000 call scan10
movf digctr,w ;get 10000's digit
movwf tenthou ;save copy
sublw OxOO ;compare - digit=O?
btfsc status,z
goto tthzero ;yes
bsf flags,O ;no, this is ms digit
movf tenthou,w
movwf lobyte
clrf hibyte
call decmult ;times 10
call decmult ;times 10
call decmult ;times 10
call decmult ;times 10, result = x 1 0 0 0 0
call addnum ;add .1 0 0 0 0 ' s to num sum registers
movf tenthou,w ;get 10000's digit
call hex2asc ;convert binary digit to ascii
movwf char
movlw Ox 20 ;display RAM address
movwf addr
movlw Ox03 ;ascii char follows, send to display RAM
movwf instr
call sndstf ;send 10000's digit to display RAM
call debounce ;time delay - debounce switches
do1000 call scan10
movf digctr,w ;get 1000's digit
movwf thou ;save copy

129
sublw OxOO icompare - digit=O?
btfsc status,z
goto thzero iyes
bsf flags,O i no, this could be ms digit
movf thou,w
movwf lobyte
clrf hibyte
call decmult i times 10
call decmult itimes 10
call decmult itimes 10, result = x 1 0 0 0
call addnum iadd 1000's to num sum registers
movf thou,w ;get 1000's digit
call hex2asc iconvert binary digit to ascii
movwf char
movlw Ox21 idisplay RAM address
movwf addr
movlw Ox03 iascii char follows, send to display RAM
movwf instr
call sndstf isend 1000's digit to display RAM
call debounce itime delay - debounce switches
do100 call scan10
movf digctr,w iget 100's digit
movwf hund isave copy
sublw OxOO icompare - digit=O?
btfsc status,z
goto hzero iyes
bsf flags, 0 ino, this could be ms digit
movf hund,w
movwf lobyte
clrf hibyte
call decmult ; times 10
call decmult itimes 10, result = x100
call addnum iadd 100's to num sum registers
movf hund,w iget 100's digit
call hex2asc iconvert binary digit to ascii
movwf char
movlw Ox22 idisplay RAM address
movwf addr
movlw Ox03 iascii char follows, send to display RAM
movwf instr
call sndstf isend 100's digit to display RAM
call debounce itime delay - debounce switches
dolO call scan10
movf digctr,w iget 10's digit
movwf ten isave copy
sublw OxOO icompare - digit=O?
btfsc status,z
goto tzero iyes
movf ten,w
movwf lobyte
clrf hibyte
call decmult itimes 10
call addnum iadd 10's to num sum registers
movf ten,w i g e t 10's digit

130
call hex2asc iconvert binary digit to ascii
movwf char
movlw Ox23 idisplay RAM address
movwf addr
movlw Ox03 iascii char follows, send to display RAM
movwf instr
call sndstf isend 10's digit to display RAM
call debounce itime delay - debounce switches
dol call scan10
movf digctr,w iget l's digit
movwf one isave copy
movwf lobyte
clrf hibyte
call addnum iadd l's to num sum registers
movf one,w iget one's digit
call hex2asc iconvert binary digit to ascii
movwf char
movlw Ox24 ;display RAM address
movwf addr
movlw Ox03 ;ascii char follows, send to display RAM
movwf instr
call sndstf ;send l's digit to display RAM
call debounce
send movlw Ox01 ;send 16 characters to display
movwf instr
call sndstf ito LCD module
;------------------------------------------------------------
call debounce idelay, display decimal entry
call debounce
call debounce
call debounce
call debounce
call debounce
call debounce
call debounce
call debounce
call debounce
,._---------------------- ---- ---------------------------------
idisplay numsum contents high byte first with time delay
movf numsumh,w iget total high byte
movwf char
movlw Ox04 ihex byte follows, convert and display
movwf instr
call sndst f
call debounce ;delay, display high byte
call debounce
call debounce
call debounce
call debounce
call debounce
call debounce
call debounce
call debounce
call debounce

131
rnovf nurnsurnl,w iget total low byte
rnovwf char
rnovlw Ox04 ihex byte follows, convert and display
rnovwf instr
call sndstf
circle goto circle idone
i----------------------------------------------------- - - - - - - -
tthzero rnovlw Ox20 iascii blank
rnovwf char
rnovlw Ox20 idisplay RAM address
rnovwf addr
rnovlw Ox03 iascii character to display RAM
rnovwf instr
call sndstf
call debounce
goto dolOOO
j----------------------------------------------------- - - - - - - -
thzero btfsc flags, 0 irns digit entered?
goto zeroth iyes
rnovlw Ox20 iascii blank
thchar rnovwf char
rnovlw Ox2l idisplay RAM address
rnovwf addr
rnovlw Ox03 iascii character to display RAM
rnovwf instr
call sndstf
call debounce
goto dolOO
zeroth rnovlw Ox30 iascii 0
goto thchar
i----------------------------------------------------- - - - - - - -
hzero btfsc flags, 0 irns digit entered?
goto zeroh iyes
rnovlw Ox20 iascii blank
hchar rnovwf char
rnovlw Ox22 idisplay RAM address
rnovwf addr
rnovlw Ox03 iascii character to display RAM
rnovwf instr
call sndstf
call debounce
goto dolO
zeroh rnovlw Ox30 iascii 0
goto hchar
j----------------------------------------------------- - - - - - - -
tzero btfsc flags, 0 irns digit entered?
goto zerot iyes
rnovlw Ox20 iascii blank
tchar rnovwf char
rnovlw Ox23 idisplay RAM address
rnovwf addr
rnovlw Ox03 iascii character to display RAM
rnovwf instr
call sndstf

132
call debounce
goto dol
zerot movlw Ox30 ;ascii 0
goto tchar
j----------------------------------------------------- - - - - - - -
;returns with digit in digctr

scan10 bsf portb,O irows high


bsf portb,l
bsf portb,2
bsf portb,3
clrf digctr idigit counter=O
bcf portb,3 irow=4
btfss portb,S itest column 2
return i"O" key press
bsf portb,3 ideselect row 4
movlw Ox01
movwf digctr idigit counter=l
movwf rowctr irow counter=l
movwf rowbits irow bits = 0000 0001
rowout movf rowbits,w iget row bits
xorlw OxOf icomplement row bits
movwf portb ioutput row bits
movlw Ox01
movwf colctr icolumn counter=l
movlw Ox10 ;0001 0000
movwf colbits icol=l
tstcol movf portb,w iread port B
andlw Ox70 imask off rows and bit 7
movwf temp ; columns
movf colbits,w ;get column bits
xorlw Ox70 ;complement column bits
subwf temp,w ;compare with contents of temp
btfsc status,z
return ;digit available
lastc movf colctr,w ;get column count
sublw Ox03
btfsc status,z ;=3 ?
goto lastr
rlf colbits,f ;shift column bits
bcf colbits,O ;fix carry flag garbage
incf colctr,f
incf digctr,f
goto tstcol
lastr movf rowctr,w ;get row count
sublw Ox03
btfsc status,z ;=3 ?
goto scan10 ;scan 10 digit keys again
rlf rowbits,f ishift row bits
bcf rowbits,O ;fix carry flag garbage
incf rowctr,f
incf digctr,f
goto rowout
j----------------------------------------------------- - - - - - - -

133
debounce movlw Ox02 ito counter
movwf count
dbloop movlw Oxff iM
movwf mcount ito M counter
loadn movlw Oxff iN
movwf ncount ito N counter
decn decfsz ncount,f idecrement N
goto decn iagain
decfsz mcount,f idecrement M
goto loadn iagain
decfsz count,f
goto dbloop ithru loop within a loop twice -
400 milliseconds
return idone
;------------------------------------------------------------
sndstf movf instr,w iget instruction
movwf sendreg ito be sent
call ser out ito serial out subroutine
movf char,w iget character or hex byte
movwf sendreg ito be sent
call ser out ito serial out subroutine
movf addr,w iget address
movwf sendreg ito be sent
call ser out ito serial out subroutine
return
;------------------------------------------------------------
ser out bcf intcon,5 idisable tmrO interrupts
bcf intcon,7 idisable global interrupts
clrf tmrO iclear timer/counter
clrwdt iclear wdt prep prescaler assign
bsf status,rpO ito page 1
movlw b' 11011000 I iset up timer/counter
movwf opt reg
bcf status,rpO iback to page 0
movlw Ox08 iinit shift counter
movwf count
bcf porta,1 istart bit
clrf tmrO istart timer/counter
bcf intcon,2 iclear tmrO overflow flag
time1 btfss intcon,2 itimer overflow?
goto time1 ino
bcf intcon,2 iyes, clear overflow flag
nxtbit rlf sendreg,f irotate msb into carry flag
bcf porta, 1 iclear port A, bit 1
btfsc status,c itest carry flag
bsf porta, 1 ibit is set
time2 btfss intcon,2 itimer overflow?
goto time2 ino
bcf intcon,2 iclear overflow flag
decfsz count,f ishifted 8?
goto n xtbit ino
bsf porta, 1 iyes, output mark
time3 btfss intcon,2 i t i me r overflow?
goto time3 ino

134
return idone
;--------------------------------------------------------------------
imultiply 2-byte binary number by 10 decimal
ienter sub with number in hibyte and lobyte - e xit with result
in hybyte and lobyte

decmult movf lobyte,w iget low byte


movwf copylo istore copy
movf hibyte,w iget high byte
movwf copyhi istore copy
bcf status,c iclear carry flag
rlf lobyte,f irotate low byte
rlf hibyte,f irotate high byte
bcf status,c iclear carry flag
rlf lobyte,f irotate low byte
rlf hibyte,f irotate high byte
movf copylo,w ifetch low byte
addwf lobyte,f iadd low bytes
btfss status,c ;carry set?
goto contin ;no, continue
incf hibyte,f ;yes, add 1 to hi byte
contin movf copyhi,w ;fetch high byte
addwf hibyte,f ;add high bytes
bcf status,c ;clear carry flag
rlf lobyte,f ;rotate low byte
rlf hibyte,f ;rotate high byte
return
;------------------------------------------------------------
;add digit to numsum registers - enter with digit in hibyte
and lobyte

addnum movf lobyte,w ;fetch low byte


addwf numsuml,f ;add low bytes, result in numsuml
btfsc status,c ;carry set?
incf numsumh,f ;yes, add 1 to msb result
movf hibyte,w ;fetch high byte
addwf numsumh,f ;add high bytes, result in numsumh
return
j-------------------------------------------------- --- - - - - - - -
;enter with hex digit in w

hex2asc movwf hold ;store copy of hex digit


sublw Ox09 ; subtract w from 1 less than OxOa
btfss status,c ;carry flag set i f w < OxOa
goto add3?
goto add30
add3? movf hold,w ;get hex digit
addlw Ox3?
return ;return with ascii in w
add30 movf hold,w ;get hex digit
addlw Ox30
return ; return with ascii in w
j----------------------------------------------------- - - - - - - -
end

135
;------------------------------------------------------------
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
;============================================================

Using The 5-digit Decimal To 16-bit Binary Entry Program

• Power up the F870 and PICILCD boards


• LCD will display "HELLO" briefly and then go blank.
• Key in 5 digits (including leading zeros)
• 0 - must key in 5 zeros
• Range of numbers is 0 to 65535.

The number keyed in will be displayed and will be in the "numsum" registers . The most signifi-
cant byte will be displayed on completion of a time delay followed by the least significant byte
after a second time delay.

Examples:

------------------------------------------
Key In Hex High Byte Low Byte
------------------------------------------
00000 OxOOOO 0000 0000 0000 0000
00015 OxOOOF 0000 0000 0000 1111
00016 Ox0010 0000 0000 0001 0000
65535 OxFFFF 1111 1111 1111 1111
65536 OxOOOO 0000 0000 0000 0000
12345 Ox3039 0011 0000 0011 1001

The resulting number may be loaded into a counter or used for set point comparison or whatever.

Hit the reset switch on both the TC board and PICILCD board between entries.

136
DIGITAL PULSE GENERATOR

The object here is to be able to key in the desired pulse width in microseconds and to have that
pulse output when the "ready" switch is opened. The 5-digit decimal number keyed in must be
displayed on the LCD and converted to 16-bit binary for use in the compare register to deter-
mine the pulse width. The pulse is output via the CCPl pin and is terminated when the contents
of timer 1 match the contents of the compare register. The pulse is positive going.

The range of possible pulse widths is 25 to 65,535 microseconds. The range could be extended
to longer pulses by using an external clock providing 1 millisecond or 1 second pulses (as exam-
ples) .

Two programs are combined to accomplish this. They are:

• Pulse generator (one128.asm).


• Keypad/LCD user interface (decent63.asm).

+5VDC

10K . ~
TCBOARD PICJ1..CD

RAO RA1 RAO


TO RD

RO.d'l
- ..n.. CCP1
I I LCD
II
1-
Port B
680 ....

:!!I !:z ",


",
7
--- KEYPAD BOARD

1 2 3

4 5 6
Note : Use 3 keypad pulJup
resistors on TC board .
7 8 9

* 0 #

137
No

gohi

No

Get Interval From


numsum Register And
Load It Into Compare
Register

DIP Switches

1 2 345 6 7
COO C ceo

L Closed On Reset
Open When Ready

138
; =======PULSGEN.ASM=================================4/28/02==
list p=l6f870
__config h'3f7l'
radix hex
;------------------------------------------------------------
cpu equates (memory map)
tmrO equ OxOl
status equ Ox03
porta equ OxOS
portb equ Ox06
portc equ Ox07
intcon equ OxOb
pirl equ OxOc
tmrll equ OxOe
tmrlh equ OxOf
tlcon equ OxlO
ccprll equ OxlS
ccprlh equ Oxl6
ccplcon equ Oxl7
sendreg equ Ox20
count equ Ox21
instr equ Ox22
char equ Ox23
addr equ Ox24
digctr equ Ox2S
rowctr equ Ox26
colctr equ Ox27
rowbits equ Ox28
colbits equ Ox29
temp equ Ox2a
ncount equ Ox2b
mcount equ Ox2c
test n equ Ox2d
math equ Ox2e
copy_8xn equ Ox2f
numsuml equ Ox30
numsumh equ Ox3l
tenthou equ Ox32
thou equ Ox33
hund equ Ox34
ten equ Ox3S
one equ Ox36
hold equ Ox37
lobyte equ Ox38
hibyte equ Ox39
copylo equ Ox3a
copyhi equ Ox3b
flags equ Ox3c
opt reg equ Ox8l
trisa equ Ox8S
trisb equ Ox86
trisc equ Ox87
piel equ Ox8c
adconl equ Ox9f

139
i------------------------ --------------------- --- ----- - - - - - - -
bit equates
c equ 0
z equ 2
rpO equ 5
ccp1 equ 2 ;ccp1 bit 2, port C
j------------------ ----------------------------------- - - - - - - -
org OxOOO

start bsf status,rpO ;switch to bank 1


movlw b' 00000110' ;turn off A/D, po rt A
movwf adcon1
movlw b'OOOOOOOl' ;port A inputs/outputs
movwf trisa
bsf trisa,O ;port A, bit 0 input
movlw b' 01110000' ;port B inputs/outputs
movwf trisb
bcf trisc,ccp1 ;ccp1 pin output
bcf status,rpO ;switch back to bank 0
bcf portc,ccp1 ;ccp1 pin low
bsf porta, 1 ;output mark, bit 1 (serial - LCD)
bsf portb,O ;rows high
bsf portb,l
bsf portb,2
bsf portb,3
bcf portb,? ;unused line low
call debounce
call debounce
movlw OxOO ;blanks to display RAM
movwf instr
call sndstf ;send instruction to LCD module
call debounce
movlw Ox01 ;send 16 characters to display
movwf instr
call sndstf ;send instruction to LCD module
call debounce
clrf numsurnh ;clean out
clrf numsuml ;clean out
clrf flags
do10000 call scan10
movf digctr,w ;get 10000's digit
movwf tenthou ;save copy
sublw OxOO ;compare - digi t=O?
btfsc status,z
goto tthzero ;yes
bsf flags,O ;no, this is ms digit
movf tenthou,w
movwf lobyte
clrf hibyte
call decmult ;times 10
call decmult ;times 10
call decmult ;times 1 0
call decmult ;times 10, result = x 1 0 0 0 0
call addnum ;add 10000's to num sum regist ers

140
movf tenthou,w ;get 10000's digit
call hex2asc ;convert binary digit to ascii
movwf char
movlw Ox20 ;display RAM address
movwf addr
movlw Ox03 ;ascii char follows, send to display RAM
movwf instr
call sndstf ;send 10000' s digit to display RAM
call debounce ;time delay - debounce switches
do1000 call scan10
movf digctr,w ;get 1000's digit
movwf thou ;save copy
sublw OxOO ; compare - digit=O?
btfsc status,z
goto thzero ;yes
bsf flags, 0 ;no, this could be ms digit
movf thou,w
movwf lobyte
clrf hibyte
call decmult ;times 10
call decmult ;times 10
call decmult ;times 10, result = x 1 0 0 0
call addnum ;add 1000's to num sum registers
movf thou,w ;get 1000's digit
call hex2asc ;convert binary digit to ascii
movwf char
movlw Ox21 ;display RAM address
movwf addr
movlw Ox03 ;ascii char follows, send to display RAM
movwf instr
call sndstf ;send 1000's digit to display RAM
call debounce ;time delay - debounce switches
do100 call scan10
movf digctr,w ;get 100's digit
movwf hund ;save copy
sublw OxOO ; compare - digit=O?
btfsc status,z
goto hzero ;yes
bsf flags,O ; no, this could be ms digit
movf hund,w
movwf lobyte
clrf hibyte
call decmult ;times 10
call decmult ;times 10, r esul t = x 1 0 0
call addnum ;add 100 's to num sum registers
movf hund,w ;get 100's digit
call hex2asc ; convert binary digit to ascii
movwf char
movlw Ox22 ;display RAM address
movwf addr
movlw Ox03 ; a s ci i char follows, send t o d ispl a y RAM
movwf instr
call sndstf ;send 100's digit to display RAM
call debounce ; time delay - debounce switches

141
dolO call scan10
movf digctr,w ;get 10's digit
movwf ten ;save copy
sublw OxOO ;compare - digit=O?
btfsc status,z
goto tzero ;yes
movf ten,w
movwf lobyte
clrf hibyte
call decmult ;times 10
call addnum ;add 10'5 to num sum registers
movf ten,w ;get 10's digit
call hex2asc ;convert binary digit to ascii
movwf char
movlw Ox23 ;display RAM address
movwf addr
movlw Ox03 ;ascii char follows, send to display RAM
movwf instr
call sndstf ;send 10'5 digit to display RAM
call debounce ;time delay - debounce switches
dol call scan10
movf digctr,w ;get 1'5 digit
movwf one ;save copy
movwf lobyte
clrf hibyte
call addnum ;add 1'5 to num sum registers
movf one,w ;get one's digit
call hex2asc ;convert binary digit to ascii
movwf char
movlw Ox24 ;display RAM address
movwf addr
movlw Ox03 ;ascii char follows, send to display RAM
movwf instr
call sndstf ;send 1'5 digit to display RAM
call debounce
send movlw Ox01 ;send 16 characters to display
movwf instr ·
call sndstf ;to LCD module
call debounce ;delay, display decimal entry
;------------------------------------------------------------
bcf intcon,7 ;disable global interrupts
bcf intcon,6 ;disable peripheral interrupts
bsf status,rpO ;bank 1
bcf pie1,0 ;disable timer 1 interrupts
bcf pie1,2 ;disable ccp1 interrupts
bcf status,rpO ;bank 0
bcf pir1,2 ;clear ccp1 interrupt flag
clrf ccp1con ;ccp1 module off
movlw b'OOOOOOOO' ;tmr1 prescaler and tmr1 setup,
movwf t1con tmr1 off
clrf tmr1h ;clear timer 1 high
clrf tmr11 ;clear timer 1 low
ready btfss porta, 0 ; ready?
goto ready ;not yet

142
trick clrf ccprlh iclear compare register high
movlw OxOl iload compare r egister low
movwf ccprll
movlw b'OOOOlOOO' iccpl compare mode, ccpl pin high
movwf ccplcon on match, ccpl module on,
ccpl pin low
bsf tlcon,O itimer 1 on
gohi btfss pirl,2 iccpl interrupt flag set?
goto gohi inot yet
movf numsurnh,w iget interval high byte
movwf ccprlh iload compare register high
movf numsuml,w iget interval low byte
movwf ccprll iload compare register low
bsf ccplcon,O iccpl pin low on match
circle goto circle idone
i----------------------------------------------------- - - - - - - -
tthzero movlw Ox20 iascii blank
movwf char
movlw Ox20 idisplay RAM address
movwf addr
movlw Ox03 iascii character to display RAM
movwf instr
call sndstf
call debounce
goto dolOOO
j----------------------------------------------------- - - - - - - -
thzero btfsc flags,O ims digit entered?
goto zeroth iyes
movlw Ox20 iascii blank
thchar movwf char
movlw Ox2l idisplay RAM address
movwf addr
movlw Ox03 iascii character to display RAM
movwf instr
call sndstf
call debounce
goto dolOO
zeroth movlw Ox30 iascii 0
goto thchar
;------------------------------------------------------------
hzero btfsc flags, 0 ims digit entered?
goto zeroh iyes
movlw Ox20 iascii blank
hchar movwf char
movlw Ox22 idisplay RAM address
movwf addr
movlw Ox03 iascii character to display RAM
movwf instr
call sndstf
call debounce
goto dolO
zeroh movlw Ox30 iascii 0
goto hchar
j----------------------------------------------------- - - - - - - -

143
tzero btfsc flags, 0 ims digit entered?
goto zerot iyes
movlw Ox20 iascii blank
tchar movwf char
movlw Ox23 idisplay RAM address
movwf addr
movlw Ox03 iascii character to display RAM
movwf instr
call sndstf
call debounce
goto dol
zerot movlw Ox30 iascii 0
goto tchar
;------------------------------------------------------------
ireturns with digit in digctr

scan10 bsf portb,O irows high


bsf portb,l
bsf portb,2
bsf portb,3
clrf digctr idigit counter=O
bcf portb,3 irow=4
btfss portb,S itest column 2
return i"O" key press
bsf portb,3 ideselect row 4
movlw Ox01
movwf digctr idigit counter=l
movwf rowctr irow counter=l
movwf rowbits irow bits = 0000 0001
rowout movf rowbits,w iget row bits
xorlw OxOf icomplement row bits
movwf portb ioutput row bits
movlw Ox01
movwf colctr icolumn counter=l
movlw Ox10 i0001 0000
movwf colbits icol=l
tstcol movf portb,w iread port B
andlw Ox70 imask off rows and bit 7
movwf temp ico lumns
movf colbits,w iget column bits
xorlw Ox70 icomplement column bits
subwf temp,w icompare with contents of temp
btfsc status,z
return idigit available
lastc movf colctr,w iget column count
sublw Ox03
btfsc status,z i=3 ?
goto lastr
rlf colbits,f ishift column bits
bcf colbits,O ifix carry flag garbage
incf colctr,f
incf digctr,f
goto tstcol
lastr movf rowctr,w iget row count

144
sublw Ox03
btfsc status,z i=3 ?
goto scan10 iscan 10 digit keys again
rlf rowbits,f ishift row bits
bcf rowbits,O ifi x carry flag garbage
incf rowctr,f
incf digctr,f
goto rowout
;------------------------------------------------------------
debounce movlw Ox02 ito counter
movwf count
dbloop movlw Oxff iM
movwf mcount ito M counter
loadn movlw Oxff iN
movwf ncount ito N counter
decn decfsz ncount,f idecrement N
goto decn iagain
decfsz mcount,f idecrement M
goto loadn iagain
decfsz count,f
goto dbloop ithru loop within a loop twice -
400 milliseconds
return idone
,-- - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sndstf movf instr,w iget instruction
movwf sendreg ito be sent
call ser out ito serial out subroutine
movf char,w iget character or hex byte
movwf sendreg ito be sent
call ser out ito serial out subroutine
movf addr,w iget address
movwf sendreg ito be sent
call ser out ito serial out subroutine
return
;------------------------------------------------------------
ser out bcf intcon,5 idisable tmrO interrupts
bcf intcon,7 idisable global interrupts
clrf tmrO iclear timer/counter
clrwdt iclear wdt prep prescaler assign
bsf status,rpO ito page 1
movlw b' 11011000' iset up timer/counter
movwf opt reg
bcf status,rpO iback to page 0
movlw Ox08 iinit shift counter
movwf count
bcf porta, 1 istart bit
clrf tmrO istart timer/counter
bcf intcon,2 iclear tmrO overflow f l a g
time1 btfss intcon, 2 i t i me r overflow?
goto time1 ino
bcf intcon, 2 iyes, clear overflow flag
n xtbit rlf sendreg,f irotate msb into carry flag
bcf porta, 1 iclear port A, bit 1
btfsc status,c itest carry flag

145
bsf porta, 1 ibit is set
time2 btfss intcon,2 itimer overflow?
goto time2 ino
bcf intcon,2 iclear overflow flag
decfsz count,f ishifted 8?
goto nxtbit ino
bsf porta, 1 i yes, output mark
time3 btfss intcon,2 itimer overflow?
goto time3 ino
return idone
;--------------------------------------------------------------------
imultiply 2-byte binary number by 10 decimal
ienter sub with number in hibyte and lobyte - e xit with result
in hybyte and lobyte

decmult movf lobyte,w iget low byte


movwf copylo istore copy
movf hibyte,w iget high byte
movwf copyhi istore copy
bcf status,c iclear carry flag
rlf lobyte,f irotate low byte
rlf hibyte,f irotate high byte
bcf status,c iclear carry flag
rlf lobyte,f irotate low byte
rlf hibyte,f i r o t a t e high byte
movf copylo,w ifetch low byte
addwf lobyte,f iadd low bytes
btfss status,c icarry set?
goto contin ino, continue
incf hibyte,f iyes, add 1 to hi byte
contin movf copyhi,w ifetch high byte
addwf hibyte,f iadd high bytes
bcf status,c iclear carry flag
rlf lobyte,f irotate low byte
rlf hibyte,f irotate high byte
return
;------------------------------------------------------------
iadd digit to numsum registers - enter with digit in hibyte
and lobyte

addnum movf lobyte,w ifetch low byte


addwf numsuml,f iadd low bytes, result in numsuml
btfsc status,c icarry set?
incf numsumh,f iyes, add 1 to msb result
movf hibyte,w ifetch high byte
addwf numsumh,f iadd high bytes, result in numsumh
return
;------------------------------------------------------------
ienter with hex digit in w

hex2asc movwf hold istore copy of hex digit


sublw Ox09 isubtract w from 1 less than OxOa
btfss status,c icarry flag set if w < OxOa
goto add37

146
goto add30
add37 movf hold, w ;get hex digit
addlw Ox37
return ;return with ascii in w
add30 movf hold,w ;get hex digit
addlw Ox30
return ;return with ascii in w
i----------------------------------------------------- - - - - - - -
end
;------------------------------------------------------------
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
;============================================================

This device may be tested by using a PIC16F84 to measure pulse width. An example was given
earlier which measures pulse width up to 255 microseconds (pgentst.asm). This program may
be modified to measure pulse widths covering the range of the device (25 to 65,535 microsec-
onds).

Notice that short pulses can not be seen with the eye. I can't see a 1,000 microsecond pulse, but
I can see a 5,000 microsecond pulse.

Procedure:

1) In preparation for power-up:


"ready" switch on F870 closed.
2) Power-up F870 .
3) Key in the pulse width (microseconds).
4) Open the "ready" switch on the F870.
5) Observe interval via LED or PIC 16F84 circuit or storage oscilloscope.

147
DIGITAL FREQUENCY GENERATOR

Building a frequency generator is more fun and the output can be observed more easily using an
oscilloscope. The object here is to be able to key in the desired pulse width (time the output is
high or low) in microseconds. The 5-digit decimal number keyed in must be displayed on the
LCD and converted to 16-bit binary for use in the compare register to determine the pulse width.
The signal is output via the CCPl pin. The signal changes level when the contents of timer 1
match the contents of the compare register. Each time a match occurs, the interval value is
added to the compare register and the process is repeated .

The range of possible pulse widths is 25 to 65,535 microseconds which translates to a frequency
range of 7.6 Hz to 20 KHz . The range could be extended to lower frequencies by using an exter-
nal clock providing 1 millisecond or 1 second pulses (as examples) . The upper frequency limit
is largely determined by the PIC16F870 clock oscillator frequency which gets divided to gener-
ate the output signal. The software overhead also is a factor in determining the maximum fre-
quencyattainable. So - for lower frequencies, use a slower time base. For higher frequencies,
use a faster clock oscillator for the PIC16F870.

Two programs are combined to accomplish this. They are:

• Frequency generator (freeadd.asm).


• KeypadlLCD user interface (decent63 .asm).

TCBOARD PICIlCD

RA1 RAO
TD RD

...n.. CCP1
II
LCD
II
1-
PortS
680 .. ~
::! !:~ ,- V7
-=;:: KEYPAD BOARD

1 2 3

4 5 6
Note: Use 3 keypad pullup
resistors on Te board .
7 8 9

• 0 #

148
Main Interrupt
Clear CCP1 Interrupt
Program Service
Flag
Routine

+
Add Time Interval To
Compare Register

+

Toggle Output

Return From Interrupt

gohi

No

Get Interval From


numsum Register And
Load It Into Compare
Register

Circle

DIP Switches

1 2 345 6 7
o 0 0 C ceo

149
;=======FREQGEN.ASM=================================4/28/02==
list p=16f870
__config h'3f71'
radix hex
i----------------------------------------------------- - - - - - - -
cpu equates (memory map)
tmrO equ OxOl
status equ Ox03
porta equ OxOS
portb equ Ox06
portc equ Ox07
intcon equ OxOb
pirl equ OxOc
tmrll equ OxOe
tmrlh equ OxOf
tlcon equ OxlO
ccprll equ OxlS
ccprlh equ Ox16
ccplcon equ Ox17
sendreg equ Ox20
count equ Ox21
instr equ Ox22
char equ Ox23
addr equ Ox24
digctr equ Ox2S
rowctr equ Ox26
colctr equ Ox27
rowbits equ Ox28
colbits equ Ox29
temp equ Ox2a
ncount equ Ox2b
mcount equ Ox2c
test n equ Ox2d
math equ Ox2e
copy_8xn equ Ox2f
numsuml equ Ox30
numsumh equ Ox31
tenthou equ Ox32
thou equ Ox33
hund equ Ox34
ten equ Ox3S
one equ Ox36
hold equ Ox37
lobyte equ Ox38
hibyte equ Ox39
copylo equ Ox3a
copyhi equ Ox3b
flags equ Ox3c

lsb2 equ Ox3d


msb2 equ Ox3e

opt reg equ Ox81


trisa equ Ox8S

150
trisb equ Ox86
trisc equ Ox87
pie1 equ Ox8c
adcon1 equ Ox9f
;------------------------------------------------------------
bit equates
c equ 0
z equ 2
rpO equ 5
ccp1 equ 2 iccp1 bit 2, port C
;------------------------------------------------------------
org OxOOO
goto start iskip over location pointed to by
interrupt vector
org Ox004
goto iserv

start bsf status,rpO iswitch to bank 1


movlw b' 00000110' iturn off A/D, port A
movwf adcon1
movlw b'OOOOOOOO' iport A outputs
movwf trisa
movlw b' 01110000' iport B inputs/outputs
movwf trisb
bcf trisc,ccp1 iccp1 pin output
bcf status,rpO iswitch back to bank 0
bcf portc,ccp1 iccp1 pin low
bsf porta, 1 ioutput mark, bit 1 (serial - LCD)
bsf portb,O irows high
bsf portb,l
bsf portb,2
bsf portb,3
bcf portb,7 iunused line low
call debounce
call debounce
movlw OxOO iblanks to display RAM
movwf instr
call sndstf isend instruction to LCD module
call debounce
movlw Ox01 isend 16 characters to display
movwf instr
call sndstf isend instruction to LCD module
call debounce
clrf numsumh iclean out
clrf numsuml iclean out
clrf flags
do10000 call scan10
movf digctr,w iget 10000's digit
movwf tenthou isave copy
sublw OxOO icompare - digit=O?
btfsc status,z
goto tthzero iyes
bsf flags, 0 ino, this is ms digit
movf tenthou,w

151
movwf lobyte
clrf hibyte
call decmult itimes 10
call decmult itimes 10
call decmult itimes 10
call decmult itimes 10, result = x10000
call addnum iadd 10000' s to num sum registers
movf tenthou,w iget 10000' s digit
call hex2asc iconvert binary digit to ascii
movwf char
movlw Ox20 idisplay RAM address
movwf addr
movlw Ox03 iascii char follows, send to display RAM
movwf instr
call sndstf isend 10000' s digit to display RAM
call debounce itime delay - debounce switches
do1000 call scan10
movf digctr,w iget 1000's digit
movwf thou isave copy
sublw OxOO icompare - digit=O?
btfsc status,z
goto thzero iyes
bsf flags,O r no , this could be ms digit
movf thou,w
movwf lobyte
clrf hibyte
call decmult itimes 10
call decmult itimes 10
call decmult itimes 10, result = x1000
call addnum iadd 1000's to num sum registers
movf thou,w iget 1000's digit
call hex2asc iconvert binary digit to ascii
movwf char
movlw Ox21 idisplay RAM address
movwf addr
movlw Ox03 iascii char follows, send to display RAM
movwf instr
call sndstf isend 1000's digit to display RAM
call debounce itime delay - debounce switches
do100 call scan10
movf digctr,w iget 100's digit
movwf hund isave copy
sublw OxOO icompare - digit=O?
btfsc status,z
goto hzero iyes
bsf flags,O i no, this could be ms digit
movf hund,w
movwf lobyte
clrf hibyte
call decmult itimes 10
call decmult itimes 10, result = x100
call addnum iadd 100's to num sum registers
movf hund,w iget 100's digit
call hex2asc iconvert binary digit to ascii

152
movwf char
movlw Ox22 idisplay RAM address
movwf addr
movlw Ox03 i a s c i i char follows, send to display RAM
movwf instr
call sndstf isend 100's digit to display RAM
call debounce itime delay - debounce switches
dolO call scan10
movf digctr,w iget 10's digit
movwf ten isave copy
sublw OxOO icompare - digit=O?
btfsc status,z
goto tzero iyes
movf ten,w
movwf lobyte
clrf hibyte
call decmult itimes 10
call addnum iadd 10's to num sum registers
movf ten,w iget 10's digit
call hex2asc iconvert binary digit to ascii
movwf char
movlw Ox23 idisplay RAM address
movwf addr
movlw Ox03 iascii char follows, send to display RAM
movwf instr
call sndstf isend 10's digit to display RAM
call debounce itime delay - debounce switches
dol call scan10
movf digctr,w iget l's digit
movwf one isave copy
movwf lobyte
clrf hibyte
call addnum iadd l's to num sum r e g i s t e r s
movf one,w iget one's digit
call hex2asc iconvert binary digit to ascii
movwf char
movlw Ox24 idisplay RAM address
movwf addr
movlw Ox03 iascii char foll ows, se nd to display RAM
movwf instr
call sndstf isend l's digit to displa y RAM
call debounce
send movlw Ox01 isend 16 characters to display
movwf instr
call sndstf ito LCD module
call debounce idelay, display d ecimal entry
;-------------------------------------- -- ---- ----------------
bcf intcon,7 idisable global interrupts
bcf intcon,6 idisable peripheral i nterrupts
bsf status,rpO iba n k 1
bcf pie1,0 idisable timer 1 i n t e r r u p t s
bcf pie1,2 idisable ccp1 interrupts
bcf status,rpO ibank 0
bcf pir1, 2 iclear ccp1 interrupt flag

153
clrf ccplcon iccpl module off
movlw b'OOOOOOOO' itmrl prescaler and tmrl setup,
movwf tlcon tmrl off
clrf tmrlh iclear timer 1 high
clrf tmrll iclear timer 1 low
trick clrf ccprlh iclear compare register high
movlw OxOl iload compare register low
movwf ccprll
movlw b'OOOOlOOO' iccpl compare mode, ccpl pin high
movwf ccplcon on match, ccpl module on,
ccpl pin low
bsf tlcon,O itimer 1 on
gohi btfss pirl,2 iccpl interrupt flag set?
goto gohi inot yet
movf numsumh,w iget interval high byte
movwf ccprlh iload compare register high
movf numsuml,w iget interval low byte
movwf ccprll iload compare register low
bsf ccplcon,O iccpl pin low on match
bsf intcon,7 ienable global interrupts
bsf intcon,6 ienable peripheral interrupts
bsf status,rpO ibank 1
bsf piel,2 ienable ccpl compare interrupt
bcf status,rpO ibank 0
circle goto circle idone
;------------------------------------------------------------
tthzero movlw Ox20 iascii blank
movwf char
movlw Ox20 idisplay RAM address
movwf addr
movlw Ox03 iascii character to display RAM
movwf instr
call sndstf
call debounce
goto dolOOO
;------------------------------------------------------------
thzero btfsc flags,O ims digit entered?
goto zeroth iyes
movlw Ox20 iascii blank
thchar movwf char
movlw Ox2l idisplay RAM address
movwf addr
movlw Ox03 iascii character to display RAM
movwf instr
call sndstf
call debounce
goto dolOO
zeroth movlw Ox30 iascii 0
goto thchar
;------------------------------------------------------------
hzero btfsc flags,O ims digit entered?
goto zeroh iyes
movlw Ox20 iascii blank
hchar movwf char

154
movlw Ox22 idisplay RAM address
movwf addr
movlw Ox03 iascii character to display RAM
movwf instr
call sndstf
call debounce
goto dolO
zeroh movlw Ox30 iascii 0
goto hchar
i----------------------------------------------------- - - - - - - -
tzero btfsc flags, 0 ims digit entered?
goto zerot iyes
movlw Ox20 iascii blank
tchar movwf char
movlw Ox23 idisplay RAM address
movwf addr
movlw Ox03 iascii character to display RAM
movwf instr
call sndstf
call debounce
goto dol
zerot movlw Ox30 iascii 0
goto tchar
i----------------------------------------------------- - - - - - - -
ireturns with digit in digctr

scan10 bsf portb,O irows high


bsf portb,l
bsf portb,2
bsf portb,3
clrf digctr idigit counter=O
bcf portb,3 irow=4
btfss portb,S itest column 2
return i"O" key press
bsf portb,3 ideselect row 4
movlw Ox01
movwf digctr idigit counter=l
movwf rowctr irow counter=l
movwf rowbits irow bits = 0000 0001
rowout movf rowbits,w iget row bits
xorlw OxOf icomplement row bits
movwf portb ioutput row bits
movlw Ox01
movwf colctr ;column counter=l
movlw Ox10 ;0001 0000
movwf colbits icol=l
tstcol movf portb,w iread port B
andlw Ox70 imask off rows and bit 7
movwf temp icolumns
movf colbits,w iget column bits
xorlw Ox70 icomplement column bits
subwf temp,w icompare with contents of temp
btfsc status,z
return idigit available

155
lastc movf colctr,w ;get column count
sublw Ox03
btfsc status,z ;=3 ?
goto lastr
rlf colbits,f ;shift column bits
bcf colbits,O ;fix carry flag garbage
incf colctr,f
incf digctr,f
goto tstcol
lastr movf rowctr,w ;get row count
sublw Ox03
btfsc status,z ;=3 ?
goto scanlO ;scan 10 digit keys again
rlf rowbits,f ;shift row bits
bcf rowbits,O ;fix carry flag garbage
incf rowctr,f
incf digctr,f
goto rowout
j----------------------------------------------------- - - - - - - -
debounce movlw Ox02 ;to counter
movwf count
dbloop movlw Oxff ;M
movwf mcount ;to M counter
loadn movlw Oxff ;N
movwf ncount ;to N counter
decn decfsz ncount,f ;decrement N
goto decn ;again
decfsz mcount,f ;decrement M
goto loadn ;again
decfsz count,f
goto dbloop ;thru loop within a loop twice -
400 milliseconds
return ; done
j----------------------------------------------------- - - - - - - -
sndstf movf instr,w ;get instruction
movwf sendreg ;to be sent
call ser out ;to serial out subroutine
movf char,w ;get character or hex byte
movwf sendreg ;to be sent
call ser out ;to serial out subroutine
movf addr,w ;get address
movwf sendreg ;to be sent
call ser out ;to serial out subroutine
return
j------------------------------------------------ ----- - - - - - - -
ser out bcf intcon,5 ;disable tmrO interrupts
bcf intcon,7 ;disable global interrupts
clrf tmrO ;clear timer/counter
clrwdt ;clear wdt prep prescaler assign
bsf status,rpO ;to page 1
movlw b'll011000' ;set up timer/counter
movwf opt reg
bcf status,rpO ;back to page 0
movlw Ox08 ;init shift counter

156
movwf count
bcf porta, 1 istart bit
clrf tmrO istart timer/counter
bcf intcon,2 iclear tmrO overflow flag
time1 btfss intcon,2 itimer overflow?
goto time1 ino
bcf intcon,2 iyes, clear overflow flag
nxtbit rlf sendreg,f irotate msb into carry flag
bcf porta, 1 iclear port A, bit 1
btfsc status,c itest carry flag
bsf porta, 1 ibit is set
time2 btfss intcon,2 itimer overflow?
goto time2 ino
bcf intcon,2 iclear overflow flag
decfsz count,f ishifted 8?
goto nxtbit ino
bsf porta, 1 iyes, output mark
time3 btfss intcon,2 itimer overflow?
goto time3 ino
return idone
;--------------------------------------------------------------------
imultiply 2-byte binary number by 10 decimal
ienter sub with number in hibyte and lobyte - exit with result
in hybyte and lobyte

decmult movf lobyte,w iget low byte


movwf copylo istore copy
movf hibyte,w iget high byte
movwf copyhi istore copy
bcf status,c iclear carry flag
rlf lobyte,f irotate low byte
rlf hibyte,f irotate high byte
bcf status,c iclear carry flag
rlf lobyte,f irotate low byte
rlf hibyte,f irotate high byte
movf copylo,w ifetch low byte
addwf lobyte,f iadd low bytes
btfss status,c icarry set?
goto contin ino, continue
incf hibyte,f i yes, add 1 to hi byte
contin movf copyhi,w ifetch high byte
addwf hibyte,f iadd high bytes
bcf status,c iclear carry flag
rlf lobyte,f irotate low byte
rlf hibyte,f irotate high byte
return
j----------------------------------------------------- - - - - - - -
iadd digit to numsum registers - enter with digit in hibyte
and lobyte

addnum movf lobyte,w ifetch low byte


addwf numsuml,f iadd low bytes, result in numsuml
btfsc status,c icarry set?
incf numsumh,f iyes, add 1 to msb result

157
movf hibyte,w ifetch high byte
addwf numsumh,f iadd high bytes, result in numsumh
return
i--------------- -------------------------------------- - - - - - - -
ienter with hex digit in w

hex2asc movwf hold istore copy of hex digit


sublw Ox09 isubtract w from 1 less than OxOa
btfss status,c icarry flag set i f w < OxOa
goto add37
goto add30
add37 movf hold,w iget hex digit
addlw Ox37
return ireturn with ascii in w
add30 movf hold,w iget hex digit
addlw Ox30
return ireturn with ascii in w
:------------------------------------------------------------
iserv bcf pirl,2 iclear ccpl interrupt flag,
enable further interrupts
dbladd movf numsuml,w ifetch interval low
addwf lsb2,f iadd low bytes, result in lsb2
btfsc status,c icarry set?
incf msb2,f iyes, add 1 to msb result
movf numsumh,w ifetch interval high
addwf msb2,f iadd high bytes, result in msb2
movf msb2,w iload compare register high
movwf ccprlh
movf lsb2,w iload compare register low
movwf ccprll
btfss ccplcon,O icontrol bit status?
goto sbit ibit is clear
cbit bcf ccplcon,O iclear control bit
nop idelay 1 cycle to equalize paths
retfie ireturn from interrupt
sbit bsf ccplcon,O iset control bit
retfie ireturn from interrupt
;------------------------------------------------------------
end
:------------------------------------------------------------
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
;============================================================

158
With an oscilloscope connected to the output, key in various pulse widths from 25 to 65,535
microseconds and observe the output signal. Try keying in 20 and 65,536 microseconds. What
happens and why?

Procedure:

1) Power-up F870.
2) Key in the pulse width of the output signal in microseconds.
3) Observe the output using an oscilloscope.

159
TIME INTERVAL MEASUREMENT INSTRUMENT

This instrument will measure time intervals from approximately 25 microseconds to 65,535
microseconds. The internal instruction clock is used as the known frequency which is a big fac-
tor in determining the range. A clock oscillator of much higher frequency would be needed to
measure shorter time intervals (impractical) and a slower clock oscillator is needed to measure
lower frequencies (no problem). A selection of two time bases (internal instruction clock or
external clock selected via a switch and software) could be used to extend the range of this
instrument. You might like to try it.

The measurement portion of the software works like pdccp.asm. The 16-bit result is converted
to 5-digit BCD. The digits are converted to ASCII for display purposes. High order zeros are
suppressed.

Several programs are combined to accomplish this. They are:

• Period measurement (pdccp.asm).


• KeypadlLCD user interface (decent63 .asm).
• 16-bit binary to 5-digit BCD - range OxOOOO to OxFFFF from Ple'n Up The Pace
or MlC1'OCOntrol'nApps (dblb2dy.asm).

The format chosen for displaying the decimal result is:

Display RAM Ox20 21 22 23 24 25 26 27 28 29 2A 28 2C 20 2E 2F


Address
[6JST5T3J 5 I
I
onek
I
ten

tenk hund one

160
+5VDC

10K .. ;.
TCBOARD PICILCD

RAO RA1 RAO


TO RD

. II
LCD
.n. CCP1 II
'84 ON A BOARD
1.
RBO

+5 VDC

10K ....
RS1

161
Main Interrupt
Program Service
Routine

Yes

Clear CCP1 Interrupt Complement 1st


Flag Reading

CCP1 Module On,


Capture On Rising Edge Change To Capture On Add Result To 2nd
Falling Edge Reading

Clear 1st Capture And


Done Flags

No

Yes

No

Convert 16-bit Binary


Result To 5-digit BCD

DIP Switches

1 2 3 4 5 6 7
COO C ceo

L Closed On Reset
Open When Ready

162
i=======PDCK.ASM====================================4/29/02==
list p=l6f870
__config h'3f7l'
radix hex
;------------------------------------------------------------
itimer land ccpl module - period measurement demo - lcd
timer 1 free running
capture at start and end of period pulse
;------------------------------------------------------------
cpu equates (memory map)
indf equ OxOO
tmrO equ OxOl
pc equ Ox02
status equ Ox03
fsr equ Ox04
porta equ Ox05
portc equ Ox07
intcon equ OxOb
pirl equ OxOc
tmrll equ OxOe
tmrlh equ OxOf
tlcon equ Ox10
ccpr11 equ OxlS
ccpr1h equ Ox16
ccp1con equ Ox17
tenk equ Ox20
onek equ Ox21
hund equ Ox22
ten equ Ox23
one equ Ox24
sendreg equ Ox2S
count equ Ox26
instr equ Ox27
char equ Ox28
addr equ Ox29
ncount equ Ox2a
mcount equ Ox2b
hold equ Ox2c
index equ Ox2d
dig_ctr equ Ox2e
Isb1 equ Ox2f
msb1 equ Ox30
Isb2 equ Ox31
msb2 equ Ox32
flags equ Ox33 idone 3, 1st capture 2, overflow 1,
ms digit 0
one hi equ Ox34
one 10 equ Ox3S
-
opt reg equ Ox81
trisa equ Ox8S
trisc equ Ox87
pie1 equ Ox8c
adcon1 equ Ox9f

163
;------------------------------------------------------------
bit equates
c equ 0
ovflw equ 1
z equ 2
rpO equ 5
ccp1 equ 2 ;ccp1 bit 2, port C
;------------------------------------------------------------
org OxOOO
goto start ;skip over location pointed to by
interrupt vector and tables
org Ox004
goto iserv
;------------------------------------------------------------
tbl 10 addwf pc,f ;add index to program counter
retlw Ox10 ;10,000 decimal
retlw Oxe8 ;1,000 decimal
retlw Ox64 ;100 decimal
retlw OxOa ;10 decimal
,
e _

tbl hi addwf pc,f ;add index to program counter


retlw Ox27 ;10,000 decimal
retlw Ox03 ;1,000 decimal
retlw OxOO ;100 decimal
retlw OxOO ;10 decimal
;------------------------------------------------------------
start bsf status,rpO ;switch to bank 1
movlw b' 00000110' ;turn off A/D, port A
movwf adcon1
movlw b'OOOOOOOl' ;port A inputs/outputs
movwf trisa
bsf trisc,ccp1 ;ccp1 pin input
bcf status,rpO ;switch back to bank 0
bsf porta,l ;output mark, bit 1 (serial - LCD)
call debounce
call debounce
movlw OxOO ;blanks to display RAM
movwf instr
call sndstf ;send instruction to LCD module
call debounce
movlw Ox01 ;send 16 characters to display
movwf instr
call sndstf ;send instruction to LCD module
call debounce
;----------------------- -------------------------------------
bcf intcon,7 ;disable global interrupts
bcf intcon,6 ;disable peripheral interrupts
bsf status,rpO ;bank 1
bcf pie1,0 ;disable timer 1 interrupts
bcf pie1,2 ;disable ccp1 interrupts
bcf status,rpO ;bank 0
bcf pir1,2 ;clear ccp1 interrupt flag
clrf ccp1con ;ccp1 module off
movlw b'OOOOOOOO' ;tmr1 prescaler and tmr1 setup,

164
movwf t1con tmr1 off
clrf tmr1h iclear timer 1 high
clrf tmr11 iclear timer 1 low
bsf t1con,0 itimer 1 on, free running
movlw b'00000101' icapture on rising edge, ccp1
movwf ccp1con module on
bcf flags,2 iclear 1st capture flag
bcf flags,3 iclear done flag
bsf intcon,7 ienable global interrupts
bsf intcon,6 ienable peripheral interrupts
bsf status,rpO ibank 1
bsf pie1,2 ienable ccp1 interrupts
bcf status,rpO ibank 0
ready btfss porta, 0 iready?
goto ready
done btfss flags, 3 idone?
goto done
;------------------------------------------------------------
iperiod high byte in msb2
iperiod low byte in Isb2i

iconvert 16-bit binary time interval to S-digit bcd


call dblb2d icall conversion subroutine
isend digits to display ram
bcf flags, 0 iclear ms digit flag
dotenk movf tenk,w iget 10000's digit
sublw OxOO icompare - digit=O?
btfsc status,z
goto tthzero iyes
bsf flags,O ino, this is ms digit
movf tenk,w iget 10000's digit
call hex2asc iconvert binary digit to ascii
movwf char
movlw Ox20 idisplay RAM address
movwf addr
movlw Ox03 iascii char follows, send to display RAM
movwf instr
call sndstf isend 10000's digit to display RAM
call debounce itime delay
doonek movf onek,w iget 1000's digit
sublw OxOO icompare - digit=O?
btfsc status,z
goto thzero iyes
bsf flags, 0 ino, t h i s could be ms digit
movf onek,w iget 1000's digit
call hex2asc iconvert binary digit to ascii
movwf char
movlw Ox21 idisplay RAM address
movwf addr
movlw Ox03 iascii char follows, send to display RAM
movwf instr
call sndstf isend 1000's digit to display RAM
call debounce itime delay

165
dohund movf hund,w iget 100's digit
sublw OxOO icompare - digit=O?
btfsc status,z
goto hzero iyes
movf hund,w iget 100's digit
call hex2asc iconvert binary digit to ascii
movwf char
movlw Ox22 idisplay RAM address
movwf addr
movlw Ox03 iascii char follows, send to display RAM
movwf instr
call sndstf isend 100's digit to display RAM
call debounce itime delay
doten movf ten,w iget 10's digit
sublw OxOO icompare - digit=O?
btfsc status,z
goto tzero iyes
movf ten,w iget 10's digit
call hex2asc iconvert binary digit to ascii
movwf char
movlw Ox23 idisp1ay RAM address
movwf addr
movlw Ox03 iascii char follows, send to display RAM
movwf instr
call sndstf isend 10's digit to display RAM
call debounce itime delay
doone movf one,w iget l's digit
call hex2asc iconvert binary digit to ascii
movwf char
movlw Ox24 idisplay RAM address
movwf addr
movlw Ox03 iascii char follows, send to display RAM
movwf instr
call sndstf isend l's digit to display RAM
call debounce
isend time units to display ram
m movlw Ox6d iascii m
movwf char
movlw Ox26 idisplay RAM address
movwf addr
movlw Ox03 iascii character to display RAM
movwf instr
call sndstf
call debounce
i movlw Ox69 iascii i
movwf char
movlw Ox27 idisplay RAM address
movwf addr
movlw Ox03 iascii character to display RAM
movwf inst r
call sndstf
call debounce
cee1 movlw Ox63 iascii c
movwf char

166
movlw Ox28 ;display RAM address
movwf addr
movlw Ox03 ;ascii character to display RAM
movwf instr
call sndstf
call debounce
r movlw Ox72 ;ascii r
movwf char
movlw Ox29 ;display RAM address
movwf addr
movlw Ox03 ;ascii character to display RAM
movwf instr
call sndstf
call debounce
o movlw Ox6f ;ascii 0
movwf char
movlw Ox2a ;display RAM address
movwf addr
movlw Ox03 ;ascii character to display RAM
movwf instr
call sndstf
call debounce
s movlw Ox73 ;ascii s
movwf char
movlw Ox2b ;display RAM address
movwf addr
movlw Ox03 ;ascii character to display RAM
movwf instr
call sndstf
call debounce
e movlw Ox65 ;ascii e
movwf char
movlw Ox2c ;display RAM address
movwf addr
movlw Ox03 ;ascii character to display RAM
movwf instr
call sndstf
call debounce
cee2 movlw Ox63 ;ascii c
movwf char
movlw Ox2d ;display RAM address
movwf addr
movlw Ox03 ;ascii character to display RAM
movwf instr
call sndstf
call debounce
send movlw Ox01 ;send 16 characters to display
movwf instr
call sndstf ito LCD module
circle goto circle ; done
i----------------------------------------------------- - - - - - - -
iserv btfsc flags,2 ;lst capture flag set?
goto cap2 ;yes, goto 2nd capture
bsf flags,2 ;no, set 1st capture flag

167
bcf pir1,2 ;clear ccp1 interrupt flag,
enable second interrupt
bcf ccp1con,0 ;second capture on falling edge
movf ccpr1h,w ;get 1st capture high
movwf one hi ;store
movf ccpr11,w ;get 1st capture low
movwf one 10 ;store
retfie
cap2 bsf status,rpO ;bank 1
bcf pie1,2 ;disable ccp1 interrupts
bcf status,rpO ;bank 0
comf one_hi,f ;complement 1st capture high
comf one_lo,f ;complement 1st capture low
movf ccpr1h,w ;get 2nd capture high
movwf msb2 ;store
movf ccpr11,w ;get 2nd capture low
movwf lsb2 ;store
dbladd movf one_lo,w ;fetch complement of 1st low
addwf lsb2,f ;add low bytes, result in lsb2
btfsc status,c ;carry set?
incf msb2,f ;yes, add 1 to msb result
movf one_hi,w ifetch complement of 1st high
addwf msb2,f iadd high bytes, result in msb2
bsf flags,3 iset "done" flag
retfie
;------------------------------------------------------------
debounce movlw Ox02 ito counter
movwf count
dbloop movlw Oxff iM
movwf mcount ito M counter
loadn movlw Oxff iN
movwf ncount ito N counter
decn decfsz ncount,f ;decrement N
goto decn iagain
decfsz mcount,f idecrement M
goto loadn iagain
decfsz count,f
goto dbloop ;thru loop within a loop twice -
400 milliseconds
return idone
;------------------------------------------------------------
sndstf movf instr,w ;get instruction
movwf sendreg ito be sent
call ser out ito serial out subroutine
movf char,w iget character or hex byte
movwf sendreg ito be sent
call ser out ito serial out subroutine
movf addr,w iget address
movwf sendreg ito be sent
call ser out ito serial out subroutine
return
;------------------------------------------------------------
ser out bcf intcon,5 ;disable tmrO interrupts
bcf intcon,7 idisable global interrupts

168
clrf tmrO ;clear timer/counter
clrwdt ;clear wdt prep prescaler assign
bsf status,rpO ito page 1
movlw b' 11011000' ;set up timer/counter
movwf opt reg
bcf status,rpO ;back to page 0
movlw Ox08 ;init shift counter
movwf count
bcf porta,l ;start bit
clrf tmrO ;start timer/counter
bcf intcon,2 ;clear tmrO overflow flag
timel btfss intcon,2 ;timer overflow?
goto timel ;no
bcf intcon,2 ;yes, clear overflow flag
nxtbit rlf sendreg,f ;rotate msb into carry flag
bcf porta,l ;clear port A, bit 1
btfsc status,c ;test carry flag
bsf porta,l ;bit is set
time2 btfss intcon,2 ;timer overflow?
goto time2 ;no
bcf intcon,2 ;clear overflow flag
decfsz count,f ;shifted 8?
goto n xtbit ;no
bsf porta,l ;yes, output mark
time3 btfss intcon,2 ;timer overflow?
goto time3 ;no
return ;done
;-------- ------------------------------ ----------------------
;enter with hex digit in w

hex2asc movwf hold ;store copy of hex digit


sublw Ox09 ; subtract w from 1 less than OxOa
btfss status,c ;carry flag set i f w < OxOa
goto add37
goto add30
add37 movf hold,w ;get hex digit
addlw Ox37
return ;return with ascii in w
add30 movf hold,w ;get hex digit
addlw Ox30
return ;return with ascii in w
,------------------- -- --- - -------- ----- -- ----- - ----------- ----
tthzero movlw Ox20 ;ascii blank
movwf char
movlw Ox20 ; d i s p l a y RAM address
movwf addr
movlw Ox03 iascii character to display RAM
movwf instr
call sndstf
call debounce
goto doonek
;------------------------------------------------------------
thzero btfsc flags, 0 ;ms digit entered?
goto zeroth iyes

169
movlw Ox20 ;ascii blank
thchar movwf char
movlw Ox2l ;display RAM address
movwf addr
movlw Ox03 ;ascii character to display RAM
movwf instr
call sndstf
call debounce
goto dohund
zeroth movlw Ox30 ;ascii 0
goto thchar
i----------------------------------------------------- - - - - - - -
hzero btfsc flags, 0 ;ms digit entered?
goto zeroh ;yes
movlw Ox20 ;ascii blank
hchar movwf char
movlw Ox22 ;display RAM address
movwf addr
movlw Ox03 ;ascii character to display RAM
movwf instr
call sndstf
call debounce
goto dot en
zeroh movlw Ox30 ;ascii 0
goto hchar
;------------------------------------------------------------
tzero btfsc flags,O ;ms digit entered?
goto zerot ;yes
movlw Ox20 ;ascii blank
tchar movwf char
movlw Ox23 ;display RAM address
movwf addr
movlw Ox03 ;ascii character to display RAM
movwf instr
call sndstf
call debounce
goto doone
zerot movlw Ox30 ;ascii 0
goto tchar
;------------------------------------------------------------
;16-bit binary to S-digit bcd conversion

dblb2d clrf tenk


clrf onek
clrf hund
clrf ten
clrf one
clrf index
clrf dig_ctr
bcf flags,ovflw
subtr movf index,w ;get current index into w
call tbl 10 ;get ls chunk for subtraction
movwf lsbl
movf index,w ;get current index into w

170
call tbl hi ;get ms chunk for subtraction
movwf msbl
call dblsub ;to double precision subtraction
btfsc status,c ;test carry flag
goto incdig
movlw Ox20 ;load base address of table
movwf fsr
movf index,w ;get index
addwf fsr,f ; add offset
movf dig_ctr,w ;get digit counter contents
movwf indf ;store at digit loc (indexed)
movf index,w ;get index
call tbl- 10 ;get Is chunk for addition
movwf lsbl
movf index,w ;get index
call tbl hi ;get ms chunk for addition
movwf msbl
bcf flags,ovflw
call dblplus ;to double precision addition
movf index,w ;get index
sublw Ox03 ;index=3?
btfsc status,z
goto finish
incf index,f ; increment digit index
clrf dig_ctr
goto subtr
j----------------------------------------------------- - - - - - - -
incdig incf dig_ctr,f ;increment digit counter
goto subtr
j----------------------------------------------------- - - - - - - -
finish movf lsb2,w ;get l's=remainder
movwf one
return ; done
j----------------------------------------------------- - - - - - - -
dblsub bcf flags,ovflw ;clear overflow flag
comf lsbl,f ;2's complement stuff
comf msbl, f
movf lsbl,w
addlw OxOl
movwf lsbl
btfsc status,c
incf msbl, f
dblplus movf lsbl,w
addwf lsb2,f ;add low bytes
btfsc status,c
bsf flags,ovflw ;indicate overflow occurred
movf msbl, w
addwf msb2,f ;add high bytes
btfss flags,ovflw
goto dblx
btfsc status,c
goto db11
movlw OxOl
addwf msb2,f

171
goto dblx
db11 incf msb2, f
dbl x return
i- --- ------------------- -- --------------- ------------- -------
end
j------------------------------------------------------------
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
j============================================================

Procedure:

1) In preparation for power-up:


A) "Ready" switch on F870 closed.
B) "Send" switch on F84 closed.
2) Power-up both F84 and F870 .
3) Open the "ready" switch on the F870 .
4) Open the "send" switch on F84.
5) The number of microseconds the incoming pulse was high will be displayed .

172
FREQUENCY MEASUREMENT INSTRUMENT

This instrument will measure frequencies from 100 Hz (0.1 KHz) to 655.4 KHz . The sample
time is 0.1 second which is a big factor in determining the range. A shorter sample time is
required for higher frequencies and a longer sample time is needed to measure lower frequen-
cies . Switch selectable sample times (via code blocks) could be used to extend the range of this
instrument. You might like to try it.

The measurement portion of the software works like freq.asm. The 16-bit result is converted to
5-digit BCD. The least significant digit is rounded off. Then the remaining digits are converted
to ASCII for display purposes. High order zeros are suppressed and the decimal point is insert-
ed.

Several programs are combined to accomplish this. They are:

• Frequency measurement (freq.asm).


• Keypad/LCD user interface (decent63 .asm).
• 16-bit binary to 5-digit BCD - range OxOOOO to OxFFFF from PIC'n Up The Pace
or Mlcrocontrol'n Apps (dblb2dy .asm).
• Round off least significant digit - modified decmd.asm from PIC'n Up The Pace
or Microcontrol'n Apps for one digit.

The format chosen for displaying the decimal result is:

Disp lay RAM Ox20 21 22 23 24 25 26 27 28


Address

L
supressJ
Hi Order
L Zero Not
Supressed
Blank

Zeros

100 KHz signal in

10,000 pulses = 100,000 pulses 100.0 KHz


0.1 second second

Round off least signi ficant digit.


Place decimal point.

173
+5VDC

10K .. ~
TCBOARD PICIlCD

RAO RA1 RAO


TO RD

. ...n..n..n.. TMR1 II LCD


II
'84 ON A BOARD ~
RBO

174
Clear TMRO Rollover
Counter

No

Convert 16·bit Contents


OfTMR1 To 5-digit BCD

Round Off Least


Significant Digit

Send 4 Digits To LCD,


Supress High Order
Zeros

DIP SwItches

1 2 345 6 7
COO C 0 C C

L Closed On Reset
Open When Ready

175
Once the frequency is in decimal form, it should be rounded off as the measurement is accurate
to within approximately 0.1 KHz .

Example: 107.98 108.0

We will round off the decimal result to the nearest tenth of a KHz.

A little thought reveals that the rounding off process can ripple back all the way to the most sig-
nificant digit. The round off routine rounds off the least significant digit. It does not clean up
trash left in it's place because that information will not be displayed.

Details on rounding off are in PIC'n Up'!he Pace or Mlcrocontrol'n Apps.

i=======FREQCK.ASM==================================4/28/02==
list p=16f870
__config h'3f71'
radix hex
i------------------------------- - ---------------- ----- - - - - - - -
ifrequency measurement demo - lcd
gate via tmrO, internal clock, prescaler 1:1
;------------------------------------------------------------
cpu equates (memory map)
indf equ OxOO
tmrO equ Ox01
pc equ Ox02
status equ Ox03
fsr equ Ox04
porta equ OxOS
portc equ Ox07
intcon equ OxOb
tmr1l equ OxOe
tmr1h equ OxOf
t1con equ Ox10
tenk equ Ox20
onek equ Ox21
hund equ Ox22
ten equ Ox23
one equ Ox24
sendreg equ Ox2S
count equ Ox26
instr equ Ox27
char equ Ox28
addr equ Ox29
ncount equ Ox2a
mcount equ Ox2b
hold equ Ox2c
index equ Ox2d
dig_ctr equ Ox2e
lsb1 equ Ox2f
msb1 equ Ox30
lsb2 equ Ox31
msb2 equ Ox32

176
flags equ Ox33
opt reg equ Ox81
trisa equ Ox85
trisc equ Ox87
piel equ Ox8c
adconl equ Ox9f
j----------------------------------------------------- - - - - - - -
bit equates
c equ 0
ovflw equ 1
z equ 2
rpO equ 5
j----------------------------------------------------- - - - - - - -
org OxOOO

goto start ijump over tables


j----------------------------------------------------- - - - - - - -
tbl 10 addwf pc,f iadd index to program counter
retlw Oxl0 il0,000 decimal
retlw Oxe8 i l , OOO decimal
retlw Ox64 il00 decimal
retlw OxOa il0 decimal
j----------------------------------------------------- - - - - - - -
tbl hi addwf pc,f iadd index to program counter
retlw Ox27 il0,000 decimal
retlw Ox03 il,OOO decimal
retlw OxOO il00 decimal
retlw OxOO il0 decimal
j----------------------------------------------------- - - - - - - -
start bsf status,rpO iswitch to bank 1
movlw b' 00000110 I iturn off A/D, port A
movwf adconl
movlw b'OOOOOOOl' iport A inputs/outputs
movwf trisa
bsf trisc,O iport C, bit 0 input, tmrl input
bcf status,rpO iswitch back to bank 0
bsf porta,l ioutput mark, bit 1 (serial - LCD)
call debounce
call debounce
movlw OxOO iblanks to display RAM
movwf instr
call sndstf isend inst ruction to LCD module
call debounce
movlw OxOl isend 16 characters to display
movwf instr
call sndstf isend instruction to LCD module
call debounce
j---------------------------------- ------------------- - - - - - - -
bcf intcon,7 idisable global interrupts
bcf intcon,6 idisable peripheral interrupts
bcf intcon,5 idisable timer 0 interrupts
bsf status,rpO ibank 1
bcf piel,O idisable tmrl interrupt
bcf status,rpO ibank 0

177
movlw b'00000010' iprescaler and tmr1 setup,
movwf t1con tmr1 off
clrf tmr1h i c l e a r time r 1 high
clrf tmr11 iclea r timer 1 l o w, clear p rescaler
clrf tmrO iclear timer 0
clrwdt iclr WDT, prep prescaler assign
bsf status,rpO ibank 1
movlw b '11011111' iset up timer 0
movwf opt reg
bcf status,rpO ibank 0
clrf count iclear tmrO rollover counter
ready btfss porta, 0 iready?
goto ready
bsf t1con,0 iturn on timer 1
clrf tmrO iclear timer 0
bcf intcon,2 iclear timer 0 interrupt flag
oflow1 btfss intcon,2 itimer 0 overflow?
goto oflow1 inot yet
bcf intcon,2 iclear timer 0 interrupt flag
incf count,f iinc timer 0 overflow counter
movf count,w icompare
sublw Oxff idecimal 255
btfss status,z itest z flag, skip next instruction
if flag is set
goto oflow1 iagain
oflow2 btfss intcon,2 itimer 0 overflow?
goto oflow2 inot yet
bcf intcon,2 iclear tmrO interrupt flag
incf count,f iinc timer 0 overflow counter
movf count,w icompare
sublw Ox86 idecimal 134
btfss status,z itest z flag, skip ne xt instruction
if flag is set
goto oflow2 iagain
clrf tmrO iclear timer 0
tl28 btfss tmrO,7 ilook for timer 1 128
goto t128
t32 btfss tmrO,5 ilook for timer 1 128+32=160
goto t32
bcf t1con,0 istop timer 1
;- -----------------------------------------------------------
iprep for calling conversion subroutine
movf tmr1h,w iget timer 1 high
movwf msb2
movf tmr11,w iget timer 1 low
movwf lsb2
call dblb2d icall conversion subroutine
iround off l's digit
call decrnd icall round off subroutine
isend digits to display ram
bcf flags,O i c l e a r ms digit flag
dotenk movf tenk,w i g e t 10000's digit
sublw OxOO icompare - digit=O?
btfsc status, z

178
goto tthzero iyes
bsf flags, 0 ino, this is ms digit
movf tenk,w iget 10000's digit
call hex2asc iconvert binary digit to ascii
movwf char
movlw Ox20 idisplay RAM address
movwf addr
movlw Ox03 iascii char follows, send to display RAM
movwf instr
call sndstf isend 10000's digit to display RAM
call debounce itime delay
doonek movf onek,w iget 1000's digit
sublw OxOO icompare - digit=O?
btfsc status,z
goto thzero iyes
movf onek,w iget 1000's digit
call hex2asc iconvert binary digit to ascii
movwf char
movlw Ox21 idisplay RAM address
movwf addr
movlw Ox03 iascii char follows, send to display RAM
movwf instr
call sndstf isend 1000's digit to display RAM
call debounce itime delay
dohund movf hund,w iget 100's digit
call hex2asc iconvert binary digit to ascii
movwf char
movlw Ox22 idisplay RAM address
movwf addr
movlw Ox03 iascii char follows, send to display RAM
movwf instr
call sndstf isend 100's digit to display RAM
call debounce itime delay
doten movf ten,w iget 10's digit
call hex2asc iconvert binary digit to ascii
movwf char
movlw Ox24 idisplay RAM address
movwf addr
movlw Ox03 iascii char follows, send to display RAM
movwf instr
call sndstf isend 10's digit to display RAM
call debounce
isend decimal point and frequency units to display ram
decpt movlw Ox2e iascii decimal point
movwf char
movlw Ox23 idisplay RAM address
movwf addr
movlw Ox03 iascii character to display RAM
movwf instr
call sndstf
call debounce
k movlw Ox4b iascii K
movwf char
movlw Ox26 idisplay RAM address

179
movwf addr
movlw Ox03 iascii character to display RAM
movwf instr
call sndstf
call debounce
h movlw Ox48 iascii H
movwf char
movlw Ox27 idisplay RAM address
movwf addr
movlw Ox03 iascii character to display RAM
movwf instr
call sndstf
call debounce
zee movlw Ox7a iascii z
movwf char
movlw Ox28 idisplay RAM address
movwf addr
movlw Ox03 iascii character to display RAM
movwf instr
call sndstf
call debounce
send movlw Ox01 isend 16 characters to display
movwf instr
call sndstf ito LCD module
circle goto circle idone
i- --- ------------- ------------------------------------ - - - - - - -
debounce movlw Ox02 ito counter
movwf count
dbloop movlw Oxff iM
movwf mcount ito M counter
loadn movlw Oxff iN
movwf ncount ito N counter
decn decfsz ncount,f idecrement N
goto decn iagain
decfsz mcount,f idecrement M
goto loadn iagain
decfsz count,f
goto dbloop ithru loop within a loop twice -
400 milliseconds
return idone
i----- ------------------------ --- --------------------- - - - - - - -
sndstf movf instr,w iget instruction
movwf sendreg ito be sent
call ser out ito serial out subroutine
movf char,w iget character or hex byte
movwf sendreg ito be sent
call ser out ito serial out subroutine
movf addr,w iget address
movwf sendreg ito be sent
call ser out ito serial out subroutine
return
i------------------------------------------------- ---- - - - - - - -
s er out bcf intcon,5 idisable tmrO interrupts
bcf intcon,7 idisable global interrupts
clrf tmrO iclear timer/counter

180
clrwdt iclear wdt prep prescaler assign
bsf status,rpO ito page 1
movlw b' 11011000' iset up timer/counter
movwf opt reg
bcf status,rpO iback to page 0
movlw Ox08 iinit shift counter
movwf count
bcf porta,1 istart bit
clrf tmrO istart time r/counter
bcf intcon,2 iclear tmrO overflow flag
time1 btfss intcon,2 itimer overflow?
goto time1 ino
bcf intcon,2 iyes, clear overflow flag
n xtbit rlf sendreg,f irotate msb into ca rry flag
bcf porta,1 iclear port A, bit 1
btfsc status,c itest carry flag
bsf porta, 1 ibit is set
time2 btfss intcon,2 itimer ove rflow?
goto time2 ino
bcf intcon,2 iclear overflow flag
decfsz count,f ishifted 8?
goto nxtbit ino
bsf porta, 1 iyes, output mark
time3 btfss intcon,2 itimer overflow?
goto time3 ino
return idone
i----------------------------------------------------- - - - - - - -
ienter with hex digit in w

hex2asc movwf hold istore copy of he x digit


sublw Ox09 isubtract w from 1 less than OxOa
btfss status,c icarry flag set if w < OxOa
goto add3?
goto add30
add3? movf hold,w iget hex digit
addlw Ox3?
return ireturn with ascii in w
add30 movf hold,w iget hex digit
addlw Ox30
return ireturn with ascii in w
j-------------------------------- --------------------- - - - - - - -
tthzero movlw Ox20 iascii blank
movwf char
movlw Ox20 idisplay RAM address
movwf addr
movlw Ox03 iascii character to display RAM
movwf instr
call sndstf
call debounce
goto doonek
,._------------------------------- ----- -----------------------
thzero btfsc flags,O ims digit entered?
goto zeroth i yes

181
movlw Ox20 ;ascii blank
thchar movwf char
movlw Ox2l ;display RAM address
movwf addr
movlw Ox03 ;ascii character to display RAM
movwf instr
call sndstf
call debounce
goto dohund
zeroth movlw Ox30 ;ascii 0
goto thchar
;------------------------------------------------------------
;l6-bit binary to S-digit bcd conversion

dblb2d clrf tenk


clrf onek
clrf hund
clrf ten
clrf one
clrf index
clrf dig_ctr
bcf flags,ovflw
subtr movf index,w ;get current index into W
call tbl 10 ;get Is chunk for subtraction
movwf lsbl
movf index,w ;get current index into w
call tbl hi ;get ms chunk for subtraction
movwf msbl
call dblsub ito double precision subtraction
btfsc status,c ;test carry flag
goto incdig
movlw Ox20 ;load base address of table
movwf fsr
movf index,w ;get index
addwf fsr,f ;add offset
movf dig_ctr,w ;get digit counter contents
movwf indf ;store at digit 10c (indexed)
movf index,w ;get index
call tbl 10 ;get Is chunk for addition
movwf lsbl
movf index,w ;get index
call tbl hi ;get ms chunk for addition
movwf msbl
bcf flags,ovflw
call dblplus ito double precision addition
movf index,w ;get index
sublw Ox03 ;index=3?
btfsc status,z
goto finish
incf index,f ;increment digit index
clrf dig_ctr
goto subtr
;------------------------------------------------------------
incdig incf ;increment digit counter

182
goto subtr
;------------------------------------------------------------
finish movf lsb2,w ;get l's=remainder
movwf one
return ;done
i----------------------------------------------------- - - - - - - -
dblsub bcf flags,ovflw ;clear overflow flag
comf lsbl,f ;2's complement stuff
comf msbl, f
movf lsbl,w
addlw OxOl
movwf lsbl
btfsc status,c
incf msbl,f
dblplus movf lsbl,w
addwf lsb2,f ;add low bytes
btfsc status,c
bsf flags,ovflw ;indicate overflow occurred
movf msbl,w
addwf msb2,f ;add high bytes
btfss flags,ovflw
goto dblx
btfsc status,c
goto db11
movlw OxOl
addwf msb2,f
goto dblx
db11 incf msb2,f
dblx return
;------------------------------------------------------------
;decimal roundoff subroutine
;rounds off the least significant digit of a
S-digit decimal number
;the contents of file register one are left as trash

decrnd movf one,w ;get one into W


sublw Ox04 ;subtract W from 4, result in W
btfsc status,c
return ;carry set, leave as is
rndl incf ten ;carry clear, round up
movf ten,w ; compare
sublw OxOa ;result 10 decimal?
btfss status,z
return ;not 10
clrf ten
rndlO incf hund
movf hund,w ; compare
sublw OxOa ; result 10 decimal?
btfss status,z
return ;not 10
clrf hund
incf onek
movf onek,w ; compare
sublw OxOa ;result 10 decimal?

183
btfss status,z
return inot 10
clrf onek
incf tenk
return
-------------------------------------------------------------
,
end
;------------------------------------------------------------
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
brown-out reset enabled
lvp disabled
debug mode disabled
; ============================================================

Procedure:

1) In preparation for power-up:


A) "Ready " switch on F870 closed.
B) "Send" switch on F84 closed .
2) Power-up both F84 and F870 .
3) Open the "ready" switch on the F870 .
4) The frequency of the incoming signal will be displayed in KHz.

Try measuring frequencies from 0.1 to 655.4 KHz. The "ready" switch is not really necessary.
Once under way, new measurements may be taken by resetting the F870.

You will doubtless think of ways to improve this instrument and I would encourage you to do so.
My suggestions would include:

• Loop back to sample the input on a periodic basis.


• Shorten the "debounce" time delay which was used here to make it easy to show the
commonality between the instrument programs. There is no keypad in this
application, so there are no switches to debounce. A delay is required to allow the
PIC/LCD module to send 16 characters to the display. 5 milliseconds
should do the trick.
• Add range switching by adding switching and sample time code modules.

CREATING YOUR OWN COMBINATION SIGNAL GENERATOR AND INSTRUMENT

There is lots of opportunity to expand on what you have learned by designing and building your
own combination device . You have probably noticed that the hardware involved in the four sig-
nal generator/instruments is almost identical. The keypad is not used in the two measurement
instruments, but it is not in the way either. It has two function keys that are not being used (so
far) . We have talked about selectable time bases and selectable sample times . A third PIC
microcontroller could be used to generate multiple time bases and the F870 could tell the third
PIC microcontroller what to do.
HMMmmmmm

184
APPENDIX A
PROGRAM LISTINGS vs. PAGE NUMBER

By Page Number Alphanumeric

128.asm 6 128.asm 6
pgentst.asm 9 2000.asm 13
32.asm 10 32.asm 10
2000.asm 13 65280.asm 16
65280.asm 16 capt.asm 55
freqout.asm 18 ccppwm.asm 108
pgmr1.asm 24 ccppwmx.asm 121
tmr2.asm 30 cmpr.asm 59
tsttmr2.asm 34 decent63.asm 128
tmr1.asm 48 free.asm 69
read.asm 52 freeadd.asm 73
capt.asm 55 freq.asm 91
cmpr.asm 59 freqck.asm 176
sngl.asm 64 freqgen.asm 150
one128.asm 66 freqout.asm 18
free.asm 69 hdwpwmx.asm 104
freeadd.asm 73 hdwpwmy.asm 113
period.asm 79 icd2.asm 212
pdccp.asm 84 lcdtst.asm 196
freq.asm 91 lcdslv.asm 199
seconds.asm 94 one128.asm 66
time.asm 98 pdccp.asm 84
hdwpwmx. asm 104 pdck.asm 163
ccppwm.asm 108 period.asm 79
hdwpwmy . asm 113 pgentst.asm 9
ccppwmx.asm 121 pgmr1.asm 24
decent63.asm 128 pulsgen.asm 139
pulsgen.asm 139 read.asm 52
freqgen.asm 150 seconds.asm 94
pdck.asm 163 sngl.asm 64
freqck.asm 176 time.asm 98
lcdtst.asm 196 tmr1.asm 48
lcdslv.asm 199 tmr2.asm 30
icd2.asm 212 tsttmr2.asm 34

185
APPENDIX 8
PIC16F870 CONTROL REGISTERS

186
T1CON

Controls Timer 1 (TMRI).

U U ANI RNJ RNJ ANI RNJ RNJ

_ _ _- - ' - !
T_1_C_K_P_S1_1 T1CKPSO T10SCEN
'-1 ! T1SYNC TMR1CS I TMR10N I Ox10

7 o
R = Readablebit
W .. Writeable bit
U = Unimplementedbit, read as ·0·
Power on reset 00000000

Bits 7,6 Unimplemented, read as "0"

Bits 5,4 T1CKPS1:T1CKPSO: Timer 1 Input Clock Prescale


Select Bits
00 = 1:1 Prescale value
01 = 1:2 Prescale value
10 1:4 Prescale value
11 = 1:8 Prescale value

Bit 3 T10SCEN: Timer 1 Oscillator Enable Bit


1 = Oscillator Enabled
o = Oscillator Shut Off
Note: The oscillator inverter and feedback
resistor may be turned off to eliminate
power drain.

Bit 2 T1SYNC: Timer 1 External Clock Input


Synchronization Control Bit
When TMR1CS Bit = 1:
1 Do not synchronize external clock
input
o = Synchronize e xternal clock input
When TMR1CS Bit = 0:
This bit is ignored. Timer 1 uses the
internal clock whe n TMR1CS = 0

Bit 1 TMR1CS: Timer 1 Clock Source Select Bit


1 = External clock from T1CKI (on rising edge)
o = Internal clock (Fose/4)
Bit 0 TMR10N: Timer On Bit
1 = Enables Timer 1
o = Stops Timer 1

187
T2CON

Controls Timer 2 (TMR2).

U RN.J RNJ RN.J RN.J RN.J RN.J RN.J


I
I TOUTPS3 ! TOUTPS2
I I I
! TOUTPS1 ~ TOUTPSO : TMR20N
I T2CKPS1 I
: T2CKPSO
I Ox12

7 0
R = Readable bit
W = Writeable bit
U = Unimplemented bit. read as ·0·
Power on reset 00000000

Bit 7 Unimplemented, read as "0"

Bits 6,3 TOUTPS3:TOUTPSO: Timer 2 Output Post scale


Select Bits
0000 = 1:1 Postscale value
0001 1:2 Postscale value


1111 1:16 Post scale value

Bit 2 TMR20N: Timer 2 On Bit


1 TMR2 On
a = TMR2 Off
Bits 1,0 T2CKPS1:T2CKPSO: Timer 2 Clock Prescale Select Bits
00 = 1:1 Prescale value
01 1:4 Prescale value
1x 1:16 Prescale value

188
CCP1CON

Controls CCPl Module.

U U RNV RNV RNV RNV RNV

CCP1X CCP1Y CCP1M3 CCP1M2 CCP1M1 CCP1MO I ox17

7 o
R = Readab le bit
W = Writeable bit
U = Unimplemented bit, read as "0"
Power on reset 00000000

Bit 7,6 Unimplemented, read as "a"


Bits 5,4 CCP1X:CCP1Y: PWM Least Significant Bits
Capture Mode - Unused
Compare Mode - Unused
PWM Mode - 2 bits are least significant bits of the
PWM duty cycle. The 8 most significant bits
are in CCPR1L.

Bits 3-0 CCP1M3:CCP1MO: CCP1 Mode Select Bits


0000 = CCP1 Module Off (Resets CCP1 Module)
0100 Capture mode, every falling edge
0101 Capture mode, every rising edge
0110 = Capture mode, every 4th rising edge
0111 = Capture mode, every 16th rising edge
1000 = Compare mode, set output pin on match
and set CCP1 interrupt flag
1001 = Compare mode, clear pin output on match
and set CCP1 interrupt flag
1010 = Compare mode, software interrupt on match
and set CCP1 interrupt flag, CCP1 pin
unaffected
1011 = Compare mode, trigger special event on
match, set CCP1 interrupt flag and clear TMR1.
Also starts AID conversion (if AID module
is enabled) .
11xx = PWM mode

189
INTCON

Interrupt Control Register.

RNJ RNJ RNJ RNJ RfIN

GIE PEIE TOlE INTE RBIE TOIF INTF RBIF OxOB


I
7 0
R = Readable bit
W = Writeable bit
Power on reset OOOOOOOx

Bit 7 GIE: Global Interrupt Enable Bit


1 = Enables all unmasked interrupts
o = Disables all interrupts
Bit 6 PEIE: Peripheral Interrupt Enable Bit
1 = Enables all unmasked peripheral interrupts
o = Disables all peripheral interrupts
Bit 5 TOIE: TMRO Overflow Interrupt Enable Bit
1 = Enables TMRO overflow interrupts
o = Disables TMRO overflow interrupts
Bit 4 INTE: RBO/INT External Interrupt Enable Bit
1 = Enables RBO/INT e xternal interrupts
o = Disables RBO/INT external interrupts
Bit 3 RBIE: RB Port Change Enable Bit
1 = Enables RB port change interrupts
o = Disables RB port change interrupts
Bit 2 TOIF: TMRO Overflow Interrupt Flag
1 = TMRO overflowed (must be cleared in software)
o = TMRO did not overflow
Bit 1 INTF: RBO/INT External Interrupt Flag
1 = RBO/INT e xternal interrupt occurred (must be
cleared in software)
o = RBO/INT e xternal interrupt did not occur
Bit 0 RBIF: RB Port Change Interrupt Flag
1 = At least one of the RB7:RB4 pins changed state
(must be cleared in software)
o = None of the RB7:RB4 pins have changed state

190
PIE1

Peripheral Interrupt Enable Register.

a
ADIE RCIE TXIE SSP IE CCP11E TMR21E TMR11E I Ox8C

7 o
R .. Readable bit
W .. Writeable bit
a .. Reserved, ANY, maintain as "0"
Power on reset 00000000

Bit 7 Reserved, always maintain this bit clear

Bit 6 ADIE: AID Converter Interrupt Enable Bit


1 = Enables AID converter interrupts
o = Disables AID converter interrupts
Bit 5 RCIE: USART Receive Interrupt Enable Bit
1 = Enables USART receive interrupts
o = Disables USART receive interrupts
Bit 4 TXIE: USART Transmit Interrupt Enable Bit
1 = Enables USART transmit interrupts
o = Disables USART transmit interrupts
Bit 3 SSPIE: Synchronous Serial Port Interrupt Enable Bit
1 = Enables SSP interrupts
o = Disables SSP interrupts
Bit 2 CCP1IE: CCPl Interrupt Enable Bit
1 = Enables CCPl interrupts
o = Disables CCPl interrupts
Bit 1 TMR2IE: TMR2 Interrupt Enable Bit
1 = Enables TMR2 interrupts
o = Disables TMR2 interrupts
Bit 0 TMR1IE: TMRl Overflow Interrupt Enable Bit
1 = Enables TMRl overflow interrupts
o = Disables TMRl overflow interrupts

191
PIR1

Peripheral Interrupt Flag Register.

Q R R

ADIF RCIF TXIF SSPIF CCP11F TMR21F TMR11F I OxOC

7 o
R = Readable bit
W = Writeable bit
Q = Reserved, RNV, maintain as "0"
Power on reset 00000000

Bit 7 Reserved, always maintain this bit clear

Bit 6 ADIF: A/D Converter Interrupt Flag


1 = An A/D conversion is completed
o = An A/D conversion is not complete
Bit 5 RCIF: USART Receive Interrupt Flag
1 = USART receive buffer is full
o = USART receive buffer is empty
Bit 4 TXIF: USART Transmit Interrupt Flag
1 = USART transmit buffer is full
o = USART transmit buffer is empty
Bit 3 SSPIF: Synchronous Serial Port Interrupt Flag
(must be cleared in software)
1 = Transmission/reception complete
o = Waiting to transmit/receive
Bit 2 CCP1IF: CCPl Interrupt Flag
Capture Mode
1 = Capture occurred (flag must be cleared in
software)
o = Capture has not occurred
Compare Mode
1 = Compare match occurred (flag must be
cleared in software)
o = Compare match has not occurred
PWM Mode - Unused

Bit 1 TMR2IF: TMR2 Interrupt Flag


1 = TMR2 interrupt occurred (must be cleared in
software)
o = TMR2 interrupt has not occurred
Bit 0 TMR1IF: TMRl Overflow Interrupt Flag
1 = TMRl overflow occurred (must be cleared in
software)
o = TMRl overflow has not occurred
192
APPENDIX C
'84 ON A BOARD

A simple circuit module may be assembled for use in the experiments in this book. It includes
an I 8-pin zero insertion force (ZIF) socket for a PICI6F84 with clock oscillator, reset, power
supply terminal block, power supply decoupling capacitors, and screw terminal blocks as a
means of connecting the microcontroller port lines to the other circuitry used in the experiments .
These items are required in or are common to all of the experiments which use the PICI6F84.

Port A Port B
God +5V 4 3 2 1 0 7 6 543 2 o
10 01 10 ) 0 ) 0 I I) 0 ( o ( ~ ( ql

RA4 RA3 RA2 RA1 RAO RB7 RB6 RB5 RB4 RB3 RB2 RBl RBOI
Vdd I INT

~~
10

--
+
==
0.1 ILl TOCKI

Vss
PIC16F84A

OSC2 OSCl 'MCi:R

i
NC

141 8 10K .. :-,


A
4MHzClock v
Oscillator 100

7
Reset ~r
1! NC

193
I would definitely use a ZIF socket for the F84 to avoid bending or damaging the pins. 18-pin
ZIF sockets are becoming expensive and difficult to find. The once common part made by the
TEXTOOL division of 3M is still available from Digi-Key. It is the gold plated (literally) ver-
sion (3M part number 218-3341-00-0602J) and the cost is around $18.

A 24-pin Aries socket is available from JDR Microdevices, JAMECO, Digi-Key and others.
The Aries part number is 24-6554-10 (tin plated contacts) and the price will be in the $8 range.
Simply ignore the extra 6 pins.

For the experiments in this book, there are no unused input port lines, so pullup resistors for
unused inputs are not needed .

The additional components specific to each of the experiments may be installed on a solderless
breadboard. The number of components and wiring installed on the solderless breadboard will
be minimal and chances are you won't want to preserve the specialized part of the circuit after
you have done the experiment anyway (on to better things) .

Two '84 on a board modules are needed for some of the experiments.

194
APPENDIX 0
PIC/LCD

The PIC/LCD circuit presented in PIC'n Up The Pace and Microcontrol'n Appg is shown here
along with code to test it after it is assembled as well as the code needed to use it as a serial slave
LCD unit as part of the experiments in this book.

PIC/LCD Circuit

+5V _.....-
.....,
10K .. ~ .. -
;.- lRA2 RAl 18 -
2 RA3 RAO 17 Serial Input

3 RA4fTOCKI OSCl 16

--
4 MCLR OSC2 15 - N.C.
+5V

5 Vss
PIC16F84A
Vdd 14 ~+5V
1
.::b. 6 RBOIINT RB7 13
4MHz
Clock
Oscillator
+5V 7 RBl RB6 12

10K .. ~ - 8 RB2 RB5 11 f--- N.C.


l ~
1000
A r-- 9 RB3 RB4 10 I - -
y

Reset ~

~ Goo +5V

0 7
10 (
E
AS
10 ILl + 0.1 J.1l
+5V
= ==
;::
--b
==- +5V
00000000000000

LCD
I I

195
Testing The Circuit

The complete assembly source code for testing the display demo circuit follows . The display
should have a blinking cursor at the left position and all other characters should be blank. This
demonstrates that the circuit and LCD module are operating properly.

i=======LCDTST.ASM====================================4/21/02
list p=16F84a
__config h'3ff1'
radix hex
;------------------------------------------------------------
cpu equates (memory map)
status equ Ox03
porta equ Ox05
portb equ Ox06
count1 equ OxOc
count 2 equ OcOd
trisa equ Ox85
trisb equ Ox86
;------------------------------ ------------------------------
bit equates
rpO equ 5
;------------------------------------------------- - ----------
org OxOOO

sta rt bsf status,rpO iswitch to bank 1


movlw b'OOOOOOOO' ioutputs
movwf trisa
movwf trisb
bcf status,rpO iswitch back to bank 0
movlw b'OOOOOOOO' iall outputs low

196
movwf porta
movwf portb
call del 5 iallow lcd time to initialize itself
call initlcd iinitialize display
circle goto circle idone
j----------------------------------------------------- - - - - - - -
initlcd bcf porta, 1 iE line low
bcf porta,2 iRS line low, set up for control
call del 125 idelay 125 microseconds
movlw Ox38 i8-bit, 5X7
movwf portb i 0011 1000
call pulse ipulse and delay
movlw OxOf idisplay on, cursor blinking
movwf portb i 0000 1111
call pulse
movlw Ox01 iclear display
movwf portb iOOOO 0001
call pulse
call del 5 idelay 5 milliseconds after in it
return
j----------------------------------------------------- - - - - - - -
del 125 movlw Ox2a iapprox 42x3 cycles (decimal)
movwf count1 iload counter
repeat decfsz count1,f idecrement counter
goto repeat inot 0
return icounter 0, ends delay
j----------------------------------------------------- - - - - - - -
del 5 movlw Ox28 idecimal 40
movwf count2 ito counter
delay call del 125 idelay 125 microseconds
decfsz count2,f ido it 40 times = 5 milliseconds
goto delay
return icounter 0, ends delay
j----------------------------------------------------- - - - - - - -
pulse bsf porta, 1 iPulse E line
nop idelay
bcf porta, 1
call del 125 idelay 125 microseconds
return
j----------------------------------------------------- - - - - - - -
end
j-------------------- ------------------------------ --- - - - - - - -
iat device program time, select:
code protection off
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
j====================================================== = = = = = =

197
LCD MODULE SERIAL INTERFACE

It is useful to employ the LCD module as part of a more complex system by connecting it to the
TC board via a I-wire serial interface. We can do it by combining techniques and boards we
already have . The TC board will be the master and the PICILCD unit will be the slave.

1 Wire

MASTER SLAVE

RA1 RAO
TO RO PICILCD

TCBOARD

II LCD
II
-
My list of functions the slave should perform under direction of the master is:

• Display "HELLO" at power-on


• Display "TEST" on command
• Blank display
• Place a single ASCII character in anyone of 16 display RAM locations
• Send the contents of display RAM to the display
• Display a hex byte as 2 hex digits and as 8 bits

There are lots of ways to do this. My solution follows. You may want to modify mine for your
own use or to do it a different way altogether. See PICln Up The Pace or Microcontrol'n Apps
for details .

Any time something is sent to the display, an instruction must be included to tell the slave what
to do. Sometimes there will be an ASCII character or hex byte to be sent. Sometimes an
address (RAM location) must be specified. I decided the easiest way to do this is always send 3
bytes at a time even if only 1 or 2 are needed. This makes the software simpler at the flow chart
level.

198
Packets - 3 bytes (2nd and 3rd may be garbage).

Instruction ASCII Character Display RAM


Or Address
Hex Byte

OxOO Blank display RAM


Ox01 Send 16 characters to display
Ox02 Disp lay "TEST"
Ox03 ASCII character and display
RAM address follow - send
character to display RAM
Ox04 Hex byte follows - convert to
ASCII and display

OxOO
Ox01
OX02
OX03 Ox (char) Ox (addr)
Ox04 Ox(hex)

The PIC16F84 code for operating the PICILCD module as a slave follows . The code for the
PIC16F870 on the TC board used in the experiments in this book includes the code to perform
the master function for communication between the two boards.

;=======LCDSLV.ASM==================================4/16/02 ==
list p=16f84a
__config h'3ff1'
radix hex
i----------------------------------------------------- - - - - - - -
cpu equates (memory map)
indf equ OxOO
tmrO equ Ox01
pc equ Ox02
status equ Ox03
fsr equ Ox04
porta equ OxOS
portb equ Ox06
intcon equ OxOb
hexbyte equ OxOc
mS_dig equ OxOd
Is_dig equ OxOe
hold equ OxOf
sa equ Ox10
sb equ Oxll
sc equ Ox12

199
sd equ Ox13
count1 equ Ox14
count2 equ Ox15
rcvreg equ Ox16
count equ Ox17
temp equ Ox18
instr equ Ox19
char equ Ox1a
addr equ Ox1b
holdbits equ Ox1c
opt reg equ Ox81
trisa equ Ox85
trisb equ Ox86
i----------------------------------------------------- - - - - - - -
bit equates
c equ o
z equ 2
rpO equ 5
i----------------------------------------------------- - - - - - - -
org OxOOO

start bsf status,rpO ;switch to bank 1


movlw b'OOOOOOOl' ;port A inputs/outputs
movwf trisa
movlw b'OOOOOOOO' ;port B outputs
movwf trisb
bcf status,rpO ;back to bank 0
movlw b'OOOOOOOO' ;all outputs low
movwf portb
bcf porta,l ;all outputs low
bcf porta,2
bcf porta,3
bcf porta, 4
call blanks ;fill display RAM with blanks
call hello ;create message in display RAM
call del 5 ;allow lcd time to initialize itself
call initlcd ;initialize display
call disp16 ;send 16 characters to display
enterm clrf rcvreg ;yes
call ser in ;to serial in subroutine
movf rcvreg,w ;get byte received
movwf instr ;store instruction
clrf rcvreg
call ser in ;to serial in subroutine
movf rcvreg,w ;get byte received
movwf char ;store character or byte
clrf rcvreg
call ser in ;to serial in subroutine
movf rcvreg,w ;get byte received
movwf addr ;store address
movf instr,w ;get copy of instruction
sublw OxOO ;compare with OxOO
btfsc status,z ;z flag set if bytes are equal
goto blnkram ;bytes equal

200
movf instr,w iget copy of instruction
sublw OxOl icompare with OxOl
btfsc status,z iZ flag set i f bytes are equal
goto send16 ibytes equal
movf instr,w iget copy of instruction
sublw Ox02 icompare with Ox02
btfsc status,z i z flag set if bytes are equal
goto sndtst ibytes equal
movf instr,w iget copy of instruction
sublw Ox03 icompare with Ox03
btfsc status,z i z flag set i f bytes are equal
goto chr ram ibytes equal
movf instr,w iget copy of instruction
sublw Ox04 icompare with Ox04
btfsc status,z i z flag set if bytes are equal
goto convhex ibytes equal
goto enterm iwait for next transmission
i---------------------- --- ---------------------------- - - - - - - -
blnkram call blanks ifill display ram with blanks
goto enterm iback to main
i------------------------- ---------------------------- - - - - - - -
send16 call disp16 isend display RAM contents to LCD
goto enterm iback to main
;-------------------------- ----------------------------------
sndtst call test iload display RAM with msg "TEST"
call disp16 isend display RAM contents to LCD
goto enterm iback to main
;------------------------------------------------------------
chr ram movf addr,w iget copy of display RAM address
movwf fsr istore in file select register
movf char,w iget copy of character to be display
movwf indf ito RAM address pointed to by FSR
goto enterm iback to main
.-------------------------------------------------------------
convhex movf char,w iget copy of hex byte to be converted
call disphex iconvert hex byte for display
call disp16 isend display RAM contents to LCD
goto enterm iback to main
;------------------------------------------------------------
disphex movwf hexbyte istore copy of hex byte
call blanks ifill display RAM with blanks
call sephex iseparate hex byte into 2 ASCII digits
movf mS_dig,w iget MS digit
movwf Ox20 ito display RAM
movf lS_dig,w iget LS digit
movwf Ox2l ito display RAM
swapf hexbyte,w iget copy of hex byte, swap MS/LS
call hexbits icall hex to bits
movf sa,w iget first bit
movwf Ox23 ito display RAM
movf sb,w iget second bit
movwf Ox24 ito display RAM
movf sc,w ietc.
movwf Ox25

201
movf s d,w
movw f Ox 26
movf he xbyte ,w ;get copy of hex byte
ca l l hexb i t s ;call he x to bits
mov f sa,w ;get first bit
movwf Ox 28 ;to disp lay RAM
movf s b,w ;get second bit
movwf Ox29 ; t o display RAM
movf sc,w ;etc.
movwf Ox 2a
mo vf sd,w
movwf Ox2b
return
i - - ------- ----------- ---- ----------------------------- - - - - - - -
s ephe x movf hexbyte ,w ;get copy of hex byte
andlw OxOf ;mask hi nybble
call hex2asc ;hex to ASCII conversion
movwf ls_dig ;store
swapf hexbyte,w ;get copy of he x byte, swap MS/LS
andlw OxOf ;mask hi nybble
call he x2asc ;hex to ASCII convers ion
movwf ms_dig ;store
return
i- ---------- - - ---------- ------------------------------ - - - - - - -
h e x2asc movwf hold ; store copy of hex d igit
sublw Ox09 ; subtract w from 1 less than Ox Oa
btfss status,c ;carry flag set i f w < OxOa
goto add37
goto add30
add37 movf hold,w ;get hex digit
addlw Ox37
ret urn ; return with ascii in w
add30 movf hold,w ;get hex digit
a d dlw Ox30
re turn ; return with ascii in w
i - - - - - - - ---- - - - - - - ----------------- --- --- - - ------ ----- - - - - - - -
hexbits movwf hold ;save c opy of hex digit
movlw sd ;st a rt wi t h last scratch pad address
mov wf fsr ;put it in file select register
movl w OxfO ; f ill the most s i gnificant four bits of he x digit
i orwf hold , w with l's for use as markers
movw f holdbits ; a n d store
b cf s t a t u s, c ;clear carry flag so that 0 will be
shifted in with rotate
b itslp movlw a' O' ;assume ASCI I 0 is needed
b tf sc holdbits,O ; i f LSB is 0 , then ASCII 0 is correct,
skip ne xt instruction
movl w a'l ' ; ASCII 1 needed instead
mov wf indf ; 0 or 1 to proper scratch pad register using
indirect addressing via the FSR
de c f f sr,f ;point to ne xt lower scratch pad register
a d d re s s wh i c h which will ho ld the ASCII
charact e r for the next most significant bit
rrf holdbits ,f ;rotate the lsb out, rotate marker bits right

202
btfsc holdbits,4 iis bit 4 a 0 yet? If so, it came from the
carry flag and we are done.
goto bitslp iii not, bits loop until done
return
j-------------------------------------------------- -- - - - - - - - -
blanks movlw Ox10 icount=16
movwf count1
movlw Ox20 ifirst display RAM address
movwf fsr iindexed addressing
movlw Ox20 iascii blank
store movwf indf i s t o r e in display RAM location
pointed to by file select register
decfsz countl,f i16?
goto incfsr ino
return iyes, done
incfsr incf fsr,f iincrement file select register
goto store
j----------------------------------------------------- - - - - - - -
hello movlw 'H'
movwf Ox20
movlw 'E'
movwf Ox21
movlw 'L'
movwf Ox22
movwf Ox23
movlw '0'
movwf Ox24
return
j----------------------------------------------------- - - - - - - -
test movlw 'T'
movwf Ox20
movwf Ox23
movlw 'E'
movwf Ox21
movlw 'S'
movwf Ox22
movlw
movwf Ox24
return
j--------------------------- -------------------------- - - - - - - -
initlcd bcf porta, 1 iE line low
bcf porta,2 iRS line low, set up for control
call del 125 idelay 125 microseconds
movlw Ox38 i8-bit, 5X7
movwf portb ; 0011 1000
call pulse ipulse and dela y
movlw OxOc idisplay on, cursor off
movwf portb i 0000 1100
call pulse
movlw Ox06 iincrement mode, no display shift
movwf po rtb i 0000 0110
call pulse
call del 5 idelay 5 milliseconds - required
before sending data

203
return
i------------------------------------------------------ - - - - - -
disp16 bcf porta, 1 iE line low
bcf porta,2 iRS line low, set up for control
call del 125 idelay 125 microseconds
movlw Ox80 icontrol word = address first half
movwf portb
call pulse ipulse and delay
bsf porta,2 iRS=1, set up for data
call del 125 idelay 125 microseconds
movlw Ox20 iinitialze file select register
movwf fsr
getchar movf OxOO,w iget character from display RAM
location pointed to by file select
register
movwf portb
call pulse isend data to display
movlw Ox27 i8th character sent?
subwf fsr,w isubtract w from fsr
btfsc status,z itest z flag
goto half iset up for last 8 characters
movlw 2f itest number
subwf fsr,w
btfsc status,z itest z flag
return i16 characters sent to lcd
incf fsr,f imove to next character location
goto getchar
half bcf porta,2 iRS=O, set up for control
call del 125 idelay 125 microseconds
movlw OxcO icontrol word = address second half
movwf portb
call pulse ipulse and delay
bsf porta,2 iRS=1, set up for data
incf fsr,f iincrement file select register to
select next character
call del 125 idelay 125 microseconds
goto get char
;------------------------------------------------------------
del 125 movlw Ox2a iapprox 42x3 cycles (decimal)
movwf count1 iload counter
repeat decfsz count1,f idecrement counter
goto repeat inot 0
return icounter 0, ends delay
i----------------------------------------------------- - - - - - - -
del 5 movlw Ox28 idecimal 40
movwf count2 ito counter
delay call del 125 idelay 125 microseconds
decfsz count2,f ido it 40 times = 5 milliseconds
goto delay
return icounter 0, ends delay
;------------------------------------------------------------
pulse bsf porta, 1 ipulse Eline
nop idelay
bcf porta, 1

204
call del 125 idelay 125 microseconds
return
j----------------------------------------------------- - - - - - - -
ser in bcf intcon,5 idisable tmrO interrupts
bcf intcon,7 idisable global interrupts
clrf tmrO iclear timer/counter
clrwdt iclear wdt prep prescaler assign
bsf status,rpO ito page 1
movlw b111011000 I iset up timer/counter
movwf opt reg
bcf status,rpO iback to page 0
movlw Ox08 iinit shift counter
movwf count
sbit btfsc porta, 0 ilook for start bit
goto sbit imark
movlw Ox80 istart bit received, half bit time
movwf tmrO iload and start timer/counter
bcf intcon,2 iclear tmrO overflow flag
time1 btfss intcon,2 itimer overflow?
goto time1 ino
btfsc porta, 0 istart bit still low?
goto sbit ifalse start, go back
clrf tmrO iyes, half bit time - start timer/ctr
bcf intcon,2 iclear tmrO overflow flag
time2 btfss intcon,2 itimer overflow?
goto time2 ino
bcf intcon,2 iyes, clear tmrO overflow flag
movf porta,w iread port A
movwf temp istore
rrf temp,f irotate bit 0 into carry flag
rlf rcvreg,f irotate carry into rcvreg bit 0
decfsz count,f ishifted 8?
goto time2 ino
time3 btfss intcon,2 itimer overflow?
goto time3 ino
return iyes, byte received
j------------------------------------------------------ - - - - - -
end
j----------------------------------------------------- - - - - - - -
iat device program time, select:
code protection off
watchdog timer disabled (default is enabled)
standard crystal XT (using 4 MHz osc for test)
power-up timer on
j===================================================== =======

205
206
APPENDIX E
KEYPAD

USING THE KEYPAD AND PIC/LCD WITH THE TC BOARD

TCBOARD PIC/LCD

RA1 RAO
TO RD

II LCD
II
~
Port B

, '"7
KEYPAD BOARD

+5VDC

.. ~
<. <~
1000 Use Pull up Resistors
0
j
, 1 2 3 On TC Board

1
j
, 4 5 6

2
j
, 7 8 9
Port B
3 A
, * 0 #

4
5

207
The keypad shown has 12 switches arranged in a 4 row by 3 column matrix . Pressing a key
closes a switch which electrically connects one row to one column .

COLUMNS
2 3

2--1--+-1--+-1--+-
ROWS
3- -+_1_-+_1_-+-

4--fl---+--fl---+--4---+--

Each keypad switch in the matrix is connected to the microcontroller as shown:

COLUMN
+5VDC

..> 10K

Key ~r'

Output Port
... ~ ROW
1000

If output port line is 0,


Input Port sw~ch closure will assert
=
input line low 0

The 5-digit decimal to 16-bit binary entry program in the Designing And Building Your Own
Test Equipment chapter can be used to test your keypad if you are using it the first time.

Details on scanning keypads are available in Ple'n Up The Pace or Microcontrorn Apps.

208
APPENDIX F
USING THE leo

USING THE ico


When using the ICD with the F870 applications in this book, the following must be considered:

Port B bits 7 and 6 are used by the ICD which means some minor changes are necessary. The
easiest thing to do is swap the upper 4 bits of ports Band C. This effects only those programs
which include the following:

• Displaying data using 8 LEDs which must now be split between the two ports
• Scanning the 10-key keypad.

209
The modified schematic is shown below.

Gnd +SV

r ~ f f f f ~ J:. I( o

~ DIP Socket
I 7 G S 4 3 2 1 0 10 ILl +

= ~
0.11Jf
:: =
....-- Test
Point
~
+SV
~ +SV

Keypad
Column +SV
Pullups

RC7 ReG ReS RC4 RB3 RB2 RB1 RBO Vdd Vss RB7 RBG RBS RB4

PIC16F870
+SV MCLR TOCKI CLKI CLKO CCP1
Vpp RAO RA1 RA2 RA3 RA4 RAS Vss OSC1 OSC2 RCO RC1 RC2 RC3
47K"~
L
.I N.C.
W'

~ 10oa.--.....-+---4t-+--.-+---......... -+-~.-- _ _t--_.....-+---4t-+--.-+----'


Reset
+SV
! .. ~ .. ~ . ~

ck~iaIte~
• Sockets
Serial Output TMRO Input +SV
To PIC/LCD,
Input
T TMR1 Input CCP1
4.0 MHz
All Pull4J Resistors 10K ClockOsc • Cap = 22 pI (2 reqd'
Xtal = 32.768 KHz
Sockets to allow removal.
Note: Port B and Port C Unes
are not shown in their physical
ll-
N.C.
Parts used lor external clock
experiment only
locations on the device.

TC BOARD MODIFIED FOR USE WITH ICD

210
The F870 programs in this book are listed in the following table. The programs which need to
be modified for F870 use in conjunction with the leD are identified.

F870 Programs
By Page Number Modification Required

pgmr1.asm 24 Split LED display


tmr2.asm 30 OK as is
tsttmr2.asm 34 OK as is
tmr1.asm 48 OK as is
read .asm 52 Split LED display
capt.asm 55 Split LED display
cmpr.asm 59 OK as is
sngl.asm 64 OK as is
one 128 .asm 66 OK as is
free.asm 69 OK as is
freeadd.asm 73 OK as is
period .asm 79 Split LED display
pdccp.asm 84 Split LED display
freq .asm 91 Split LED display
seconds.asm 94 OK as is
time.asm 98 Split LED display
hdwpwmx.asm 104 OK as is
ccppwm.asm 108 OK as is
hdwpwmy.asm 113 OK as is
ccppwmx.asm 121 OK as is
decent63.asm 128 Change keypad column connections
pulsgen.asm 139 Change keypad column connections
freqgen.asm 150 Change keypad column connections
pdck.asm 163 OK as is
freqck .asm 176 OK as is

Details on these modifications follow.

Split LED Display

In this situation where a byte-wide port is not available for displaying data or a bit pattern via
8 LEDs, we can solve the problem by splitting the bits between two ports.

Port C Port B
1 1 1
76543210
Bits

211
i=======ICD2.ASM===========================l2/29/01==
li s t p=l6f870
radix hex
; -- - -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
cpu equates (memory map)
status equ Ox03
po rta equ OxOS
po rtb equ Ox06
porte equ Ox07
t risa equ Ox8S
t risb equ Ox86
trisc equ Ox87
adconl equ Ox9f
crack equ Ox20 isave reg for split subroutine
e _
,
bit equates
rpO equ S
i------------------------ ----------------------------
org OxOOO
nop
start bsf status,rpO iswitch to bank 1
movlw b'OOOOOllO' iturn off A/D, port A
movwf adconl
movlw b'OOOOOOOO' iinputs/outputs
movwf trisa
movwf trisb
movwf trisc
bcf status,rpO iback to bank 0
movlw OxOf iload w with bit pattern
i--- --------------------------------------------------
movwf crack isave
call split
i - - - - - - - - - - - - - - - - - --- -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ci rcle goto circle idone
; - - - - - - - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - --- - - - - - - - - - - - - - -
split btfss crack, 0
goto clr- 0
bsf po r tb,O
do 1 btfss crack,l
goto clr- 1
bsf portb,l
do 2 btfss crack,2
goto clr 2
bsf portb,2
do 3 btf ss crac k, 3
goto clr 3
bsf po rtb,3
do 4 btfss c r ac k , 4
goto clr 4
bsf porte, 4
do S bt f s s c r a ck , S
g ot o clr S
bs f porte,S

212
do 6 btfss crack, 6
goto elr 6
bsf porte, 6
do 7 btfss eraek,7
goto elr 7
bsf porte,7
return
elr 0 bef portb,O
goto do 1
elr 1 bef portb,l
goto do 2
elr 2 bef portb,2
goto do 3
elr 3 bef portb,3
goto do 4
elr 4 bef porte, 4
goto do S
elr S bef porte,S
goto do 6
elr 6 bef porte, 6
goto do 7
elr 7 bef porte,7
return
;-----------------------------------------------------
end
;-----------------------------------------------------
iat device programming time, select:
memory unprotected
watchdog timer disabled (default is enabled)
standard crystal (using 4 MHz ose for test) XT
power-up timer on
brown-out reset disabled
lvp disabled
debug mode enabled
i=====================================================

For the F870 programs which use 8 LEDs on port B (identified in the table as needing a split
LED display) , define the register "crack" in the equates and call the subroutine shown in the
demonstration program. In other words, substitute the guts of the demo program
for movwf portb in the F870 program .

213
Keypad Connections

A keypad is used in three projects in the chapter on Designing And building Your Own Test
Equipment.

TCBOARD PICIlCD

RAl RAO
TO RD

II
LCD
II
Port B

, ~
PortC

, ~
1
4 3
KEYPAD BOARD

+5VDC

oC~

.~
oC
~

1000 Use PuJlup Resistors


0 A
, 1 2 3 On TC Board

1
j
, 4 5 6

2 A
, 7 8 9
Port B
3 A
, • 0 #

4
5

The lines connected to port B bits 4, 5, and 6 are moved to port C bits 4, 5 and 6 (see, also,
schematic shown previously). The keypad code modifications are straightforward. The pro-
grams effected are:

• decent63 .asm page 128


• pulsgen.asm page 139
• freqgen.asm page 150

214
The code changes which follow apply to all three programs.

Add porte equ Ox07

Replace movlw b I 011100000' iport B inputs/outputs


movwf trisb

With movwf trisb iport B outputs


movlw b '011100000 I iport C inputs/outputs
movwf trise

In the subroutine sean10 (pages 133, 144, and 155):

Replace btfss portb,5 itest eolumn 2

With btfss porte,S itest eolumn 2

Replace tsteol movf portb,w iread port B


andlw Ox70 imask off rows and bit 7

With tsteol movf porte,w iread port C


and1w Ox70 imask off bits 7, 3, 2, 1, 0

DIP switches 5, 6, and 7 are closed to enable the pullup resistors (see schematic).

Programming Considerations

The following must be done :

• Configuration bits:
- LVP disabled, RB3 is digital I/O
- DEBUG enabled (lCD mode enabled) .
• Cannot use port B bits 6 and 7 (used by ICD).
• Lose 1 level of stack when ICD is used .
• Program memory
- Address OxOOO must contain NOP instruction.

I recommend removing the _CONFIG directive from the program listing (for use with the
LCD) because it has no effect and may be confusing

215
216
APPENDIX G - SOURCES

Digi-Key Electronic Components


701 Brooks Avenue South PIC microcontrollers
Thief River Falls MN 56701-0677
Tel 800 344 4539
Web http://www.digikey.com

Jameco Electronics Electronic Components


1355 Shoreway Road PIC microcontrollers
Belmont CA 94002-4100
Tel 800 831 4242
Fax 800 237 6948
Web http://www.jameco.com
email info@jameco.com

JDR Microdevices Electronic Components


1850 South 10th Street PIC microcontrollers
San Jose CA 95112
Tel 800 538 5000
Fax 800 538 5005
Internet Orders http://www.jdr.com

Marlin P. Jones & Assoc Inc Electronic Components


P.O. Box 12685
Lake Park FL 33403
Tel 800 652 6733
Fax 800 432 9937
Order Online http://www.mpja.com
email orders@mpja.com

Microchip Technology Inc. PIC microcontrolle rs


2355 W. Chandler Blvd Development Systems
Chandler AZ 85224 In-Circuit Debugger
Tel 480 792 7966
Fax 480 792 4338
Web http://www.microchip.com

microEngineering Labs PIC Programmers


P.O. Box 60039 Development Boards
Colorado Springs CO 80960 Bootloader
Tel 719 520 5323
Fax 719 520 1867
web http://www.melabs.com
email support@melabs.com

217
Milford Instruments PIC Development Kits
120 High Street
South Milford, LEEDS UK LS25 5AQ
Tel 01977 683 665
Fax 01977 681465
Web http://www.milinst.demon.co.uk
eMail sales@milinst.com

218

Você também pode gostar