pico-tracker/firmware/inc/sercom/spi.h

1134 wiersze
31 KiB
C

/**
* SAM D20/D21/R21 Serial Peripheral Interface Driver
*
* Copyright (C) 2012-2014 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \page License
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel microcontroller product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* \asf_license_stop
*
*/
#ifndef SPI_H_INCLUDED
#define SPI_H_INCLUDED
/**
* SAM D20/D21/R21 Serial Peripheral Interface Driver (SERCOM SPI)
*
* This driver for SAM D20/D21/R21 devices provides an interface for
* the configuration and management of the SERCOM module in its SPI
* mode to transfer SPI data frames.
*
* The following peripherals are used by this module:
*
* - SERCOM (Serial Communication Interface)
*
* Module Overview
*
* The Serial Peripheral Interface (SPI) is a high-speed synchronous
* data transfer interface using three or four pins. It allows fast
* communication between a master device and one or more peripheral
* devices.
*
* A device connected to the bus must act as a master or a slave. The
* master initiates and controls all data transactions.
*
* The SPI master initiates a communication cycle by pulling low the
* Slave Select (SS) pin of the desired slave. The Slave Select pin is
* active low. Master and slave prepare data to be sent in their
* respective shift registers, and the master generates the required
* clock pulses on the SCK line to interchange data. Data is always
* shifted from master to slave on the Master Out - Slave In (MOSI)
* line, and from slave to master on the Master In - Slave Out (MISO)
* line. After each data transfer, the master can synchronize to the
* slave by pulling the SS line high.
*
* \subsection asfdoc_sam0_sercom_spi_bus SPI Bus Connection
* In \ref asfdoc_sam0_spi_connection_example "the figure below", the
* connection between one master and one slave is shown.
*
* \anchor asfdoc_sam0_spi_connection_example
* \dot
* digraph spi_slaves_par {
* subgraph cluster_spi_master {
* shift_reg [label="Shift register", shape=box];
* mosi_m [label="MOSI", shape=none];
* miso_m [label="MISO", shape=none];
* sck_m [label="SCK", shape=none];
* ss_m [label="GPIO pin", shape=none];
* {rank=same; mosi_m miso_m sck_m ss_m}
* label="SPI Master";
* }
* subgraph cluster_spi_slave {
* mosi_s [label="MOSI", shape=none];
* miso_s [label="MISO", shape=none];
* sck_s [label="SCK", shape=none];
* ss_s [label="SS", shape=none];
* shift_reg_s [label="Shift register", shape=box];
* {rank=same; mosi_s miso_s sck_s ss_s}
* label="SPI Slave";
* rankdir=LR;
* }
* shift_reg:e -> mosi_m:w [label=""];
* mosi_m:e -> mosi_s:w [label=""];
* mosi_s:e -> shift_reg_s:w [label=""];
* miso_s:w -> miso_m:e [label=""];
* sck_m -> sck_s;
* ss_m -> ss_s;
* shift_reg_s:se -> miso_s:e [label=""];
* miso_m:w -> shift_reg:sw [label=""];
* rankdir=LR;
* }
* \enddot
*
* The different lines are as follows:
*
* MOSI Master Input Slave Output. The line where the data is shifted
* out from the master and in to the slave.
*
* MISO Master Output Slave Input. The line where the data is shifted
* out from the slave and in to the master.
*
* SCK Serial Clock. Generated by the master device.
*
* SS Slave Select. To initiate a transaction, the master must pull
* this line low.
*
* If the bus consists of several SPI slaves, they can be connected in
* parallel and the SPI master can use general I/O pins to control
* separate SS lines to each slave on the bus.
*
* It is also possible to connect all slaves in series. In this
* configuration, a common SS is provided to \c N slaves, enabling
* them simultaneously. The MISO from the \c N-1 slaves is connected
* to the MOSI on the next slave. The \c Nth slave connects its MISO
* back to the master. For a complete transaction, the master must
* shift \c N+1 characters.
*
* SPI Character Size
*
* The SPI character size is configurable to 8 or 9 bits.
*
* Master Mode
*
* When configured as a master, the SS pin will be configured as an output.
*
* Data Transfer
*
* Writing a character will start the SPI clock generator, and the
* character is transferred to the shift register when the shift
* register is empty. Once this is done, a new character can be
* written. As each character is shifted out from the master, a
* character is shifted in from the slave. If the receiver is enabled,
* the data is moved to the receive buffer at the completion of the
* frame and can be read.
*
* Slave Mode
*
* When configured as a slave, the SPI interface will remain inactive
* with MISO tri-stated as long as the SS pin is driven high.
*
* Data Transfer
*
* The data register can be updated at any time. As the SPI slave
* shift register is clocked by SCK, a minimum of three SCK cycles are
* needed from the time new data is written, until the character is
* ready to be shifted out. If the shift register has not been loaded
* with data, the current contents will be transmitted.
*
* If constant transmission of data is needed in SPI slave mode, the
* system clock should be faster than SCK. If the receiver is
* enabled, the received character can be read from the. When SS line
* is driven high, the slave will not receive any additional data.
*
* Address Recognition
*
* When the SPI slave is configured with address recognition, the
* first character in a transaction is checked for an address
* match. If there is a match, the MISO output is enabled and the
* transaction is processed. If the address does not match, the
* complete transaction is ignored.
*
* If the device is asleep, it can be woken up by an address match in
* order to process the transaction.
*
* \note In master mode, an address packet is written by the
* \ref spi_select_slave function if the address_enabled configuration is
* set in the \ref spi_slave_inst_config struct.
*
* Data Modes
*
* There are four combinations of SCK phase and polarity with respect
* to serial data. \ref asfdoc_sam0_spi_mode_table "The table below"
* shows the clock polarity (CPOL) and clock phase (CPHA) in the
* different modes. <i>Leading edge</i> is the first clock edge in a
* clock cycle and <i>trailing edge</i> is the last clock edge in a
* clock cycle.
*
* \anchor asfdoc_sam0_spi_mode_table
* <table>
* <caption>SPI Data Modes</caption>
* <tr>
* <th>Mode</th>
* <th>CPOL</th>
* <th>CPHA</th>
* <th>Leading Edge</th>
* <th>Trailing Edge</th>
* </tr>
* <tr>
* <td> 0 </td>
* <td> 0 </td>
* <td> 0 </td>
* <td> Rising, Sample </td>
* <td> Falling, Setup </td>
* </tr>
* <tr>
* <td> 1 </td>
* <td> 0 </td>
* <td> 1 </td>
* <td> Rising, Setup </td>
* <td> Falling, Sample </td>
* </tr>
* <tr>
* <td> 2 </td>
* <td> 1 </td>
* <td> 0 </td>
* <td> Falling, Sample </td>
* <td> Rising, Setup </td>
* </tr>
* <tr>
* <td> 3 </td>
* <td> 1 </td>
* <td> 1 </td>
* <td> Falling, Setup </td>
* <td> Rising, Sample </td>
* </tr>
* </table>
*
*
* SERCOM Pads
*
* The SERCOM pads are automatically configured as seen in
* \ref asfdoc_sam0_spi_sercom_pad_table "the table below". If the receiver
* is disabled, the data input (MISO for master, MOSI for slave) can be used
* for other purposes.
*
* In master mode, the SS pin(s) must be configured using the \ref spi_slave_inst
* struct.
*
* \anchor asfdoc_sam0_spi_sercom_pad_table
* <table>
* <caption>SERCOM SPI Pad Usages</caption>
* <tr>
* <th> Pin </th>
* <th> Master SPI </th>
* <th> Slave SPI </th>
* </tr>
* <tr>
* <td> MOSI </td>
* <td> Output </td>
* <td> Input </td>
* </tr>
* <tr>
* <td> MISO </td>
* <td> Input </td>
* <td> Output </td>
* </tr>
* <tr>
* <td> SCK </td>
* <td> Output </td>
* <td> Input </td>
* </tr>
* <tr>
* <td> SS </td>
* <td> User defined output enable </td>
* <td> Input </td>
* </tr>
* </table>
*
* Operation in Sleep Modes
*
* The SPI module can operate in all sleep modes by setting the
* run_in_standby option. The operation in slave and master mode is
* shown in the table below.
*
* <table>
* <tr>
* <th> run_in_standby </th>
* <th> Slave </th>
* <th> Master </th>
* </tr>
* <tr>
* <td> false </td>
* <td> Disabled, all reception is dropped </td>
* <td> GCLK disabled when master is idle, wake on transmit complete </td>
* </tr>
* <tr>
* <td> true </td>
* <td> Wake on reception </td>
* <td> GCLK is enabled while in sleep modes, wake on all interrupts </td>
* </tr>
* </table>
*
* Clock Generation
*
* In SPI master mode, the clock (SCK) is generated internally using the
* SERCOM baud rate generator. In SPI slave mode, the clock is provided by
* an external master on the SCK pin. This clock is used to directly clock
* the SPI shift register.
*
* Pin MUX Settings
*
* The pin MUX settings must be configured properly, as not all settings
* can be used in different modes of operation.
*
*/
#include "sercom/sercom.h"
#include "system/pinmux.h"
#include "system/port.h"
#include <string.h>
#define CONF_SPI_MASTER_ENABLE true
#define CONF_SPI_SLAVE_ENABLE false
#define CONF_SPI_TIMEOUT 10000
#define SPI_WAIT_FOR_SYNC(hw) while(hw->STATUS.reg & SERCOM_SPI_STATUS_SYNCBUSY)
/**
* Define SERCOM SPI features set according to different device family.
*/
# if (SAMD21) || (SAMR21)
/** SPI slave select low detection */
# define FEATURE_SPI_SLAVE_SELECT_LOW_DETECT
/** Slave select can be controlled by hardware */
# define FEATURE_SPI_HARDWARE_SLAVE_SELECT
/** SPI with error detect feature */
# define FEATURE_SPI_ERROR_INTERRUPT
/** SPI sync scheme version 2 */
# define FEATURE_SPI_SYNC_SCHEME_VERSION_2
# endif
# ifndef PINMUX_DEFAULT
/** Default pin mux */
# define PINMUX_DEFAULT 0
# endif
# ifndef PINMUX_UNUSED
/** Unused PIN mux */
# define PINMUX_UNUSED 0xFFFFFFFF
# endif
# ifndef SPI_TIMEOUT
/** SPI timeout value */
# define SPI_TIMEOUT 10000
# endif
/**
* Interrupt flags for the SPI module.
*/
enum spi_interrupt_flag {
/**
* This flag is set when the contents of the data register has been moved
* to the shift register and the data register is ready for new data
*/
SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY = SERCOM_SPI_INTFLAG_DRE,
/**
* This flag is set when the contents of the shift register has been
* shifted out
*/
SPI_INTERRUPT_FLAG_TX_COMPLETE = SERCOM_SPI_INTFLAG_TXC,
/** This flag is set when data has been shifted into the data register */
SPI_INTERRUPT_FLAG_RX_COMPLETE = SERCOM_SPI_INTFLAG_RXC,
# ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT
/** This flag is set when slave select low */
SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW = SERCOM_SPI_INTFLAG_SSL,
# endif
# ifdef FEATURE_SPI_ERROR_INTERRUPT
/** This flag is set when combined error happen */
SPI_INTERRUPT_FLAG_COMBINED_ERROR = SERCOM_SPI_INTFLAG_ERROR,
# endif
};
/**
* SPI transfer mode.
*/
enum spi_transfer_mode {
/** Mode 0. Leading edge: rising, sample. Trailing edge: falling, setup */
SPI_TRANSFER_MODE_0 = 0,
/** Mode 1. Leading edge: rising, setup. Trailing edge: falling, sample */
SPI_TRANSFER_MODE_1 = SERCOM_SPI_CTRLA_CPHA,
/** Mode 2. Leading edge: falling, sample. Trailing edge: rising, setup */
SPI_TRANSFER_MODE_2 = SERCOM_SPI_CTRLA_CPOL,
/** Mode 3. Leading edge: falling, setup. Trailing edge: rising, sample */
SPI_TRANSFER_MODE_3 = SERCOM_SPI_CTRLA_CPHA | SERCOM_SPI_CTRLA_CPOL,
};
/**
* Frame format for slave mode.
*/
enum spi_frame_format {
/** SPI frame */
SPI_FRAME_FORMAT_SPI_FRAME = SERCOM_SPI_CTRLA_FORM(0),
/** SPI frame with address */
SPI_FRAME_FORMAT_SPI_FRAME_ADDR = SERCOM_SPI_CTRLA_FORM(2),
};
/**
* SPI signal mux settings
*
* Set the functionality of the SERCOM pins.
* As not all settings can be used in different modes of operation, proper
* settings must be chosen according to the rest of the configuration.
*
* See \ref asfdoc_sam0_sercom_spi_mux_settings for a description of the
* various MUX setting options.
*/
enum spi_signal_mux_setting {
/** SPI MUX setting A */
SPI_SIGNAL_MUX_SETTING_A =
(0x0 << SERCOM_SPI_CTRLA_DOPO_Pos) |
(0x0 << SERCOM_SPI_CTRLA_DIPO_Pos),
/** SPI MUX setting B */
SPI_SIGNAL_MUX_SETTING_B =
(0x0 << SERCOM_SPI_CTRLA_DOPO_Pos) |
(0x1 << SERCOM_SPI_CTRLA_DIPO_Pos),
/** SPI MUX setting C */
SPI_SIGNAL_MUX_SETTING_C =
(0x0 << SERCOM_SPI_CTRLA_DOPO_Pos) |
(0x2 << SERCOM_SPI_CTRLA_DIPO_Pos),
/** SPI MUX setting D */
SPI_SIGNAL_MUX_SETTING_D =
(0x0 << SERCOM_SPI_CTRLA_DOPO_Pos) |
(0x3 << SERCOM_SPI_CTRLA_DIPO_Pos),
/** SPI MUX setting E */
SPI_SIGNAL_MUX_SETTING_E =
(0x1 << SERCOM_SPI_CTRLA_DOPO_Pos) |
(0x0 << SERCOM_SPI_CTRLA_DIPO_Pos),
/** SPI MUX setting F */
SPI_SIGNAL_MUX_SETTING_F =
(0x1 << SERCOM_SPI_CTRLA_DOPO_Pos) |
(0x1 << SERCOM_SPI_CTRLA_DIPO_Pos),
/** SPI MUX setting G */
SPI_SIGNAL_MUX_SETTING_G =
(0x1 << SERCOM_SPI_CTRLA_DOPO_Pos) |
(0x2 << SERCOM_SPI_CTRLA_DIPO_Pos),
/** SPI MUX setting H */
SPI_SIGNAL_MUX_SETTING_H =
(0x1 << SERCOM_SPI_CTRLA_DOPO_Pos) |
(0x3 << SERCOM_SPI_CTRLA_DIPO_Pos),
/** SPI MUX setting I */
SPI_SIGNAL_MUX_SETTING_I =
(0x2 << SERCOM_SPI_CTRLA_DOPO_Pos) |
(0x0 << SERCOM_SPI_CTRLA_DIPO_Pos),
/** SPI MUX setting J */
SPI_SIGNAL_MUX_SETTING_J =
(0x2 << SERCOM_SPI_CTRLA_DOPO_Pos) |
(0x1 << SERCOM_SPI_CTRLA_DIPO_Pos),
/** SPI MUX setting K */
SPI_SIGNAL_MUX_SETTING_K =
(0x2 << SERCOM_SPI_CTRLA_DOPO_Pos) |
(0x2 << SERCOM_SPI_CTRLA_DIPO_Pos),
/** SPI MUX setting L */
SPI_SIGNAL_MUX_SETTING_L =
(0x2 << SERCOM_SPI_CTRLA_DOPO_Pos) |
(0x3 << SERCOM_SPI_CTRLA_DIPO_Pos),
/** SPI MUX setting M */
SPI_SIGNAL_MUX_SETTING_M =
(0x3 << SERCOM_SPI_CTRLA_DOPO_Pos) |
(0x0 << SERCOM_SPI_CTRLA_DIPO_Pos),
/** SPI MUX setting N */
SPI_SIGNAL_MUX_SETTING_N =
(0x3 << SERCOM_SPI_CTRLA_DOPO_Pos) |
(0x1 << SERCOM_SPI_CTRLA_DIPO_Pos),
/** SPI MUX setting O */
SPI_SIGNAL_MUX_SETTING_O =
(0x3 << SERCOM_SPI_CTRLA_DOPO_Pos) |
(0x2 << SERCOM_SPI_CTRLA_DIPO_Pos),
/** SPI MUX setting P */
SPI_SIGNAL_MUX_SETTING_P =
(0x3 << SERCOM_SPI_CTRLA_DOPO_Pos) |
(0x3 << SERCOM_SPI_CTRLA_DIPO_Pos),
};
/**
* For slave mode when using the SPI frame with address format.
*
*/
enum spi_addr_mode {
/**
* \c address_mask in the \ref spi_config struct is used as a mask to the register.
*/
SPI_ADDR_MODE_MASK = SERCOM_SPI_CTRLB_AMODE(0),
/**
* The slave responds to the two unique addresses in \c address and
* \c address_mask in the \ref spi_config struct.
*/
SPI_ADDR_MODE_UNIQUE = SERCOM_SPI_CTRLB_AMODE(1),
/**
* The slave responds to the range of addresses between and including \c address
* and \c address_mask in in the \ref spi_config struct.
*/
SPI_ADDR_MODE_RANGE = SERCOM_SPI_CTRLB_AMODE(2),
};
/**
* \brief SPI modes enum
*
* SPI mode selection.
*/
enum spi_mode {
/** Master mode */
SPI_MODE_MASTER = 1,
/** Slave mode */
SPI_MODE_SLAVE = 0,
};
/**
* \brief SPI data order enum
*
* SPI data order.
*
*/
enum spi_data_order {
/** The LSB of the data is transmitted first */
SPI_DATA_ORDER_LSB = SERCOM_SPI_CTRLA_DORD,
/** The MSB of the data is transmitted first */
SPI_DATA_ORDER_MSB = 0,
};
/**
* \brief SPI character size enum
*
* SPI character size.
*
*/
enum spi_character_size {
/** 8 bit character */
SPI_CHARACTER_SIZE_8BIT = SERCOM_SPI_CTRLB_CHSIZE(0),
/** 9 bit character */
SPI_CHARACTER_SIZE_9BIT = SERCOM_SPI_CTRLB_CHSIZE(1),
};
/**
* \brief SPI peripheral slave instance structure
*
* SPI peripheral slave software instance structure, used to configure the
* correct SPI transfer mode settings for an attached slave. See
* \ref spi_select_slave.
*/
struct spi_slave_inst {
/** Pin to use as Slave Select */
uint8_t ss_pin;
/** Address recognition enabled in slave device */
bool address_enabled;
/** Address of slave device */
uint8_t address;
};
/**
* \brief SPI peripheral slave configuration structure
*
* SPI Peripheral slave configuration structure
*/
struct spi_slave_inst_config {
/** Pin to use as Slave Select */
uint8_t ss_pin;
/** Enable address */
bool address_enabled;
/** Address of slave */
uint8_t address;
};
/**
* \brief Initializes an SPI peripheral slave device configuration structure to default values
*
* This function will initialize a given SPI slave device configuration
* structure to a set of known default values. This function should be called
* on any new instance of the configuration structures before being modified by
* the user application.
*
* The default configuration is as follows:
* \li Slave Select on GPIO pin 10
* \li Addressing not enabled
*
* \param[out] config Configuration structure to initialize to default values
*/
static inline void spi_slave_inst_get_config_defaults(
struct spi_slave_inst_config *const config)
{
assert(config);
config->ss_pin = 10;
config->address_enabled = false;
config->address = 0;
}
/**
* This function will initialize the software SPI peripheral slave, based on
* the values of the config struct. The slave can then be selected and
* optionally addressed by the \ref spi_select_slave function.
*
* \param[out] slave Pointer to the software slave instance struct
* \param[in] config Pointer to the config struct
*
*/
static inline void spi_attach_slave(struct spi_slave_inst *const slave)
// struct spi_slave_inst_config *const config)
{
assert(slave);
// Assert(config);
// slave->ss_pin = config->ss_pin;
// slave->address_enabled = config->address_enabled;
// slave->address = config->address;
/* Set config on Slave Select pin */
port_pin_set_config(slave->ss_pin,
PORT_PIN_DIR_OUTPUT,
PORT_PIN_PULL_UP,
false);
/* Set high */
port_pin_set_output_level(slave->ss_pin, true);
}
enum sercom_status_t spi_init(SercomSpi *const hw,
enum spi_mode mode,
enum spi_data_order data_order,
enum spi_transfer_mode transfer_mode,
enum spi_signal_mux_setting mux_setting,
enum spi_character_size character_size,
bool run_in_standby,
bool receiver_enable,
uint32_t master_baudrate,
enum spi_frame_format slave_frame_format,
enum spi_addr_mode slave_address_mode,
uint8_t slave_address,
uint8_t salve_address_mask,
bool savle_preload_enable,
# ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT
bool select_slave_low_detect_enable,
# endif
# ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT
bool master_slave_select_enable,
# endif
enum gclk_generator generator_source,
uint32_t pinmux_pad0,
uint32_t pinmux_pad1,
uint32_t pinmux_pad2,
uint32_t pinmux_pad3);
enum sercom_status_t spi_init_default(SercomSpi *const hw);
/**
* This function will enable the SERCOM SPI module.
*
* \param[in,out] module Pointer to the software instance struct
*/
static inline void spi_enable(SercomSpi* const hw)
{
/* Sanity check arguments */
assert(hw);
// system_interrupt_enable(_sercom_get_interrupt_vector(module->hw));
SPI_WAIT_FOR_SYNC(hw);
/* Enable SPI */
hw->CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE;
}
/**
* This function will disable the SERCOM SPI module.
*
* \param[in,out] module Pointer to the software instance struct
*/
static inline void spi_disable(SercomSpi* const hw)
{
/* Sanity check arguments */
assert(hw);
// system_interrupt_disable(_sercom_get_interrupt_vector(module->hw));
SPI_WAIT_FOR_SYNC(hw);
/* Disable SPI */
hw->CTRLA.reg &= ~SERCOM_SPI_CTRLA_ENABLE;
}
void spi_reset(SercomSpi* const hw);
enum sercom_status_t spi_set_baudrate(SercomSpi* const hw,
uint32_t baudrate);
/**
* This function will check if the SPI master module has shifted out last data,
* or if the slave select pin has been drawn high by the master for the SPI
* slave module.
*
* \param[in] module Pointer to the software instance struct
*
* \return Indication of whether any writes are ongoing
* \retval true If the SPI master module has shifted out data, or slave select
* has been drawn high for SPI slave
* \retval false If the SPI master module has not shifted out data
*/
static inline bool spi_is_write_complete(SercomSpi* const hw)
{
/* Sanity check arguments */
assert(hw);
/* Check interrupt flag */
return (hw->INTFLAG.reg & SERCOM_SPI_INTFLAG_TXC);
}
/**
* This function will check if the SPI module is ready to write data.
*
* \param[in] module Pointer to the software instance struct
*
* \return Indication of whether the module is ready to read data or not
* \retval true If the SPI module is ready to write data
* \retval false If the SPI module is not ready to write data
*/
static inline bool spi_is_ready_to_write(SercomSpi* const hw)
{
/* Sanity check arguments */
assert(hw);
/* Check interrupt flag */
return (hw->INTFLAG.reg & SERCOM_SPI_INTFLAG_DRE);
}
/**
* This function will check if the SPI module is ready to read data.
*
* \param[in] module Pointer to the software instance struct
*
* \return Indication of whether the module is ready to read data or not
* \retval true If the SPI module is ready to read data
* \retval false If the SPI module is not ready to read data
*/
static inline bool spi_is_ready_to_read(SercomSpi* const hw)
{
/* Sanity check arguments */
assert(hw);
/* Check interrupt flag */
return (hw->INTFLAG.reg & SERCOM_SPI_INTFLAG_RXC);
}
/**
* This function will send a single SPI character via SPI and ignore any data
* shifted in by the connected device. To both send and receive data, use the
* \ref spi_transceive_wait function or use the \ref spi_read function after
* writing a character. The \ref spi_is_ready_to_write function
* should be called before calling this function.
*
* Note that this function does not handle the SS (Slave Select)
* pin(s) in master mode; this must be handled from the user application.
*
* \note In slave mode, the data will not be transferred before a master
* initiates a transaction.
*
* \param[in] module Pointer to the software instance struct
* \param[in] tx_data Data to transmit
*
* \return Status of the procedure
* \retval STATUS_OK If the data was written
* \retval STATUS_BUSY If the last write was not completed
*/
static inline enum sercom_status_t spi_write(SercomSpi* const hw,
uint16_t tx_data)
{
/* Sanity check arguments */
assert(hw);
/* Check if the data register has been copied to the shift register */
if (!spi_is_ready_to_write(hw)) {
/* Data register has not been copied to the shift register, return */
return SERCOM_STATUS_BUSY;
}
/* Write the character to the DATA register */
hw->DATA.reg = tx_data & SERCOM_SPI_DATA_MASK;
return SERCOM_STATUS_OK;
}
enum sercom_status_t spi_write_buffer_wait(SercomSpi* const hw,
const uint8_t *tx_data,
uint16_t length);
/**
* This function will return the last SPI character shifted into the receive
* register by the \ref spi_write function
*
* \note The \ref spi_is_ready_to_read function should be called before calling
* this function.
*
* \note Receiver must be enabled in the configuration
*
* \param[in] module Pointer to the software instance struct
* \param[out] rx_data Pointer to store the received data
*
* \returns Status of the read operation.
* \retval STATUS_OK If data was read
* \retval STATUS_ERR_IO If no data is available
* \retval STATUS_ERR_OVERFLOW If the data is overflown
*/
static inline enum sercom_status_t spi_read(SercomSpi* const hw,
uint16_t *rx_data)
{
/* Sanity check arguments */
assert(hw);
/* Check if data is ready to be read */
if (!spi_is_ready_to_read(hw)) {
/* No data has been received, return */
return SERCOM_STATUS_IO;
}
/* Return value */
enum sercom_status_t retval = SERCOM_STATUS_OK;
/* Check if data is overflown */
if (hw->STATUS.reg & SERCOM_SPI_STATUS_BUFOVF) {
retval = SERCOM_STATUS_OVERFLOW;
/* Clear overflow flag */
hw->STATUS.reg |= SERCOM_SPI_STATUS_BUFOVF;
}
/* Read the character from the DATA register */
if (hw->CTRLB.bit.CHSIZE) { /* If 9-bit */
*rx_data = (hw->DATA.reg & SERCOM_SPI_DATA_MASK);
} else {
*rx_data = (uint8_t)hw->DATA.reg;
}
return retval;
}
enum sercom_status_t spi_read_buffer_wait(SercomSpi* const hw,
uint8_t *rx_data,
uint16_t length,
uint16_t dummy);
enum sercom_status_t spi_transceive_wait(SercomSpi* const hw,
uint16_t tx_data,
uint16_t *rx_data);
enum sercom_status_t spi_transceive_buffer_wait(SercomSpi* const hw,
uint8_t *tx_data,
uint8_t *rx_data,
uint16_t length);
enum sercom_status_t spi_select_slave(SercomSpi* const hw,
uint8_t ss_pin,
bool address_enabled,
uint8_t address,
const bool select);
/**
* Mux Settings
*
* The following lists the possible internal SERCOM module pad function
* assignments, for the four SERCOM pads in both SPI Master, and SPI Slave
* modes. Note that this is in addition to the physical GPIO pin MUX of the
* device, and can be used in conjunction to optimize the serial data pin-out.
*
* \section asfdoc_sam0_sercom_spi_mux_settings_master Master Mode Settings
* The following table describes the SERCOM pin functionalities for the various
* MUX settings, whilst in SPI Master mode.
*
* \note If MISO is unlisted, the SPI receiver must not be enabled for the
* given MUX setting.
*
* <table>
* <tr>
* <th>Mux/Pad</th>
* <th>PAD 0</th>
* <th>PAD 1</th>
* <th>PAD 2</th>
* <th>PAD 3</th>
* </tr>
* <tr>
* <td>A</td>
* <td>MOSI</td>
* <td>SCK</td>
* <td>-</td>
* <td>-</td>
* </tr>
* <tr>
* <td>B</td>
* <td>MOSI</td>
* <td>SCK</td>
* <td>-</td>
* <td>-</td>
* </tr>
* <tr>
* <td>C</td>
* <td>MOSI</td>
* <td>SCK</td>
* <td>MISO</td>
* <td>-</td>
* </tr>
* <tr>
* <td>D</td>
* <td>MOSI</td>
* <td>SCK</td>
* <td>-</td>
* <td>MISO</td>
* </tr>
* <tr>
* <td>E</td>
* <td>MISO</td>
* <td>-</td>
* <td>MOSI</td>
* <td>SCK</td>
* </tr>
* <tr>
* <td>F</td>
* <td>-</td>
* <td>MISO</td>
* <td>MOSI</td>
* <td>SCK</td>
* </tr>
* <tr>
* <td>G</td>
* <td>-</td>
* <td>-</td>
* <td>MOSI</td>
* <td>SCK</td>
* </tr>
* <tr>
* <td>H</td>
* <td>-</td>
* <td>-</td>
* <td>MOSI</td>
* <td>SCK</td>
* </tr>
* <tr>
* <td>I <sup>(1)</sup></td>
* <td>MISO</td>
* <td>SCK</td>
* <td>-</td>
* <td>MOSI</td>
* </tr>
* <tr>
* <td>J <sup>(1)</sup></td>
* <td>-</td>
* <td>SCK</td>
* <td>-</td>
* <td>MOSI</td>
* </tr>
* <tr>
* <td>K <sup>(1)</sup></td>
* <td>-</td>
* <td>SCK</td>
* <td>MISO</td>
* <td>MOSI</td>
* </tr>
* <tr>
* <td>L <sup>(1)</sup></td>
* <td>-</td>
* <td>SCK</td>
* <td>-</td>
* <td>MOSI</td>
* </tr>
* <tr>
* <td>M <sup>(1)</sup></td>
* <td>MOSI</td>
* <td>-</td>
* <td>-</td>
* <td>SCK</td>
* </tr>
* <tr>
* <td>N <sup>(1)</sup></td>
* <td>MOSI</td>
* <td>MISO</td>
* <td>-</td>
* <td>SCK</td>
* </tr>
* <tr>
* <td>O <sup>(1)</sup></td>
* <td>MOSI</td>
* <td>-</td>
* <td>MISO</td>
* <td>SCK</td>
* </tr>
* <tr>
* <td>P <sup>(1)</sup></td>
* <td>MOSI</td>
* <td>-</td>
* <td>-</td>
* <td>SCK</td>
* </tr>
* </table>
*
* <i>(1) Not available in all silicon revisions.</i>
*
* \section asfdoc_sam0_sercom_spi_mux_settings_slave Slave Mode Settings
* The following table describes the SERCOM pin functionalities for the various
* MUX settings, whilst in SPI Slave mode.
*
* \note If MISO is unlisted, the SPI receiver must not be enabled for the
* given MUX setting.
*
* <table>
* <tr>
* <th>Mux/Pad</th>
* <th>PAD 0</th>
* <th>PAD 1</th>
* <th>PAD 2</th>
* <th>PAD 3</th>
* </tr>
* <tr>
* <td>A</td>
* <td>MISO</td>
* <td>SCK</td>
* <td>/SS</td>
* <td>-</td>
* </tr>
* <tr>
* <td>B</td>
* <td>MISO</td>
* <td>SCK</td>
* <td>/SS</td>
* <td>-</td>
* </tr>
* <tr>
* <td>C</td>
* <td>MISO</td>
* <td>SCK</td>
* <td>/SS</td>
* <td>-</td>
* </tr>
* <tr>
* <td>D</td>
* <td>MISO</td>
* <td>SCK</td>
* <td>/SS</td>
* <td>MOSI</td>
* </tr>
* <tr>
* <td>E</td>
* <td>MOSI</td>
* <td>/SS</td>
* <td>MISO</td>
* <td>SCK</td>
* </tr>
* <tr>
* <td>F</td>
* <td>-</td>
* <td>/SS</td>
* <td>MISO</td>
* <td>SCK</td>
* </tr>
* <tr>
* <td>G</td>
* <td>-</td>
* <td>/SS</td>
* <td>MISO</td>
* <td>SCK</td>
* </tr>
* <tr>
* <td>H</td>
* <td>-</td>
* <td>/SS</td>
* <td>MISO</td>
* <td>SCK</td>
* </tr>
* <tr>
* <td>I <sup>(1)</sup></td>
* <td>MOSI</td>
* <td>SCK</td>
* <td>/SS</td>
* <td>MISO</td>
* </tr>
* <tr>
* <td>J <sup>(1)</sup></td>
* <td>-</td>
* <td>SCK</td>
* <td>/SS</td>
* <td>MISO</td>
* </tr>
* <tr>
* <td>K <sup>(1)</sup></td>
* <td>-</td>
* <td>SCK</td>
* <td>/SS</td>
* <td>MISO</td>
* </tr>
* <tr>
* <td>L <sup>(1)</sup></td>
* <td>-</td>
* <td>SCK</td>
* <td>/SS</td>
* <td>MISO</td>
* </tr>
* <tr>
* <td>M <sup>(1)</sup></td>
* <td>MISO</td>
* <td>/SS</td>
* <td>-</td>
* <td>SCK</td>
* </tr>
* <tr>
* <td>N <sup>(1)</sup></td>
* <td>MISO</td>
* <td>/SS</td>
* <td>-</td>
* <td>SCK</td>
* </tr>
* <tr>
* <td>O <sup>(1)</sup></td>
* <td>MISO</td>
* <td>/SS</td>
* <td>MOSI</td>
* <td>SCK</td>
* </tr>
* <tr>
* <td>P <sup>(1)</sup></td>
* <td>MISO</td>
* <td>/SS</td>
* <td>-</td>
* <td>SCK</td>
* </tr>
* </table>
*
*/
#endif /* SPI_H_INCLUDED */