;--------------------------------------------------------------------------------
;	12V鉛電池・充放電コントローラー Ver 1.7
;	Programmed by GRANADA, Since 2007/09/11-
;
;	2007/10/17 パルス充電対応
;	2009/01/14 コメント追加
;	2009/02/19 負荷再接続処理追加（ただしコメントアウト）
;	2009/03/13 過放電時の処理改良（ただしコメントアウト） 、ソース見直し（EQU -> #DEFINE 変更等）
;	2009/04/13 電圧値を一部変更、満充電後クールダウン処理追加、パルス充電をマイルドに変更
;
;	本プログラムはタブを4文字に設定すると綺麗に整形されます
;	製作記事：http://www.asahi-net.or.jp/~SE1M-NITU/html/pic_charge_controler.htm
;--------------------------------------------------------------------------------

	LIST		P=12F675
	INCLUDE		P12F675.INC
	ERRORLEVEL	-205
	ERRORLEVEL	-302

CB = _CPD_OFF
CB &= _CP_OFF
CB &= _BODEN_OFF
CB &= _MCLRE_OFF
CB &= _PWRTE_ON
CB &= _WDT_OFF
CB &= _EXTRC_OSC_NOCLKOUT	; RCIO モードで Port 4 も使用可能に

	__CONFIG	CB
	__IDLOCS	H'0100'

;--------------------------------------------------------------------------------
;	変数アドレス定義
;--------------------------------------------------------------------------------

CBLOCK			 H'20'	; 変数エリア先頭アドレス

VOLTAGE					; バッテリー電圧ストア用
EMPTY_FLAG				; 過放電フラグ
						; 一旦過放電を検出したら過放電解消後にマニュアルでリセットしない限り
						; 負荷を切り離して警告LED点灯（負荷による発振様のスイッチング防止）
FULL_FLAG				; 満充電検出フラグ
FIRST_FULL				; 満充電に達したことがあるかのフラグ
COUNTER					; 時間待ち用ワーク
PULSE_COUNT				; パルス充電用カウンタ
BLINK_COUNT				; ノーマル LED 点滅用カウンタ

ENDC

;--------------------------------------------------------------------------------
;	定数定義
;--------------------------------------------------------------------------------

;--------------------------------------------------------------------------------
;	各種検出電圧、手持ちの鉛電池の特性、太陽電池の出力電流に合わせて最適化必要
;--------------------------------------------------------------------------------

								; AN1 ポートに電池電圧の 1/5 の電圧が加わるよう VR を調製する。
								; A/D変換の基準電圧 = 電源電圧 = 5V の場合、
								; 8bit で A/D 変換を行うと 5V が 255 分割されることになるので、
#DEFINE		FULL_V		D'139'	; 満充電時     (13.6V) の 8bit A/D 変換値 = 13.6/5 * 255/5
#DEFINE		RECHARGE_V	D'135'	; 再充電許可時 (13.2V) の 8bit A/D 変換値 = 13.2/5 * 255/5
#DEFINE		EMPTY_V		D'122'	; 過放電時    （12.0V）の 8bit A/D 変換値 = 11.2/5 * 255/5
								; となる

#DEFINE		CHARGE_PORT		D'0'	; 充電制御 FET はポート 0, 充電    = L, 満充電 = H
#DEFINE		ANALOG_IN		D'1'	; 電圧検出アナログ入力はポート 1
#DEFINE		LOAD_PORT		D'2'	; 放電制御 FET はポート 2, 負荷 OK = H, 過放電 = L
#DEFINE		RESET_PORT		D'3'	; リセットキーはポート 3
#DEFINE		ALERT_LED		D'4'	; 過放電警告 LED はポート 4

;--------------------------------------------------------------------------------
;	プログラム先頭
;--------------------------------------------------------------------------------

	ORG		H'0'
	GOTO	INITIALIZE

;--------------------------------------------------------------------------------
;	割り込み処理（現状未使用）
;--------------------------------------------------------------------------------

	ORG		H'4'			; 割り込み処理先頭アドレス
	RETFIE					; 何もしないで割り込み終了

;--------------------------------------------------------------------------------
;	初期化
;--------------------------------------------------------------------------------

