;***** FreqCounterII.asm **************************************************
;
;	周波数カウンターⅡ
;
;	Copyright (C) 2017   by M.Yamamoto. All rights reserved.
;
;     ( coding:  Ver. 0.00   2017/02/05  by M.Yamamoto )
;	coding:  Ver. 1.00   2017/02/23  初期バージョン
;	update:  Ver. 1.01   2017/02/26  １秒ゲート,SW入力の変更
;
;******************************************************* PicAsm 0242 ******

;	Device: PIC16F873A	Clock: 20MHz (クリスタル)
	
; Ver. 1.01
; ---------
; １秒ゲートの作成方法と、ゼロサプレススイッチのＯＮ検出方法の見直しを行った。
; 
; １秒ゲートの作成方法では、前バージョンでは 10mSのタイマー１の割り込み周期に補正をかけ、その
; 常に補正がかかった割り込み周期を 100回カウントする方法であったのを改め、変更後は、1 ～ 99回
; 目までは無補正の割り込み周期をカウントし、100回目だけに補正をかけるようにした。結果、後者の
; 方がきめ細かな時間単位で調節が可能となった。具体的には、前者では補正値を１だけ変更しても全
; 体では 100変更したことになってしまうが、後者では補正値は全体でも１づつの変更が可能である。
; 
; ゼロサプレススイッチのＯＮ検出方法では、前バージョンでは１秒のゲート外で検出していたのを、
; ゲート内で検出するように改めた。１秒のゲート内では文字通り１秒間の時間があるわけだが、それ
; に対して、ゲート外ではほんの一瞬の時間しかない。その時間比でカウンター動作を繰り返している
; が、当然ながらスイッチのＯＮ検出をするためには、ゲート外ではスイッチのＯＮを見逃すおそれが
; 大きいため、ゲート内で検出する方が圧倒的に有利となる。

;==========================================================================

;	PIC16F873A のピンと用途

;	  (1) MCLR: マスター・クリア入力	(28) RB7: LCD DB7 出力
;	  (2) RA0:  RCK 出力			(27) RB6: LCD DB6 出力
;	  (3) RA1:  /CCLR 出力			(26) RB5: LCD DB5 出力
;	  (4) RA2:  /CCKEN 出力			(25) RB4: LCD DB4 出力
;	  (5) RA3:  ZS_SW 入力			(24) RB3: LCD RS 出力
;	  (6) RA4:  予備			(23) RB2: LCD R/W 出力
;	  (7) RA5:  デバッグ用			(22) RB1: LCD E 出力
;	  (8) VSS:  電源 GND			(21) RB0: RCO 入力
;	  (9) OSC1: オシレータ水晶入力		(20) VDD: 電源 +5V DC
;	 (10) OSC2: オシレータ水晶入力		(19) VSS: 電源 GND
;	 (11) RC0:  QA 入力 			(18) RC7: QH 入力
;	 (12) RC1:  QB 入力 			(17) RC6: QG入力
;	 (13) RC2:  QC 入力 			(16) RC5: QF 入力
;	 (14) RC3:  QD 入力 			(15) RC4: QE 入力

;==========================================================================

	list		p=pic16f873A		;デバイス情報
	include		p16f873A.inc		;インクルードファイル
	errorlevel	-302			;Eliminate bank warning

;==========================================================================
;		コンフィギュレーションの設定
;==========================================================================
 
	__config _CP_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC

;==========================================================================
;		定数、変数の定義とレジスタ割付け
;==========================================================================

#define 	ver_no	"1.01"

		radix	dec

RCK		equ	0		;HC590信号 RCK    0:レジスタ状態は変化しない, 
					;		  1(立上りエッジ):カウンタのデータをレジスタにストア
CCLR		equ	1		;HC590信号 /CCLR  0:カウンタ値クリア, 1:値維持
CCKEN		equ	2		;HC590信号 /CCKEN 0:カウント許可, 1:禁止

ZS_SW		equ	3		;ゼロサプレススイッチ

