;***** RemConACSW.asm *****************************************************
;
;	赤外線リモコンデータ記憶型ＡＣスイッチ
;
;	origin:  http://orange.zero.jp/electronics/irrem/iracsw1/irac_mp.asm
;
;     ( coding:  Ver. 0.00   2005/02/08  by M.Yamamoto )
;	coding:  Ver. 1.00   2005/03/06  初期バージョン, PIC12F629 に変更
;	update:  Ver. 1.01   2015/12/06  公開に伴ってコメントの強化
;	update:  Ver. 1.02   2020/06/11  リモコン未操作時のLED NG誤表示を抑止
;	bugfix:  Ver. x.xx
;
;******************************************************* PicAsm 0184 ******

;	Device: PIC12F629	Clock: 4MHz (セラロック)

#ifdef comment

 <機能と使用法>

・本機は既に手持ちのテレビやビデオなどの赤外線リモコン送信機を使って、他の機器の
  ＡＣ１００Ｖ電源をＯＮ／ＯＦＦするための装置である。

・リモコンのボタンを押したときに送られるデータを、記憶モードで記憶しておくことで、
  次のボタン操作から一押しごとに、ＯＮ／ＯＦＦを繰り返すサイクリック動作をする。

・コントローラ（マイコン）には、ＥＥＰＲＯＭ内蔵の１２Ｆ６２９を使用しているため、
  記憶したデータは電源を切っても消えることが無く、停電しても大丈夫である。
  また、記憶は瞬時で、何度でも書き替えができ、他のボタンへの変更も簡単に行える。

・本機に電源が入った状態では、リモコンスイッチとして動作する通常モードあり、初期
  状態では記憶しているデータが異なる（無い）ため、動作するリモコンはない。
  このため、まず記憶用スイッチを押して記憶モードにする。

・スイッチが押されると、発光ダイオードが数回点滅して記憶モードに入ったことを知ら
  せた後、発光ダイオードを消灯した状態でリモコンからの赤外線データを待つ。
  制限時間などは無く、いつまでも待つので、１ｍ程度離れた所から記憶したいリモコン
  の任意のキーを押す。

・赤外線データが受光されるとほとんど瞬間に記憶が終了し、再び発光ダイオードを数回
  点滅して記憶が終了したことを知らせた後、リモコンスイッチとして動作する通常モー
  ドに戻り、発光ダイオードは点灯状態になる。

・以降は登録したキーでリモコンスイッチとして動作する。

・キーの変更も同じ要領で記憶用スイッチを押して行い、この時一旦電源を切ったりする
  必要はなく、何度でも変更可能である。

#endif

;==========================================================================
;		環境設定
;==========================================================================

; PIC12F629 では、固体ごとに異なっていて工場出荷時に書き込まれている、以下の
; 2つのキャリブレーション情報を考慮する必要がある
; 固体にプログラミングする前に、あらかじめ読み出しておく必要がある

; BANDGAP 値:  CONFIGURATION WORD (ADDRESS: 2007h) の bit13,12 に記録
;              ブラウンアウトリセット、パワーオンリセット等で使う電圧の敷居値
;              を校正する値		

BandGapVal	equ	h'01FF'		;固体ごとに異なる (上位 2 bit)

; OSCCAL 値:   プログラムエリアの最終番地 3FFh に記録
;              内部発振回路を使うときの発振周波数を校正する値			

OscCalVal	equ	h'34B0'		;固体ごとに異なる (下位 8 bit)

	list		p=pic12f629
	include		p12f629.inc
	errorlevel	-302		;Suppress bank warning

	__config	BandGapVal & _CPD_OFF & _CP_OFF & _BODEN_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC

;==========================================================================
;		ラベル定義
;==========================================================================

#define		SigIn	GPIO,GP0	;赤外信号入力ポート
#define		AcCont	GPIO,GP1	;ＡＣコントロール出力
#define		LedOut	GPIO,GP2	;発光ダイオード
#define		MemSw	GPIO,GP3	;記憶開始スイッチ

		cblock	h'20'
