Menu

How to use the SPI mode

Charlier Maximilien
There is a newer version of this page. You can find it here.

How to use the SPI mode

After opening a connection with the Bus Pirate (see minimal
example
), it
must be switched to binary mode with the bp_bin_init function. SPI
mode can then be selected using function bp_bin_mode_spi. All the
functions and constants described below are declared in the
spi.h header file.


Initial setup

The SPI mode can be configured using a variety of parameters such as
the clock "speed", the polarity and phase (i.e. when data is sent or
sampled relatively to clock edges). It is also possible to power the
external device from the Bus Pirate by enabling the power supply
output (3.3V and 5V). Finally, the SPI outputs can be setup in an
open-drain configuration to allow communication with devices connected
to a different power rail.

Clock speed

Use function bp_bin_spi_set_speed(BP * bp, unsigned char speed) to
set the SPI clock speed. Constants for selectable speeds are defined in
spi.h. Note that some speeds are not handled correctly by the Bus
Pirate firmware (see
forum).

  • BP_BIN_SPI_SPEED_30K
  • BP_BIN_SPI_SPEED_125K
  • BP_BIN_SPI_SPEED_250K
  • BP_BIN_SPI_SPEED_1M
  • BP_BIN_SPI_SPEED_2M
  • BP_BIN_SPI_SPEED_2_6M
  • BP_BIN_SPI_SPEED_4M
  • BP_BIN_SPI_SPEED_8M

Polarity and phase

Function bp_bin_spi_set_config(BP * bp, unsigned char config) is
used to configure the SPI mode, that is clock polarity, sample phase
and output type. The configuration is built by OR'ing the predefined
constants defined below.

Clock polarity

  • Idle low : BP_BIN_SPI_CLK_IDLE_LOW
  • Idle high : BP_BIN_SPI_CLK_IDLE_HIGH

Output clock edge

  • Idle to active : BP_BIN_SPI_CLK_EDGE_LOW
  • Active to idle : BP_BIN_SPI_CLK_EDGE_HIGH

Input sample phase

  • Middle : BP_BIN_SPI_SMP_MIDDLE
  • End : BP_BIN_SPI_SMP_END

Output type

  • Open drain (H=HI-Z, L=GND) : BP_BIN_SPI_LV_HIZ
  • Normal (H=3.3V, L=GND) : BP_BIN_SPI_LV_3V3

The example below shows how to use the bp_bin_spi_set_config
function. In this example, the clock is active high (_IDLE_LOW), data
is sent on the clock falling edge (_EDGE_HIGH), data is sampled at the
middle of a clock cycle (_SMP_MIDDLE), and output pins (CLK and MOSI)
are not open-drain (_LV_3V3).

:::C
if (bp_bin_spi_set_config(bp, BP_BIN_SPI_CLK_IDLE_LOW |
                              BP_BIN_SPI_CLK_EDGE_HIGH |
                      BP_BIN_SPI_SMP_MIDDLE |
                      BP_BIN_SPI_LV_3V3) != BP_SUCCESS)
  return -1;

Power, pull-ups and I/Os

Function int bp_bin_spi_set_periph(BP * bp, unsigned char config)
is used to configure power supply, internal pull-up resistors and
additional I/O pins (CS and AUX). Each of them can be enabled
separately by OR'ing the following constants. When one constant
is included, its corresponding function is enabled (e.g. if
**BP_BIN_SPI_PERIPH_POWER is included, the power supply will be
activated).

  • BP_BIN_SPI_PERIPH_POWER : enables power supply
  • BP_BIN_SPI_PERIPH_PULLUPS : enables pull-up resistors (need to power Vpu pin)
  • BP_BIN_SPI_PERIPH_AUX : AUX pin high
  • BP_BIN_SPI_PERIPH_CS : CS pin high

The following example clarifies this usage. In the example, only the
power supply is enabled.

:::C
if (bp_bin_spi_set_periph(bp, BP_BIN_SPI_PERIPH_POWER) < 0)
  return -1;

SPI transactions

This part describes how SPI transactions can be started, ended and how
data can be read and written.

Chip select (/CS)

The Chip Select (/CS) line is used to select the active device on the
SPI bus. The /CS line goes low at the beginning of an SPI transaction
and goes high at the end.

Function bp_bin_spi_cs(bp, 0) is used to assert the /CS signal
(make it going low) while function bp_bin_spi_cs(bp, 1) is used to
de-assert the /CS signal.

Read/Write

Reads and writes happen at the same time on the SPI bus. As a
consequence, reading and writing is performed with a single function :
bp_bin_spi_bulk(BP * bp, unsigned char * data, unsigned char
nlen)
.

Parameter data is an array of nlen bytes that will be written to the
SPI bus (through the MOSI line). That same array will contain the
bytes read from the SPI bus (through the MISO line).