LCD		equ	PORTB		;LCD 制御用ポート
TRIS_LCD	equ	TRISB		;LCD 制御用ポートの入出力切り替え
_E		equ	1		;LCD E:   動作起動信号
_RW		equ	2		;LCD R/W: 読み出し／書き込み選択信号
_RS		equ	3		;LCD RS:  レジスタ選択信号
_BF		equ	7		;LCD ビジーフラグ

;					; (Ver. 1.01 にて変更)
;xx		equ	24 			;補正値
;tm1_cycle	equ	65536 - 50000 + xx	;4/20MHz * 50000 = 0.2μS * 50000 = 0.01 S = 10 mS

tm1_cycle1	equ	65536 - 50000		;4/20MHz * 50000 = 0.2μS * 50000 = 0.01 S = 10 mS
yy		equ	3125 			;補正値
tm1_cycle2	equ	65536 - 50000 + yy	;100回目の割り込み時のみ使用

		cblock  h'20'
sec1cnt					;１秒カウンタ

bin3					;周波数入力カウンタ（最上位）
bin2					;
bin1					;
bin0					;周波数入力カウンタ（最下位）
dec9					;BIN → DEC 変換の出力エリア（最上位）
dec8					;
dec7					;
dec6					;
dec5					;
dec4					;
dec3					;
dec2					;
dec1					;
dec0					;BIN → DEC 変換の出力エリア（最下位）
dv10cnt					;bindec32サブルーチンで使用
dv10wk					;bindec32サブルーチンで使用

addr_high	;(LCD.subで使用)	;読み出すデータの high アドレス
addr_low	;(LCD.subで使用)	;読み出すデータの low アドレス
lcdtmp		;(LCD.subで使用)	;LCD への入出力データの一時保存

disp_cnt				;スタートメッセージの表示回数
dsp_point				;LCD表示位置ポインタ
zsflg					;ゼロサプレスフラグ

wt1cnt					;ウェイト用ループカウンタ
wt2cnt					;ウェイト用ループカウンタ
wt3cnt					;ウェイト用ループカウンタ
wt4cnt					;ウェイト用ループカウンタ
		endc

		cblock	h'70'
s_save					;STATUS レジスタの退避用
p_save					;PCLATH レジスタの退避用
w_save					;W レジスタの退避用
		endc

;==========================================================================
;		マクロ命令定義
;==========================================================================

;		LCD メッセージデータ表示マクロ

Disp_Lcd	macro	@com,@msg,@x
		movlw	@com
		call	Lcd_Cmd		;LCD へコマンドを出力
	#if 	@x == 0
	#else
		movlw	high @msg	;メッセージデータの先頭アドレスの high アドレス
		movwf	addr_high	;
		movlw	low @msg	;メッセージデータの先頭アドレスの low アドレス
		movwf	addr_low	;
	#endif
		call	Lcd_Disp	;メッセージデータ１行表示
		endm

;==========================================================================
;		プログラム開始
;==========================================================================

		org	0		;リセットベクタ
		goto	init
		org	4		;割り込みベクタ
		goto	interrupt

;==========================================================================
;		サブルーチンファイルのインクルード
;==========================================================================

		org	h'08'

		include		C:\Data\Project\PIC\_sub\LCD.sub

;==========================================================================
;		初期化処理
;==========================================================================

init
		bcf	STATUS,RP1	;バンク 0
		bcf	STATUS,RP0
		clrf	ADCON0		;ADCON0 の設定, A/D 未使用

		bsf	STATUS,RP0	;バンク 1
		movlw	h'06'		;PCFG=0110: すべてデジタルポート指定
		movwf	ADCON1

		movlw	h'80'		;PBPU=1: プルアップ抵抗を使用しない
		movwf	OPTION_REG

