;************************************************************************************
;　サーボモータ制御ポイントマシンプログラム　(PIC12F675)
;　振れ幅を半固定ＶＲで調整できるように対応
;　２つのスイッチでサーボモータを動かし、ポイントを駆動する
;　スイッチは単発信号でも動作するようにラッチして動作する
;　サーボホーンが左端、右端になった場合にＬＥＤ（GPO，GP1）切り替え出力
;　サーボモータ動作中はＬＥＤを双方点灯させる
;　EEPROMに左端・右端の位置を記憶することで電源再投入後も直前の位置がリセットされないように対応
;
;						PIC12F675
;						+-------+
;		+5V  		--	|		| -- 	GND
;		左回転		--	|		| --	LEDL出力			
;		調整ＶＲ		--	|		|　--	LEDR出力
;		右回転		--	|		|　--	サーボモータ出力
;						+-------+
;
;　調整方法：
;    1.左回転と右回転のピンをLOWにして電源投入し、ピンはＬＯＷのままで調整モード（ＬＥＤ両点灯）
;    2.ＶＲで振れ幅を調整　（時計まわり＞MIN　反時計まわり＞MAX）
;	 3.左回転と右回転のピンをHighにしてセットアップモード終了（ＬＥＤ両点滅）
;    4.電源再投入で通常起動
;
; Ver3.00　SERVO5v300.asm 　2012/04/27　　 
;　参考文献　やさしいPICマイコンプログラミング入門
;	     PIC12F629/675 Dataseet (DS41190G)　　　　　　　　
;　copyright(c) 2010,11,12　名古屋電鉄		http://www007.upp.so-net.ne.jp/nagoden/
;************************************************************************************

	list	p=12f675
	#include	<p12f675.inc>

	__CONFIG   _CP_OFF & _CPD_OFF & _BODEN_ON & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT 

	#define		LEDL	GPIO,0
	#define		LEDR	GPIO,1
	#define		SERVO	GPIO,2
						
	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 アドレス用
	RTCF_R		EQU 0x28	;ラッチフラグ右
	RTCF_L		EQU 0x29	;ラッチフラグ左
	ADDR1		EQU	0x2A	;EEPROM への書き込みアドレス（左右位置）
	ADDR2		EQU	0x2B	;EEPROM への書き込みアドレス(振れ幅)
	MCNT		EQU	0x2C	;振れ幅（約90°・1ms）を255で分割した時の値

		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
		bsf		TRISIO,3
		bsf		TRISIO,4
		bsf		TRISIO,5
		bcf		STATUS,RP0 ;bank=0
	;アナログチャンネルの設定
		bsf		STATUS,RP0 ;bank=1
		movlw	b'00011000'	;  8TSOC(001)  GP3/AN3(1000)
		movwf	ANSEL	;
		bcf		STATUS,RP0 ;bank=0
	;出力ポート初期化（０にする）
		bcf		SERVO
		bcf		LEDL
		bcf		LEDR
	;EEPROMのアドレスセット
		movlw	0x00
		movwf	ADDR1	
		movlw	0x01
		movwf	ADDR2	
;---------------------------------------------------
;回転幅の設定（電源投入時にGP3とGP5がLOWだったらセットアップモード）
;セットアップモード時は２つのＬＥＤを点灯
;GP4/AN3の電圧で１から２５５に設定しサーボの位置を確定させる
;モード中にGP3とGP5がHighになったらEEPROMに値書き込み、セットアップモード終了
;---------------------------------------------------
	;入力ポートのチェック
		btfsc		GPIO,3		;GP3が０かチェック
		goto		FLG_CLR		;1だったら通常モード
		btfsc		GPIO,5		;GP5が０かチェック
		goto		FLG_CLR 	;1だったら通常モード
	;セットアップモード開始
	;ＬＥＤ２つ点灯
		bsf		LEDL
		bsf		LEDR
AD_LOOP
	;入力ポートのチェック
	;セットアップモード中にGP3とGP5がHIGHになったら終了
		btfss		GPIO,3		;GP3が１かチェック
		goto		NEXT_LOOP	;0だったらモード継続
		btfss		GPIO,5		;GP5が１かチェック
		goto		NEXT_LOOP 	;0だったらモード継続
	;両方Highだったらモード終了しEEPROMに書き込み	
		movf		ADRESH,w
		movwf		EE_DAT		;EE_DAT に読み取った値をセット
        movf		ADDR2,w  	;書き込みアドレス１をセット
		movwf		EE_ADRS
		call		EEPROM_W	;EEPROMへ書き込み

SETUP_END
	;LED点滅ループ
		movlw	d'200'
		call	DLY_N
		movlw	d'200'
		call	DLY_N
		bsf		LEDL
		bsf		LEDR
		movlw	d'200'
		call	DLY_N
		bcf		LEDL
		bcf		LEDR
        goto SETUP_END 

