10

For the impatient, you can skip the background.

Background

I'm programming a set of microcontrollers that communicate with SPI. There's one master and n slaves that share the bus. There's no chip select. (It's not a bad design, but n is large and there's not enough space for n extra lines).

It's therefore the responsibility of the slaves to keep their MISO in high impedance and at most one of them speak. This is done by responding only when their id is polled.

Now we'd like to have an initial discovery phase where the master discovers slaves with what ids are attached to it. To make life easier (on some aspects), we'd like to have the id unique (and therefore e.g. 32 bits). This makes it impossible for the master to simply poll ids one by one and see who responds (there are too many possibilities).

To solve this problem, I devised a variation of binary search where the slaves collectively respond and the master is able to quickly find the minimum id. The slave with that id is told to not participate anymore and the algorithm repeats. (Details unimportant).

There's one problem though. The collective response needs to be the logical OR (or logical AND) of all the responses. I've been told that the line can be configured in such a way that the MISO bus can act as a logical OR. What I've been told is:

  • Set MISO on master as Pull-up and
  • Set MISO on every slave as Open-drain.

I've tried this, but with even a single slave, this configuration doesn't work (the oscilloscope shows a constant zero on the line). If I configure MISO on the master as high-impedance input, I can see with the oscilloscope that the voltage drops to half where the bits of the outputs from two slaves differ (basically short-circuit I presume).

Note: configuring MISO on master as high-impedance and slaves each as push-pull, I can talk to each of them individually even if there are many of them on the same bus. I mean, I doubt it's a problem of the line itself.

Question

My question is, if this is at all possible, and if so, how can I configure the input and output pins of the master and the slaves so that the shared MISO line would act as logical OR (or logical AND)?


Edit

  1. Turned out it becomes an OR with negative-true logic (basically an AND).

  2. The problem with single slave was resolved with writing 1 to the pull-up pin on the master. Previously it had an initial state of 0.

Edit 2

Turned out the ST slave overrides my GPIO configuration of MISO as open-drain and was forcing it high when one was written. I resolved to silencing SPI and outputting MISO in this particular case manually.

Shahbaz
  • 297
  • 2
  • 10
  • I'd hate to ask, because I'm sure you've thought of it, but have you considered using I2C or CAN? They're designed for n devices, whereas SPI is really designed to be used with a chip select for each device. – Bob Sep 26 '13 at 13:09
  • @bob, yes. They are too slow. Anyway, if the answer to my question is "it's impossible", then we'd just have to do a bit of manual work, but still the end product is much better with SPI. – Shahbaz Sep 26 '13 at 13:12
  • 1
    It's a pity that you are using 32 bits as the address because if you were using 24 bits (16,772,216 variations) you could send a "discover" command and wait 16,772,216 clocks and you could have all your slave information. At 10Mbps that would take less than 2 seconds and no clashes to figure out. Hey ho - you got me thinking so +1 for that. – Andy aka Sep 26 '13 at 13:23
  • @Andyaka, 24 bits may also be not bad (but 32 bits is certainly better). if I understood you correctly, you mean each slave responds at its id'th clock with a 1 and the master looks at which clocks generated a one? That's not bad, except the slaves respond in bytes. So each slave responds with 8 bits, and unless I can make the bus act as OR, still the response from one slave gets "lost" within the responses by the other slave (the 1 from one slave gets pulled down by the 0s from all the rest). – Shahbaz Sep 26 '13 at 13:32
  • @Shahbaz if you have control over the slave's code you could make this a "special" where the slave only responds with 1 bit at their allocated time. Yes, you got the gist of my musings. – Andy aka Sep 26 '13 at 13:34
  • @Andyaka, I do have control over the slaves, but I haven't seen an option for my STs (the slaves) to respond with a single bit (not that I saw anything like that in any other micro that I worked with). – Shahbaz Sep 26 '13 at 13:38
  • Could you tells us which MCUs are involved? – avakar Sep 26 '13 at 14:29
  • @avakar, Slaves are ST (STM8S) and master is ARM (Cortex-M8). Do you need more specific models? – Shahbaz Sep 26 '13 at 14:48
  • @Shahbaz, could you be more concrete? I wanted to look up the datasheets. – avakar Sep 26 '13 at 14:54
  • @avakar, STM8S003F3U and CY8C5366LTI (which is a Cortex-M3, actually). – Shahbaz Sep 26 '13 at 18:12

4 Answers4

5

Your SPI-without-select is what Microchip uses on their MCP23017 chips (and others). Nothing wrong with that approach.

Yes, what you want is possible, but you must get the slaves to be open-drain. You could cheat by putting a (schottky) diode in series with each output if you can't get them to behave as open-drain.

