PIC16F886–I2C– S-5851A アセンブラ

SII社 S-5851A 温度センサーです
I2C で制御し温度センサーを読み出します
-40℃ ~ +125℃ の範囲の仕様です
DSCN0098

緑色の小さな基板がセンサーです秋月電子でたったの110円
DSCN0096

DSCN0097

先ずは動画です
センサーを指で触れてデータが変化している様子が確認出来ます
但しこれでは使い物に成りません、バイナリを10進変換し表示
しなければ全く意味を持ちません、単なる実験です

I2C-BUS 波形です
マニュアルが日本語なのでとても助かりました

回路図、センサー基板
本当にシンプル、殆ど外付け部品は有りません
ですが特筆すべきはスレーブアドレス設定端子のAD0 / AD1 です
これは Low / Hi  設定だけでは無くて Open  でも状態認識します
詰まり一つの端子で Low / Open / Hi の状態が在るので
スレーブアドレスはその分多く合計8個を同じBUS上に並べられるのです
bandicam 2018-12-25 20-43-36-864
回路図、PIC16F886ホスト(マスター)側の基板です
bandicam 2018-12-25 20-43-13-986

I2C BUS 波形の拡大です、波形の詳細説明は写真に写っています
信号の方向はマスターから見て RX=受信 TX=送信 です、特に
ACKは双方向なので要注意です
S=スタート・コンフィギュレーション、送信
P=ストップ・コンフィギュレーション、送信
SR=リスタート、送信
DATA=測温データ、受信
A=ACK、送信又は受信
NA=NACK、送信

最初はコンフィギュレーションレジスタのイニシャライズ
写真の波形の詳細説明、間違ってます、正しくは
 S →  0x90 →  A →  0x01 →  A →  0x00 →  A →  P   全送信(TX)
DSCN0101

DSCN0102

次は測温データレジスタの読み出し
DSCN0103

DSCN0104

DSCN0105

お仕舞いは カレントリード です
これは連続して読み出す場合 0x91  を書くだけでデータが読み出せます
簡易的にして効率化を図っているのですね
DSCN0106

DSCN0107

波形を観測していて気が付いた点があります、それは測温データの

読み出し一回目は必ず 0x00 0x00 が返って来るという事です
言い換えれば測温データは前回のデータを記憶していてデータが

読み出されるとリフレッシュされる、と言い換えられます、詰まり
電源オンで一回目の読み出しはゼロクリアされた測温データが残って居て
それを読み出して初めて新鮮な測温データが書かれるという事です

温度レジスタ構成です、マイナス側に注目ですね
bandicam 2018-12-25 23-45-16-115

ソースコード等のファイルですがこのブログにアップロードすると
矢鱈に画面が縦長に成ってしまうのでダウンロード出来るように
アップロードしました、こちらからダウンロードして下さい。

素人プログラムなので笑われるのは覚悟です、が、これに依るトラブル
等は一切関知致しません、遊びの範囲でご利用下さい。

PIC16F886–I2C–MCP23017 アセンブラ

MCP23017 はMicroChip社のIO-Expanderです
8+8bitのI/Oを持ちI2Cで制御します
アドレスを変えることで合計8個並列に並べる事が出来ます
1個辺り16bitですから128の入出力が出来ます
然かも秋月電子では1個
¥110で
8bitのIO-Expander PCF8574、1個
¥130より安いです
再露出DSCN0067

GPIO(GPA/GPB)はそれぞれ1bit単位でin/outの設定が可能です
out  に設定した場合、Hi / Low 両方共 25mA の電流値が担保されています
但し負荷を接続する場合は注意しなければなりません、下記

VSS を基準としたその他全てのピンの電圧(VDD は除く) ……………………..
…………………….-0.6 ~ (VDD + 0.6) V

とデータシートに書かれていますので例えばDC24Vリレー等を直接
ドライブすることは出来ません、平たく言うと VSS~VDD間で使え
という事ですね、通常のC-MOS-TTLと同等と考えれば良さそうです

各in/outの内部には100KΩのプルアップ抵抗が用意されていて
GPPUレジスタを”1″にセットするとプルアップ有効と成ります
但し入力として設定した時に限りプルアップは使えます

デフォルト(電源ON後)では無効にセットされます
その他にもビット反転機能も備えていて入力に設定した場合
IPOLレジスタを”1″にすると反転します

20171214211056


今回も泥臭い乍らアセンブラで作りました
回路はシンプルです少しの外付けで完成します
DSCN0061

緑色のLEDは出力、INTA / INTB です、ここでは使用していませんが
とても柔軟に設定出来る様に成っていて読んでいるととても奥が深く
様々な要求から生まれたのだろうと感じ取れます


DSCN0062

DSCN0063

ホスト側はPIC16F886です
DSCN0065

DSCN0066

先ずは動画です
MCP23017 の GPIO-GPAを出力に設定
出力に対して0x00→0x01→0x02・・・0xFF→0x00→・・・
を繰り返しているだけのLチカです
また GPIO-GPBを入力として読み込んだデータを
ホスト側PIC16F886のPORTBに出力しLEDでモニタします
仕事としては無意味ですが、まぁやらないより益しという事で・・・

回路図、MCP23017基板
bandicam 2018-12-21 17-04-55-793

回路図、PIC16F886基板
bandicam 2018-12-21 17-04-36-422

データの詳細です、もっと詳しくはデータブックをダウンロード
してご確認下さい
スレーブアドレス、0x40(A0=A1=A3=0V)
GPA制御アドレス、0x00(IODIRA)
GPB制御アドレス、0x01(IODIRB)
GPA出力アドレス、0x12(GPIOA) 又は 0x14(OLATA) どっちでも出ます
GPB入力アドレス、0x13(GPIOB)

先ずはGPA制御アドレス、0x00(IODIRA)を出力に設定します
スタート→スレーブアドレス0x40→ACK→制御レジスタ0x00(IODIRA)→
ACK→0x00(全出力)→ACK→ストップ
DSCN0051

DSCN0052

次ぎはGPB制御アドレス、0x01(IODIRB)を全入力に設定します
これは実はやらなくても良いのです、理由は電源投入後、自動的に入力
設定に成るからです、デフォルトは全入力だという事です、が、敢えて
実験のためにイニシャライズしてます

スタート→スレーブアドレス0x40→ACK→制御レジスタ0x01(IODIRB)→
ACK→0xFF(全入力)→ACK→ストップ

DSCN0053

DSCN0054

次ぎは実際にGPAにデータ出力します
GPA出力アドレス、0x12(GPIOA) 又は 0x14(OLATA) どっちでも出ます
スタート→スレーブアドレス0x40→ACK→GPAレジスタ0x14(OLATA)→ACK→
連続データ0x00~0xFF(繰り返し)→ACK→ストップ→LOOP

