;---------------------------------------------------------------
; LC VFO周波数をPICカウンタ制御で安定化する。
;---------------------------------------------------------------
; ORG 7N4KYG 高石
; Elecraft K-1 Version JA1XFA 田島
; Ver.  1.00 2013/12/01 Clock 10MHz版作成
;  1.01 2013/12/09
; 1.02 2013/12/14
; 1.03 2013/12/15 送信中のVFO制御を止める
; 1.04 2013/12/16 不要ルーチンを削除
; 1.05 2013/12/17 周波数が動かなかったら制御をパス
; 1.05.1 2013/12/18 同上 バグ修正
; 1.06 2013/12/25 周波数ステップの変更を追加
; 1.06.1 2013/12/27 原作のバグ修正ほか
; 1.07 2014/01/18 バグ、コメント修正ほか
; 1.08 2014/01/18 バグ、コメント修正ほか
; 1.09 2014/01/19 バグ、コメント修正ほか
; 1.10 2014/01/19 RIT使用直前の周波数を更新しない点を修正
; 1.101 2014/01/19 エンコーダ入力周り改修版
; 1.101R 2014/01/20 エンコーダ入力周り改修版(本来の接続)
;---------------------------------------------------------------
;ハードウェア(リグ)の信号条件
; RIT/PTTは単発ではなくレベルを維持する信号であること。
; つまり押しボタン的な信号条件は不可。トグルスイッチ的な信号が必要。
;
;システムクロック変更時にはカウンタのループ回数も変更すること。
;***** at Clock=20MHz *****
;wait_10us equ 164 ;100 usec の定数

;***** at Clock=16MHz ***** 80%
;wait_10us equ 131 ;100 usec の定数(若干短い)

;***** at Clock=10MHz ***** 50%
wait_10us equ 82 ;100 usec の定数

;***** at Clock= 4MHz ***** 20%
;WAIT_10US equ 32 ;100 usec の定数

;******************
;システム構成の定義
;******************
 .include 16f84.h  ;CPUに16F84を指定する
 .osc  hs  ;外部発振器、高速
 .wdt  off  ;ウォッチドッグを使用しない
 .pwrt  off  ;パワーアップタイマを使用しない
 .protect off  ;プロテクトはしない

;************************************************
;送受信に関するパラメータ群
;発信周波数 osc
;周波数ステップ
;************************************************
;Normal
step3  equ 00000000b ;周波数ステップ = 40Hz 2013/12/09
step2  equ 00000000b ;   3    2    1   0
step1  equ 00000000b ;  00   00   00  04 = 4
step0  equ 00000100b ;プログラム上で10倍比較ゆえに40。

;Wide
wstep3  equ 00000000b ;周波数ステップ = 100Hz 2013/12/28
wstep2  equ 00000000b ;   3    2    1   0
wstep1  equ 00000000b ;  00   00   00  0a = 10(Dec)
wstep0  equ 00001010b ;プログラム上で10倍比較ゆえに100。

;VFO発振周波数(電源On時に自動設定する周波数)/10 の値
;単位 発振周波数/10(KHz) CPUクロックには関係しない
;
; システム起動時の発振周波数の設定
; 2.990,4MHz
;設定値は、発振周波数の1/10となる
;
setf3 equ 00h  ; 2013-12-14
setf2 equ 04h  ; 04 b3 52
setf1 equ 0b3h  ;
setf0 equ 052h  ;

;******************************************
;ロータリーエンコーダに用いる H/W 定義
;******************************************
rea  equ rb.0  ;A相
reb  equ rb.1  ;B相

;******************************************
;周波数ステップ切り替えポート
; H=40Hz,L=100Hz
;******************************************
step_sw equ rb.2  ;周波数ステップSW
;******************************************
;ロータリーエンコーダで使用するメモリー定義
;******************************************
rodat ds 1
ropas ds 1

;******************************************
;システムに用いるメモリー定義
;******************************************
 org 0ch
