Bi-directional SPI communication with shift registers

Hi all,

I was wondering how could I combine a 74HC595 and a 74HC165 shift register to control 8 inputs and 8 outputs with only one chip select pin.

The idea is to receive and transmit data simultaneously.I have seen a few diagrams for sending and a few others just to receive, but haven't found anything combined.

Any ideas?
Regards

why not use a port expander ?

MCP23s17,

MCP23017 is for I2C

I'm using all the analog inputs, therefore I2C is not an option. Ill have a look at the MCP23S17, although the original question still remains

Well, looks like no one has an idea, so I ordered both an MCP23S17 and an 74HC165 chip to play around.

Technically I believe one only needs an extra pin to load the parallel input to the 165 buffer before starting the SPI transmission which will both send (to the 595) and receive data (on the 165).

Guess I'll have to try and see... Ill post back with my findings

74HC/HCT299
8-bit universal shift register; 3-state

85 cents at Futurlec, ships from Hong Kong.

casemod:
The idea is to receive and transmit data simultaneously.

on the surface, this sounds impossible.
would send, then receive, then send, then receive, sequentially work ?
the other problem you mentioned is using only 1 CS pin.

could you write to the 595 and have one of the 595 outputs, set the 165 to send?
that way, the 595 tells the 165 when it is done and the line is clear for the 165 ?

With the universals you can set pin modes according to the docs. There are different ones.
I dunno how many pins they all might need.

The trouble I see with using the same pins to write and read is how you wire them but that's the same as IO pins on your chip, only slower. Still you could do massive multiplexers with them and run the lot with an ATtiny!

Well, looks like no one has an idea

No, I's say some has not patience. It is Christmas Eve you know.

It's actually pretty simple to do.

digitalWrite (latchPin, LOW);
digitalWrite (latchPin, HIGH); // captures data on '165 inputs
dataIn = SPI.transfer(dataOut); // read in from '165 while writing out to '595
digitalWrite (latchPin, LOW);
digitalWrite (latchPin, HIGH); // move data to output of '595

Pin 10 must be set to output, good idea to use it for the latchPin
Pin 11 is data going out
Pin 12 is data coming in
Pin 13 is the clock.

Must have #include<SPI.h> at the top of the sketch before any code (use Import Library from the menu)
Must have SPI.begin(); in setup()

