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