Program #3 - 3 Minute Countdown Timer

;-------------------------------------------------------------------------;
; EGGTIMER.ASM A 3 minute countdown timer for boiling eggs ;
;-------------------------------------------------------------------------;
 LIST P=16F84 ; tells which processor is used
 INCLUDE "p16f84.inc" ; defines various registers etc. Look it over.
 ERRORLEVEL -224 ; supress annoying message because of tris
 __CONFIG _PWRTE_ON & _LP_OSC & _WDT_OFF ; configuration switches
;-------------------------------------------------------------------------;
; Here we set up the user defined registers ;
;-------------------------------------------------------------------------;
 CBLOCK H'0C' ; first free register address is 12
 sec ; keeps track of seconds
 sec10 ; keeps track of tens of seconds
 mins ; keeps track of minutes
 w_temp ; holds value of W during interrupt
 status_temp ; holds value of STATUS during interrupt
 finflag ; act as a flag to indicate end of countdown
 oldsec10 ; holds last displayed value of sec10
 ENDC
 ORG 0 ; start a program memory location zero
 goto main ; jump over the interrupt routine
 ORG 4
;-------------------------------------------------------------------------;
; here is the interrupt routine, happens every second if GIE is enabled ;
;-------------------------------------------------------------------------;
 movwf w_temp ; save W
 swapf STATUS,W ; save status 
 movwf status_temp ; without changing flags
 decf sec, f ; decrement seconds register
 movlw H'FF' ; check if underflow
 subwf sec, W ; will give zero if sec = H'FF'
 btfss STATUS, Z ; skip next instruction if underflow
 goto restore ; no underflow, leave interrupt routine
 movlw 9 ; change seconds register to 9
 movwf sec
 decf sec10, f ; now we follow the same procedure ...
 movlw H'FF' ; for the sec10 register
 subwf sec10, W 
 btfss STATUS, Z ; skip if underflow
 goto restore ; no underflow, leave
 movlw 5 ; change sec10 register to 5
 movwf sec10
 decf mins, f ; and decrement minutes register
 movlw H'FF' ; check if ...
 subwf mins, W ; an underflow of minutes ...
 btfss STATUS, Z ; yes means the count is finished
 goto restore ; no underflow
 incf finflag, f ; set the finished flag
restore: 
 swapf status_temp,W ; get original status back
 movwf STATUS ; into status register
 swapf w_temp,f ; old no flags trick again
 swapf w_temp,W ; to restore W
 bcf INTCON,T0IF ; clear the TMR0 interrupt flag
 retfie ; finished reset GIE
;=========================================================================;
; This is the main program ;
;=========================================================================;
main:
;-------------------------------------------------------------------------;
; initialize the ports, set up interrupts etc: ;
;-------------------------------------------------------------------------;
 movlw B'00000000' ; all bits low in W
 tris PORTA ; contents of W copied to PORT A ...
 movlw B'00010000' ; RB4 input, all other output
 tris PORTB ; and PORT B
 movlw B'00000100' ; port B pull-ups active
 ; prescalar assigned to TMR0 and set 1:32
 option ; rolls over each second
 movlw B'00100000' ; T0IE set, GIE not set yet... 
 movwf INTCON ; in the interrupt register
 clrf PORTB ; display 0
 
;-------------------------------------------------------------------------;
; initialize some other registers: ;
;-------------------------------------------------------------------------;
 clrf sec ; start with sec = zero
 clrf sec10 ; and sec10 = zero
 clrf oldsec10 ; make oldsec10 the same
 movlw D'3' ; and minutes = 3
 movwf mins 
 clrf finflag ; clear the finished flag
;-------------------------------------------------------------------------;
; wait for pushbutton to start ;
;-------------------------------------------------------------------------;
 btfsc PORTB, 4 ; switch closed, (gives 0)?
 goto $ -1 ; not yet
 ; switch has been detected closed
 ; ( no debounce necessary )
 clrf TMR0 ; start with timer at zero
 bcf INTCON, T0IF ; and make sure the interrupt flag is clear
 bsf INTCON, GIE ; enable interrupts, countdown starts
