Show Contents

# SPI API

The Serial Peripheral Interface (SPI) is a full-duplex, synchronous, four-wire serial communication interface. SPI devices use a master-slave architecture with a single master. Multiple slave devices are supported with individual slave select (SS) lines.

The ConnectCore platforms have several SPI interfaces; you can find more information in their Hardware reference manuals and in the Serial Peripheral Interface (SPI) BSP documentation topic).

Digi adds an API to Linux to manage these SPI interfaces as master, able to communicate with slave devices. To use this AP, include the following header file:

    #include <libdigiapix/spi.h>

You can configure them and communicate with other SPI slave devices.

Request an SPI

Free SPI

Configure SPI communication

Communicate with the SPI interface

SPI example

## Request an SPI

Before using an SPI, you must request that interface to ensure it is available on the system. This request creates a link between the master device and the slave device. You can request an SPI with one of the following functions:

Function | Description
--- | ---
spi_t *ldx_spi_request(unsigned int spi_device, unsigned int spi_slave) | Requests an SPI by its device and slave number. The device number (spi_device) is the assigned Linux number to the SPI interface, and the slave number (spi_slave) is the chip select (SS) index of the slave device to communicate with. It returns a pointer to spi_t on success, NULL on error.
spi_t *ldx_spi_request_by_alias(char const * const spi_alias) | Requests an SPI by its alias name. The SPI alias mapping must be defined in the /etc/libdigiapix.conf file under the [SPI] section. See Establish SPI aliases. It returns a pointer to spi_t on success, NULL on error.

CAUTION! The requested SPI must be freed once it is no longer needed. See Free SPI.

Both functions may fail and return NULL for the following reasons:

- The provided device and slave numbers, or the associated device and slave numbers to the given alias do not exist.
- The API encountered problems allocating memory to initialize the SPI. Your system may have run out of resources.

    Requesting an SPI

    [...]

    /* Request an SPI using its alias */
    spi_t spi1 = ldx_spi_request_by_alias("DEFAULT_SPI");

    /* Request an SPI using device = 1 and slave = 0 */
    spi_t spi2 = ldx_spi_request(1, 0);

    printf("SPI1 uses device %d and slave %d\n", spi1->spi_device, spi1->spi_slave);
    printf("SPI2 uses device %d and slave %d\n", spi2->spi_device, spi2->spi_slave);

    [...]

### Establish SPI aliases

To help you identify the SPI interfaces of your design, you can assign aliases to your SPI Linux IDs. Map the assigned device number, slave, and name in the /etc/libdigiapix.conf file:

1. Add a section called [SPI], if one doesn't already exist.
2. Below the section name, add the list of mapped SPI interfaces following the format:

>     <alias> = <spi_device>,<spi_slave>

> Where:

> - <alias> is the human readable name for the SPI.
> - <spi_device> is the SPI device number.
> - <spi_slave> is the SPI slave index to communicate with (SS) .

    Example of SPI section

    [SPI]

    # SPI-1 on SPI board connector, slave 0.
    DEFAULT_SPI = 0,0

For example, using the configuration above, you can request an SPI with the API using DEFAULT_SPI alias. See Request an SPI.

You can get the SPI device number and the slave number associated with an alias using these functions:

    int ldx_spi_get_device(const char * const spi_alias)

    int ldx_spi_get_slave(const char * const spi_alias)

Note For information on including libdigiapix.conf in your Digi Embedded Yocto images, see Hardware interface aliasing.

### List available SPI interfaces

You can determine the list of available SPI interfaces on the target with the ldx_spi_list_available_devices() API function:

    int ldx_spi_list_available_devices(uint8_t **devices)

The function returns the number of available SPI interfaces, or -1 if there is an error.

