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

ソースファイルです、下記の構成です
・p16f886.inc
・I2C_MCP23017_ExpIO_v001.asm
・p16f886_mcp23017_read.asm
・p16f886_mcp23017_sub_i2c.asm
・p16f886_mcp23017_write.asm
・subroutin.asm
言い訳します
プログラミングをされる方はC言語とかでスマートにやってしまいますが
なにせ私には出来ないと言うか覚えるのが面倒くさくて未だにアセンブラ
が精一杯でプロからみて児戯に思えますがその程度のものとご理解下さい
無駄や煩雑で美しくないプログラミングです

*****************************************************

・p16f886.inc
MICROCHIP社から配布されているものです
追加部分のみ記載します
;==========================================================================
;       User DATA Area add by Marukawa
;==========================================================================
;スレーブアドレスとデータの宣言
;--------------
WSADRS		EQU	    H'0040'	;スレーブアドレス WRITE
WSREG_GPA   	EQU	    H'0000'	;制御レジスタドレスGPA WRITE
WSREG_GPB   	EQU	    H'0001'	;制御レジスタドレスGPB WRITE
WDATA		EQU	    H'0000'	;MCP23017 に書き込むデータ
;--------------
RSADRS		EQU	    H'0041'	;スレーブアドレス READ
RSREG		EQU	    H'0013'	;PORTレジスタ GPIOB アドレス READ
;--------------
IODIRA_DATA	EQU	    0x00	;GPA is all output ;GPADATA に格納する
IODIRB_DATA	EQU	    0xFF	;GPB is all input
;--------------
OLATA_ADRS	EQU	    0x14	;GPA ビット出力、レジスタアドレス
;	OLATA_ADRS	EQU	    0x12	;GPA ビット出力ラッチ、レジスタアドレス    ]どちらでもOK↓
OLATA_DATA	EQU	    0x01	;GPAに実際に出力する8bitデータ(GPA0----GPA7) ]どちらでもOK↑
;==========================================================================
;       User RAM Area add by Marukawa
;==========================================================================
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	;スレーブアドレス・ライト
WREGADRS	;制御アドレス・ライト
;WRITELADRS	;メモリ下位アドレス・ライト
EPROMWDATA	;書き込むデータ
READADRS	;スレーブアドレス・リード
RREGADRS	;制御アドレス・リード
RDATA		;MCP23017から読んだデータ
I2CWDATA	;送信データ
GPADATA		;GPAに実際に出力する8bitデータ(GPA0----GPA7)
GPBDATA		;GPBに実際に出力する8bitデータ(GPB0----GPB7)
TESTDATA	;何にでも流用する
ENDC		;20hからユーザーのメモリが連続して割り当てられた終了宣言

・I2C_MCP23017_ExpIO_v001.asm
メインプログラムです