CrossRoads:
[quote[Well, looks like no one has an idea
No, I's say some has not patience. It is Christmas Eve you know.

It's actually pretty simple to do.

digitalWrite (latchPin, LOW);

digitalWrite (latchPin, HIGH); // captures data on '165 inputs
dataIn = SPI.transfer(dataOut); // read in from '165 while writing out to '595
digitalWrite (latchPin, LOW);
digitalWrite (latchPin, HIGH); // move data to output of '595



Pin 10 must be set to output, good idea to use it for the latchPin
Pin 11 is data going out
Pin 12 is data coming in
Pin 13 is the clock.

Must have #include<SPI.h> at the top of the sketch before any code (use Import Library from the menu)
Must have SPI.begin(); in setup()

How has your 9x9x9 led cube come along? That's all output registers?

Still at 7x9x9, waiting on the last 2 layers. Work for hire has been crazy leading up to Christmas, so MrsCrossRoads hasn't added the last 2 layers.
Control board and basic programming is working fine. Yes, it is all output registers, no need to read them back in. cd74ac164 (24mA outputs) to drive columns, and 74hc595 to control N-channel MOSFETs to sink current from layers.

dave-in-nj:
on the surface, this sounds impossible.

Impossible? Not at all. Sounds like he's trying to make a normal SPI bus, which always operates bidirectionally, although often we ignore the input or output side of a particular transfer. The SPI hardware in the processor has a shift register. You start by loading the register with the output byte, then the hardware starts clocking the shift register. As the bits are shifted out MOSI from one end, it is shifting in bits from MISO on the other end. When done, the internal shift register holds the input byte. You can't do this with shiftin() and shiftout() since they are unidirectional, but you can do it with SPI.transfer() or by manually bit-banging the interface.

CrossRoads:

digitalWrite (latchPin, LOW);

digitalWrite (latchPin, HIGH); // captures data on '165 inputs
dataIn = SPI.transfer(dataOut); // read in from '165 while writing out to '595
digitalWrite (latchPin, LOW);
digitalWrite (latchPin, HIGH); // move data to output of '595

Interesting. I questioned this at first, but as I think about it, it looks like it will work, but only if these shift registers are the only thing on the bus. If there are other devices using the same SPI bus (with a different CS) this will fall apart. Say you do the above transfer. Then, when you do the next transfer, the first thing that happens is the CS line is pulsed. This will once again latch the '595 shift register to its outputs. That's not a problem since the shift register still has the previous value from the last transfer.

But if there are other devices on the SPI bus, when the latch is pulsed that first time, the '595 shift register will have whatever was the last byte sent to the other device. That value will get latched to the outputs for the time between the two latch pulses. This could have very bad effects depending on what's connected to those outputs. Now, sharing the bus is not part of the OP's problem statement, so this may not be an issue for him.

SPI is a very simple protocol. But it's hard to implement a true SPI interface with shift registers.

dave-in-nj:
why not use a port expander ?

I forgot about those. Thanks for the reminder! I'm working on a project where I was trying (somewhat unsuccessfully) to make a true SPI interface out of shift registers. I just reworked it using a pair of 23s17, and it simplifies the schematic, and greatly cleans up the board layout (and takes less room.) It increases the code complexity, but I think it will be worth it.

If there are other devices on the SPI bus, then yes, the HC595 output could be corrupted. Than an additional logic gate would be required to prevent SCK from clocking in the other data. Running the HC595 CS line thru an inverter to make an active HIGH CS, ANDing it with SCK to drive SRCLK would resolve that.

At the same time, the corrupted data would not be at the HC595 outputs very long, just 20 clock cycles or so, 1-2uS, so there may or mat not be a noticeable change at the system level.

CrossRoads:
At the same time, the corrupted data would not be at the HC595 outputs very long, just 20 clock cycles or so, 1-2uS, so there may or mat not be a noticeable change at the system level.

Yep. it all depends on what's out there. If the '595 is driving LEDs, you probably won't see it (might see a faint glow on LEDs that are off if the '595 is updated regularly. If it's driving mechanical relays, they probably won't even start to move in that amount of time. If they are driving the top and bottom transistors of an H bridge, you could get some high current shoot-though spikes. And if it is driving edge sensitive logic, it could cause undesired operation.

I'll be that 94% of the time anybody on this forum will probably be driving LEDs or relays with the '595, and this simply won't be an issue. But it's not a bad idea to keep this limitation in mind.

It sure would make things easier if there were a pair of shift registers that worked like normal SPI: a single enable line where the falling edge starts the transfer, and a rising edge ends it. For in parallel-to-serial shifter, the falling edge would load the shift register, while for a serial-to-parallel shifter the rising edge would latch the outputs. And of course, the serial output of the parallel-to-serial chip would be tri-state except when the enable line is low. I've never seen a pair of chips that operate quite like that (although I have to admit I haven't searched real hard.)

Bi-directional shift register pin is either input or output at any one time. You can't write and read the same pin at once.

But 1 8 bit universal register could handle a 4x4 multiplex of buttons or leds by changing inputs and outputs and states to only power one pin and only read one other at a time. With 2 universals you could multiplex 8x8, only a bit slower and still be way faster than a finger on a button.

It could also drive 2-way communications up to some baud rate. 1 universal could run 8 1-wire buses, couldn't it?

I have thought that it would be cool to have an SPI chain of 1 output register and 1 input register. Then every byte sent to output would also read a byte of input. But what is the project?

GoForSmoke:
But 1 8 bit universal register could handle a 4x4 multiplex of buttons or leds by changing inputs and outputs and states to only power one pin and only read one other at a time. With 2 universals you could multiplex 8x8, only a bit slower and still be way faster than a finger on a button.

That's an interesting concept. But is it really a pin savings? You're saving an I/O pin by turning it around and using it in both directions, but wouldn't you need at least one pin to turn around the direction of the universal shift register? Seems like a lot of complexity for no real gain. The best engineering solution is the one that's the simplest.

It could also drive 2-way communications up to some baud rate. 1 universal could run 8 1-wire buses, couldn't it?

It might be possible, but again, why? Using a DS2482-800 would be a LOT easier, with sample code already available. Less hardware to design, and MUCH less coding effort. But if you want to design an interesting Rube Goldberg machine that's more complicated than necessary, go for it!

I have thought that it would be cool to have an SPI chain of 1 output register and 1 input register. Then every byte sent to output would also read a byte of input. But what is the project?

That's what I was working on, daisy changing two pairs of shift registers giving me 16 outputs and 16 inputs, with all data transfer handled by a pair of SPI.transfer() calls. But I've been struggling with a clean way of doing it that allows the SPI bus to also be used for other devices. Using the I/O expander mentioned in the second post cut the chip count in two, and greatly cleaned up the board layout, with only a slight increase in code complexity.

ShapeShifter:
That's an interesting concept. But is it really a pin savings? You're saving an I/O pin by turning it around and using it in both directions, but wouldn't you need at least one pin to turn around the direction of the universal shift register? Seems like a lot of complexity for no real gain. The best engineering solution is the one that's the simplest.

I can't find the docs I had read before. My understanding was that pin mode changes were done via serial input but for the registers I can find it's like all the pins are input or output.
Using 4 or 5 pins to run 1 to a chain of those, I think that's pin savings but since I don't even find the damn chips that's nothing at all.

It might be possible, but again, why? Using a DS2482-800 would be a LOT easier, with sample code already available. Less hardware to design, and MUCH less coding effort. But if you want to design an interesting Rube Goldberg machine that's more complicated than necessary, go for it!
That's what I was working on, daisy changing two pairs of shift registers giving me 16 outputs and 16 inputs, with all data transfer handled by a pair of SPI.transfer() calls. But I've been struggling with a clean way of doing it that allows the SPI bus to also be used for other devices. Using the I/O expander mentioned in the second post cut the chip count in two, and greatly cleaned up the board layout, with only a slight increase in code complexity.

That I/O expander sounds neat but 1-wire which is good for 1-wire.
I was looking at the Max7301 SPI expander with it looks like 28 I/O? And then I saw it's only in surface mount.

However using new search words I did find the SPI MCP23S17 16-pin expander for $1.35 in PDIP package.

I an sure that you can get more out of the datasheet (covers the I2C and SPI versions) than I.

I've order the parts so we shall see.

Ill probably make a pcb with everything once its done and share here. I'm making a number of other PCB's with isolated I2C and SPI for level shifting if someone is interested. Should have them all by March.

Always handy to have 8 additional inputs/outputs that one can plug to a 4 pin header out of the PCB.

As to the SPI BUS, yes, it is shared with an LCD, an RFID tag and a second microcontroller. (that's the whole idea!). I'm thinking in using some sort of binary to decimal decoder for the CS pins as I have a number of devices sharing the bus.

Anyone knows of a 2 bit binary decoder by the way? That would allow 4 different devices with two SS lines. Just trying to save the burden of making that using logic gates.

I've been reading the MCP23017 datasheet. Apparently requires registers to be set, for for something really simple it actually increases the overhead a bit. I am also not sure I could read/write at the same time.

Lastly, anyone with some practical experience on SPI bus lengths? Say with a 250KHz clock.

Regards

GoForSmoke:
That I/O expander sounds neat but 1-wire which is good for 1-wire.

Of course it's for 1-wire, that's what you were talking about, using a shift register to drive 8 independent 1-wire busses. I offered that chip as a more practical alternative.

However using new search words I did find the SPI MCP23S17 16-pin expander for $1.35 in PDIP package.
MCP23017 Datasheet

Yep, that's the chip mentioned in the second post. I've used it in the past, and while it takes a little configuration (setting a bunch of initial register values) it's pretty much set and forget after that. During each I/O access, there's slightly more transfer overhead than shift registers, as you have to send an initial address byte, but it's still easy to use.

casemod:
I'm thinking in using some sort of binary to decimal decoder for the CS pins as I have a number of devices sharing the bus.

Anyone knows of a 2 bit binary decoder by the way? That would allow 4 different devices with two SS lines. Just trying to save the burden of making that using logic gates.

The chip you're looking for is called a demultiplexer. Here are a few:

I've been reading the MCP23017 datasheet. Apparently requires registers to be set, for for something really simple it actually increases the overhead a bit. I am also not sure I could read/write at the same time.

There is indeed a little more traffic going over the bus, but for most applications that isn't an issue. And no, it isn't simultaneous input and output. For an intense DSP type application, you will get more throughput using a custom shift register solution, but if you need that type of intense speed you're probably not going to be using an Arduino in the first place. There's a certain elegance to simultaneous I/O, but unfortunately there isn't an ideal pair of in/out shift registers that act like a true SPI bus: either external control logic is required, or you take a shortcut like presented before and eliminate using the bus for other purposes. Is it really worth the effort to get simultaneous I/O? (If you really want a fast and slick expander, and want to push the envelope, put an FPGA or CPLD out there!)

I'm actually programming in AVR Studio, with some occasional modified arduino libraries.

This is not just about performance, its matching each device to a specific task.

For example, sending data to a display. Easy enough with a shift register, but modifying the library to address the chip and send the address bits first can be a real PITA. For a color display, a huge amount of data has to be sent, so it is desirable that it does go out quickly, rather than wasting cycles sending unneeded address data. That's where an SR excels over a port expander.

It's an easy conversion to grab a 3.2" display, attach that to a pair of SR and use a teensy or a dsPIC with SPI and DMA to send data in 16 bit mode much faster than what I would be able to achieve setting individual ports. Not to mention the availability (or lack of) of a native SPI screen with such size.

Of course for non intensive applications it might actually be preferred to use a PORT expander rather than a SR, after all the interrupt capability and the address feature are quite nice, in fact not having do look for the data every n seconds could actually increase overall performance.

Regarding the demultiplexer, I found the ideal one: SN74LVC1G139. SSOP or SOIC8

casemod:
This is not just about performance, its matching each device to a specific task.

Absolutely! A proper match is crucial.

The port expander chips are designed as just that: port expansion. As you indicate, they have some advantages when used to get more GPIO pins. I've used them in the past to read a bank of configuration DIP switches, and to set some status indicator and relay outputs. Generally low throughput stuff. In that case, the overhead is negligible. There are other signals on those systems that required higher throughput, for example SPI through optoisolators to an isolated ADC. I would never consider routing those through a port expander, they come directly from the processor.

In fact, the last time I used a port expander, I put 8 bits of DIP switches on port B, and used port A for output. At power up, I configured the expander, read the DIP switches, put it in non-address increment mode, then wrote to port A. After that, it just required writing a single byte each time, and because the address didn't increment, each write updated port A -- no extra overhead, but of course only one port accessible. (This is all working from memory, and it's been a few years.)

Your display is another such application that is not suited to a port expander. In fact, it's high enough throughput that I wonder why you're even going with shift registers and not coming directly off the processor?