	LIST   P=PIC16F886
	#include P16F886.INC

;
; LCD・ADコンバータテストプログラム for PIC16F886
; (c) 2012 Toshio Sugihara. All rights reserved.
; 火災など、重大な事故が発生する可能性もありますが、
; 著者は責任を負いかねます。ご自分の責任において、
; 個人的な趣味でお楽しみください。
;

	__CONFIG _CONFIG1, B'1110001111101100'	; BORは2.1V、MCLR使用、クロックは内蔵を使用
	__CONFIG _CONFIG2, B'1111111011111111'	; WDTはオン

; 起動時0に初期化する変数
bufa		equ	H'20'	; Aポート出力バッファ
bufb		equ	H'21'	; Bポート出力バッファ
bufc		equ	H'22'	; Cポート出力バッファ

wcount	equ	H'23'	; 待ち処理用カウンタ

lcdbuf4	equ	H'24'	; LCD4bit書出処理用バッファ
lcdbuf8	equ	H'25'	; LCD8bit書出処理用バッファ

hexbuf1	equ	H'26'	; HEX1バイト表示機能バッファ1
hexbuf2	equ	H'27'	; HEX1バイト表示機能バッファ2

vpbufh	equ	H'28'	; Vpの上バイト(下2bitのみ有効)
vpbufl	equ	H'29'	; Vpの下バイト(全bit有効)

vlbufh	equ	H'2a'	; VLの上バイト(下2bitのみ有効)
vlbufl	equ	H'2b'	; VLの下バイト(全bit有効)

vdbufh	equ H'2c'	; Vddの上バイト(下2bitのみ有効)
vdbufl	equ H'2d'	; Vddの下バイト(全bit有効)

ibufh	equ	H'2e'	; 電流上バイト(下2bitのみ有効)
ibufl	equ	H'2f'	; 電流下バイト(全bit有効)

statea	equ	H'30'	; 状態変数A
					; bit
					; 7 Aバンク状態 00:Empty 01:Charge
					; 6             10:Discharge 11:Full
					; 5 Aバンク充電SW 1:オン 0:オフ
					; 4 Aバンク放電SW 1:オン 0:オフ
					; 3 Bバンク状態 00:Empty 01:Charge
					; 2             10:Discharge 11:Full
					; 1 Bバンク充電SW 1:オン 0:オフ
					; 0 Bバンク放電SW 1:オン 0:オフ
stateb	equ	H'31'	; 状態変数B
					; 7 放電バンク 0:A 1:B
					; 6 充電バンク 0:A 1:B
					; 5 Vpが7V以上?
					; 4 Vpが5V以上?
					; 3 VLが3.6V以上？
					; 2 Vddが3.0V以上？
					; 1 iが4.73mA以上？
					; 0 強制放電状態？
					
ccount0	equ	H'32'	; 累積電流カウンタL
ccount1	equ	H'33'	; 累積電流カウンタM 
ccount2	equ	H'34'	; 累積電流カウンタH 

tcount0	equ	H'35'	; 起動からの時間カウンタL
tcount1	equ	H'36'	; 起動からの時間カウンタM 
tcount2	equ	H'37'	; 起動からの時間カウンタH 



	org H'0000'
boot
	goto main

	org H'0004'
; 割込
interruption
	;AD変換の割込フラグを下げる
	bcf PIR1,ADIF
	retfie

