PIC ile darkroom timer

Başlatan ilker_32, 23 Temmuz 2009, 12:26:55

ilker_32



;-------------------------------------------------------------------------;
;                             Darkroom Timer                              ;
;               April '99  Stan Ockers (ockers@anl.gov)                   ;
;                  circuit diagram in CNTDN.PCX                           ;
;                further description in CNTDN.TXT                         ;
;                                                                         ;
;       Counts down from 0-99 min and 0-59 sec giving an alarm at 0       ;
;     initial counts are held in data EEPROM setable with one button      ;
;                                                                         ;
;  RBO-RB3 to bases of transistors connect to common cathode of displays. ;
;  RA0-RA3 to 1,2,4,8 BCD inputs of CD4511 7 segment latch and driver.    ;
;  RB7 to start pushbutton used to start countdown and silence alarm.     ;
;  RB6 goes to time set pushbutton use to sucessively set the digits.     ;
;  RA4 with pull-up resistor goes to PB to select from 15 starting counts ;
;  RB4 and RB5 go to speaker which gives an alarm.                        ;
;-------------------------------------------------------------------------;
 LIST  P=16F84
 #INCLUDE "p16f84.inc"
;-------------------------------------------------------------------------;
;    Here we define our own personal registers and give them names        ;
;-------------------------------------------------------------------------;
SEC       EQU H'0C'          ; this register holds the value of seconds
SEC10     EQU H'0D'          ; holds value of 10's of seconds
MIN       EQU H'0E'          ; holds value of minutes
MIN10     EQU H'0F'          ; holds value of 10's of minutes
DIGCTR    EQU H'10'          ; 8 bit counter, only 2 lowest bits actually used
DIGIT     EQU H'11'          ; hold digit number to access table
INTCNT    EQU H'12'          ; counts # interrupts to determine when 1 sec up
FUDGE     EQU H'13'          ; allows slight adjustment every 7 interrupts
RUNFLG    EQU H'14'          ; bit 0 only, tells if countdown in progress
W_TEMP    EQU H'15'          ; temporarily holds value of W
STATUS_TEMP EQU H'16'        ; temporarily holds value of STATUS
SECNT     EQU H'17'          ; used in counting 50, 20 msec delays for 1 sec
CNTMSEC   EQU H'18'          ; used in timing of milliseconds
ALARM     EQU H'19'          ; bit 0 only, used as flag for when to alarm
OFFSET    EQU H'1A'          ; hold offset of address in EEPROM
;-------------------------------------------------------------------------;
;    Here we give names to some numbers to make their use more clear      ;
;-------------------------------------------------------------------------;
  #DEFINE   START_PB  D'7'
  #DEFINE   SET_PB    D'6'
  #DEFINE   SELECT_PB D'4'
  #DEFINE   RB4       D'4'
  #DEFINE   RB5       D'5'
;-------------------------------------------------------------------------;
;         We set the start of code to orginate a location zero            ;
;-------------------------------------------------------------------------;
     ORG 0
           GOTO MAIN                  ; jump to the main routine
           NOP                        
           NOP                        
           NOP                        
           GOTO INTERRUPT             ; interrupt routine
;-------------------------------------------------------------------------;
;    This table is used to get a bit pattern that will turn on a digit    ;
;-------------------------------------------------------------------------;
BITPAT      ADDWF PCL,f                ; get bit pattern for transistors
           RETLW H'0E'                ; a low, (0), turns the transistor on  
           RETLW H'0D'                  
           RETLW H'0B'                  
           RETLW H'07'                  
;-------------------------------------------------------------------------;
;          Initialization routine sets up ports and timer                 ;
;-------------------------------------------------------------------------;
INIT        MOVLW H'C0'                ; PB6 & PB7 inputs all others outputs
           TRIS PORTB                
           MOVLW H'10'                ; Port RA4 input, others outputs
           TRIS PORTA                
           MOVLW H'03'                ; prescaler on TMR0 and 1:16
           OPTION                    
           MOVLW H'A0'                ; GIE & T0IE set T0IF cleared
           MOVWF INTCON              
           MOVLW H'F4'                ; initialize INTCNT
           MOVWF INTCNT              
           MOVLW H'06'                ; initialize FUDGE
           MOVWF FUDGE                
           CLRF OFFSET                ; initialize OFFSET
           RETURN                    
