;******************************************************************************
;Switch Pon  single point swicher  
;PIC16F873A 
;copyright (c) 2015 名古屋電鉄 http://www007.upp.so-net.ne.jp/nagoden/
;switch_pon01.asm  2015/04/20 Ver1.00 
;******************************************************************************
;【概要】
;PIC16F873AのUSART機能を使いLOCONETに接続し、押しボタンスイッチの操作により、Switchコマンドを
;Loconetに送信します。
;
;EEPROMには以下の情報を持ちます。
; 　1.Switch番号
;	押しボタンスイッチを押したときに出力するアドレス番号を保持します。
;	アドレスはSwitchのコマンドが１１ビットのデータとして扱っているため　上位４ビット，下位7ビットの２バイトとして
;	記録します。
;
;	EEPROMの設定を容易にするためにセットアップモードを実装しています。
;	PIC16F873A　の14番ピン（RC3）がLowの時にセットアップモードとなり、スロットルのスイッチ番号
;　 　を指定して　ｔ　を押すことで、コマンドにあるスイッチ番号（アドレス）をEEPROMにスイッチ番号を書き込むようにします。
;
;------------------------------------------------------------------------------
;●USARTモードは以下の指定をしています 
;------------------------------------------------------------------------------
;   Mode : Asyncronous transfer mode only
;   Speed: 16K bps(12h 20Mhz) 
;   Bit  : startx1 datax8 stopx1
;　	※Loconetはボーレート16600bpsと特殊です。ここでは設定値から16447bpsにしています。
;------------------------------------------------------------------------------
;●PIC16F873A　ピンアサイン 
;------------------------------------------------------------------------------
;	RB0～RB7		未使用
;	RA0～RA5  未使用
; 	RC0～RC 	未使用
; 	RC3 		セットアップ用ジャンパーピン　
; 	RC4			セットアップ状態表示用　ＬＥＤ
; 	RC5			入力ピン		
; 	RC6 		TX（送信）　Loconetへ送信
; 	RC7　		RX（受信）　Loconetより受信 
;-------------------------------------------------------------------------------
;●Loconetでのスイッチコマンド
;-------------------------------------------------------------------------------
;  4. Switch　の　ｔ　Or C を選択時のコマンド 
;        B0 aa bb cc
;		aa  0 A6 A5 A4  A3  A2 A1 A0
; 		bb  0  0 S1 S2  A10 A9 A8 A7 
; 			A7～A0 Switchのアドレス
; 			s1=0 Thrown  s1=1 Close
;			s2=0 output off   S2=1 output 0n   
;  			ｃｃ  check SUM;
;        注：DCS50KとDT300で出力が違い dt300では ONと0FFを続けて出力するが、DCS50KではONのみを出力
;           DCS50K   B0 00 30 7F → c      B0 00 10 5F → t
;
;---------------------------------------------------------------------------------
	list      p=16f873a
	#include <p16f873a.inc>

	__CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _WRT_OFF & _LVP_OFF & _CPD_OFF

;*****************************************************************************
; EEPROMへのデータ定義（デフォルト値）
;*****************************************************************************
	org	0x2100		;
;----- 初期データ ---------説明----------------------
	de	0x00		;スイッチ番号の上位4ビット ｘ ｘ ｘ  ｘ  A10 A9 A8 A7  
	de	0x00		;スイッチ番号の下位７ビット  ｘ  A6 A5 A4   A3  A2 A1 A0
