;--------------------------------------------------------------------------
;	簡易弾速測定器　制御ソフト　ver0.21
;	2665年(平成17年) 石岡マロンズ 技術研究本部
;	URL:http://www.max.hi-ho.ne.jp/ksanso/ishioka/
;--------------------------------------------------------------------------
;*=*=*=*=*=*=*=*=*=*=*
;　変数の定義など
;*=*=*=*=*=*=*=*=*=*=*
	LIST	    P=PIC16F84A		;PIC16F84Aに適用
	INCLUDE		P16F84A.INC		;PIC16F84Aインクルードファイル読込

	__CONFIG   _CP_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC

;変数の定義
CNT_H	EQU	10H		;通過時間カウンタ（上位）
CNT_L	EQU	11H		;通過時間カウンタ（下位）
SUBA_H	EQU	12H		;引かれる数(H)
SUBA_L	EQU	13H		;引かれる数(L)
SUBB_H	EQU	14H		;引く数(H)
SUBB_L	EQU	15H		;引く数(L)
SYO_H	EQU	16H		;商(H)
SYO_L	EQU	17H		;商(L)
LED3	EQU	18H		;7セグ1桁目(100位)
LED2	EQU	19H		;7セグ2桁目(10位)
LED1	EQU	1AH		;7セグ3桁目(1位)
LEDOUT	EQU	1BH		;7セグ出力桁選択用
CNT1	EQU	20H		;タイマカウンタ 0.2ms用
CNT2	EQU	21H		;タイマカウンタ	5ms用
CNT3	EQU	22H		;タイマカウンタ	20ms用
CNT4	EQU	23H		;タイマカウンタ	200ms用
CNT5	EQU	24H		;タイマカウンタ	500ms用
CNT990	EQU	2FH		;デバッグ用

;*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
;　プログラム本体
;*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
	ORG		0x000
	GOTO	INIT			;初期設定へ

;+++++++++++++++++++++++++++++++++++++++++++++++++
;　初期設定
;+++++++++++++++++++++++++++++++++++++++++++++++++
INIT
	; Bank1の設定
 	BSF		STATUS,RP0	;バンク1に切替
	MOVLW	B'00000111'	;RA0:A位置検知入力
						;RA1:B位置検知入力
						;RA2:RESETスイッチ
						;RA3:ブザー出力
						;RA4:(未使用)
	MOVWF	TRISA		;上記設定を反映
	CLRF	TRISB		;PORTBは全部出力
						;RB0:(未使用)
						;RB1～3:表示桁切替出力
						;RA4～6:7セグ出力(デコーダへ)
	BCF		STATUS,RP0	;バンク0に戻す

;外部出力の初期化（初期値=0)
	CALL	RST			;変数の初期化

	;初期表示（ソフトバージョン表示）
	MOVLW	D'0'		;3桁目
	MOVWF	LED3
	MOVLW	D'2'		;2桁目
	MOVWF	LED2
	MOVLW	D'1'		;1桁目
	MOVWF	LED1


;+++++++++++++++++++++++++++++++++++++++++++++++++
;　メインルーチン
;+++++++++++++++++++++++++++++++++++++++++++++++++
MAIN
	BCF		PORTA,3		;ブザーオフ
;****************************
;*** リセットスイッチ処理 ***
;****************************
RST_SW
	CALL	LED7SEG		;7セグ表示ルーチンへ
	BTFSC	PORTA,2		;リセットが押されたか？0でオン
	GOTO	RST_SW		;入力なし,最初に戻る
	CALL	TIM20M	    ;20ms待つ
	BTFSC	PORTA,2		;再確認
	GOTO	RST_SW		;ノイズと判断,最初に戻る

	CALL	RST			;変数初期化
	CLRF	LED1
	CLRF	LED2
	CLRF	LED3

;*****************
;*** A位置検知 ***
;*****************
SEN_A
	BTFSS	PORTA,0		;A位置検知したか？1でオン
	GOTO	SEN_A	    ;入力なし,検知待ち
						;入力ありならカウント開始