;-------------------------------------------------------------------------;
;   This is the interrupt routine that is jumped to when TMR0 overflows   ;
;-------------------------------------------------------------------------;
INTERRUPT   MOVWF W_TEMP               ; save W
           SWAPF STATUS,W             ; save status
           MOVWF STATUS_TEMP          ; without changing flags
           INCF DIGCTR,f              ; next digit #
           MOVF DIGCTR,W              ; get it into W
           ANDLW H'03'                ; mask off 2 lowest bits
           MOVWF DIGIT                ; save it for later
           ADDLW H'0C'                ; point at register to display
           MOVWF FSR                  ; use as pointer
           MOVF INDF,W                ; get value of reg pointed to into W
           MOVWF PORTA                ; output to CD4511
           MOVF DIGIT,W               ; recall digit #
           CALL BITPAT                ; get bit pattern
           MOVWF PORTB                ; select transistor
           DECFSZ INTCNT,f            ; finished 1 sec ?
           GOTO RESTORE               ; not yet, return and enable inter.
           CALL EVERYSEC              ; go to every second routine
           MOVLW H'F4'                ; reset INTCNT to normal value
           MOVWF INTCNT              
           DECFSZ FUDGE,f             ; time for fudge?
           GOTO RESTORE               ; not yet, continue on
           MOVLW H'06'                ; reset FUDGE to 6
           MOVWF FUDGE                
           INCF INTCNT,f              ; INTCNT to 245
RESTORE     SWAPF STATUS_TEMP,W        ; get original status back
           MOVWF STATUS               ; into status register
           SWAPF STATUS_TEMP,f        ; old no flags trick again
           SWAPF STATUS_TEMP,W        ; to restore W
           BCF INTCON,T0IF            ; clear the TMR0 interrupt flag
           RETFIE                     ; finished
;-------------------------------------------------------------------------;
;       This routine is called by the interrupt routine every second      ;
;-------------------------------------------------------------------------;
EVERYSEC    BTFSS RUNFLG,0             ; return if runflg not set
           RETURN                    
           DECF SEC,f                 ; decrement seconds digit
           INCFSZ SEC,W               ; test for underflow
           GOTO CKZERO                
           MOVLW H'09'                ; reset sec to 9
           MOVWF SEC                  
           DECF SEC10,f               ; decrement SEC10
           INCFSZ SEC10,W             ; check underflow
           GOTO CKZERO                
           MOVLW H'05'                  
           MOVWF SEC10                
           DECF MIN,f                
           INCFSZ MIN,W              
           GOTO CKZERO                
           MOVLW H'09'              
           MOVWF MIN                  
           DECF MIN10,f              
CKZERO      MOVF SEC,f                 ; test SEC for zero
           BTFSS STATUS,Z            
           RETURN                    
           MOVF SEC10,f               ; check SEC10 for zero
           BTFSS STATUS,Z            
           RETURN                    
           MOVF MIN,f                 ; check MIN for zero
           BTFSS STATUS,Z            
           RETURN                    
           MOVF MIN10,f               ; check MIN10 for zero
           BTFSS STATUS,Z            
           RETURN                    
           CLRF RUNFLG                ; stop the countdown
           BSF ALARM, 0               ; set the alarm flag
           RETURN                    
;-------------------------------------------------------------------------;
;          This is a routine to read a byte from the data EEPROM          ;
;-------------------------------------------------------------------------;
READEE      MOVWF EEADR                ; set up eeprom address from W
           BSF STATUS,RP0             ; change to page 1
           BSF EECON1,RD              ; set the read bit
           BCF STATUS,RP0             ; back to page 0
           MOVF EEDATA,W              ; return value in W
           RETURN                    
;-------------------------------------------------------------------------;
;         This routine fills the display registers from data EEPROM       ;
;-------------------------------------------------------------------------;
GETEE       MOVLW H'01'                ; EEprom location 1 +
           ADDWF OFFSET,W             ; offset from start
           CALL READEE                ; into W
           MOVWF SEC                  ; into SEC register
           MOVLW H'02'                ; location 2 +
           ADDWF OFFSET,W             ; offset from start
           CALL READEE                ; into W
           MOVWF SEC10                ; into SEC10 register
           MOVLW H'03'                ; location 3 +
           ADDWF OFFSET,W             ; offset from start
           CALL READEE                ; into W
           MOVWF MIN                  ; into MIN register
           MOVLW H'04'                ; location 4 +
           ADDWF OFFSET,W             ; offset from start
           CALL READEE                ; into W
           MOVWF MIN10                ; into MIN10 register
           RETURN                    
