2
\$\begingroup\$

I've got five SPI SRAM chips, which I want to control off a single Arduino. I've seen setups like this, which share SCLK, MOSI and MISO, with separate SS pins:

Multiple slave SPI
(image courtesty of Wikipedia)

However, I'd like to avoid using five pins for separate SS enables. I'm considering using a counter to select the different slaves, so I can cut the count down to 2 pins.

The idea is to use two pins for the counter's direction and clock. The code would look something like this:

int currentSlave = 1; // on setup I'll set the counter to 1

void SelectSlave(int id)
{
  // error checking
  if (id < 1 || id > 5) Serial.writeln("Invalid slave ID passed to SelectSlave.");

  // calculate the ID difference
  int diff = abs(id - currentSlave);
  if (diff == 0) return; // no need to do anything

  if (id > currentSlave) set(CTR_DIRECTION); // increment
  if (id < currentSlave) clear(CTR_DIRECTION); // decrement

  // calculate the number of clock pulses to send to the counter
  int pulses = 0;
  if (id > currentSlave) pulses = (1 << (id - 1)) - (1 << (currentSlave - 1));
  if (id < currentSlave) pulses = (1 << (currentSlave - 1)) - (1 << (id - 1));

  // send the clock pulses
  for(int i = 0; i < pulses; i++)
  {
    set(CTR_CLOCK);
    delay(1);
    clear(CTR_CLOCK);
    delay(1);
  }
}

I have a few questions:

  • Are there any issues related to turning multiple SS pins on and off during the interim counting period?
  • Can I rely on (most) counters being set to zero when it is first powered on?
  • Are there other/better ways to cut pin counts in this kind of setup?
\$\endgroup\$
  • \$\begingroup\$ Rather than using a direction line and clock, you might consider just using a reset line and clock. That way you can always be sure the counter is at the correct value, without having to remember where it was. Not quite as fast if you only have to move to an adjacent slave, but much simpler. You probably would want a reset line anyway, so this saves a pin. \$\endgroup\$ – tcrosley May 7 '12 at 23:52
  • \$\begingroup\$ @tcrosley Yeah, I considered that too. I'll probably end up using a 4024 counter, since it's small and simple. \$\endgroup\$ – Polynomial May 8 '12 at 0:29
  • \$\begingroup\$ Since you will have to have inverters on the output of the 4024 anyway (since chip selects are almost always negative), you might consider using NAND gates instead and a retriggerable one-shot that would keep all the chip selects high until you stopped sending clock pulses. \$\endgroup\$ – tcrosley May 8 '12 at 0:47
  • \$\begingroup\$ Not necessarily. I could just count to ~n instead of n. So instead of 1, 2, 4, 8, 16, the pulse counts would be 30, 29, 27, 23, 15. \$\endgroup\$ – Polynomial May 8 '12 at 0:51
  • \$\begingroup\$ Cute, as long as having multiple CS's on is not a problem (as discussed elsewhere). After a reset, they will all be on. \$\endgroup\$ – tcrosley May 8 '12 at 1:07
3
\$\begingroup\$

You might consider a serial-in-parallel-out shifter like 74HC164. You need 2 pins. You can generate as many enables as you wish with 2 pins. A 74HC138 decoder would need more pins as the number of outputs increased. A counter would need many many clock pulses to enable only one output at a time in some cases. The 74HC164 shifter needs at most n clock pulses to produce n enables.

\$\endgroup\$
  • \$\begingroup\$ Ah, so essentially I could "flush" the register by sending 8 clock pulses with the data pin high, then send a clock pulse with the data pin low to add a single "enable" bit. Then I can send more clock pulses with the data pin high to push the enable bit down to the device I need. That's smart, I'll look into it. Update: Ah, I actually have a few spare 74HC164N ICs hanging around, awesome :) \$\endgroup\$ – Polynomial May 8 '12 at 5:49
  • 1
    \$\begingroup\$ Typically you would use the first 3 outputs for your enables. Then you don't need to flush. Just clock in 3 data bits. \$\endgroup\$ – user9224 May 8 '12 at 10:10
  • \$\begingroup\$ Well, first 5 in my case :) \$\endgroup\$ – Polynomial May 8 '12 at 15:22