DSCN0055

DSCN0056

次ぎはGPBからのデータ入力を読み出します
GPB入力アドレス、0x13(GPIOB)
スタート→スレーブアドレス0x40→ACK→GPBレジスタ0x13(GPIOB)→ACK→
リスタート→スレーブアドレス0x41(READ)→ACK→読み出しデータ→NACK→ストップ→LOOP

DSCN0057
DSCN0058

動画です

I2CのSCL / SDA の波形です、次のような流れです
GPAに出力 → GPBを読み込む → PIC16F886-PORTBのLEDでモニタ
これを繰り返しています
0x40→0x14→出力DATA   続いて 0x40→0x13→0x41→入力DATA

ACKですがデータシートでは抽象的に「このタイミングですよ」
みたいに波形が書かれているだけですよね、それは真実だし
そうでなければならないのは判るんですが実験していると

ちゃんとACK返ってきているのか、特に色々なデバイスが
I2C-BUSラインにぶら下がって居ると何か決定的な手掛かりが
欲しくなるのです、そういう時は矢張りオシロスコープが

一番です、詰まりデジタル的なロジックは判っていても
実際のアナログ的な部分を確認するためには必須ですよね
実際の波形を視てみましょう
DSCN0068

この電圧軸だとACKらしき足跡が見えませんが、電圧軸を拡大すると
視えて来ます、そうそう、これがACKの足跡なのです
特に双方向 BUS BUFFER 等の回路を設計する際にはこの足跡が
大変重要性を帯びてきます
DSCN0069

“PIC16F886–I2C–MCP23017 アセンブラ” の続きを読む

PIC16F886 24LC64 EEPROM I2C アセンブラ

再露出DSCN0050

PCF8574  IO EXPANDER の序でにEEPROMもドキュメントを
残そうと取り組んでみました、皆様のようにサクッ!!っとC言語とかで
制御出来なくて地道にPICマイコンのアセンブラで組んでみましたが
思いの外難しくて時間が掛かりました。

(ソースプログラムはこちらからダウンロード)

特記すべきはネット記事では見つからなかったものの良く探せば同様
のトラブルが在った事を解決後に知りましたが下記の様な事です・・・

EEPROMの書き込み時間は最低 5ms を必要とする

・・・という事実、これを知らなかったばかりに書き込み後直ちに
読み出しすると最初のスレーブアドレスを送信してフリーズして
仕舞うのです、この問題を解決するためにオシロスコープとにらめっこ
して何日無駄にしたか・・・・はぁ、って感じです、たまたま
デバッグする為にタイマーを入れたらすんなり動いたので

「なんで?」

もしやインターバルが必要なのかとネット検索したら同様のトラブル
の記事がみつかりまして「ああ、やっぱりそうか」と、更には
EEPROMのデータシートを視れば書き込みに5msと書いて有る
ではないですか、そんな訳で無駄な悪戦苦闘を強いられました。
データシートの当該部分です赤枠内
bandicam 2018-12-28 07-23-30-631

取り敢えずこの動画はEEPROM のメモリー番地 0x000~0x0FF
の255のアドレスに00/01/02/03・・・・FD/FE/FF/00と一個ずつ加算
して書いて同時に読み出してLチカさせています。

実際のI2C波形です、一つずつインクリメントしている様子が判ります

ちゃんと書けているか確認しました、以前から所持していた秋月電子
PIC PROGRAMMER を使いました、この基板はPICだけで無く
アプリケーション・ソフトをダウンロードすればEEPROMの
読み書き込みも可能なのです
DSCN0047

但し、これを使うに当たっては色々と問題が在りました
先ず、今時のパソコンにはCOM1(9pin-D-sub / serial)が無い
で、あるにも関わらず我がDELL T3500には付いていた、だから今まで
PIC PROGRAMMERは問題無く動いていた、なのに秋月電子の

アプリケーション・ソフト、Serial EEPROM Programmer Ver6.5.8
は何故だか「エラー’13’型式が合いません」なるシステムエラーが出て
使えないのです、ならばという事で古いノートパソコンを引っ張り出して
それにインストールされている windows xp professional  の元で

動かしたものの同様のエラーで動作しません、もうなにをやってもダメ
そこで思いついたのは USB/Serial変換器 REX -USB60F
これです ↓
DSCN0049

これを使うことでSerial EEPROM Programmer Ver6.5.8は動くのです
折角COM1 / serial ポートが在るのにそれはダメでわざわざ
USB/Serial変換器を使うと動くという矛盾、その嫌らしさ・・・・
然かも COM14  とか、もう信じられない
という訳でちゃんと書けていることが確認出来ました
bandicam 2018-12-19 19-56-45-791

オシロスコープで波形観測するには肝心な部分だけを視たいのに
他の波形が重複して見辛いことこの上ないですよね、その問題は
簡単に解決出来ました、PICの空きポートを利用してそこにトリガー
パルスを出してディレーで眺めれば楽勝です

製作した EEPROM 基板です、LEDは短絡しています
DSCN9946

単純にEEPROMだけですが、保護ダイオード、パスコン、チェック端子
とシンプルな基板です

DSCN9947

全体回路図
bandicam 2018-12-18 20-22-22-539

これでは何も見えませんね、PIC部分
bandicam 2018-12-18 20-22-45-987

EEPROM部分
bandicam 2018-12-18 20-23-08-323

I2Cのプロトコルは下記の通り、データシートの抜粋です
bandicam 2018-12-28 07-20-50-075

bandicam 2018-12-28 07-21-05-061

bandicam 2018-12-28 07-21-25-172

bandicam 2018-12-28 07-21-41-500

デバッグしていて何が一番知りたかったか、それは実際の波形です
SCL / SDA の相互位相関係で全てが決まります、それぞれの立ち上がり
立ち下がり、その時の状況で様々な意味を持たせているので抽象的な
データシートの波形では満足できませんでした、特にACKは僅かな凹みが
足跡の様に残るのです、オシロスコープに顔を近づけないと判別出来ない
のですが、ACKが出ている、出ていない、の大きな手掛かりと成ります
写真は下記の条件での波形です

・EEPROM 24LC64 秋月で50円、マイクロチップ社製 255byte x 32頁
・SLAVE ADDRESS  0xA0(WRITE) / 0xA1(READ)
・MEMORY  ADDRESS 0x00
・WRITE & READ  DATA 0x55

WRITE→READ間インターバルはおよそ 6ms
データシートでは「書き込み時間」と称していますが「書いてから読み出す
までに最低5msは待って呉れ」とは書かれていないのです、データシートの
嫌らしさ、不親切さは自社の欠点や不利益な部分を別の真実という形で表現
し結果的に危険性が伝わらない、と云う形で書かれていると私は永年感じて
居ます

