;------------------------------------------------------------------------------
; RPM Meter
;------------------------------------------------------------------------------
; バージョン履歴
; 2022/12/11 Ver.0.04	div24の移植が間違っていたため228RPM付近で変な表示になっていた、参考サイト変更
;			高回転数になると画面の更新が速すぎて文字が読めないため、別途リフレッシュ上限カウンタを設けた
;			TMR1HHHの繰り上がり処理が割り込み中のキャプチャ発生で正しく行われない可能性あり
; 2022/12/04 Ver.0.03	10MHzと4MHzのソースを統合してCLKFREQで切り替えるようにした
; 2022/12/03 Ver.0.02	完成
; 2022/12/03 Ver.0.01	LEDダイナミック点灯試験終了
; 2022/12/03		基板作成
;------------------------------------------------------------------------------
; IRP,RP1,RP0を切り替える所で◆ RP=, ● IRP=をコメントに置く
; PCLATHを切り替える所で■ PCLATHをコメントに置く
; サブルーチンからの戻りの時、
;  RP1,RP0は0に戻してリターンする。
;  IRP,PCLATHは戻さずにリターンする。
; ルーチン呼び出しの時もRP=0を基本として記述している。
;------------------------------------------------------------------------------
; デバイス	PIC16F873,PIC16F873A,PIC16F876,PIC16F876A
; 動作周波数	4MHz or 10MHz
; 今回PCLATHは使用せず高速化の為に処理を完全に省いている
;------------------------------------------------------------------------------
	LIST		P=PIC16F873A,R=DEC,N=0
	INCLUDE		"P16F873A.INC"
	ERRORLEVEL	0,-302,-306,-307
	__CONFIG 	0x3F72		; FOSC : HS
					; WDTE : Disable
					; PWRTE: Enable
					; BODEN: Enable
					; VLP  : RB3=I/O
					; CPD  : Not Protect
					; WRT  : Disable
					; CP   : Not Protect
	#define	CLKFREQ	4		; クロック周波数によって10または4を指定する
;------------------------------------------------------------------------------
; 変数宣言
;------------------------------------------------------------------------------
TMR1HHH		EQU	0x20		; TMR1Hの繰り上がりレジスタ
CCPR1HHH	EQU	0x21		; CCP割り込み発生時TMR1HHHをキャプチャした値

COLUMN		EQU	0x22		; 表示するLEDの桁
DIGIT1		EQU	0x23		; 上位桁のBCD値
DIGIT2		EQU	0x24
DIGIT3		EQU	0x25
DIGIT4		EQU	0x26		; 下位桁
PATTERN		EQU	0x27		; 表示パターン
REFCYCL		EQU	0x28		; 表示リフレッシュサイクルカウンタ

FLAG		EQU	0x2F

MATH00		EQU	0x30		; LSB
MATH01		EQU	0x31		;  :
MATH02		EQU	0x32		; MSB
MATH10		EQU	0x33
MATH11		EQU	0x34
MATH12		EQU	0x35
MATH20		EQU	0x36
MATH21		EQU	0x37
MATH22		EQU	0x38
MATH30		EQU	0x39
MATH31		EQU	0x3A
MATH32		EQU	0x3B

TMP00		EQU	0x70		; 一時変数

W_		EQU	0x7C		; 割り込み退避用
STATUS_		EQU	0x7D
;PCLATH_	EQU	0x7E
FSR_		EQU	0x7F
W2_		EQU	0xFC

;------------------------------------------------------------------------------
; 定数宣言
;------------------------------------------------------------------------------
; PORTA 全未使用のためアナログモード 初期化も行っていない
; PORTB
LED_4		EQU	1		; RB0 1の桁
LED_3		EQU	2		; RB1
LED_2		EQU	4		; RB2
LED_1		EQU	8		; RB3 1000の桁
SEG_D		EQU	4
SEG_E		EQU	5
SEG_G		EQU	6
SEG_F		EQU	7
; PORTC
; 0	オープン(L出力)
; 1	オープン(L出力)
SENSOR		EQU	2		; ホールセンサ入力
DEBUG		EQU	3		; デバッグ用出力
SEG_A		EQU	4
SEG_B		EQU	5
SEG_C		EQU	6
SEG_DP		EQU	7