You must free the devices pointer when ldx_spi_list_available_devices() returns a value greater than 0.

    Getting SPI device list

    [...]

    uint8_t *devices = NULL;
    int i, devices_number = 0;

    [...]

    /* Retrieve the list of available SPI interfaces */
    devices_number = ldx_spi_list_available_devices(&devices);

    if (devices_number > 0) {
    printf("The target has %d available SPI devices:\n", devices_number);
    for (i = 0; i < devices_number; i++) {
    printf(" - SPI-%d\n", devices[i]);
    }
    } else {
    printf("The target does not have any SPI interface available\n");
    }
    [...]

    free(devices);

    [...]

### List available slaves of an SPI interface

Besides listing the available SPI interfaces, you can also get the number of available slaves for an specific SPI interface:

    int ldx_spi_list_available_slaves(uint8_t spi_device, uint8_t **slaves)

ldx_spi_list_available_slaves function returns the number of available slaves for the given SPI interface, or -1 if there is an error.

CAUTION! You must free the slaves pointer when ldx_spi_list_available_slaves() returns a value greater than 0.

    Getting SPI device slaves list

    [...]

    #define DEFAULT_SPI_DEVICE  0

    [...]

    uint8_t *slaves = NULL;
    int i, slaves_number = 0;

    [...]

    /* Retrieve the list of available SPI slaves for SPI Device 0*/
    slaves_number = ldx_spi_list_available_slaves(DEFAULT_SPI_DEVICE, &slaves);

    if (slaves_number > 0) {
    printf("SPI interface %d has %d available slaves:\n", DEFAULT_SPI_DEVICE, slaves_number);
    for (i = 0; i < slaves_number; i++) {
    printf(" - SPI-%d.%d\n", DEFAULT_SPI_DEVICE, slaves[i]);
    }
    } else {
    printf("SPI interface %d does not have available slaves\n", DEFAULT_SPI_DEVICE);
    }

    [...]

    free(slaves);

    [...]

## Free SPI

You must free a requested SPI interface when it is not required anymore. To do so, use the ldx_spi_free() function.

    int ldx_spi_free(spi_t *spi)

    Freeing a requested SPI interface

    [...]

    spi_t *spi = ...;

    [...]

    /* Free SPI once it is not required anymore */
    ldx_spi_free(spi);

    [...]

## Configure SPI communication

You can configure several SPI interface parameters using the SPI API:

- The transfer mode
- The bits per word
- The bus speed

### Configure the transfer mode

The SPI interface allows you to configure the following transfer parameters:

#### Clock mode

Clock mode specifies how the clock signal and the data lines are managed during the transfer. Its possible values are:

Clock mode | Description
--- | ---
SPI_CLK_MODE_0 | CPOL=0 CPHA=0 - The data must be available before the first clock signal rising. The clock idle state is zero. The data on MISO and MOSI lines must be stable while clock is high and can be changed when clock is low. The data is captured on the clock's low-to-high transition and propagated on high-to-low clock transition.
SPI_CLK_MODE_1 | CPOL=0 CPHA=1 - The first clock signal rising can be used to prepare the data. The clock idle state is zero. The data on MISO and MOSI lines must be stable while clock is low and can be changed when clock is high. The data is captured on the clock's high-to-low transition and propagated on low-to-high clock transition.
SPI_CLK_MODE_2 | CPOL=1 CPHA=0 - The data must be available before the first clock signal falling. The clock idle state is one. The data on MISO and MOSI lines must be stable while clock is low and can be changed when clock is high. The data is captured on the clock's high-to-low transition and propagated on low-to-high clock transition.
SPI_CLK_MODE_3 | CPOL=1 CPHA=1 - The first clock signal falling can be used to prepare the data. The clock idle state is one. The data on MISO and MOSI lines must be stable while clock is high and can be changed when clock is low. The data is captured on the clock's low-to-high transition and propagated on high-to-low clock transition.

#### Chip select

The chip select configuration determines how this line behaves during the transfer. Its possible values are:

Chip select | Description
--- | ---
SPI_CS_ACTIVE_LOW | The chip select is active at low level
SPI_CS_ACTIVE_HIGH | The chip select is active at high level
SPI_CS_NO_CONT | The chip select is not controlled during the transfer

#### Bit order