;-------------------------------------------------------------------------;
; This is the main loop that displays the time and checks if finished ;
;-------------------------------------------------------------------------;
loop: ; now go into a loop displaying registers in..
 ; sequence each time sec10 changes, (every
 ; ten seconds), and checking for finished flag
 btfsc finflag, 0 ; skip next if finflag not set
 goto finished ; time up
 movf oldsec10, W ; check if sec10 has changed
 subwf sec10, W ; zero flag set if sec10 is same as oldsec
 btfsc STATUS, Z ; skip over if not the same
 goto loop ; else keep checking
 movf sec10, W ; replace oldsec10
 movwf oldsec10 ; making it equal to sec10
 movf mins, W ; display minutes
 movwf PORTB ; on LEDs
 call onesecond ; for one second
 clrf PORTB ; blank briefly
 call msec250
 movf sec10, W ; now the same with sec10
 movwf PORTB ; show 10's of seconds
 call onesecond ; for one second
 clrf PORTB ; blank
 goto loop
;-------------------------------------------------------------------------;
; We come to this point when the countdown is over ;
;-------------------------------------------------------------------------;
finished:
 movlw H'F' ; turn on all leds indicating finish
 movwf PORTB
 goto $ ; go into an endless loop
;-------------------------------------------------------------------------;
; Four calls to a delay for 250 millisecond = 1 second delay ;
;-------------------------------------------------------------------------;
onesecond: ; a subroutine that delays for 1 seconds
 call msec250
 call msec250
 call msec250
 call msec250
 return
;-------------------------------------------------------------------------;
; This subroutine delays for 250 milliseconds ;
;-------------------------------------------------------------------------;
msec250: ; a subroutine to delay 250 msec
 movlw D'250' ; W is changed but no separate register needed
nmsec: ; could call it here with # msec in W
 nop ; each nop is 0.122 milliseconds
 nop
 nop ; each total loop is 8 X 0.122 = 0.976 msec
 nop
 addlw H'FF' ; same as subtracting 1 from W
 btfss STATUS, Z ; skip if result is zero
 goto nmsec ; this is 2 X 0.122 msec 
 return ; back to calling point
 end ; end of program

3 Minute Coundown Timer

When you run the program the LEDs will be initially blank. When the pushbutton on RB4 is pressed the count will be given every 10 seconds, first by flashing minutes and then tens of seconds. The numbers are in BCD but you should be able to catch them. Seconds are not flashed because they are always zero. If tens of seconds is zero, it will not be seen either. Rather than counting down in binary and then converting to decimal, three registers mins, sec10 and sec are used to handle the numbers directly in decimal. All LEDs are turned on when the count is over.

Interrupts

Program # 2 used an interrupt flag, (T0IF), but didn't use interrupts. This program uses interrupts. When interrupt conditions are satisfied, everything stops, the program jumps to address 4 and execution continues from there. Instructions are executed until a 'retfie' instruction returns to where the interruption happened and carries on as before.

The interrupt routine shouldn't modify any registers or flags used in the main program. That is the reason for the complex set of instructions at the beginning and end of the isr, (interrupt subroutine). Both 'W' and STATUS are likely to be changed in the isr and must be saved. The original values will be restored before exiting the isr.

Saving of these registers must be done with instructions that don't affect any of the flags in STATUS. One of these is 'swapf' which exchanges the high and low 4 bits of a register. Another is 'movwf'. Look over how the registers are saved in swapped form at the beginning and restored with 'swapf' at the end of the interrupt routine.

This program uses a timer zero interrupt. The main program is busy flashing the LEDs to indicate times. At the same time there seems to be a program running in the background that is updating the three time registers every second.

This is done by having the regular program interrupted every time TMR0 rolls over. TMR0 is given a 1:32 prescalar so this happens every second. Three requirements have to be met to generate a TMR0 interrupt.

  1. In INTCON register, T0IE, (Timer 0 Interrupt Enable), has to be set.
  2. In INTCON register, GIE, (General Interrupt Enable), as to be set.
  3. In INTCON register, T0IF, (Timer 0 Interrupt Flag), has to be set, (goes high).

Notice that GIE is not set until the 'button' is pressed to start the countdown. Setting GIE then activates the count. GIE is disabled each time an interrupt occurs and is reset by 'retfie'. T0IF is reset by code in the interrupt routine itself. Notice also that the first instruction is 'goto main' which jumps over the isr at program memory location 4.

