;G6UYJ FREQUENCY COUNTER using PIC16F84
;
;The code is not exactly the neatest or the most compact but it works for me
;
;		
;	RA0 = LCD E	RB0 = LCD DATA 0 / CTR RESET
;	RA1 = LCD RS	RB1 = LCD DATA 1 / GATE
;	RA2 = PRE_MOD	RB2 = LCD DATA 2 / SEL0
;	RA3 = COUNT UP	RB3 = LCD DATA 3 / SEL1
;	RA4 = TMR0 I/P	RB4 = 
;			RB5 = 
;			RB6 = MODE SW
;			RB7 = GATE SW
;
;Notes for Complier

	LIST P=16F84		;set PIC for assembler

	__CONFIG 0x3FF1		;XT, no WDT, PWR-UP, NO CP

#include <P16F84.INC>


;PORT DEFINITIONS

LCD_PORT		equ	PORTB
LCD_CMD_PORT	equ	PORTA

E		equ	0
RS		equ	1
PRE_MOD		equ	2
COUNT_UP		equ	3
RESET		equ	3
GATE		equ	2
CH_SEL0		equ	0
CH_SEL1		equ	1
SW_MODE		equ	7
SW_GATE		equ	6

;MEMORY DEFINITIONS

TEMP1		equ	0x10
TEMP2		equ	0x11
TEMP3		equ	0x12
CHAR		equ	0x13
DISPLAY_INFO	equ	0x14	;low nibble units, bit 7 leading zeros
COUNT		equ	0x15
BCD2ASCII		equ	0x16
GATE_TIME		equ	0x17
MODE		equ	0x18
GATE_TIME1	equ	0x19
GATE_TIME2	equ	0x1A
GATE_TIME3	equ	0x1B

DISPLAY1		equ	0x1C	;LS BYTE
DISPLAY2		equ	0x1D
DISPLAY3		equ	0x1E
DISPLAY4		equ	0x1F
DISPLAY5		equ	0x20	;MS BYTE

FREQ1		equ	0x21	;LS BYTE
FREQ2		equ	0x22
FREQ3		equ	0x23
FREQ4		equ	0x24	;MS BYTE


	ORG 	0x00
	goto	INIT

	ORG	0x04
	goto	ISR

INIT	clrf	STATUS
        	clrf    	PORTA
        	clrf    	PORTB

	bsf	STATUS,RP0		;set PAGE1
	movlw	0x18
	movwf	TRISA		;RA0-2 O/P RA3-4 I/P
	movlw	0xF0
	movwf	TRISB		;RB4-7 I/P

	movlw	0x37
	movwf	OPTION_REG		;Prescaler /256, RB PULL UP ENABLED

	bcf	STATUS,RP0		;set PAGE0

	clrf	MODE		;set ch0 at startup
	clrf	GATE_TIME		;set 1 sec for startup

	movlw	0x30		;Put 0x30 into memory
	movwf	BCD2ASCII		;to use for LCD display


	goto	INIT_LCD
RETURN_INIT_LCD

	bcf	INTCON,RBIE		;enable interrupts
	movf	PORTB,W
	bcf	INTCON,RBIF
	bsf	INTCON,RBIE
	bsf	INTCON,GIE

START
	goto	COUNTER

RETURN_COUNTER
	movlw	0x04
	subwf	MODE,W
	btfss	STATUS,Z
	goto	RETURN_OFFSET
	goto	IC202_OFFSET
RETURN_OFFSET

	goto	B2BCD
RETURN_B2BCD

	goto	UPDATE_DISPLAY
RETURN_UPDATE_DISPLAY


THE_END	goto	START

ISR
	call	delay		;debounce
	call	delay
	call	delay
	call	delay
	btfss	PORTB,SW_GATE	;test which key has been pressed
	goto	CHANGE_GATE
	btfss	PORTB,SW_MODE
	goto	CHANGE_MODE
	goto	START

CHANGE_MODE
	movlw	0x01
	addwf	MODE,F
	movlw	0x05
	subwf	MODE,W
	btfsc	STATUS,Z
	clrf	MODE
	goto	EXIT_ISR

CHANGE_GATE
	movlw	0x01
	addwf	GATE_TIME,F
	movlw	0x03
	subwf	GATE_TIME,W
	btfsc	STATUS,Z
	clrf	GATE_TIME

EXIT_ISR
	movlw	0xff		;loop for about 16ms??
	movwf	TEMP3
