;-------------------------------------------------------------------------; ; 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
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.
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.
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.
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 (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, ( 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.
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?
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.
Can you figure a way to have all of the last digits 9-0 displayed?
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.
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:
Interested:
Questions: