;************************************************************************************
;サーボモータ制御ポイントマシンプログラム　（PIC12F629)
;２つのスイッチでサーボモータを動かし、ポイントを駆動する
;サーボホーンの振れ角度はMCNTの値を変更することで調整可能
;サーボホーンが左端、右端になった場合にＬＥＤ（GPO，GP1）切り替え出力
;切り替え時に時間がかかる場合があるので、切り替え中はＬＥＤを双方点灯させる
;EEPROMに左端・右端の位置を記憶することで電源再投入後も直前の位置がリセットされないように対応
;EEPROMを使用しない場合はソース中のコメント参照
;
; Ver1.01 servo1_00.asm 初版
; Ver1.02               2010/06/12 サブルーチンのリターンを　retlw　0　→　return に修正　
; Ver1.03 servo1_03.asm 2011/12/23 電源投入時の暴走対策　WDT追加　	ブラウンアウト　OFF→ON
;参考文献　やさしいPICマイコンプログラミング入門
;	     PIC12F629 Dataseet (DS41190F)　　　　　　　　
;copyright(c) 2010,11　名古屋電鉄		http://www007.upp.so-net.ne.jp/nagoden/
;************************************************************************************

	list	p=12f629
	#include	<p12f629.inc>

	__CONFIG   _CP_OFF & _CPD_OFF & _BODEN_ON & _MCLRE_OFF & _WDT_ON & _PWRTE_ON & _INTRC_OSC_NOCLKOUT 

	#define		LEDL	GPIO,0
	#define		LEDR	GPIO,1
	#define		SERVO	GPIO,2
	#define		MCNT	d'180'	;振れ幅（約90°・1ms）を255で分割した時の位置　1～255　
								;この値でホーンのフレ幅を決定します。
	#define		ADDR1	00h		;EEPROM への書き込みアドレス
	#define		ADDR2	01h		;EEPROM への書き込みアドレス(未使用）

	CNT1		EQU	0x20	;タイマー用
	CNT2		EQU	0x21	;タイマー用
	CNT3		EQU	0x22	;ホーンの位置制御用
	CNT4		EQU	0x23	;ホーンの現在位置
	CNT5		EQU 0x24	;最大値計算用
	DIR			EQU 0x25	;ホーン位置フラグ
	EE_DAT		EQU 0x26	;EEPROM 書き込み用
	EE_ADRS		EQU 0x27	;EEPROM アドレス用


		ORG		0x000
		goto	main
		ORG		0x004
		retfie
main
		;内部クロック補正
		bsf		STATUS,RP0 ; bank=1
		call		0x3ff
		movwf		OSCCAL
		bcf		STATUS,RP0 ; bank=0
		;割り込みを禁止
		clrf		INTCON
		;コンパレータ使用禁止
		movlw		0x07
		movwf		CMCON
		;GPIO 0,1,2,3を出力、4,5を入力
		bsf		STATUS,RP0 ;bank=1
		bcf		TRISIO,0
		bcf		TRISIO,1
		bcf		TRISIO,2
		bcf		TRISIO,3
		bsf		TRISIO,4
		bsf		TRISIO,5
		bcf		STATUS,RP0 ;bank=0
		;タイマークロックを内部、T1をオフ
		bcf		T1CON,T1OSCEN
		bcf		T1CON,TMR1CS
		bcf		T1CON,TMR1ON
		; WDT にプリスケーラを割当 (v1.03)
		clrf	TMR0		; TMR0 をクリア
		bsf		STATUS, RP0	; バンク1に切り替え
		clrwdt				; WDT とプリスケーラのクリア
		movlw	b'00001111'	; 最終的に設定するプリスケール値 (1:128)を設定する
		movwf	OPTION_REG	;
		bcf		STATUS, RP0	; バンク0に切り替え

		;出力ポート初期化（０にする）
		bcf		SERVO
		bcf		LEDL
		bcf		LEDR
		;EEPEOMを使用しない場合は下記の４行の；をはずす
		;movlw		d'1'
		;movwf		CNT4
		;bsf		LEDL
		;goto		main_loop

		;EEPROM 読み出し
		bsf 		STATUS,RP0 	;bank 1
		movlw 		ADDR1 		;呼び出すアドレス1セット
		movwf 		EEADR 		;EEADR(9Bh) アドレス呼び出し
		bsf 		EECON1,RD 	;EE Read(9Ch) readに設定 
		movf 		EEDATA,W 	;EEDATA(9Dh) 呼び出しデータをワークレジスタにセット
		movwf		DIR     	;読み取ったワークレジスタの内容をDIRにセット
		bcf 		STATUS,RP0 	;bank 0
     
SET_DEFALT
		;ホーン初期値 ＥＥＰＲＯＭデータ（ＤＩＲ方向）が０のときは１　１の時は上限値にセット
        movf		DIR,f
		btfss		STATUS,Z	;dirが０かチェック
        goto 		SET_LMAX
		;DIRが０なら　ｃｎｔ4を左位置へセット
		movlw		d'1'
		movwf		CNT4
		bsf			LEDL
		goto		main_loop