Your enumeration approach is the same as used by the Dallas one-wire bus for enumereation, and by the CAN bus for arbitration.

But a serious drawback of your approach is that the speed is now limited by the rise time, driven by the pull-up resistor. This will be slower than when driven by a push-pull output, and will likely limit the speed at which you can operate the bus.

If you have two pins to spare on each slave you could daisy-chain them, and have an enumeration scheme based on their place in the daisy chain.

Wouter van Ooijen
  • 48,572
  • 1
  • 63
  • 136
  • Yes I forgot to mention I was also told I need to reduce the speed (which I did by about 20 fold (down from 4Mpbs to 128Kbps)). That's an initial phase though and my algorithm can deal with the slower speed (it's still quite fast). Unfortunately, I doubt we'd redesign the hardware now. The cost is more than simply ignoring this phase and telling the master what ids to expect. – Shahbaz Sep 26 '13 at 13:40
  • Back to the question, I've already configured the slaves as open-drain. How should I configure the master? – Shahbaz Sep 26 '13 at 13:41
  • 1
    Nothing special at the master's MISO pin, except the pullup. I doubt you will reach 128Kbps with a pull-up design, but YMMV. Reading some in-depth I2C documents might help, that is a wire-or pull-up bus, so every trick applied there might help you. – Wouter van Ooijen Sep 26 '13 at 14:39
  • Thanks a lot. I'll try slowing the bus down even more to see what happens. I guess I have to finally study and understand what these pull-up, open-drain and others actually mean. (Software engineer here!) – Shahbaz Sep 26 '13 at 14:46
  • 64Kbps also didn't work. Do you have a guess what could be more appropriate? – Shahbaz Sep 26 '13 at 14:59
  • The I2C bus started at 100 kHz, newer designs reach 400 kHz (or even higher), so my guess would be that 100 kHz should be possible on a bus with a not-too-large capacitance. How large is the bus (line) phyiscally, and how large is n? (Side note: a real hardware engineer might be useful to do some calculations for you. A scope would be handy to, to check the signal waveform (especially the rise time)). – Wouter van Ooijen Sep 26 '13 at 15:10
  • You cannot calculate or even guess the speed (or the pull-up resistor) to be used without knowing the bus line capacitance, or inferring it from measuring the signal rise time with some known pull-up resistor. – Laszlo Valko Sep 26 '13 at 16:29
  • @Laszlo: that why I said "on a bus with not-too-large-capacitance", thinking of the limitations that apply to I2C. – Wouter van Ooijen Sep 26 '13 at 17:47
  • The bus is about 20cm long. n that I'm testing now is 2, but could be up to 60 – Shahbaz Sep 26 '13 at 18:08
  • 1
    Put an oscilloscope on the bus and check what happens. The rise time might be too slow, but there could also be ringing. – Wouter van Ooijen Sep 26 '13 at 20:43
4
  • Set MISO on master as Pull-up and
  • Set MISO on every slave as Open-drain.

I've tried this, but with even a single slave, this configuration doesn't work (the oscilloscope shows a constant zero on the line).

You need to check what is the equivalent resistance of the master i/o pin in pull-up mode.

Typically, the pull-up mode has a very high resistance, maybe 50 kOhms or higher. It's intended to keep the pin from glitching due to emi or other noise, or to set a default for very slow control signals, and at the same time not to waste too much power doing that.

As Wouter pointed out, in an open-drain bus the speed is limitted by the pull-up resistor. Higher resistor values make the bus slower. Typical values in I2C (which gets 100 or 400 kHz) are 1 to 5 kOhms. You will want a similar pull-up resistance in order to achieve a similar speed.

I think you need to use an external pull-up resistor (of 1 to 5 kOhms or so) rather than the master's i/o pin pull-up to make this scheme work.

The Photon
  • 129,671
  • 3
  • 164
  • 309
  • Thanks for the hints. I'm not an electronics guy, but I'd have to ask my coworker to take a look at your suggestion. I need the wired-or for only an initial phase of the program and in the normal phase the pin is not configured as pull-up. So probably external resistor is not an option. – Shahbaz Sep 26 '13 at 18:18
  • If you have one free i/o pin on the master micro, you could connect it to the bus through, say, 5 kOhms. Then turn it high during bus enumeration and turn it high-Z during normal communication. – The Photon Sep 26 '13 at 19:09
1

In order for a wired-and bus to work, the nodes on the bus need to be open-drain, i.e. they must transmit

  • the logic low by pulling down strongly, and
  • the logic high by disconnecting from the bus.

Furthermore, the bus must be pulled up weakly.