epradr					;EEPROM のアドレス指定
wkdata					;ワークデータ
wkdata2					;ワークデータ2
lpcnt					;ループカウンタ
count					;カウンタ
okflg					;チェックルーチンからの戻り値
					; 入力した赤外信号データ (1: 有効, 0: 無効)
					; 受信データと記憶データの比較 (1: 一致, 0: 不一致)
reg1					;汎用レジスタ
reg2					;汎用レジスタ
reg3					;汎用レジスタ
reg4					;汎用レジスタ
regtop					;受信信号の格納先頭アドレス
		endc

datset		equ	d'32'		;記憶、比較するパルス列の数

;==========================================================================
;		プログラム開始
;==========================================================================

		org	0		;Reset Vector
		goto	start
		org	4		;Interrupt Vector
		goto	start

;==========================================================================
;		初期化処理
;==========================================================================

start
		bsf	STATUS,RP0      ;Bank 1
		bcf	PCON,NOT_POR	;パワー ON でリセットする
		bsf	PCON,NOT_BOD	;ブラウンアウトリセットしない
;		call	3ffh		;内部発振周波数校正値の読み込み（外部発振では不要）
;		movwf	OSCCAL		;OSCCAL に校正値を保存（外部発振では不要）

		bcf	STATUS,RP0	;Bank 0
		clrf	GPIO		;
		movlw	b'00000111'	;GP2-0 を Digital I/O
		movwf	CMCON		;CMCON の設定
		bsf	STATUS,RP0	;Bank 1
		movlw	b'00001001'	;GP3,0: 入力, GP2,1: 出力モード
		movwf	TRISIO 		;TRISIO の設定
		movlw	b'00000000'	;bit7   GPPU:0 プルアップ抵抗を使用
					;bit5   TOCS:0 内部クロックを使用
					;bit3   PSA:0  プリスケーラをTMR0タイマーに使用
					;bit2-0 PS:000 プリスケーラのスケール値 1:1
		movwf	OPTION_REG	;OPTION_REG の設定
		bcf	STATUS,RP0	;Bank 0

		bcf	AcCont		;AC コントロール出力 = OFF
		bsf	LedOut		;発光ダイオード点灯
		movlw	d'250'
		call	wait_1m		;1m * 250 = 約 250m 秒のウエイト
		movlw	d'250'
		call	wait_1m		;1m * 250 = 約 250m 秒のウエイト

		clrf	okflg		;チェックルーチンからの戻り値をクリア

;==========================================================================
;		メイン・ルーチン
;==========================================================================

main
		btfss	MemSw		;記憶開始スイッチが押された か?
		goto	m03		;押されたなら記憶モードに入る

		btfsc	SigIn		;赤外信号が下がるのを待つ
		goto	main
		call	ir_wait		;ウエイト
		btfsc	SigIn		;赤外信号が下がったことを確認
		goto	main

		; 通常モード

		call	dti01		;１サイクルの幅を datset の指定数入力する
		call	data_check	;入力した値が有効なデータであるかチェック
		btfss	okflg,0		;有効なデータ か?
	;;	goto	m01		;No					;;（Ver. 1.02 変更）
		goto	main		;No					;;

		call	comp_check	;受信データと記憶データが一致しているかチェック
		btfsc	okflg,0		;一致している か?
		goto	m02		;Yes

m01		call	led_flic_ng	;NG表示: LedOut の発光ダイオードをゆっくり 3回 点滅
		goto	main

m02		movlw	1 << GP1
		xorwf	GPIO,F		;ＡＣコントロール出力を反転
		call	led_flic_ok	;OK表示: LedOut の発光ダイオードを早く 10回 点滅
		goto	main

		; 記憶モード

