Help using the MCP23008
Dr_Acula
Posts: 5,484
I would be most grateful for the forum's collective wisdom using the MCP23008 I/O expander on the I2C bus.
ADDIT - solved post #5
A search on the forum gives this thread http://forums.parallax.com/showthread.php?135052-mcp23008-Sourcing-Voltage
Which finishes with
I was wondering if this author would be able to share the code, or if anyone else has written a driver?
There is a very nice MCP23016 driver in the obex here http://obex.parallax.com/objects/251/
And of course the datasheets http://ww1.microchip.com/downloads/en/DeviceDoc/21919e.pdf for the MCP23008 and http://ww1.microchip.com/downloads/en/DeviceDoc/20090C.pdf for the MCP23016
Please forgive my ignorance though, but my eyes tend to glaze over reading those microchip datasheets
Exactly where are the differences highlighted between a MCP23008 and a MCP23016? Anyway... I find reading code tends to be far more useful. I found some C driver code http://rdpartyrobotcdr.sourceforge.net/documentation/_m_c_p23008-driver_8h.html and source code http://rdpartyrobotcdr.sourceforge.net/documentation/_m_c_p23008-driver_8h_source.html
So the Spin code for a MCP23016 is this
And to make things really simple, I don't even want to read pins, only write to pins. So all I really need is
1) Initialise the chip - default hardware address A0-A2 all low, which I think from the C code is #define MCP_I2C_ADDR 0x40 /*!< Default base address (A0-A2 tied to gnd) */
2) Send out a byte to the 8 pins.
3) Send another byte etc
Schematic is very simple - MCP23008 on the same I2C bus as the propeller eeprom.
It is a nice little chip - $1.20 from futurlec.
Any help would be most appreciated
ADDIT - solved post #5
A search on the forum gives this thread http://forums.parallax.com/showthread.php?135052-mcp23008-Sourcing-Voltage
Which finishes with
Well, apologies.. I was being totaly and utterly stupid.. I downloaded the i2c stuff from the object exchange and was using the MCP23016 Lib, which of course has different register addresses.... I was not setting a pin high, i was changing it to an input... how stupid of me!
Maybe to make up for my stupidity I should write a driver specificly for this IC
I was wondering if this author would be able to share the code, or if anyone else has written a driver?
There is a very nice MCP23016 driver in the obex here http://obex.parallax.com/objects/251/
And of course the datasheets http://ww1.microchip.com/downloads/en/DeviceDoc/21919e.pdf for the MCP23008 and http://ww1.microchip.com/downloads/en/DeviceDoc/20090C.pdf for the MCP23016
Please forgive my ignorance though, but my eyes tend to glaze over reading those microchip datasheets
So the Spin code for a MCP23016 is this
PRI MCP23016_Demo | counter, mcpstate, ackbit
' demo the MCP32016 i2c I/O Expander
debug.str(string("Init...."))
MCP23016Object.WriteIOregister0(i2cSCL,mcp23016addr,%0000_0000)
MCP23016Object.WriteIOregister1(i2cSCL,mcp23016addr,%0000_0000)
waitcnt(clkfreq*2 + cnt)
repeat counter from 0 to 8
debug.str(string("Counting..."))
debug.dec(counter)
MCP23016Object.writeGP0(i2cSCL,mcp23016addr,counter)
MCP23016Object.writeGP1(i2cSCL,mcp23016addr,8-counter)
' read GP0 state
mcpState := MCP23016Object.ReadGP0(i2cSCL,mcp23016addr)
debug.str(string(" GP0 State: %"))
debug.bin(mcpState,8)
' read GP1 state
mcpState := MCP23016Object.ReadGP1(i2cSCL,mcp23016addr)
debug.str(string(" GP1 State: %"))
debug.bin(mcpState,8)
debug.putc("%")
waitcnt(clkfreq*2+cnt)
repeat counter from 0 to 8
debug.str(string("Counting..."))
debug.dec(counter)
case counter // 3
0 : ' case 0
debug.strln(string("set GP0 High, GP1 Low"))
MCP23016Object.WriteGP0(i2cSCL,mcp23016addr,%1111_1111)
MCP23016Object.WriteGP1(i2cSCL,mcp23016addr,%0000_0000)
1: ' case 1
debug.strln(string("set GP0 Low, GP1 High"))
MCP23016Object.WriteGP0(i2cSCL,mcp23016addr,%0000_0000)
MCP23016Object.WriteGP1(i2cSCL,mcp23016addr,%1111_1111)
2: ' case 2
debug.strln(string("set GP0 Low, GP1 Low"))
MCP23016Object.WriteGP0(i2cSCL,mcp23016addr,%0000_0000)
MCP23016Object.WriteGP1(i2cSCL,mcp23016addr,%0000_0000)
' read GP0 and GP1 state
mcpState := MCP23016Object.ReadGP0(i2cSCL,mcp23016addr)
debug.str(string("GP0 State: %"))
debug.bin(mcpState,8)
mcpState := MCP23016Object.ReadGP1(i2cSCL,mcp23016addr)
debug.str(string(" GP1 State: %"))
debug.bin(mcpState,8)
debug.putc(13)
waitcnt(clkfreq*2 + cnt)
And to make things really simple, I don't even want to read pins, only write to pins. So all I really need is
1) Initialise the chip - default hardware address A0-A2 all low, which I think from the C code is #define MCP_I2C_ADDR 0x40 /*!< Default base address (A0-A2 tied to gnd) */
2) Send out a byte to the 8 pins.
3) Send another byte etc
Schematic is very simple - MCP23008 on the same I2C bus as the propeller eeprom.
It is a nice little chip - $1.20 from futurlec.
Any help would be most appreciated