cn  ds 1 ; 0ch カウンタ
wrk  ds 1 ; 0dh ワーク
freq  ds 4 ; 0eh-11h 現在の周波数
stfreq  ds 4 ; 12h-15h 設定周波数
trxfreq  ds 4 ; 16h-19h 送受信周波数
ritfreq  ds 4 ; 1ah-1dh RIT受信周波数
x  ds 4 ; 1eh-21h long 計算ワーク
y  ds 4 ; 22h-25h long 計算ワーク
;**********************
;システムで使用する変数
;**********************
wrks  ds 1 ; 26h ステータスの退避メモリ
wrkw  ds 1 ; 27h アキュムレータ退避メモリ
timecnt0 ds 1 ; 28h 割込みサービス変数
waitnss0 ds 1 ; 29h
waitnss1 ds 1 ; 2ah
waitnss2 ds 1 ; 2bh
par  ds 1 ; 2ch 関数間の受渡しデータ
parr  ds 1 ; 2dh 関数間の受渡しデータ
d8  ds 1 ; 2eh 関数間の補助受渡しデータ
os_wrk1  ds 1 ; 2fh μsec wait カウンタ
os_wrk2  ds 1 ; 30h 1msec wait カウンタ
rbddr  ds 1 ; 31h rb の設定データ
pa  ds 1 ; 32h ポートの読込みデータ
os_flg  ds 1 ; 33h フラグ用メモリ
lpcnt1  ds 1 ; 34h
lpcnt2  ds 1 ; 35h
;
;    ; Max 4fh
;*********************************************************
;各種フラグ&ポートの定義
;*********************************************************
timeup  equ os_flg.0 ;タイムアップフラグ
rs   equ os_flg.1 ;論理ポート
rrs   equ rb.2  ;物理ポート
ee   equ rb.3  ;


vdown  equ ra.0  ;vco-チャージ
vup   equ ra.1  ;vco+チャージ

;2013/12/27 rit_sw/tx_sw 配線図に合わせた。原作bugのfix.
rit_sw  equ ra.2  ;rit-on→sw
tx_sw  equ ra.3  ;送信時on→sw

count_in equ ra.4  ;tmr0のポートの開閉

;*********************************************************
;*****             プログラムセクション              *****
;*********************************************************
;*********************************************************
;システムの初期化と起動
;*********************************************************
 org 0  ;
 goto start  ;

;*********************************************************
;ロータリーエンコーダかの割り込み
;
;2014-Jan-21
;K−1は逆ダイヤルのためUp/Downの周波数計算を反対にした
;*********************************************************
 org 4  ;エンコーダの割り込み

 call wait1ms  ;1msウエイト
;
;機械式エンコーダで取りこぼしの場合は
;チャッタ除去のためウェイトを増す。
; call wait1ms  ;1msウエイト
; call wait1ms  ;1msウエイト

 movb rodat.1,reb ;B 入力
 btfss reb  ;rb1が1か確認する
 goto setp  ;
setm
 call inc_freq ;周波数を加算する(逆ダイヤル対応)
; call dec_freq ;周波数を減算する
 goto setf  ;

setp
 call dec_freq ;周波数を減算する(逆ダイヤル対応)
; call inc_freq ;周波数を加算する
setf
 btfss tx_sw  ;送信中?
 goto tx_new  ;yes..

 btfsc rit_sw  ;受信中、かつ RIT Off?
 goto tx_new  ;yes..

;受信中で RIT On
 mov ritfreq[0],stfreq[0] ;基準値を更新
 mov ritfreq[1],stfreq[1] ;
 mov ritfreq[2],stfreq[2] ;
 mov ritfreq[3],stfreq[3] ;
 goto int_pro

;送信中、または受信中で RIT Off
tx_new
 mov trxfreq[0],stfreq[0] ;基準値を更新
 mov trxfreq[1],stfreq[1] ;
 mov trxfreq[2],stfreq[2] ;
 mov trxfreq[3],stfreq[3] ;

int_pro
; call fcont_enc ;割り込み中でも周波数補正させる。
 goto main  ;メインに復帰、割り込み処理終了