key_up	call	delay
	decfsz	TEMP3,F
	goto	key_up
	btfss	PORTB,SW_MODE	;test for keys down
	goto	EXIT_ISR
	btfss	PORTB,SW_GATE
	goto	EXIT_ISR
	bcf	INTCON,RBIE		;enable interrupts
	movf	PORTB,W
	bcf	INTCON,RBIF
	bsf	INTCON,RBIE
	bsf	INTCON,GIE
	goto	UPDATE_MODE_GATE


lcd_send_cmd
	bcf	INTCON,GIE
	bcf	LCD_CMD_PORT,RS	;send command
	goto	lcd_send
lcd_send_char
	bcf	INTCON,GIE
	bsf	LCD_CMD_PORT,RS	;send char
lcd_send	movwf	CHAR
	swapf	CHAR,w
	andlw	0x0f
	movwf	LCD_PORT
	bsf	LCD_CMD_PORT,E
	nop
	nop
	bcf	LCD_CMD_PORT,E
	call	delay
	call	delay
	movf	CHAR,w
	andlw	0x0f
	movwf	LCD_PORT
	bsf	LCD_CMD_PORT,E
	nop
	nop
	bcf	LCD_CMD_PORT,E
	call	delay
	call	delay
	bsf	INTCON,GIE
	return


delay	movlw	0xff
	movwf	TEMP1
dloop	decfsz	TEMP1,F
	goto	dloop
	return

INIT_LCD
	call	delay
	call	delay
	call	delay
	call	delay
	movlw	0x32		;init
	call	lcd_send_cmd
	movlw	0x2C		;2 line
	call	lcd_send_cmd
	movlw	0x08		;disp off
	call	lcd_send_cmd
	movlw	0x01		;clear disp
	call	lcd_send_cmd
	movlw	0x06		;entry mode
	call	lcd_send_cmd
	movlw	0x0C		;cursor
	call	lcd_send_cmd

	movlw	0x86		;move cursor
	call	lcd_send_cmd
	movlw	0x47		;G
	call	lcd_send_char
	movlw	0x36		;6
	call	lcd_send_char
	movlw	0x55		;U
	call	lcd_send_char
	movlw	0x59		;Y
	call	lcd_send_char
	movlw	0x4A		;J
	call	lcd_send_char

	goto	RETURN_INIT_LCD

UPDATE_DISPLAY
	movlw	0x02
	call	lcd_send_cmd	;goto home
	bcf	DISPLAY_INFO,7
digit10	swapf	DISPLAY5,W
	andlw	0x0f
	btfss	STATUS,Z
	goto	dig10a	
	movlw	0x20
	call	lcd_send_char
	movlw	0x20
	call	lcd_send_char
	goto	digit9
dig10a	addwf	BCD2ASCII,W
	call	lcd_send_char	;display GHz units
	bsf	DISPLAY_INFO,7
	movlw	0x2C
	call	lcd_send_char	;display comma
digit9	movf	DISPLAY5,W
	andlw	0x0f
	btfss	STATUS,Z
	goto	digit9a	
	btfsc	DISPLAY_INFO,7
	goto	digit9a
	movlw	0x20
	call	lcd_send_char
	goto	digit8
digit9a	addwf	BCD2ASCII,W
	call	lcd_send_char	;display MHz hundreds
	bsf	DISPLAY_INFO,7
digit8	swapf	DISPLAY4,W
	andlw	0x0f
	btfss	STATUS,Z
	goto	digit8a	
	btfsc	DISPLAY_INFO,7
	goto	digit8a
	movlw	0x20
	call	lcd_send_char
	goto	digit7
digit8a	addwf	BCD2ASCII,W
	call	lcd_send_char	;display MHz tens
digit7	movf	DISPLAY4,W
	andlw	0x0f
	addwf	BCD2ASCII,W
	call	lcd_send_char	;display MHz units
	movlw	0x2E
	call	lcd_send_char	;display decimal point
	swapf	DISPLAY3,W
	andlw	0x0f
	addwf	BCD2ASCII,W
	call	lcd_send_char	;display kHz hundreds
	movf	DISPLAY3,W
	andlw	0x0f
	addwf	BCD2ASCII,W
	call	lcd_send_char	;display kHz tens
	swapf	DISPLAY2,W
	andlw	0x0f
	addwf	BCD2ASCII,W
	call	lcd_send_char	;display kHz units
	movlw	0x2C
	call	lcd_send_char	;display comma
	movf	DISPLAY2,W
	andlw	0x0f
	addwf	BCD2ASCII,W
	call	lcd_send_char	;display Hz hundreds
	swapf	DISPLAY1,W
	andlw	0x0f
	addwf	BCD2ASCII,W
	call	lcd_send_char	;display Hz tens
	movf	DISPLAY1,W
	andlw	0x0f
	addwf	BCD2ASCII,W
	call	lcd_send_char	;display Hz units
	movlw	0x4D
	call	lcd_send_char	;display M
	movlw	0x48
	call	lcd_send_char	;display H
	movlw	0x7a
	call	lcd_send_char	;display z