;*****************************************************************************
;変数定義
;*****************************************************************************
;割り込みルーチンで使用
w_temp		EQU	0x20 
status_temp	EQU	0x21
;内部処理変数
ADDR		EQU	0x22		;EEPROM アドレス
EE_DAT		EQU	0x23		;EEPROM データ
netcmd		EQU	0x24		;USART受信用
cmd_chk		EQU	0x25		;未使用
flg_d0		EQU	0x26		;コマンド受信フラグ
cmd_cnt		EQU	0x27		;データ受信時のバイト数
cnt1		EQU	0x28		;タイマーカウント
cnt2		EQU	0x29		;タイマーカウント
cnt3		EQU	0x2A		;タイマーカウント
cnt7		EQU	0x2B		;送信チェック用のタイマーカウント
PC_STATUS	EQU	0x2C		;ポートＣ（シリアルポートチェック用）
tc_flg		equ	0x2D		;スイッチ　ｔ　ｃ　のフラグ	
;送信用変数
Send_data	equ	0x2E		;送信データ
work_data	equ	0x2F		;送信３バイト目の編集用
inp_chk		equ	0x30		;押し続けの連続処理防止フラグ
chk_sum		equ	0x31		;コマンド４バイト目のチェックサム用
;EEPROMからの値		
SWMSB		EQU	0x32		;スイッチ番号の上位4ビット(EEPROM値)
SWLSB		EQU	0x33		;スイッチ番号の下位７ビット(EEPROM値)
CMDMSB		EQU	0x34		;スイッチ番号の上位4ビット(コマンド値)
CMDLSB		EQU	0x35		;スイッチ番号の下位７ビット(コマンド値)

;コマンド受信　内部処理変数(間接アドレッシングで使用のためアドレス４Ａ~４Ｆまでは変更禁止)
cmd1		EQU	0x4A		;受信コマンド１バイト目
cmd2		EQU	0x4B		;受信コマンド２バイト目
cmd3		EQU	0x4C		;受信コマンド３バイト目
cmd4		EQU	0x4D		;受信コマンド４バイト目
cmd5		EQU	0x4E		;受信コマンド５バイト目
cmd6		EQU	0x4F		;受信コマンド６バイト目

;*****************************************************************************
;マクロ定義
;*****************************************************************************
;バンクの指定
BANK0	MACRO
	BCF	STATUS,RP0	;BANK 0に切替
	BCF	STATUS,RP1	;
	ENDM
BANK1	MACRO
	BSF	STATUS,RP0	;BANK 1に切替
	BCF	STATUS,RP1	;
	ENDM
BANK2	MACRO
	BCF	STATUS,RP0	;BANK 2に切替
	BSF	STATUS,RP1	;
	ENDM
BANK3	MACRO
	BSF	STATUS,RP0	;BANK 3に切替
	BSF	STATUS,RP1	;
	ENDM
;*****************************************************************************
;プログラムスタート
;*****************************************************************************
	ORG	0x000             
 	goto	init  				
	ORG	0x004			; 割り込みベクタ
	movwf	w_temp        	  	; ワークレジスタ退避
	movf	STATUS,w          	; ステータスレジスタをワークレジスタに
	movwf	status_temp       	; ワークレジスタ（ステータス）退避
	  				; ←ここに割り込みルーチン設定
	BANK0					; バンク０に切り替え	
	movf	status_temp,w     	;　退避ステータスのワークレジスタへ戻し
	movwf	STATUS            	; ワークレジスタからステータスへ戻し
	swapf	w_temp,f		;　ワークレジスタの戻し	
	swapf	w_temp,w          	; ワークレジスタの戻し
	retfie                    	; 割り込み終了
init
;*****************************************************************************
;ＰＩＣの入力ポートとUSARTの設定
;*****************************************************************************
	BANK1
	;ポートの設定
	movlw	0x06		;コレをしないとportＡのデジタル入力ができません
	movwf	ADCON1		;ADCON1に06Hを設定しポートＡをすべてデジタルに
	movlw	B'00000000'
	movwf	TRISA		; ポートAは全て出力端子　
	movlw	B'00000000'
	movwf	TRISB		; ポートBは全て出力端子
	movlw	0x80
	movwf	OPTION_REG	; ポートＢプルアップなし
	movlw	B'10101000'
	movwf	TRISC		; ポートCはRC7とRC5,RC3が入力端子　後は出力
	BANK0
	clrf	PORTA		; PORTクリア
	clrf	PORTB		; PORTクリア
	clrf	PORTC		; PORTクリア

	;USARTの設定 PIC12F873 20Mhｚ
	BANK1
	movlw	0x20			;↓でAsync mode　に設定 
	movwf	TXSTA			;TXSTAにセット
	movlw	d'18'			;18→ボーレート 16Kbps   16447.37=20Mhz/((18+1)*64)
	movwf	SPBRG			;SPBRGにボーレート設定		
	BANK0					;Bank 0に切り替え
	movlw	0x90			;↓でシリアルピン設定、継続受信許可　に設定 
	movwf	RCSTA			;RCSTAにセット

