Bus Pirate: Raw SPI mode


See the latest version in the documentation wiki.

Raw SPI is a new mode that provides high-speed access to the Bus Pirate SPI hardware. It was developed in conjunction with Michal Ludvig, so that AVRDude can use the  Bus Pirate to program AVR microcontrollers and EEPROMs.

Firmware v2.3 includes two new raw I/O modes that give computer software and scripts direct access to the Bus Pirate hardware. Hopefully this opens the door to a whole new class of Bus Pirate applications, like chip programmers. In this post we describe the raw SPI access mode. We’ll describe the raw bitbang mode in a few days. The protocol is documented below the break.

Raw SPI mode command table

  • 00000000 – Enter raw bitbang mode, reset to raw bitbang mode
  • 00000001 – SPI mode/rawSPI version string (SPI1)
  • 00000010 – CS low (0)
  • 00000011 – CS high (1)
  • 00001101 – Sniff all SPI traffic
  • 00001110 – Sniff when CS low
  • 00001111 – Sniff when CS high
  • 0001xxxx – Bulk SPI transfer, send 1-16 bytes (0=1byte!)
  • 0100wxyz – Configure peripherals, w=power, x=pullups, y=AUX, z=CS
  • 01100xxx – Set SPI speed, 30, 125, 250khz; 1, 2, 2.6, 4, 8MHz
  • 1000wxyz – SPI config, w=output type, x=idle, y=clock edge, z=sample

Commands are a single byte, except bulk SPI transfers. The Bus Pirate responds to SPI write commands with the data read from the SPI bus during the write. Most other commands return 0x01 for success, or 0x00 for failure/unknown command.

Key points

  • Send the raw byte value 0x00 into the user command prompt 20 times to enter bitbang mode.
  • Enter 0x01 in bitbang mode to enter raw SPI mode.
  • Return to raw bitbang mode from raw SPI mode by sending 0x00 one time.
  • Operations that write a byte to the SPI bus also return a byte read from the SPI bus.
  • Hex values shown here, like 0x00, represent actual byte values; not typed ASCII entered into a terminal.
  • Other values are shown as 8bit binary numbers. Here’s a binary->decimal->hex converter.


00000000 – Enter raw bitbang mode, reset to raw bitbang mode

This command has two purposes. First, send it to the command line interface 20 times to enter the raw bitbang binary mode. It’s also used to exit the raw SPI mode and return to raw bitbang mode.

Send the value 0x00 to the Bus Pirate command line interface 20 times to enter raw bitbang mode. The Bus Pirate replies ‘BBIOx’, where x is the raw bitbang version number (currently 1).

Once in raw SPI mode (see command 00000001), the 0x00 command returns to raw bitbang mode. Send 0x00 once to return to raw bitbang mode.

00000001 – Enter raw SPI mode, display version string

Once in raw bitbang mode, send 0x01 to enter raw SPI mode. The Bus Pirate responds ‘SPIx’, where x is the raw SPI protocol version (currently 1). Get the version string at any time by sending 0x01 again.

0000001x – CS high (1) or low (0)

Toggle the Bus Pirate chip select pin, follows HiZ configuration setting. CS high is pin output at  3.3volts, or HiZ. CS low is pin output at  ground. Bus Pirate responds 0x01.

00001101 – Sniff all SPI traffic, 0000111x – Sniff when CS low(0)/high(1) (updated in v5.1)

The SPI sniffer is implemented in hardware and should work up to 10MHz. It follows the configuration settings you entered for SPI mode. The sniffer can read all traffic, or filter by the state of the CS pin.

  • [/]  – CS enable/disable
  • \xy – escape character (\) precedes two byte values X (MOSI pin) and Y (MISO pin) (updated in v5.1)

Sniffed traffic is encoded according to the table above. The two data bytes are escaped with the ‘\’ character to help locate data in the stream.

Send ‘r’ to restart the sniffer without returning to SPI mode. Send any other byte to exit, Bus Pirate responds 0x01 on exit.

If the sniffer can’t keep with the SPI data, the MODE LED turns off and the sniff is aborted. (new in v5.1)

0001xxxx – Bulk SPI transfer, send/read 1-16 bytes (0=1byte!)

Bulk SPI allows direct byte reads and writes. The Bus Pirate expects xxxx+1 data bytes. Up to 16 data bytes can be sent at once, each returns a byte read from the SPI bus during the write.

Note that 0000 indicates 1 byte because there’s no reason to send 0. BP replies 0x01 to the bulk SPI command, and returns the value read from SPI after each data byte write.

0100wxyz – Configure peripherals w=power, x=pull-ups, y=AUX, z=CS