;*********************************************************
;スタートアップ
;*********************************************************
start
 bsf status,5  ;set page 1
 mov option,#01100000b

 ;ra4(tmr0)の設定
     ;プリスケーラを含む
     ;prescaler 1/2 set
     ;RBプルアップ
     ;立ち下がりカウント
 bcf status,5  ;set page 0
 clr tmr0   ;Clear TMR0
 mov !ra,#00011100b  ;ra2,3,4を入力,ra0,1をOUT に

;2013/12/27 RB2 ステップ切替に使う (=0 then 40Hz/=1 then 100Hz)
; mov !rb,#00000011b  ;rb0,1 以外を全部 OUT に
 mov !rb,#00000111b  ;rb0,1,2 in 他全てOUT (RB2 Step切替)
 mov ra,#00000000b  ;ポート初期化
 mov rb,#00000000b  ;ポート初期化

 mov stfreq[3],#setf3 ;基準周波数を設定
 mov stfreq[2],#setf2 ;
 mov stfreq[1],#setf1
 mov stfreq[0],#setf0

 mov trxfreq[0],stfreq[0] ;送信周波数に基準値を設定
 mov trxfreq[1],stfreq[1] ;
 mov trxfreq[2],stfreq[2] ;
 mov trxfreq[3],stfreq[3] ;
 mov intcon,#10010000b ;rb0割り込みを許可する

; 初期化後メイン処理ループへ
;*********************************************************
;メインルーチン
;*********************************************************
main
 call f_count  ;カウンター
 call vfo_cont ;vfo制御
 goto main  ;メインに戻る

;************************************************************************
;周波数補正ルーチン エンコーダ割込より呼ばれる。
;************************************************************************
fcont_enc
 call f_count  ;周波数カウント その後vfo制御へ
;************************************************************************
;VFO制御
;stfreq-freq(セット周波数-現在の周波数)で引き算して差があるとき
;周波数が記憶している周波数(stfreq)になるように、+/-の制御信号を出す。
;************************************************************************
vfo_cont
 mov intcon,#00000000b ;rb0割り込みを許可しない。
;1.09
 btfss tx_sw   ;tx on?(送信中?)
 goto send_or_ritoff
;受信中
 btfss rit_sw   ;rit onなら
 goto rit_on   ;rit_onへジャンプする。

;RIT Off 受信中もしくは送信中
send_or_ritoff
 mov stfreq[0],trxfreq[0] ;送信周波数を基準にセット。
 mov stfreq[1],trxfreq[1] ;
 mov stfreq[2],trxfreq[2] ;
 mov stfreq[3],trxfreq[3] ;
;1.10 bug fix
 mov ritfreq[0],trxfreq[0] ;RIT周波数をクリア。
 mov ritfreq[1],trxfreq[1] ;
 mov ritfreq[2],trxfreq[2] ;
 mov ritfreq[3],trxfreq[3] ;

 goto freq_check  ;周波数ずれチェックへ

rit_on
;受信中かつ RIT On
 mov stfreq[0],ritfreq[0] ;受信周波数を基準にセット。
 mov stfreq[1],ritfreq[1] ;
 mov stfreq[2],ritfreq[2] ;
 mov stfreq[3],ritfreq[3] ;

;周波数があまり動かなかった場合の処理。2013-12-17 JA1XFA
;一計測周期前の周波数と今計測した周波数がずれたか調べる
;LC発振では精密な周波数維持は難しいので、計測値の最下
;桁の2ビット分の変動は無視する。
freq_check
 mov x[0],stfreq[0]  ;一つ前の受信周波数を作業変数に代入
 movlw 11111100b
 andwf x[0],1   ;下2ビットマスク

 mov y[0],freq[0]  ;最新計測周波数を作業変数に代入
 movlw 11111100b
 andwf y[0],1   ;下2ビットマスク