;	goto	RETURN_UPDATE_DISPLAY

UPDATE_MODE_GATE
	movlw	0xC0
	call	lcd_send_cmd	;move to second line for MODE

	movf	MODE,W			;display 70MHz
	btfss	STATUS,Z
	goto	display_mode1
	movlw	'7'
	call	lcd_send_char
	movlw	'0'
	call	lcd_send_char
	movlw	'M'
	call	lcd_send_char
	movlw	'H'
	call	lcd_send_char
	movlw	'z'
	call	lcd_send_char
	movlw	' '
	call	lcd_send_char
	goto	display_gate

display_mode1
	movlw	0x01			;display 150MHz
	subwf	MODE,W
	btfss	STATUS,Z
	goto	display_mode2
	movlw	'1'
	call	lcd_send_char
	movlw	'5'
	call	lcd_send_char
	movlw	'0'
	call	lcd_send_char
	movlw	'M'
	call	lcd_send_char
	movlw	'H'
	call	lcd_send_char
	movlw	'z'
	call	lcd_send_char
	goto	display_gate

display_mode2
	movlw	0x02			;display 2GHz
	subwf	MODE,W
	btfss	STATUS,Z
	goto	display_mode3
	movlw	'2'
	call	lcd_send_char
	movlw	'G'
	call	lcd_send_char
	movlw	'H'
	call	lcd_send_char
	movlw	'z'
	call	lcd_send_char
	movlw	' '
	call	lcd_send_char
	movlw	' '
	call	lcd_send_char
	goto	display_gate

display_mode3
	movlw	0x03			;display 4GHz
	subwf	MODE,W
	btfss	STATUS,Z
	goto	display_mode4
	movlw	'4'
	call	lcd_send_char
	movlw	'G'
	call	lcd_send_char
	movlw	'H'
	call	lcd_send_char
	movlw	'z'
	call	lcd_send_char
	movlw	' '
	call	lcd_send_char
	movlw	' '
	call	lcd_send_char
	goto	display_gate

display_mode4
	movlw	'I'			;display IC202
	call	lcd_send_char
	movlw	'C'
	call	lcd_send_char
	movlw	'2'
	call	lcd_send_char
	movlw	'0'
	call	lcd_send_char
	movlw	'2'
	call	lcd_send_char
	movlw	' '
	call	lcd_send_char

display_gate
	movlw	0xC9			
	call	lcd_send_cmd		;move to gate time

	movf	GATE_TIME,W		;display 1 second gate time
	btfss	STATUS,Z
	goto	half_sec
	movlw	' '
	call	lcd_send_char
	movlw	' '
	call	lcd_send_char
	movlw	' '
	call	lcd_send_char
	movlw	'1'
	call	lcd_send_char
	goto	display_sec
	
half_sec
	movlw	0x01			;display 1/2 second gate time
	subwf	GATE_TIME,W
	btfss	STATUS,Z
	goto	quarter_sec
	movlw	' '
	call	lcd_send_char
	movlw	'0'
	call	lcd_send_char
	movlw	'.'
	call	lcd_send_char
	movlw	'5'
	call	lcd_send_char
	goto	display_sec

quarter_sec
	movlw	0x02			;display 1/2 second gate time
	subwf	GATE_TIME,W
	btfss	STATUS,Z
	goto	display_sec
	movlw	'0'
	call	lcd_send_char
	movlw	'.'
	call	lcd_send_char
	movlw	'2'
	call	lcd_send_char
	movlw	'5'
	call	lcd_send_char

display_sec
	movlw	's'
	call	lcd_send_char
	movlw	'e'
	call	lcd_send_char
	movlw	'c'
	call	lcd_send_char

	goto	RETURN_UPDATE_DISPLAY

B2BCD
	bcf	STATUS,0
	movlw	.32
	movwf	COUNT
	clrf	DISPLAY5
	clrf	DISPLAY4
	clrf	DISPLAY3
	clrf	DISPLAY2
	clrf	DISPLAY1