main_init
;*****************************************************************************
;ＥＥＰＲＯＭを読んで　スイッチの番号を取得
;ここで取得されるアドレスに従って制御するコマンドを出力する
;*****************************************************************************
	;スイッチ番号
	movlw	0x00
	movwf	ADDR		
	call	eeprom_r	
	movwf	SWMSB	
	movlw	0x01
	movwf	ADDR		
	call	eeprom_r	
	movwf	SWLSB
main
;*****************************************************************************
;メインルーチン
;*****************************************************************************
mode_chk
	;セットアップモードチェック　(セットアップモードの時　S_MODE=1)
	clrf	tc_flg
	btfsc	PORTC,3			;RC3が０　ショートピンでショートしていた場合
	goto	main_loop		;１の場合は通常モード
	goto	setup_loop		;０の場合はセットアップモード

setup_loop
	;セットアップモード時
	call	rcv_usart		;コマンド受信処理
	goto	setup_loop

main_loop
	;通常モード
	;ポートの入力待ち
	;チャタリングを防止するために100ms間を空けて２回	PORTC,5(RC5)を読み取る
	movlw	d'100'
	call	dly_Nm
	btfsc	PORTC,5
	goto	c_inpchk
	movlw	d'100'
	call	dly_Nm
	btfsc	PORTC,5
	goto	c_inpchk
	movf	inp_chk,f
	btfss	STATUS,Z	;ボタン押し続けはinp_chk１なので後続処理をしない
	goto	main_loop	

c_snd
	call	SEND	
	movlw	d'1'
	movwf	inp_chk		;押し続けの連続処理防止フラグ
flg_inv
	;送信処理後のtc_flg反転
	movf	tc_flg,f
	btfss	STATUS,Z
	goto	flg_inv2		;0（ｔ）の場合
	movlw	d'1'
	movwf	tc_flg
	goto	main_loop
flg_inv2
	movlw	d'0'
	movwf	tc_flg
	goto	main_loop

c_inpchk
	clrf	inp_chk
	goto	main_loop

SEND
;*************************************************************************
;　コマンド送信処理
;        B0　aa bb cc
;		aa  0 A6 A5 A4　 A3  A2 A1 A0
; 		bb  0  0 S1 S2  A10 A9 A8 A7 
;　			A7～A0　Switchのアドレス
;　			s1=0 Thrown 　s1=1 Close
;			s2=0　output on   S2=1 output 0ff   
;  			ｃｃ  check SUM
;*************************************************************************
; 　tc_flg=0　Thrown tc_flg=1 Close　
s_bitset
	movf	SWMSB,w
	movwf	work_data
	movf	tc_flg,f
	btfss	STATUS,Z
	goto	s_bitset2	;０の場合（t）の場合
	movlw	b'00110000'	;s1=1 Closeのビット
	iorwf	work_data,f
	goto	chksum		;０の場合（t）の場合
s_bitset2
	movlw	b'00010000'	;s1=1 Closeのビット
	iorwf	work_data,f
	goto	chksum		;０の場合（t）の場合

chksum
	clrf	chk_sum		;クリア
	movlw	0xB0		;スイッチコマンドの1バイト目　0xB0
 	xorwf	chk_sum,f     	;BOとchk_sumをxorしてチェック値変数（chk_sum）にセット
	movf	SWLSB,w		;スイッチコマンドの2バイト目　
 	xorwf	chk_sum,f     	;2バイト目とchk_sumをxorしてチェック値変数（chk_sum）にセット
	movf	work_data,w	;スイッチコマンドの3バイト目　
 	xorwf	chk_sum,f	;3バイト目とchk_sumをxorしてチェック値変数（chk_sum）にセット
	movlw	0xff
 	xorwf	chk_sum,f   	;ビット反転してチェック値変数（chk_sum）にセット→（4バイト目コマンド）  