;---------------------------------------------------------------------------
; 計算対象値の上位3桁は全ビットを比較する
;---------------------------------------------------------------------------
 sub x[0],y[0]  ;最下桁が等しいかチェック
 jnz freq_calc  ;等しくなければ+-計算へ

 mov x[1],stfreq[1]  ;桁が上がっているので全ビットチェック
 mov y[1],freq[1]  ;
 sub x[1],y[1]  ;2桁目は?
 jnz freq_calc  ;等しくなければ+-計算へ

 mov x[2],stfreq[2]  ;同上
 mov y[2],freq[2]  ;
 sub x[2],y[2]  ;3桁目は?
 jnz freq_calc  ;等しくなければ+-計算へ

 mov x[3],stfreq[3]  ;同上
 mov y[3],freq[3]  ;
 sub x[3],y[3]  ;最上桁は?
 jz dainyuu   ;等しかった。全桁一致。制御不要。

;周波数ズレ計算のため変数へデータを再セットする。
freq_calc
 mov x[0],freq[0]  ;現在の周波数
 mov x[1],freq[1]  ;
 mov x[2],freq[2]  ;
 mov x[3],freq[3]  ;

 mov y[0],stfreq[0]  ;設定周波数
 mov y[1],stfreq[1]  ;
 mov y[2],stfreq[2]  ;
 mov y[3],stfreq[3]  ;

;*************************************
; 周波数ズレを計算、周波数を設定。
;*************************************
 call sub_xy   ;現在の周波数-設定周波数
 btfss x[3].7   ;+-の振り分け
     ;正なら代入、負なら逆数を整数に変換
 goto purasu   ;整数は、そのまま代入

;******周波数マイナスの時の処理*******
 mov y[0],x[0]  ;引き算の準備
 mov y[1],x[1]  ;-は逆ビット数なので整数にする。
 mov y[2],x[2]  ;
 mov y[3],x[3]  ;

 clr x[0]   ;マイナスを整数に変換
     ;notを使わず0より引く
 clr x[1]   ;
 clr x[2]   ;
 clr x[3]   ;
 call sub_xy   ;マイナスを整数へ変換のための引き算

;**************************************************************
;** バリキャップへの電圧印加時間、マイナスの時の動作時間作り **
;**************************************************************
; 応答時間(VFOのセットアップ時間)が不足ならハードのコンデンサを
; 減らすなどの対応が必要
;
;*****周波数マイナスの時は、電圧up********
syuuseim
 bcf vdown  ;ra1-off コンデンサ電圧放電やめインターロック
 cje x[1],#0,jpm0 ;cje== if fr==<#lite then goto

 bsf vup  ;ra0-on コンデンサへ電圧印加
 mov par,#10  ;10ms wait
 call waitnms  ;
 goto jpm02

jpm0
 cjb x[0],#32,jpm01 ;cjb== if fr<<#lite then goto
 bsf vup  ;ra0-on コンデンサへ電圧印加
 call wait1ms  ; 1ms wait
 goto jpm02

jpm01
 cjb x[0],#1,jpm02 ;20hz以内は、不感帯
 bsf vup  ;ra0-on コンデンサへ電圧印加
 mov parr,x[0] ;cへのチャージ時間に代入
 call wait_nss ;
jpm02
 bcf vup  ;ra0-off コンデンサへ電圧印加やめ
 goto dainyuu  ;変数に代入

;***** バリキャップへの電圧印加時間 **********
;***** 周波数プラスの時の動作時間作り ********
;***** 制御時間不足ならハード変更検討 ********
;
purasu    ;周波数プラスの処理 電圧下げる。
 bcf vup  ;ra0-offコンデンサへ電圧印加やめインターロック
 cje x[1],#0,jpp0 ;cjb== if fr<<#lite then goto

 bsf vdown  ;ra1-on コンデンサ電圧放電へ
 mov par,#10  ;wait 10ms
 call waitnms  ;
 goto jpp02
;
jpp0
 cjb x[0],#32,jpp01 ;cjb== if fr<<#lite then goto
 bsf vdown  ;ra0-on コンデンサへ電圧印加
 call wait1ms  ;wait 1ms
 goto jpp02
