# Multiplexing low speed SPI slaves with a high speed SPI master (time-division?)

I have 12 SPI slaves, supporting SPI at a maximum clock rate of 2MHz. The SPI master (an ESP32), can run at up to 80MHz (and integer divisions thereof). My slaves are all 8-channel ADCs (MCP3208), which I need to poll continously. Therefore, I want to somehow maximize the throughput.

I was thinking that it might be possible to do time-division multiplexing on the slaves, by running the master at 40 MHz and create 20 time divisions, each being a 2MHz line for a potential slave. This would effectively combine my slaves into a single SPI device with higher speed (while interleaving all bits for the master). Unfortunately, I cannot find any resources on this topic.

Below is a diagram showing the concept, by using an external clock divider to create a 2MHz clock from the 40MHz base clock (which should ideally be offset for each slave..), a multiplexer for MOSI, and a demultiplexer for MISO lines.

Unfortunately, I am missing the required background knowledge to judge if this is at all possible, and if the required ICs even exist.

• If the esp32 has to bit bang the SPI bus instead of using the SPI peripheral, it's pretty unlikely to still be able to achieve 40 MHz. Jun 26, 2019 at 14:30
• Or if you use an external circuit like you show, will the esp32 be able to de-interleave the received data at 40 MHz? Jun 26, 2019 at 14:32
• If all the slaves are 2Mhz, why not just run the SPI channel at 2Mhz? Everything is going to be slowed down to that speed anyway... I don't see any gain of running the master 40 times faster than the slaves, the slaves can't output data 40 times faster so when you read back in you need to take into account that 1 bit-change is 40 times longer than it would normally be (because the pulse will last for 40 clock cycles instead of 1), so you'll need some kind of complicated routine to decode it. I think you chose the wrong ADC and "fixing" a solution that is easier done by selecting the right ADC. Jun 26, 2019 at 15:00
• @RonBeyer Because when i run all at 2MHz, I am effectively reducing the throughput to 1/12 per slave, wasting additional samples of the ADCs. It's true that i could have chosen other ADCs, but I wasn't able to find anything in the same price range (~2\$ per ADC), that would allow me to do this. Jun 26, 2019 at 15:04
• Also the esp uses a hardware SPI interface, which accesses the memory via DMA. Therefore, as it is also a dual core system, i should be able to decode the data fast enough. Jun 26, 2019 at 15:06

I don't think this is at all practical.

First, how are you going to get the SPI interface to interleave data? I doubt very, very much that you can pull this off with a dedicated synchronous serial peripheral, and as @ThePhoton points out, if you have to bit-bang the interface the effective clock rate will fall dramatically.

You need a separate 2 MHz clock for each ADC, so that each device sees the appropriate clock edge only when its correct data value is present. You can't just demux the data lines unless you add storage for each data line so that it holds the correct value until the clock edge occurs.

For incoming data you need to do the reverse...capture each ADC MISO line on the appropriate clock edge.

• You are right for the slave inputs, but I think that in this case I can skip the MOSI muxer, and drive all of them with the exact same 2MHz clock, effectively making all slaves operate simultaneously. (Because the MOSI message to all ADCs is always the same). For incoming MISO data, I don't need to capture at exactly the clock edge, as the slaves will hold the value for the whole clock cycle. And as I'm only having 12 slaves, there should be no issues with rise and fall times when I skip the first and last 4 sub-cycles. Jun 26, 2019 at 15:45
• Apart from that you are obviously right, this is very impractical. I was just curious if it wuld work Jun 26, 2019 at 15:46

In addition to what's shown in your diagram, you also need a counter to generate the SEL signals for the [de]multiplexers. And this counter needs to count through 12 states rather than 8 or 16.

Before you're done you'll be implementing all this in a CPLD.

I'd recommend instead to find a different ADC. Either a multi-channel one with a faster SPI interface. Or a parallel output one that will let you do the readbacks through something like a 74LVC595A serial-parallel buffer (which can then read from your SPI port at well into the 10's of MHz).

• Yes, probably the best idea. I guess I'm going to use a 16 channel ADC with 20 MHz SPI clock. Jun 26, 2019 at 18:28

The ADC chips will not work with a clock frequency higher than the 2MHz specified in the datasheet.

Your best bet will be to divide down the MCLK signal generated by the MSP and provide it to all ADCs in parallel. From the datasheet, the ADC chips have a maximum conversion rate of 100k samples/second and this is dependent on a the clock that's driving the chip.

One way you could approach this would be to bit-bang the entire thing. If you use common MCLK, MOSI, and /CS lines with each MISO pin tied to a separate GPIO pin, you could bang out a common command to all chips at the same time. Reading the values from all 12 ADCs would then be done by reading the GPIO registers on each cycle of the bit-banged clock.

On most micros, with care choosing the GPIO pins, it would be possible to read the state of 12 pins with only a few instructions: a byte- or word-wide read into a register and a bit shift on the register, repeated 12 times for the 12-bit ADC values. The register would then hold the bitstream to be sorted out in software, possibly in a different (not time-critical) section of code.

I've never used an ESP32; I don't know how practical/possible it would be to read the GPIO pins in this fashion.

If you can't control the GPIO timing of the ESP32 due to interrupts happening (while it manages the wifi and stack, for example) it may be worth considering adding a cheap microcontroller to sit between the ESP32 and the 12 ADC chips and handle all of this for you; presenting instead a single SPI- or UART-connected slave that you could use with the hardware-assisted blocks present in the ESP32.