; FLAG
HIGHSPEED	EQU	0		; 8192RPM以上の1/10表示モード
CAPTURED	EQU	1		; キャプチャ済みポーリングフラグ
ZERORPM		EQU	2		; 強制的に0回転にするフラグ
REFRESH		EQU	3		; 

; クロック依存の定数
#if CLKFREQ == 4
 __IDLOCS	4
T1CKPS		EQU	B'10'		; T1CONに指定するプリスケーラの値
#define		RPMREF	60		; 計算が24bitに収まるよう基準秒数を指定
RPMREF_H	EQU	0xE4		;   30秒基準の時は計算時にキャプチャ値を2で割っている
RPMREF_M	EQU	0xE1		; RPM計算の基準値
RPMREF_L	EQU	0xC0		;  4MHz T1CKPS=1:4 4us 60RPM=250,000count*60=15,000,000(0xE4E1C0)
ZEROCOUNT	EQU	5		; 0RPMとするカウント値 0x50000 45RPM
					; LED桁間タイマのプリ・ポストスケーラ
T2CKOUTPS	EQU	B'01001000'	;  Pre=1, Post=10, 4MHz=2.56ms 全桁リフレッシュ98Hz
REFLIMIT	EQU	63		; LED文字更新の上限レートマスク、T2割り込みと同期している 2.56ms*64=0.164ms(6.1Hz)
#endif

#if CLKFREQ == 10
 __IDLOCS	10
T1CKPS		EQU	B'11'
#define		RPMREF	30
RPMREF_H	EQU	0x8F		; 10MHz T1CKPS=1:8 3.2us 30RPM=312,500count*30=9,375,000(0x8F0D18)
RPMREF_M	EQU	0x0D
RPMREF_L	EQU	0x18
ZEROCOUNT	EQU	7		; 0x70000 43RPM

T2CKOUTPS	EQU	B'00101001'	; Pre=4, Post=6, 10MHz=2.4576ms 全桁リフレッシュ102Hz
REFLIMIT	EQU	63		; 2.4576*64=0.157ms(6.4Hz)
#endif
;------------------------------------------------------------------------------
; 電源ON
;------------------------------------------------------------------------------
 	CALL	startup_sequence
	GOTO	MAINLOOP
;------------------------------------------------------------------------------
; 割り込み分岐
;------------------------------------------------------------------------------
	ORG	4
INTERRUPT
	MOVWF	W_			; Wセーブ
	SWAPF	STATUS,W		; STATUSセーブ
	CLRF	STATUS			; ◆ RP=0
	MOVWF	STATUS_

	MOVF	TMR1HHH,W		; いち早くTMR1HHHを手動でキャプチャしておく
	BTFSC	PIR1,CCP1IF		; キャプチャ
	 GOTO	INT_CCP1IF
	BTFSC	PIR1,TMR1IF		; TMR1ロールオーバーカウント
	 GOTO	INT_TMR1IF
	BTFSC	PIR1,TMR2IF		; LEDダイナミック点灯処理
	 GOTO	INT_TMR2IF
	
	CLRF	PIR1
INT_RETURN
	SWAPF	STATUS_,W		; STATUSリストア
	MOVWF	STATUS
	SWAPF	W_,F			; Wリストア
	SWAPF	W_,W
	RETFIE
;------------------------------------------------------------------------------
INT_CCP1IF				; キャプチャイベント
	CLRF	TMR1L
	CLRF	TMR1H
	CLRF	TMR1HHH
	MOVWF	CCPR1HHH		; TMR1HHHは割り込み分岐前にキャプチャ済みなので格納だけ行う
	BSF	FLAG,CAPTURED
	BCF	PIR1,CCP1IF
	GOTO	INT_RETURN
;------------------------------------------------------------------------------
INT_TMR1IF				; TMR1Hロールオーバー処理
	BCF	PIR1,TMR1IF
	INCF	TMR1HHH,W
	MOVWF	TMR1HHH
	SUBLW	ZEROCOUNT		; ZEROCOUNT以上になればパルス幅が長すぎると判断して強制的に0RPMのキャプチャを発生させる
	BTFSS	STATUS,Z
	 GOTO	INT_RETURN
	BSF	FLAG,CAPTURED
	BSF	FLAG,ZERORPM
	GOTO	INT_RETURN
