kopia lustrzana https://github.com/bristol-seds/pico-tracker
1134 wiersze
31 KiB
C
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 */
|