Register usage

There are many more user named registers used in this program than previously. Two of these are w_temp and status_temp which are use to save values of W and the STATUS register. Another user defined register is 'finflag' which is set when the coundown is finished. Actually, only one bit, (bit 0), of the register is used. Finflag transfers the fact that the coundown is complete from the interrupt routine where it is detected to the main routine where it is acted upon. The register oldsec10 holds the value of sec10 that was last displayed. Only when sec10 differs from oldsec10 is the display updated.

Updating the Count

The count is initially 3 minutes, 0 seconds. Each second the isr decrements this by one second. Underflow is checked by seeing if the digit has gone to H'FF'. If this happens, the digit is reset to a starting value and the next digit to the left is decremented. When the underflow travels through all three digits, the finished flag is set.

ADDLW

We haven't seen the addlw instruction yet. It obviously adds a literal, (number), to W. But, in this case it is used to subtract 1 from W. You might be tempted to us 'sublw'. I have, and regretted it. 'sublw 3' does not subtract 3 from W, it subtracts W from 3! H'0FF' is the twos compliment value of '-1'. Adding twos compliment is the same as subtracting.The instruction occurs in the nmsec subroutine which introduces a way to provide a delay without using an extra register as we did before. This subroutine can also be entered at two points, msec250 to get a 1/4 second delay or at nmsec with the number of millisecs of delay put in W first.

SUBWF

'subwf (register), W' is a new instruction in the interrupt routine. Notice that W is subtracted from f, not the other way around. In this case we are only looking for a zero so it doesn't make a difference. But, in many cases it might be important. Also, if the destination is W, the register f is not altered.

BCF

BCF, ( clear a bit in the register f), provides a way to clear an individual bit in a register without changing other bits. There is also a BSF instruction to set individual bits.

Now its your turn

a mistake!

If you run the program you find that the 4 LEDs don't come on when the count reaches 0 but 10 seconds later. Can you figure out what is wrong and fix it?

add a speaker

The obvious addition to the program is a speaker to indicate when time is up. I find the easiest way to do this is to put a piezoelectric speaker directly between a port and +4.5V. The ones having a paper cone attached seem louder. A piezo speaker is actually a capacitor so a 100 ohm resistor in series is a good idea to limit the original surge current.

The code to drive the speaker would simply bring the port high, hold it for a millisecond, bring it low, hold it for a millisecond and repeat over and over. See if you can add a speaker at the point where all the LEDs are turned on.

display last 10 digits of coundown

Can you figure a way to have all of the last digits 9-0 displayed?

a variable starting value

A way to change the starting value might be interesting too. Say, if the battery were attached with the button not pressed the starting time would default to 3 minutes. If the button is pressed when power is supplied, the display starts flashing 1,2,3...etc until the button is released. The time at release would be the starting time in minutes. Could you write that program? Remember button debouncing because you would then have to wait for an additional press to start the countdown.

one button, many functions

It is also inconvient to power-down and back up to restart the countdown. How about a press of the button at the end to turn off the LEDs. Yet another press would reload the starting count, (maybe display the minutes). Another press would begin the countdown. Can you make the changes?

See:

Questions:

Questions:


file: /Techref/piclist/cheapic/eggtimer.htm, 16KB, , updated: 2013年8月14日 08:29, local time: 2025年9月5日 10:02,
40.74.122.252:LOG IN

©2025 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions?
Please DO link to this page! Digg it! / MAKE!

<A HREF="http://massmind.org/techref/piclist/cheapic/eggtimer.htm"> Eggtimer Program </A>

After you find an appropriate page, you are invited to your to this massmind site! (posts will be visible only to you before review) Just type a nice message (short messages are blocked as spam) in the box and press the Post button. (HTML welcomed, but not the <A tag: Instead, use the link box to link to another page. A tutorial is available Members can login to post directly, become page editors, and be credited for their posts.


Link? Put it here:
if you want a response, please enter your email address:
Attn spammers: All posts are reviewed before being made visible to anyone other than the poster.
Did you find what you needed?

Welcome to massmind.org!

Welcome to massmind.org!

.

AltStyle によって変換されたページ (->オリジナル) /