Make sure to put some capacitors on the SPI lines to ground. Something like 220pf and put small series resistors on the driver pins on every chip. An SPI bus can be very susceptible to picking up noise (especially in industrial environments). Updating the outputs continuously and not only when there is a change helps to recover from the case where an SPI transfer goed wrong. The resistors and capacitance will limit the maximum transfer rate; I'd keep this as low as possible so the capacitance can be large.
Ehh. Filtering is nice, but the clock needs to be sharp, and only so much delay can be tolerated before [returned] data gets misaligned. The amount that is acceptable depends on the clock frequency, and whether the slave devices have schmitt triggers on their inputs -- which they should, but who knows.
However for I/O expansion I2C is just easier. You only need two wires to each chip and the I2C bus takes care of the addressing.
@T3sl4co1l : I have to disagree that SPI is more suitable to take offboard. I'd say I2C is more suitable because it has noise filtering by design (or at least it is supposed to have this if it follows the specs) where SPI does not. Actually I2C is used to communicate between electronic devices over cables. Think about HDMI. For longer distances you can even get special I2C buffers.
Precisely: HDMI is a fully shielded interface.
Digital filtering doesn't do anything if the logic levels are being actively and repetitively violated by RF noise. And even if it's analog filtering on chip, enough RF to activate ESD diodes will still violate levels.
Only analog filtering can help with that -- but only as much as the filter can offer, which is limited by the total bus capacitance and desired clock speed. That is, the number of filters, their number of poles, and their cutoff frequency, are all constrained by total bus capacitance.
Example: if you need say -40dB by 10MHz (to deal with some conducted, and the low end of radiated, noise), then a 2nd order filter needs Fc ~ 1MHz, and for Zo = 100R (ballpark typical for non-paired multiconductor cable), C = 1.6nF. With that at each end of the line, and 2.2k pullups, risetime is 5-10us, pushing Fclk below 100kHz most likely. Whereas if you use Fc = 3MHz and a 4th order filter, you need 3 times less capacitance each (~1nF), but twice as many. 2/3rds the capacitance helps a bit, but it's still a burden (now you can do maybe 150kHz).
Maybe you target radiated noise more directly, say at 50 or 100MHz, in which case the capacitors are much more reasonable, 100pF and thereabouts; but you're still wide open to conducted noise.
This does make a good case for small extension boards -- like a keypad or display board, with no external connections, only its cable back to the main board. This will have a resonance defined by the size of the boards and the distance between them, and the length and style of cable, and how many grounds are used (in particular, the size of grounding). A modest say 0.3m cable will give a first resonance around 100MHz, with no anomalous susceptibility below there. So a 2nd order filter cutting at 10MHz or so is quite reasonable, and should pass modest RFI levels (3, maybe 10 V/m).
The same of course applies to SPI, where the filtering also serves to minimize emissions (SPI pin drivers are rarely if ever filtered or slew-rate limited). SPI is probably more vulnerable here simply because more signals are needed, and it would be nice to pack in more grounds to compensate, but, y'know, pins, connectors, size, cost...
Going back to the OP's situation -- SPI can still be attractive, in more limited circumstances. If the pin directions or types don't need to be controllable, then a huge ass shift register chain can be used. No CS's needed, just one common strobe. Just keep sending frames until the data's gone once around, then hit the strobe to set all the outputs (e.g. 74HC595) and sample all the inputs (e.g. 74HC165). Basically, bake-your-own shift register. Great for LEDs, GPOs and GPIs, but not so great for GPIOs, anything time critical (it's polled only), etc.
I mean, you can tack on some GPIO functionality -- say, add a tristate buffer driven from a GPO, so the port direction is just another bit on the chain. Or diode + pull-up/down from more GPOs to set pull states as needed. You probably aren't going to go too far in this direction before the parts count gets messy, at which point the IO expanders will look more attractive.
Tim