The peculiar behavior that you see with a single non-transmitting master and a single transmitting slave could be explained either by the master pulling up strongly or the slave pulling down weakly.

You need to determine which of the above is happening.

Put the slave into high-impedance mode and connect the bus to ground via a 10k resistor. If the line voltage doesn't change significantly, then the master is pulling up strongly and you need to fix that. Otherwise, do the same procedure with the slave (this time connect the resistor to Vcc); if the line voltage rises significantly, the slave is pulling down weakly (fix that). Otherwise, look for time-space distortions in the area around you.

avakar
  • 3,206
  • 2
  • 18
  • 18
  • Excuse my ignorance in electronics, but if the slave pulls down strongly, doesn't that make the bus act as AND? I'm saying since the slaves that want high are disconnecting and the ones that want low are pulling down, so the overall result is down, no? – Shahbaz Sep 26 '13 at 14:43
  • @Shahbaz, my bad, of course the bus will be wired-and, I fixed the answer. If you want wired-or, just invert the polarities (master pull down weakly, slaves pull up strongly). – avakar Sep 26 '13 at 14:51
  • Reading in wikipedia, I realized that they refer to it as wired-and or wired-or with negative-true logic. – Shahbaz Sep 26 '13 at 18:09
1

I would suggest having a passive pull-up or pull-down on the bus (I'll assume pull-up), and having slaves actively drive the bus (driving high and driving low) when they have something to say and float it otherwise. Have query-address commands which takes an address and a mask, and instructs each slave to output 00 or do nothing (keep floating its output) based upon whether it likes the address and mask. If possible, have the master actively drive the bus high some time before slaves start driving it. Depending upon the strength of the pull-up and whether the master drives the bus high, before the slaves are allowed to pull it low, it might be necessary to limit bus speed during the setup phase. On the other hand, once setup is complete, having each selected slave actively drive both high and low levels will allow the bus to run much faster than could an open-collector bus.

supercat
  • 46,736
  • 3
  • 87
  • 148
  • If two slaves actively drive high and low, what am I expected to read from the bus? – Shahbaz Sep 26 '13 at 19:37
  • One should avoid ever having one slave trying to drive high while another is driving low. A slave should only drive the bus when either (1) it knows it will be the only thing doing so, or (2) it knows that it and everyone else who is driving the bus will be driving it to the opposite of its passive idle state. – supercat Sep 26 '13 at 19:47
  • What does this mean then? ... having each selected slave actively drive both high and low levels will allow the bus to ... – Shahbaz Sep 26 '13 at 19:53
  • @Shahbaz: When a slave has something to say, it should actively drive the bus high to transmit "1" bits and low to transmit "0" bits. When a slave has nothing to say, it shouldn't drive the bus at all. Note that having slaves actively drive the bus high when they want to send "1" bits will allow the bus to operate much faster than would relying upon upon a passive pull-up to drive the bus high. – supercat Sep 26 '13 at 20:01
  • @Shahbaz: What supercat is trying to say, is that in the enumeration state, a resistor should pull up the line, and slaves should only send "0" or nothing (open drain output), but afterwards, in normal communication, only a single slave should be active at a time, and the active slave should send "0" or "1" (normal output). Thus the pull-up resistor and the line capacitance only limits the bit rate during enumeration. Afterwards, in normal communication, the bit rate can be higher, as the active driving allows. – Laszlo Valko Sep 26 '13 at 22:28
  • @LaszloValko: Yup. Not only that, but line capacitance need not limit speed even during enumeration if for each enumeration step, the shared line is driven high by one device (probably the master), then floated (but kept high by the pull-up), and then conditionally pulled down (or left pulled up). If there are separate MISO and MOSI lines, the master could switch MISO to a "solid high" output when sending the middle part of the "address query" (during which slaves would all be floating) and release it just before sending the last byte. – supercat Sep 26 '13 at 23:47
  • @LaszloValko and supercat, yes in normal operation, that's exactly what's happening. I have tested up to 8Mpbs and the system works in the normal operation mode. The problem (and the question) was on the discovery phase, where the slaves have to collectively respond. – Shahbaz Sep 27 '13 at 08:19
  • @Shahbaz: I'd suggest that if MISO is separate from MOSI, having the master output a high on MISO while the slaves are all floating it and then switch to weak pull-up, and not having any slave that isn't being individually addressed ever do anything with MOSI other than pull it low or float it should give you good results. – supercat Sep 27 '13 at 15:19
  • @supercat, thanks but I found the root of the problem. The microcontroller I've been using as slave was overriding my configuration of MISO (as open-drain) and was forcing the bus to high instead of leaving it floating (when outputting 1). – Shahbaz Sep 27 '13 at 16:02