;-------------------------------------------------------------------------;
;              This routine writes a byte to data EEPROM                  ;
;-------------------------------------------------------------------------;
WRITEEE     BSF STATUS,RP0             ; set up EEADR and EEDATA first
           CLRF EECON1                
           BSF EECON1,WREN            ; enable write
           MOVLW H'55'                ; magic sequence
           MOVWF EECON2              
           MOVLW H'AA'                  
           MOVWF EECON2              
           BSF EECON1,WR              
EELOOP      BTFSC EECON1,WR            ; wait for WR to go low
           GOTO EELOOP                ; not yet
           BSF EECON1,WREN            
           BCF EECON1,EEIF            ; clear the interrupt flag
           BCF STATUS,RP0             ; return to page 0
           RETURN                    
;-------------------------------------------------------------------------;
;         This routine puts display registers into data EEPROM            ;
;-------------------------------------------------------------------------;
PUTEE       MOVF SEC,W                 ; put digit registers into EEprom
           MOVWF EEDATA              
           MOVLW H'01'                ; EEPROM location 1 +  
           ADDWF OFFSET,W             ; offset from start
           MOVWF EEADR                
           CALL WRITEEE              
           MOVF SEC10,W              
           MOVWF EEDATA              
           MOVLW H'02'                ; EEPROM location 2 +  
           ADDWF OFFSET,W             ; offset from start
           MOVWF EEADR                
           CALL WRITEEE              
           MOVF MIN,W                
           MOVWF EEDATA              
           MOVLW H'03'                ; EEPROM location 3 +  
           ADDWF OFFSET,W             ; offset from start
           MOVWF EEADR                
           CALL WRITEEE              
           MOVF MIN10,W              
           MOVWF EEDATA              
           MOVLW H'04'                ; EEPROM location 4 +  
           ADDWF OFFSET,W             ; offset from start
           MOVWF EEADR                
           CALL WRITEEE              
           RETURN                    
;-------------------------------------------------------------------------;
;         This is the main routine, the program starts here               ;
;-------------------------------------------------------------------------;
MAIN        CALL INIT                  ; set up ports etc.
;-------------------------------------------------------------------------;
;           We will return to this point when alarm is shut off.          ;
;-------------------------------------------------------------------------;
EE2D        CALL GETEE                 ; put eeprom in display regs.
           BCF RUNFLG, 0              ; clear run flag so no countdown
           BCF ALARM, 0               ; clear alarm flag
           CALL WAITSTARTUP           ; wait till no switches pressed
           CALL WAITSETUP
           CALL WAITSELECT
;-------------------------------------------------------------------------;
;      This loop checks for either pushbutton and acts  accordingly       ;
;-------------------------------------------------------------------------;
KEYCHKLOOP  BTFSS PORTB,START_PB       ; check for start pressed
           GOTO STARTCNT              ; yes, start count
           BTFSS PORTB,SET_PB         ; check for set pressed
           GOTO SETDISP               ; yes, set display
           BTFSS PORTA,SELECT_PB      ; check select pushbutton pressed
           GOTO SETSELECT             ; yes, select starting count
           GOTO KEYCHKLOOP            ; loop to catch key press
;-------------------------------------------------------------------------;
;    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
           MOVLW D'244'               ; reset INTCNT
           MOVWF INTCNT
           CLRF TMR0                  ; and clear timer 0
           BSF RUNFLG, 0              ; start the countdown
;-------------------------------------------------------------------------;
;        Once started just loop looking for cancel or reaching 0000       ;
;-------------------------------------------------------------------------;
MAINLOOP    BTFSS PORTB,START_PB       ; countdown in progress, check start
           GOTO EE2D                  ; start over again if pressed
           BTFSC ALARM, 0             ; reached 0000 yet?
           GOTO SOUNDALARM            ; yes, turn alarm on
           GOTO MAINLOOP              ; no start switch, continue looping
;-------------------------------------------------------------------------;
;    This code sounds the alarm and waits on start to be pressed          ;
;-------------------------------------------------------------------------;
SOUNDALARM
FINALWAIT   BCF PORTB,RB4              ; speaker leads set up
           BSF PORTB,RB5              ; opposite polarity
           MOVLW 2                    ; delay 2 milliseconds
           CALL NMSEC
           BSF PORTB,RB4              ; flip the speaker leads
           BCF PORTB,RB5
           MOVLW 2                    ; another 2 msec delay
           CALL NMSEC
           BTFSC PORTB,START_PB       ; start button pressed
           GOTO  FINALWAIT            ; not yet
           CALL DLY20                 ; debounce just to make sure
           BTFSC PORTB,START_PB       ; second look
           GOTO FINALWAIT             ; nah, keep waiting
           BCF PORTB,RB4              ; speaker leads set to same polarity
           BCF PORTB,RB5
           CALL WAITSTARTUP           ; now wait for the switch up
           GOTO EE2D                  ; start all over again