;*****************
;*** B位置検知 ***
;*****************
;B位置検知は2μs周期(10サイクル）
;カウント数が65536を超えると、タイムアップでエラー
SEN_B
	BTFSC	PORTA,1		;B位置検知したか？1でオン(2)
	GOTO	TIMECAL		;カウント終了,通過時間演算へ
	INCF	CNT_L,F		;通過時間カウンタ+1(1)
	BTFSS	STATUS,Z	;CNT_Lの桁が溢れたか？(1or2)
	GOTO	SEN_BZ		;桁上がり処理をスキップ(2)
	INCF	CNT_H,F		;桁上がり発生時は、上位8ビットを+1(1)
	BTFSC	STATUS,Z	;CNT_Hの桁が溢れたか？(2)
	GOTO	TIMEUP		;タイムアップ時の処理へ
;	CALL	CYC990		;デバッグ用(990)
	GOTO	SEN_B		;B位置検知の最初に戻る(2)
SEN_BZ
	GOTO	$+1			;(2)
;	CALL	CYC990		;デバッグ用(990)
	GOTO	SEN_B		;B位置検知の最初に戻る(2)

;****************
;*** 速度演算 ***
;*****************
;(40000/CNTの割算)
;アルゴリズムを簡略化するため、引き算をn回繰り返す
TIMECAL
;*** 0除算防止処理 ***
;0で割り算するとプログラムが暴走するので、それを防止する処理
	MOVF	CNT_L,F			;
	BTFSS	STATUS,Z		;カウンタ下位バイトが0か？
	GOTO	FASTD			;0ではないので、過速度検知判定へ
	MOVF	CNT_H,F			;
	BTFSS	STATUS,Z		;カウンタ上位バイトが0か？
	GOTO	FASTD			;0ではないので、過速度検知判定へ
	GOTO	ZERODIG			;ゼロ除算エラー処理へ
;*** 過速度検知 ***
;カウント数が50以下(弾速800m/s以上)の時は、エラーと見なす。
FASTD
	MOVF	CNT_H,F			;
	BTFSS	STATUS,Z		;カウンタ上位バイトが0か？
	GOTO	TIME_INIT		;0ではないので、速度演算へ
	MOVLW	D'50'			;Wに50代入
	SUBWF	CNT_L,W			;カウンタ下位バイト-50
	BTFSS	STATUS,C		;ボロー発生時は次行実行
	GOTO	FASTERR			;過速度検知へジャンプ

TIME_INIT
;*** 割られる数(40000cnt) ***
;40000=9C40H
	MOVLW	09CH
	MOVWF	SUBA_H
	MOVLW	040H
	MOVWF	SUBA_L
;*** 割る数 ****
	MOVF	CNT_H,W			;カウント数上位をWに読出し(1)
	MOVWF	SUBB_H			;引かれる（割られる）数上位にセット
	MOVF	CNT_L,W			;カウント数下位をWに読出し(1)
	MOVWF	SUBB_L			;引かれる（割られる）数下位にセット
TIME_MAIN					;18サイクル(商の桁上がり時のみ20)
	CALL	GEN_X			;2バイトの減算サブルーチン(12)
	BTFSS	STATUS,C		;ボローが発生していれば0,次行実行(1or2)
	GOTO	TIME_END		;割算終了(2)
	INCF	SYO_L,F			;商を+1(1)
	BTFSS	STATUS,Z		;桁上がり発生で1,次行スキップ(1or2)
	GOTO	TIME_MAIN		;(2)
	INCF	SYO_H,F			;(1)
	GOTO	TIME_MAIN		;(2)
TIME_END
	GOTO 	DEC_CONV		;10進変換処理へ

;********************
;*** 速度表示演算 ***
;********************
;*** 16進→10進の換算 ***
DEC_CONV
	MOVF	SYO_H,W		;割算結果上位をWに読出し
	MOVWF	SUBA_H		;被減算上位に書込み
	MOVF	SYO_L,W		;割算結果下位をWに読出し
	MOVWF	SUBA_L		;被減算下位に書込み
;100の位
DIG3
;100=0064H
	CLRF 	SUBB_H		;上位に00Hを代入
	MOVLW	064H
	MOVWF	SUBB_L		;下位に64Hを代入
DIG3A
	CALL	GEN_X		;2バイト減算
	BTFSS	STATUS,C	;100を引いた結果、ボローが発生していれば次行実行
	GOTO	DIG3B		;余りの処理へジャンプ
	INCF	LED3		;+1
	MOVF	SUBA_L,F	;減算結果下位が0でないかチェック
	BTFSS	STATUS,Z	;0でなければDIG3Aに戻る
	GOTO	DIG3A
	MOVF	SUBA_H,F	;減算結果上位が0でないかチェック
	BTFSS	STATUS,Z	;0でなければDIG3Aに戻る,0なら100の位は確定
	GOTO	DIG3A
	GOTO	DIG3Z
DIG3B;引きすぎた余りを戻す処理
	CALL	ADD_X
DIG3Z;100位が0ならLEDを消灯する処理
	MOVF	LED3,F		;LED3が0でないかチェック
	BTFSS	STATUS,Z	;0でなければDIG2へ
	GOTO	DIG2
	MOVLW	D'15'
	MOVWF	LED3		;15をセット(消灯）

;10の位
;(DIG3の結果は0063Hよりも小さいから、上位バイトは無視できる。)
DIG2
	MOVLW	D'10'		
	SUBWF	SUBA_L,W	;10を引いてWに代入
	BTFSS	STATUS,C	;引算の結果が負か？
	GOTO	DIG2Z		;引算の結果が負なら2桁目の演算終了
	MOVWF	SUBA_L		;引算の結果をSUBA_Lに代入
	INCF	LED2		;+1
	MOVF	SUBA_L,F	;減算結果下位が0でないかチェック
	BTFSS	STATUS,Z	;引算の結果は0か?0なら2桁目の演算終了
	GOTO	DIG2		
DIG2Z;100位,10位ともに0ならLEDを消灯する処理
	MOVF	LED2,F
	BTFSS	STATUS,Z	;LED2が0でないかチェック
	GOTO	DIG1		;0でなければ消す必要はないのでDIG1へ
	MOVLW	D'15'		
	SUBWF	LED3,W		;15を引いてWに代入
	BTFSS	STATUS,Z	;引算の結果が0か？
	GOTO	DIG1		;0でなければ消す必要はないのでDIG1へ
	MOVLW	D'15'
	MOVWF	LED2		;15をセット(消灯）
;1の位
;(DIG2の結果は000AHよりも小さいから、上位バイトは無視できる。)
DIG1
	MOVLW	D'1'		
	SUBWF	SUBA_L,W	;10を引いてWに代入
	BTFSS	STATUS,C	;引算の結果が負か？
	GOTO	MAIN_END	;引算の結果が負なら2桁目の演算終了
	MOVWF	SUBA_L		;引算の結果をSUBA_Lに代入
	INCF	LED1		;+1
	MOVF	SUBA_L,F	;減算結果下位が0でないかチェック
	BTFSS	STATUS,Z	;引算の結果は0か?0なら2桁目の演算終了
	GOTO	DIG1		
	GOTO	MAIN_END

;*** 零除算時の表示 ***
ZERODIG
	MOVLW	D'9'		;3桁目
	MOVWF	LED3
	MOVLW	D'0'		;2桁目
	MOVWF	LED2
	MOVLW	D'15'		;1桁目
	MOVWF	LED1
	GOTO	MAIN_EREND	;最初に戻る

;*** 過速度検知時の表示 ***
FASTERR
	MOVLW	D'9'		;3桁目
	MOVWF	LED3
	MOVLW	D'9'		;2桁目
	MOVWF	LED2
	MOVLW	D'15'		;1桁目
	MOVWF	LED1
	GOTO	MAIN_EREND	;最初に戻る

;*** タイムアップ時の表示 ***
TIMEUP
	MOVLW	D'0'		;3桁目
	MOVWF	LED3
	MOVLW	D'0'		;2桁目
	MOVWF	LED2
	MOVLW	D'15'		;1桁目
	MOVWF	LED1
	GOTO	MAIN_EREND

;*** 正常終了時処理 ***
MAIN_END
	BSF		PORTA,3		;ブザーオン
	CALL	TIM200M
	CALL	RST			;変数を全て0に
	GOTO 	MAIN
;*** 異常終了時処理 ***
MAIN_EREND
	BSF		PORTA,3		;ブザーオン
	CALL	TIM200M
	BCF		PORTA,3		;ブザーオフ
	CALL	TIM200M
	BSF		PORTA,3		;ブザーオン
	CALL	TIM200M
	CALL	RST			;変数を全て0に
	GOTO 	MAIN

;+++++++++++++++++++++++++++++++++++++++++++++++++
;　サブルーチン
;+++++++++++++++++++++++++++++++++++++++++++++++++
;**************************
;*** タイマサブルーチン ***
;**************************
;１サイクル=4クロック(20MHzクロックの時,1サイクル=0.2μ秒)
;*** 0.2msタイマ ***
;基本
TIM02M
	MOVLW	D'248'		;2（呼出元のCALLで2サイクル）+1=3
	MOVWF	CNT1		;3+1=4
TIMLP1	NOP
	DECFSZ	CNT1,F		;1+1+2=4(ループ1回で4サイクル)
	GOTO	TIMLP1		;4+4*248-1=995（最後の1周は1サイクル少ないので-1）
	GOTO	$+1			;995+2=997
	NOP					;997+1=998
	RETURN				;998+2=1000
;*** 5m秒タイマ ***
;7セグ制御用
TIM5M
	MOVLW	D'25'		;2（呼出元のCALLで2サイクル）+1=3
	MOVWF	CNT2		;3+1=4
TIMLP2
	CALL	TIM02M		;1000サイクル
	DECFSZ	CNT2,F		;1000+1+2=1003(ループ1回で1003サイクル)
	GOTO	TIMLP2		;4+1003*25-1=25078
	RETURN				;25078+2=25080(誤差：0.3%)
;*** 20m秒タイマ ***
;スイッチ入力用
TIM20M
	MOVLW	D'100'		;2（呼出元のCALLで2サイクル）+1=3
	MOVWF	CNT3		;3+1=4
TIMLP3
	CALL	TIM02M		;1000サイクル
	DECFSZ	CNT3,F		;1000+1+2=1003(ループ1回で1003サイクル)
	GOTO	TIMLP3		;4+1003*100-1=100303
	RETURN				;100303+2=100305(誤差：0.3%)
;*** 200m秒タイマ ***
;ブザー用
TIM200M
	MOVLW	D'10'		;2（呼出元のCALLで2サイクル）+1=3
	MOVWF	CNT4		;3+1=4
TIMLP4
	CALL	TIM20M		;100305サイクル
	DECFSZ	CNT4,F		;100305+1+2=100307
	GOTO	TIMLP4		;4+100307*10-1=1003073
	RETURN				;1003073+2=1003075(誤差：0.3%)
;*** 500m秒タイマ ***
;ブザー用
TIM500M
	MOVLW	D'25'		;2（呼出元のCALLで2サイクル）+1=3
	MOVWF	CNT5		;3+1=4
TIMLP5
	CALL	TIM20M		;100305サイクル
	DECFSZ	CNT5,F		;100305+1+2=100307
	GOTO	TIMLP5		;4+100307*25-1=2507678
	RETURN				;2507678+2=2507680(誤差：0.3%)
;*** 990サイクル稼ぎ（デバッグ用） ***
CYC990
	MOVLW	D'246'		;2（呼出元のCALLで2サイクル）+1=3
	MOVWF	CNT990		;3+1=4
TIMLP990	NOP
	DECFSZ	CNT990,F	;1+1+2=4(ループ1回で4サイクル)
	GOTO	TIMLP990	;4+4*246-1=987（最後の1周は1サイクル少ないので-1）
	NOP					;987+1=988
	RETURN				;988+2=990

;**************************************
;*** リセットボタンを押した後の動作 ***
;**************************************
RST
;変数の初期化（全て初期値=0)
	CLRF	CNT1
	CLRF	CNT2
	CLRF	CNT3
	CLRF	CNT_H
	CLRF	CNT_L
	CLRF	SUBA_H
	CLRF	SUBA_L
	CLRF	SUBB_H
	CLRF	SUBB_L
	CLRF	SYO_H
	CLRF	SYO_L
	CLRF	LEDOUT

	RETURN

;*******************
;*** 2バイト減算 ***
;*******************
;演算結果はSUBA_H,SUBA_Lに上書きされる
;*** 任意の数の2バイト減算 ***
GEN_X					;11or12サイクル（呼び出し元CALL含む）
	MOVF	SUBB_L,W	;減算下位をWに読出し(1)
	SUBWF	SUBA_L,F	;SUBA_L-SUBB_L(1)
	MOVF	SUBB_H,W	;減算上位をWに読出し(1)
	BTFSS	STATUS,C	;SUBA_L-SUBB_Lでボローあったか？(1or2)
	INCFSZ	SUBB_H,W	;ボローありのとき、被減算上位+1してWに読出し(1or2)
	SUBWF	SUBA_H,W	;被減算上位からWを引く(1)
	MOVWF	SUBA_H		;(1)
	RETURN				;(2)

;*******************
;*** 2バイト加算 ***
;*******************
;演算結果はSUBA_H,SUBA_Lに上書きされる
;*** 任意の数の2バイト加算 ***
ADD_X					;11or12サイクル（呼び出し元CALL含む）
	MOVF	SUBB_L,W	;加算下位をWに読出し(1)
	ADDWF	SUBA_L,F	;SUBA_L+SUBB_L(1)
	MOVF	SUBB_H,W	;加算上位をWに読出し(1)
	BTFSC	STATUS,C	;SUBA_L+SUBB_Lでキャリーあったか？(1or2)
	INCFSZ	SUBB_H,W	;キャリーありのとき、加算上位+1してWに読出し(1or2)
	ADDWF	SUBA_H,F	;SUBA_H+SUBB_H(1)
	RETURN


;*****************
;*** 7セグ表示 ***
;*****************
LED7SEG
;3桁目点灯
	MOVF	LED3,W		;7セグに表示させたい数字をWに代入
	IORLW	B'10000000'	;3桁目を選択（後でbit7とbit3を入替）
	MOVWF	LEDOUT		;LEDOUTに代入
	SWAPF	LEDOUT,W	;上4桁と下4桁を入れ替え、Wにコピー
	MOVWF	PORTB		;PORTBに出力
	CALL	TIM5M		;5ms待つ
	CLRF	PORTB		;全消去
;2桁目点灯
	MOVF	LED2,W		;7セグに表示させたい数字をWに代入
	IORLW	B'01000000'	;3桁目を選択（後でbit7とbit3を入替）
	MOVWF	LEDOUT		;LEDOUTに代入
	SWAPF	LEDOUT,W	;上4桁と下4桁を入れ替え、Wにコピー
	MOVWF	PORTB		;PORTBに出力
	CALL	TIM5M		;5ms待つ
	CLRF	PORTB		;全消去
;1桁目点灯
	MOVF	LED1,W		;7セグに表示させたい数字をWに代入
	IORLW	B'00100000'	;3桁目を選択（後でbit7とbit3を入替）
	MOVWF	LEDOUT		;LEDOUTに代入
	SWAPF	LEDOUT,W	;上4桁と下4桁を入れ替え、Wにコピー
	MOVWF	PORTB		;PORTBに出力
	CALL	TIM5M		;5ms待つ
	CLRF	PORTB		;全消去

	RETURN				;サブルーチン終わり

	END					;おしまい