;		movlw	b'00111000'	;RA5-3: 入力, RA2-0: 出力
		movlw	b'00011000'	;RA4-3: 入力, RA5,RA2-0: 出力, RA5:デバッグ用
		movwf	TRISA
		movlw	b'00000001'	;RB7-1: 出力, RB0: 入力
		movwf	TRISB
		movlw	b'11111111'	;RC7-0: 入力
		movwf	TRISC

		bsf	PIE1,TMR1IE	;タイマー１割り込みを許可

		bcf	STATUS,RP0	;バンク 0
		movlw	b'00000001'	;bit7-6: 未使用
					;bit5-4(T1CKPS1,0 )=00: プリスケーラのスケール値 1:1
					;bit3(T1OSCEN)=0: 専用の発振器を使う場合に指定
					;bit2(T1SYNC)=0: 外部入力クロック指定時に有効
					;bit1(TMR1CS)=0: 内部クロックを使う
					;bit0(TMR1ON)=1: タイマー１を動作
		movwf	T1CON

		movlw	h'00'		;bit7(GIE)=0: 全割り込み禁止,
					;bit6(PEIE)=0: 周辺機器割り込み禁止,
					;bit4(INTE)=0: 外部割り込み(RB0/INT)禁止
		movwf	INTCON

		movlw	h'04'		;RA2(/CCKEN)=1: カウント禁止,
					;RA1(/CCLR)=0: カウンタクリア,
					;RA0(RCK)=0: レジスタ状態は変化しない	;()内: HC590信号名
		movwf	PORTA

		; S-10551D(LCD) 初期設定, スタートメッセージの表示

		clrf	LCD		;LCD 制御用ポート
		call	Lcd_Init	;LCD 初期設定 

		movlw	b'00001100'	;表示ON
		call	Lcd_Cmd		;LCD へコマンドを出力

		movlw	3
		movwf	disp_cnt
ini01
;		dt	" Freq.Co",0,"unterII ",0	を表示
		Disp_Lcd	b'00000010',start_msg,1	;カーソルホーム
		Disp_Lcd	b'11000000',         ,0	;DDRAM アドレスセット
		call	wait_1sec
		call	wait_500ms

;		dt	"    Ver.",0," x.xx   ",0	を表示
		Disp_Lcd	b'00000010',         ,0	;カーソルホーム
		Disp_Lcd	b'11000000',         ,0	;DDRAM アドレスセット
		call	wait_1sec
		call	wait_500ms

;		dt	"  by m.y",0,"amamoto ",0	を表示
		Disp_Lcd	b'00000010',         ,0	;カーソルホーム
		Disp_Lcd	b'11000000',         ,0	;DDRAM アドレスセット
		call	wait_1sec
		call	wait_500ms
		decfsz	disp_cnt,F
		goto	ini01

		movlw	b'00000001'	;表示クリア
		call	Lcd_Cmd		;LCD へコマンドを出力
		call	wait_1sec
;		goto	main

;==========================================================================
;		メイン・ルーチン
;==========================================================================

main
		movlw	b'00001100'	;表示オン／オフコントロール
		call	Lcd_Cmd		;D=1: 表示オン, C=0: カーソル非表示, B=0: 非ブリンク

;		dt	"  xx,xxx",0,",xxx Hz ",0	を表示
;		Disp_Lcd	b'00000010',freq_msg,0	;カーソルホーム
;		Disp_Lcd	b'11000000',        ,0	;DDRAM アドレスセット

		movlw	h'50'		;bit7(GIE)=0: 全割り込み禁止,
					;bit6(PEIE)=1: 周辺機器割り込み許可,
					;bit4(INTE)=1: 外部割り込み(RB0/INT)許可
		movwf	INTCON

		clrf	zsflg		;ゼロサプレスフラグの初期設定

main01	;	movlw	1						;
	;	btfss	PORTA,ZS_SW	;ゼロサプレススイッチ = OFF か？; (Ver. 1.01 にて削除)
	;	xorwf	zsflg,F		;No. フラグをビット反転		;

		clrf	bin3		;周波数入力カウンタの初期設定
		clrf	bin2
		clrf	bin1
		clrf	bin0

	;	movlw	high tm1_cycle	;				;
		movlw	high tm1_cycle1	;				; (Ver. 1.01 にて変更)
		movwf	TMR1H		;TMR1H に設定
	;	movlw	low tm1_cycle	;				;
		movlw	low tm1_cycle1	;				; (Ver. 1.01 にて変更)
		movwf	TMR1L		;TMR1L に設定
		movlw	100
		movwf	sec1cnt		;１秒カウンタの初期設定
		bcf	zsflg,7		;スイッチON入力受付済み解除	; (Ver. 1.01 にて追加)

		bsf	PORTA,CCLR	;RA1(/CCLR)=1: カウンタ値維持
		bsf	INTCON,GIE	;bit7(GIE)=1: 全割り込み許可
		bcf	PORTA,CCKEN	;RA2(/CCKEN)=0: カウント許可: (カウント開始)