NEXT_LOOP
	;ＡＤコンバータの設定
		movlw	b'00001101'	;  ADFM 0:Leftjustified VCSG 0:VDD  AN3 11xx
							;  GO/DONE =1  ADON = 1
		movwf	ADCON0	;
	;WAIT　1ms待つ
		movlw	d'1'
		call	DLY_N
	;変換開始
        bsf    ADCON0,GO
AD_WAIT    
		btfsc	ADCON0,NOT_DONE		;AD変換完了確認
 		goto	AD_WAIT
	;変換後の値　ADRESH の内容（上位８ビット）を読む
		movf	ADRESH,w
		movwf	CNT3
	;＋１する（０のとき－１でＦＦになるので）
		incf	CNT3,f		;+１する
SERVO_START
	;サーボを起動
	;パルス発生開始 1ms
		bsf		SERVO
		movlw	d'1'
		call	DLY_N
S_LOOP
	;このループでホーン位置決定のための時間を調整(1mS＋ｎ）
		nop
		decfsz	CNT3,f
		goto	S_LOOP
		bcf		SERVO ;パルス終わり
	;残りのパルスサイクル時間生成（20ms-2msの時間）
		movlw	d'18'
		call	DLY_N
		goto	AD_LOOP

	;セットアップモードはここまで

;---------------------------------------------------
;ここから通常モード
;---------------------------------------------------
FLG_CLR
	;フラグクリア
		movlw	d'0'
		movwf	RTCF_R		;ラッチフラグ右
		movlw	d'0'
		movwf	RTCF_L		;ラッチフラグ左


	;EEPROM 読み出し（方向）
		bsf 		STATUS,RP0 	;bank 1
		movf 		ADDR1,w		;呼び出すアドレス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

	;EEPROM 読み出し（振れ幅）
		bsf 		STATUS,RP0 	;bank 1
		movf 		ADDR2,w		;呼び出すアドレス1セット
		movwf 		EEADR 		;EEADR(9Bh) アドレス呼び出し
		bsf 		EECON1,RD 	;EE Read(9Ch) readに設定 
		movf 		EEDATA,w 	;EEDATA(9Dh) 呼び出しデータをワークレジスタにセット
		movwf		MCNT     	;読み取ったワークレジスタの内容をＭＣＮＴにセット
		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
		movf		MCNT,w
		movwf		CNT4
		decf		CNT4,f		;元の値-１処理
		bsf			LEDR

main_loop
	;ウオッチドッグタイマクリア
		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
	;ラッチフラグの確認してフラグが立っていればスイッチチェックなしで回転処理へ
        movf		RTCF_R,f
		btfss		STATUS,Z	;RTCF_Rが0かチェック  0の時zフラグ１なので１かどうかチェック
		goto 		ROT_RIGHT	;RTCF_Rが1のとき
        movf		RTCF_L,f	;
  		btfss		STATUS,Z	;RTCF_Lが0かチェック  0の時zフラグ１なので１かどうかチェック
		goto 		ROT_LEFT	;RTCF_Lが1のとき
	;スイッチがおされたかのチェック
		btfsc		GPIO,3		;GP3が０かチェック
		goto		CHK_NXT 	;1だったら（押されてなければ）GP5チェック
		goto		LATCH_RIGHT
CHK_NXT
		btfsc		GPIO,5		;GP5が０かチェック
		goto		main_loop 	;1だったら（押されてなければ）戻る
		goto		LATCH_LEFT

LATCH_RIGHT
	;方向転換後に再度フラグを立てない処理
        movf		DIR,f
		btfss		STATUS,Z	;dirが１かチェック
        goto		ROT_RIGHT
	;ラッチフラグ右=1
		movlw		d'1'
		movwf		RTCF_R
		goto		ROT_RIGHT

LATCH_LEFT
	;方向転換後に再度フラグを立てない処理
        movf		DIR,f
		btfsc		STATUS,Z	;dirが０かチェック
        goto		ROT_LEFT
	;ラッチフラグ左=1
		movlw		d'1'
		movwf		RTCF_L
		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
	;ラッチフラグ左クリア
		movlw		d'0'
		movwf		RTCF_L
		goto		main_loop

ROT_RIGHT
	;右回転処理
		incf		CNT4,f		;+１する
		movf		CNT4,w		;ワークレジスタにセット
		movwf		CNT5		;ワークレジスタの値をcnt5に保管
		movf		MCNT,w		;上限値をワークレジスタにセット
		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
	;ラッチフラグ右クリア
		movlw		d'0'
		movwf		RTCF_R
		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
 	;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書き込み許可  
		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  
 		bcf 	STATUS,RP0 	;Bank 0  

        return

		END