The example below clarifies the usage of this function. In the
example, one byte (0x00) is written to the device, then 4 bytes are
read.

:::C
/* Assert /CS signal : start of transaction */
if (bp_bin_spi_cs(bp, 0) != BP_SUCCESS)
  return -1;

/* Send 0x00 and perform 4 reads */
unsigned char data[]= {0x00, 0xFF, 0xFF, 0xFF, 0xFF};
if (bp_bin_spi_bulk(bp, data, 5) != BP_SUCCESS)
  return -1;
/* At this stage, data[1:4] contain the 4 bytes read */

/* De-assert /CS signal : end of transaction */
if (bp_bin_spi_cs(bp, 1) != BP_SUCCESS)
  return -1;

SPI example - DW1000 Ultra Wide Band 802.15.4 radio transceiver

In this section, we present a complete example where the Bus Pirate is
used to interact with a DecaWave DW1000
transceiver
, using
the SPI binary mode. The example connects to the Bus Pirate, setup SPI
mode and reads the 32-bit DEVICE_ID register which should contain
a value such as 0xDECA0130.

The DW1000 supports different ways to read a register through SPI. We
focus on the simplest where a one-byte read command is sent first that
specifies the register address, then bytes are read starting at
that register address. Every subsequent read is performed at the next
address. In the example, the 32-bit DEVICE_ID register at address 0x00
is read. Hence, byte 0x00 is written to the device, followed by 4
reads. The first byte read is the least significant byte of the 32-bit
register (the DW1000 is low-endian).

:::C
#include <stdlib.h>
#include <stdio.h>
#include "buspirate.h"
#include <spi.h>

#define DW1000_DEVICE_ID 0x00

void error(const char * msg)
{
  fprintf(stderr, "Error: %s\n", msg);
  fflush(stderr);
  exit(EXIT_FAILURE);
}

// -----[ configure_bus_pirate ]-------------------------------------
/**

 * Configure the bus pirate to talk with DW1000 transceiver
 *
 * Protocol    : SPI (mode 0)
 * Speed       : 30kHz
 * Clock polarity : idle low
 * Output clock edge : active to idle
 * Input sample phase: middle
 * Supply power: yes
 * Output mode : 3.3V
 */
static int configure_bus_pirate(BP * bp)
{
  unsigned char version;

  if (bp_bin_init(bp, &version) != BP_SUCCESS)
    return -1;

  if (bp_bin_mode_spi(bp, &version) != BP_SUCCESS)
      return -1;

  if (bp_bin_spi_set_speed(bp, BP_BIN_SPI_SPEED_30K) != BP_SUCCESS)
    return -1;

  if (bp_bin_spi_set_config(bp, BP_BIN_SPI_CLK_IDLE_LOW |
                                BP_BIN_SPI_CLK_EDGE_HIGH |
                                BP_BIN_SPI_SMP_MIDDLE |
                                BP_BIN_SPI_LV_3V3config) != BP_SUCCESS)
    return -1;

  if (bp_bin_spi_set_periph(bp, (BP_BIN_SPI_PERIPH_POWER)) != BP_SUCCESS)
    return -1;

  return 0;
}

int dw1000_read_device_id(BP * bp, uint32_t * value)
{
  if (bp_bin_spi_cs(bp, 0) != BP_SUCCESS)
    return -1;

  unsigned char data[]= { DW1000_DEVICE_ID, 0xFF, 0xFF, 0xFF, 0xFF};
  if (bp_bin_spi_bulk(bp, data, 5) != BP_SUCCESS)
    return -1;

  *value= data[1] + (data[2] << 8) + (data[3] << 16) + (data[4] << 24);

  if (bp_bin_spi_cs(bp, 1) != BP_SUCCESS)
    return -1;
  return 0;
}

int main(int argc, char ** argv)
{
  uint32_t value;
  BP * bp;

  if (argc < 2)
    error("Not enough arguments : Bus Pirate device name required.");

  printf("Connecting to Bus Pirate.\n  device = %s...\n", argv[1]);
  bp= bp_open(argv[1]);
  if (bp == NULL)
    error("could not connect to Bus Pirate.");

  if (configure_bus_pirate(bp) < 0)
    error("could not configure SPI mode");

  printf("Read Device ID from register 0x00\n");
  if (dw1000_read_device_id(bp, &value) < 0)
     error("could not read DEVICE_ID");
  printf("DEVICE_ID = %.8X\n", value);

  if (bp_reset(bp) != BP_SUCCESS)
    error("could not reset device to user terminal");

  printf("Closing connection.\n");
  bp_close(bp);
  exit(EXIT_SUCCESS);
}

MongoDB Logo MongoDB