;-------------------------------------------------------------------------;
;                    Wait for release of start button                     ;
;-------------------------------------------------------------------------;
WAITSTARTUP BTFSS PORTB,START_PB       ; wait for release
           GOTO WAITSTARTUP           ; not released yet
           CALL DLY20                 ; debounce release
           BTFSS PORTB,START_PB       ; 2nd check, make sure released
           GOTO WAITSTARTUP           ; keep checking
           RETURN
;-------------------------------------------------------------------------;
;                    Wait for release of set button                       ;
;-------------------------------------------------------------------------;
WAITSETUP   BTFSS PORTB,SET_PB         ; wait for release
           GOTO WAITSETUP             ; not yet
           CALL DLY20                 ; debounce release
           BTFSS PORTB,SET_PB         ; 2nd check, make sure released
           GOTO WAITSETUP             ; keep checking
           RETURN
;-------------------------------------------------------------------------;
;                    Wait for release of select button                    ;
;-------------------------------------------------------------------------;
WAITSELECT  BTFSS PORTA,SELECT_PB      ; wait for release
           GOTO WAITSELECT            ; not yet
           CALL DLY20                 ; debounce release
           BTFSS PORTA,SELECT_PB      ; 2nd check, make sure released
           GOTO WAITSELECT            ; keep checking
           RETURN
;-------------------------------------------------------------------------;
;       Routine to follow sets the countdown time digit by digit          ;
;-------------------------------------------------------------------------;
SETDISP     CALL WAITSETUP             ; wait for set key to be released
           MOVLW H'0A'                ; put A's in digits, (no display)
           MOVWF MIN10                ; 10's of minutes
           MOVWF MIN                  ; minutes
           MOVWF SEC10                ; 10's of seconds
           MOVWF SEC                  ; seconds
STARTMIN10  CLRF  MIN10                ; 0 now in MIN10
MOREMIN10   MOVLW H'32'                ; 50 delays of 20 msec
           MOVWF SECNT                ; into counting register
WAIT1       CALL DLY20                
           BTFSS PORTB,SET_PB         ; set key pressed?
           GOTO MINSET                ; yes MIN10 now set
           DECFSZ  SECNT,f            ; finished 1 sec delay?
           GOTO WAIT1                 ; continue wait
           INCF MIN10,f               ; every second increment 10's MIN
           MOVLW H'0A'                ; reached 10?
           SUBWF MIN10,W              
           BTFSC STATUS,Z             ; Z set if reached 10
           GOTO STARTMIN10            ; start again with 0
           GOTO MOREMIN10             ; set up another 1 sec delay
MINSET      CALL WAITSETUP             ; wait for release of set key
STARTMIN    CLRF MIN                   ; 0 into MIN
MOREMIN     MOVLW H'32'                ; 50 delays of 20 msec
           MOVWF SECNT                ; into counting register
WAIT2       CALL DLY20                
           BTFSS PORTB,SET_PB         ; set pressed?
           GOTO SETSEC10              ; yes, finished with MIN
           DECFSZ SECNT,f             ; finished 1 sec delay?
           GOTO WAIT2                 ; continue wait
           INCF MIN,f                 ; every second increment MIN
           MOVLW H'0A'                ; reached 10?
           SUBWF MIN,W              
           BTFSC STATUS,Z             ; Z set if reached 10
           GOTO STARTMIN              ; put zero in if Z set
           GOTO MOREMIN               ; set up another 1 sec delay
SETSEC10    CALL WAITSETUP             ; wait release
STARTSEC10  CLRF SEC10                 ; 0 into SEC10
MORESEC10   MOVLW H'32'                ; 50 delays of 20 msec
           MOVWF SECNT                ; into counting register
WAIT3       CALL DLY20                
           BTFSS PORTB,SET_PB         ; set pressed?
           GOTO  SETSEC               ; yes quit incrementing
           DECFSZ SECNT,f             ; finished 1 sec delay?
           GOTO WAIT3                 ; continue wait
           INCF SEC10,f               ; every second increment 10's SEC
           MOVLW H'06'                ; reached 6?
           SUBWF SEC10,W              
           BTFSC STATUS,Z             ; Z set if reached 6
           GOTO STARTSEC10            ; put zero in if Z set
           GOTO MORESEC10             ; set up another 1 sec delay
SETSEC      CALL WAITSETUP             ; wait for release
STARTSEC    CLRF SEC                   ; 0 into SEC
MORESEC     MOVLW H'32'                ; 50 delays of 20 msec
           MOVWF SECNT                ; into counting register