main02		btfsc	zsflg,7		;スイッチON入力受付済みか？	;;(Ver. 1.01 にて変更)
		goto	main03		;Yes				;;
									;;
		btfsc	PORTA,ZS_SW	;ゼロサプレススイッチ = ON か？	;;
		goto	main03		;No				;;
									;;
		movlw	1						;;この間で HC590からオーバーフローした
		xorwf	zsflg,F		;No. フラグをビット反転		;;/RCO数を 割り込みでカウントする
		bsf	zsflg,7		;スイッチON入力受付済み設定	;;
									;;
main03		movf	sec1cnt,W	;10 mS * 100 = 1 S		;;
		btfss	STATUS,Z	;１秒経過したか？		;;
		goto	main02		;No				;;

		bsf	PORTA,CCKEN	;RA2(/CCKEN)=1: カウント禁止: (カウント終了)
		bcf	INTCON,GIE	;bit7(GIE)=0: 全割り込み禁止

		bsf	PORTA,RCK	;RA0(RCK)=1: カウンタのデータをレジスタにストア
		nop
		bcf	PORTA,RCK	;RA0(RCK)=0: レジスタ状態は変化しない
		movf	PORTC,W		;PORTC(HC590のレジスタ)の内容を
		movwf	bin0		;bin0に読み込む
		bcf	PORTA,CCLR	;RA1(/CCLR)=0: カウンタクリア

		call	bindec32	;32ビット数値を10進数10桁の文字列に変換
		call	freq_display	;LCD への "  xx,xxx,xxx Hz " 表示
		goto	main01

;==========================================================================
;		LCD への周波数表示
;==========================================================================

		; LCD への "  xx,xxx,xxx Hz " 表示
		;           0123456789ABCDEF

freq_display
		clrf	dsp_point	;LCD表示位置ポインタの初期設定
		movlw	dec9 + 2	;BIN → DEC 変換の出力エリア（最上位 + 2）
		movwf	FSR		;間接アドレスの先頭番地の初期設定
		btfsc	zsflg,0		;ゼロサプレスか？
		bsf	zsflg,1		;Yes. 仮フラグの設定

fdisp01		movf	dsp_point,W
		btfss	STATUS,Z	;dsp_point = 0 か？
		goto	fdisp02		;No

		movlw	b'00000010'	;DDRAM のアドレスをセット
		call	Lcd_Cmd		;LCD へコマンドを出力
		movlw	' '
		call	Lcd_Data	;LCD へデータを１文字出力
		incf	dsp_point,F	;LCD表示位置ポインタの更新
		movlw	' '
		call	Lcd_Data	;LCD へデータを１文字出力
		incf	dsp_point,F	;LCD表示位置ポインタの更新
		goto	fdisp01

fdisp02		movlw	4
		subwf	dsp_point,W
		btfsc	STATUS,Z	;dsp_point = 4 か？
		goto	fdisp03		;Yes

		movlw	8
		subwf	dsp_point,W
		btfss	STATUS,Z	;dsp_point = 8 か？
		goto	fdisp04		;No

		movlw	b'11000000'	;DDRAM のアドレスをセット
		call	Lcd_Cmd		;LCD へコマンドを出力

fdisp03		movlw	','
		btfsc	zsflg,1		;ゼロサプレスか？
		movlw	' '		;Yes
		call	Lcd_Data	;LCD へデータを１文字出力
		incf	dsp_point,F	;LCD表示位置ポインタの更新
		goto	fdisp01

