Yesterday’s experiment was done with a very specific purpose (yeah, sometimes I do things for a purpose…).
I’m trying to overcome a major drawback of the SPI bus. But to explain this, allow me to go on a little detour first. The I2C bus is an extremely convenient design, because it only needs 4 wires to connect several chips together:
Each chip sees the same information, but it works because each chip has been set up to respond only to a specific address on the bus. And that address happens to be conveniently located at the start of each I2C message.
But although the I2C bus is slow, usability is where the SPI “bus” suffers in comparison:
With one chip, there’s no problem: one “slave select” (SS), a “serial clock” (SCLK), a “master-out slave-in” (MOSI) pin for outgoing data, and a “master-in slave-out” (MISO) for incoming data (brilliant naming – no confusion!).
The trouble is with multiple chips, i.e. slaves. The SCLK, MOSI, and MISO pins can be shared, but not the “slave select”. Each slave will need a separate pin to select it. And that makes it impossible to use it as a real bus…
If only there were a way to get multiple slave selects onto a single pin…
And that’s where yesterday’s post comes in. Suppose we had all the SPI signals, as well as power, as well as two chip selects. Like, eh… the SPI/ISP connector, using B0 and B1 as the two select signals – how convenient!
So here’s the idea: first we send a byte over SPI with B0 set low and then we send the actual data with B1 set low. The B0 transfer is used to select one specific slave, and the B1 transfer then takes place only with that slave.
The good news is that a 74HC595 shift register has all the functionality needed to perform this task, by using the three-state enable pin as a gate to turn the shift register outputs on or off. Here’s the schematic for it all:
The idea is to put this simple circuit on each slave board. On each one, a different output from the shift register gets jumpered. In the above schematic, for example, QD (the 4th bit) has been wired up as slave select.
Here’s code for trying out this idea, optimized to only send data on B0 when a different slave is being selected:
The following code also needs to be executed to set things up properly on power-up:
// init PB0 and PB1 as outputs, set high PORTB |= bit(1) | bit(0); DDRB |= bit(1) | bit(0);
Anyway. So much for theory… stay tuned!
PS. Season’s greetings to everyone. This weblog will continue on auto-pilot for the next two weeks. Enjoy!