; PROGRAM: RPM.SRC	Version 1.4

;	******************************
;	* Digital rpm meter 2 digits *
;	******************************

;	Copyright by WolfRam Technologies Inc. All rights reserved.
;	March 15th 1997

; *********************** Identify used controller ***************************

		device	pic16f84,xt_osc,wdt_off,pwrt_on,protect_off

; ************************** Port connections ********************************

DIGS		=	ra		; Data for 1000s and 100s (bits 0..3)
PULSE		=	ra.4		; Incoming pulse - this is an open
					; collector output, so it's better
					; to use it for an input
BAR		=	rb		; Lower 4 bits select LED
LED		=	rb.4		; LED is alight or not: negative logic
CS1000		=	rb.5		; Chip select for higher digit
CS100		=	rb.6		; Chip select for lower digit

; ******** Set RAM origin above special registers, declare variables *********

		org	12

tickl		ds	1		; Refreshment counter
tickh		ds	1		; Counter high byte
bar_ind		ds	1		; Which LED is displayed
bar_max		ds	1		; Which is the last LED to light
bar_temp	ds	1		; Temporary register for calculations
bar_store	ds	1		; Store port data temporarily
rpm_count	ds	1		; Counter for pulses
last_rpm	ds	1		; The last value read
delta		ds	1		; Register for difference calculations
last_delta	ds	1		; Difference history

bit_temp	ds	1		; Flags
DONE		=	bit_temp.0	; Handshake between prg and interrupt
RP0_temp	=	bit_temp.1	; Value of RP0 at the IT call
puls_temp	=	bit_temp.2	; Status at measurement completion
LED_temp	=	bit_temp.3	; Light LED or not

w_copy		ds	1		; [interrupt] Store for W
s_copy		ds	1		; [interrupt] Store for status reg

; ****************************** Reset vector ********************************

		org	0

		mov	PCLATH,#3	; Sets the jmp pc+w address high byte
					; to the end of the first 2K page.
					; Tables start at 300H
		jmp	initialize

; *************************** Interrupt routine ******************************

		org	4

interrupt	mov	w_copy,w	; Make a copy of W
		mov	s_copy,status	; Make a copy of status
		jnb	RP0,inter2	; We are on page 0
		clrb	RP0
		setb	RP0_temp	; Remember page
inter2		jnb	T0IF,inter1	; Not timer1 overflow
		clrb	T0IF		; Clear the timer interrupt flag
					; With a refreshment period of 300 ms,
					; we have for each 100 1/min exactly
					; 1 pulse per refreshment period and
					; the bargraph has an
					; (1000/300)*(300/0.256)/16=244 Hz
					; refreshment frequency.
		inc	tickl		; Increment counter
		jnz	inter6
		inc	tickh
inter6		cjne	tickh,#4,inter4 ; Measurement not done
		cjne	tickl,#148,inter4
		setb	DONE
		clr	tickl		; Restart counter
		clr	tickh

inter4		inc	bar_ind		; Next LED
		cjne	bar_ind,#16,inter5 ; Bar not completed
		clr	bar_ind
		clrb	LED_temp	; Light LED: zero always lights

inter5		mov	w,BAR
		and	w,#0F0H		; Cut off lower 4 bits
		or	w,bar_ind	; Add LED control
		mov	bar_store,w	; Store
		cjne	bar_ind,bar_max,inter1 ; Bar not completed
		setb	LED_temp	; Clear LED

inter1		movb	bar_store.4,LED_temp ; Add LED status
		mov	w,bar_store	; Get data
		mov	BAR,w		; Write to port
		jnb	RP0_temp,inter3 ; Stay on this page
		clrb	RP0_temp	; Forget it
		setb	RP0		; Restore page
inter3		mov	status,s_copy	; Restore status register
		swap	w_copy		; Prepare for swapped move
		mov	w,<>w_copy	; Swap/move to w, status unaffected
		reti			; Return to main program

; ***************************** Start program ********************************

initialize	clr	ra
		clr	rb
		clrb	RP1		; RP1 not used, set to 0
		setb	RP0		; Set page for bank1 registers
		mov	trisa,#10H	; 0..3 digits out, 4 is input: pulse
		clr	trisb		; Controls (set bit 7 as output to
					; avoid unwanted ITs)
		clrb	T0CS		; Use internal oscillator
		clrb	RP0		; Back to page 0
		clrb	T0IE		; No interrupts on timer overflow
		setb	GIE		; Enable interrupts
		clrb	T0IF		; Clear interrupt flag

startup		clr	tickl		; Clear counter
		clr	tickh
		clr	bar_ind		; Start at first LED
		mov	bar_max,#1	; No bar at start
		mov	w,DIGS
		and	w,#10H
		or	w,#08H		; "88" indication at start
		mov	DIGS,w
		clrb	CS1000		; Latch it
		clrb	CS100
		clr	bit_temp	; Clear any info [interrupt]
		clr	rpm_count	; No data
		clr	last_rpm	; No history
		clr	last_delta
		setb	CS1000		; Latched
		setb	CS100
		setb	T0IE		; Enable timer interrupts
		clr	TMR0		; Restart timer