m03		movlw	d'11'
		call	wait_1m		;1m * 11 = 約 11m 秒のウエイト
		btfsc	MemSw		;記憶開始スイッチが押された か?
		goto	main		;確かでないなら戻る

		movlw	d'11'
		call	wait_1m		;1m * 11 = 約 11m 秒のウエイト
		btfsc	MemSw		;記憶開始スイッチが押された か?
		goto	main

		call	led_flic	;LedOut の発光ダイオードを 10回 点滅
		bcf	LedOut		;発光ダイオード消灯

m04		call	data_in		;１サイクルの幅を datset の指定数入力する
		call	data_check	;入力した値が有効なデータであるかチェック
		btfss	okflg,0		;有効なデータ か?
		goto	m04		;No

		call	data_mem	;入力したデータを EEPROM へ記憶する

		call	led_flic	;LedOut の発光ダイオードを 10回 点滅
		bsf	LedOut		;発光ダイオード点灯
		goto	main

;==========================================================================
;		データ列を datset の指定数入力する
;==========================================================================

data_in
		btfsc	SigIn		;赤外信号が下がるのを待つ
		goto	data_in
		call	ir_wait		;ウエイト
		btfsc	SigIn		;赤外信号が下がったことを確認する
		goto	data_in
		nop
		nop

dti01		movlw	regtop		;受信信号の格納先頭アドレスを
		movwf	FSR		;FSR に設定
		movlw	datset		;繰り返し回数を
		movwf	lpcnt		;ループカウンタに設定

dti02		call	sig_count	;信号が立ち上がって次に下がるまでをカウント
		movf	count,W		;カウント値を
		movwf	INDF		;アドレス位置へ書き込む
		nop
		incf	FSR,F		;ポインタ + 1

		decfsz	lpcnt,F		;ループカウンタ = 終了か?
		goto	dti02		;No

		return

;==========================================================================
;		信号が立ち上がって次に下がるまでをカウント
;	( count: 戻り値  (単位は約 38μＳ)  count = 00: オーバーフロー )
;==========================================================================

; MACLORD VEQ0595  「電源」ボタンの例