fdisp04		movlw	12
		subwf	dsp_point,W
		btfsc	STATUS,C	;dsp_point >= 12 か？
		goto	fdisp07		;Yes

		btfss	zsflg,1		;ゼロサプレスか？
		goto	fdisp05		;No

		movf	INDF,W		;変換データ
		sublw	'0'
		movlw	' '		;
		btfsc	STATUS,Z	;変換データ = '0' か？
		goto	fdisp06		;Yes

		bcf	zsflg,1		;ゼロサプレス終了

fdisp05		movf	INDF,W		;変換データ

fdisp06		call	Lcd_Data	;LCD へデータを１文字出力
		incf	dsp_point,F	;LCD表示位置ポインタの更新
		incf	FSR,F		;間接アドレスの更新

		movlw	11
		subwf	dsp_point,W
		btfsc	STATUS,Z	;dsp_point = 11 か？
		bcf	zsflg,1		;Yes. ゼロサプレス終了
		goto	fdisp01

fdisp07		movlw	high freq_msg	;メッセージデータの先頭アドレスの high アドレス
		movwf	addr_high	;
		movlw	low  freq_msg	;メッセージデータの先頭アドレスの low アドレス
		movwf	addr_low	;
		call	Lcd_Disp	;メッセージデータ１行表示
		return

;==========================================================================
;		LCD（ S-10551D の場合 ）表示用 各種メッセージ等
;==========================================================================

		; スタートメッセージ

start_msg	dt	" Freq.Co",0,"unterII ",0
		dt	"    Ver.",0," ",ver_no,"   ",0
		dt	"  by m.y",0,"amamoto ",0
		;	 01234567     89ABCDEF

		; ストップウオッチ／タイマー進行メッセージ

freq_msg	dt	" Hz ",0

;		(以下、プログラム変更で不要となった)
;
;freq_msg	dt	"  xx,xxx",0,",xxx Hz ",0
;		;	 01234567     89ABCDEF
;
;		; 表示位置テーブル
;disp_tbl
;		addwf	PCL,F
;		;	0     1     2     3     4     5     6     7
;		dt	h'02',h'03',h'05',h'06',h'07',h'41',h'42',h'43'

;==========================================================================
;		ウェイト・ルーチン (at 20 MHz)
;==========================================================================

		; 100μ秒 ウェイトルーチン
wait_100us
		movlw	99		;1
		movwf	wt1cnt		;1
wait01		goto	$+1		;2
		decfsz	wt1cnt,F	;1,2
		goto	wait01		;2,0	1+1+(2+1+2)*98+(2+2+0)=496
;		goto	$+1		;2
		return			;2	496+2=498
					;       498/20MHz*4 = 99.6 μS = 100 μS = 0.1 mS

		; 1m 秒 ウェイトルーチン
wait_1ms
		movlw	10		;1
		movwf	wt2cnt		;1
wait02		call	wait_100us	;2	2+498=500
		decfsz	wt2cnt,F	;1,2
		goto	wait02		;2,0	1+1+(500+1+2)*9+(500+2+0)=5031
		return			;2	5031+2=5033
					;       5033/20MHz*4 = 1.0066 mS = 1 mS

		; 100m 秒 ウェイトルーチン
wait_100ms
		movlw	100

		; xx m 秒 ウェイトルーチン
wait_xxms
		movwf	wt3cnt		;1
wait03		call	wait_1ms	;2	2+5033=5035
		decfsz	wt3cnt,F	;1,2
		goto	wait03		;2,0	1+(5035+1+2)*(xx-1)+(5035+2+0)=5038*(xx)
		return			;2	5038*(xx)+2
					;

		; 500m 秒 ウェイトルーチン
wait_500ms
		movlw	5
		goto	wait04

		; 1 秒 ウェイトルーチン
wait_1sec
		movlw	10
wait04		movwf	wt4cnt

wait05		call	wait_100ms
		decfsz	wt4cnt,F
		goto	wait05
		return

;==========================================================================
;		割り込み処理
;==========================================================================