The bit order determines which bit is transferred first (MSB or LSB).

Bit order | Description
--- | ---
SPI_BO_MSB_FIRST | The first transferred bit is the most significant one
SPI_BO_LSB_FIRST | The first transferred bit is the less significant one

All these configuration parameters are specified within a transfer configuration struct:

    Transfer mode struct

    /**
     * spi_transfer_cfg_t- Representation of a SPI transfer mode configuration
     *
     * @clk_mode:SPI clock mode
     * @chip_select:SPI chip select configuration
     * @bit_order:SPI bit order
     */
    typedef struct {
    spi_clk_mode_t clk_mode;
    spi_cs_t chip_select;
    spi_bo_t bit_order;
    } spi_transfer_cfg_t;

You can read and set the SPI transfer mode configuration using these functions:

Function | Description
--- | ---
int ldx_spi_get_transfer_mode(spi_t *spi, spi_transfer_cfg_t *transfer_cfg) | Reads and stores the SPI transfer mode of the SPI interface. This function returns EXIT_SUCCESS on success or SPI_MODE_ERROR if there is any error.
int ldx_spi_set_transfer_mode(spi_t *spi, spi_transfer_cfg_t *transfer_cfg) | Sets the transfer mode configuration of the SPI interface. This function returns EXIT_SUCCESS on success or SPI_MODE_ERROR if there is any error.

    Getting and setting the SPI transfer mode

    [...]

    spi_transfer_cfg_t transfer_mode = {0}
    spi_t spi = ...;

    /* Set the transfer mode parameters */
    transfer_mode.clk_mode = SPI_CLK_MODE_0;
    transfer_mode.chip_select= SPI_CS_ACTIVE_HIGH;
    transfer_mode.bit_order= SPI_BO_MSB_FIRST;

    /* Configure the transfer mode */
    ldx_spi_set_transfer_mode(spi, &transfer_mode);

    [...]

### Configure bits per word

SPI transmissions often consist of 8-bit words. However, other word sizes—for example, 16-bit words—are also common. You can read and set the SPI bits-per-word value using these functions:

Function | Description
--- | ---
spi_bpw_t ldx_spi_get_bits_per_word(spi_t *spi) | Retrieves the SPI bits-per-word: SPI_BPW_8 SPI_BPW_16 If there is an error, the function returns SPI_BPW_ERROR.
int ldx_spi_set_bits_per_word(spi_t *spi, spi_bpw_t bpw) | Sets the SPI bits-per-word: SPI_BPW_8 SPI_BPW_16 If there is an error, the function returns EXIT_FAILURE.

    Getting and setting the SPI bits-per-word

    [...]

    spi_t spi = ...;
    /* Configure the bits-per-word to be 16 */
    ldx_spi_set_bits_per_word(spi, SPI_BPW_16);

    printf("SPI %d.%d uses %d bits-per-word\n", spi->spi_device, spi->spi_slave,
           ldx_spi_get_bits_per_word(spi) == SPI_BPW_8 ? 8 : 16);

    [...]

### Configure the maximum bus speed

Before the master communicates with the slave, you must configure the maximum speed supported by the slave device. You can read and set the SPI bus speed using these functions:

Function | Description
--- | ---
int ldx_spi_get_speed(spi_t *spi) | Retrieves the SPI bus speed in Hz. If there is an error, the function returns -1.
int ldx_spi_set_speed(spi_t *spi, unsigned int speed) | Sets the SPI bus speed in Hz. The function returns EXIT_SUCCESS on success or EXIT_FAILURE if there is an error.

    Getting and setting the SPI bus speed

    [...]

    spi_t spi = ...;

    /* Configure the bus speed to be 4MHz (4000000Hz) */
    ldx_spi_set_speed(spi, 4000000);

    printf("SPI %d.%d bus speed is %dHz\n", spi->spi_device, spi->spi_slave, ldx_spi_get_speed(spi));

    [...]

## Communicate with the SPI interface

The API allows three types of communication with the slave device: read, write, and transfer.

