; filename: "atp.asm"
; This is an automatic trolley puller. When used to climb onto a wooden pony the puller will count down when started
; and then start a motor (12 RPM) that pulls the trolley away. When the end switch is pressed it stops. Sound input
; to speed up countdown.
; v0.0 221119 Copied from maglocktimer
; v0.1 221125 Fixed tens display
; GPL Copyleft 2012-2022 pic@smmedical.eu
LIST P=16F628A, F=INHX8M
#include <p16f628a.inc>
__CONFIG _INTRC_OSC_NOCLKOUT & _WDT_ON & _LVP_OFF & _CP_OFF & _BODEN_OFF
ERRORLEVEL -302 ; sod message about using proper bank
; Equates
RESET_V EQU 0x00 ; Address of RESET Vector
OSC_FREQ EQU D'4000000' ; Oscillator Frequency is 4 MHz
; Registers
GPFLAGS EQU 0x20 ; General Purpose Flags
TENS EQU 0x21 ; Left digit (MSB)
UNITS EQU 0x22 ; Right digit (LSB)
TICKS EQU 0x24 ; 0.25 seconds, 256 ticks per unit total 99*256*0.25=105.6 min ehrm...
; Defines
#define FET PORTA,2 ; Switch motor FET on (active high), wait for motor endswitch
#define UPSW PORTA,4 ; "UP" switch
#define LED1 PORTA,1 ; MSB Anode (via PNP like BC557B)
#define LED2 PORTA,0 ; LSB Anode
#define ENDSW PORTA,6 ; End switch input (active low)
#define LEDTOGL GPFLAGS,0 ; toggles LED1 and 2 (0: LSB=LED2, 1: MSB=LED1)
#define RUNNING GPFLAGS,1 ; Counting down, when unset display dot only
#define LAST GPFLAGS,2 ; Countdown almost done, display 0x
#define LAMPTST GPFLAGS,4 ; Lamp Test
org 0x00
GOTO Main
org 0x04
; Interrupt handlers
; 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. Copied from someone.
BTFSC PIR1,TMR1IF ; Check to see if the interrupt was caused by a TIMER1 rollover Overflow)
GOTO Timehandler ; 0.25 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 0xFA ; Preload TIMER1 (approx. 5 min countdown)
MOVWF TMR1H
MOVLW 0x3B
MOVWF TMR1L
BCF LAMPTST ; No need to have those after the first half second
BTFSS RUNNING ; If not counting down we must keep our hands off the time counters
RETFIE
BTFSC FET ; Countdown ended?
GOTO CHKENDSW ; Yes, check endswitch
; Check "TICKS"
DECFSZ TICKS,F
RETFIE
; Check if UNITS is zero
DECFSZ UNITS,F
RETFIE
; Check if TENS is also zero
BTFSC LAST ; TENS zero?
GOTO STARTMOTOR ; Yes
MOVLW 0x0A
MOVWF UNITS
DECFSZ TENS,F
RETFIE
BSF LAST
RETFIE
STARTMOTOR
BSF FET ; Start motor
RETFIE
CHKENDSW
BTFSC ENDSW
RETFIE
BCF FET
BCF RUNNING
BCF LAST
MOVLW 0x09
MOVWF TENS
MOVWF UNITS
RETFIE
Multiplex
BCF PIR1,TMR2IF ; Clear Interrupt source
CLRWDT ; Clear Watchdog Timer (this runs every 4.0 ms, so with 18 ms WDT time-out that should be OK)
; Check UP switch first
BTFSC UPSW ; Read UP switch ("clear" is pressed, active LOW)
GOTO DoMPX
BSF RUNNING
DoMPX
; Toggle LEDs
MOVLW B'00000001'
XORWF GPFLAGS,1 ; Toggle LEDs
BTFSS LEDTOGL ; set means LSB display
GOTO MSBLED
BSF LED1
BCF LED2
; LSB LED, check if LAMPTST
BTFSC LAMPTST
GOTO LSBLAMPTST
BTFSS RUNNING
GOTO LSBDISPDOT
MOVFW UNITS
CALL bin2seg
MOVWF PORTB
RETFIE
LSBLAMPTST
CLRF PORTB
RETFIE
LSBDISPDOT
MOVLW 0x7F ; Display dot only
MOVWF PORTB
RETFIE
MSBLED
; MSB LED, check if LAMPTST
BSF LED2
BCF LED1
BTFSC LAMPTST
GOTO MSBLAMPTST
BTFSS RUNNING
GOTO MSBDISPDOT
MOVLW 0x0A
SUBWF UNITS,W
BTFSC STATUS,Z ; UNITS zero?
GOTO FIXMSB ; Yes, compensate TENS and move to WREG
MOVFW TENS
CALL bin2seg
MOVWF PORTB
RETFIE
FIXMSB
INCF TENS,W ; Compensate TENS and move to WREG (UNITS is zero)
CALL bin2seg
MOVWF PORTB
RETFIE
MSBLAMPTST
CLRF PORTB
RETFIE
MSBDISPDOT
MOVLW 0xFF ; Display blank
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
MOVLW B'11111000' ; PORTA,0-2 Outputs
MOVWF TRISA
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 GPFLAGS
BCF FET
BSF LAMPTST ; Lamp Test during the first second
CLRF TMR1H
CLRF TMR1L
MOVLW 0x09
MOVWF TENS
MOVWF UNITS ; Preload 99 counts
MOVLW 0x78 ; 0x78=120 half-TICKS
MOVWF TICKS
WaitForInterrupt
GOTO WaitForInterrupt ; Do Nothing
; Subroutines
bin2seg ; Convert hex value to 7-segment display
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 0xC0 ; Return segment code for A (0)
END