5
\$\begingroup\$

The typical way to do this is with a demux or decoder 3 to 8 decoder.

However, in your case you would need 3 signals to produce the 5 chip select lines using this approach(you actually get 8 outputs for 3 inputs).

Your approach has merit also, as long as your not sending data while you are cycling through your chip select options. Most counters have a preset line or some method of clearing them on startup.

\$\endgroup\$
  • \$\begingroup\$ So as long as I don't initiate any SPI operations (i.e. as long as I stop sending clock pulses down the CLK pin) there shouldn't be a problem with this approach? \$\endgroup\$ – Polynomial May 7 '12 at 21:55
  • \$\begingroup\$ I can't think of any, as long as the output of the counter is capable of driving the input of your spi device. \$\endgroup\$ – Matt May 7 '12 at 21:59
  • \$\begingroup\$ Is there any reason a counter wouldn't be able to drive it? I was looking for an 8-bit up/down counter, but couldn't find a good one. Right now I'm looking at using the SN74HC590, which only does count-up, for which I can just reset to 0 if the new slave ID is lower than the current one. \$\endgroup\$ – Polynomial May 7 '12 at 22:10
  • \$\begingroup\$ @Polynomial Just remember that it has to be a decoder with active low outputs (like the 74VHC138 that Matt has proposed to you). And forget about counters (which usually activate several outputs simultaneously). You need a decoder (which activates only one output at a time) or a demultiplexer (more complex). \$\endgroup\$ – Telaclavo May 8 '12 at 1:06
  • \$\begingroup\$ @Telaclavo See comments on the question itself for my solution to the active-low issue. Is there actually a reason why I shouldn't enable multiple SPI slaves for a short time? The devices don't use interrupts, and I'm not moving any data to or from the device during switching. I'm even disabling the clock during switching. I've had a quick read over the SPI protocol, and it looks like nothing happens unless I explicitly cause it to. A 3-to-8 decoder requires an extra pin, so I'd really like to avoid it. \$\endgroup\$ – Polynomial May 8 '12 at 5:43
1
\$\begingroup\$

I have faced a similar problem in the past, but with only two SPI devices. My solution was to use a PIC12F1822 as an SS switcher. It ran at 8 MIPS using its internal oscillator, and had code that did this function:

if SS input goes low then
    if MOSI is low, then
        set SS_OUT_1 low
     else
        set SS_OUT_2 low
    end if
end if

if SS input goes high then
    set SS_OUT_1 high
    set SS_OUT_2 high
end if

It just looped forever on that. The code was written in about 20 assembler instructions, and had a pretty good latency of about a microsecond IIRC.

You could write some very similar code to allow you to select any number of slaves, using MOSI as an up/down signal, E.G.

bits = 0b11111110

begin loop
    if SS input goes low then
        if MOSI is low, then
            bits <<= 1
        else
            bits >>= 1
        end if

        SS_PORT = bits
    end if

    if SS input goes high then
        SS_PORT = 0b11111111
    end if

end loop

Although you'd have to use a PIC with more pins E.G. PIC12LF1840T48A.

\$\endgroup\$
  • 2
    \$\begingroup\$ Seems overkill for such an application - I'd have to buy/build a PIC programmer. \$\endgroup\$ – Polynomial May 7 '12 at 22:06
  • \$\begingroup\$ @Polynomial - Then substitute an ATtiny for the PIC12, you have a programmer for that. An ATtiny13A-SU has 6 IO pins, that ought to do it. \$\endgroup\$ – Kevin Vermeer May 7 '12 at 22:26
  • \$\begingroup\$ Still, I could produce it cheaper (and somewhat easier) with a counter. \$\endgroup\$ – Polynomial May 7 '12 at 23:24
  • \$\begingroup\$ @Polynomial - Or a shift register ? \$\endgroup\$ – Rocketmagnet May 8 '12 at 7:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.