;
jpp01
 cjb x[0],#1,jpp02 ;20hz以内は、不感帯
 bsf vdown  ;ra1-on コンデンサ電圧放電へ
 mov parr,x[0] ;cへのチャージ時間に代入
 call wait_nss ;
jpp02
 bcf vdown  ;ra1-off コンデンサ電圧放電やめ
dainyuu
 btfss rit_sw  ;RIT On?
 goto riton_ret

;送信中もしくは受信中で RIT Off
 mov trxfreq[0],stfreq[0] ;送信周波数を基準にセット。
 mov trxfreq[1],stfreq[1] ;
 mov trxfreq[2],stfreq[2] ;
 mov trxfreq[3],stfreq[3] ;
 goto dai_ret

;受信中かつ RIT On
riton_ret
 mov ritfreq[0],stfreq[0] ;受信周波数を基準にセット。
 mov ritfreq[1],stfreq[1] ;
 mov ritfreq[2],stfreq[2] ;
 mov ritfreq[3],stfreq[3] ;

dai_ret
 mov intcon,#10010000b ;rb0割り込みを許可する
 return

;****************
;周波数を ++ する
;****************
inc_freq
 call freq_sub ;周波数偏移を計算する。
 call add_xy  ;x = 現在周波数+ステップ
 goto  setfq

;****************
;周波数を -- する
;****************
dec_freq
 call freq_sub ;周波数偏移を計算する。
 call sub_xy  ;x = 現在周波数-ステップ

setfq
 mov stfreq[0],x[0] ;結果の受け取り
 mov stfreq[1],x[1] ;
 mov stfreq[2],x[2] ;
 mov stfreq[3],x[3] ;
 return

;***************************
; 周波数偏移計算サブ
;***************************
freq_sub
 mov x[3],stfreq[3] ;もとの周波数
 mov x[2],stfreq[2] ;
 mov x[1],stfreq[1] ;
 mov x[0],stfreq[0] ;

 btfss step_sw  ;step_sw=off then nomal
 goto nor_step

; wide step
 mov y[3],#wstep3 ;Wideステップ
 mov y[2],#wstep2
 mov y[1],#wstep1
 mov y[0],#wstep0
 return

nor_step
 mov y[3],#step3 ;通常ステップ
 mov y[2],#step2
 mov y[1],#step1
 mov y[0],#step0
 return

;***************************
;long 型式の引き算プログラム
;***************************
sub_xy
 sub x[0],y[0]
 movlw 1
 btfss c
 subwf x[1],1
 btfss c
 subwf x[2],1
 btfss c
 subwf x[3],1
 sub x[1],y[1]
 movlw 1
 btfss c
 subwf x[2],1
 btfss c
 subwf x[3],1
 sub x[2],y[2]
 movlw 1
 btfss c
 subwf x[3],1
 sub x[3],y[3]
 return
;***************************
;long 型式の足し算プログラム
;***************************
add_xy
 add x[0],y[0]
 movlw 1
 btfsc c
 addwf x[1],1
 btfsc c
 addwf x[2],1
 btfsc c
 addwf x[3],1
 add x[1],y[1]
 movlw 1
 btfsc c
 addwf x[2],1
 btfsc c
 addwf x[3],1
 add x[2],y[2]
 movlw 1
 btfsc c
 addwf x[3],1
 add x[3],y[3]
 return

;**************
;1msec ウェイト
;**************
wait1ms
 mov os_wrk2,#10
wm_lxh2
 call waitus0
 djnz os_wrk2,wm_lxh2
 return
;****************
;100usec ウェイト
;****************
waitus0
 mov os_wrk1,#wait_10us
wu_lxh
 djnz os_wrk1,wu_lxh
 return
;***********************
;n msec ウェイト
;n は par にセットしコール
;***********************
waitnms
 call wait1ms
 djnz par,waitnms
 return