loop32	rlf	FREQ1,F
	rlf	FREQ2,F
	rlf	FREQ3,F
	rlf	FREQ4,F
	rlf	DISPLAY1,F
	rlf	DISPLAY2,F
	rlf	DISPLAY3,F
	rlf	DISPLAY4,F
	rlf	DISPLAY5,F
	decfsz	COUNT,F
	goto	ADJ_DEC
	goto	RETURN_B2BCD

ADJ_DEC
	movlw	DISPLAY1
	movwf	FSR
	call	ADJ_BCD

	movlw	DISPLAY2
	movwf	FSR
	call	ADJ_BCD

	movlw	DISPLAY3
	movwf	FSR
	call	ADJ_BCD

	movlw	DISPLAY4
	movwf	FSR
	call	ADJ_BCD

	movlw	DISPLAY5
	movwf	FSR
	call	ADJ_BCD

	goto	loop32

ADJ_BCD
	movlw	0x03
	addwf	0,w
	movwf	TEMP1
	btfsc	TEMP1,3
	movwf	0
	movlw	0x30
	addwf	0,w
	movwf	TEMP1
	btfsc	TEMP1,7
	movwf	0
	retlw	0

IC202_OFFSET			;offset of 10.6985MHz
	movlw	0x04
	addwf	FREQ1,F
	btfsc	STATUS,C
	incf	FREQ2,F
	movlw	0x3F
	addwf	FREQ2,F
	btfsc	STATUS,C
	incf	FREQ3,F
	movlw	0xA3
	addwf	FREQ3,F
	btfsc	STATUS,C
	incf	FREQ4,F
	goto	RETURN_OFFSET


COUNTER

channel_mode0			;mode 0 = channel 0
	movf	MODE,W		;70MHz LF input max
	btfss	STATUS,Z
	goto	channel_mode1
	bcf	PORTB,CH_SEL0
	bcf	PORTB,CH_SEL1
	goto	channel_set

channel_mode1			;mode 1 = channel 1
	movlw	0x01		;150MHz LF input max
	subwf	MODE,W
	btfss	STATUS,Z
	goto	channel_mode2
	bsf	PORTB,CH_SEL0
	bcf	PORTB,CH_SEL1
	goto	channel_set

channel_mode2			;mode 2 = channel 2
	movlw	0x02		;2GHz HF input max
	subwf	MODE,W
	btfss	STATUS,Z
	goto	channel_mode3
	bcf	PORTB,CH_SEL0
	bsf	PORTB,CH_SEL1
	bsf	PORTA,PRE_MOD
	goto	channel_set

channel_mode3			;mode 3 = channel 3
	movlw	0x03		;4GHz HF input max
	subwf	MODE,W
	btfss	STATUS,Z
	goto	channel_mode4
	bcf	PORTB,CH_SEL0
	bsf	PORTB,CH_SEL1
	bcf	PORTA,PRE_MOD
	goto	channel_set

channel_mode4			;mode 4 = channel 1
	movlw	0x04		;IC202 LF input
	subwf	MODE,W
	btfss	STATUS,Z
	goto	channel_set
	bsf	PORTB,CH_SEL0
	bcf	PORTB,CH_SEL1

channel_set
	movlw	0x01
	movwf	FREQ1
	clrf	FREQ2
	clrf	FREQ3
	clrf	FREQ4

;TIMING CONSTANTS for 4.096MHz clock
;delay = 11a + 7a(b-1) + 3ab(c-1) + 2 + (No. of NOP)
;1SEC 6 230 246
;1/2 SEC 11 130 118
;1/4 SEC 3 166 170

	movf	GATE_TIME,W		;set up delay constant
	btfss	STATUS,Z
	goto	half_sec_delay
	movlw	0x06
	movwf	GATE_TIME1		;6
	movlw	0xE6
	movwf	GATE_TIME2		;230
	movlw	0xF6
	movwf	GATE_TIME3		;246
	goto	gate_delay
	
half_sec_delay
	movlw	0x01
	subwf	GATE_TIME,W
	btfss	STATUS,Z
	goto	quarter_sec_delay
	movlw	0x0B
	movwf	GATE_TIME1		;11
	movlw	0x82
	movwf	GATE_TIME2		;130
	movlw	0x76
	movwf	GATE_TIME3		;118
	goto	gate_delay

quarter_sec_delay
	movlw	0x02		;display 1/2 second gate time
	subwf	GATE_TIME,W
	btfss	STATUS,Z
	goto	display_sec
	movlw	0x03
	movwf	GATE_TIME1		;3
	movlw	0xA6
	movwf	GATE_TIME2		;166
	movlw	0xAA
	movwf	GATE_TIME3		;170