;------------------------------------------------------------------------------
INT_TMR2IF				; LED点灯桁切り替え
	MOVF	FSR,W			; FSRセーブ
	MOVWF	FSR_
	BCF	PIR1,TMR2IF

	MOVF	COLUMN,W
	ADDLW	DIGIT1
	MOVWF	FSR
	MOVF	INDF,W
	CALL	digitpattern
	MOVWF	PATTERN
	ANDLW	0xF0
	CLRF	PORTB			; 一旦消灯
	MOVWF	PORTC
	SWAPF	PATTERN,W
	ANDLW	0xF0
	MOVWF	PORTB

	DECF	COLUMN,W		; 2桁目の表示で高速モードの時はドットを点灯させる
	BTFSC	STATUS,Z
	 BTFSS	FLAG,HIGHSPEED		;  2桁目以外の表示or通常モードで消灯
	  BCF	PORTC,SEG_DP

	CALL	load_column
	IORWF	PORTB,F			; 点灯
	INCF	COLUMN,W
	ANDLW	3
	MOVWF	COLUMN

	INCF	REFCYCL,W		; リフレッシュ上限カウンタ循環
	ANDLW	REFLIMIT
	MOVWF	REFCYCL
	BTFSC	STATUS,Z
	 BSF	FLAG,REFRESH
	MOVF	FSR_,W			; FSRリストア
	MOVWF	FSR
	GOTO	INT_RETURN
;------------------------------------------------------------------------------
; LED点灯パターンテーブル
; .CBAFGED
;------------------------------------------------------------------------------
digitpattern
	ADDWF	PCL,F
	 RETLW	0xFB	; 0
	 RETLW	0xE0	; 1
	 RETLW	0xB7	; 2
	 RETLW	0xF5	; 3
	 RETLW	0xEC	; 4
	 RETLW	0xDD	; 5
	 RETLW	0xDF	; 6
	 RETLW	0xF8	; 7
	 RETLW	0xFF	; 8
	 RETLW	0xFD	; 9
	 RETLW	0x80	; スペース
	 RETLW	0x84	; -
	 RETLW	0x80	; スペース
	 RETLW	0x80	; スペース
	 RETLW	0x80	; スペース
	 RETLW	0x80	; スペース
;------------------------------------------------------------------------------
; LEDポート選択テーブル
;------------------------------------------------------------------------------
load_column
	MOVF	COLUMN,W
	ANDLW	3
	ADDWF	PCL,F
	 RETLW	LED_1
	 RETLW	LED_2
	 RETLW	LED_3
	 RETLW	LED_4
;------------------------------------------------------------------------------
; 電源投入時のSFR初期化
;------------------------------------------------------------------------------
startup_sequence
	CLRF	PORTB
	CLRF	PORTC

	BSF	STATUS,RP0		; ◆ RP=1
	CLRF	TRISB			; PORTB 全出力
	MOVLW	B'00000100'		; PORTC センサポートのみ入力
	MOVWF	TRISC

	MOVLW	B'00000111'		; CCP1 TMR2,TMR1割り込み許可
	MOVWF	PIE1

	BCF	STATUS,RP0		; ◆ RP=0
	CLRF	TMR1L			; パルス周期計測カウンタ
	CLRF	TMR1H
	CLRF	TMR1HHH
	SWAPF	T1CKPS,W		; プリスケーラ=T1CKPSのEQU値
	ANDLW	B'00110000'
	IORLW	B'00000001'		; TIMER1有効 内部クロック
	MOVWF	T1CON

	CLRF	TMR2			; LED桁間ウエイトタイマ
	MOVLW	T2CKOUTPS
	IORLW	B'00000100'		; TIMER2有効
	MOVWF	T2CON
	
	MOVLW	B'00000100'		; パルス周期計測キャプチャ
	MOVWF	CCP1CON			; 立ち下がりモード
	
	CLRF	FLAG
	CLRF	COLUMN
	CLRF	REFCYCL
	MOVLW	11			; ----表示
	MOVWF	DIGIT1
	MOVWF	DIGIT2
	MOVWF	DIGIT3
	MOVWF	DIGIT4
	CLRF	PIR1
	BSF	INTCON,PEIE
	BSF	INTCON,GIE
	RETURN