;***************************************************************************************************************
;This software is provided in an “AS IS” condition,NO WARRANTIES in any form apply to this software.
; Modified Dec,20,2018 by maru
;***************************************************************************************************************
; MCP23017 I2C  8-bit IO expander interface with PIC16F886;
;-------------------------------------------------------------------------------------;
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	I2C_INIT,START_I2C,STOP_I2C,MCP23017_WRITE,IDLE
EXTERN	GPA_INIT,GPB_INIT,RESTART,DATA_READ_NACK
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:
;===============================================================================
;スレーブアドレスとデータの宣言 → p16f886.ini 内で宣言、必須
;--------------
;	WSADRS		EQU	    H'0040'	;スレーブアドレス WRITE
;	WSREG_GPA   	EQU	    H'0000'	;制御レジスタドレスGPA WRITE
;	WSREG_GPB   	EQU	    H'0001'	;制御レジスタドレスGPB WRITE
;	WDATA		EQU	    H'0000'	;MCP23017 に書き込むデータ
;	;--------------
;	RSADRS		EQU	    H'0041'	;スレーブアドレス READ
;	RSREG		EQU	    H'0013'	;PORTレジスタ GPIOB アドレス READ
;--------------
;	IODIRA_DATA	EQU	    0x00	;GPA is all output ;GPADATA に格納する
;	IODIRB_DATA	EQU	    0xFF	;GPB is all input
;--------------
;	OLATA_ADRS	EQU	    0x14	;GPA ビット出力、レジスタアドレス
;	OLATA_ADRS	EQU	    0x12	;GPA ビット出力ラッチ、レジスタアドレス    ]どちらでもOK↓
;	OLATA_DATA	EQU	    0x01	;GPAに実際に出力する8bitデータ(GPA0----GPA7) ]どちらでもOK↑
;--------------
;===============================================================================
;For PORTA  RA0 = all 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
;===============================================================================
;↓↓ MCP23017 のイニシャライズ、一回だけ実施すること、繰り返すと出力データが0x7F以上書けなくなる ↓↓
;===============================================================================
;MCP23017WRITE_INITIALIZE_GPA(GPIO)
CALL		GPA_INIT
;MCP23017WRITE_INITIALIZE_GPB(GPIO)
CALL		GPB_INIT
;===============================================================================
LOOP:	;↓↓ GPAに対して実際の出力データを送信 ↓↓
;===============================================================================
;--------------------------------------
;For debug
banksel		PORTA			;For oscilloscope trigger
BSF		PORTA,0			;↑
banksel		PORTA			;For oscilloscope trigger
BCF		PORTA,0			;↑
;--------------------------------------
;GPAに実際に出力する8bitデータ(GPA0----GPA7)を設定するルーチン
CALL		IDLE
;スタート・コンディション
CALL		START_I2C
;スレーブアドレスセット
BANKSEL		I2CWDATA
MOVLW   	WSADRS			;data -> W
MOVWF		I2CWDATA		;W -> F
;書き込み
CALL		MCP23017_WRITE
;制御レジスタセット
BANKSEL		I2CWDATA
MOVLW   	OLATA_ADRS		;data -> W
MOVWF		I2CWDATA		;W -> F
;書き込み
CALL		MCP23017_WRITE
;GPAに出力するデータセット
;	BANKSEL		OLATA_DATA		;データ = 出力データ
;	MOVLW   	OLATA_DATA		;data -> W
BANKSEL		TESTDATA
MOVF		TESTDATA,W		;F -> W
MOVWF		I2CWDATA		;W -> F
;書き込み
CALL		MCP23017_WRITE
;ストップ・コンディション
CALL		STOP_I2C
;--------------------------------------
;For debug
;	banksel		PORTA			;For oscilloscope trigger
;	BSF		PORTA,0			;↑
;	banksel		PORTA			;For oscilloscope trigger
;	BCF		PORTA,0			;↑
BANKSEL		TESTDATA
INCF		TESTDATA,f		;F = F + 1
;	CALL		TIM8ms		;Wait for human's eye
;--------------------------------------
;	GOTO		LOOP
;===============================================================================
;===============================================================================
;↓↓ GPBから入力データを受信 ↓↓
;===============================================================================
;MCP23017READ
;For debug
;	banksel		PORTA			;For oscilloscope trigger
;	BSF		PORTA,0			;↑
;	banksel		PORTA			;For oscilloscope trigger
;	BCF		PORTA,0			;↑
CALL		IDLE
;スタート・コンディション
CALL		START_I2C
;スレーブアドレスセット
BANKSEL		WSADRS
MOVLW   	WSADRS			;data -> W   0x40
MOVWF		I2CWDATA		;W -> F
;書き込み
CALL		MCP23017_WRITE
;制御レジスタセット
BANKSEL		I2CWDATA
MOVLW   	RSREG			;data -> W  GPIOB レジスタ、ここを読む   0x13
MOVWF		I2CWDATA		;W -> F
;書き込み
CALL		MCP23017_WRITE
;リスタート
CALL		RESTART
;リードするよ、という送信
;制御レジスタセット
BANKSEL		I2CWDATA
MOVLW   	RSADRS			;data -> W    00x41
MOVWF		I2CWDATA		;W -> F
;書き込み
CALL		MCP23017_WRITE
;GPB0 を読み込む(データ受信)、続いて NACK を返し、読み込んでデータを W_TEMP に格納する
CALL		DATA_READ_NACK
;受信終了
;ストップ・コンディション
CALL		STOP_I2C
CALL		TIM100ms    ;For Human's eye, easy to see
;===============================================================================
;EEPROM下位メモリアドレスを +1
;	INCF	        READLADRS   ;0x00 --> 0xFF --> 0x00 を繰り返す
;===============================================================================
BANKSEL		W_TEMP
MOVF		W_TEMP,W	;W_TEMP -> W
MOVWF		PORTB		;W → PORTB に即出力(LEDモニタとして使用)
;===============================================================================
GOTO		LOOP
end

・p16f886_mcp23017_read.asm
サブルーチンです
ここで注目したいのは マスター → スレーブ に対して送信する ACK  / NACK です
通常、ACK はスレーブ → マスターの方向ですが逆の場合、混乱しやすい性質を
帯びて居ます、PIC16F886では SSPCON2  でACK  / NACK を切り替えます

ACKの場合、連続してデータを受信中スレーブに返す

    banksel            SSPCON2        ;bank1
        bcf             SSPCON2,ACKDT   ; ACKを出力
        bsf            SSPCON2,ACKEN   ; ACKENをセット
        btfsc        SSPCON2,ACKEN
        goto        $-1        ; ACKの送信が終了するループ

NACKの場合、最後の又は1byteだけ受信してスレーブに返す

    banksel            SSPCON2        ;bank1
        bsf             SSPCON2,ACKDT   ; NACKを出力
        bsf            SSPCON2,ACKEN   ; ACKENをセット
        btfsc        SSPCON2,ACKEN
        goto        $-1        ; NACKの送信が終了するループ

	list      	p=16F886	; 翻訳時にリストファイルを作ります
