Microchip® Advanced Software Framework

 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
SAM Serial Peripheral Interface Master Driver w/ Vectored I/O (SERCOM SPI)

This driver for Atmel® | SMART SAM devices provides an interface for the configuration and operation of the SERCOM module in SPI master mode and uses vectored I/O for data transfers.

The following peripherals are used by this driver:

The following devices can use this driver:

The reader is assumed to be familiar with the regular SERCOM SPI driver, and how it is configured and operated. Configuration of this driver is done a similar way and actually re-uses several enumerations (configuration values) from the regular SERCOM SPI driver.

The outline of this documentation is as follows:

Prerequisites

This driver uses the SYSTEM clock driver to select the SERCOM's clock source and to configure the SERCOM for the desired baud rate. Ensure that the selected clock source is configured and that the clock system is initialized. This is typically done indirectly with system_init(), or directly with system_clock_init().

According to the datasheet, the minimum and maximum limits for the baud rate is given by: \( \frac{1}{2^{17}} \times f_{clk} \le f_{baud} \le \frac{1}{2} \times f_{clk} \).

Module Overview

This SERCOM SPI master driver supports uni- and bidirectional transfers of 8-bit data with vectored I/O, also know as scatter/gather. It does not implement control of SS or slave addressing since the intended usage is in stacks which usually have their own protocols and handshaking schemes.

Vectored I/O

Vectored I/O enables the transfer of data from/to any number of buffers with arbitrary memory locations without having to do several transfers, i.e., one buffer at a time. This feature is useful in stacks because it allows each layer of the stack to have a dedicated data buffer, thus avoiding the need for a centralized data buffer that the different layers must use in cooperation.

The vectored I/O relies on arrays of buffer descriptors which must be passed to the driver to start a transfer. These buffer descriptors specify where in memory each buffer is, and how large they are. The figure below illustrates this for an example with three buffers of varying sizes that are transmitted.

Note that the last descriptor must indicate no next buffer in order for the driver to detect that the end of the buffer list has been reached. This means that for N buffers, N+1 buffer descriptors are needed.

Bidirectional transfers are supported without any restrictions on the buffer descriptors, so the number of bytes and buffers to receive and transmit do not have to be the same.

See Also
spi_master_vec_transceive_buffer_job() for details on starting transfers.

OS support

Since this driver is interrupt-driven, it is possible for the MCU to run other code while a transfer is on-going.

In a single-threaded application, this can be achieved by starting a transfer and then avoid any waiting for completion until absolutely required, e.g., when a new transfer is needed.

But in a multi-threaded application, for example based on FreeRTOS, one can utilize semaphores to let the OS know when a function is waiting and thus blocking the thread, and that other threads can be run instead. Put another way, the waiting can be made efficient.

This driver has an internal semaphore which is used to signal to the OS whenever a function is waiting for a transfer to complete. And since the semaphore datatypes and functions are OS-specific, the support has been made configurable by the use of macros. Note that support for FreeRTOS is already implemented, but must be enabled.

See Also
CONF_SPI_MASTER_VEC_OS_SUPPORT for more on the configurable OS support.

Special Considerations

Interrupt safety

This driver should not be used within interrupt contexts. The reason for this is that the driver itself is interrupt driven. Further, the configurable OS support is implemented with the assumption that transfers are only started in threads, not in interrupt service routines, because it gives the simplest API.

Signal MUX

The SERCOM module has two layers of signal multiplexing in SPI mode:

  1. SERCOM pad MUX: This routes the SPI signals to internal lines.
  2. PORT pin MUX: This routes the internal line to a device pin.

Both of these layers are configured in the configuration structure, using the members named mux_setting and pinmux_padN. These must be set in combination.

The driver supplies values for the SERCOM pad MUX from the standard ASF SERCOM SPI driver. For the PORT pin MUX configuration, refer to the peripheral include file for the device (pio_samd20XNN.h ) and use the macros that are prefixed with PINMUX_, such as PINMUX_PA04D_SERCOM0_PAD0. It is also possible to use the default pin MUX setting for a SERCOM pad by using the PINMUX_DEFAULT macro. The defaults are defined in the file sercom_pinout.h.

