	; // ------------------------------------------------------------- //
	; // PIC16F628 based controller for 4 channel amplifier.           //
	; // Alarm and control inputs. Amplifier power control and speaker //
	; // protection relay control. Temperature comparator.             //
	; //                                                               //
	; //                          Copyright 2004,  Peter Wolkensdorfer //
	; // ------------------------------------------------------------- //

	list	p=16f628
	#include p16f628.inc

					; // -- Device configuration bits ---

	__CONFIG _CP_OFF & _WDT_ON & _BODEN_ON & _PWRTE_ON & _INTRC_OSC_NOCLKOUT & _LVP_OFF & _MCLRE_OFF

					; // ---------- Variables -----------

temp_WREG   	equ	0x20
temp_STATUS 	equ	0x21
temp_PCLATH 	equ	0x22
temp_FSR	equ	0x24
FLAGS		equ	0x25      	; // Status bits
RemoteKey	equ	0x26		; // Keypress counter
Countdown	equ	0x27		; // Counter for timer IT
CMP_FLAGS	equ	0x28		; // Comparator flags
LoopCount	equ	0x29		; // Loop variable

					; // ------ Other status flags ------

AMP_STATE 	equ	0		; // FLAGS.0
SWITFLAG  	equ	1		; // FLAGS.1
TICK		equ	2		; // FLAGS.2

					; // ----- Port pin definitions -----

SPEAKERS	equ	4		; // PORTA.4 (open collector output)
OK_SIGNAL	equ	5		; // PORTA.5 (input)
FAULT		equ	0		; // PORTB.0 (output)
SO_COOL		equ	1		; // PORTB.1 (output)
SOFTSTART	equ	2		; // PORTB.2 (output)
AMP_POWER	equ	3		; // PORTB.3 (output)
HOT		equ	4		; // PORTB.4 (output)
VENT		equ	5		; // PORTB.5 (output)
BACKLIGHT	equ	6		; // PORTB.6 (output)
MUTE		equ	7		; // PORTB.7 (input)
REMOTE		equ	7		; // PORTA.7 (input)

	org	0x0000			; // --------- RESET vector ---------

		CLRF	PCLATH
		GOTO    main

	org	0x0004			; // ------- Interrupt vector -------

		MOVWF  	temp_WREG   	; // Goes to 0x20 or 0xA0
		SWAPF  	STATUS,W        ; // Get STATUS
		BCF    	STATUS,RP0      ; // Switch to bank 0
		MOVWF  	temp_STATUS 	; // Save STATUS
		MOVF   	PCLATH, W     	; // Get PCLATH
		MOVWF  	temp_PCLATH 	; // Save PCLATH
		CLRF   	PCLATH		; // Clear PCLATH
		MOVF	FSR,W		; // Get FSR
		MOVWF	temp_FSR	; // Save FSR

		CLRWDT			; // Clear watchdog timer
		BCF	INTCON,T0IF	; // Clear timer IT flag
		BTFSC	FLAGS,AMP_STATE	; // Check if amplifier is off
		GOTO	count_down	; // No, it is on

		BTFSS	Countdown,0	; // Run backlight at half power
		BCF	PORTB,BACKLIGHT
		BTFSC	Countdown,0
		BSF	PORTB,BACKLIGHT

count_down	DECF	Countdown	; // Count ITs
		BTFSS	STATUS,Z	; // Countdown expired?
		GOTO	restore_context	; // No

		MOVLW	102		; // Count 102 ITs to provide a 10 Hz
		MOVWF	Countdown	; // signal
		BSF	FLAGS,TICK	; // Set tick flag

restore_context	MOVF	temp_PCLATH,W	; // Restore context
		MOVWF	PCLATH
		MOVF	temp_FSR,W
		MOVWF	FSR
		SWAPF	temp_STATUS,W
		MOVWF	STATUS
		SWAPF	temp_WREG
		SWAPF	temp_WREG,W
		RETFIE

Delay100ms	MOVLW	102		; // ---- Provide 100 msec delay ----
		MOVWF	Countdown	; // Restart timer for 100 ms
		BCF	FLAGS,TICK	; // Clear tick flag

delay1		BTFSS	FLAGS,TICK	; // Check if expired
		GOTO	delay1		; // No
		RETURN

main		BCF	STATUS,RP0	; // ---------- Main loop -----------
		CLRF	FLAGS		; // All flags cleared

		MOVLW	0xAF		; // Set TRIS and PORT defaults ...
		BSF	STATUS,RP0	; // ...for PORTA
		MOVWF	TRISA		; // Only bit4 is output
		BCF	STATUS,RP0	; // Bit6 is set to output (not used)
		MOVLW	0x10		; // Bit4 is inverted
		MOVWF	PORTA

		MOVLW	0x80
		BSF	STATUS,RP0 	; // ... for PORTB
		MOVWF	TRISB		; // Only bit7 is input
		BCF	STATUS,RP0
		CLRF	PORTB

		MOVLW	0xC1		; // Set configuration
		BSF	STATUS,RP0
		MOVWF	OPTION_REG	; // Timer0 uses internal clock, with
					; // 1:4 prescaler. This provides a
					; // 0.976 kHz timer interrupt which
					; // means 1 IT per 1024 instructions
		MOVLW	4
		BCF	STATUS,RP0
		MOVWF	CMCON		; // Config 2 independent comparators

		MOVLW	0xA0
		MOVWF	INTCON		; // Enable Timer0 IT, but no other

		BSF	PORTB,BACKLIGHT	; // Turn on power key backlight

		MOVF	CMCON		; // Clear status change flags
		BCF	PIR1,CMIF
		