interrupt
		; レジスタの保存

		movwf	w_save		;W レジスタの保存
		swapf	STATUS,W
		clrf	STATUS
		movwf	s_save		;STATUS レジスタの保存
		movf	PCLATH,W
		movwf	p_save		;PCLATH レジスタの保存
		clrf	PCLATH

		btfsc	INTCON,INTF	;外部割り込み(RB0/INT) か？
		goto	int01		;Yes

		btfsc	PIR1,TMR1IF	;タイマー１オーバフロー か？
		goto	t1int01		;Yes
		goto	int_end		;No

		; 外部割り込み(RB0/INT)割り込み

int01		bcf	INTCON,INTF	;外部割り込み(RB0/INT)解除

		incf	bin1,F
		btfss	STATUS,Z
		goto	int_end		;No

		incf	bin2,F
		btfss	STATUS,Z
		goto	int_end		;No

		incf	bin3,F
		goto	int_end

		; タイマー１オーバフロー割り込み

t1int01		bcf	PIR1,TMR1IF	;タイマー１オーバフロー解除

	;	movlw	high tm1_cycle	;				;
		movlw	high tm1_cycle1	;				; (Ver. 1.01 にて変更)
		movwf	TMR1H		;TMR1H に設定
	;	movlw	low tm1_cycle	;				;
		movlw	low tm1_cycle1	;				; (Ver. 1.01 にて変更)
		movwf	TMR1L		;TMR1L に設定
		decf	sec1cnt,F	;１秒カウンタを -1 更新

		movlw	b'00100000'	;RA5: デバッグ用
		xorwf	PORTA,F		;ビット反転

		decfsz	sec1cnt,W	;１秒カウンタ = 0 か？		;
		goto	int_end		;No				;
									;
		movlw	high tm1_cycle2	;				; (Ver. 1.01 にて追加)
		movwf	TMR1H		;TMR1H に設定			;
		movlw	low tm1_cycle2	;				;
		movwf	TMR1L		;TMR1L に設定			;

		; 割り込み処理の終了

int_end		movf	p_save,W
		movwf	PCLATH		;PCLATH レジスタの復帰
		swapf	s_save,W
		movwf	STATUS		;STATUS レジスタの復帰
		swapf	w_save,F
		swapf	w_save,W	;W レジスタの復帰

		retfie			;割り込みからの復帰	

;==========================================================================
;		32ビット数値を10進数10桁の文字列に変換
;
;	入力:	bin3,bin2,bin1,bin0
;	出力:	dec9,dec8,dec7,dec6,dec5,dec4,dec3,dec2,dec1,dec0
;
;	     （ サイト "ELECTRONICS SHELVES" の "サブルーチン集" から
;		hxdec32c_mp.txt を拝借して編集し直したもの 
;		http://orange.zero.jp/electronics/pic/pic.html ）
;==========================================================================

bindec32
		movlw	dec0 + 1	;格納場所初期値
		movwf	FSR		;
		call	dev10		;最下位変換
		call	dev10
		call	dev10
		call	dev10
		call	dev10
		call	dev10
		call	dev10
		call	dev10
		call	dev10
		call	dev10		;最上位変換

		return

		; ÷10（10で除算）
dev10
		movlw	32		;32ビット繰り返し
		movwf	dv10cnt
		clrf	dv10wk

dev101		bcf	STATUS,C	;キャリフラグのクリア
		rlf	bin0,F
		rlf	bin1,F
		rlf	bin2,F
		rlf	bin3,F
		rlf	dv10wk,F
		movlw	h'f6'
		addwf	dv10wk,W
		btfsc	STATUS,W
		movwf	dv10wk
		btfsc	STATUS,W
		incf	bin0,F
		decfsz	dv10cnt,F
		goto	dev101

		decf	FSR,F
		movf	dv10wk,W
		movwf	INDF		;余り
		movlw	h'30'		;文字コードに変換
		addwf	INDF,F

		return

;==========================================================================
;		EEPROM エリア
;==========================================================================

		org	h'2100'

eep_st		de	"Copyright (C) 2017 by M.Yamamoto"

;==========================================================================

		end			;プログラムの終了
