MCP23017 はMicroChip社のIO-Expanderです
8+8bitのI/Oを持ちI2Cで制御します
アドレスを変えることで合計8個並列に並べる事が出来ます
1個辺り16bitですから128の入出力が出来ます
然かも秋月電子では1個
¥110で
¥110で
8bitのIO-Expander PCF8574、1個
¥130より安いです
¥130より安いです
↓
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″にすると反転します
今回も泥臭い乍らアセンブラで作りました
回路はシンプルです少しの外付けで完成します
↓
緑色のLEDは出力、INTA / INTB です、ここでは使用していませんが
とても柔軟に設定出来る様に成っていて読んでいるととても奥が深く
様々な要求から生まれたのだろうと感じ取れます
↓
ホスト側はPIC16F886です
↓
先ずは動画です
MCP23017 の GPIO-GPAを出力に設定
出力に対して0x00→0x01→0x02・・・0xFF→0x00→・・・
を繰り返しているだけのLチカです
また GPIO-GPBを入力として読み込んだデータを
ホスト側PIC16F886のPORTBに出力しLEDでモニタします
仕事としては無意味ですが、まぁやらないより益しという事で・・・
↓
回路図、MCP23017基板
↓
回路図、PIC16F886基板
↓
データの詳細です、もっと詳しくはデータブックをダウンロード
してご確認下さい
スレーブアドレス、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→ストップ
↓
次ぎはGPB制御アドレス、0x01(IODIRB)を全入力に設定します
これは実はやらなくても良いのです、理由は電源投入後、自動的に入力
設定に成るからです、デフォルトは全入力だという事です、が、敢えて
実験のためにイニシャライズしてます
スタート→スレーブアドレス0x40→ACK→制御レジスタ0x01(IODIRB)→
ACK→0xFF(全入力)→ACK→ストップ
↓
次ぎは実際にGPAにデータ出力します
GPA出力アドレス、0x12(GPIOA) 又は 0x14(OLATA) どっちでも出ます
スタート→スレーブアドレス0x40→ACK→GPAレジスタ0x14(OLATA)→ACK→
連続データ0x00~0xFF(繰り返し)→ACK→ストップ→LOOP
↓
次ぎはGPBからのデータ入力を読み出します
GPB入力アドレス、0x13(GPIOB)
スタート→スレーブアドレス0x40→ACK→GPBレジスタ0x13(GPIOB)→ACK→
リスタート→スレーブアドレス0x41(READ)→ACK→読み出しデータ→NACK→ストップ→LOOP
↓
I2CのSCL / SDA の波形です、次のような流れです
GPAに出力 → GPBを読み込む → PIC16F886-PORTBのLEDでモニタ
これを繰り返しています
0x40→0x14→出力DATA 続いて 0x40→0x13→0x41→入力DATA
↓
ACKですがデータシートでは抽象的に「このタイミングですよ」
みたいに波形が書かれているだけですよね、それは真実だし
そうでなければならないのは判るんですが実験していると
ちゃんとACK返ってきているのか、特に色々なデバイスが
I2C-BUSラインにぶら下がって居ると何か決定的な手掛かりが
欲しくなるのです、そういう時は矢張りオシロスコープが
一番です、詰まりデジタル的なロジックは判っていても
実際のアナログ的な部分を確認するためには必須ですよね
実際の波形を視てみましょう
↓
この電圧軸だとACKらしき足跡が見えませんが、電圧軸を拡大すると
視えて来ます、そうそう、これがACKの足跡なのです
特に双方向 BUS BUFFER 等の回路を設計する際にはこの足跡が
大変重要性を帯びてきます
↓
ソースファイルです、下記の構成です
・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 はスレーブ → マスターの方向ですが逆の場合、混乱しやすい性質を
帯びて居ます、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