gate_delay
	bsf	PORTB,GATE		;reset gate just in case

	bsf	PORTB,RESET		;reset counters and s/r
	nop			;
	bcf	PORTB,RESET		;
	clrf	TMR0		;

	bcf	PORTB,GATE		;start gate
	
	nop			;gate delay
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop

	movf	GATE_TIME1,W
	movwf	TEMP3
loop_3	movf	GATE_TIME2,W
	movwf	TEMP2
loop_2	movf	GATE_TIME3,W
	movwf	TEMP1
loop_1	decfsz	TEMP1,F
	goto	loop_1
	decfsz	TEMP2,F
	goto	loop_2
	decfsz	TEMP3,F
	goto	loop_3

	bsf	PORTB,GATE		;end gate

	movf	TMR0,W		;move RTCC to low byte
	movwf	FREQ4		

	bsf	PORTB,CH_SEL0	;set channel to +5V
	bsf	PORTB,CH_SEL1	

CD4040_0
	btfsc	PORTA,COUNT_UP	;test 4060 output
	goto	CD4040_1		;128 <= count <= 255

	bsf	PORTB,GATE		;clock 4040 once
	nop
	bcf	PORTB,GATE
	movlw	0x01		;decrement the counter
	subwf	FREQ1,F		
	btfss	STATUS,C
	decf	FREQ2,F	
	goto	CD4040_0

CD4040_1
	btfss	PORTA,COUNT_UP	;test 4060 output
	goto	prescale		;it's rolled over (255 -> 0)

	bsf	PORTB,GATE		;clock 4040 once
	nop
	bcf	PORTB,GATE
	movlw	0x01		;decrement the counter
	subwf	FREQ1,F		
	btfss	STATUS,C
	decf	FREQ2,F	
	goto	CD4040_1		

prescale
	movf	TMR0,W		;Used to check for
	movwf	TEMP1		;prescaler roll over

pres1	bsf	STATUS,RP0		;set PAGE1
	bcf	OPTION_REG,T0SE	;clock prescaler
	nop
	bsf	OPTION_REG,T0SE
	bcf	STATUS,RP0		;set PAGE0	

	decf	FREQ3,F		;decrement counter
	movf	TMR0,W		;test TMR0 for prescale rollover
	xorwf	TEMP1,W		;if unchanged , XOR -> 0

	btfsc	STATUS,Z		;if 0 then clock again
	goto	pres1
	decf	FREQ3,F		;because we flushed 404


	swapf	FREQ2,F		;swap nibbles so we can move 4 places
				;and get rid of unused FREQ2 nibble
	movlw	.4		;shift FREQ1&2 4 places because we
	movwf	TEMP1		;only have a 12bit counter
correction
	bcf	STATUS,C
	rrf	FREQ4,F
	rrf	FREQ3,F
	rrf	FREQ2,F
	decfsz	TEMP1,F
	goto	correction

multiply_data
	movf	GATE_TIME,W		;start off with corrction 
	movwf	TEMP1		;for gate time

	movf	MODE,W		;no multiplier for Mode 0
	btfss	STATUS,Z
	goto	count_mode1
	goto	data_loop

count_mode1	
	movlw	0x01
	subwf	MODE,W
	btfss	STATUS,Z
	goto	count_mode2
	movlw	0x02		; *4 for Mode 1
	addwf	TEMP1,F
	goto	data_loop

count_mode2	
	movlw	0x02
	subwf	MODE,W
	btfss	STATUS,Z
	goto	count_mode3
	movlw	0x05		; *32 for Mode 2
	addwf	TEMP1,F
	goto	data_loop

count_mode3	
	movlw	0x03
	subwf	MODE,W
	btfss	STATUS,Z
	goto	count_mode4
	movlw	0x06		; *64 for Mode 3
	addwf	TEMP1,F
	goto	data_loop

count_mode4
	movlw	0x04
	subwf	MODE,W
	btfss	STATUS,Z
	goto	data_loop
	movlw	0x02		; *4 for Mode 4
	addwf	TEMP1,F
	goto	data_loop

data_loop
	movf	TEMP1,W
	btfsc	STATUS,Z
	goto	RETURN_COUNTER
rotate_data
	bcf	STATUS,C
	rlf	FREQ1,F
	rlf	FREQ2,F
	rlf	FREQ3,F
	rlf	FREQ4,F
	decfsz	TEMP1,F
	goto	rotate_data
	goto	RETURN_COUNTER


	END