INITIALIZE:

	BANKSEL	GPIO				; Bank 0
	CLRF	GPIO				; 全ポートを L に -> 充電許可、負荷接続不許可、警告表示消灯

	MOVLW	B'00000111'			; コンパレータ未使用、これを設定しないと Port 0 の挙動が怪しい
	MOVWF	CMCON

	BANKSEL	TRISIO				; Bank 1

	CLRF	TRISIO				; ひとまず全ポートを出力に指定
	BSF		TRISIO,ANALOG_IN	; 入力に再指定
	NOP							; 連続ビット操作の際のおまじない
	BSF		TRISIO,RESET_PORT	; 入力に再指定

	MOVLW	B'00110000'			; A/D 変換クロック -> FRC、全ポートをデジタル指定
	MOVWF	ANSEL
	BSF		ANSEL,ANALOG_IN		; アナログポートのみ再指定、他はデジタルポートのまま

	CLRF	PIE1
	BSF		PIE1,ADIE			; ADIE ビットを 1 にして AD 割り込み許可

	BANKSEL	INTCON				; Bank 0
	BSF		INTCON,PEIE			; PEIE ビットを 1 にして周辺割り込みを許可
								; ただし SLEEP からの起動のみなので GIE は立てない

	MOVLW	B'00000101'			; 左詰、内部 REF、AD コンバータ電源 ON、AN1 で A/D 変換
	MOVWF	ADCON0

	CLRF	EMPTY_FLAG			; 過放電フラグをクリアしておく
	CLRF	FULL_FLAG			; 満充電フラグをクリアしておく
	CLRF	FIRST_FULL			; 満充電にまだ一度も達していない

	CALL	BLINK_LED			; 正常起動確認のため LED を数回点滅

	CALL	LOAD_CONNECT		; 負荷接続開始

;--------------------------------------------------------------------------------
;	メインループ開始
;--------------------------------------------------------------------------------

MAIN_LOOP:

	CALL	KEY_CHECK		; リセットキーチェック

	CALL	Wait_20ms		; 次のサンプリングまで 2TAD 以上待つ
	CALL	READ_VOLTAGE	; 電圧を読んでメモリにストア

;--------------------------------------------------------------------------------
;	過放電フラグをチェックし、必要なら LED 警告
;	（通常 LED 使用時は以下3行のコードを復活させること）
;--------------------------------------------------------------------------------
;
;	MOVF	EMPTY_FLAG,F	; 過放電フラグを読む。一度でも過放電に達していたら 1 になる
;	BTFSS	STATUS,Z		; Z フラグが 1（過放電ではない）なら次の命令をスキップ
;	CALL	FLASH_LED		; 過放電状態なら LED 警告

;--------------------------------------------------------------------------------
;	電圧比較、満充電か ?
;--------------------------------------------------------------------------------

	MOVF	VOLTAGE,W		; 測定電圧値を読み出す
	SUBLW	FULL_V			; FULL_V - VOLTAGE -> W
	BTFSC	STATUS,C		; 満充電時は借り入れが発生するので C=0 となり、次の命令がスキップされる
	GOTO	CHECK_RECHARGE	; 満充電ではない

;--------------------------------------------------------------------------------
;	満充電
;--------------------------------------------------------------------------------

	CALL	CHARGE_OFF		; 充電停止

	MOVLW	D'1'
	MOVWF	FULL_FLAG		; 満充電フラグセット
	MOVLW	D'1'
	MOVWF	FIRST_FULL		; 満充電に一度でも達した

	CALL	Wait_1sec		; 1秒充電停止、クールダウン
	CALL	Wait_1sec		; 1秒充電停止、クールダウン

	GOTO	MAIN_LOOP		; メイン処理再開

;--------------------------------------------------------------------------------
;	再充電許可電圧か ?
;--------------------------------------------------------------------------------

CHECK_RECHARGE:

	MOVF	VOLTAGE,W		; 測定電圧値を読み出す
	SUBLW	RECHARGE_V		; RECHARGE_V - VOLTAGE -> W

	BTFSC	STATUS,C		; 再充電許可電圧より電圧が高ければキャリーが発生して C=0 となり次の命令がスキップされる
	GOTO	CHECK_EMPTY		; 過放電チェックへ

;--------------------------------------------------------------------------------
;	再充電許可電圧以上の場合
;--------------------------------------------------------------------------------
;
;--------------------------------------------------------------------------------
;	再充電許可電圧を超えたら負荷を自動再接続するには以下の3行を復活させる
;--------------------------------------------------------------------------------
;	CLRF	EMPTY_FLAG		; 過放電フラグをクリア
;	CALL	LED_OFF			; 過放電 LED を消す
;	CALL	LOAD_CONNECT	; 負荷再接続

	MOVF	FULL_FLAG,F		; 満充電フラグをチェック。満充電に達していたら 1 になる
	BTFSS	STATUS,Z		; Z フラグが 1（満充電に達していない）なら次の命令をスキップ
	GOTO	MAIN_LOOP		; 満充電フラグが立っていたら電圧が下がるまで何もしない

