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:
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} \).
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 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.
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.
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.
The SERCOM module has two layers of signal multiplexing in SPI mode:
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.
For extra information, see Extra Information for SERCOM SPI Master Driver w/ Vectored I/O. This includes:
For a list of examples related to this driver, see Examples for SERCOM SPI Master Driver w/ Vectored I/O.
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.
void spi_master_vec_disable | ( | struct spi_master_vec_module *const | module | ) |
Disable the SERCOM SPI module.
[in,out] | module | Driver 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.
[in,out] | module | Driver instance to operate on. |
References _sercom_get_interrupt_vector(), Assert, and system_interrupt_enable().
Referenced by main(), and test_at25dfx_init().
|
inlinestatic |
Initialize configuration with default values.
[out] | config | Configuration struct to initialize. |
References spi_master_vec_config::baudrate, spi_master_vec_config::data_order, spi_master_vec_config::gclk_generator, GCLK_GENERATOR_0, spi_master_vec_config::mux_setting, PINMUX_DEFAULT, 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, SPI_DATA_ORDER_MSB, SPI_SIGNAL_MUX_SETTING_D, SPI_TRANSFER_MODE_0, and spi_master_vec_config::transfer_mode.
Referenced by at25dfx_spi_master_vec_get_config_defaults(), and main().
|
inlinestatic |
Get current status of transfer.
[in] | module | Driver instance to operate on. |
STATUS_OK | if idle and previous transfer succeeded. |
STATUS_BUSY | if a transfer is ongoing. |
<other> | if previous transfer failed. |
Referenced by spi_master_vec_get_job_status_wait().
|
inlinestatic |
Get status of transfer upon job end.
[in] | module | Driver instance to operate on. |
STATUS_OK | if idle and previous transfer succeeded. |
<other> | if previous transfer failed. |
References spi_master_vec_get_job_status(), status, and STATUS_BUSY.
Referenced by _at25dfx_chip_get_nonbusy_status(), _at25dfx_chip_issue_read_command_wait(), _at25dfx_chip_issue_write_command_wait(), 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.
The SERCOM SPI module is left disabled after initialization, and must be enabled with spi_master_vec_enable() before a transfer can be done.
[out] | module | Driver instance to initialize. |
[in,out] | sercom | SERCOM module to initialize and associate driver instance with. |
[in] | config | Driver configuration to use. |
STATUS_OK | if initialization succeeded. |
STATUS_ERR_INVALID_ARG | if 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, 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(), and test_at25dfx_init().
|
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.
[in,out] | module | Pointer to the driver instance to lock. |
STATUS_OK | if the module was locked. |
STATUS_BUSY | if the module was already locked. |
References status, 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.
[in,out] | module | Pointer 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:
To initiate a unidirectional transfer, pass NULL
as the address of either buffer descriptor array, like this:
[in,out] | module | Driver instance to operate on. |
[in] | tx_bufdescs | address of buffer descriptor array for bytes to transmit.
|
[in,out] | rx_bufdescs | address of buffer descriptor array for storing received bytes.
|
STATUS_OK | if transfer was started. |
STATUS_BUSY | if 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 _at25dfx_chip_get_nonbusy_status(), _at25dfx_chip_issue_read_command_wait(), _at25dfx_chip_issue_write_command_wait(), main(), and spi_master_vec_transceive_buffer_wait().
|
inlinestatic |
Start vectored I/O transfer, wait for it to end.
[in,out] | module | Driver instance to operate on. |
[in] | tx_bufdescs | address of buffer descriptor array for bytes to transmit.
|
[in,out] | rx_bufdescs | address of buffer descriptor array for storing received bytes.
|
STATUS_OK | if transfer succeeded. |
STATUS_BUSY | if a transfer was already on-going. |
<other> | if transfer failed. |
References spi_master_vec_get_job_status_wait(), spi_master_vec_transceive_buffer_job(), status, and STATUS_BUSY.
Referenced by main().
|
inlinestatic |
Unlock driver instance.
This function clears the instance lock, indicating that it is available for use.
[in,out] | module | Pointer to the driver instance to lock. |