素子:I/Oエキスパンダ MCP23017

I/OエキスパンダはPICのI/Oポートが不足したときに、それを拡張するための物です。MCP23017の特徴は以下の通りです。
 ・PICとの接続はI2C
 ・通信速度は最大1Mbps
 ・入出力が16本拡張される
 ・PICより安価 <-最近PICが安くなっているので、アマチュアにはあまりメリット無し
 ・I2Cの勉強用に良い
I2CのEEPROM等は書き込んだ後に読み出してみないと正しく通信できているのか確認できませんが、このI/OエキスパンダはI2Cで書き込んだ値をピンにそのまま出力できるので、動作確認が容易です。


2008-12-29

MCP23017はI2Cインターフェースを備えたI/Oエキスパンダです。I2CがあればPICでなくても使用できます。

PICT8468.jpg

28ピンで16I/Oだけ増やせる。ちょっと少ないが電源やI2Cのアドレスピンがあるのでやむなし。

PICT8465.jpg

最大通信速度は1.7MbpsだがPICは1Mbpsまで

===============================================================================
 ◆機能
===============================================================================

 ・電源投入時は全てのピンは入力モードになっているので、電源を入れただけでピンに繋がっている
  I/OがONしないようになっている。これはPICと同じ。
 ・16本ある全てのピンに個別に設定可能な項目
   -入力/出力の設定
   -ロジックレベルとピンの電圧を逆転できる
    例えば出力ピンに 1を書き込んだときLOW,0を書き込んだとき電圧をHIGH出力に出来る
    入力でも同様、スイッチのようにプルアップして使用する物は逆転させておくとON=1,OFF=0として扱えるので便利
   -入力ピンに変化が有ったとき割り込みをかけるか否か
   -割り込み種類の選択
     変化割り込み <- 直前の値との比較
     立ち上がりエッジでの割り込み
     立ち下がりエッジでの割り込み
     (機能上は立ち下がり、立ち上がりではなくDEFVALレジスタとの比較)
   -内部100kΩ抵抗によるプルアップ
   -割り込みが発生したときどのピンで割り込みが起こったのか
    PICでいう xxxIF フラグと同じ
 ・2つあるINTピン(INTA,INTB)はソフトで繋ぐことが出来るのでハードでワイヤードOR結線する必要はありません
  結線すると、どちらのポートに変化があったのかを調べるためにA,B双方を調べる必要があるので不用意に結線すべきではありません
 ・割り込みは INTCAPレジスタ、もしくはGPIO レジスタを読み出すことにより解除されます
 ・割り込みピンINTA,INTBはそれぞれ独立しています

===============================================================================
 ◆注意点
===============================================================================

 ・IOCONレジスタのBANKビットを変更すると全てのレジスターのアドレスが変わります(<-とても奇妙です)
  リセット直後は IOCON.BANK =0 になっています
  データシートのIOCONレジスタの解説にはアドレスがHex05と書いてありますが
  これはリセット直後の値ではありません、リセット直後はHex0Aです

 ・2つあるINTピンはオープンドレインもしくはACTIVE-HIGH/LOW(通常のTTLレベル)に切り替えられます。
  リセット直後はオープンドレインではなくACTIVE-HIGH/LOWになっています

 ・MCP23017のI2Cの速度は最大1.7Mbpsまでですが、PICは1Mbpsが最大です。
  I2Cの速度は 100kbps、400kbps、1Mbps の3種類です。
  (ハイエンドのPICは1.7Mbpsまでいけるのかもしれません)

 ・INTA,INTBをPICのポート7~4のどれかに結線して、ポート変化割り込み(RB7-RB4)を使用しない方がよい
  ポート変化割り込みはPORTBを読むことで解除されるが、その間にMCP23017が割り込みを発生させると
  割り込みを取り損なう可能性がある。
  無理に割り込みを使用せず、メインループの中でINTA,INTBを監視し、INTA,INTBがLOWになったら
  MCP23017からデータを読み出すようにする方が良い。

MikroBASICで実験してみたところ1.7MbpsではI2Cのクロックは出ますがデータが出ません。1MbpsまではOKでした。使用したPICはPIC16F877A。

PICT8464.jpg

通信速度1.7Mbps。SDA信号が出ない。

PICT8466.jpg

最大通信速度は1Mbps