;------------------------------------------------------------------------------
; メインループ
;   キャプチャとLED表示は割り込みで行い、
;   メインループでキャプチャ済みフラグをポーリンクして表示バッファを更新する。
;------------------------------------------------------------------------------
MAINLOOP
	BTFSC	FLAG,CAPTURED
	 BTFSS	FLAG,REFRESH		; リフレッシュ時間が経過するまではキャプチャしても表示を更新しない
	  GOTO	MAINLOOP
	BCF	FLAG,CAPTURED
	BCF	FLAG,REFRESH
	BTFSC	FLAG,ZERORPM
	 GOTO	RPMSTOP

	MOVLW	RPMREF_L		; キャプチャカウントからRPMを算出
	MOVWF	MATH00			; RPM=基準値/キャプチャ値
	MOVLW	RPMREF_M
	MOVWF	MATH01
	MOVLW	RPMREF_H
	MOVWF	MATH02

#if RPMREF == 60			; 60秒基準の時はキャプチャ済みの値を使う
	MOVF	CCPR1HHH,W
	MOVWF	MATH12
	MOVF	CCPR1H,W
	MOVWF	MATH11
	MOVF	CCPR1L,W
	MOVWF	MATH10
#endif

#if RPMREF == 30			; 30秒基準の時はキャプチャ済みの値を2で割る
	BCF	STATUS,C
	RRF	CCPR1HHH,W
	MOVWF	MATH12
	RRF	CCPR1H,W
	MOVWF	MATH11
	RRF	CCPR1L,W
	MOVWF	MATH10
#endif

	CALL	div24			; MATH0xにRPMが入る 約900サイクル
	MOVF	MATH01,W
	SUBLW	0x1F			; RPMが8192以上なら10で割った高速表示モードにする
	BCF	FLAG,HIGHSPEED
	BTFSS	STATUS,C
	 BSF	FLAG,HIGHSPEED
	BTFSC	STATUS,C
	 GOTO	TOLEDBUF
	MOVLW	10
	MOVWF	MATH10
	CLRF	MATH11
	CALL	div16			; 約375サイクル
TOLEDBUF
	MOVLW	DIGIT1			; RPMをバイナリからBCDに変換して表示バッファへ送る
	MOVWF	FSR
	BSF	STATUS,RP0		; ◆ RP=1
	BCF	PIE1,TMR2IE		; バッファの更新中は桁間タイマ割り込みを禁止
	BCF	STATUS,RP0		; ◆ RP=0
	CALL	dec_1000		; 約300サイクル
	CALL	dec_100			; 約300サイクル
	CALL	dec_10			; 約110サイクル
	CALL	dec_1			; 11サイクル
	CALL	zero_replace		; 約50サイクル
	BSF	STATUS,RP0		; ◆ RP=1
	BSF	PIE1,TMR2IE		; タイマ割り込み再開
	BCF	STATUS,RP0		; ◆ RP=0
	GOTO	MAINLOOP
	
RPMSTOP
	BCF	FLAG,ZERORPM
	BCF	FLAG,HIGHSPEED
	CLRF	MATH00
	CLRF	MATH01
	GOTO	TOLEDBUF
;------------------------------------------------------------------------------
dec_1000
	MOVLW	0x03			; 0x03E8 = 1,000
	MOVWF	MATH11
	MOVLW	0xE8
dec_div16
	MOVWF	MATH10
	CALL	div16
	GOTO	dec_1
;------------------------------------------------------------------------------
dec_100
	CLRF	MATH11			; 100
	MOVLW	100
	GOTO	dec_div16
;------------------------------------------------------------------------------
dec_10
	MOVLW	10			; 10
	MOVWF	MATH10
	CALL	div8
	GOTO	dec_1
;------------------------------------------------------------------------------
dec_1
	MOVF	MATH00,W		; BCD化
	MOVWF	INDF
	MOVF	MATH21,W		; 余りを次の桁の計算用に移動させる
	MOVWF	MATH01
	MOVF	MATH20,W
	MOVWF	MATH00
dec_return
	INCF	FSR,F
	RETURN