main
	clrwdt
	; 機能レジスタの初期化
	; ポートの入出力設定と出力クリア
	clrf STATUS		; バンク0を選択
	clrf PORTA		; 出力クリア
	clrf PORTB
	clrf PORTC
	clrf PORTE
	bsf STATUS,RP0	; バンク1を選択
	movlw B'00101111'		; A7,A6,A4が出力
	movwf TRISA	; Aポートの入出力設定
	movlw B'11110100'		; B7,6,5,4,2が入力
	movwf TRISB	; Bポートの入出力設定
	clrf TRISC	; Cポートの入出力設定
	movlw B'00001111'	; Eポートの入出力設定
	movwf TRISE
	bcf STATUS,RP0	; バンク0を選択
	clrf PORTA		; 出力を再度クリア
	clrf PORTB
	clrf PORTC
	clrf PORTE
	; 割込設定のクリア
	bsf STATUS,RP0	; バンク1を選択
	clrf INTCON		; 割込機能をオフ
	clrf PIE1
	clrf PIE2
	bcf STATUS,RP0	; バンク0を選択
	clrf PIR1	; 割込要因発生フラグをクリア
	clrf PIR2

	; プルアップオフ・WDTプリスケーラ設定
	bsf STATUS,RP0	; バンク1を選択
	movlw B'11010000'; TMR0はシステムクロックでカウント
	movwf OPTION_REG	; プリスケーラはTMR0で1:2
	bcf STATUS,RP0
	bsf STATUS,RP1	; バンク2を選択
	movlw B'00010111'
	movwf WDTCON		; WDTオン・2.11秒毎にオーバフロー
	; コンパレータは使用しない
	clrf CM1CON0
	clrf CM2CON0
	; アナログポートの設定
	bsf STATUS,RP0	; バンク3を選択
	movlw B'00011111'; AN0,1,2,3,4がアナログ入力
	movwf ANSEL
	clrf ANSELH		; この範囲はデジタルI/O
	bcf STATUS,RP1	; バンク1を選択
	clrf WPUB		; Bポートプルアップなし
	; TMR2の設定
	movlw D'228'		; 
	movwf PR2		; 228になるまでカウントアップ
	bcf STATUS,RP0	; バンク0を選択
	clrf TMR2		; タイマ値をクリア
	movlw H'0e'		; プリスケーラ1:16
	movwf T2CON		; ポストスケーラ1:2
	bcf PIR1,TMR2IF	; 繰り上がりフラグを下げる
	; 31kHzのクロックをシステムで使う(コンフィギュレーションに従う)
	bsf STATUS,RP0	; バンク1を選択
	clrf OSCCON		; 内蔵31kHzモードを選択
	
; 変数の初期化
	bcf STATUS,RP0	; バンク0を選択
	; 0x20-0x37を0クリア
	movlw H'20'	; クリア先頭アドレス
	movwf FSR
mainp2
	clrf INDF
	incf FSR,F
	movf FSR,W
	xorlw H'38'	; クリアしない先頭アドレス
	btfss STATUS,Z	; 初期化したくない先頭アドレスに
	goto mainp2		; ならないうちはループ 
	; 個別の変数初期化
	; 時刻は0xffffffで始めて初回の更新で0x000000とする。
	movlw H'ff'
	movwf tcount0
	movwf tcount1
	movwf tcount2
	; 100ms程度待つ
	movlw D'100'
	call waitxms

mainloop
	; WDTクリア
	clrwdt
	; 起動時間カウンタ更新
uptcount
	incf tcount0,F
	btfss STATUS,Z
	goto uptcountp2
	incf tcount1,F
	btfss STATUS,Z
	goto uptcountp2
	incf tcount2,F
uptcountp2
	; 各種計測
	; vddを計る
	bsf STATUS,RP0	; バンク1を選択
	bsf STATUS,RP1	; バンク3を選択
	bsf SRCON,FVREN	; 0.6Vユニットをオン
	bcf STATUS,RP0	; バンク2を選択
	bcf STATUS,RP1	; バンク0を選択
	movlw B'11111101'		; 0.6Vのvrefを計る
	movwf ADCON0
	movlw B'10000000'		; 右詰でvddをref+として計る
	call adread
	movwf vdbufl
	movf ADRESH,W
	movwf vdbufh
	bsf STATUS,RP0	; バンク1を選択
	bsf STATUS,RP1	; バンク3を選択
	bcf SRCON,FVREN	; 0.6Vユニットをオフ
	bcf STATUS,RP0	; バンク2を選択
	bcf STATUS,RP1	; バンク0を選択
	; Vpを計る
	movlw B'11001001'		; AN2のVpを計る
	movwf ADCON0
	movlw B'10010000'		; 右詰でAN3をref+として計る
	; movlw B'10000000'		; 右詰でVddをref+として計る
	call adread
	movwf vpbufl
	movf ADRESH,W
	movwf vpbufh
	; VLを計る
	movlw B'11010001'		; A4のVLを計る
	movwf ADCON0
	movlw B'10010000'		; 右詰でAN3をref+として計る
	call adread
	movwf vlbufl
	movf ADRESH,W
	movwf vlbufh
	; iを計る
	movlw B'11000001'		; AN0(ia)を計る
	btfsc stateb,6		; 充電ポートがBのときは
	movlw B'11000101'		; AN1(ib)を計る	
	movwf ADCON0
	movlw B'10010000'		; 右詰でAN3をref+として計る
	call adread
	movwf ibufl
	movf ADRESH,W
	movwf ibufh
; 状態遷移


; 動作
	; 暫定的にCポートで2バンクとも充電状態とする
	movlw B'00000110'
	movwf bufc
	movwf PORTC

