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

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


2008-12-30

MCP23S17はMCP23017のI2CインターフェースがSPIに変わっただけの物です。SPIのほうが高速通信が可能です。最大10Mbpsで通信可能ですが20MHz動作のPICではクロックの1/4の5Mbpsが最大速度です。

PICT8481.jpg

Microchip社 MCP23S17

PICT8478.jpg

実験風景 5Mbpsはブレッドボードでもぎりぎり大丈夫です。これ以上の速度になるとちょっと無理があるかもしれません。

5Mbpsともなると、20MHz動作のPICでは送信データの準備が間に合わなくなり、データが密に送信できなくなります。これ以上高速で送受信できる必要性は殆どないでしょう。mikroBasicのサブルーチンコールオーバーヘッドのため間隔が空いてしまうので、もっと密に(=高速に)送信したい場合はPICのレジスタを直接操作する必要があります。

PICT8474.jpg

5MbpsでのSPIクロック(一番上の信号) A-B間は0.2μ秒

PICT8476.jpg

5Mbpsでの3バイトデータ送信。これは下のプログラムの120~122行目のもの。

実験回路図。以下のプログラムはこの回路用のソフトです。この回路はPICに1つだけSPIデバイス(MCP23S17)を繋いでいますが、CS(チップセレクト)で選択されたデバイスのみが反応するので複数個繋ぐことも出来ます。SPIの2本有る入力、出力ピンをマスター(PIC)<->スレーブ(MCP23S17)間でクロス配線するところがポイントです。

