Manually hold MOSI output pin high in SPI mode

In SPI mode, is there a way to manually hold the MOSI line high between data packets? The device I’m communicating with uses this periodically as some sort of sync signal.

I’ve tried the _/- commands, and receive the message: “ERROR: command has no effect here”.

Using BP6, latest firmware.

1 Like

SPI has a “write with read” option. Use {} instead of for chip select, and then send whatever value you need and a read will be returned with it.


A picture’s worth a thousand words. I need the Data Output (MOSI) line to be high between packets, as in the circled area. You can see that the Slave responds with a similar high condition on the MISO line.

This device uses a continuous series of 5x 4-byte transfers, with this high condition marking the endpoint of each 20-byte sequence. I need to emulate this by arbitrarily holding the MOSI line high even when there is no clock signal (i.e. when the data bus is not active).

1 Like

In that case, maybe 3 wire mode will help? It has the bitwise commands you mentioned earlier.

It appears to lack documentation. There is also a minor chance it’s open drain and you need to enable pull-up resistors.

I can provide more information tomorrow morning, but maybe that is enough to get you going as you’re already mostly there with the bitwise syntax.

1 Like

In SPI mode, the MOSI line reverts to active-low state at the end of each transfer, even with pullups enabled.

I’ll check out 3-wire mode. Thanks.

1 Like

sPI uses the stock arm spi hardware and it does what it does, but 3 wire runs in the PIO and it has a ton more flexibility.

Now I’m really curious if it is push pull or open drain. It is based on 2wire which is open drain. I’m not at a pc at the moment and I don’t remember how I implemented it.

Verified that 3WIRE is push-pull output. It should work exactly like SPI but give you those individual pin controls.

Push-pull . . . so speak softly and use a low current limit.

I think I’m figuring the syntax out for Master mode, timing and whatnot. I’m bit-banging IO3 for the Strobe pin, leaving the port open and not connecting CS.

For example (pullups are on):

>0x80 0xa0 0x1e 0xe1 - @.3 d:150 a.3 d:300 0x90 0x80 0x1d 0xc2

The above:
-sends data 0x80a01ee1
-drives MOSI high (-)
-releases IO3 Strobe high (@.3 using the pullup)
-waits 150us (d:150)
-drives IO3 Strobe low (a.3)
-waits 300us (d:300)
-sends data 0x90801dc2

The resulting data waveforms look promising.

1 Like

Is there a way to use 3-Wire or SPI in Slave Mode? I need to talk to both Master and Slave modules independently out of circuit.

Communication in Master mode seems doable with the method in the post above. But how do I set up the BP in SPI/3-Wire Slave mode so I can talk to the other (Master) module using the clock source from the target device?

1 Like

Glad it looks promising.

The Bus Pirate has never been able to be a slave device on any hardware version, but with 5+ it should be possible.

The question has always been: how to you actually interact from the slave side? Do we set up some register spaces with canned answers? This should be doable, but how do we actually enter/load all that in a logical way?

I’m 100% in favor of adding slave modes where possible. I will need help devising a way to make that work though. Any thoughts are certainly welcome - for example, what exactly would you need the slave mode to do?

Hey @ian

maybe a “basic” bpv3-style lang again… so you can program the logic in the console… imo is the solution…

a list of regexp → responses… may works… but is not perfect…

or maybe create a python lib…

basic is my vote :smiley:

… Or maybe something like QuickJS? (too much for RP2040? xD)

https://bellard.org/quickjs/

The user could have super powerful scripts in JavaScript to use with the Bus Pirate, and JavaScript is a well-documented and widely known language…

The scripts could receive events that we define and send response commands / data, etc.

uint8_t ev_spi_cs_low(void)
{
...
    foo();
....
}

rep_byte_arr[] ....

uint8_t ev_spi_slave_byte_recv_cb(uint8_t byte)
{
...
    return rep_byte_arr[cur++];
}

uint8_t ev_spi_slave_init(void)
{
...
    conf_pio_spi_slave_for_script(CLKE...);
....
}