#include 		; 定義ファイルを読み込みます
errorlevel  	-302		; 翻訳時に302エラーが出ないようにします
errorlevel  	-205		; 翻訳時に205エラーが出ないようにします
errorlevel  	-305		; 翻訳時に305エラーが出ないようにします
;***********************************************************************
;サブルーチン、MCP23017を読む
GLOBAL	RESTART,DATA_READ_NACK
;サブルーチンのラベル名がメインプログラムでEXTERNで宣言されて居る
;その為、必ずGLOBALで受ける、つまり EXTERN と GLOBAL はペア
;===============================================================================
CODE	;サブルーチン、ここから
;===============================================================================
RESTART:
;repeated start
banksel		SSPCON2
bsf		SSPCON2,RSEN
btfsc		SSPCON2,RSEN
goto		$-1
RETURN
;===============================================================================
DATA_READ_NACK:
;	I2Cから1byte入力しW_TEMPに格納する。デバイスへはNACKを返す。
;	i2c_last_read	最終1byte入力
BANKSEL		SSPCON2
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_mcp23017_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_mcp23017_write.asm
サブルーチンです
	list      	p=16F886	; 翻訳時にリストファイルを作ります
#include 		; 定義ファイルを読み込みます
errorlevel  	-302		; 翻訳時に302エラーが出ないようにします
errorlevel  	-205		; 翻訳時に205エラーが出ないようにします
errorlevel  	-305		; 翻訳時に305エラーが出ないようにします
;*******************************************************************************
;サブルーチン、MCP23017に書く
GLOBAL	MCP23017_WRITE,GPA_INIT,GPB_INIT
EXTERN	IDLE,START_I2C,STOP_I2C
;サブルーチンのラベル名がメインプログラムでEXTERNで宣言されて居る
;その為、必ずGLOBALで受ける、つまり EXTERN と GLOBAL はペア
;===============================================================================
CODE	;サブルーチン、ここから
;===============================================================================
;*********************SEND MCP23017 CONTROL REGISTER WRITE ****************************
MCP23017_WRITE:
BANKSEL		I2CWDATA	;bank0
MOVF		I2CWDATA,W	;F -> W
MOVWF		SSPBUF		;W -> SSPBUF 直ちに送信開始 BEGIN TRANSMISSION
SENDW4:
banksel		PIR1		;bank0
BCF		PIR1,SSPIF	;
BTFSS		PIR1,SSPIF	;SEND COMPLETED?IF YES SKIP NEXT
GOTO		SENDW4
BCF		PIR1,SSPIF	;YES,CLEAR FLAG
BANKSEL		SSPCON2
BTFSC		SSPCON2,ACKSTAT	;ACK RECEIVED FROM SLAVE?IF YES SKIP
GOTO		$-1		;IF NO,END
BCF		STATUS,IRP	;Set BANK0
BCF		STATUS,RP1	;Set BANK0
BCF		STATUS,RP0	;Set BANK0
RETURN					;For debug
;*********************	NG INITIATE STOP**************************************** Not in use
CALL		IDLE
SENDY5:
BANKSEL		SSPCON2
BSF		SSPCON2,PEN
BCF		STATUS,RP0
SENDYE:
BTFSS		PIR1,SSPIF
GOTO		SENDYE
BCF		PIR1,SSPIF
;*******************************************************************************
RETURN
;*******************************************************************************
;MCP23017WRITE_INITIALIZE_GPA(GPIO)
;--------------------------------------
;GPA(レジスタ0x00=IODIRA)を全出力に設定するルーチン
GPA_INIT:
CALL		IDLE
;スタート・コンディション
CALL		START_I2C
;スレーブアドレスセット
BANKSEL		I2CWDATA
MOVLW   	WSADRS			;data -> W
MOVWF		I2CWDATA		;W -> F
;書き込み
CALL		MCP23017_WRITE
;制御レジスタセット
BANKSEL		I2CWDATA
MOVLW   	WSREG_GPA		;data -> W
MOVWF		I2CWDATA		;W -> F
;書き込み
CALL		MCP23017_WRITE
;制御レジスタへ書くデータセット
BANKSEL		I2CWDATA
MOVLW   	IODIRA_DATA		;data -> W
MOVWF		I2CWDATA		;W -> F
;書き込み
CALL		MCP23017_WRITE
;ストップ・コンディション
CALL		STOP_I2C
RETURN
;===============================================================================
;MCP23017WRITE_INITIALIZE_GPB(GPIO)
;--------------------------------------
;GPB(レジスタ0x01=IODIRA)を全入力に設定するルーチン
GPB_INIT:
CALL		IDLE
;スタート・コンディション
CALL		START_I2C
;スレーブアドレスセット
BANKSEL		I2CWDATA
MOVLW   	WSADRS			;data -> W
MOVWF		I2CWDATA		;W -> F
;書き込み
CALL		MCP23017_WRITE
;制御レジスタセット
BANKSEL		I2CWDATA
MOVLW   	WSREG_GPB		;data -> W
MOVWF		I2CWDATA		;W -> F
;書き込み
CALL		MCP23017_WRITE
;制御レジスタへ書くデータセット
BANKSEL		I2CWDATA
MOVLW   	IODIRB_DATA		;data -> W
MOVWF		I2CWDATA		;W -> F
;書き込み
CALL		MCP23017_WRITE
;ストップ・コンディション
CALL		STOP_I2C
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

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です