Shop OBEX P1 Docs P2 Docs Learn Events
Help using the MCP23008 — Parallax Forums

Help using the MCP23008

Dr_AculaDr_Acula Posts: 5,484
edited 2012-06-20 11:00 in Propeller 1
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
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 :( 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
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

  • LeonLeon Posts: 7,620
    edited 2012-06-17 04:54
    The MCP23008 is an 8-bit device, whereas the MCP23016 is 16-bit. The MCP23016 is obsolete, and the MCP23017 or MCP23018 should be used instead.
  • JonnyMacJonnyMac Posts: 9,194
    edited 2012-06-17 08:29
    The MCP23017 is very easy to use and doesn't require an external RC circuit. I've attached my [very simple] object. It handles essential configuration and IO.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-06-17 15:56
    Thanks JonnyMac & Leon. I guess what I am looking for is the difference between drivers for the MCP23008 and other chips. Maybe no-one has written a MCP23008 driver yet? If there are no drivers I'll need to write one.

    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.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-06-20 04:50
    Got some code working for the MCP23008. This is a very stripped down version of existing i2c objects and a modification of the MCP23016 driver. Only need outputs at the moment.
    '' 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 chip
              
    
  • average joeaverage joe Posts: 795
    edited 2012-06-20 11:00
    Very nice work Doc!
    I'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!
Sign In or Register to comment.