;********************************************************************
;　送信前の信号チェック   
;	受信データの確認　1byte分のデータ（１０ビット）を　
;	RC7から60μs間隔で１０ビット分（１０回）読んで
;	０が存在しているとき（データが流れている時）は600ﾏｲｸﾛ秒待って
;	再度１０ビット分チェックする。
;	０が存在しないときは１０ビット分のチェックでループから抜ける。
;********************************************************************

PC_STA
	movlw	d'10'
	movwf	cnt7
PC_STA1	
	bsf	PORTC,4		;LED　ON
	movf 	PORTC,w		;ポートＣの値を読み込み
	movwf	PC_STATUS       ;portC→Workﾚｼﾞｽﾀ→PC_STATUS
	btfsc	PC_STATUS,7	; ０だったら600ﾏｲｸﾛ秒待って再度10回ループ
	goto 	PC_STA2		;　０でなければ　１０回ループを継続
	call    dly_600u	;600ﾏｲｸﾛ秒ウエイト
	goto	PC_STA		;ウエイト後最初からチェック
PC_STA2
	call	dly_60u		;60u秒待ち
	decfsz	cnt7,f		;cntを-1
	goto	PC_STA1		;ｃｎｔ7が０になるまでループ
 	bcf	PORTC,4		;１０回回ってＯＫならLED　OFF
;********************************************************************
CMD_TX
	;スイッチコマンド送信処理
	movlw	0xB0
	call	LOCO_TX		;1バイト目送信
	movf	SWLSB,w
	call	LOCO_TX		;2バイト目送信
 	movf	work_data,w	
	call	LOCO_TX		;3バイト目送信
	movf	chk_sum,w
	call	LOCO_TX		;4バイト目送信

;**********************************************************
;送信コマンド（受信データ）のチェック
;**********************************************************
;本来であれば送信したデータと受信したデータ（ループバック）してきたコマンドを確認して
;衝突していれば送信データと受信データが同じでなくデータは正しく送られていないので
;再送の処理をかけたいが、ここでは省略する

    return
;*****************************************************************************
;セットアップモードUSART コマンド受信処理
;*****************************************************************************
rcv_usart
	;----------------------------------------------------------
	;受信したコマンドが該当（D0,BC,B2,B0)だったら受信フラグを立てて残りバイト受信
	;----------------------------------------------------------
	movlw	0x4A		;set buffer top address
	movwf	FSR		;間接アドレスポインタ初期化
	clrf	cmd_cnt
	clrf	flg_d0
lprcv
	;受信ステータスチェック
	btfss	PIR1,RCIF	;受信完了フラグのチェック
	goto	re_recv
	;エラーチェック
	btfsc	RCSTA,FERR	;FRAME エラー
	goto	frame_err
	btfsc	RCSTA,OERR	;オーバーラン　エラー
	goto	ovrun_err
	;データ受信
	bsf	PORTC,4		;LED on
 	movf	RCREG,W		;受信データ取得しワークエリアに
	movwf	netcmd		;ワークにある受信したデータをnetcmdにセット
	;D0受信フラグ（flg_d0）が０以外の場合コマンドチェックせず以降のコマンド受信
	movf	flg_d0,f
	btfss	STATUS,Z	;０かどうか判定
	goto	other_cmd	;0<>なら　D0受信フラグ＝1なのでother_cmdへ
	goto	tsp_cmd		;0= なら　１バイト目のコマンドチェック
frame_err
	movf	RCREG,W		;ダミーで読み取りフレームエラー解除
        btfss	RCSTA,OERR
	goto	rcv_end
ovrun_err
	BCF	RCSTA,CREN	;crenのビットをリセットすることでｏｖｅｒRunエラー解除 		
	BSF	RCSTA,CREN
	goto	rcv_end	
re_recv
	movf	flg_d0,f
	btfss	STATUS,Z	;０かどうか判定
	goto	lprcv		;0<>なら　D0受信フラグ＝1なので受信待ち
	goto	rcv_end		;０ならそのまま受信ループ終了