; 表示更新
	; tcount0の下4bitが0ならLCD初期化
	movf tcount0,W
	andlw H'0f'
	btfsc STATUS,Z
	call lcdinit
	; 1行め先頭へ異動
	movlw B'10000000'
	call lcdrs0
	; 経過時間表示
	movlw A't'
	call lcdrs1
	movf tcount2,W
	call lcdhex
	movf tcount1,W
	call lcdhex
	movf tcount0,W
	call lcdhex
	; Vp下8bit表示(上位bit0,bit1のいずれかが1の場合は0xffとする)
	movlw A'p'
	call lcdrs1
	movf vpbufl,W
	btfsc vpbufh,0
	movlw H'ff'
	btfsc vpbufh,1
	movlw H'ff'
	call lcdhex
	; VL下8bit表示(上位bit0,bit1のいずれかが1の場合は0xffとする)
	movlw A'l'
	call lcdrs1
	movf vlbufl,W
	btfsc vlbufh,0
	movlw H'ff'
	btfsc vlbufh,1
	movlw H'ff'
	call lcdhex
	; Vdd中8bit(hのbit0とlのbit7..1を表示。hのbit1が1なら0xff)
	movlw A'd'
	call lcdrs1
	bcf STATUS,C
	btfsc vdbufh,0
	bsf STATUS,C
	rrf vdbufl,W
	btfsc vdbufh,1
	movlw H'ff'
	call lcdhex
	; 2行め先頭へ異動
	movlw B'10101000'	
	call lcdrs0
	; 累積充電量を出力
	movlw A'c'
	call lcdrs1
	movf ccount2,W
	call lcdhex
	movf ccount1,W
	call lcdhex
	movf ccount0,W
	call lcdhex
	; 現在の充電電流を出力(上位のbit0,bit1いずれかが1なら0xff)
	movlw A'i'
	call lcdrs1
	movf ibufl,W
	btfsc ibufh,0
	movlw H'ff'
	btfsc ibufh,1
	movlw H'ff'
	call lcdhex
	; システム状態A,Bを順に出力
	;movlw A's'
	;call lcdrs1
	;movf statea,W
	;call lcdhex
	;movf stateb,W
	;call lcdhex
	; デバッグとしてVpの上下10bitを全て表示
	movlw A'x'
	call lcdrs1
	movf vpbufh,W
	andlw H'03'		; 上位は下2bitだけ表示する
	call lcdhex
	movf vpbufl,W	; 8bit全部表示する
	call lcdhex

	; 識別記号('ア')
	movlw H'b1'
	call lcdrs1

; 1秒単位経過まで待つ
wait1s
	btfss PIR1,TMR2IF
	goto wait1s
	bcf PIR1,TMR2IF	; タイマフラグを下げる
	goto mainloop	; メインループを繰り返す

; ここからサブルーチン

; ADコンバータ操作
; 入力
;  ADCON0には設定を入れてから来る(モジュールもオン)
;  WレジスタにADCON1に入れたい内容をセット
; 出力
;  ADRESHにHの結果が入っている
;  WレジスタにADRESLの結果が入っている
; 開始時点
;  バンク0を選択している
; 終了時点
;  バンク0を選択している
;  ADコンバータはオフにしている
adread
	bsf STATUS,RP0	; バンク1を選択
	; ADCON1に設定を書く
	movwf ADCON1
	; Vref+の電源オン
	bcf STATUS,RP0	; バンク0を選択
	bsf bufa,4
	movf bufa,W
	movwf PORTA
	; ADのPIR1をクリア
	bcf PIR1,ADIF

	; 少し待つ(約10ms)
	movlw D'10'
	call waitxms

	; ADのPIE1をセット
	bsf STATUS,RP0	; バンク1を選択
	bsf PIE1,ADIE
	; INTCONをセット
	bcf STATUS,RP0	; バンク0を選択
	bsf INTCON,PEIE	; PIEの設定を使う
	bsf INTCON,GIE	; グローバル割り込みフラグオン
	; AD変換開始
	bsf ADCON0,1		; 変換開始
	SLEEP
	NOP
	; Vref+の電源オフ
	bcf bufa,4
	movf bufa,W
	movwf PORTA
	; 割込の設定を降ろす
	bcf INTCON,GIE
	bcf INTCON,PEIE
	bcf PIR1,ADIF
	bsf STATUS,RP0	; バンク1を選択
	bcf PIE1,ADIE
	; 下バイトをWレジスタに入れる
	movf ADRESL,W
	bcf STATUS,RP0	; バンク0を選択
	return	

