title "showertimer.asm" ; To aid in saving the environment this timer will show time used in the shower. Once switched on the quad 7-segment ; LED display shows time elapsed from 00:00 in minutes:seconds. When time gets past 30:00 display goes off and PIC to sleep ; to save battery power. Which is a salvaged 18650 cell from a laptop battery. ; Version 0.1: Copied from Maglocktimer ; Version 0.2: Fixed TRISA, no drive, many other bugs ; Version 1.0: Fixed supply voltage (_BODEN_OFF), release ; Version 1.1: Fixed SLEEP (GIE, WDT) ; Version 1.2: One leading zero (minutes) is quite enough ; 28 February 2016 ; GPL Copyleft 2016 pic@polonai.se LIST P=16F628A, F=INHX8M #include <p16f628a.inc> __CONFIG _INTRC_OSC_NOCLKOUT & _WDT_OFF & _LVP_OFF & _CP_OFF & _BODEN_OFF ; Equates RESET_V EQU 0x00 ; Address of RESET Vector OSC_FREQ EQU D'4000000' ; Oscillator Frequency is 4 MHz ; Registers TMINS EQU 0x21 ; 10 Minutes value MINUTES EQU 0x22 ; Minutes value TSECS EQU 0x23 ; 10 Seconds value HSECS EQU 0x24 ; Half Seconds value (because of TIMER1) #define DIG1 PORTA,1 ; 10 minutes anode #define DIG2 PORTA,2 ; minutes anode #define DIG3 PORTA,3 ; 10 seconds anode #define DIG4 PORTA,4 ; seconds anode org 0x00 GOTO Main org 0x04 ; Interrupt handler ; NOTE: Since virtually every part of this program runs inside an interrupt handler, it's not necesssary ; to back up any critical registers when entering or exiting the handler. BTFSC PIR1,TMR1IF ; Check to see if the interrupt was caused by a TIMER1 rollover Overflow) GOTO Timehandler ; 0.5 second timer BTFSC PIR1,TMR2IF ; Check to see if the interrupt was caused by a TIMER2 rollover (overflow) GOTO Multiplex ; 16 ms multiplex time for LED display RETFIE ; If none of the above is true, then just leave the interrupt handler Timehandler BCF PIR1,TMR1IF ; Clear Interrupt Source MOVLW 0x0B ; Preload TIMER1 (0.5 second) MOVWF TMR1H MOVLW 0xDB MOVWF TMR1L ; Increment seconds INCF HSECS,1 MOVLW 0x14 ; Test for overflow SUBWF HSECS,0 ; if true, Z=1, increment 10 seconds BTFSS STATUS,Z RETFIE CLRF HSECS ; Increment 10 seconds INCF TSECS,1 MOVLW 0x06 ; Test for overflow SUBWF TSECS,0 ; if true, Z=1, increment minutes BTFSS STATUS,Z RETFIE CLRF TSECS ; Increment minutes INCF MINUTES,1 MOVLW 0x0A ; Test for overflow SUBWF MINUTES,0 ; if true, Z=1, increment 10 minutes BTFSS STATUS,Z RETFIE CLRF MINUTES ; Increment 10 minutes INCF TMINS,1 MOVLW 0x03 ; Test for longer than 30 minutes SUBWF TMINS,0 ; if false, Z=0, exit BTFSS STATUS,Z RETFIE MOVLW 0xFF MOVWF PORTB ; Switch off LEDs BCF INTCON,GIE ; Disable global interrupts SLEEP ; Power cycle to restart Multiplex BCF PIR1,TMR2IF ; Clear Interrupt source MOVLW 0xFF ; Pull Port B high to reduce ghosting MOVWF PORTB ; Test Digit BTFSS DIG1 ; Digit 1? GOTO DIG1HANDLER BTFSS DIG2 ; Digit 2? GOTO DIG2HANDLER BTFSS DIG3 ; Digit 3? GOTO DIG3HANDLER ; We're at the ten seconds digit (DIG3) BSF DIG4 ; Switch off 4th digit BCF DIG3 ; Switch on 3rd digit MOVFW TSECS ; Get 10 seconds CALL bin2seg MOVWF PORTB RETFIE DIG3HANDLER ; 10 seconds digit BSF DIG3 ; Switch off 3rd digit BCF DIG2 ; Switch on 2nd digit MOVFW MINUTES ; Get minutes CALL bin2seg MOVWF PORTB RETFIE DIG2HANDLER ; minutes digit BSF DIG2 ; Switch off 2nd digit BCF DIG1 ; Switch on 1st digit MOVFW TMINS ; Get 10 minutes SUBLW 0 BTFSC STATUS,Z ; Is TMIN zero? (Z=1) RETFIE ; Yes, keep display blanked MOVFW TMINS ; Get 10 minutes again CALL bin2seg MOVWF PORTB RETFIE DIG1HANDLER ; 10 minutes digit BSF DIG1 ; Switch off 1st digit BCF DIG4 ; Switch on 4th digit BCF STATUS,C ; Clear carry RRF HSECS,0 ; Divide half seconds by two to get whole seconds and place in W reg CALL bin2seg MOVWF PORTB RETFIE ; END Interrupthandlers Main ;Init stuff BCF STATUS,RP0 ; Go to bank 0 BCF STATUS,RP1 ; Go to bank 0 MOVLW 0x07 ; Turn comparators off MOVWF CMCON BSF STATUS,RP0 ; Go to bank 1 CLRF TRISA ; Set port A as all outputs CLRF TRISB ; Set port B as all outputs BSF OPTION_REG,PSA ; Something with Watchdog Timer BCF STATUS,RP0 ; Go back to bank 0 BSF T1CON,T1CKPS1 ; These set the TIMER1 prescaler. Here are the possible values: BSF T1CON,T1CKPS0 ; 00=1:1 01=1:2 10=1:4 11=1:8 BCF T1CON,T1OSCEN ; Turn off the TIMER1 oscillator to save power (we don't need it because we're using the internal oscillator) BCF T1CON,TMR1CS ; Select the internal oscillator for TIMER1 BSF T1CON,TMR1ON ; Enable TIMER1 BSF T2CON,T2CKPS1 ; Prescale 1:16 for TIMER2 BCF T2CON,T2CKPS0 ; BCF T2CON,TOUTPS3 ; Postscale 1:1 for TIMER2 BCF T2CON,TOUTPS2 ; BCF T2CON,TOUTPS1 ; BCF T2CON,TOUTPS0 ; BSF T2CON,TMR2ON ; Enable TIMER2 ; Enable interrupts BCF INTCON,INTF ; Clear INTF flag before enabling interrupts BCF INTCON,T0IF ; Clear T0IF (TIMER0 Interrupt Flag) before enabling interrupts BCF PIR1,TMR1IF ; Clear TMR1IF (TIMER1 Interrupt Flag) before enabling interrupts BCF PIR1,TMR2IF ; Clear TMR2IF (TIMER1 Interrupt Flag) before enabling interrupts BCF INTCON,INTE ; Clear interrupt on RB0/INT pin (an External interrupt) BSF INTCON,PEIE ; Enable PEIE (PEripheral Interrupt Enable - for TIMER1, the 16 bit timer) BSF STATUS,RP0 ; Go to bank 1 BSF PIE1,TMR1IE ; Enable interrupt on TIMER1 overflow (when the TMR1 register pair wraps around from 0xFFFF to 0x0000) BSF PIE1,TMR2IE ; Enable interrupt on TIMER2 overflow BCF PIE1,CMIE ; Disable interrupt on comparator output change BCF STATUS,RP0 ; Go to bank 0 BSF INTCON,GIE ; Enable global interrupts ; INIT Registers CLRF TMR1H CLRF TMR1L CLRF TMINS CLRF MINUTES CLRF TSECS CLRF HSECS ; INIT Ports CLRF PORTB CLRF PORTA BSF DIG1 BSF DIG2 BSF DIG3 BSF DIG4 WaitForInterrupt GOTO WaitForInterrupt ; Do Nothing ; Subroutine convert binary to 7 segments, PORTB,1 goes to segment a, etc. bin2seg ADDWF PCL, F ; Jump into the lookup table RETLW 0xC0 ; Return segment code for 0 RETLW 0xF9 ; Return segment code for 1 RETLW 0xA4 ; Return segment code for 2 RETLW 0xB0 ; Return segment code for 3 RETLW 0x99 ; Return segment code for 4 RETLW 0x92 ; Return segment code for 5 RETLW 0x82 ; Return segment code for 6 RETLW 0xF8 ; Return segment code for 7 RETLW 0x80 ; Return segment code for 8 RETLW 0x90 ; Return segment code for 9 RETLW 0xFF ; Return segment code for blank RETLW 0x7F ; Decimal Point Only END