;****************
;2乗タイマ ウェイト
;****************
wait_nss
 cjb parr,#0,gotowait1 ;誤動作防止マイナス計算される。
 mov waitnss2,parr
wait_nss2
 mov waitnss1,parr
wait_nss1
 djnz waitnss1,wait_nss1
 djnz waitnss2,wait_nss2
gotowait1
 return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;*********************************************************
;周波数カウンタ部 freq[0]〜freq[3] の32ビットに計測値得る
;*********************************************************
f_count
 clr freq[0]  ;周波数カウントの前処理
 clr freq[1]  ;
 clr freq[2]  ;
 clr freq[3]  ;

 bsf count_in ;ゲートを開ける。
 clr tmr0  ;counter reset
 bcf intcon,2 ;reset T0IF

;各Clock における LPCNT2/LPCNT1 の数値(ゲートタイム時間)
;20MHzを基準とすると10MHzなら10/20=0.5でループ回数は半分になる。
;同様に4MHzなら4/20=0.2で五分の一で同一ゲートタイムになる。
;                         ↓              ↓
; Clock 20MHz (15*LPCNT2 133 + 5)*LPCNT1 125 = 250,000ステップ(7N4KYG)
; Clock 16MHz (15*LPCNT2 133 + 5)*LPCNT1 100 = 200,000 (JA1XFA)
; Clock 10MHz (15*LPCNT2  83 + 5)*LPCNT1 100 = 125,000 (JA1XFA)
; Clock  4MHz (15*LPCNT2  83 + 5)*LPCNT1  40 =  50,000 ( -"-  )

;50msループ 開始!
; mov lpcnt1,#125 ;loop counter1 回数 Clock 20MHz
 mov lpcnt1,#100 ;loop counter1 回数 Clock 10MHz
; mov lpcnt1,#40 ;loop counter1 回数 Clock  4MHz
meslp1
; mov lpcnt2,#133 ;loop counter2 回数 Clock 20MHz
 mov lpcnt2,#83 ;loop counter2 回数 Clock 10MHz/4MHz
meslp2
 btfss intcon,2 ;13 steps loop
 goto dumy1  ;
 bcf intcon,2 ;reset T0IF
 movlw 1  ;カウントアップ
 goto  next  ;

dumy1
 nop   ;ステップ数調整
 nop   ;ステップ数調整
 movlw 0  ;カウントアップ無し
next
 nop   ;ステップ数調整
 nop   ;ステップ数調整
 addwf freq[1],1 ;freq[1]+T0IF
 rlf freq[1],0 ;carry to d0
 andlw 1  ;mask
 addwf freq[2],1 ;BYTE2+Carry
 decfsz lpcnt2,1 ;check loop end
;内側ループ
 goto meslp2  ;loop
 decfsz lpcnt1,1 ;
;外側ループ
 goto meslp1  ;
;--------- Count Loop End -----------------------------------------
 bcf count_in ;ゲートを閉める。50msの計測時間終了
 nop   ;50msループから脱出 カウント終了
 movf tmr0,0  ;TMR0をfreq[0]に取り出し
 movwf freq[0]  ;061112()と[]間違え有った
 btfss intcon,2 ;オーバーフロー確認   
 goto tobu1  ;オーバーフローで無いなら
 bcf intcon,2 ;reset T0IF
 btfsc freq[0],7 ;繰上りミス防止
 goto tobu1
 movlw 1
 addwf freq[1],1 ;freq[1]+T0IF
 rlf freq[1],0 ;carry to d0
 andlw 1  ;mask
 addwf freq[2],1 ;freq[2]+Carry
;**************************************************************
;測定間隔は100ms。現計測は50ms、そしてプリスケーラで1/2したので
;データを4倍する。
;**************************************************************
tobu1
 call tobu2  ;まず2倍。プリスケーラの分
tobu2
 bcf status,0
 rlf freq[0],1 ;2倍にする。
 rlf freq[1],1
 rlf freq[2],1
 rlf freq[3],1
 bcf status,0
 return
;----------------------------------------------------------
end