; 0000:A9 29 53 28 29 29 28 29 28 29 29 53 53 29 53 53 ･)S())()())SS)SS
; 0010:53 53 54 53 53 53 29 00 00 00 CF A9 28 53 29 28 SSTSSS)･････(S)(

sig_count
		clrf	count		;カウンタの初期設定

sc01		call	ir_wait		;ウエイト
		incf	count,F		;カウンタ + 1
		btfsc	STATUS,Z	;オーバーフロー か?
		goto	sc02		;Yes

		btfss	SigIn		;信号が下がっている か?
		goto	sc01		;Yes
		call	ir_wait		;ウエイト
		btfss	SigIn		;信号が上がったことを確認
		goto	sc01

sc02		call	ir_wait		;ウエイト
		incf	count,F		;カウンタ + 1
		btfsc	STATUS,Z	;オーバーフロー か?
		goto	sc03		;Yes

		btfsc	SigIn		;信号が上がっている か?
		goto	sc02		;Yes
		call	ir_wait		;ウエイト
		btfsc	SigIn		;信号が下がったことを確認
		goto	sc02

sc03		return

;==========================================================================
;		入力した値が有効なデータであるかチェック
;		( 戻り値 okflg=1: 有効、okflg=0: 無効 )
;==========================================================================

data_check
		bsf	okflg,0		;戻り値に有効をセット

		movlw	regtop		;受信信号の格納先頭アドレスを
		movwf	FSR		;FSR に設定
		movlw	2		;繰り返し回数を
		movwf	lpcnt		;ループカウンタに設定

dck01		incf	FSR,F		;2,3 個目のデータ
		movf	INDF,W		;データを読み出す
		sublw	0
		btfss	STATUS,Z	;読み出しデータ = 0 か?
		goto	dck02		;No

		decfsz	lpcnt,F		;ループカウンタ = 終了か?
		goto	dck01		;No

		bcf	okflg,0		;戻り値に無効をセット

dck02		return

;==========================================================================
;		受信データと記憶データが一致しているかチェック
;		( 戻り値 okflg=1: 一致、okflg=0: 不一致 )
;==========================================================================

comp_check
		movlw	regtop		;受信信号の格納先頭アドレスを
		movwf	FSR		;FSR に設定
		clrf	epradr		;記憶しているデータ (EEPROM) の先頭アドレスを設定
		movlw	datset		;繰り返し回数を
		movwf	lpcnt		;ループカウンタに設定

cpck01		call	epr_read	;EEPROM の指定アドレスから１バイト読み出し
		movf	INDF,W		;データを読み出し
		movwf	wkdata2		;wkdata2 に
		movlw	2
		subwf	wkdata2,F	;wkdata2 - 2
		movlw	5		;繰り返し回数を
		movwf	reg4		;ループカウンタに設定

cpck02		movf	wkdata2,W
		subwf	wkdata,W	;比較
		btfsc	STATUS,Z	;一致した か?
		goto	cpck03		;Yes

		incf	wkdata2,F	;wkdata2 + 1
		decfsz	reg4,F
		goto	cpck02

		bcf	okflg,0		;データ不一致をセット
		goto	cpck04

cpck03		incf	FSR,F		;レジスタのアドレスポインタ＋１
		incf	epradr,F	;EEPROM のアドレスポインタ＋１
		decfsz	lpcnt,F
		goto	cpck01

		bsf	okflg,0		;データ一致をセット

cpck04		return

;==========================================================================
;		入力したデータ値を EEPROM へ記憶する
;==========================================================================

data_mem
		movlw	regtop		;受信信号の格納先頭アドレスを
		movwf	FSR		;FSR に設定
		clrf	epradr		;EEPROM の先頭アドレスを設定
		movlw	datset		;繰り返し回数を
		movwf	lpcnt		;ループカウンタに設定

dtm01		movf	INDF,W		;データを読み出し
		movwf	wkdata		;ワークに設定

		call	epr_write	;EEPROM の指定アドレスへの１バイト書き込み

		incf	FSR,F		;ポインタ + 1
		incf	epradr,F	;EEPROM のアドレス + 1
		decfsz	lpcnt,F		;ループカウンタ = 終了か?
		goto	dtm01		;No

		return

;==========================================================================
;		EEPROM の指定アドレスから１バイト読み出し
;	( epradr: 読み出しアドレス, wkdata: 読み出しデータ )
;==========================================================================

epr_read
		movf	epradr,W	;読み出しアドレスを
		bsf	STATUS,RP0	;Bank 1
		movwf	EEADR		;EEADR に設定
		bsf	EECON1,RD	;読み出し制御 = ON
		movf	EEDATA,W	;EEDATA から読み出し
		bcf	STATUS,RP0	;Bank 0
		movwf	wkdata		;wkdata に設定
		return

;==========================================================================
;		EEPROM の指定アドレスへの１バイト書き込み
;	( epradr: 書き込みアドレス, wkdata: 書き込みデータ )
;==========================================================================

epr_write
		movf	epradr,W	;書き込みアドレスを
		bsf	STATUS,RP0	;Bank 1
		movwf	EEADR		;EEADR に設定
		bcf	STATUS,RP0	;Bank 0
		movf	wkdata,W	;書き込みデータを
		bsf	STATUS,RP0	;Bank 1
		movwf	EEDATA		;EEDATA に設定
		bsf	EECON1,WREN	;書き込みイネーブル = ON
		movlw	h'55'
		movwf	EECON2		;h'55' を EECON2 に書き込む
		movlw	h'aa'
		movwf	EECON2		;h'aa' を EECON2 に書き込む
		bsf	EECON1,WR	;書き込み制御 = ON
epw01:		btfsc	EECON1,WR	;書き込み 終了か?
		goto	epw01
		bcf	STATUS,RP0	;Bank 0
		bcf	PIR1,EEIF	;書き込み完了割り込みフラグのクリア
		bsf	STATUS,RP0	;Bank 1
		bcf	EECON1,WREN	;安全のため 書き込みイネーブル = OFF
		bcf	STATUS,RP0	;Bank 0
		return

;==========================================================================
;		LedOut の発光ダイオードを指定回数点滅
;==========================================================================

		; 記憶モードに入るときと出るときの表示: 10回 点滅

led_flic
		movlw	d'10'
		movwf	lpcnt		;指定回数を設定

flc01		bsf	LedOut		;発光ダイオード点灯
		movlw	d'100'
		call	wait_1m		;1m * 100 = 約 100m 秒のウエイト
		bcf	LedOut		;発光ダイオード消灯
		movlw	d'100'
		call	wait_1m		;1m * 100 = 約 100m 秒のウエイト
		decfsz	lpcnt,F		;指定回数 = 終了か?
		goto	flc01		;No

		return

		; 受信データと記憶データが一致 OK表示: 早く 10回 点滅

led_flic_ok
		movlw	d'10'
		movwf	lpcnt		;指定回数を設定

fok01		bcf	LedOut		;発光ダイオード消灯
		movlw	d'50'
		call	wait_1m		;1m * 50 = 約 50m 秒のウエイト
		bsf	LedOut		;発光ダイオード点灯
		movlw	d'50'
		call	wait_1m		;1m * 50 = 約 50m 秒のウエイト
		decfsz	lpcnt,F		;指定回数 = 終了か?
		goto	fok01		;No

		return

		; 受信データと記憶データが不一致 NG表示: ゆっくり 3回 点滅

led_flic_ng
		movlw	d'3'
		movwf	lpcnt		;指定回数を設定

fng01		bcf	LedOut		;発光ダイオード消灯
		movlw	d'250'
		call	wait_1m
		movlw	d'250'
		call	wait_1m		;1m * 500 = 約 500m 秒のウエイト
		bsf	LedOut		;発光ダイオード点灯
		movlw	d'250'
		call	wait_1m
		movlw	d'250'
		call	wait_1m		;1m * 500 = 約 500m 秒のウエイト
		decfsz	lpcnt,F		;指定回数 = 終了か?
		goto	fng01		;No

		return

;==========================================================================
;		1m 秒 x (W) ウエイトタイム作成ルーチン
;	( W に指定された間のループ。250 でおよそ 1/4 秒。クロック 4MHz )
;==========================================================================

wait_1m
		movwf	reg1		;1
wt01		movlw	4		;1
		movwf	reg2		;1
wt02		movlw	d'49'		;1
		movwf	reg3		;1
wt03		goto	$+1		;2
		decfsz	reg3,F		;1,2
		goto	wt03		;2,0	1+1+(2+1+2)*48+(2+2+0)=246
		decfsz	reg2,F		;1,2
		goto	wt02		;2,0	1+1+(246+1+2)*3+(246+2+0)=997
		decfsz	reg1,F		;1,2
		goto	wt01		;2,0	1+(997+1+2)*(W-1)+(997+2+0)=1000*W

		return			;2	1000*W+2
					;	(1000*W+2)/4000000*4 S

;==========================================================================
;		ウエイト  １単位 33μＳのループ (4MHz時)
;==========================================================================

ir_wait
		movlw	d'10'		;1
		movwf	reg1		;1
iw01		decfsz	reg1,F		;1,2
		goto	iw01		;2,0	1+1+(1+2)*9+(2+0)=31

		return			;2	31+2=33

;==========================================================================
;		発振周波数校正値呼び出しルーチン
;	（外部発振では必要ないが、値をメモっておく目的で挿入）
;==========================================================================

		org	3ffh
		retlw	low OscCalVal	;外部発振では必要なし

	        end