Note that for spi_master_vec_init() to function properly with the macro PINMUX_DEFAULT, the order of the values in pinmux_padN must be correct, i.e., pinmux_pad0 must contain the pin MUX setting for multiplexing SERCOM pad 0, and so on.

Extra Information

For extra information, see Extra Information for SERCOM SPI Master Driver w/ Vectored I/O. This includes:

Examples

For a list of examples related to this driver, see Examples for SERCOM SPI Master Driver w/ Vectored I/O.

API Overview

Modules

 Quick Start Guide(s)
 In this section you can find a list of all Quick Start guides related to the SAM Serial Peripheral Interface Master Driver w/ Vectored I/O (SERCOM SPI).
 

Data Structures

struct  spi_master_vec_bufdesc
 Buffer descriptor structure. More...
 
struct  spi_master_vec_config
 Driver configuration structure. More...
 
struct  spi_master_vec_module
 Driver instance. More...
 

Typedefs

typedef uint16_t spi_master_vec_buflen_t
 Buffer length container. More...
 

Enumerations

enum  _spi_master_vec_direction {
  SPI_MASTER_VEC_DIRECTION_READ,
  SPI_MASTER_VEC_DIRECTION_WRITE,
  SPI_MASTER_VEC_DIRECTION_BOTH,
  SPI_MASTER_VEC_DIRECTION_IDLE
}
 Transfer direction. More...
 

Configuration and Initialization

static void spi_master_vec_get_config_defaults (struct spi_master_vec_config *const config)
 Initialize configuration with default values. More...
 
enum status_code spi_master_vec_init (struct spi_master_vec_module *const module, Sercom *const sercom, const struct spi_master_vec_config *const config)
 Initialize hardware and driver instance. More...
 

Enable/Disable and Reset

void spi_master_vec_enable (const struct spi_master_vec_module *const module)
 Enable the SERCOM SPI module. More...
 
void spi_master_vec_disable (struct spi_master_vec_module *const module)
 Disable the SERCOM SPI module. More...
 
void spi_master_vec_reset (struct spi_master_vec_module *const module)
 Reset the SERCOM SPI module. More...
 

Lock/Unlock

static enum status_code spi_master_vec_lock (struct spi_master_vec_module *const module)
 Attempt to get lock on driver instance. More...
 
static void spi_master_vec_unlock (struct spi_master_vec_module *const module)
 Unlock driver instance. More...
 

Read/Write and Status

enum status_code spi_master_vec_transceive_buffer_job (struct spi_master_vec_module *const module, struct spi_master_vec_bufdesc tx_bufdescs[], struct spi_master_vec_bufdesc rx_bufdescs[])
 Start vectored I/O transfer. More...
 
static enum status_code spi_master_vec_get_job_status (const struct spi_master_vec_module *const module)
 Get current status of transfer. More...
 
static enum status_code spi_master_vec_get_job_status_wait (const struct spi_master_vec_module *const module)
 Get status of transfer upon job end. More...
 
static enum status_code spi_master_vec_transceive_buffer_wait (struct spi_master_vec_module *const module, struct spi_master_vec_bufdesc tx_bufdescs[], struct spi_master_vec_bufdesc rx_bufdescs[])
 Start vectored I/O transfer, wait for it to end. More...
 

typedef uint16_t spi_master_vec_buflen_t

Buffer length container.

Transfer direction.

Enumerator
SPI_MASTER_VEC_DIRECTION_READ 
SPI_MASTER_VEC_DIRECTION_WRITE 
SPI_MASTER_VEC_DIRECTION_BOTH 
SPI_MASTER_VEC_DIRECTION_IDLE 

void spi_master_vec_disable ( struct spi_master_vec_module *const  module)

Disable the SERCOM SPI module.