Comments
addit - note to myself http://www.nutsvolts.com/index.php?/blog/post/adding_more_digital_i_o_to_your_16_bit_microcontroller_experiments_part_2_t
this is the SPI version but it should be the same as SPI.
'' Simple I2C MCP23008 demonstration J Moxham '' output to pins (see MCP23016 objects in the Obex for input and other commmands) '' Thanks to James Burrows and Michael Green '' based on C code http://www.nutsvolts.com/index.php?/blog/post/adding_more_digital_i_o_to_your_16_bit_microcontroller_experiments_part_2_t CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 SCL = 28 ' pins never change SDA = 29 PUB i2c_Demo i2c_Initialize ' start the driver i2c_writelocation($40,$0,$0) ' write to device $40 MCP23008, all pins are outputs repeat i2c_writelocation($40,$09,%11111110) ' device $40, address $09 is output - leds are active low waitcnt((clkfreq/2)+cnt) ' small delay i2c_writelocation($40,$09,%11111101) ' next led on waitcnt((clkfreq/2)+cnt) i2c_writelocation($40,$09,%11111011) waitcnt((clkfreq/2)+cnt) i2c_writelocation($40,$09,%11110111) waitcnt((clkfreq/2)+cnt) i2c_writelocation($40,$09,%11101111) waitcnt((clkfreq/2)+cnt) i2c_writelocation($40,$09,%11011111) waitcnt((clkfreq/2)+cnt) i2c_writelocation($40,$09,%10111111) waitcnt((clkfreq/2)+cnt) i2c_writelocation($40,$09,%01111111) waitcnt((clkfreq/2)+cnt) PUB i2c_Initialize ' An I2C device may be left in an outa[SCL] := 1 ' reinitialized. Drive SCL high. dira[SCL] := 1 dira[SDA] := 0 ' Set SDA as input repeat 9 outa[SCL] := 0 ' Put out up to 9 clock pulses outa[SCL] := 1 if ina[SDA] ' Repeat if SDA not driven high quit ' by the EEPROM PUB i2c_Start ' SDA goes HIGH to LOW with SCL HIGH outa[SCL]~~ ' Initially drive SCL HIGH dira[SCL]~~ outa[SDA]~~ ' Initially drive SDA HIGH dira[SDA]~~ outa[SDA]~ ' Now drive SDA LOW outa[SCL]~ ' Leave SCL LOW PUB i2c_Stop ' SDA goes LOW to HIGH with SCL High outa[SCL]~~ ' Drive SCL HIGH outa[SDA]~~ ' then SDA HIGH dira[SCL]~ ' Now let them float dira[SDA]~ ' If pullups present, they'll stay HIGH PUB i2c_Write(data) ': ackbit '' Write i2c data. Data byte is output MSB first, SDA data line is valid '' only while the SCL line is HIGH. Data is always 8 bits (+ ACK/NAK). '' SDA is assumed LOW and SCL and SDA are both left in the LOW state. 'ackbit := 0 ' ack bit commented out as this chip always works data <<= 24 repeat 8 ' Output data to SDA outa[SDA] := (data <-= 1) & 1 outa[SCL]~~ ' Toggle SCL from LOW to HIGH to LOW outa[SCL]~ dira[SDA]~ ' Set SDA to input for ACK/NAK outa[SCL]~~ 'ackbit := ina[SDA] ' Sample SDA when SCL is HIGH outa[SCL]~ outa[SDA]~ ' Leave SDA driven LOW dira[SDA]~~ PUB i2c_writeLocation(device_address, register, value) i2c_start ' start the chip i2c_write(device_address) ' write to this chip i2c_write(register) ' write to this register i2c_write(value) ' write value i2c_stop ' stop the chipI've decided to use one of these chips on my "foot-controller" since it's due for a rebuild so adding input functions will come soon. These chips seem fairly simple in theory and the code seems to agree. Now to test the theory!