And along with this, maybe a main script-thread that can interact with the global array or whatever…

1 Like

PIO-Idea for fine-grain-bit control (for very slow SPI cases):

  • RP overclock to 250MHz. USB should works fine at this speed
  • A super fast script lang
  • A super fast simple PIO (250MHz) with "bit-signal-control" that can be configured from the script: CLKE, etc.
  • That PIO will consume from a SLAVE → MASTER buffer when the SPI master generates the clock, etc.
  • The script will receive events like CS LOW, a byte has been received (MASTER → SLAVE buffer), etc.
  • The script will populate the SLAVE → MASTER buffer, so data is available when needed.
  • The script must run parallel logic to decide how to fill the SLAVE → MASTER buffer, making decisions based on the incoming MASTER → SLAVE buffer, Or decisions based on other factors… etc.

super fast simple PIO with “bit-signal-control” pseudo-slow-spi-crap-PIO-idea:

wait 0/1 pin x [?]
in pins 
push ; data bit to script
nop [?] ; time for script ...
pull  ; data bit from script
out ...  ; bit pin output
...

btw, I’m fully aware of how slow and bad of an idea this is :joy:.

But the script would have control over every bit that gets sent in response to the master’s data.

If this is unfeasible because it’s too slow or crap idea, we could implement the same idea at the byte level instead:

For example, we could implement autopull + autopush at the byte level, which would stay blocked until the script **feeds data in time ** (Let’s pray it’s script-fast enough! :joy:) to unlock PIO and keep everything running smoothly… I’m not sure what SPI slave speed / scenario would make this viable… Maybe I’m talking nonsense :joy:. I think having at least byte-level control for SPI Slave is essential for many cases…

Maybe is better idea use the RP’s real SPI peripheral?

Maybe pure bit-banging using SIO is better for scripts? (no extra PIO cycles… no extra PIO instructions machinery… DMA extra cycles…)

@henrygab , @ian & community opinion??

1 Like

I’d have to think about what my preferences for command interface would be. Not my area of expertise.

But … As to what specifically I’m trying to do…

My target device has a mainboard and a control panel, linked by SPI. The control panel has many lights and buttons etc. These 2 boards communicate periodically using SPI, with mainboard as Master, control panel as Slave. Data format is a series of 5x 4-byte transfers, full-duplex. Data from control panel contains info about button status etc. Data from mainboard contains instructions to the control panel about which lights to turn on etc.

Baud rate is 62500, with pauses between each 4-byte block. You can see the data trace at the beginning of this thread.

Basically, I need to issue commands to the mainboard as if I was the control panel. And read back what the mainboard is trying to display on the control panel.

To do this, I need to have an array of 20 data bytes that gets repeated over and over in 3-wire Slave mode. I will want to change the values in the array from time to time, without disrupting the data flow. For instance, to simulate a 300ms button press I would need to set the bit representing a certain button, wait 300ms, then clear that bit. Several SPI refreshes will happen during that time.

3-wire rather than native SPI mode because I need manual IO pin control, and data line high/low control between data packets (see above).

1 Like

It really is a challenge to build a “universal” slave interface :slight_smile:

@Dreg I think you might be close with a host PC application that can be programmed with all the desired values.

A script interface is interesting if implementing it is easy, but then once you’re programming its not that far from designing a slave directly on a PICO board or whatever.

The problem I see with the PC application is the speed that can be achieved in SPI slave mode.

1 Like

Sure, it’s a really interesting problem. There has to be a smart solution.

Once you get beyond basic register reads, like @Joe24 needing to change values on a timer, it gets complicated to do it in an elegant way and also at reasonable speeds.

For debugging slaves my goto is a logic analyzer with real hardware. If that won’t do, I write a simple custom slave device to emulate whatever I need to test.

It is sort of like the “universal production test rig”. It’s something a bunch of people have toyed with for years, but at the end of the day a custom rig for each project is the cheapest and easiest route.

If we can come up with something reasonable I’ll try to implement it.