Enable (1) and disable (0) Bus Pirate peripherals and pins. Bit w enables the power supplies, bit x toggles the on-board pull-up resistors, y sets the state of the auxiliary pin, and z sets the chip select pin. Features not present in a specific hardware version are ignored. Bus Pirate responds 0x01 on success.

Note: CS pin always follows the current HiZ pin configuration. AUX is always a normal pin output (0=GND, 1=3.3volts).

01100xxx –  SPI speed

000=30kHz, 001=125kHz, 010=250kHz, 011=1MHz, 100=2MHz, 101=2.6MHz, 110=4MHz, 111=8MHz

This command sets the SPI bus speed according to the values shown. Default startup speed is 000 (30kHz).

1000wxyz – SPI config, w=HiZ/3.3v, x=CKP idle, y=CKE edge, z=SMP sample

This command configures the SPI settings. Options and start-up defaults are the same as the user terminal SPI mode. w= pin output HiZ(0)/3.3v(1), x=CKP clock idle phase (low=0), y=CKE clock edge (active to idle=1), z=SMP sample time (middle=0). The Bus Pirate responds 0x01 on success.

Default raw SPI startup condition is 0010. HiZ mode configuration applies to the SPI pins and the CS pin, but not the AUX pin. See the PIC24FJ64GA002 datasheet and the SPI section[PDF] of the PIC24 family manual for more about the SPI configuration settings.

0010xxxx, 0011yyyy – Enter SPI data byte in nibbles, read SPI byte Deprecated in v5.1

Data bytes can also be entered in two nibbles using this pair of commands. 0011yyyy loads the upper 4 bits of a data byte. 0010xxxx loads the lower 4 bits of the data byte, and writes it to the SPI bus. The bus write returns a byte read.

A byte is transmitted over SPI each time the low bits are sent. Be sure to send the upper bits first, then send the lower bits to complete the data and transmit it. If the upper four bits are the same as the previous byte, then you can increase speed by sending just the lower four bits.

Entering the upper 4 bits returns 1 for success, or 0 for failure. Entering the low bits returns a byte read from the SPI bus.

