Interfacing with the outside world is a fairly common microcontroller task. Outside of certain use cases microcontrollers are arguably primarily useful because of how easily they can interface with other devices. If we just wanted to read and write some data we wouldn’t have gotten that Arduino! But some tasks are more common than others; for instance we’re used to being on the master side of the interface equation, not the slave side. (That’s the job for the TI engineer who designed the temperature sensor, right?) As [Pat] discovered when mocking out a missing SPI GPIO extender, sometimes playing the other role can contain unexpected difficulties.
The simple case for a SPI slave is exactly that: simple. SPI can be wonderful in its apparent simplicity. Unlike I2C there are no weird addressing schemes, read/write bits, stop and start clock conditions. You toggle a clock line and a bit of data comes out, as long as you have the right polarity schemes of course. As a slave device the basic algorithm is of commensurate complexity. Setup an interrupt on the clock pin, wait for your chip select to be asserted, and on each clock edge shift out the next bit of the current word. Check out [Pat]’s eminently readable code to see how simple it can be.
But that last little bit is where the complexity lies. When you’re the master it’s like being the apex predator, the king of the jungle, the head program manager. You dictate the tempo and everyone on the bus dances to the beat of your clock edge. Sure the datasheet for that SRAM says it can’t run faster than 8 MHz but do you really believe it? Not until you try driving that clock a little quicker to see if there’s not a speedier transfer to be had! When you’re the slave you have to have a bit ready every clock edge. Period. Missing even a single bit due to, say, an errant print statement will trash the rest of transaction in ways which are hard to detect and recover from. And your slave code needs to be able to detect those problems in order to reset for the next transaction. Getting stuck waiting to send the 8th bit of a transaction that has ended won’t do.
Check out [Pat]’s very friendly post for a nice refresher on SPI and their discoveries working through the problems of building a SPI slave. There are some helpful tips about how to keep things responsive in a device performing other tasks.
“Whether your eager to start manufacturing or just looking to see what’s possible, we work with you to build the solution you need now while preparing for the future. We specialize in sensing, actuation and communications (LoRa, BLE, LTE-M1, WiFi, using GATT, MQTT and more).”
–> YOU’RE <–
Not as bad as, say, people giving stuff to John and “I”. I once fired an employee who couldn’t ken the distinction between subjects and objects.
Isn’t a SPI GPIO extender simply a buffered shift register?
It can be, or it can be something highly complex such as the MAX6957. It depends on your requirements and needs.
MCP23S17 is also possible.
“Check out [Pat]’s very friendly post”
Where exactly is that post? Your link points to a company website, not a single post…
Shoot! Sorry about that! Looks like @russdill got it right, and Elliot swooped in and edited the post for me. So it should be correct now.
Correct link is here:
https://flyingcarsandstuff.com/2017/02/creating-a-sensor-or-peripheral-with-an-spi-slave-interface/
It’s a fun little project, so the details don’t matter too much, but one big issue is it doesn’t detect if the chip select line toggles off during a transaction. A robust SPItransfer function would detect an input change, resetting the state machine to the start.
Thanks!
if someone is interested why SPI interface can be tricky presentation on SPI Slave support for Linux https://www.youtube.com/watch?v=tCa_ydVwKhM can be quite informative