- Write data to the SPI interface
- Read data from the SPI interface
- Transfer data to the SPI interface

### Write data to the SPI interface

You can send data to the SPI slave device using the ldx_spi_write() function:

Function | Description
--- | ---
int ldx_spi_write(spi_t *spi, uint8_t *tx_data, unsigned int length) | Sends data to the SPI slave device. The data to write is passed in the tx_data buffer. The number of bytes to write are specified with the length parameter. The function returns EXIT_SUCCESS on success or EXIT_FAILURE if there is an error.

    Writing data to the SPI interface

    [...]

    #define DATA_SIZE16

    [...]

    int i = 0;
    uint8_t tx_buffer[DATA_SIZE] = {0};
    spi_t spi = ...;

    /* Fill the tx buffer with random data */
    for (i = 0; i < DATA_SIZE; i++) {
    tx_buffer[i] = rand() % 255;
    }

    printf("Writing %d bytes to SPI %d.%d...\n", DATA_SIZE, spi->spi_device, spi->spi_slave);

    ldx_spi_write(spi, tx_buffer, DATA_SIZE);

    [...]

### Read data from the SPI interface

You can read data from the SPI slave device using the ldx_spi_read() function:

Function | Description
--- | ---
int ldx_spi_read(spi_t *spi, uint8_t *rx_data, unsigned int length) | Reads data from the SPI slave device. The read data will be stored in the rx_data buffer. The number of bytes to read are specified with the length parameter. The function returns EXIT_SUCCESS on success or EXIT_FAILURE if there is an error

    Reading data from the SPI interface

    [...]

    #define DATA_SIZE16

    [...]

    int i = 0;
    uint8_t rx_buffer[DATA_SIZE] = {0};
    spi_t spi = ...;

    printf("Reading %d bytes from SPI %d.%d...\n", DATA_SIZE, spi->spi_device, spi->spi_slave);

    ldx_spi_read(spi, rx_buffer, DATA_SIZE);

    for (i = 0; i < DATA_SIZE; i++) {
    printf("rx_data[%d] = 0x%02x\n", i, rx_data[i]);
    }

    [...]

### Transfer data to the SPI interface

You can simultaneously write and read data in one operation using the ldx_spi_transfer() function:

Function | Description
--- | ---
int ldx_spi_transfer(spi_t *spi, uint8_t *tx_data, uint8_t *rx_data, unsigned int length) | Transfers data with the SPI slave device. The data to write is passed in the tx_data buffer. The read data will be stored in the rx_data buffer. The number of bytes to transfer are specified with the length parameter. The function returns EXIT_SUCCESS on success or EXIT_FAILURE if there is an error

CAUTION! tx_data and rx_data buffer length in bytes must be the value of length.

    Transferring data to the SPI interface

    [...]

    #define DATA_SIZE16

    [...]

    int i = 0;
    uint8_t tx_buffer[DATA_SIZE] = {0};
    uint8_t rx_buffer[DATA_SIZE] = {0};
    spi_t spi = ...;

    /* Fill the tx buffer with random data */
    for (i = 0; i < DATA_SIZE; i++) {
    tx_buffer[i] = rand() % 255;
    }
    printf("Transferring %d bytes with SPI %d.%d...\n", DATA_SIZE, spi->spi_device, spi->spi_slave);

    ldx_spi_transfer(spi, tx_buffer, rx_buffer, DATA_SIZE);

    for (i = 0; i < DATA_SIZE; i++) {
    printf("rx_data[%d] = 0x%02x\n", i, rx_data[i]);
    }

    [...]

## SPI example

This example writes the page 0 of an external EEPROM memory with random data and then reads the data back to validate it.

You can import the example in Eclipse using the Digi Embeddded Yocto plugin. For more information, see Create a new DEY sample project. This example is included in Digi Embedded Yocto. Go to GitHub to see the application source code.

© 2018 Digi International Inc. All rights reserved.
SPI API updated on 22 January 2018 02:43:37 PM