Join the Conversation


  1. Nice. Couple of questions though…
    1) Is this the final version of the protocol or can we still do some mods?
    2) The reset command 0x00 – does it reset the BP into HiZ mode?
    3) 0011000x (CS) – the fact that it’s inverted is IMO really confusing. If someone is that far that he is using binary mode he probably knows whether he wants CS High or Low – there’s no need for ‘user friendly’ enable/disable confusion.
    4) 0100wxyz (Configure peripherals) – can we have a command for reading the peripherals config as well? Maybe make a rule “if (cmd & 0x10 == 0x00) { it’s a get_config } else { it’s a set_config }”
    Similar for reading the the speed and SPI config.
    5) Instead of returning 0x01 for set_config it may be worth returning the previous state.
    6) I’m not sure why you retained the nibble-based transfers? The same functionality as for (0x1X 0x2Y) can be achieved in the two bytes with bulk transfer (0x50 0xXY).

    I’m going to make an avrdude patch now ;-)

  2. 1)We can mod it all we want :)
    2) 0x00 returns to raw bitbang mode (with HiZ pin states). I added command 0b1111 in raw bitbang mode to reset the Bus Pirate and return to the user terminal.
    3) Changed it.
    4 & 5) Great idea, absolutely, but is it OK to leave it out until SPI2? I need to push the v2.3 firmware to manufacturing and this will take a bit of effort. You can always assume the default settings after moving from raw bitbang mode to raw SPI mode, and your settings take effect if you get 0x01.
    6) I wrote a Perls script that programs/dumps/erases EEPROMs and I used that command. To read an EEPROM I load 0xFF in two nibbles, then just hit it with the lower nibble to read bytes until the end.

  3. @Ian – how about leaving all this binmode stuff out from 2.3 and include it in 2.4 after it settles and shows its strengths and weaknesses? It would be annoying to have two slightly incompatible binmode versions out in the wild.

    For now AVRdude is all right with text mode parsing. Today I increased its speed by 75% comparing to the original patch, so it’s actually usable :-) Lets do the binmode right in the next couple of weeks for firmware 2.4.

    How does that sound?

  4. Are 4&5 the only items you’d like to see before a v1 protocol? It’s too much of a pain to remove it prior to the next release, so it’ll be there. The question is how ‘final’ it is.

    Here’s the update I’d propose:
    # 00000001 – SPI mode/rawSPI version string (SPI1)
    # 00000010 – CS low (0)
    # 00000011 – CS high (1)
    # 0001xxxx – Bulk SPI transfer, send 1-16 bytes (0=1byte!)
    # 0010xxxx – Low 4 bits of byte + single byte write/read
    # 0011xxxx – High 4 bits of byte
    # 0100wxyz – Configure peripherals w=power, x=pullups, y=AUX, z=CS
    # 0101wxyz – read peripherals w=power, x=pullups, y=AUX, z=CS
    # 01100xxx – Set SPI speed,
    # 01110xxx – Read SPI speed,
    # 1000wxyz – SPI config, w=output type, x=idle, y=clock edge, z=sample
    # 1001wxyz – read SPI config, w=output type, x=idle, y=clock edge, z=sample

    Edit: swapped config. peripheral and set SPI speed.

    Added three low speed options to SPI:
    000=1kHz, 001=5kHz, 010=10khz, 011=30kHz 101=125kHz, 110=250khz, 111=1000kHz

    30khz is the lowest possible speed for hardware SPI, we can always use a software library if we need something slower.

  5. I updated the guide to use the new command format. I’ve updated the code to use the new values, and added the read value commands. I’m doing a test compile now, and then I’ll release 2.3.

  6. For an official binmode firmware would like to see:
    1) Indication what binmode version is supported in the ‘I’ output

    2) Have an attack plan for I2C, 1W, UART, … support, and also ADC and perhaps Freq probe. Not only SPI. The commands doing similar stuff should have similar semantics for all protocols.

    3) Make sure that the BP’s responses to binmode commands are easy to handle. For instance that they have a constant length – at the moment 0x00 response is BBIO1, 0x01 is SPI1 and the rest is one byte. That may make parsing a bit inconvenient (not sure, it just rings the bell. I haven’t implemented binmode for avrdude yet).

    4) The two dedicated CS commands are redundant because we can now read peripherals status and set CS from there.

    5) I’m still not in favour of 0x2x and 0x3x for sending SPI data. It’s redundant to Bulk spi. This is not about being user friendly because you will always control binmode from a program.

    6) Where has bitbanging support gone? IMO we should reserve commands with the MSB set for bitbanging. That is:
    if (cmd & 0x80) { set_pins(cmd & 0x7F) } else { process_command(cmd) }

    7) Regarding speeds – Standard speeds for I2C are 100kHz and 400kHz if I’m not mistaken. SPI should probably support those as well. What’s the max freq the PIC can generate? Perhaps have 1k (for the extreme cases where 30k is too fast), 30k, 100k, 400k, 1MHz, PIC-Max MHz.

    I don’t believe in releasing an official firmware with an immature protocol. That’ll just cause pain for the app developers in the long run. Especially since fw2.3 should be the official firmware for BPv3 and therefore with potentially thousands of users. No binmode is better than bad binmode – at least there’ll be only one protocol requiring support from the apps.

    How come it’s too much pain to remove it? Simply disable handling of 0x00 in a prompt and you’re done. If there is no way to enter binmode it’s the same as if there is no binmode at all ;-)

  7. 1+3) I don’t want to add more cruft to user mode, so the version string is announced when you send 0x01 and enter raw SPI mode. It will always be four bytes long, so just expect four bytes.

    2+6) Raw bitbang mode is the primary raw mode. Other modes like SPI (and future modes) are accessed with commands from that library. For raw SPI, first you enter raw bitbang mode (0x00), then you choose raw SPI from the raw bitbang mode. Other modes will be the same way. I’ll document this mode tomorrow, but I wanted to get this one out today because it might actually be used.

    4) Yes, but it’s nice to be flexible, especially for new developers that want to send a simple byte command and not manage the bits for all peripherals.

    7) I’ll handle I2C speeds in an I2C library. Pic can’t generate less than 30kHz SPI, but it can go up to 8mHz.

  8. I’m using the 4bit commands in my own apps, so I plan to keep them. Is there a negative to having them? They allow me to do single byte read operations without overhead.

  9. 1) What does <fw2.3 respond to 0x00? Syntax error? I already have 2.3-pre so I can't test that without downgrading.

    4+5) nothing negative, I just don't like redundancy ;-)

    7) 8MHz would be nice. For instance ENC28J60 rev B4 requires SPI at 8MHz or higher (see errata for this revision, corrected in B5+ I guess).

    Also – can you document how to get out from binmode? Is it 0xFF?

    All right, will see if SPI2 will be needed ;-)

  10. Thank you so much for your help. I think it’s becoming a much stronger, more logical protocol.

    1) Version prior to 2.3 don’t respond at all. The command prompt ignores all non-ASCII characters. If you don’t see “BBIOx”, then it’s not working.

    To reset:
    * If you’re in raw spi, exit to raw bitbang with 0x00.
    * Send 0x0F(0b1111), the bus pirate resets.

    Here’s the commands for binmode, if you’re curious:
    * 00000000 //Enter binmode, show version string “BBIO1”.
    * 00000001 //enter rawSPI mode, responds “SPI1”.
    * …. //more protocols
    * 00001111 //reset Bus Pirate (returns to user terminal)
    * 010xxxxx //set pins as input(1) or output(0), responds with read.
    * 1xxxxxxx //set pins high (1) or low (0), responds with read.

    Bitbang bitmaping:

  11. I agree, it’s got a good shape. Thanks for your patience with all my whining ;-)

    One more feature request for later: in bitbanging mode implementing waiting for a pin change and returning pin status when change happens. AVRs have ‘PCINT’ – Pin Change INTerrupt, I’m sure PICs have something similar. That may be useful for sort of a “logic analyser” app. But that can indeed wait after 2.3 ;-)

  12. It looks like there’s a unintended consequence of the protocol. Some terminals seem to send a NULL character (0x00) on startup, which is sending the Bus Pirate into binary mode accidentally. I’ve made a nightly compile which requires that 0x00 be entered 20 times to enter raw binary mode, and this seems to have cleared up any problems. I’ve updated this guide and the bitbang mode guide.

  13. Hi, i am villamany from Spain, i think that could be a good feature to add commands for bulk read/write up to 256 (1 byte command + 1 byte bulk size), and up to 65536 (1byte command + 2 byte bulk size).
    And add commands for bulk reads only(sending always dummy byte 255) and for bulk write only (ignoring all received bytes). This can be very usefull for reduce the amount of data betwen ftdi and pc. And great speed gain for example programing flash memories. Thanks…

  14. Any chance of logging rate above 115,200? Or more condensed log format?
    We have an application that chatters away at 126 SPI byte cycles in 78msec, every 100msec. In raw binary mode, each SPI byte cycle costs 6 log bytes:
    where ‘O’ and ‘I’ are binary MOSI and MISO bytes, respectively.
    So our application generates 756 bytes of log at 10Hz, or 75,600bps long-term, but about 98kbps during the 78msec active section, which overruns 115.2kbps because it’s actually a little bursty in there. [Just lucky that we “almost” fit in 115,200bps, I guess.]
    The next (doubled) baudrate would have ample margin, assuming the PIC has horsepower to serve it. Or: If raw binary SPI reported each MOSI/MISO pair with single backslash and no parens, the serial burden would be half:
    I’ll try to build this myself, but anyone who can beat me to it gets my cheers!

    1. I’m doing v5.1 updates today, I’m already going to do a minor update to the SPI sniffer, I’ll use your condensed format too.

    2. I implemented the suggested format, along with the ability to reset the SPI sniffer without returning to SPI mode (using ‘r’ in terminal or binary mode), and the sniffer now stops and turns off the MODE LED when there’s a buffer overflow. Would you please give it a test? The latest nightly compile is in the SVN, a direct download link is in the forum thread I posted below.

      1. It works great! I’ve got my VB.net app drinking from the 115200bps fountain without loss for almost 5 minutes already.

        You might display “SPI2” as a clue since this is different logging output.

        Cheers! Cheers!

      2. Thanks for the update. Would you be able/willing to put your app under an open source license and including it in the forum or SVN? I can give you access to the SVN.

        I’m reticent to change to SPI2 because some pretty big apps (AVRdude) depend on other core features of SPI1, but you’re right, we should differentiate.

  15. It’s looking like the SPI hardware (PIC peripheral) is able to mis-sync, and stay mis-synced, if probes are attached to a live SPI bus at the wrong moment. It appears CS assertion/deassertion does not reset SPI byte framing. Here is a digested snippet [the raw 5.1 “\xy” is displayed “XX(YY)”, and the “…” are abbreviations for “00(00)”]:
    …/reconnected probes here/
    You can see MOSI messages, 80-7E-blahblah-7F, alternating with MISO messages, 7E-blahblah-7F. Whenever I break and reconnect probes, BusPirate comes up in a different sync; sometimes I get lucky and sometimes not. If I leave probes reconnected, SPI sync never recovers by itself.
    SPI setup bytes are:
    Const bENTER_RAW_BITBANG_MODE As Byte = &H00 ‘ 0b00000000, 20 times
    Const bENTER_RAW_SPI_SUBMODE As Byte = &H01 ‘ 0b00000001
    Const bSET_CS_HI_HI_Z As Byte = &H03 ‘ 0b00000011 ‘ per HiZ setting
    Const bSET_SPI_SPEED_8MHZ As Byte = &H67 ‘ 0b01100111
    Const bSET_SPI_CLOCK_IDLE_HIGH As Byte = &H84 ‘ 0b10000100 (or 0b10000110 for active-to-idle edge samping?)
    Const bSNIFF_WHEN_CS_LOW As Byte = &H0E ‘ 0b00001110 ‘ per HiZ setting

  16. I’m having trouble getting into bitbang mode. I can connect with a terminal to the bus pirate, but when I type “0x00” on the keyboard, it says “0x00 ready”. Do I really need to type it 20 times?

Leave a comment

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail. You can also subscribe without commenting.