Sunday, 16 April 2017

Serial UART hub/network with master/slave devices

We recently had cause to build a simple serial/UART "network" of slave devices. We had a single "controller" device (which receives data from a PC and broadcasts it along the bus) and a number of similar "slave" type devices.

Normally, when it comes to multiple devices along a bus, we'd be thinking of either SPI (broadcast the message to all devices with an identifier in the message to which the appropriate devices repond) or I2C (each device could have its own unique hardware ID to which we address the messages).

But for a recent project we were asked if we could create a serial/UART bus. At first it seemed quite straight forward - simply tie all the TX lines of the "slaves" together and connect to the "RX" of the "master" and invert; tie all the RX lines of the "slaves" to each other and connect them to the "TX" of the master device.

The basic idea is that the master would broadcast a message to all devices, including a device ID in the message. When any device receives an end-of-message marker, it looks at the device ID. If the message is not intended for that device, it simply ignores it.

The theory works great.
Sometimes in hardware it works just fine.
But sometimes it goes horribly wrong.

Now of course if two devices try talking at once, you just get garbled nonsense (so at the end of each message we include a simple XOR sum to check if a message is valid). So this set-up only works if you can be sure that only one device is going to  try to use the bus at any one time.

But sometimes we were getting devices resetting. Not all of them, and not all at the same time. Just some devices, sometimes. Which in turn indicates that one device is trying to drive a line high, while another is trying to drive it low. When this happens, we're effectively creating a dead-short between power and ground; so it's no wonder that the devices are resetting!

By simply putting a diode on each of the TX lines and a pull-up resistor on the "master" RX line we can overcome this problem easily. Now, when a device tries to drive a TX line high, the  current can't get through the diode. But the pull-up resistor lets the TX line (connected to the RX of the master) float high. So the end result is the same.

But if another device drives the TX line low, it's enough to overcome the pull-up resistor, so the entire TX bus goes low (and the master RX line goes low). If one device tries to drive the TX line high and another low, the TX line goes low. The data at the other end might get garbled, but the important thing is that we don't get slave devices resetting.

It's basically the same idea used with SPI communications - drive a line low, release it to let it float high. But if we can't guarantee that our slave devices aren't going to try to drive the TX line high, the diode simply blocks that behaviour. When no devices are pulling the TX line low, it floats high anyway (which is the idle state of a UART transmitter anyway).

But a trick worth knowing!