;--------------------------------------------------------------------------------
;	電源投入後、一度でも満充電に達したか
;--------------------------------------------------------------------------------

	MOVF	FIRST_FULL,F	; 一度も満充電に達していないかのフラグをチェック
	BTFSC	STATUS,Z		; Z フラグが 0（既に一度でも満充電に達した）なら次の命令をスキップ
	GOTO	MAIN_LOOP		; まだ一度も満充電に達していならパルス充電にはせず通常充電

;--------------------------------------------------------------------------------
;	パルス充電
;--------------------------------------------------------------------------------

	CALL	PULSE_CHARGE	; パルス充電する
	CALL	CHARGE_ON		; 充電制御 FET ON にして
	CALL	Wait_20ms		; 少し待って電圧を回復させる

	GOTO	MAIN_LOOP		; メイン処理再開

;--------------------------------------------------------------------------------
;	電圧比較、過放電か ?
;--------------------------------------------------------------------------------

CHECK_EMPTY:

	CALL	CHARGE_ON		; とにかく電圧が十分下がっているので充電制御 FET ON
	CLRF	FULL_FLAG		; 満充電フラグをクリア

	MOVF	VOLTAGE,W		; 測定電圧値を読み出す
	SUBLW	EMPTY_V			; EMPTY_V - VOLTAGE -> W
	BTFSS	STATUS,C		; 過放電時はキャリーが発生しないから C=1 となり次の命令をスキップ
	GOTO	MAIN_LOOP		; 電圧は正常範囲内

;--------------------------------------------------------------------------------
;	過放電
;--------------------------------------------------------------------------------

	MOVLW	D'1'
	MOVWF	EMPTY_FLAG		; 過放電フラグを立てる

	CALL	LOAD_DISCONNECT	; 負荷制御 FET OFF して負荷を切り離す
	CALL	FLASH_LED		; LED 警告開始
;	CLRF	FIRST_FULL		; 再充電許可電圧〜満充電を通常充電モードに戻すならこの行を復活

	GOTO	MAIN_LOOP		; A/D 変換再開

;--------------------------------------------------------------------------------
;	A/D 変換を実行し、値をメモリにストアする
;--------------------------------------------------------------------------------

READ_VOLTAGE:

	BCF		PIR1,ADIF		; ADIF を 0 に（A/D 変換終了時このビットが 1 になる）
	BSF		ADCON0,GO		; GO (start) bit を立てて A/D 変換開始

	SLEEP					; A/D 変換しながら SLEEP モードへ
	NOP						; A/D 変換が終了したら自動的に再開

	MOVF	ADRESH,W		; A/D 変換値を読み出して
	MOVWF	VOLTAGE			; ひとまず汎用メモリに保存

	RETURN

;--------------------------------------------------------------------------------
;	充電を許可する
;--------------------------------------------------------------------------------

CHARGE_ON:

	BCF		GPIO,CHARGE_PORT	; 充電制御 FET ON
	RETURN

;--------------------------------------------------------------------------------
;	充電を止める
;--------------------------------------------------------------------------------

CHARGE_OFF:

	BSF		GPIO,CHARGE_PORT	; 充電制御 FET OFF
	RETURN

;--------------------------------------------------------------------------------
;	一定時間パルス充電
;--------------------------------------------------------------------------------

PULSE_CHARGE:

;	MOVLW	D'3'			; 充電パルス回数設定
;	MOVWF	PULSE_COUNT

PULSE_LOOP:

	CALL	CHARGE_ON		; 充電制御 FET ON
	CALL	Wait_100ms
	CALL	CHARGE_OFF		; 充電制御 FET OFF
	CALL	Wait_1sec		; 1秒充電停止してクールダウン

;	DECFSZ	PULSE_COUNT,F
;	GOTO	PULSE_LOOP		; in case DECFSZ false

	RETURN

;--------------------------------------------------------------------------------
;	負荷接続を許可する
;--------------------------------------------------------------------------------

LOAD_CONNECT:

	BSF		GPIO,LOAD_PORT	; 負荷制御 FET ON で通電
	RETURN

;--------------------------------------------------------------------------------
;	負荷を切り離す
;--------------------------------------------------------------------------------

LOAD_DISCONNECT:

	BCF		GPIO,LOAD_PORT	; 負荷制御 FET OFF で負荷切り離し
	RETURN