Obviously you wouldn’t be able to write data from a terminal fast enough to keep up with a live situation. But if there was a way to have some basic “if X, then send predetermined message A/B/C” functionality, that would be very useful.

In my case, I was thinking maybe an extension of the existing BP macro feature. I’d write a different macro for each “control panel button” that I want to trigger. Except that the data would have to repeat endlessly (maybe a script?). And I’d need some conditional control over the start/sync point.

On this specific device, if the MOSI line is high at the falling edge of Strobe, I know this is the start of a data transfer (see trace above). At that point, I would fire Macro A which would load the output buffer with (or bit-bang) the 20 bytes of Message A.

1 Like

At the most basic level, there would need to be the ability to accept serial input data in lockstep to an external data clock, and to set some kind of interrupt when the receive buffer is full.

1 Like

The spi module has a slave mode, and we can also write one for the PIO. The hardware infrastructure is there.

At the most basic level we could just spit out precanned data for a given byte command. Maybe load them from a config file.

sPI device interfaces aren’t that consistent though. Maybe it’s a command followed by 3 address bytes (spi flash), maybe it’s a write and read back operation (spi SRAM, psram). Sometimes it’s a configuration register being set that should have some effect later (ADC?).

It feels like the options are something really basic (a text file with commands to match and the response to give), or a full fledged script system as mentioned by dreg.

Is there an existing scripting framework to integrate? Micropython exists. I wouldn’t want to roll my own as it would never be as nice as something properly maintained. We’d need basic logic (if then else), some way to store data?, A timer for your use case, etc, etc.

I can make something very basic to test. I’m not sure how useful it would be, but we can see how it works:

Address Bytes RX Bytes TX Data TX
3 3 ? 0 1 2 3 4 5 6 7 8 9 10

For example an SPI flash read command is 0x03, followed by 3 address bytes (depending on density), the we return some bytes.

Immediately we run into a problem. 0x03 sets up a read, which can then read out the entire chip (say 64Mbits). If we reach the end of the Data TX and the read is still happening, what is the best behavior? I guess looping back to the beginning of the dummy data.

If a single or a very small number of slave commands would be useful, we could add a slave command that loads a single command and response into RAM.

slave -a 0x03 -r 0x03 -t 0 1 2 3 4 5 6 7 8 9 10

Something similar to above but listening for a single command at a time.

The other aspect is speed. I believe you mentioned 65.2mhz, and that’s going to be an issue for several reasons.

At 62.5MHz we only have two clock ticks to respond to the command. It will take the RP2040 way longer than that to decide how to respond to the command and load the data to output. Second, the buffers and protection resistors usually top out at 14-20MHz, depending on how everything is connected.

It might be realistic to aim for a 10MHz slave if we’re only watching for a single command, or use a lookup table (how to load this?).

I will test the SPI peripheral in slave mode so I understand how it works, but this will only echo the master data into the Bus Pirate terminal. The real grind is designing an interface that is generally useful beyond one specific project.

I made a simple slave command in SPI mode (not pushed):

SPI> {0}{1}{2}

CS Enabled
TX: 0(3)
CS Disabled
CS Enabled
TX: 1(4)
CS Disabled
CS Enabled
TX: 2(5)
CS Disabled
SPI> {0}

CS Enabled
TX: 0(6)
CS Disabled

Master.

SPI> slave
Starting SPI slave
Read: 00, Wrote: 04
Read: 01, Wrote: 05
Read: 02, Wrote: 06
Read: 00, Wrote: 07

Slave.

There is an off by a byte error, probably because I’m using the sdk function wrong. By default the RP2040 spi uses a CS between each byte, which is madness, so let’s try to do a full frame…

SPI> {0 1 2}

CS Enabled
TX: 0(5) 1(5) 2(134)
CS Disabled
Read: 04, Wrote: 06
Read: 00, Wrote: 07
Read: 02, Wrote: 08

Mode one does seem to work. Values RX and TX are wrong, but again, I think I am misunderstanding how the SDK slave function is supposed to be used.

Pushed the test code to spi_slave branch.