DSCN9976

I2C クロック周波数はほぼ100KHz

DSCN9977

オシロスコープ画面、左側が読み出し、右が書き込み
これを延々と繰り返すだけです

DSCN9978

読み出し START→ 0xA0→  ACK→  0x00→  ACK
DSCN9979

読み出し 0x00→  ACK→  RESTART→ 0xA1

DSCN9980

読み出し 上の写真の 0xA1 は重複、続いて ACK→  0x55→  NACK→  STOP

DSCN9981

書き込み START→  0xA0→  ACK→  0x00→  ACK

DSCN9982

書き込み 0x00→  ACK→  0x55→  ACK→  STOP

DSCN9983

こうした具体的な波形で無いと私には無理。

プログラムです、下記のファイルと成ります
・p16f886.inc
・p16f886_24C64_006.asm   メイン
・p16f886_24C64_read.asm   サブルーチン、読み出し
・p16f886_24C64_sub_i2c.asm サブルーチン
・p16f886_24C64_write.asm   サブルーチン、書き込み
・subroutin.asm         サブルーチン

プログラムの流れは・・・

・データ 0x55 を書く
・読み込む
・LEDを点灯させる(PORTB)

・・・と単純で無意味な動作です、然し乍ら方法を知る為には
やらないよりはまし

 ———————————————————————————

・p16f886.inc はMICRO CHIP 社で配布しているものです
 それに追加した部分だけを示します

CBLOCK   H'20'	;20hからユーザーのメモリが連続して割り当てられる開始宣言
W_TEMP		;For Interupt
S_TEMP		;For Interupt
COUNT		;For サブルーチン変数
COUNT2		;For サブルーチン変数
COUNT3		;For サブルーチン変数
COUNT4		;For サブルーチン変数
COUNT10		;For サブルーチン変数
COUNT11		;For サブルーチン変数
COUNT12		;For サブルーチン変数
COUNT13		;For サブルーチン変数
TMR0_INT_C	;TMR0割込回数カウンタ(65.28msでインクリメント但し水晶=4MHz)
TMR0_INT_D	;上記カウンタが255を超えたら+1するカウンタ
; MAX = 4244.8秒 ( 65.28ms x 255 x 255 ) およそ70分
FLAG		;FLAG RESISTER
;0=
;1=
;2=
;3=
;4=
;5=
;6=
;7=
SWFLAG		;SW FLAG ONLY RESISTER
;0=SWの状態ビット0
;1=SWの状態ビット1
;2=SW ON ビット、MAINプログラムではこれが立った事でSW ONを知る
;3=
;4=
;5=
;6=
;7=
SWFLAG2		;SW FLAG ONLY RESISTER
;0=SWの状態ビット0
;1=SWの状態ビット1
;2=SW ON ビット、MAINプログラムではこれが立った事でSW ONを知る
;3=
;4=
;5=
;6=
;7=
COUNTB		;For Interrupt 避難用
COUNT2B		;For Interrupt 避難用
COUNT3B		;For Interrupt 避難用
COUNT4B		;For Interrupt 避難用
TEMP1		;For A/D value MSB
TEMP2		;For A/D value LSB
TEMP3		;For Adjustable timer routin
PTA		;For I/O register
PTB		;For I/O register
PTC		;For I/O register
PTE		;For I/O register
WRITEADRS	;スレーブアドレス・ライト
WRITEHADRS	;メモリ上位アドレス・ライト
WRITELADRS	;メモリ下位アドレス・ライト
EPROMWDATA	;書き込むデータ
READADRS	;スレーブアドレス・リード
READHADRS	;メモリ上位アドレス・リード
READLADRS	;メモリ下位アドレス・リード

・p16f886_24C64_006.asm   メイン
;***************************************************************************************************************
;This software is provided in an “AS IS” condition,NO WARRANTIES in any form apply to this software.
; picmicrolab.com 5.31.2014
; Modified Dec,10,2018 by maru
;***************************************************************************************************************
; PCF8574 I2C  8-bit IO expander interface with PIC16F876A;
;-------------------------------------------------------------------------------------;
LIST		P=PIC16F886
include		P16f886.inc
errorlevel  	-302		; 翻訳時に302エラーが出ないようにします
errorlevel  	-205		; 翻訳時に205エラーが出ないようにします
errorlevel  	-305		; 翻訳時に305エラーが出ないようにします
;サブルーチンは別ファイルをコール
;	EXTERN	TIMADJ,TIM8ms,TIM100ms,TIM500ms,TIM10ms,ADGET,OUT_A,OUT_B,OUT_C,SWON,SWON2
EXTERN	E24LC64_READ,I2C_INIT,START_I2C,STOP_I2C,E24LC64_WRITE,IDLE
EXTERN	TIM100ms,TIM8ms,TIM10ms
;別ファイルに存在するサブルーチンのラベル名を宣言する
;別ファイルでは必ず GLOBAL で受ける、つまり EXTERN と GLOBAL はペア。
; __CONFIG _CP_OFF & _WDT_OFF & _BODEN_OFF & _PWRTE_OFF & _HS_OSC & _LVP_OFF & _DEBUG_OFF & _CPD_OFF
__CONFIG  _CONFIG1 , _CP_OFF & _DEBUG_OFF  & _CPD_OFF & _LVP_OFF & _MCLRE_ON & _PWRTE_ON & _WDT_OFF & _INTOSCIO & _BOR_OFF
org		0x00
reset:
goto start
org		0x04
start:
;===============================================================================
;スレーブアドレスとデータの宣言
;--------------
WSADRS		EQU	    H'00A0'	;スレーブアドレス WRITE
WSHADRS		EQU	    H'0000'	;メモリアドレス上位 WRITE
WSLADRS		EQU	    H'0000'	;メモリアドレス下位 WRITE
ROMWDATA	EQU	    H'0055'	;E24LC64 に書き込むデータ
;--------------
RSADRS		EQU	    H'00A1'	;スレーブアドレス READ
RSHADRS		EQU	    H'0000'	;メモリアドレス上位 READ
RSLADRS		EQU	    H'0000'	;メモリアドレス下位 READ
;--------------
MOVLW   	WSADRS			;data -> W
MOVWF		WRITEADRS		;W -> F
MOVLW   	WSHADRS			;data -> W
MOVWF		WRITEHADRS		;W -> F
MOVLW   	WSLADRS			;data -> W
MOVWF		WRITELADRS		;W -> F
;--------------
MOVLW		ROMWDATA		;data -> W
MOVWF		EPROMWDATA		;W -> F
;--------------
MOVLW   	RSADRS			;data -> W
MOVWF		READADRS		;W -> F
MOVLW		RSHADRS			;data -> W
MOVWF		READHADRS		;W -> F
MOVLW		RSLADRS			;data -> W
MOVWF		READLADRS		;W -> F
;--------------
;	READADRS	;スレーブアドレス・リード・レジスタ
;	WRITEADRS	;スレーブアドレス・ライト・レジスタ
;===============================================================================
;For PORTA  RA0=OUT using trigger pulse for oscilloscope
BANKSEL		TRISA
MOVLW		b'00000000'
MOVWF		TRISA
banksel		PORTA
CLRF		PORTA
;===============================================================================
;For MCLR INIT 1pin MCLR to use reset sw 1pin (PORTE-RE3)
BANKSEL		PORTE
CLRF		PORTE
BANKSEL		ANSEL
CLRF		ANSEL
BANKSEL		TRISE
MOVLW		b'00001000'
MOVWF		TRISE
;===============================================================================
;***	iNTERNAL OSC 設定 ( OSCCON-8Fh )
;OSCINIT
BANKSEL		OSCCON
MOVLW   	70h        	;CLOCK=4MHz (70H=8MHzでMAX)
MOVWF   	OSCCON
BCF		STATUS,5	;Back to BANK0
;===============================================================================
;I2C の為の初期化
LOOP:
CALL		I2C_INIT
;===============================================================================
;LOOP:
;===============================================================================
;E24LC64WRITE
CALL		IDLE
CALL		START_I2C
CALL		E24LC64_WRITE
CALL		STOP_I2C
;===============================================================================
;For EEPROM WRITE WAITING TIME(Musd be 5ms minimum) if no wait gonna be freeze
CALL		TIM10ms
;===============================================================================
;E24LC64READ
BANKSEL		PORTA	    ;For oscilloscope trigger
CLRF		PORTA	    ;↑
BSF		PORTA,0	    ;↑
CALL		IDLE
CALL		START_I2C
CALL		E24LC64_READ
CALL		STOP_I2C
banksel		PORTA	    ;For oscilloscope trigger
BCF		PORTA,0	    ;↑
;===============================================================================
BANKSEL		W_TEMP
MOVF		W_TEMP,W	;W_TEMP -> W
MOVWF		PORTB		;W → PORTB に即出力(LEDモニタとして使用)
;===============================================================================
GOTO		LOOP
end

