SSブログ

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




nice!(0)  コメント(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

PIC16F886 24LC64 EE..PIC16F886--I2C-- S-5.. ブログトップ

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。