After a recent discussion on the forum regarding shift registers, I thought I would share what I have done with the feedback from others.

Its a 16-Bit I/O Expander with Serial Interface. In other words, it extends the number of logic I/O's available to a micro controller (handy for driving a large number of LEDs for example!).
A quick overview of features from the MCP23S17 Datasheet:
Although the device is capable of driving +/-25mA on each pin, it is restricted to the maximum collective current as shown in the list above (~150mA). In terms of comparison with other devices I have found on the net, this "limitation" is of minor concern and trumps 95% of other shift registers.
The user module (linked further down) allows a combined update speed of ~17uS for all 16 outputs (~60Khz). Is this a limitation? In terms of functionality its a consideration, not a show stopper. Most persistence of vision projects with LOTS of LEDs need a refresh rate of ~25Hz (which is 2400% less then what these devices can deliver).
There can only be 8 different device addresses. This limits the total number of I/Os to 128 (8 * 16).

(I was interested to see how much time it took to update all 32 outputs, hence the frequency meter. As it turns out, this happens
at ~30Khz with a PIC configured to run at 40Mhz)
Can be downloaded here - User Module Pack (MCP23S17_MULT.bas)
Before compiling the example programs - you will need to update SPI.bas as explained by Graham in this post (if you are using a PIC with SPI errata).
Here's a program that will increment a LongWord type variable and display each of the 4 bytes on a separate port - in an infinite loop.
Device = 18F2620 Clock = 40 Config OSC = HSPLL, MCLRE = Off Include "mcp23s17_mult.bas" Dim tmpLongWord As LongWord Const mcp1 = %111, // First chip address mcp2 = %000 // Second chip address MCP23S17_MULT.SetTrisPortA(mcp1,$00) MCP23S17_MULT.SetTrisPortB(mcp1,$00) MCP23S17_MULT.SetTrisPortA(mcp2,$00) MCP23S17_MULT.SetTrisPortB(mcp2,$00) Low(PORTB.0) tmpLongWord = 0 While True tmpLongWord = tmpLongWord + 1 MCP23S17_MULT.WritePortA(mcp1,tmpLongWord.byte0) MCP23S17_MULT.WritePortB(mcp1,tmpLongWord.byte1) MCP23S17_MULT.WritePortA(mcp2,tmpLongWord.byte2) MCP23S17_MULT.WritePortB(mcp2,tmpLongWord.byte3) PORTB.0 = 1 PORTB.0 = 0 Wend
Here's another example program that configures one port as all inputs (also enables internal pullup resistors), and the other port as all outputs. The output port will then mirror the input port.
Device = 18F2620 Clock = 40 Config OSC = HSPLL, MCLRE = Off Include "mcp23s17_mult.bas" Dim i1, i2 As Word Const mcp1 = %000, // First chip address mcp2 = %111 // Second chip address MCP23S17_MULT.SetTrisPortA(mcp1,0) MCP23S17_MULT.SetTrisPortB(mcp1,$FF) MCP23S17_MULT.SetPullUpsPortB(mcp1,$FF) MCP23S17_MULT.SetTrisPortA(mcp2,0) MCP23S17_MULT.SetTrisPortB(mcp2,$FF) MCP23S17_MULT.SetPullUpsPortB(mcp2,$FF) While true i1 = MCP23S17_MULT.ReadPortB(mcp1) MCP23S17_MULT.WritePortA(mcp1,i1) i2 = MCP23S17_MULT.ReadPortB(mcp2) MCP23S17_MULT.WritePortA(mcp2,i2) Wend