・p16f886_24C64_read.asm   サブルーチン、読み出し

	list      	p=16F886	; 翻訳時にリストファイルを作ります
#include 		; 定義ファイルを読み込みます
errorlevel  	-302		; 翻訳時に302エラーが出ないようにします
errorlevel  	-205		; 翻訳時に205エラーが出ないようにします
errorlevel  	-305		; 翻訳時に305エラーが出ないようにします
;***********************************************************************
;サブルーチン、E24LC64を読む
GLOBAL	E24LC64_READ
;サブルーチンのラベル名がメインプログラムでEXTERNで宣言されて居る
;その為、必ずGLOBALで受ける、つまり EXTERN と GLOBAL はペア
;===============================================================================
CODE	;サブルーチン、ここから
;===============================================================================
;	WRITEADRS	;スレーブアドレス・リードの為のライト
E24LC64_READ
BANKSEL		WRITEADRS
MOVF		WRITEADRS,W	;F -> W  スレーブアドレス一発目はライトモード
MOVWF		SSPBUF		;W -> F  INITIATE SEND この瞬間に送信される
;SLAVE ADDRESS
SENDC3:
BANKSEL		PIR1
BTFSS		PIR1, SSPIF	;SEND COMPLETED? YES,SKIP NEXT 送信完了?
GOTO		SENDC3
BCF		PIR1, SSPIF	;YES,CLEAR FLAG 割り込みフラグ・クリア
BANKSEL		SSPCON2
BTFSC		SSPCON2,ACKSTAT	;ACK RECEIVED FROM SLAVE?IF YES SKIP
; スレーブからの ACK待ち 0=受信済み次ぎをスキップ
GOTO		$-1		;IF NO,END
;===============================================================================
;	メモリアドレス上位送信
BANKSEL		READHADRS
MOVF		READHADRS,W	;F -> W
MOVWF		SSPBUF		;W -> F  INITIATE SEND この瞬間に送信される
;SLAVE ADDRESS
SENDC3B:
BANKSEL		PIR1
BTFSS		PIR1, SSPIF	;SEND COMPLETED? YES,SKIP NEXT 送信完了?
GOTO		SENDC3B
BCF		PIR1, SSPIF	;YES,CLEAR FLAG 割り込みフラグ・クリア
BANKSEL		SSPCON2
BTFSC		SSPCON2,ACKSTAT	;ACK RECEIVED FROM SLAVE?IF YES SKIP
; スレーブからの ACK待ち 0=受信済み次ぎをスキップ
GOTO		$-1		;IF NO,END
;	RETURN
;===============================================================================
;	メモリアドレス下位送信
BANKSEL		READLADRS
MOVF		READLADRS,W	;F -> W
MOVWF		SSPBUF		;W -> F  INITIATE SEND この瞬間に送信される
SENDC3C:
BANKSEL		PIR1
BTFSS		PIR1, SSPIF	;SEND COMPLETED? YES,SKIP NEXT 送信完了?
GOTO		SENDC3C
BCF		PIR1, SSPIF	;YES,CLEAR FLAG 割り込みフラグ・クリア
BANKSEL		SSPCON2
BTFSC		SSPCON2,ACKSTAT	;ACK RECEIVED FROM SLAVE?IF YES SKIP
; スレーブからの ACK待ち 0=受信済み次ぎをスキップ
GOTO		$-1		;IF NO,END
;===============================================================================
;repeated start
banksel		SSPCON2
bsf		SSPCON2,RSEN
btfsc		SSPCON2,RSEN
goto		$-1
;===============================================================================
;	READADRS	;スレーブアドレス・リード
BANKSEL		READADRS	;bank0
MOVF		READADRS,W	;F -> W  スレーブアドレス・リード
MOVWF		SSPBUF		;W -> F  INITIATE SEND この瞬間に送信される
SENDC3D:
BANKSEL		PIR1
bcf		PIR1,SSPIF
BTFSS		PIR1, SSPIF	;SEND COMPLETED? YES,SKIP NEXT 送信完了?
GOTO		SENDC3D
BCF		PIR1, SSPIF	;YES,CLEAR FLAG 割り込みフラグ・クリア
BANKSEL		SSPCON2
BTFSC		SSPCON2,ACKSTAT	;ACK RECEIVED FROM SLAVE?IF YES SKIP
; スレーブからの ACK待ち 0=受信済み次ぎをスキップ
GOTO		$-1		; Wait to ACK END
;===============================================================================
;	I2Cから1byte入力しWREGに格納する。デバイスへはNACKを返す。
;	i2c_last_read	最終1byte入力
bsf		SSPCON2,RCEN    ; 受信を許可
btfsc		SSPCON2,RCEN
goto		$-1		; PICが受信許可状態になるまでループ
banksel		PIR1		;bank0
btfss		PIR1,SSPIF
goto		$-1
bcf		PIR1,SSPIF
banksel		SSPBUF		;bank0
movf		SSPBUF,W
MOVWF		W_TEMP		;W -> W_TEMP  I/O EXPANDER へ出力するデータの確保
banksel		SSPCON2		;bank1
bsf		SSPCON2,ACKDT   ; NACKを出力
bsf		SSPCON2,ACKEN   ; ACKENをセット
btfsc		SSPCON2,ACKEN
goto		$-1		; ACKの送信が終了するループ
banksel		PIR1		;bank0
bcf		PIR1,SSPIF
return
END

