Sie sind auf Seite 1von 7





25 MARCH 2009

Keyword: assembly, timer 0, interrupt routine, timing delay.


A timer counts down from any specific time to zero. It usually used as an alert to people such
as alarm clock, oven, boiler, washing machine and etc. This is a simple countdown timer writing
in assembly.


A timer is a specialized type of clock. A timer can be used to control the sequence of an event
or process. Timer counts down from a specified time interval, like an hourglass. Timers can be
mechanical, electromechanical, electronic (quartz), or even software as most computers include
digital timers of one kind or another.


This project is to demonstrate on how to program a countdown timer using assembly language
which involved programming the PIC. The PIC that will be used is PIC16F84.

When the unit is turned on the last used starting count, minutes 0-99, seconds 0-59, is showing
on the display. Countdown starts when the start button is pressed. An alarm is sounded when
the count reaches zero. The alarm continues until start is pressed again. This press also
returns the starting count to the display. Pressing start before reaching zero also returns to
starting conditions. The start count can be changed if the set button is pressed before
countdown. Each digit is lit in turn, incrementing from zero until the set button is pressed again.

The new start count is saved in EEPROM after the final press of the set button. There are 15
settable start counts. You cycle through them using the select pushbutton. The set button
changes only the starting count presently displayed.

Figure 1: Schematic diagram for countdown timer.

The Source Code

The source code for this project is in ‘cntdn.ASM’ file.

a) List and Include Directive

‘LIST’ defines the processor, while additional code brought in by '#INCLUDE' define all special
function registers, bits etc.
LIST P=16F84


b) Label

#DEFINEs are used to make the code clearer.

; Here we give names to some numbers to make their use more clear ;


c) Program start

'ORG 0' says to start the code at location 0 and 'END' marks the end of the program.

; We set the start of code to originate a location zero ;



GOTO MAIN ; jump to the main routine

d) Initialization process

There are two routine going on at the same time. The main routine sets initial conditions and
then loops, checking switches and for an alarm flag at termination of the count.

; Initialization routine sets up ports and timer ;


INIT MOVLW H'C0' ; PB6 & PB7 inputs all others outputs


An interrupt routine does the multiplexing of the display and decrements the count every
second if a countdown is in progress. It also sets an alarm flag when the count reaches zero.
The interrupt is based on the overflow of timer 0, (TMR0).


; This is the interrupt routine that is jumped to when TMR0 overflows ;



CALL BITPAT ; get bit pattern

i) Timer zero (TMR0)

Timer zero continually increments. When it rolls over, a flag, T0IF in the INTCON register, is
set. The flag in software must be cleared. The timer zero interrupt is enabled (T0IE in INTCON
= 1), and enable interrupts in general, (GIE in INTCON = 1). Both bits set; an overflow of
TMR0 will raise T0IF and cause a CALL to location 4 which is a jump to the interrupt routine.
GIE is cleared when the routine is entered so other interrupts won't interfere. GIE will be reset
at the end of the routine by RETFIE, (return and enable GIE). T0IF must be cleared or it will
right back in the interrupt situation again. Code is also necessary at the beginning and end of
the routine to save and restore the values of W and the STATUS register.

ii) Timer Start Process

When we start up the PIC, TMR0 is set to increment on pulses from Port A bit 4 pin, (T0CS in
OPTION = 1). Clear T0CS (Timer 0 Clock Select), to 0 to make TMR0 increment with the
instruction cycle. This is every microsecond for a 4 MHz crystal. TMR0 will overflow after 256
µs. This is too fast. Use the prescaler to slow the rate down. The prescaler comes up assigned
to the watchdog timer, (PSA of OPTION = 1). PSA = 0 will assign it to TMR0. Bits 0-3 control
the division ratio for the prescaler. Set bits 0 and 1 to get a 1:16 rate. This gives an overflow
every 256 X 16 = 4096 µs. All of this adds up to putting a 3 in the OPTION register.
After it is set up it just goes on automatically in the background. Every 4 ms the interrupt
routine is entered. The digit to display is changed and the value from the appropriate register,
(SEC, SEC10, MIN or MIN10), is sent to the CD4511, (through Port A), where segments to be lit
are decided. A pattern is selected to turn on the appropriate transistor and sent to Port B.
Every second a call is made to EVERYSEC which decrements the count and checks for 0000. If
zero is reached the flag bit in ALARM is set. A counter INTCNT is decremented each time the
interrupt routine is entered. It is normally initially set to 244, (H'F4'). 244 X 4096 = 999424µs,
slightly less than 1 second. Every 7th time it is set to 245 instead, through the use of the
counter FUDGE. This is 1003520 µs. The average works out to 1000009 µs.