mcp23s17sch.GIF
プログラム & 回路図
ファイル ファイルタイプ 添付ファイルの解説
MCP23S17_mikrobasic.zip OTHER MikroBASIC V7.2ソース&HEXファイル一式
MCP23S17_eagle.zip EAGLE 回路図 上のイメージの物と同じです (基板パターンは有りません)
プログラムの解説
0001  '--------------------------------------------------------- 2008-12-29 ----------
0002 ' MCP23S17 I/O Expander
0003 '-------------------------------------------------------------------------------
0004 program MCP23S17
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 MCP23S17_ADDRESS = 0x40
0073 symbol INTA = 5
0074 symbol INTB = 4
0075 symbol MCP23S17_CS_TRIS = TRISB
0076 symbol MCP23S17_CS_PORT = PORTB
0077 symbol MCP23S17_CS_BIT = 2
0078
0079 symbol ENUM_DIRECTION = 0 ' I/O DIRECTION
0080 symbol ENUM_POLARITY = 1 ' INPUT POLARITY
0081 symbol ENUM_INTERRUPT = 2 ' INTERRUPT-ON-CHANGE CONTROL
0082 symbol ENUM_DEFAULT = 3 ' DEFAULT COMPARE REGISTER FOR INTERRUPT-ON-CHANGE
0083 symbol ENUM_INTCON = 4 ' INTERRUPT CONTROL
0084 symbol ENUM_PULLUP = 5 ' PULL-UP CONFIGURATION
0085
0086 symbol PORT_OUTPUT = 0
0087 symbol PORT_INPUT = 1
0088
0089 dim mcp23S17_int_PORTA as boolean
0090 dim mcp23S17_int_PORTB as boolean
0091
0092 dim dummy as byte
0093
0094 '===============================================================================
0095 ' Interrupt
0096 '===============================================================================
0097 sub procedure interrupt
0098 if INTCON.RBIF = 1 then
0099 dummy = PORTB
0100 INTCON.RBIF = 0
0101 if PORTB.INTA = 0 then
0102 mcp23S17_int_PORTA = True
0103 end if
0104 if PORTB.INTB = 0 then
0105 mcp23S17_int_PORTB = True
0106 end if
0107 end if
0108 end sub
0109
0110
0111 '-------------------------------------------------------------------------------
0112 ' MCP23S17 SPI Write
0113 '-------------------------------------------------------------------------------
0114 sub procedure mcp23S17_SPI_Write(
0115 dim hw_address as byte,
0116 dim reg_address as byte,
0117 dim value as byte )
0118
0119 MCP23S17_CS_PORT.MCP23S17_CS_BIT = 0
0120 Spi_Write(hw_address)
0121 Spi_Write(reg_address)
0122 Spi_Write(value)
0123 MCP23S17_CS_PORT.MCP23S17_CS_BIT = 1
0124 end sub
0125
0126 '-------------------------------------------------------------------------------
0127 ' MCP23S17 SPI Read
0128 '-------------------------------------------------------------------------------
0129 sub function mcp23S17_SPI_Read(
0130 dim hw_address as byte,
0131 dim reg_address as byte ) as byte
0132
0133 MCP23S17_CS_PORT.MCP23S17_CS_BIT = 0
0134 Spi_Write(hw_address or 0x01 )
0135 Spi_Write(reg_address)
0136 result = Spi_Read(0)
0137 MCP23S17_CS_PORT.MCP23S17_CS_BIT = 1
0138 end sub
0139
0140 '-------------------------------------------------------------------------------
0141 ' MCP23S17 initialize
0142 '-------------------------------------------------------------------------------
0143 sub procedure mcp23S17_init( dim hw_address as byte )
0144 dim IOCON_val as byte
0145
0146 IOCON_val = 0x00
0147 IOCON_val.ODR = OPEN_DRAIN
0148
0149 mcp23S17_SPI_Write(hw_address,IOCON_ADDR,IOCON_val)
0150 end sub
0151
0152 '-------------------------------------------------------------------------------
0153 ' MCP23S17 rewrite register
0154 '-------------------------------------------------------------------------------
0155 sub procedure mcp23S17_rewrite_register(
0156 dim hw_address as byte,
0157 dim register_addr as byte,
0158 dim register_bit as byte,
0159 dim value as byte )
0160
0161 dim reg_val as byte
0162
0163 reg_val = mcp23S17_SPI_Read(hw_address,register_addr)
0164 reg_val.register_bit = value
0165 mcp23S17_SPI_Write(hw_address,register_addr,reg_val)
0166 end sub
0167
0168 '-------------------------------------------------------------------------------
0169 ' MCP23S17 initialize
0170 '-------------------------------------------------------------------------------
0171 sub procedure mcp23S17_config(
0172 dim hw_address as byte,
0173 dim port as byte,
0174 dim bit as byte,
0175 dim config_value as byte )
0176
0177 select case port
0178 case MCP23x17_PORT_A
0179 mcp23S17_rewrite_register(hw_address,IODIRA_ADDR ,bit,((config_value >> ENUM_DIRECTION) and 0x01))
0180 mcp23S17_rewrite_register(hw_address,IPOLA_ADDR ,bit,((config_value >> ENUM_POLARITY ) and 0x01))
0181 mcp23S17_rewrite_register(hw_address,GPINTENA_ADDR,bit,((config_value >> ENUM_INTERRUPT) and 0x01))
0182 mcp23S17_rewrite_register(hw_address,DEFVALA_ADDR ,bit,((config_value >> ENUM_DEFAULT ) and 0x01))
0183 mcp23S17_rewrite_register(hw_address,INTCONA_ADDR ,bit,((config_value >> ENUM_INTCON ) and 0x01))
0184 mcp23S17_rewrite_register(hw_address,GPPUA_ADDR ,bit,((config_value >> ENUM_PULLUP ) and 0x01))
0185 case MCP23x17_PORT_B
0186 mcp23S17_rewrite_register(hw_address,IODIRB_ADDR ,bit,((config_value >> ENUM_DIRECTION) and 0x01))
0187 mcp23S17_rewrite_register(hw_address,IPOLB_ADDR ,bit,((config_value >> ENUM_POLARITY ) and 0x01))
0188 mcp23S17_rewrite_register(hw_address,GPINTENB_ADDR,bit,((config_value >> ENUM_INTERRUPT) and 0x01))
0189 mcp23S17_rewrite_register(hw_address,DEFVALB_ADDR ,bit,((config_value >> ENUM_DEFAULT ) and 0x01))
0190 mcp23S17_rewrite_register(hw_address,INTCONB_ADDR ,bit,((config_value >> ENUM_INTCON ) and 0x01))
0191 mcp23S17_rewrite_register(hw_address,GPPUB_ADDR ,bit,((config_value >> ENUM_PULLUP ) and 0x01))
0192 end select
0193 end sub
0194
0195
0196 '-------------------------------------------------------------------------------
0197 ' MCP23S17 output
0198 '
0199 ' argument
0200 ' hw_address : MCP23S17 H/W address
0201 ' port : PORT_A or PORT_B
0202 ' bit : bit position
0203 ' value : write value
0204 '-------------------------------------------------------------------------------
0205 sub procedure mcp23S17_out(
0206 dim hw_address as byte,
0207 dim port as byte,
0208 dim bit as byte,
0209 dim value as byte )
0210
0211 select case port
0212 case MCP23x17_PORT_A
0213 mcp23S17_rewrite_register(hw_address,OLATA_ADDR,bit,value)
0214 case MCP23x17_PORT_B
0215 mcp23S17_rewrite_register(hw_address,OLATB_ADDR,bit,value)
0216 end select
0217 end sub
0218
0219 '-------------------------------------------------------------------------------
0220 ' MCP23S17 input BYTE
0221 '
0222 ' argument
0223 ' hw_address : MCP23S17 H/W address
0224 ' port : PORT_A or PORT_B
0225 ' return
0226 ' value : write value
0227 '-------------------------------------------------------------------------------
0228 sub function mcp23S17_in(
0229 dim hw_address as byte,
0230 dim port as byte) as byte
0231
0232 select case port
0233 case MCP23x17_PORT_A
0234 result = mcp23S17_SPI_Read(hw_address,GPIOA_ADDR)
0235 case MCP23x17_PORT_B
0236 result = mcp23S17_SPI_Read(hw_address,GPIOB_ADDR)
0237 end select
0238 end sub
0239
0240 '-------------------------------------------------------------------------------
0241 ' MCP23S17 input BIT
0242 '
0243 ' argument
0244 ' hw_address : MCP23S17 H/W address
0245 ' port : PORT_A or PORT_B
0246 ' bit : bit position
0247 ' return
0248 ' value : write value
0249 '-------------------------------------------------------------------------------
0250 sub function mcp23S17_in_bit(
0251 dim hw_address as byte,
0252 dim port as byte,
0253 dim bit as byte) as byte
0254
0255 dim tmp_byte as byte
0256
0257 tmp_byte = mcp23S17_in(hw_address,port)
0258 result = (tmp_byte >> bit) and 0x01
0259 end sub
0260
0261 '===============================================================================
0262 ' program MAIN
0263 '===============================================================================
0264 main:
0265 dim config_type_LED as byte
0266 dim config_type_SWITCH as byte
0267
0268 dim led_red_L as byte
0269 dim led_red_R as byte
0270
0271 '---- disable port B pull-up -------------
0272 OPTION_REG.NOT_RBPU = 1
0273
0274 TRISB.3 = PORT_OUTPUT ' MCP23S17 Reset
0275 PORTB.3 = 1
0276
0277 MCP23S17_CS_TRIS.MCP23S17_CS_BIT = PORT_OUTPUT ' MCP23S17 ChipSelect
0278 MCP23S17_CS_PORT.MCP23S17_CS_BIT = 1
0279 Delay_ms(10)
0280
0281 TRISB.7 = PORT_OUTPUT
0282 TRISB.6 = PORT_OUTPUT
0283 TRISB.5 = PORT_INPUT
0284 TRISB.4 = PORT_INPUT
0285
0286 Spi_Init
0287
0288 mcp23S17_init( MCP23S17_ADDRESS )
0289
0290 config_type_LED.ENUM_DIRECTION = DIRECTION_OUTPUT
0291 config_type_LED.ENUM_POLARITY = POLARITY_SAME
0292 config_type_LED.ENUM_INTERRUPT = INTERRUPT_DISABLE
0293 config_type_LED.ENUM_PULLUP = PULLUP_DISABLE
0294 mcp23S17_config( MCP23S17_ADDRESS, MCP23x17_PORT_A, 6, config_type_LED )
0295 mcp23S17_config( MCP23S17_ADDRESS, MCP23x17_PORT_B, 1, config_type_LED )
0296
0297 config_type_SWITCH.ENUM_DIRECTION = DIRECTION_INPUT
0298 config_type_SWITCH.ENUM_POLARITY = POLARITY_OPPOSITE
0299 config_type_SWITCH.ENUM_INTERRUPT = INTERRUPT_ENABLE
0300 config_type_SWITCH.ENUM_PULLUP = PULLUP_DISABLE
0301 config_type_SWITCH.ENUM_DEFAULT = DEFAULT_SET
0302 config_type_SWITCH.ENUM_INTCON = INTCON_PREVIOUS
0303 mcp23S17_config( MCP23S17_ADDRESS, MCP23x17_PORT_B, 0, config_type_SWITCH )
0304 mcp23S17_config( MCP23S17_ADDRESS, MCP23x17_PORT_A, 7, config_type_SWITCH )
0305
0306 #ifdef USE_INTERRUPT
0307 INTCON.RBIE = 1
0308 INTCON.RBIF = 0 ' Clear PORTB interrupt flag
0309 INTCON.GIE = 1 ' Enable interrupts
0310 #endif
0311
0312 '---dummy read INTA and INTB LOW -> HIGH
0313 led_red_R = mcp23S17_in_bit( MCP23S17_ADDRESS, MCP23x17_PORT_A, 7 )
0314 led_red_L = mcp23S17_in_bit( MCP23S17_ADDRESS, MCP23x17_PORT_B, 0 )
0315
0316 mcp23S17_int_PORTA = False
0317 mcp23S17_int_PORTB = False
0318 while( TRUE )
0319 #ifdef USE_INTERRUPT
0320 if mcp23S17_int_PORTA = True then
0321 mcp23S17_int_PORTA = False
0322 #else
0323 if PORTB.INTA = 0 then
0324 #endif
0325 led_red_R = mcp23S17_in_bit( MCP23S17_ADDRESS, MCP23x17_PORT_A, 7 )
0326 mcp23S17_out( MCP23S17_ADDRESS, MCP23x17_PORT_A, 6, led_red_R )
0327 end if
0328
0329 #ifdef USE_INTERRUPT
0330 if mcp23S17_int_PORTB = True then
0331 mcp23S17_int_PORTB = False
0332 #else
0333 if PORTB.INTB = 0 then
0334 #endif
0335 led_red_L = mcp23S17_in_bit( MCP23S17_ADDRESS, MCP23x17_PORT_B, 0 )
0336 mcp23S17_out( MCP23S17_ADDRESS, MCP23x17_PORT_B, 1, led_red_L )
0337 end if
0338 wend
0339 end.
0340 '-------- E N D O F P R O G R A M -----------------------------------------
行番号
解説
111~138
I2CインターフェースのMCP23017と大きく違うのは、この部分だけです。それ以外は殆ど同じです。