・p16f886_24C64_sub_i2c.asm サブルーチン

	list      	p=16F886	; 翻訳時にリストファイルを作ります
#include 		; 定義ファイルを読み込みます
errorlevel  	-302		; 翻訳時に302エラーが出ないようにします
errorlevel  	-205		; 翻訳時に205エラーが出ないようにします
errorlevel  	-305		; 翻訳時に305エラーが出ないようにします
;***********************************************************************
;サブルーチン、PCF8574を読む
GLOBAL	I2C_INIT,START_I2C,STOP_I2C,IDLE
;サブルーチンのラベル名がメインプログラムでEXTERNで宣言されて居る
;その為、必ずGLOBALで受ける、つまり EXTERN と GLOBAL はペア
;===============================================================================
CODE	;サブルーチン、ここから
;===============================================================================
I2C_INIT
;===============================================================================
;I2C の為の初期化
BANKSEL		SSPCON		;BANK0
movlw		0x28		;0x28 = シリアルポートを動作させ、SDA とSCL ピンをシリアルポートピンにする。
;0x28 = I2C マスターモード、クロック= FOSC / (4 * (SSPADD+1) )
movwf		SSPCON
BANKSEL		SSPSTAT		;BANK1
BSF		SSPSTAT, SMP
BCF		SSPSTAT, CKE
CLRF		TRISB		;BANK1
BSF		TRISC, 0x04	;SDA=IN
BSF		TRISC, 0x03	;SCL=IN
MOVLW		0x13		;I2C BUS speed  小さい程早く成る
MOVWF		SSPADD		;BANK1
NOP
RETURN
;********************* START CONDITION *****************************************
START_I2C
BANKSEL		SSPCON2
BSF		SSPCON2, SEN	; INITIATE START
SENDB2:
BANKSEL		PIR1
BTFSS		PIR1, SSPIF	;START COMPLETED?YES SKIP NEXT
GOTO		SENDB2
BCF		PIR1, SSPIF	;YES,CLEAR FLAG
RETURN
;*********************	INITIATE STOP*******************************************
STOP_I2C
SENDB5:
BANKSEL		SSPCON2		;bank1
BSF		SSPCON2,PEN
BCF		STATUS,RP0	;bank0
SENDBE:
BTFSS		PIR1,SSPIF
GOTO		SENDBE
BCF		PIR1,SSPIF
RETURN
;===============================================================================
;idle
IDLE:
banksel SSPCON2
;
;    CLRF    SSPCON2
;    btfsc   SSPCON2,ACKDT
;    goto    $-1
;
btfsc   SSPCON2,ACKEN
goto    $-1
btfsc   SSPCON2,RCEN
goto    $-3
btfsc   SSPCON2,PEN
goto    $-5
btfsc   SSPCON2,RSEN
goto    $-7
btfsc   SSPCON2,SEN
goto    $-9
RETURN
END
・p16f886_24C64_write.asm   サブルーチン、書き込み

	list      	p=16F886	; 翻訳時にリストファイルを作ります
#include 		; 定義ファイルを読み込みます
errorlevel  	-302		; 翻訳時に302エラーが出ないようにします
errorlevel  	-205		; 翻訳時に205エラーが出ないようにします
errorlevel  	-305		; 翻訳時に305エラーが出ないようにします
;***********************************************************************
;サブルーチン、PCF8574を読む
GLOBAL	I2C_INIT,START_I2C,STOP_I2C,IDLE
;サブルーチンのラベル名がメインプログラムでEXTERNで宣言されて居る
;その為、必ずGLOBALで受ける、つまり EXTERN と GLOBAL はペア
;===============================================================================
CODE	;サブルーチン、ここから
;===============================================================================
I2C_INIT
;===============================================================================
;I2C の為の初期化
BANKSEL		SSPCON		;BANK0
movlw		0x28		;0x28 = シリアルポートを動作させ、SDA とSCL ピンをシリアルポートピンにする。
;0x28 = I2C マスターモード、クロック= FOSC / (4 * (SSPADD+1) )
movwf		SSPCON
BANKSEL		SSPSTAT		;BANK1
BSF		SSPSTAT, SMP
BCF		SSPSTAT, CKE
CLRF		TRISB		;BANK1
BSF		TRISC, 0x04	;SDA=IN
BSF		TRISC, 0x03	;SCL=IN
MOVLW		0x13		;I2C BUS speed  小さい程早く成る
MOVWF		SSPADD		;BANK1
NOP
RETURN
;********************* START CONDITION *****************************************
START_I2C
BANKSEL		SSPCON2
BSF		SSPCON2, SEN	; INITIATE START
SENDB2:
BANKSEL		PIR1
BTFSS		PIR1, SSPIF	;START COMPLETED?YES SKIP NEXT
GOTO		SENDB2
BCF		PIR1, SSPIF	;YES,CLEAR FLAG
RETURN
;*********************	INITIATE STOP*******************************************
STOP_I2C
SENDB5:
BANKSEL		SSPCON2		;bank1
BSF		SSPCON2,PEN
BCF		STATUS,RP0	;bank0
SENDBE:
BTFSS		PIR1,SSPIF
GOTO		SENDBE
BCF		PIR1,SSPIF
RETURN
;===============================================================================
;idle
IDLE:
banksel SSPCON2
;
;    CLRF    SSPCON2
;    btfsc   SSPCON2,ACKDT
;    goto    $-1
;
btfsc   SSPCON2,ACKEN
goto    $-1
btfsc   SSPCON2,RCEN
goto    $-3
btfsc   SSPCON2,PEN
goto    $-5
btfsc   SSPCON2,RSEN
goto    $-7
btfsc   SSPCON2,SEN
goto    $-9
RETURN
END
・subroutin.asm         サブルーチン
無駄なプログラムが多いです、流用なのでご容赦

