• Slave Select via SPI Shift Registers

    12/24/2018 at 01:04 0 comments

    TL;DR: You can’t use Shift Registers over SPI to control the Slave Select of SPI devices on the same bus because you’ll end up sending the commands for the Shift Registers to the other devices when you want to disable them.

    The table at the bottom sets out the flow and why it breaks, and the words between here and there is my tale of woe, and a bunch of useful details.

    I spent a while (understatement, as is usual with these things) working on a project where I wanted to control many devices (accelerometers, MPU9250) via SPI, and have those devices Slave Select (SS) lines controlled by a Shift Register. I wanted to simplify wiring, programming, and achieve high speeds, so I decided I wanted to control the 74HC595 Shift Register via SPI as well, because they support this.

    For those of you who are cleverer than me you’re probably already shaking your heads at this approach, and for good reason.

    It didn’t work.

    I experimented with a great number of settings - SPI Modes, speeds, wiring, pulldowns, pullups, endless samples of code to prove that things worked every step of the way.

    Still nothing.

    So I started digging into the errors I was getting from the calling code library (the awesome Bolderflight MPU9250 library which I’ve locally modified to support a Shift Register). It turns out the errors are raised when comparing what was written in with what was read back, and seeing a discrepancy.

    So errors were good, because it meant I was writing to the registers, but the data being written to them was wrong. Which meant the Shift Register was working but something was messing with what was being written to device - perhaps another device or write somewhere that was pushing out extra info? Seemed unlikely as my main prototype didn’t have any problems when calling the accelerometers via GPIO SS.

    It was at this point it hit me - I was writing to the Shift Register to disable the MPU9250 while the MPU9250 was still listening to the SPI channel.

    I didn’t do much more digging after that, in fact I’ve written this whole post without fully checking that moving the shift register to a separate SPI Bus (using a Teensy 3.6 which has 3) actually fixes the problem. (Ok, I did check it, and it works).

    So the moral of the story is, as set out in the TL;DR section: You can’t do Shift Registers via SPI to control SPI devices on the same SPI bus because you end up with multiple devices listening at once, and it messes things up.

    The following table shows how the flow works, and where it goes wrong.

    Action

    SPI Listeners

    Notes

    Selecting Device using Shift Register

    SPI.BeginTransaction

    Shift Reg Transaction

    digitalWrite(latchPin, LOW)

    Shift Register (start)

    SHR Listens

    SPI.Transfer

    Shift Register

    SHR Transfer Data

    digitalWrite(latchPin, HIGH)

    Shift Register (end)
    Device (start)

    SHR Listen Ends

    Device Listens

    SPI.EndTransaction

    Device

    Shift Reg Transaction End

    Communicating with Device

    SPI.BeginTransaction

    Device

    SPI.Transfer

    Device

    Device Transfer Data

    SPI.EndTransaction

    Device

    De-selecting Device using Shift Register

    Note that the Device
    is still listening
    (we are trying to disable it)
    so it will receive
    the data being sent
    to the shift register

    SPI.BeginTransaction

    Device

    digitalWrite(latchPin, LOW)

    Shift Register (start)

    Device

    SHR Listens
    Device Listens

    SPI.Transfer

    Shift Register

    Device

    SHR Transfer Data
    Device Listens

    digitalWrite(latchPin, HIGH)

    Shift Register (end)
    Device (end)

    SHR Listen Ends

    Device Listen Ends

    SPI.EndTransaction