WAIT4       CALL DLY20                
           BTFSS PORTB,SET_PB         ; set button pressed?
           GOTO  FINSET               ; yes finished setting digits
           DECFSZ SECNT,f             ; finished 1 sec delay?
           GOTO WAIT4                 ; continue wait
           INCF SEC,f                 ; every second increment SEC
           MOVLW H'0A'                ; reached 10?
           SUBWF SEC,W              
           BTFSC STATUS,Z             ; Z set if reached 10
           GOTO STARTSEC              ; put zero in if Z set
           GOTO MORESEC               ; set up another 1 sec delay
FINSET      BCF INTCON, GIE            ; disable interrupts
           CALL PUTEE                 ; put new digits into EEPROM
           BSF INTCON, GIE            ; re-enable interrupts
           CALL WAITSETUP             ; make sure set switch up
           GOTO KEYCHKLOOP            ; start checking buttons again
;-------------------------------------------------------------------------;
;        Selects starting count by changing EEPROM location 0             ;
;-------------------------------------------------------------------------;
SETSELECT   MOVLW D'4'                 ; offset up 4
           ADDWF OFFSET,F             ; next offset position
           MOVLW D'60'                ; reached 16th yet?
           SUBWF OFFSET,W             ; will give zero if yes
           BTFSC STATUS,Z             ; skip if not 64
           CLRF  OFFSET               ; reset position to zero
           MOVLW 0                    ; EEPROM location
           MOVWF EEADR                ; set up address
           MOVF OFFSET,W              ; offset # into W
           MOVWF EEDATA               ; set up data
           BCF INTCON,GIE             ;  clear GIE, disable interrupts
           CALL WRITEEE               ; save # in location 0
           BSF INTCON,GIE             ; re-enable interrupts
           CALL GETEE                 ; get new start count into display
           CALL WAITSELECT            ; make sure select switch is up
           GOTO KEYCHKLOOP            ; start checking buttons again
;-------------------------------------------------------------------------;
;  The following are various delay routines based on instruction length.  ;  
;  The instruction length is assumed to be 1 microsecond (4Mhz crystal).  ;
;-------------------------------------------------------------------------;
DLY20       MOVLW 20                   ; delay for 20 milliseconds
               ;*** N millisecond delay routine ***
NMSEC       MOVWF CNTMSEC              ; delay for N (in W) milliseconds
MSECLOOP    MOVLW D'248'               ; load takes 1 microsec
           CALL MICRO4                ; by itself CALL takes ...
                                      ; 2 + 247 X 4 + 3 + 2 = 995
           NOP                        ; 1 more microsec
           DECFSZ CNTMSEC,f           ; 1 when skip not taken, else 2
           GOTO MSECLOOP              ; 2 here: total 1000 per msecloop
           RETURN                     ; final time through takes 999 to here
                                      ; overhead in and out ignored
               ;***  1 millisecond delay routine ***
ONEMSEC     MOVLW D'249'               ; 1 microsec for load W
                                      ; loops below take 248 X 4 + 3 = 995
MICRO4      ADDLW H'FF'                ; subtract 1 from 'W'
           BTFSS STATUS,Z             ; skip when you reach zero
           GOTO MICRO4                ; loops takes 4 microsec, 3 for last
           RETURN                     ; takes 2 microsec
                                      ; call + load  W + loops + return =
                                      ; 2 + 1 + 995 + 2 = 1000 microsec
;-------------------------------------------------------------------------;
;    Here we set up the initial values of the digits in data EEPROM       ;
;-------------------------------------------------------------------------;
          ORG H'2100'
         DE 0, 1, 0, 0, 0    ; 1st starting #
         DE  2, 0, 0, 0      ; 2nd starting #
         DE  3, 0, 0, 0      ; 3rd starting #
         DE  4, 0, 0, 0      ; 4th starting #
         DE  5, 0, 0, 0      ; 5th starting #
         DE  6, 0, 0, 0      ; 6th starting #
         DE  7, 0, 0, 0      ; 7th starting #
         DE  8, 0, 0, 0      ; 8th starting #
         DE  9, 0, 0, 0      ; 9th starting #
         DE  0, 1, 0, 0      ; 10th starting #
         DE  1, 1, 0, 0      ; 11th starting #
         DE  2, 1, 0, 0      ; 12th starting #
         DE  3, 1, 0, 0      ; 13th starting #
         DE  4, 1, 0, 0      ; 14th starting #
         DE  5, 1, 0, 0      ; 15th starting #
           END