;***********************************************************************************
;  PIC16F886 Extra Subroutines	Ver 1.00 2009/11/25
;***********************************************************************************
list      	p=16F886	; 翻訳時にリストファイルを作ります
#include 		; 12F683用定義ファイルを読み込みます
errorlevel  	-302		; 翻訳時に302エラーが出ないようにします
errorlevel  	-205		; 翻訳時に205エラーが出ないようにします
errorlevel  	-305		; 翻訳時に305エラーが出ないようにします
;============================================================
GLOBAL	TIMADJ,TIM8ms,TIM100ms,TIM500ms,TIM10ms,ADGET,OUT_A,OUT_B,OUT_C,SWON,SWON2
;サブルーチンのラベル名がメインプログラムでEXTERNで宣言されて居る
;その為、必ずGLOBALで受ける、つまり EXTERN と GLOBAL はペア
;============================================================
CODE	;サブルーチン、ここから
;============================================================
;A/D コンバータースタート、10bit有る、ADINITで左シフトに設定してあるので上位8ビットがTEMP1に入る
;ADCON0のbit2を1にするとスタートし、それが0に成ったらA/D変換終了
;A/D回路はノイズが出るらしく、ここではいちいちA/Dを使う設定をし、変換したら使わない設定にするADCON0,0
ADGET				;A/D Convertion
BSF	ADCON0,0	;USE A/D Convertion
CALL	SC01
BSF	ADCON0,1	;A/D Covert to start
CALL	SC01		;Wait 40us for A/D Convertion Capacitor Charge Time
LP00B
CLRWDT			;WATCH DOG TIMER RESET
BTFSC	ADCON0,1
GOTO	LP00B
MOVF	ADRESH,W	;A/D Value MSB to W
MOVWF	TEMP1		;W to TEMP1
BSF	STATUS,5	;Change to Bank1 (STATUS Register Bit5 set to 1)
MOVF	ADRESL,W	;A/D Value LSB to W
BCF	STATUS,5	;Back to Bank0 (STATUS Register Bit5 set to 0)
MOVWF	TEMP2		;W to TEMP2
BCF	ADCON0,0	;NO-USE A/D Convertion (For Noise cut)
RETURN
SC01				;Timer 40us
MOVLW   24H
MOVWF   COUNT
LP01B
DECFSZ  COUNT,F	;Dec COUNT untill 0
GOTO    LP01B
RETURN
;============================================================
;TEMP3レジスタの数値を01--FFhで変化させる事で待ち時間を設定出来る
TIMADJ				;Adjustable TIMER
MOVLW	D'1' 		;DataA write to the W register(under the ALU)
MOVWF	COUNT4		;Data set to the COUNT4
LP5B	MOVLW	D'12' 		;DataA write to the W register(under the ALU)
MOVWF	COUNT3		;Data set to the COUNT3
LP5C	DECFSZ	COUNT2		;COUNT2 DEC
GOTO	LP5C		;CNTR INC UNTILL 0
CLRWDT			;WATCH DOG TIMER RESET
MOVF	TEMP3,W		;TEMP1 to W
MOVWF	COUNT2		;W to COUNT2
DECFSZ	COUNT3		;COUNT3 DEC
GOTO	LP5C		;REPT DEC UNTILL 0
DECFSZ	COUNT4		;REPT2 DEC
GOTO	LP5B		;REPT2 DEC UNTILL 0
RETURN			;BACK TO Main Rutin
;============================================================
;10ms待つ
TIM10ms				;10msTIMER
MOVLW	D'1' 		;DataA write to the W register(under the ALU)
MOVWF	COUNT3		;Data set to the COUNT2
LP08B	MOVLW	D'13' 		;DataA write to the W register(under the ALU)
MOVWF	COUNT2		;Data set to the COUNT
LP08C	INCFSZ	COUNT		;CNTR INC
GOTO	LP08C		;CNTR INC UNTILL 256
CLRWDT			;WATCH DOG TIMER RESET
DECFSZ	COUNT2		;COUNT DEC
GOTO	LP08C		;COUNT DEC UNTILL 0
DECFSZ	COUNT3		;COUNT2 DEC
GOTO	LP08B		;COUNT2 DEC UNTILL 0
RETURN			;BACK TO MAIN
;============================================================
;8ms待つ
TIM8ms				;8msTIMER
MOVLW	D'1' 		;DataA write to the W register(under the ALU)
MOVWF	COUNT3		;Data set to the COUNT2
LP08D	MOVLW	D'1' 		;DataA write to the W register(under the ALU)
MOVWF	COUNT2		;Data set to the COUNT
LP08E	INCFSZ	COUNT		;CNTR INC
GOTO	LP08E		;CNTR INC UNTILL 256
CLRWDT			;WATCH DOG TIMER RESET
DECFSZ	COUNT2		;COUNT DEC
GOTO	LP08E		;COUNT DEC UNTILL 0
DECFSZ	COUNT3		;COUNT2 DEC
GOTO	LP08D		;COUNT2 DEC UNTILL 0
RETURN			;BACK TO MAIN
;============================================================
;100ms待つ
TIM100ms				;100msTIMER
MOVLW	D'10' 		;DataA write to the W register(under the ALU)
MOVWF	COUNT3		;Data set to the COUNT2
LP08F	MOVLW	D'13' 		;DataA write to the W register(under the ALU)
MOVWF	COUNT2		;Data set to the COUNT
LP08G	INCFSZ	COUNT		;CNTR INC
GOTO	LP08G		;CNTR INC UNTILL 256
CLRWDT			;WATCH DOG TIMER RESET
DECFSZ	COUNT2		;COUNT DEC
GOTO	LP08G		;COUNT DEC UNTILL 0
DECFSZ	COUNT3		;COUNT2 DEC
GOTO	LP08F		;COUNT2 DEC UNTILL 0
RETURN			;BACK TO MAIN
;============================================================
;500ms待つ
TIM500ms				;100msTIMER
MOVLW	D'50' 		;DataA write to the W register(under the ALU)
MOVWF	COUNT3		;Data set to the COUNT2
LP08H	MOVLW	D'13' 		;DataA write to the W register(under the ALU)
MOVWF	COUNT2		;Data set to the COUNT
LP08I	INCFSZ	COUNT		;CNTR INC
GOTO	LP08I		;CNTR INC UNTILL 256
CLRWDT			;WATCH DOG TIMER RESET
DECFSZ	COUNT2		;COUNT DEC
GOTO	LP08I		;COUNT DEC UNTILL 0
DECFSZ	COUNT3		;COUNT2 DEC
GOTO	LP08H		;COUNT2 DEC UNTILL 0
RETURN			;BACK TO MAIN
;============================================================
;PORTAが出力に設定されて居る場合PTAレジスタのビットを立てればそのビットを出力する
OUT_A	NOP			;PORT OUTPUT ROUTIN
CLRWDT			;WATCH DOG TIMER RESET
MOVF	PTA,W		;DATA COPY FROM PTA TO W RESISTER
MOVWF	PORTA		;DATA COPY FROM W TO PORTA
RETURN
;============================================================
;PORTBが出力に設定されて居る場合PTAレジスタのビットを立てればそのビットを出力する
OUT_B	NOP			;PORT OUTPUT ROUTIN
CLRWDT			;WATCH DOG TIMER RESET
MOVF	PTB,W		;DATA COPY FROM PTB TO W RESISTER
MOVWF	PORTB		;DATA COPY FROM W TO PORTB
RETURN
;============================================================
;PORTCが出力に設定されて居る場合PTCレジスタのビットを立てればそのビットを出力する
OUT_C	NOP			;PORT OUTPUT ROUTIN
CLRWDT			;WATCH DOG TIMER RESET
MOVF	PTC,W		;DATA COPY FROM PTC TO W RESISTER
MOVWF	PORTC		;DATA COPY FROM W TO PORTC
RETURN
;============================================================
;入力はPA0(17pin)
;ここでは入力が H-->L-->H を一通り経由すると SWFLAGのbit2 が0-->1に成る
;チャタリングは30msに設定されて居る
;特徴は入力の状態をずっと見ているのでは無く時々見に行きフラグを変化させている
;その為、入力を見に行って他のことが何も出来ないのではなく同時に並行して他の
;作業を出来ることである
;------------------------------
SWON	CLRWDT
BCF	SWFLAG,2	;SW ON のフラグをクリア
BCF	STATUS,Z
BCF	STATUS,C
BCF	STATUS,DC
MOVLW	D'1'		;D'5'--->W  1= 10ms
MOVWF	COUNT4		;W--->COUNT4
;-----------------------SW ポートの H/Lチェック
MOVF	TRISA,W		;IO-->W
MOVWF	PTA		;W-->PTA
BTFSC	PTA,0		;SW=Hつまり押されていない、次へ
;SW=Lつまり押された、次をスキップ
GOTO	SWONC		;押されて居ないへ進む
;-----------------------SW ポートは L チャタリングチェック
SWONB	CLRWDT
CALL	TIM10ms
CALL	TIM10ms
CALL	TIM10ms
MOVF	TRISA,W		;IO-->W
MOVWF	PTA		;W-->PTA
BTFSC	PTA,0		;SW=1つまり押されていない、次へ
;SW=0つまり押された、次をスキップ
RETURN			;押されて居ない、チャタリング、MAINへ戻る
;	CALL	TIM10ms		;10ms timer チャタリング対策
;	DECFSZ	COUNT4,1	;COUNT4 = COUNT4 - 1 ゼロに成ったら次をスキップ
;ゼロで無いならば次へ進む
;	GOTO	SWONB		;再度押されたかチェックを繰り返す
;-----------------------
MOVLW	D'1'		; 1 --> W
SUBWF	SWFLAG,1	;SWFLAG=SWFLAG-1
BTFSC	STATUS,C	;C=1(キャリーフラグ)ならばプラス、次へ
;C=0ならばゼロかマイナス次をスキップ
GOTO	SWONB1
BTFSS	STATUS,Z	;Z=0ならマイナス次 Z=1ならゼロ次をスキップ
GOTO	SET0		;SWFLAGをゼロにしてMAINに戻る
SWONB1	SUBWF	SWFLAG,1	;SWFLAG=SWFLAG-1
BTFSC	STATUS,C	;C=1(キャリーフラグ)ならばプラス、次へ
;C=0ならばゼロかマイナス次をスキップ
GOTO	SWONB2
BTFSS	STATUS,Z	;Z=0ならマイナス次 Z=1ならゼロ次をスキップ
GOTO	SET2		;SWFLAGを2にしてMAINに戻る
SWONB2	SUBWF	SWFLAG,1	;SWFLAG=SWFLAG-1
BTFSC	STATUS,C	;C=1(キャリーフラグ)ならばプラス、次へ
;C=0ならばゼロかマイナス次をスキップ
GOTO	SWONB3
BTFSS	STATUS,Z	;Z=0ならマイナス次 Z=1ならゼロ次をスキップ
GOTO	SET2		;SWFLAGを2にしてMAINに戻る
SWONB3	GOTO	SET0		;SWFLAGをゼロにしてMAINに戻る
;-----------------------SW ポートは H チャタリングチェック
SWONC	CLRWDT
CALL	TIM10ms
CALL	TIM10ms
CALL	TIM10ms
MOVF	TRISA,W		;IO-->W
MOVWF	PTA		;W-->PTA
BTFSS	PTA,0		;SW=0つまり押されていない、次をスキップ
;SW=1つまり押された、次へ
RETURN			;押されて居ない、チャタリング、MAINへ戻る
;	CALL	TIM10ms		;10ms timer チャタリング対策
;	DECFSZ	COUNT4,1	;COUNT4 = COUNT4 - 1 ゼロに成ったら次をスキップ
;ゼロで無いならば次へ進む
;	GOTO	SWONC		;再度押されたかチェックを繰り返す
;-----------------------
MOVLW	D'1'		; 1 --> W
SUBWF	SWFLAG,1	;SWFLAG=SWFLAG-1
BTFSC	STATUS,C	;C=1(キャリーフラグ)ならばプラス、次へ
;C=0ならばゼロかマイナス次をスキップ
GOTO	SWONC1
BTFSS	STATUS,Z	;Z=0ならマイナス次 Z=1ならゼロ次をスキップ
GOTO	SET1		;SWFLAGを1にしてMAINに戻る
SWONC1	SUBWF	SWFLAG,1	;SWFLAG=SWFLAG-1
BTFSC	STATUS,C	;C=1(キャリーフラグ)ならばプラス、次へ
;C=0ならばゼロかマイナス次をスキップ
GOTO	SWONC2
BTFSS	STATUS,Z	;Z=0ならマイナス次 Z=1ならゼロ次をスキップ
GOTO	SET1		;SWFLAGを1にしてMAINに戻る
SWONC2	SUBWF	SWFLAG,1	;SWFLAG=SWFLAG-1
BTFSC	STATUS,C	;C=1(キャリーフラグ)ならばプラス、次へ
;C=0ならばゼロかマイナス次をスキップ
GOTO	SWONC3
BTFSS	STATUS,Z	;Z=0ならマイナス次 Z=1ならゼロ次をスキップ
GOTO	SET3		;SWFLAGを3にしてMAINに戻る、つまりSWは一通りの経過を経て押された
SWONC3	GOTO	SET0		;SWFLAGをゼロにしてMAINに戻る
;-----------------------
SET0	CLRF	SWFLAG		;SET0
RETURN
SET1	MOVLW	D'1'		;SET1
MOVWF	SWFLAG		;W --> SWFLAG
RETURN
SET2	MOVLW	D'2'		;SET2
MOVWF	SWFLAG		;W --> SWFLAG
RETURN
SET3	MOVLW	D'3'		;SET3 つまりSW操作は正しく一通り行われた
MOVWF	SWFLAG		;W --> SWFLAG
BSF	SWFLAG,2	;SW ON のフラグを立てる
RETURN
;============================================================
;入力はPA1(18pin)
;ここでは入力が H-->L-->H を一通り経由すると SWFLAG2のbit2 が0-->1に成る
;チャタリングは30msに設定されて居る
;特徴は入力の状態をずっと見ているのでは無く時々見に行きフラグを変化させている
;その為、入力を見に行って他のことが何も出来ないのではなく同時に並行して他の
;作業を出来ることである
SWON2	CLRWDT
BCF	SWFLAG2,2	;SW ON のフラグをクリア
BCF	STATUS,Z
BCF	STATUS,C
BCF	STATUS,DC
MOVLW	D'1'		;D'5'--->W  1= 10ms
MOVWF	COUNT4		;W--->COUNT4
;-----------------------SW ポートの H/Lチェック
MOVF	TRISA,W		;IO-->W
MOVWF	PTA		;W-->PTA
BTFSC	PTA,1		;SW=Hつまり押されていない、次へ
;SW=Lつまり押された、次をスキップ
GOTO	SWON2C		;押されて居ないへ進む
;-----------------------SW ポートは L チャタリングチェック
SWON2B	CLRWDT
CALL	TIM10ms
CALL	TIM10ms
CALL	TIM10ms
MOVF	TRISA,W		;IO-->W
MOVWF	PTA		;W-->PTA
BTFSC	PTA,1		;SW=1つまり押されていない、次へ
;SW=0つまり押された、次をスキップ
RETURN			;押されて居ない、チャタリング、MAINへ戻る
;	CALL	TIM10ms		;10ms timer チャタリング対策
;	DECFSZ	COUNT4,1	;COUNT4 = COUNT4 - 1 ゼロに成ったら次をスキップ
;ゼロで無いならば次へ進む
;	GOTO	SWON2B		;再度押されたかチェックを繰り返す
;-----------------------
MOVLW	D'1'		; 1 --> W
SUBWF	SWFLAG2,1	;SWFLAG2=SWFLAG2-1
BTFSC	STATUS,C	;C=1(キャリーフラグ)ならばプラス、次へ
;C=0ならばゼロかマイナス次をスキップ
GOTO	SWON2B1
BTFSS	STATUS,Z	;Z=0ならマイナス次 Z=1ならゼロ次をスキップ
GOTO	SETB0		;SWFLAG2をゼロにしてMAINに戻る
SWON2B1	SUBWF	SWFLAG2,1	;SWFLAG2=SWFLAG2-1
BTFSC	STATUS,C	;C=1(キャリーフラグ)ならばプラス、次へ
;C=0ならばゼロかマイナス次をスキップ
GOTO	SWON2B2
BTFSS	STATUS,Z	;Z=0ならマイナス次 Z=1ならゼロ次をスキップ
GOTO	SETB2		;SWFLAG2を2にしてMAINに戻る
SWON2B2	SUBWF	SWFLAG2,1	;SWFLAG2=SWFLAG2-1
BTFSC	STATUS,C	;C=1(キャリーフラグ)ならばプラス、次へ
;C=0ならばゼロかマイナス次をスキップ
GOTO	SWON2B3
BTFSS	STATUS,Z	;Z=0ならマイナス次 Z=1ならゼロ次をスキップ
GOTO	SETB2		;SWFLAG2を2にしてMAINに戻る
SWON2B3	GOTO	SETB0		;SWFLAG2をゼロにしてMAINに戻る
;-----------------------SW ポートは H チャタリングチェック
SWON2C	CLRWDT
CALL	TIM10ms
CALL	TIM10ms
CALL	TIM10ms
MOVF	TRISA,W		;IO-->W
MOVWF	PTA		;W-->PTA
BTFSS	PTA,1		;SW=0つまり押されていない、次をスキップ
;SW=1つまり押された、次へ
RETURN			;押されて居ない、チャタリング、MAINへ戻る
;	CALL	TIM10ms		;10ms timer チャタリング対策
;	DECFSZ	COUNT4,1	;COUNT4 = COUNT4 - 1 ゼロに成ったら次をスキップ
;ゼロで無いならば次へ進む
;	GOTO	SWON2C		;再度押されたかチェックを繰り返す
;-----------------------
MOVLW	D'1'		; 1 --> W
SUBWF	SWFLAG2,1	;SWFLAG2=SWFLAG2-1
BTFSC	STATUS,C	;C=1(キャリーフラグ)ならばプラス、次へ
;C=0ならばゼロかマイナス次をスキップ
GOTO	SWON2C1
BTFSS	STATUS,Z	;Z=0ならマイナス次 Z=1ならゼロ次をスキップ
GOTO	SETB1		;SWFLAG2を1にしてMAINに戻る
SWON2C1	SUBWF	SWFLAG2,1	;SWFLAG2=SWFLAG2-1
BTFSC	STATUS,C	;C=1(キャリーフラグ)ならばプラス、次へ
;C=0ならばゼロかマイナス次をスキップ
GOTO	SWON2C2
BTFSS	STATUS,Z	;Z=0ならマイナス次 Z=1ならゼロ次をスキップ
GOTO	SETB1		;SWFLAG2を1にしてMAINに戻る
SWON2C2	SUBWF	SWFLAG2,1	;SWFLAG2=SWFLAG2-1
BTFSC	STATUS,C	;C=1(キャリーフラグ)ならばプラス、次へ
;C=0ならばゼロかマイナス次をスキップ
GOTO	SWON2C3
BTFSS	STATUS,Z	;Z=0ならマイナス次 Z=1ならゼロ次をスキップ
GOTO	SETB3		;SWFLAG2を3にしてMAINに戻る、つまりSWは一通りの経過を経て押された
SWON2C3	GOTO	SETB0		;SWFLAG2をゼロにしてMAINに戻る
;-----------------------
SETB0	CLRF	SWFLAG2		;SETB0
RETURN
SETB1	MOVLW	D'1'		;SETB1
MOVWF	SWFLAG2		;W --> SWFLAG2
RETURN
SETB2	MOVLW	D'2'		;SETB2
MOVWF	SWFLAG2		;W --> SWFLAG2
RETURN
SETB3	MOVLW	D'3'		;SETB3 つまりSW操作は正しく一通り行われた
MOVWF	SWFLAG2		;W --> SWFLAG2
BSF	SWFLAG2,2	;SW ON のフラグを立てる
RETURN
;********************************************************************************************
END