;------------------------------------------------------------------------------
; 頭から0を除去する
;
; 引数
;	なし
;
; 戻り値
;	DIGIT		表示文字列(NULLで終端)
;
; 使用変数
;	TMP00
;------------------------------------------------------------------------------
zero_replace
	MOVLW	3
	MOVWF	TMP00
	MOVLW	DIGIT1
	MOVWF	FSR
ZERO_REPL_LOOP
	MOVF	INDF,W
	BTFSS	STATUS,Z
	 RETURN
	MOVLW	10			; 0ならばスペースに置換
	MOVWF	INDF
	INCF	FSR,F
	DECFSZ	TMP00,F
	 GOTO	ZERO_REPL_LOOP
	RETURN

;------------------------------------------------------------------------------
; 8bit 割り算 http://elm-chan.org/docs/avrlib/div08.txt
;
; 引数
;	MATH00	割られる数(0x00..0xff)
;	MATH10	割る数(0x01..0x7f)(非破壊)
;
; 戻り値
;	MATH00	商
;	MATH20	余り
;
; 使用変数
;	TMP00
;------------------------------------------------------------------------------
div8
	CLRF	MATH20			; clr	mod		initialize variables
	MOVLW	8			; ldi	lc,8		mod = 0;
	MOVWF	TMP00			;			lc = 8;
DIV8_1					; ---- calcurating loop
	BCF	STATUS,C		; lsl	var1		var1 = var1 << 1;
	RLF	MATH00,F
	RLF	MATH20,F		; rol	mod		mod = mod << 1 + carry;
	MOVF	MATH10,W		; cp	mod,var2	if (mod < var2) {
	SUBWF	MATH20,W
	BTFSS	STATUS,C
	 GOTO	DIV8_2			; brcs	PC+3		} else {
	INCF	MATH00,F		; inc	var1		  var1++;
	MOVWF	MATH20			; sub	mod,var2	  mod -= var2;
DIV8_2					;			}
	DECFSZ	TMP00,F			; dec	lc		if (--lc > 0)
	 GOTO	DIV8_1			; brne	PC-7		  continue loop;
	RETURN
;------------------------------------------------------------------------------
; 16bit 割り算 http://elm-chan.org/docs/avrlib/div16.txt
;
; 引数
;	MATH00	LSB 割られる数(0x0000..0xffff)
;	MATH01	MSB
;	MATH10	LSB 割る数(0x0001..0x7fff)(非破壊)
;	MATH11	MSB
;
; 戻り値
;	MATH00	LSB 商
;	MATH01	MSB
;	MATH20	LSB 余り
;	MATH21	MSB
;
; 使用変数
;	TMP00
;------------------------------------------------------------------------------
div16
	CLRF	MATH20		; clr	mod0		initialize variables
	CLRF	MATH21		; clr	mod1		mod = 0;
	MOVLW	16		; ldi	lc,16		lc = 16;
	MOVWF	TMP00
DIV16_1				;---- calcurating loop
	BCF	STATUS,C	; lsl	var10		var1 = var1 << 1;
	RLF	MATH00,F
	RLF	MATH01,F	; rol	var11
	RLF	MATH20,F	; rol	mod0		mod = mod << 1 + carry;
	RLF	MATH21,F	; rol	mod1
	MOVF	MATH10,W	; cp	mod0,var20	if (mod < var2) {
	SUBWF	MATH20,W
	MOVF	MATH11,W
	BTFSS	STATUS,C	; cpc	mod1,var21
	 INCF	MATH11,W	;  Cフラグを足す
	SUBWF	MATH21,W
	BTFSS	STATUS,C
	 GOTO	DIV16_2		; brcs	PC+4		} else {
	INCF	MATH00,F	; inc	var10		  var1++;
	MOVF	MATH10,W	; sub	mod0,var20	  mod -= var2;
	SUBWF	MATH20,F
	MOVF	MATH11,W	; sbc	mod1,var21
	BTFSS	STATUS,C	;  Cフラグを足す
	 INCF	MATH11,W
	SUBWF	MATH21,F
DIV16_2				;			}
	DECFSZ	TMP00,F		; dec	lc		if (--lc > 0)
	 GOTO	DIV16_1		; brne	PC-11		  continue loop;
	RETURN