start2		cje	tickh,#0,start2	; Wait for first cycle
		clr	tickh		; Cheat IT routine
		inc	bar_max		; Light next LED
		cjne	bar_max,#17,start2 ; Cycle
start3		cje	tickh,#0,start3	; Wait for cycle
		clr	tickh		; Cheat IT routine
		dec	bar_max		; Clear next LED
		cjne	bar_max,#1,start3 ; Cycle
		and	DIGS,#10	; Zero indication
		clrb	CS1000		; Latch it
		clrb	CS100
		mov	tickh,#3	; Shorten startup period
		setb	CS1000
		setb	CS100

start1		jnb	DONE,start1	; Wait for the first round
		clrb	DONE		; Let's go!

; *********************** Normal operation of the rpm ************************

rpm		jb	DONE,rpm1	; Measurement completed
		jnb	PULSE,rpm	; Wait for the pulse
		inc	rpm_count	; Count it
rpm2		jb	DONE,rpm1
		jb	PULSE,rpm2	; Wait for the end of the pulse
		jmp	rpm

rpm1		movb	puls_temp,PULSE ; Store status
		clrb	DONE		; Clear indication
		cjbe	rpm_count,#99,rpm12 ; Maximum is 9900 1/min
		mov	rpm_count,#99
rpm12		cjne	rpm_count,last_rpm,rpm3 ; Change since last value
					; Watch! No change does not mean that
					; the display is not refreshed. So if
					; new data is coming in (small change,
					; see below), it is not displayed at
					; the first run, only on the second.
		clr	last_delta	; Indicate no change
		jmp	rpm6		; Redraw

rpm3		mov	delta,last_rpm	; Store last data in temporary register
		mov	last_rpm,rpm_count ; Store new data
		sub	delta,rpm_count	; Compare new one with last one
		cje	delta,#1,rpm7	; Small change - need to check history
		cje	delta,#0FFH,rpm7 ; And again

		mov	last_delta,#128	; Indicate large difference
		jmp	rpm6		; The change is big: redraw

rpm7		cje	delta,last_delta,rpm6 ; Continuous tendency: redraw
		cje	last_delta,#128,rpm9 ; History shows changes: redraw
		mov	last_delta,delta ; Store new value
		jmp	rpm8		; No drawing

rpm9		mov	last_delta,delta ; Store new value
rpm6		and	DIGS,#10H	; Clear indication
		mov	w,rpm_count
		call	digits		; Get digits
		mov	delta,w		; Store temporarily
		and	w,#0FH		; Lower digit
		or	DIGS,w		; Write 'em
		clrb	CS100		; Latch it
		swap	delta
		mov	w,delta		; Get higher digit
		and	w,#0FH		; Mask
		setb	CS100
		mov	DIGS,w		; Write

		clrb	CS1000		; Latch
		clr	bar_temp	; Calculate bar length
rpm11		cjb	rpm_count,#5,rpm10 ; Too small
		sub	rpm_count,#5	; Substract 500
		inc	bar_temp	; Add next LED
		jmp	rpm11

rpm10		inc	bar_temp	; The bar_max-th LED is not light
		mov	bar_max,bar_temp ; Set bar maximum
		setb	CS1000

rpm8		clr	rpm_count	; Clear data
		jnb	puls_temp,rpm	; Store the start by next pulse
		clrb	puls_temp
rpm5		jb	PULSE,rpm5	; Wait for the end of the pulse
		jmp	rpm

; ********************************* Tables ***********************************

		org	768		; 300H

digits		jmp	pc+w			; Madly revving engine...
		retw	00H, 01H, 02H, 03H, 04H, 05H, 06H, 07H
		retw	08H, 09H, 10H, 11H, 12H, 13H, 14H, 15H
		retw	16H, 17H, 18H, 19H, 20H, 21H, 22H, 23H
		retw	24H, 25H, 26H, 27H, 28H, 29H, 30H, 31H
		retw	32H, 33H, 34H, 35H, 36H, 37H, 38H, 39H
		retw	40H, 41H, 42H, 43H, 44H, 45H, 46H, 47H
		retw	48H, 49H, 50H, 51H, 52H, 53H, 54H, 55H
		retw	56H, 57H, 58H, 59H, 60H, 61H, 62H, 63H
		retw	64H, 65H, 66H, 67H, 68H, 69H, 70H, 71H
		retw	72H, 73H, 74H, 75H, 76H, 77H, 78H, 79H
		retw	80H, 81H, 82H, 83H, 84H, 85H, 86H, 87H
		retw	88H, 89H, 90H, 91H, 92H, 93H, 94H, 95H
		retw	96H, 97H, 98H, 99H
