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 Hardware reference manuals and Serial Peripheral Interface (SPI).
Digi adds an API to Linux to manage these SPI interfaces as master, able to communicate with slave devices. To use this API, include the following header file:
#include <libdigiapix/spi.h>
You can configure them and communicate with other SPI slave devices.
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 |
---|---|
|
Requests an SPI by its device and slave number. The device number ( It returns a pointer to |
|
Requests an SPI by its alias name. The SPI alias mapping must be defined in the It returns a pointer to |
The requested SPI must be freed once it is no longer needed. See Free an 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.
/* 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:
-
Add a section called [SPI], if one does not already exist.
-
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) .
-
[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);
For information on including libdigiapix.conf in your Digi Embedded Yocto images, see Define interface aliases.
|
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.
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.
You must free the slaves pointer when ldx_spi_list_available_slaves() returns a value greater than 0.
|
#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 an 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);
spi_t *spi = ...;
/* [...] */
ldx_spi_free(spi);
Configure SPI communication
You can configure several SPI interface parameters using the SPI API:
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 |
---|---|
|
|
|
|
|
|
|
|
Chip select
The chip select configuration determines how this line behaves during the transfer. Its possible values are:
Chip select | Description |
---|---|
|
The chip select is active at low level |
|
The chip select is active at high level |
|
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 |
---|---|
|
The first transferred bit is the most significant one |
|
The first transferred bit is the less significant one |
All these configuration parameters are specified within a transfer configuration 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 |
---|---|
|
Reads and stores the SPI transfer mode of the SPI interface. This function returns |
|
Sets the transfer mode configuration of the SPI interface. This function returns |
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 |
---|---|
|
Retrieves the SPI bits-per-word:
If there is an error, the function returns |
|
Sets the SPI bits-per-word:
If there is an error, the function returns |
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 |
---|---|
|
Retrieves the SPI bus speed in Hz. If there is an error, the function returns -1. |
|
Sets the SPI bus speed in Hz. The function returns |
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:
Write data to the SPI interface
You can send data to the SPI slave device using the ldx_spi_write()
function:
Function | Description |
---|---|
|
Sends data to the SPI slave device.
The function returns |
#define DATA_SIZE 16
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 |
---|---|
|
Reads data from the SPI slave device.
The function returns |
#define DATA_SIZE 16
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 |
---|---|
|
Transfers data with the SPI slave device.
The function returns |
tx_data and rx_data buffer length in bytes must be the value of length .
|
#define DATA_SIZE 16
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 Embedded 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.