; LCDにHEXを8bit分表示
lcdhex
	movwf hexbuf1	; 表示対象1バイト分を保存
	; 上バイトを表示
	swapf hexbuf1,W
	andlw H'0f'
	movwf hexbuf2	; これから表示する1文字
	sublw D'9'		; Wが9以下でC=1, 10以上でC=0
	movlw H'30'
	btfss STATUS,C
	movlw H'37'		; 10以上のときは'A'～'F'
	addwf hexbuf2,W
	call lcdrs1
	; 下バイトを表示
	movf hexbuf1, W
	andlw H'0f'
	movwf hexbuf2	; これから表示する1文字
	sublw D'9'
	movlw H'30'
	btfss STATUS,C
	movlw H'37'
	addwf hexbuf2,W
	call lcdrs1
	return

; LCDを4bitモードで初期化
lcdinit
	; 20ms待つ
	movlw D'20'
	call waitxms
	; 省エネ解除
	call lcdpout
	; データ伝送
	movlw B'00110000'
	call lcd4b
	movlw D'6'
	call waitxms ; 6ms待つ
	movlw B'00110000'
	call lcd4b
	movlw D'6'
	call waitxms ; 6ms待つ
	movlw B'00110000'
	call lcd4b
	movlw B'00100000'
	call lcd4b
	; 5x7ドットで2行
	movlw B'00100000'
	call lcd4b
	movlw B'10000000'
	call lcd4b
	; ディスプレイオフ
	movlw B'00000000'
	call lcd4b
	movlw B'10000000'
	call lcd4b
	movlw D'2'
	call waitxms
	; ディスプレイクリア
	movlw B'00000000'
	call lcd4b
	movlw B'0001000'
	call lcd4b
	movlw D'2'
	call waitxms
	; カーソルは右へ動く
	movlw B'00000000'
	call lcd4b
	movlw B'01100000'
	call lcd4b
	; 表示オン
	movlw B'00000000'
	call lcd4b
	movlw B'11000000'
	call lcd4b
	; 省エネ再開
	call lcdpin
	return

; RS=0でLCDに8bit書込
lcdrs0
	; データを保存
	movwf lcdbuf8
	; 省エネ解除
	call lcdpout
	; 上4bitをRS=0で書込
	movf lcdbuf8,W
	andlw H'f0'
	call lcd4b
	; 下4bitをRS=0で書込
	swapf lcdbuf8,W
	andlw H'f0'
	call lcd4b
	; 省エネ再開
	call lcdpin
	;call lcdup
	return

; RS=1でLCDに8bit書込
lcdrs1
	; データを保存
	movwf lcdbuf8
	; 省エネ解除
	call lcdpout
	; 上4bitをRS=1で書込
	movf lcdbuf8,W
	andlw H'f0'
	iorlw H'04'
	call lcd4b
	; 下4bitをRS=1で書込
	swapf lcdbuf8,W
	andlw H'f0'
	iorlw H'04'
	call lcd4b
	; 省エネ再開
	call lcdpin
	;call lcdup
	return

; LCDのD7..D4とRSにつながる端子を出力に設定
; B7..B4,B2を出力とする
lcdpout
	bsf STATUS,RP0	; バンク1を選択
	movf TRISB,W
	andlw H'0b'
	movwf TRISB
	bcf STATUS,RP0	; バンク0を選択
	return
	
; LCDのD7..D4とRSにつながる端子を入力に設定
; B7..B4,B2を入力とする
lcdpin
	bsf STATUS,RP0	; バンク1を選択
	movf TRISB,W
	iorlw H'f4'
	movwf TRISB
	bcf STATUS,RP0	; バンク0を選択
	return

; B7..B4,B2を1とする
lcdup
	movf bufb,W
	iorlw H'f4'
	movwf bufb
	movwf PORTB
	return

; Wレジスタの内容をLCDに書き出します
; W:7..4bit データD7..D4
; W:2 RS(1か0か)
lcd4b
	; bit7-4,2のみセーブ
	andlw B'11110100'	
	movwf lcdbuf4
	; PORTBに出力(E=0)
	movf bufb, W
	andlw B'00000011'
	iorwf lcdbuf4, W	; データをセット,E=0
	movwf bufb
	movwf PORTB
	; PORTBのbit3(E)を上げ下げ
	bsf bufb, 3
	movf bufb,W
	movwf PORTB
	bcf bufb, 3
	movf bufb,W
	movwf PORTB
	return

; W×1.03ms相当を休む
; Wを0とすると256を指定したのと同じになる。
; 4+(W×8)命令実行して戻る
; (呼び出しもとのcallと、ここでのreturnを含む)
waitxms
	movwf wcount
waitxmsp1
	goto waitxmsp2
waitxmsp2
	goto waitxmsp3
waitxmsp3
	nop
	decfsz wcount,F
	goto waitxmsp1
	return
	end
	