;------------------------------------------------------------------------------
; 24bit 割り算 http://www.piclist.com/techref/microchip/math/div/24by24tn.htm
;
; 引数
;	MATH00	LSB 割られる数
;	MATH01
;	MATH02	MSB
;	MATH10	LSB 割る数(破壊)
;	MATH11
;	MATH12	MSB
;
; 戻り値
;	MATH00	LSB 商
;	MATH01
;	MATH02	MSB
;	MATH20	LSB 余り
;	MATH21
;	MATH22	MSB
;
; 使用変数
;	TMP00,MATH30-32
;------------------------------------------------------------------------------
BCount		EQU	TMP00
nratorL		EQU	MATH00	; Numerator / Result
nratorM		EQU	MATH01
nratorH		EQU	MATH02
denomL		EQU	MATH10	; Denominator
denomM		EQU	MATH11
denomH		EQU	MATH12
remainL		EQU	MATH20	; Remainder
remainM		EQU	MATH21
remainH		EQU	MATH22
shiftL		EQU	MATH30	; Temporary
shiftM		EQU	MATH31
shiftH		EQU	MATH32

div24
	MOVLW	24		; set decimal 24 loop count
	MOVWF	BCount
	MOVF	nratorH,W	; copy Numerator into Shift Holding ram registers
	MOVWF	shiftH
	MOVF	nratorM,W
	MOVWF	shiftM
	MOVF	nratorL,W
	MOVWF	shiftL
	CLRF	nratorH		;  clear final Answer Numerator Ram locations
	CLRF	nratorM
	CLRF	nratorL
	
	CLRF	remainH		;  clear final Answer Remainder Ram locations
	CLRF	remainM
	CLRF	remainL
DLOOP
	BCF	STATUS,C	; bit clear Carry Flag in STATUS register
	RLF	shiftL,F	; Shift numerator(dividend) Left to move
	RLF	shiftM,F	; next bit to remainder
	RLF	shiftH,F	; and shift in next bit of result

	RLF	remainL,F	; shift carry (next Dividend bit) into remainder
	RLF	remainM,F
	RLF	remainH,F
	MOVF	denomH,W
	SUBWF	remainH,W	; subtract divsor(denomH) from(newly shifted left) Remainder HIGH byte.
	BTFSS	STATUS,Z
	GOTO	NOCHK		; skip if result was ZERO from good subtraction result

	MOVF	denomM,W
	SUBWF	remainM,W	; subtract divsor(denomM) from(newly shifted left) Remainder MIDDLE byte.
	BTFSS	STATUS,Z
	GOTO	NOCHK		; skip if result was ZERO from good subtraction result
	
	MOVF	denomL,W
	SUBWF	remainL,W	; subtract divsor(denomL) from(newly shifted left) Remainder LOW byte.
NOCHK
	BTFSS	STATUS,C	;  Carry SET? then denom is larger than reemainder
	GOTO	NOGO

	MOVF	denomL,W			 
	SUBWF	remainL,F	; Subtract denominator from remainder value in Low Byte
	BTFSC	STATUS,C	; Carry Set? Then execute fixup code for when a borrow is generated
	GOTO	NODEC_remainM	; when no borrow bit is needed from the higher byte positions.
	DECF	remainM,F	; Decrement to Borrow from Middle Byte, because carry was SET.
	MOVF	remainM,W
	XORLW	0xFF		; Check if rollover from Borrow occurred. remainM value went from 0 to 0xFF
	BTFSC	STATUS,Z
	DECF	remainH,F	; ZERO bit set, yes rollover, so Decrement to Borrow from High Byte, too!
NODEC_remainM
	MOVF	denomM,W
	SUBWF	remainM,F	; Subtract denominator from remainder value in Middle Byte
	BTFSS	STATUS,C
	DECF	remainH,F	; Decrement High Byte, to borrow 1 bit
	MOVF	denomH,W   		 
	SUBWF	remainH,F	; Subtract denominator from remainder value in High Byte
	BSF	STATUS,C	; set CARRY bit to rotate in Numerator Result next.
NOGO
	RLF	nratorL,F	; rotate Numerator result left 1 bit
	RLF	nratorM,F
	RLF	nratorH,F
	DECFSZ	BCount,F	; decrement the Loop Bit Counter
	GOTO	DLOOP
	RETURN
;------------------------------------------------------------------------------
        END