SET_LMAX
		;dirが０以外は　ＤＩＲを１にして　CNT4を右位置へセット
		movlw		d'1'
		movwf		DIR
		movlw		MCNT
		movwf		CNT4
		decf		CNT4,f		;元の値-１処理
		bsf			LEDR

main_loop
		;ウオッチドッグタイマクリア  (v1.03)
		clrwdt				; WDT　クリア

		;位置データをカウンタにセット
		movf		CNT4,w
		movwf		CNT3
		;パルス発生開始 1ms
		bsf			SERVO
		movlw		d'1'
		call		DLY_N
		movf		CNT4,w
		movwf		CNT3
P_LOOP
		;このループでホーン位置決定のための時間を調整
		nop
		decfsz		CNT3,f
		goto		P_LOOP

		bcf			SERVO ;パルス終わり

		;残りのパルスサイクル時間生成（20ms-2msの時間）
		movlw		d'18'
		call		DLY_N

BT_CHK
		;スイッチ押下チェック
		btfsc		GPIO,4		;GP4が０かチェック
		goto		chk_nxt 	;1だったら（押されてなければ）GP5チェック
		goto		ROT_RIGHT
chk_nxt
		btfsc		GPIO,5		;GP5が０かチェック
		goto		main_loop 	;1だったら（押されてなければ）戻る
		goto		ROT_LEFT

ROT_LEFT ;左回転処理
		decfsz		CNT4,f		;-1して０か判断して
		goto		led_on		;０以外のとき（切り替え時にＬＥＤを双方点灯するとき）
		;goto		main_loop	;０以外のとき（切り替え時にＬＥＤを双方点灯させないとき）
		incf		CNT4,f		;０の時は+1する
		movf		DIR,f		;DIRが０かチェック
		btfss		STATUS,Z	;０かチェック（zフラグの判断）
        goto		SET_LED		;dirが１ならLED切り替え
		goto		main_loop
led_on
		;モータ動作時は両方のＬＥＤを点灯させる
		bsf			LEDL
		bsf			LEDR
		goto		main_loop

SET_LED
		;最小値（０）になった場合、ＤＩＲを０にセットして方向の記憶のためにEEPROMへ書き込みとLEDの切り替えを行う
		movlw		d'0'
		movwf		DIR			;dirに０をセット
		movf		DIR,w
		movwf		EE_DAT		;EE_DAT にDIRをセット
        movf		ADDR1,w  	;書き込みアドレス１をセット
		movwf		EE_ADRS
		call		EEPROM_W	;EEPROMへ書き込み
		bsf			LEDL
		bcf			LEDR
		goto		main_loop

ROT_RIGHT ;右回転処理
		incf		CNT4,f		;+１する
		movf		CNT4,w		;ワークレジスタにセット
		movwf		CNT5		;ワークレジスタの値をcnt5に保管
		movlw		MCNT		;上限値をワークレジスタにセット
		subwf		CNT5,w		;CNT5とワークレジスタを引き算して
		btfss		STATUS,Z	;０かどうか判断（０の場合は指定した上限値）
        goto		led_on		;切り替え時にＬＥＤを双方点灯するとき
		;goto		main_loop　　　;切り替え時にＬＥＤを双方点灯させないとき
		decf		CNT4,f		;元の値-１処理
		;Max値を超えた場合、ＤＩＲを１にセットして方向の記憶のためにEEPROMへ書き込みとLEDの切り替えを行う
		movf		DIR,f		;ワークレジスタにセット
		btfss		STATUS,Z	;dirが０かチェック
        goto		main_loop	;dirが１ならループに戻る 
		movlw		d'1'
		movwf		DIR			;dirに１をセット
		movf		DIR,w
		movwf		EE_DAT		;EE_DAT にDIRをセット	
        movf		ADDR1,w  	;書き込みアドレス１をセット
		movwf		EE_ADRS
		call		EEPROM_W	;EEPROMへ書き込み
		bcf			LEDL
		bsf			LEDR
		goto		main_loop


DLY_N	;タイマールーチン
        ;ｃａｌｌ前にワークレジスタに値をセットしておくこと
		;NmS
		movwf		CNT1
DLP1	; 			1mS
		movlw		d'200'
		movwf		CNT2
DLP2
		nop
		nop
		decfsz		CNT2,f
		goto		DLP2
		decfsz		CNT1,f
		goto		DLP1

		return

EEPROM_W
		;EEPEOMを使用しない場合はすぐ下の行の；を取る
		;return
 		;EEPROMへの書き込みルーチン
		bsf STATUS,RP0 	;Bank 1  
		movf EE_ADRS,w 	;EEPROMのアドレス指定  
		movwf EEADR 	;アドレスをEEADRへセット  
		movf EE_DAT,W	;内容をワークレジスタにセット  
		movwf EEDATA 	;EEDATAにセット  
		bsf EECON1,WREN ;EEPROM書き込み許可  
 		movlw 55H 		;  
		movwf EECON2 	;  
 		movlw 0AAH  
 		movwf EECON2  
 		bsf EECON1,WR 	;EEPROM書き込み開始  
wr_wait    
 		btfsc EECON1,WR ;EEPROM書き込み完了確認
 		goto wr_wait  
 		bcf STATUS,RP0 	;Bank 0  

        return

		END