Parameters
[in,out]moduleDriver instance to operate on.

References _sercom_get_interrupt_vector(), Assert, NULL, SPI_MASTER_VEC_DIRECTION_IDLE, STATUS_OK, and system_interrupt_disable().

Referenced by spi_master_vec_reset().

void spi_master_vec_enable ( const struct spi_master_vec_module *const  module)

Enable the SERCOM SPI module.

This function must be called after spi_master_vec_init() before a transfer can be started.

Parameters
[in,out]moduleDriver instance to operate on.

References _sercom_get_interrupt_vector(), Assert, and system_interrupt_enable().

Referenced by main().

static enum status_code spi_master_vec_get_job_status ( const struct spi_master_vec_module *const  module)
inlinestatic

Get current status of transfer.

Parameters
[in]moduleDriver instance to operate on.
Returns
Current status of driver instance.
Return values
STATUS_OKif idle and previous transfer succeeded.
STATUS_BUSYif a transfer is ongoing.
<other>if previous transfer failed.

Referenced by spi_master_vec_get_job_status_wait().

static enum status_code spi_master_vec_get_job_status_wait ( const struct spi_master_vec_module *const  module)
inlinestatic

Get status of transfer upon job end.

Parameters
[in]moduleDriver instance to operate on.
Returns
Current status of driver instance.
Return values
STATUS_OKif idle and previous transfer succeeded.
<other>if previous transfer failed.

References spi_master_vec_get_job_status(), and STATUS_BUSY.

Referenced by main(), and spi_master_vec_transceive_buffer_wait().

enum status_code spi_master_vec_init ( struct spi_master_vec_module *const  module,
Sercom *const  sercom,
const struct spi_master_vec_config *const  config 
)

Initialize hardware and driver instance.

This function configures the clock system for the specified SERCOM module, sets up the related pins and their MUX, initializes the SERCOM in SPI master mode, and prepares the driver instance for operation.

Precondition
system_init() must have been called prior to this function.

The SERCOM SPI module is left disabled after initialization, and must be enabled with spi_master_vec_enable() before a transfer can be done.

Parameters
[out]moduleDriver instance to initialize.
[in,out]sercomSERCOM module to initialize and associate driver instance with.
[in]configDriver configuration to use.
Returns
Status of initialization.
Return values
STATUS_OKif initialization succeeded.
STATUS_ERR_INVALID_ARGif driver has been misconfigured.

References _sercom_get_sercom_inst_index(), _sercom_get_sync_baud_val(), _sercom_instances, _sercom_set_handler(), _spi_master_vec_int_handler(), Assert, spi_master_vec_config::baudrate, spi_master_vec_config::data_order, spi_master_vec_config::gclk_generator, spi_master_vec_config::mux_setting, NULL, spi_master_vec_config::pinmux_pad0, spi_master_vec_config::pinmux_pad1, spi_master_vec_config::pinmux_pad2, spi_master_vec_config::pinmux_pad3, spi_master_vec_config::run_in_standby, sercom_set_gclk_generator(), system_gclk_chan_config::source_generator, SPI_MASTER_VEC_DIRECTION_IDLE, STATUS_ERR_INVALID_ARG, STATUS_OK, system_apb_clock_set_mask(), SYSTEM_CLOCK_APB_APBC, system_gclk_chan_enable(), system_gclk_chan_get_config_defaults(), system_gclk_chan_get_hz(), system_gclk_chan_set_config(), system_is_debugger_present(), and spi_master_vec_config::transfer_mode.

Referenced by main().

static enum status_code spi_master_vec_lock ( struct spi_master_vec_module *const  module)
inlinestatic

Attempt to get lock on driver instance.

This function checks the instance's lock, which indicates whether or not it is currently in use, and sets the lock if it was not already set.

The purpose of this is to enable exclusive access to driver instances, so that, e.g., transactions by different services will not interfere with each other.