;--------------------------------------------------------------------------------
;	警告表示（LED点滅）自己点滅 LED の場合
;--------------------------------------------------------------------------------

FLASH_LED:

	CALL	LED_ON
	RETURN

;--------------------------------------------------------------------------------
;	警告表示（LED点滅）ノーマル LED の場合
;--------------------------------------------------------------------------------
;
;FLASH_LED:
;
;	CALL	LED_ON
;	CALL	Wait_200ms
;	CALL	Wait_200ms
;	CALL	LED_OFF			; 警告 LED 消灯
;	CALL	Wait_200ms
;	CALL	Wait_200ms
;
;	RETURN

;--------------------------------------------------------------------------------
;	警告（LED）表示点灯
;--------------------------------------------------------------------------------

LED_ON:

	BSF		GPIO,ALERT_LED	; 警告 LED 点灯
	RETURN

;--------------------------------------------------------------------------------
;	警告（LED）表示停止
;--------------------------------------------------------------------------------

LED_OFF:

	BCF		GPIO,ALERT_LED	; 警告 LED 消灯
	RETURN

;--------------------------------------------------------------------------------
;	正常起動確認（自動点滅 LED の場合）
;--------------------------------------------------------------------------------

BLINK_LED:

	CALL	LED_ON
	CALL	Wait_1sec
	CALL	Wait_1sec
	CALL	Wait_1sec
	CALL	LED_OFF

	RETURN

;--------------------------------------------------------------------------------
;	正常起動確認（ノーマル LED の場合）
;--------------------------------------------------------------------------------
;
;BLINK_LED:					; 通常 LED を使用した場合の点滅処理
;
;	MOVLW	D'3'			; 点滅回数
;	MOVWF	BLINK_COUNT
;
;BLINK:
;
;	CALL	LED_ON			; 点灯
;	CALL	Wait_200ms
;	CALL	Wait_200ms
;	CALL	LED_OFF			; 消灯
;	CALL	Wait_200ms
;	CALL	Wait_200ms
;
;	DECFSZ	BLINK_COUNT,F
;	GOTO	BLINK			; 所定回数まで繰り返す
;
;	RETURN

;--------------------------------------------------------------------------------
;	リセットキー操作チェック
;--------------------------------------------------------------------------------

KEY_CHECK:

	BTFSC	GPIO,RESET_PORT	; ボタンが押されたか?
	RETURN					; 押されていなければ戻る、押されていればこのコードはスキップされる

	CALL	Wait_20ms		; 押されたら20ミリ秒待つ（チャタリング防止）

	BTFSC	GPIO,RESET_PORT	; まだボタンが押されているか?
	RETURN					; 押されていなければ戻る

RESET:

	CALL	CHARGE_ON		; ひとまず充電は許可する

	CALL	READ_VOLTAGE	; A/D 変換実行

	MOVF	VOLTAGE,W		; 測定電圧値を読み出す
	SUBLW	EMPTY_V			; EMPTY_V - VOLTAGE -> W
	BTFSC	STATUS,C		; 正常電圧範囲内ならキャリーが発生し C=0 となり次の命令をスキップ
	GOTO	MAIN_LOOP		; 電圧は未だ過放電状態なので何もしない

	CLRF	EMPTY_FLAG		; 過放電状態ではないので、過放電フラグをクリアする
	CALL	LED_OFF			; 警告 LED 消灯

	CALL	LOAD_CONNECT	; 負荷を接続してみる
	CALL	Wait_20ms		; 少し待って電圧を安定化

	RETURN

;--------------------------------------------------------------------------------
;	クロック 1KHz 時 1 step = 1/250 Sec = 4ms
;--------------------------------------------------------------------------------

Wait_20ms:

    MOVLW	D'1'
    MOVWF	COUNTER
    GOTO	WAIT_LOOP

Wait_100ms:

    MOVLW	D'5'
    MOVWF	COUNTER
    GOTO	WAIT_LOOP

Wait_200ms:

    MOVLW	D'10'
    MOVWF	COUNTER
    GOTO	WAIT_LOOP

Wait_1sec:

    MOVLW	D'50'
    MOVWF	COUNTER
    GOTO	WAIT_LOOP

WAIT_LOOP:					;5 steps = 20 clocks = 20ms if 1KHz clock

	GOTO	$+1				; 2 steps

	DECFSZ	COUNTER,F
	GOTO	WAIT_LOOP		; 所定の回数までループ

	RETURN

;--------------------------------------------------------------------------------
;	プログラム終了
;--------------------------------------------------------------------------------

	END