;------------------------------------------------------------------------------
;1バイト目のコマンドチェック
;コマンドに応じて　flg_d0　に値をセット D0->0 B2->1 BC->2 B0->3
;------------------------------------------------------------------------------
tsp_cmd
ctrl_cmd3
	;-------------------------------
	;コマンドがＢ０（スイッチコマンドのtｃ選択）かチェック
	;-------------------------------
	movf	netcmd,w
	sublw	0xB0	
	btfss	STATUS,Z	;０かどうか判定
	goto	rcv_end		;0<>ならrcv_endへ飛ぶ
	;B0だったら受信フラグに４を立てる
	movlw	d'4'		;受信フラグ＝4にセット
	movwf	flg_d0		;受信フラグ（flg_d0）に４をセット
	movlw	d'4'
	movwf	cmd_cnt		;B0を受信時 4バイト受信
	goto	other_cmd	;
;-------------------------------------------------------------------------------
;２バイト目以降のコマンド処理
;-------------------------------------------------------------------------------
other_cmd
	;受信フラグが１なら指定バイト数になるまで順次受信
	movf	netcmd,w	;ワークにある受信したデータをnetcmdにセット
	movwf	INDF		;save in buffer
	incf	FSR,F		;FSR+1
	decf	cmd_cnt,f	;cmd_cnt-1
	btfss	STATUS,Z	;０かどうか判定
	goto	lprcv		;既定のコマンド数に達しない場合は受信処理に戻り次コマンド受信
	;受信コマンド判別
	movf	flg_d0,w
	sublw	D'4'		;1->D0 2->B2 3->BC 4->B0
	btfsc	STATUS,Z	;０かどうか判定
	goto	cmd_B0		;B0(スイッチ選択）コマンドの処理へ
	goto	rcv_end

;*****************************************************************************
;　コマンドＢ０の処理(Switch切替コマンド)
;*****************************************************************************
cmd_B0
	;----------------------------------------------
	;	B0　aa bb cc　（ｔ　Or　C　を選択時）
	;		aa  0 A6 A5 A4　 A3  A2 A1 A0
	; 		bb  0  0 S1 S2  A10 A9 A8 A7 
	;　			A7～A0　Switchのアドレス
	;　			s1=0 Thrown 　s1=1 Close
	;			s2=0　output on   S2=1 output 0ff   
	;  		ｃｃ  check SUM;
	;	tで　スイッチ番号を　EEPROMに書き込む
	;----------------------------------------------
;セットアップモードの処理
setup_sta
	btfsc	cmd3,5		;５ビット目でtかｃが押されたか判別 ０がｔ　１がｃ
	goto	cmd_b0end	;c(-)が押された
	btfss	cmd3,4		;ｔ(+)が押された
	goto	cmd_b0end	;S2=1 output 0ff の場合
	movf	cmd2,w
	andlw	b'01111111'	;アドレス以外のビット情報を取る			
	movwf	CMDLSB		;下位7ビットのSWITCHアドレスの情報
	movf	cmd3,w
	andlw	b'00001111'	;アドレス以外のビット情報を取る
	movwf	CMDMSB		;上位4ビットのSWITCHアドレスの情報

  
	;スイッチ番号をオフセットアドレスとしてEEPROMに保存
	movlw	0x00
	movwf	ADDR
	movf	CMDMSB,w	
	movwf	EE_DAT	
	call	eeprom_w
	movlw	0x01
	movwf	ADDR
	movf	CMDLSB,w	
	movwf	EE_DAT	
	call	eeprom_w	

setup_end
	;EEPROMに書き込んだらＬＥＤ点滅でループ
	bcf	PORTC,4		;LED off
	movlw	d'250'
	call	dly_Nm
	bsf	PORTC,4		;LED off
	movlw	d'250'
	call	dly_Nm
	goto	setup_end

cmd_b0end
		
;*****************************************************************************
;　コマンド処理終了によるフラグのクリア
;*****************************************************************************
rcv_end
	movlw	d'0'		;D0受信フラグ＝0にセット
	movwf	flg_d0		;D0受信フラグ（flg_d0）に0セット
	bcf	PORTC,4		;LED off
	RETURN
;*****************************************************************************
;ＥＥＰＲＯＭ読み書きルーチン
;*****************************************************************************
eeprom_r
	;EEPROM 読み出し
	BANK2
	movf 	ADDR,w		;呼び出すアドレス1セット
	movwf 	EEADR 		;EEADR(10Dh) アドレス呼び出し
        BANK3
	bcf	EECON1,EEPGD	;Point to Data memory
	bsf 	EECON1,RD	;EE Read(18Ch) readに設定 
	BANK2
	movf 	EEDATA,w 	;EEDATA(10Ch) 呼び出しデータをワークレジスタにセット:;
	BANK0
	return
eeprom_w
 	;EEPROMへの書き込み
	BANK2  
	movf	ADDR,w 		;EEPROMのアドレス指定  
	movwf	EEADR		;アドレスをEEADRへセット  
	movf 	EE_DAT,w	;内容をワークレジスタにセット  
	movwf 	EEDATA		;EEDATAにセット  
	BANK3
	bcf	EECON1,EEPGD	;Point to Data memory
	bsf 	EECON1,WREN 	;EEPROM書き込み許可  
	bcf	INTCON,GIE	;割り込み禁止
	movlw	0x55 
	movwf	EECON2 	  
	movlw	0xAA
	movwf	EECON2
	bsf	EECON1,WR 	;EEPROM書き込み開始
	bcf	EECON1,WREN 	;EEPROM書き込み禁止  
	bsf	INTCON,GIE	;割り込み許可
wr_wait    
 	btfsc 	EECON1,WR	;EEPROM書き込み完了確認
 	goto 	wr_wait  
	BANK0
        return


;*****************************************************************************
;時間遅延ルーチン クロック20MHz用
;*****************************************************************************
	;---------------------------------
	; 60マイクロ秒遅延
	; 1/16600bps　=　60.24μｓ　=60μｓ
	;---------------------------------
dly_60u	; 60uS
	movlw	d'60'
	movwf	cnt1
dly_60u1
	nop
	nop
	nop
	nop
	decfsz	cnt1,f
	goto	dly_60u1
	return
	;---------------------------------
	; 100マイクロ秒遅延
	;---------------------------------
dly_100u	; 100uS
	movlw	d'100'
	movwf	cnt2
dly_100u1
	nop
	nop
	nop
	nop
	decfsz	cnt2,f
	goto	dly_100u1
	return
	;---------------------------------
	; 600マイクロ秒遅延
	;---------------------------------
dly_600u	; 600uS
	movlw	d'6'
	movwf	cnt1
dly_600u1
	call 	dly_100u
	decfsz	cnt1,f
	goto	dly_600u1
	return
	;---------------------------------
	; nミリ秒遅延
	;call 前にワークにループ数をセット
	;例：movlw		d'10'
	;---------------------------------
dly_Nm	; NmS
	movwf	cnt1
dly_Nm1
	call	dly_1m
	decfsz	cnt1,f
	goto	dly_Nm1
	return
	;---------------------------------
	; 1ミリ秒遅延
	;---------------------------------
dly_1m	; 1ms
	movlw		d'4'
	movwf	cnt2
dly_1m1	
	movlw		d'250'
	movwf	cnt3
dly_1m2	; 250us
	nop
	nop
	nop
	nop
	decfsz	cnt3,f
	goto	dly_1m2
	decfsz	cnt2,f
	goto	dly_1m1
	return

;**********************************
; 送信ルーチン
; 送信バッファにデータをセット
; ワークレジスタに値をセットしてｃａｌｌ
;**********************************
LOCO_TX
	movwf	Send_data
	BANK1
LOOP_TX
	btfss	TXSTA,TRMT	;ステータスチェック
	goto	LOOP_TX
;
	BANK0
	movf	Send_data,W	;送信データをワークレジスタにセット
	movwf	TXREG		;送信・・・ビット単位の出力は、この先PICにおまかせ・・・・
	return


	END