Parameters
[in,out]modulePointer to the driver instance to lock.
Return values
STATUS_OKif the module was locked.
STATUS_BUSYif the module was already locked.

References STATUS_BUSY, STATUS_OK, system_interrupt_enter_critical_section(), and system_interrupt_leave_critical_section().

void spi_master_vec_reset ( struct spi_master_vec_module *const  module)

Reset the SERCOM SPI module.

This function will disable and reset the SPI module to its power on default values.

Parameters
[in,out]modulePointer to a driver instance.

References Assert, and spi_master_vec_disable().

enum status_code spi_master_vec_transceive_buffer_job ( struct spi_master_vec_module *const  module,
struct spi_master_vec_bufdesc  tx_bufdescs[],
struct spi_master_vec_bufdesc  rx_bufdescs[] 
)

Start vectored I/O transfer.

This function initiates a uni- or bidirectional SPI transfer from/to any number of data buffers. The transfer is interrupt-driven and will run in the background, after this function has returned.

The buffers to transmit from or receive into must be described in arrays of buffer descriptors. These arrays must end with descriptors that specify zero buffer length. The first descriptor in an array can not specify zero length. The number of bytes to transmit and to receive do not have to be equal.

If the address for a receive buffer is set to NULL, the received bytes corresponding to that buffer descriptor will be discarded. This is useful if slave is already set up to transfer a number of bytes, but the master has no available buffer to receive them into. As an example, to receive the two first bytes and discard the 128 following, the buffer descriptors could be:

// Read two status bytes
{.data = status_buffer, .length = 2},
// Discard 128 data bytes
{.data = NULL, .length = 128},
// End of reception
{.length = 0},
};

To initiate a unidirectional transfer, pass NULL as the address of either buffer descriptor array, like this:

// Transmit some buffers
// Receive some buffers
Precondition
spi_master_vec_init() and spi_master_vec_enable() must have been called before this function.
Parameters
[in,out]moduleDriver instance to operate on.
[in]tx_bufdescsaddress of buffer descriptor array for bytes to transmit.
  • NULL if the transfer is a simplex read.
[in,out]rx_bufdescsaddress of buffer descriptor array for storing received bytes.
  • NULL if the transfer is a simplex write.
Returns
Status of transfer start.
Return values
STATUS_OKif transfer was started.
STATUS_BUSYif a transfer is already on-going.

References Assert, spi_master_vec_bufdesc::data, length, spi_master_vec_bufdesc::length, SPI_MASTER_VEC_DIRECTION_BOTH, SPI_MASTER_VEC_DIRECTION_READ, SPI_MASTER_VEC_DIRECTION_WRITE, STATUS_BUSY, STATUS_OK, system_interrupt_enter_critical_section(), and system_interrupt_leave_critical_section().

Referenced by main(), and spi_master_vec_transceive_buffer_wait().

static enum status_code spi_master_vec_transceive_buffer_wait ( struct spi_master_vec_module *const  module,
struct spi_master_vec_bufdesc  tx_bufdescs[],
struct spi_master_vec_bufdesc  rx_bufdescs[] 
)
inlinestatic

Start vectored I/O transfer, wait for it to end.

Parameters
[in,out]moduleDriver instance to operate on.
[in]tx_bufdescsaddress of buffer descriptor array for bytes to transmit.
  • NULL if the transfer is a simplex read.
[in,out]rx_bufdescsaddress of buffer descriptor array for storing received bytes.
  • NULL if the transfer is a simplex write.
Returns
Status of transfer start.
Return values
STATUS_OKif transfer succeeded.
STATUS_BUSYif a transfer was already on-going.
<other>if transfer failed.

References spi_master_vec_get_job_status_wait(), spi_master_vec_transceive_buffer_job(), and STATUS_BUSY.

Referenced by main().

static void spi_master_vec_unlock ( struct spi_master_vec_module *const  module)
inlinestatic

Unlock driver instance.

This function clears the instance lock, indicating that it is available for use.

Parameters
[in,out]modulePointer to the driver instance to lock.