実験回路図です。以下のソフトはこの回路で動作確認しました。
LED1,LED2は割り込み信号の状況をモニターする物で実際にはなくても動きます。またINTA,INTBはオープンドレインモードで使用しています。そのためR6,R7が必要です。オープンドレインモードでなければ、この抵抗を取り去ることが出来ます。

mcp23017sch.GIF

手前のケースに入った箱はロジアナです。奥はPICkit2クローン。

PICT8471.jpg

実験風景

プログラム & 動いているところ & 回路図
ファイル ファイルタイプ 添付ファイルの解説
MCP23017_mikrobasic.zip OTHER MikroBASIC V7.2ソース&HEXファイル一式
mcp23017mov.wmv MOVIE 動いているところ
MCP23017_eagle.zip EAGLE 回路図 上のイメージの物と同じです (基板パターンは有りません)
プログラムの解説
0001  '--------------------------------------------------------- 2008-12-29 ----------
0002 ' MCP23017 I/O Expander
0003 '-------------------------------------------------------------------------------
0004 program MCP23017
0005
0006 #define USE_INTERRUPT
0007
0008 '---- GPIO Port A (IOCON.BANK=0)------
0009 symbol IODIRA_ADDR = 0x00
0010 symbol IPOLA_ADDR = 0x02
0011 symbol GPINTENA_ADDR = 0x04
0012 symbol DEFVALA_ADDR = 0x06
0013 symbol INTCONA_ADDR = 0x08
0014 symbol IOCON_ADDR = 0x0A
0015 symbol GPPUA_ADDR = 0x0C
0016 symbol INTFA_ADDR = 0x0E
0017 symbol INTCAPA_ADDR = 0x10
0018 symbol GPIOA_ADDR = 0x12
0019 symbol OLATA_ADDR = 0x14
0020 '---- GPIO Port B (IOCON.BANK=0)------
0021 symbol IODIRB_ADDR = 0x01
0022 symbol IPOLB_ADDR = 0x03
0023 symbol GPINTENB_ADDR = 0x05
0024 symbol DEFVALB_ADDR = 0x07
0025 symbol INTCONB_ADDR = 0x09
0026 symbol GPPUB_ADDR = 0x0D
0027 symbol INTFB_ADDR = 0x0F
0028 symbol INTCAPB_ADDR = 0x11
0029 symbol GPIOB_ADDR = 0x13
0030 symbol OLATB_ADDR = 0x15
0031
0032 '----- I/O DIRECTION REGISTER
0033 symbol DIRECTION_INPUT = 1
0034 symbol DIRECTION_OUTPUT = 0
0035
0036 '----- INPUT POLARITY REGISTER
0037 symbol POLARITY_OPPOSITE = 1
0038 symbol POLARITY_SAME = 0
0039
0040 '----- INTERRUPT-ON-CHANGE CONTROL REGISTER
0041 symbol INTERRUPT_ENABLE = 1
0042 symbol INTERRUPT_DISABLE = 0
0043
0044 '----- DEFAULT COMPARE REGISTER FOR INTERRUPT-ON-CHANGE
0045 symbol DEFAULT_SET = 1
0046 symbol DEFAULT_CLEAR = 0
0047
0048 '----- INTERRUPT CONTROL REGISTER
0049 symbol INTCON_DEFAULT = 1
0050 symbol INTCON_PREVIOUS = 0
0051
0052 '----- PULL-UP CONFIGURATION REGISTER
0053 symbol PULLUP_ENABLE = 1
0054 symbol PULLUP_DISABLE = 0
0055
0056 symbol MCP23x17_PORT_A = 1
0057 symbol MCP23x17_PORT_B = 2
0058
0059 '----- IOCON Register bit -----------------------------------------
0060 symbol BANK = 7 ' Controls how the registers are addressed
0061 symbol MIRROR = 6 ' INT Pins Mirror bit
0062 symbol SEQOP = 5 ' Sequential Operation mode bit
0063 symbol DISSLW = 4 ' Slew Rate control bit for SDA output
0064 symbol HAEN = 3 ' Hardware Address Enable bit (MCP23S17 only)
0065 symbol ODR = 2 ' Configures the INT pin as an open-drain output
0066 symbol INTPOL = 1 ' Sets the polarity of the INT output pin
0067
0068 symbol OPEN_DRAIN = 1
0069 symbol ACTIVE_DRIVER = 0
0070
0071 '----- User Hardware Depend ----------------------------
0072 symbol MCP23017_ADDRESS = 0x40
0073 symbol INTA = 5
0074 symbol INTB = 4
0075
0076 symbol ENUM_DIRECTION = 0 ' I/O DIRECTION
0077 symbol ENUM_POLARITY = 1 ' INPUT POLARITY
0078 symbol ENUM_INTERRUPT = 2 ' INTERRUPT-ON-CHANGE CONTROL
0079 symbol ENUM_DEFAULT = 3 ' DEFAULT COMPARE REGISTER FOR INTERRUPT-ON-CHANGE
0080 symbol ENUM_INTCON = 4 ' INTERRUPT CONTROL
0081 symbol ENUM_PULLUP = 5 ' PULL-UP CONFIGURATION
0082
0083 symbol PORT_OUTPUT = 0
0084 symbol PORT_INPUT = 1
0085
0086 dim mcp23017_int_PORTA as boolean
0087 dim mcp23017_int_PORTB as boolean
0088
0089 dim dummy as byte
0090
0091 '===============================================================================
0092 ' Interrupt
0093 '===============================================================================
0094 sub procedure interrupt
0095 if INTCON.RBIF = 1 then
0096 dummy = PORTB
0097 INTCON.RBIF = 0
0098 if PORTB.INTA = 0 then
0099 mcp23017_int_PORTA = True
0100 end if
0101 if PORTB.INTB = 0 then
0102 mcp23017_int_PORTB = True
0103 end if
0104 end if
0105 end sub
0106
0107
0108 '-------------------------------------------------------------------------------
0109 ' MCP23017 I2C Write
0110 '-------------------------------------------------------------------------------
0111 sub procedure mcp23017_I2C_Write(
0112 dim hw_address as byte,
0113 dim reg_address as byte,
0114 dim value as byte )
0115
0116 I2C_Start
0117 I2C_Wr(hw_address)
0118 I2C_Wr(reg_address)
0119 I2C_Wr(value)
0120 I2C_Stop
0121 end sub
0122
0123 '-------------------------------------------------------------------------------
0124 ' MCP23017 I2C Read
0125 '-------------------------------------------------------------------------------
0126 sub function mcp23017_I2C_Read(
0127 dim hw_address as byte,
0128 dim reg_address as byte ) as byte
0129
0130 I2C_Start
0131 I2C_Wr(hw_address)
0132 I2C_Wr(reg_address)
0133 I2C_Repeated_Start
0134 I2C_Wr(hw_address or 0x01 )
0135 result = I2C_Rd(0)
0136 I2C_Stop
0137 end sub
0138
0139 '-------------------------------------------------------------------------------
0140 ' MCP23017 initialize
0141 '-------------------------------------------------------------------------------
0142 sub procedure mcp23017_init( dim hw_address as byte )
0143 dim IOCON_val as byte
0144
0145 IOCON_val = 0x00
0146 IOCON_val.ODR = OPEN_DRAIN
0147
0148 mcp23017_I2C_Write(hw_address,IOCON_ADDR,IOCON_val)
0149 end sub
0150
0151 '-------------------------------------------------------------------------------
0152 ' MCP23017 rewrite register
0153 '-------------------------------------------------------------------------------
0154 sub procedure mcp23017_rewrite_register(
0155 dim hw_address as byte,
0156 dim register_addr as byte,
0157 dim register_bit as byte,
0158 dim value as byte )
0159
0160 dim reg_val as byte
0161
0162 reg_val = mcp23017_I2C_Read(hw_address,register_addr)
0163 reg_val.register_bit = value
0164 mcp23017_I2C_Write(hw_address,register_addr,reg_val)
0165 end sub
0166
0167 '-------------------------------------------------------------------------------
0168 ' MCP23017 initialize
0169 '-------------------------------------------------------------------------------
0170 sub procedure mcp23017_config(
0171 dim hw_address as byte,
0172 dim port as byte,
0173 dim bit as byte,
0174 dim config_value as byte )
0175
0176 select case port
0177 case MCP23x17_PORT_A
0178 mcp23017_rewrite_register(hw_address,IODIRA_ADDR ,bit,((config_value >> ENUM_DIRECTION) and 0x01))
0179 mcp23017_rewrite_register(hw_address,IPOLA_ADDR ,bit,((config_value >> ENUM_POLARITY ) and 0x01))
0180 mcp23017_rewrite_register(hw_address,GPINTENA_ADDR,bit,((config_value >> ENUM_INTERRUPT) and 0x01))
0181 mcp23017_rewrite_register(hw_address,DEFVALA_ADDR ,bit,((config_value >> ENUM_DEFAULT ) and 0x01))
0182 mcp23017_rewrite_register(hw_address,INTCONA_ADDR ,bit,((config_value >> ENUM_INTCON ) and 0x01))
0183 mcp23017_rewrite_register(hw_address,GPPUA_ADDR ,bit,((config_value >> ENUM_PULLUP ) and 0x01))
0184 case MCP23x17_PORT_B
0185 mcp23017_rewrite_register(hw_address,IODIRB_ADDR ,bit,((config_value >> ENUM_DIRECTION) and 0x01))
0186 mcp23017_rewrite_register(hw_address,IPOLB_ADDR ,bit,((config_value >> ENUM_POLARITY ) and 0x01))
0187 mcp23017_rewrite_register(hw_address,GPINTENB_ADDR,bit,((config_value >> ENUM_INTERRUPT) and 0x01))
0188 mcp23017_rewrite_register(hw_address,DEFVALB_ADDR ,bit,((config_value >> ENUM_DEFAULT ) and 0x01))
0189 mcp23017_rewrite_register(hw_address,INTCONB_ADDR ,bit,((config_value >> ENUM_INTCON ) and 0x01))
0190 mcp23017_rewrite_register(hw_address,GPPUB_ADDR ,bit,((config_value >> ENUM_PULLUP ) and 0x01))
0191 end select
0192 end sub
0193
0194
0195 '-------------------------------------------------------------------------------
0196 ' MCP23017 output
0197 '
0198 ' argument
0199 ' hw_address : MCP23017 H/W address
0200 ' port : PORT_A or PORT_B
0201 ' bit : bit position
0202 ' value : write value
0203 '-------------------------------------------------------------------------------
0204 sub procedure mcp23017_out(
0205 dim hw_address as byte,
0206 dim port as byte,
0207 dim bit as byte,
0208 dim value as byte )
0209
0210 select case port
0211 case MCP23x17_PORT_A
0212 mcp23017_rewrite_register(hw_address,OLATA_ADDR,bit,value)
0213 case MCP23x17_PORT_B
0214 mcp23017_rewrite_register(hw_address,OLATB_ADDR,bit,value)
0215 end select
0216 end sub
0217
0218 '-------------------------------------------------------------------------------
0219 ' MCP23017 input BYTE
0220 '
0221 ' argument
0222 ' hw_address : MCP23017 H/W address
0223 ' port : PORT_A or PORT_B
0224 ' return
0225 ' value : write value
0226 '-------------------------------------------------------------------------------
0227 sub function mcp23017_in(
0228 dim hw_address as byte,
0229 dim port as byte) as byte
0230
0231 select case port
0232 case MCP23x17_PORT_A
0233 result = mcp23017_I2C_Read(hw_address,GPIOA_ADDR)
0234 case MCP23x17_PORT_B
0235 result = mcp23017_I2C_Read(hw_address,GPIOB_ADDR)
0236 end select
0237 end sub
0238
0239 '-------------------------------------------------------------------------------
0240 ' MCP23017 input BIT
0241 '
0242 ' argument
0243 ' hw_address : MCP23017 H/W address
0244 ' port : PORT_A or PORT_B
0245 ' bit : bit position
0246 ' return
0247 ' value : write value
0248 '-------------------------------------------------------------------------------
0249 sub function mcp23017_in_bit(
0250 dim hw_address as byte,
0251 dim port as byte,
0252 dim bit as byte) as byte
0253
0254 dim tmp_byte as byte
0255
0256 tmp_byte = mcp23017_in(hw_address,port)
0257 result = (tmp_byte >> bit) and 0x01
0258 end sub
0259
0260 '===============================================================================
0261 ' program MAIN
0262 '===============================================================================
0263 main:
0264 dim config_type_LED as byte
0265 dim config_type_SWITCH as byte
0266
0267 dim led_red_L as byte
0268 dim led_red_R as byte
0269
0270 '---- disable port B pull-up -------------
0271 OPTION_REG.NOT_RBPU = 1
0272
0273 TRISB.3 = PORT_OUTPUT ' MCP23017 Reset
0274 PORTB.3 = 1
0275
0276 TRISB.7 = PORT_OUTPUT
0277 TRISB.6 = PORT_OUTPUT
0278 TRISB.5 = PORT_INPUT
0279 TRISB.4 = PORT_INPUT
0280
0281 I2C_Init(400000)
0282
0283 mcp23017_init( MCP23017_ADDRESS )
0284
0285 config_type_LED.ENUM_DIRECTION = DIRECTION_OUTPUT
0286 config_type_LED.ENUM_POLARITY = POLARITY_SAME
0287 config_type_LED.ENUM_INTERRUPT = INTERRUPT_DISABLE
0288 config_type_LED.ENUM_PULLUP = PULLUP_DISABLE
0289 mcp23017_config( MCP23017_ADDRESS, MCP23x17_PORT_A, 6, config_type_LED )
0290 mcp23017_config( MCP23017_ADDRESS, MCP23x17_PORT_B, 1, config_type_LED )
0291
0292 config_type_SWITCH.ENUM_DIRECTION = DIRECTION_INPUT
0293 config_type_SWITCH.ENUM_POLARITY = POLARITY_OPPOSITE
0294 config_type_SWITCH.ENUM_INTERRUPT = INTERRUPT_ENABLE
0295 config_type_SWITCH.ENUM_PULLUP = PULLUP_DISABLE
0296 config_type_SWITCH.ENUM_DEFAULT = DEFAULT_SET
0297 config_type_SWITCH.ENUM_INTCON = INTCON_PREVIOUS
0298 mcp23017_config( MCP23017_ADDRESS, MCP23x17_PORT_B, 0, config_type_SWITCH )
0299 mcp23017_config( MCP23017_ADDRESS, MCP23x17_PORT_A, 7, config_type_SWITCH )
0300
0301 #ifdef USE_INTERRUPT
0302 INTCON.RBIE = 1
0303 INTCON.RBIF = 0 ' Clear PORTB interrupt flag
0304 INTCON.GIE = 1 ' Enable interrupts
0305 #endif
0306
0307 '---dummy read INTA and INTB LOW -> HIGH
0308 led_red_R = mcp23017_in_bit( MCP23017_ADDRESS, MCP23x17_PORT_A, 7 )
0309 led_red_L = mcp23017_in_bit( MCP23017_ADDRESS, MCP23x17_PORT_B, 0 )
0310
0311 mcp23017_int_PORTA = False
0312 mcp23017_int_PORTB = False
0313 while( TRUE )
0314 #ifdef USE_INTERRUPT
0315 if mcp23017_int_PORTA = True then
0316 mcp23017_int_PORTA = False
0317 #else
0318 if PORTB.INTA = 0 then
0319 #endif
0320 led_red_R = mcp23017_in_bit( MCP23017_ADDRESS, MCP23x17_PORT_A, 7 )
0321 mcp23017_out( MCP23017_ADDRESS, MCP23x17_PORT_A, 6, led_red_R )
0322 end if
0323
0324 #ifdef USE_INTERRUPT
0325 if mcp23017_int_PORTB = True then
0326 mcp23017_int_PORTB = False
0327 #else
0328 if PORTB.INTB = 0 then
0329 #endif
0330 led_red_L = mcp23017_in_bit( MCP23017_ADDRESS, MCP23x17_PORT_B, 0 )
0331 mcp23017_out( MCP23017_ADDRESS, MCP23x17_PORT_B, 1, led_red_L )
0332 end if
0333 wend
0334 end.
0335 '-------- E N D O F P R O G R A M -----------------------------------------
行番号
解説
6行
この行をコメントにすると割り込み処理を使用しない動作になる。このまま変更しなければポートBの変化割り込みが使用される
8~30
MCP23017のレジスタアドレス
60~66
IOCONレジスタだけはMCP23017全体の動作に関わる物を設定する。これ以外のレジスタは16本有るポートの状態に影響を与える
72~74
この実験回路特有の設定。I2Cのアドレスと、INTA,Bを接続しているPICのPORTBのビット位置
76~81
これはこのプログラム特有の設定、番号に意味はないので。何故ENUM_DIRECTIONが0なのか?といったことを考えてはいけない。単に連番を振っただけ。
86~87
割り込みがかかるとこの変数の値が変わる
96
ポート変化割り込みはポートBを読み込まないと、割り込みがリセットされない
108~
汎用性を持たせたサブルーチンにしてあります
281
通信速度400kbps
308~309
読み込むことでINTA,INTBの割り込みが解除されるので、ダミーリードしている。