The interruption time can be calculated by the following formula.

The interruption time = (4/OSC freq) x Prescaler x Hard timer count x Soft count
= (4/ 4x106 ) x 256 x 244 x 16
= 0.999424 seconds

Above-mentioned value is a value with few errors and little interruption time. It is necessary to adjust after

The interruption time = (4/OSC freq) x Prescaler x Hard timer count x Soft count
= (4/ 4 x106 )x 256 x 245 x 16
= 1.00352 seconds

Figure 2: Interruption Time formula

iii) Timing Using Instruction Length

TMR0 is handy when something has to occur at regular intervals. Based on a 4 MHz crystal the
routine at ONEMSEC takes one millisecond, if include the two microseconds necessary for the
call. In similar fashion NMSEC take the number of milliseconds in W when the routine is
entered. The most elementary loop in the timing routines is at MICRO4. Each time through this
loop requires 4 microseconds, (two single cycle instructions and one two cycle instruction).
Notice that when W goes from 1 to 0, the last time through takes 3 microseconds. Call with 249
in W and the total time looping adds up to 995 microseconds. Add 2 for the call, two for the
return and 1 for the load of W and you end up with exactly 1000 microseconds.

For multiples of 1 millisecond, (NMSEC), we need to load an external counter and keep track of
this counter as we go through a number of loops. Since we have to allow for any number of
loops 1-255, the best we can do is make each loop come out 1 msec and ignore the slight over
head getting into the looping situation. This would be 4 microseconds to load W, do the call
and load CNTMSEC.

e) Push Button

; This loop checks for either pushbutton and acts accordingly ;


KEYCHKLOOP BTFSS PORTB,START_PB ; check for start pressed

GOTO STARTCNT ; yes, start count

A couple of routines are used in switch debouncing.

Normally open push button is attached to Port B pins RB7, (start), and RB6, (set). The port
pins are set high by activating internal pull-ups. Pull-ups are not activated upon power on
reset. To activate them make sure bit 7 of OPTION is low. When you push one of these
buttons, connection is made with a contact that is grounded. This will pull the pin low. The
problem is that the contact bounces and the connection is made and broken a number of times
before the contacts settle down into the closed position.

One solution is to put a capacitor across the switch. If it is about the right size, the time it
takes to charge prevents rapid changes of the state of the pin and sort of average out the
bounces which usually last only milliseconds or two. Usually the size is something between 0.01
and 0.1 mF. Another electronic solution is a RS flip-flop for each switch.

f) Count-down process

; If start key has been pressed then start countdown process, ;
; I initially released this code with only the setting of the ;
; run flag included. If you think about it you must also reset ;
; TMR0 to zero. TMR0 is free running and could have any value ;
; 0-255 when the button in pressed. Also INTCNT has to be ;
; initialized because the previous count could have been cancelled. ;
STARTCNT CALL WAITSTARTUP ; wait for release of start key

Processing is executed every second. It is checking first, do subtract the 1st counter in the
second and whether or not it becomes 0. In case of 0, because there is possibility of the time-
out situation, it is doing the checking whether or not all counters are 0.
When either counter is not 0, because it is not in the time-out condition, count-down processing
is executed. In the count-down processing, it checks the contents of each counter and in case
of 0, a maximum is set. The maximum of the 10th of the second is 5 and as for the other digit,
9 is a maximum. When the contents of the counter are not 0, it subtracts one. In the
interruption processing, only time-out checking and count-down processing are executed. The
time-out processing (Timer stop and Relay OFF) and the LED display processing are executed
by the timer start processing.

g) Saving Starting Count In EEPROM

Initial data is placed in EEPROM when the PIC is programmed using the DE directive at the end
of CNTDN.ASM. Location 0 of EEPROM holds an offset which decides the four locations holding
digits to be placed in the display for the starting count. Location is initially set to zero and then
incremented by four each time the select pushbutton is pressed. The four selected locations are
modified and replaced by using the set pushbutton.

h) End of coding

At the end of coding, END directive is used.


The timer is working, but there have some problem with this timer program. It is not a friendly
user because we need to set the time manually by waiting the number to increase one by one
and set the desired time. It involved writing a count to TMR0 so the remaining counts to the
255 to 0 rollover would give the desired time. But then, the timing of the first second could be
way off. TMR0 is running continually and could have any value 0-255 when the button is
pressed. We have to set it to zero to get the full 256 counts. Thus, there’s still need a lot of
practice to program this countdown timer to make it run perfectly.