main_loop	BCF	STATUS,RP0	; // ------ Main amp control loop -----
		BTFSS	PORTB,FAULT	; // Check if trouble flag set
		GOTO	no_trouble	; // No problem so far

		BCF	PORTB,BACKLIGHT	; // Short backlight blinks if trouble
		CALL	Delay100ms	; // Trouble cannot be cleared without
		CALL	Delay100ms	; // powering down the amplifier
		CALL	Delay100ms	; // completely.
		BSF	PORTB,BACKLIGHT
		CALL	Delay100ms
		GOTO	main_loop

no_trouble	BTFSS	PORTA,REMOTE	; // Check if power key or remote
		CALL	remote_onoff	; // Yes
		BTFSC	PORTA,REMOTE	; // No keypress
		CLRF	RemoteKey	; // Clear keypress counter

		BTFSS	FLAGS,AMP_STATE	; // Check current state
		GOTO	main_loop	; // Do nothing else if power down

check_fault	BTFSS	PORTA,OK_SIGNAL	; // Check if fault occurred
		GOTO	temp_ctrl	; // No, running fine

		BSF	PORTB,FAULT	; // Set trouble flag
		GOTO	remote_off	; // Shut down immediately

temp_ctrl	BTFSC	CMCON,6		; // Check comparator #1
		GOTO	getting_warm	; // Hot state first stage reached

		BCF	PORTB,VENT	; // Shut down cooling
		NOP
		BCF	PORTB,HOT	; // Turn on hot state LED
		NOP
		BSF	PORTB,SO_COOL	; // Running fine
		GOTO	check_mute	; // Check if mute command received

getting_warm	BSF	PORTB,VENT	; // Turn on cooling
		NOP
		BSF	PORTB,HOT	; // Indicate hot state
		BTFSS	CMCON,7		; // Check comparator #2
		GOTO	check_mute	; // Not really hot

		BCF	PORTB,SO_COOL	; // Turn off power LED
		BSF	PORTA,SPEAKERS	; // Turn off speakers
		GOTO	temp_ctrl	; // Wait for cooling down

check_mute	BTFSC	PORTB,MUTE	; // Check if mute signal required
		BSF PORTA,SPEAKERS	; // If so, turn off speakers
		BTFSS	PORTB,MUTE	; // Muting not required
		BCF PORTA,SPEAKERS	; // Turn on speakers

		GOTO	main_loop	; // Restart cycle

					; // ---- Turn amplifier on or off ----

					; // Note: This version does not use
					; // the softstart feature

remote_onoff	BTFSS	FLAGS,TICK	; // Synchronize to 100ms loop
		RETURN

		BCF	FLAGS,TICK	; // Clear 10 Hz flag
		MOVF	RemoteKey,W	; // Check keypress length
		SUBLW	4		; // 3-400 ms length required
		BTFSC	STATUS,Z	; // Keypress is long enough
		GOTO	long_key	; // Yes

		INCF	RemoteKey	; // Count keypress
		BCF	RemoteKey,7	; // Inhibit counter overflow
		RETURN

long_key	INCF	RemoteKey	; // Avoid immediate off after on
		BTFSS	FLAGS,AMP_STATE	; // Check if amplifier is on
		GOTO	remote_on	; // No, keypress means turn on

remote_off	BSF	PORTA,SPEAKERS	; // Turn off loudspeakers
		CALL	Delay100ms	; // Wait a little
		BCF	PORTB,AMP_POWER	; // Turn off power
		CALL	Delay100ms	; // Wait
		BCF	PORTB,SOFTSTART	; // Release softstart short relay
		NOP
		BCF	PORTB,SO_COOL	; // Indicate power off
		BCF	FLAGS,AMP_STATE	; // Set state flag
		RETURN

remote_on	BSF	PORTB,SO_COOL	; // Indicate power on
		NOP
		BSF	PORTB,SOFTSTART	; // Close softstart short relay
		CALL	Delay100ms	; // Wait
		BSF	PORTB,AMP_POWER	; // Turn on power
		MOVLW	50
		MOVWF	LoopCount	; // 5 seconds delay for stabilization

spk_on_loop	BTFSS	PORTA,OK_SIGNAL	; // Check if fault is signalled
		GOTO	safe_turn_on	; // Fault signal cleared

		CALL	Delay100ms	; // Wait for a little
		MOVF	LoopCount,W	; // Get counter
		ANDLW	1		; // Create 200 ms clock to blink
		BTFSC	STATUS,Z	; // during fault signal
		BCF	PORTB,SO_COOL	; // Blink off
		BTFSS	STATUS,Z
		BSF	PORTB,SO_COOL	; // Blink on
		DECFSZ	LoopCount	; // Check if delay expired
		GOTO	spk_on_loop	; // No, next cycle
		BSF	PORTB,FAULT	; // Set fault output
		GOTO	remote_off	; // Turn off amplifier

safe_turn_on	BSF	PORTB,SO_COOL	; // Turn on power LED
		NOP
		BCF	PORTA,SPEAKERS	; // Turn on speakers
		BSF	FLAGS,AMP_STATE	; // Set state flag
		RETURN

	end
