mbed library sources

Dependents:   Encrypted my_mbed lklk CyaSSL_DTLS_Cellular ... more

Superseded

This library was superseded by mbed-dev - https://os.mbed.com/users/mbed_official/code/mbed-dev/.

Development branch of the mbed library sources. This library is kept in synch with the latest changes from the mbed SDK and it is not guaranteed to work.

If you are looking for a stable and tested release, please import one of the official mbed library releases:

Import librarymbed

The official Mbed 2 C/C++ SDK provides the software platform and libraries to build your applications.

targets/hal/TARGET_Atmel/TARGET_SAM21/spi_api.c

Committer:
mbed_official
Date:
2015-07-17
Revision:
592:a274ee790e56
Parent:
579:53297373a894

File content as of revision 592:a274ee790e56:

/* mbed Microcontroller Library
 * Copyright (c) 2006-2013 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "mbed_assert.h"
#include "spi_api.h"

#include <math.h>

#include "cmsis.h"
#include "pinmap.h"
#include "sercom.h"

#include "pinmap_function.h"

#define SPI_MOSI_INDEX	0
#define SPI_MISO_INDEX	1
#define SPI_SCLK_INDEX	2
#define SPI_SSEL_INDEX	3

/**
 * \brief SPI modes enum
 *
 * SPI mode selection.
 */
enum spi_mode {
    /** Master mode. */
    SPI_MODE_MASTER         = 1,
    /** Slave mode. */
    SPI_MODE_SLAVE          = 0,
};

#if DEVICE_SPI_ASYNCH
#define pSPI_S(obj)			(&obj->spi)
#define pSPI_SERCOM(obj)	obj->spi.spi
#else
#define pSPI_S(obj)			(obj)
#define pSPI_SERCOM(obj)	(obj->spi)
#endif
#define _SPI(obj)			pSPI_SERCOM(obj)->SPI

/** SPI default baud rate. */
#define SPI_DEFAULT_BAUD	50000//100000


/** SPI timeout value. */
#  define SPI_TIMEOUT 10000

extern uint8_t g_sys_init;
uint16_t dummy_fill_word = 0xFFFF;


static inline bool spi_is_syncing(spi_t *obj)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    /* Return synchronization status */
    return (_SPI(obj).SYNCBUSY.reg);
}

static inline void spi_enable(spi_t *obj)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    /* Wait until the synchronization is complete */
    while (spi_is_syncing(obj));

    /* Enable SPI */
    _SPI(obj).CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE;
}

static inline void spi_disable(spi_t *obj)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    /* Wait until the synchronization is complete */
    while (spi_is_syncing(obj));

    /* Disable SPI */
    _SPI(obj).CTRLA.reg &= ~SERCOM_SPI_CTRLA_ENABLE;
}

static inline bool spi_is_write_complete(spi_t *obj)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    /* Check interrupt flag */
    return (_SPI(obj).INTFLAG.reg & SERCOM_SPI_INTFLAG_TXC);
}

static inline bool spi_is_ready_to_write(spi_t *obj)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    /* Check interrupt flag */
    return (_SPI(obj).INTFLAG.reg & SERCOM_SPI_INTFLAG_DRE);
}

static inline bool spi_is_ready_to_read(spi_t *obj)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    /* Check interrupt flag */
    return (_SPI(obj).INTFLAG.reg & SERCOM_SPI_INTFLAG_RXC);
}

static inline bool spi_write(spi_t *obj, uint16_t tx_data)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    /* Check if the data register has been copied to the shift register */
    if (!spi_is_ready_to_write(obj)) {
        /* Data register has not been copied to the shift register, return */
        return false;
    }

    /* Write the character to the DATA register */
    _SPI(obj).DATA.reg = tx_data & SERCOM_SPI_DATA_MASK;

    return true;
}

static inline bool spi_read(spi_t *obj, uint16_t *rx_data)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    /* Check if data is ready to be read */
    if (!spi_is_ready_to_read(obj)) {
        /* No data has been received, return */
        return false;
    }

    /* Check if data is overflown */
    if (_SPI(obj).STATUS.reg & SERCOM_SPI_STATUS_BUFOVF) {
        /* Clear overflow flag */
        _SPI(obj).STATUS.reg |= SERCOM_SPI_STATUS_BUFOVF;
    }

    /* Read the character from the DATA register */
    if (_SPI(obj).CTRLB.bit.CHSIZE == 1) {
        *rx_data = (_SPI(obj).DATA.reg & SERCOM_SPI_DATA_MASK);
    } else {
        *rx_data = (uint8_t)_SPI(obj).DATA.reg;
    }

    return true;
}

static uint32_t spi_find_mux_settings(spi_t *obj)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);
    uint8_t i_dipo;
    uint8_t i_dopo;
    uint32_t dipo = 0;
    uint32_t dopo = 0;
    uint32_t mux_pad;

    uint32_t mux_settings = 0;

    uint32_t sercom_index = _sercom_get_sercom_inst_index(pSPI_SERCOM(obj));

    if (pSPI_S(obj)->mode == SPI_MODE_MASTER) {
        i_dipo = SPI_MISO_INDEX;
        i_dopo = SPI_MOSI_INDEX;
    } else {
        i_dipo = SPI_MOSI_INDEX;
        i_dopo = SPI_MISO_INDEX;
    }

    /* Find MUX setting */
    if (pSPI_S(obj)->pins[i_dipo] != NC) {
        /* Set Data input MUX padding for master */
        mux_pad = pinmap_pad_sercom(pSPI_S(obj)->pins[i_dipo], sercom_index);
        if (mux_pad != NC) {
            /* MUX pad value is same as DIPO value */
            dipo = mux_pad;
            mux_settings |= ((dipo << SERCOM_SPI_CTRLA_DIPO_Pos) & SERCOM_SPI_CTRLA_DIPO_Msk);
        }
    }

    if (pSPI_S(obj)->pins[i_dopo] != NC) {
        /* Set Data output MUX padding for master */
        mux_pad = pinmap_pad_sercom(pSPI_S(obj)->pins[i_dopo], sercom_index);
        if (mux_pad != NC) {
            if (mux_pad != 0) {
                dopo = mux_pad - 1;
            } else {
                if (3 == pinmap_pad_sercom(pSPI_S(obj)->pins[SPI_SCLK_INDEX], sercom_index)) {
                    dopo = 3;
                } else {
                    dopo = 0;
                }
            }
            mux_settings |= ((dopo << SERCOM_SPI_CTRLA_DOPO_Pos) & SERCOM_SPI_CTRLA_DOPO_Msk);
        }
    }

    return mux_settings;
}

/**
 * \defgroup GeneralSPI SPI Configuration Functions
 * @{
 */

/** Initialize the SPI peripheral
 *
 * Configures the pins used by SPI, sets a default format and frequency, and enables the peripheral
 * @param[out] obj  The SPI object to initialize
 * @param[in]  mosi The pin to use for MOSI
 * @param[in]  miso The pin to use for MISO
 * @param[in]  sclk The pin to use for SCLK
 * @param[in]  ssel The pin to use for SSEL
 */
void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);
    MBED_ASSERT(sclk != NC);

    uint16_t baud = 0;
    uint32_t ctrla = 0;
    uint32_t ctrlb = 0;
    enum status_code error_code;

    if (g_sys_init == 0) {
        system_init();
        g_sys_init = 1;
    }

    /* Calculate SERCOM instance from pins */
    uint32_t sercom_index = pinmap_find_sercom(mosi, miso, sclk, ssel);
    pSPI_SERCOM(obj) = (Sercom*)pinmap_peripheral_sercom(NC, sercom_index);

    /* Disable SPI */
    spi_disable(obj);

    /* Check if reset is in progress. */
    if (_SPI(obj).CTRLA.reg & SERCOM_SPI_CTRLA_SWRST) {
        return;
    }

    uint32_t pm_index, gclk_index;
#if (SAML21)
    if (sercom_index == 5) {
        pm_index     = MCLK_APBDMASK_SERCOM5_Pos;
        gclk_index   =  SERCOM5_GCLK_ID_CORE;
    } else {
        pm_index     = sercom_index + MCLK_APBCMASK_SERCOM0_Pos;
        gclk_index   = sercom_index + SERCOM0_GCLK_ID_CORE;
    }
#else
    pm_index     = sercom_index + PM_APBCMASK_SERCOM0_Pos;
    gclk_index   = sercom_index + SERCOM0_GCLK_ID_CORE;
#endif

    /* Turn on module in PM */
#if (SAML21)
    if (sercom_index == 5) {
        system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBD, 1 << pm_index);
    } else {
        system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index);
    }
#else
    system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index);
#endif

    /* Set up the GCLK for the module */
    struct system_gclk_chan_config gclk_chan_conf;
    system_gclk_chan_get_config_defaults(&gclk_chan_conf);
    gclk_chan_conf.source_generator = GCLK_GENERATOR_0;
    system_gclk_chan_set_config(gclk_index, &gclk_chan_conf);
    system_gclk_chan_enable(gclk_index);
    sercom_set_gclk_generator(GCLK_GENERATOR_0, false);

    /* Set the SERCOM in SPI master mode */
    _SPI(obj).CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(0x3);
    pSPI_S(obj)->mode = SPI_MODE_MASTER;

    /* TODO: Do pin muxing here */
    struct system_pinmux_config pin_conf;
    system_pinmux_get_config_defaults(&pin_conf);
    pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT;

    pSPI_S(obj)->pins[SPI_MOSI_INDEX] = mosi;
    pSPI_S(obj)->pins[SPI_MISO_INDEX] = miso;
    pSPI_S(obj)->pins[SPI_SCLK_INDEX] = sclk;
    pSPI_S(obj)->pins[SPI_SSEL_INDEX] = ssel;
    /* Configure the SERCOM pins according to the user configuration */
    for (uint8_t pad = 0; pad < 4; pad++) {
        uint32_t current_pin = pSPI_S(obj)->pins[pad];
        if (current_pin != NC) {
            pin_conf.mux_position = pinmap_function_sercom(current_pin, sercom_index);
            if ((uint8_t)NC != pin_conf.mux_position) {
                system_pinmux_pin_set_config(current_pin, &pin_conf);
            }
        }
    }

    /* Get baud value, based on baudrate and the internal clock frequency */
    uint32_t internal_clock = system_gclk_chan_get_hz(gclk_index);
    //internal_clock = 8000000;
    error_code = _sercom_get_sync_baud_val(SPI_DEFAULT_BAUD, internal_clock, &baud);
    if (error_code != STATUS_OK) {
        /* Baud rate calculation error */
        return;
    }
    _SPI(obj).BAUD.reg = (uint8_t)baud;

    /* TODO: Find MUX settings */
    ctrla |= spi_find_mux_settings(obj);

    /* Set SPI character size */
    ctrlb |= SERCOM_SPI_CTRLB_CHSIZE(0);

    /* Enable receiver */
    ctrlb |= SERCOM_SPI_CTRLB_RXEN;

    /* Write CTRLA register */
    _SPI(obj).CTRLA.reg |= ctrla;

    /* Write CTRLB register */
    _SPI(obj).CTRLB.reg |= ctrlb;

    /* Enable SPI */
    spi_enable(obj);
}

/** Release a SPI object
 *
 * TODO: spi_free is currently unimplemented
 * This will require reference counting at the C++ level to be safe
 *
 * Return the pins owned by the SPI object to their reset state
 * Disable the SPI peripheral
 * Disable the SPI clock
 * @param[in] obj The SPI object to deinitialize
 */
void spi_free(spi_t *obj)
{
    // [TODO]
}

/** Configure the SPI format
 *
 * Set the number of bits per frame, configure clock polarity and phase, shift order and master/slave mode
 * @param[in,out] obj   The SPI object to configure
 * @param[in]     bits  The number of bits per frame
 * @param[in]     mode  The SPI mode (clock polarity, phase, and shift direction)
 * @param[in]     slave Zero for master mode or non-zero for slave mode
 */
void spi_format(spi_t *obj, int bits, int mode, int slave)
{
    PinMode pull_mode;
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    /* Disable SPI */
    spi_disable(obj);


    if (slave) {
        /* Set the SERCOM in SPI mode */
        _SPI(obj).CTRLA.bit.MODE = 0x2;
        pSPI_S(obj)->mode = SPI_MODE_SLAVE;
        pull_mode = PullNone;
        /* Enable PLOADEN to avoid sending dummy character by slave */
        _SPI(obj).CTRLB.bit.PLOADEN = 1;
    } else {
        /* Set the SERCOM in SPI mode */
        _SPI(obj).CTRLA.bit.MODE = 0x3;
        pSPI_S(obj)->mode = SPI_MODE_MASTER;
        pull_mode = PullUp;
    }

    /* Change pull mode of pins */
    for (uint8_t pad = 0; pad < 4; pad++) {
        if (pSPI_S(obj)->pins[pad] != NC) {
            pin_mode(pSPI_S(obj)->pins[pad], pull_mode);
        }
    }

    /* Change MUX settings */
    uint32_t ctrla = _SPI(obj).CTRLA.reg;
    ctrla &= ~(SERCOM_SPI_CTRLA_DIPO_Msk | SERCOM_SPI_CTRLA_DOPO_Msk);
    ctrla |= spi_find_mux_settings(obj);
    _SPI(obj).CTRLA.reg = ctrla;

    /* Set SPI Frame size - only 8-bit and 9-bit supported now */
    _SPI(obj).CTRLB.bit.CHSIZE = (bits > 8)? 1 : 0;

    /* Set SPI Clock Phase */
    _SPI(obj).CTRLA.bit.CPHA = (mode & 0x01)? 1 : 0;

    /* Set SPI Clock Polarity */
    _SPI(obj).CTRLA.bit.CPOL = (mode & 0x02)? 1 : 0;

    /* Enable SPI */
    spi_enable(obj);
}

/** Set the SPI baud rate
 *
 * Actual frequency may differ from the desired frequency due to available dividers and bus clock
 * Configures the SPI peripheral's baud rate
 * @param[in,out] obj The SPI object to configure
 * @param[in]     hz  The baud rate in Hz
 */
void spi_frequency(spi_t *obj, int hz)
{
    uint16_t baud = 0;
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    /* Disable SPI */
    spi_disable(obj);

    /* Find frequency of the internal SERCOMi_GCLK_ID_CORE */
    uint32_t sercom_index = _sercom_get_sercom_inst_index(pSPI_SERCOM(obj));
    uint32_t gclk_index   = sercom_index + SERCOM0_GCLK_ID_CORE;
    uint32_t internal_clock = system_gclk_chan_get_hz(gclk_index);

    /* Get baud value, based on baudrate and the internal clock frequency */
    enum status_code error_code = _sercom_get_sync_baud_val(hz, internal_clock, &baud);

    if (error_code != STATUS_OK) {
        /* Baud rate calculation error, return status code */
        /* Enable SPI */
        spi_enable(obj);
        return;
    }

    _SPI(obj).BAUD.reg = (uint8_t)baud;

    /* Enable SPI */
    spi_enable(obj);
}

/**@}*/
/**
 * \defgroup SynchSPI Synchronous SPI Hardware Abstraction Layer
 * @{
 */

/** Write a byte out in master mode and receive a value
 *
 * @param[in] obj   The SPI peripheral to use for sending
 * @param[in] value The value to send
 * @return Returns the value received during send
 */
int spi_master_write(spi_t *obj, int value)
{
    uint16_t rx_data = 0;

    /* Sanity check arguments */
    MBED_ASSERT(obj);

#if DEVICE_SPI_ASYNCH
    if (obj->spi.status == STATUS_BUSY) {
        /* Check if the SPI module is busy with a job */
        return 0;
    }
#endif

    /* Wait until the module is ready to write the character */
    while (!spi_is_ready_to_write(obj));

    /* Write data */
    spi_write(obj, value);

    if (!(_SPI(obj).CTRLB.bit.RXEN)) {
        return 0;
    }

    /* Wait until the module is ready to read the character */
    while (!spi_is_ready_to_read(obj));

    /* Read data */
    spi_read(obj, &rx_data);

    return rx_data;
}

/** Check if a value is available to read
 *
 * @param[in] obj The SPI peripheral to check
 * @return non-zero if a value is available
 */
int spi_slave_receive(spi_t *obj)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    return spi_is_ready_to_read(obj);
}

/** Get a received value out of the SPI receive buffer in slave mode
 *
 * Blocks until a value is available
 * @param[in] obj The SPI peripheral to read
 * @return The value received
 */
int spi_slave_read(spi_t *obj)
{
    int i;
    uint16_t rx_data = 0;

    /* Sanity check arguments */
    MBED_ASSERT(obj);

    /* Check for timeout period */
    for (i = 0; i < SPI_TIMEOUT; i++) {
        if (spi_is_ready_to_read(obj)) {
            break;
        }
    }
    if (i == SPI_TIMEOUT) {
        /* Not ready to read data within timeout period */
        return 0;
    }

    /* Read data */
    spi_read(obj, &rx_data);

    return rx_data;
}

/** Write a value to the SPI peripheral in slave mode
 *
 * Blocks until the SPI peripheral can be written to
 * @param[in] obj   The SPI peripheral to write
 * @param[in] value The value to write
 */
void spi_slave_write(spi_t *obj, int value)
{
    int i;

    /* Sanity check arguments */
    MBED_ASSERT(obj);

    /* Check for timeout period */
    for (i = 0; i < SPI_TIMEOUT; i++) {
        if (spi_is_ready_to_write(obj)) {
            break;
        }
    }
    if (i == SPI_TIMEOUT) {
        /* Not ready to write data within timeout period */
        return;
    }

    /* Write data */
    spi_write(obj, value);
}

/** Checks if the specified SPI peripheral is in use
 *
 * @param[in] obj The SPI peripheral to check
 * @return non-zero if the peripheral is currently transmitting
 */
int spi_busy(spi_t *obj)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    return spi_is_write_complete(obj);
}

/** Get the module number
 *
 * @param[in] obj The SPI peripheral to check
 * @return The module number
 */
uint8_t spi_get_module(spi_t *obj)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);
    return _sercom_get_sercom_inst_index(pSPI_SERCOM(obj));
}


#if DEVICE_SPI_ASYNCH
/**
 * \defgroup AsynchSPI Asynchronous SPI Hardware Abstraction Layer
 * @{
 */


/**
 * \internal
 * Writes a character from the TX buffer to the Data register.
 *
 * \param[in,out]  module  Pointer to SPI software instance struct
 */
static void _spi_write_async(spi_t *obj)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    uint16_t data_to_send;
    uint8_t *tx_buffer = obj->tx_buff.buffer;

    /* Do nothing if we are at the end of buffer */
    if (obj->tx_buff.pos < obj->tx_buff.length) {
        /* Write value will be at least 8-bits long */
        if (tx_buffer) {
            data_to_send = tx_buffer[obj->tx_buff.pos];
        } else {
            data_to_send = dummy_fill_word;
        }
        /* Increment 8-bit index */
        obj->tx_buff.pos++;

        if (_SPI(obj).CTRLB.bit.CHSIZE == 1) {
            if (tx_buffer)
                data_to_send |= (tx_buffer[obj->tx_buff.pos] << 8);
            /* Increment 8-bit index */
            obj->tx_buff.pos++;
        }
    } else {
        /* Write a dummy packet */
        /* TODO: Current implementation do not enter this condition, remove if not needed */
        data_to_send = dummy_fill_word;
    }

    /* Write the data to send*/
    _SPI(obj).DATA.reg = data_to_send & SERCOM_SPI_DATA_MASK;

    /* Check for error */
    if ((_SPI(obj).INTFLAG.reg & SERCOM_SPI_INTFLAG_ERROR) && (obj->spi.mask & SPI_EVENT_ERROR)) {
        obj->spi.event |= SPI_EVENT_ERROR;
    }
}

/**
 * \internal
 * Reads a character from the Data register to the RX buffer.
 *
 * \param[in,out]  module  Pointer to SPI software instance struct
 */
static void _spi_read_async(spi_t *obj)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    uint8_t *rx_buffer = obj->rx_buff.buffer;

    /* Check if data is overflown */
    if (_SPI(obj).STATUS.reg & SERCOM_SPI_STATUS_BUFOVF) {
        /* Clear overflow flag */
        _SPI(obj).STATUS.reg |= SERCOM_SPI_STATUS_BUFOVF;
        if (obj->spi.mask & SPI_EVENT_RX_OVERFLOW) {
            /* Set overflow error */
            obj->spi.event |= SPI_EVENT_RX_OVERFLOW;
            return;
        }
    }

    /* Read data, either valid, or dummy */
    uint16_t received_data = (_SPI(obj).DATA.reg & SERCOM_SPI_DATA_MASK);

    /* Do nothing if we are at the end of buffer */
    if ((obj->rx_buff.pos >= obj->rx_buff.length) && rx_buffer) {
        return;
    }

    /* Read value will be at least 8-bits long */
    rx_buffer[obj->rx_buff.pos] = received_data;
    /* Increment 8-bit index */
    obj->rx_buff.pos++;

    if (_SPI(obj).CTRLB.bit.CHSIZE == 1) {
        /* 9-bit data, write next received byte to the buffer */
        rx_buffer[obj->rx_buff.pos] = (received_data >> 8);
        /* Increment 8-bit index */
        obj->rx_buff.pos++;
    }

    /* Check for error */
    if ((_SPI(obj).INTFLAG.reg & SERCOM_SPI_INTFLAG_ERROR) && (obj->spi.mask & SPI_EVENT_ERROR)) {
        obj->spi.event |= SPI_EVENT_ERROR;
    }
}

/**
 * \internal
 * Clears all interrupt flags of SPI
 *
 * \param[in,out]  module  Pointer to SPI software instance struct
 */
static void _spi_clear_interrupts(spi_t *obj)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    uint8_t sercom_index = _sercom_get_sercom_inst_index(obj->spi.spi);

    /* Clear all interrupts */
    _SPI(obj).INTENCLR.reg =
        SERCOM_SPI_INTFLAG_DRE |
        SERCOM_SPI_INTFLAG_TXC |
        SERCOM_SPI_INTFLAG_RXC |
        SERCOM_SPI_INTFLAG_ERROR;
    NVIC_DisableIRQ(SERCOM0_IRQn + sercom_index);
    NVIC_SetVector((SERCOM0_IRQn + sercom_index), (uint32_t)NULL);
}

/**
 * \internal
 * Starts transceive of buffers with a given length
 *
 * \param[in,out]  obj   Pointer to SPI software instance struct
 *
 */
static enum status_code _spi_transceive_buffer(spi_t *obj)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    uint16_t interrupt_status = _SPI(obj).INTFLAG.reg;
    interrupt_status &= _SPI(obj).INTENSET.reg;

    if (interrupt_status & SERCOM_SPI_INTFLAG_DRE) {
        /* Clear DRE interrupt */
        _SPI(obj).INTENCLR.reg = SERCOM_SPI_INTFLAG_DRE;
        /* Write data */
        _spi_write_async(obj);
        /* Set TXC interrupt */
        _SPI(obj).INTENSET.reg |= SERCOM_SPI_INTFLAG_TXC;
    }
    if (interrupt_status & SERCOM_SPI_INTFLAG_TXC) {
        /* Clear TXC interrupt */
        _SPI(obj).INTENCLR.reg = SERCOM_SPI_INTFLAG_TXC;
        if ((obj->rx_buff.buffer) && (obj->rx_buff.pos < obj->rx_buff.length)) {
            while (!spi_is_ready_to_read(obj));
            _spi_read_async(obj);
            if ((obj->tx_buff.pos >= obj->tx_buff.length) && (obj->tx_buff.length < obj->rx_buff.length)) {
                obj->tx_buff.length = obj->rx_buff.length;
                obj->tx_buff.buffer = 0;
            }
        }
        if (obj->tx_buff.pos < obj->tx_buff.length) {
            /* Set DRE interrupt */
            _SPI(obj).INTENSET.reg |= SERCOM_SPI_INTFLAG_DRE;
        }
    }

    if (obj->spi.event & (SPI_EVENT_ERROR | SPI_EVENT_RX_OVERFLOW) || (interrupt_status & SERCOM_SPI_INTFLAG_ERROR)) {
        /* Clear all interrupts */
        _spi_clear_interrupts(obj);

        if (interrupt_status & SERCOM_SPI_INTFLAG_ERROR) {
            obj->spi.event = STATUS_ERR_BAD_DATA;
        }

        /* Transfer interrupted, invoke the callback function */
        if (obj->spi.event & SPI_EVENT_RX_OVERFLOW) {
            obj->spi.status = STATUS_ERR_OVERFLOW;
        } else {
            obj->spi.status = STATUS_ERR_BAD_DATA;
        }
        return obj->spi.status;
    }

    if ((obj->tx_buff.pos >= obj->tx_buff.length) && (obj->rx_buff.pos >= obj->rx_buff.length) && (interrupt_status & SERCOM_SPI_INTFLAG_TXC)) {
        /* Clear all interrupts */
        _spi_clear_interrupts(obj);

        /* Transfer complete, invoke the callback function */
        obj->spi.event = SPI_EVENT_INTERNAL_TRANSFER_COMPLETE;
        obj->spi.status = STATUS_OK;
    }

    return obj->spi.status;
}

/** Begin the SPI transfer. Buffer pointers and lengths are specified in tx_buff and rx_buff
 *
 * @param[in] obj       The SPI object which holds the transfer information
 * @param[in] tx        The buffer to send
 * @param[in] tx_length The number of words to transmit
 * @param[out]rx        The buffer to receive
 * @param[in] rx_length The number of words to receive
 * @param[in] bit_width The bit width of buffer words
 * @param[in] event     The logical OR of events to be registered
 * @param[in] handler   SPI interrupt handler
 * @param[in] hint      A suggestion for how to use DMA with this transfer **< DMA currently not implemented >**
 */
void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint)
{
    uint16_t dummy_read;
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    uint8_t sercom_index = _sercom_get_sercom_inst_index(obj->spi.spi);

    obj->spi.tx_buffer = tx;
    obj->tx_buff.buffer = tx;
    obj->tx_buff.pos = 0;
    if (tx) {
        /* Only two bit rates supported now */
        obj->tx_buff.length = tx_length * ((bit_width > 8)? 2 : 1);
    } else {
        if (rx) {
            obj->tx_buff.length = rx_length * ((bit_width > 8)? 2 : 1);
        } else {
            /* Nothing to transfer */
            return;
        }
    }

    obj->spi.rx_buffer = rx;
    obj->rx_buff.buffer = rx;
    obj->rx_buff.pos = 0;
    if (rx) {
        /* Only two bit rates supported now */
        obj->rx_buff.length = rx_length * ((bit_width > 8)? 2 : 1);
    } else {
        /* Disable RXEN */
        spi_disable(obj);
        _SPI(obj).CTRLB.bit.RXEN = 0;
        spi_enable(obj);
        obj->rx_buff.length = 0;
    }

    /* Clear data buffer if there is anything pending to read */
    while (spi_is_ready_to_read(obj)) {
        dummy_read = _SPI(obj).DATA.reg;
    }

    obj->spi.mask = event;

    obj->spi.dma_usage = hint;

    /*if (hint == DMA_USAGE_NEVER) {** TEMP: Commented as DMA is not implemented now */
    /* Use irq method */
    uint16_t irq_mask = 0;
    obj->spi.status = STATUS_BUSY;

    /* Enable interrupt */
    NVIC_SetVector((SERCOM0_IRQn + sercom_index), handler);
    NVIC_EnableIRQ(SERCOM0_IRQn + sercom_index);

    /* Clear all interrupts */
    _SPI(obj).INTENCLR.reg = SERCOM_SPI_INTFLAG_TXC | SERCOM_SPI_INTFLAG_RXC | SERCOM_SPI_INTFLAG_ERROR;
    _SPI(obj).INTFLAG.reg =  SERCOM_SPI_INTFLAG_TXC | SERCOM_SPI_INTFLAG_ERROR;
    _SPI(obj).STATUS.reg |=  SERCOM_SPI_STATUS_BUFOVF;

    /* Set SPI interrupts */
    if (tx) {
        irq_mask |= SERCOM_SPI_INTFLAG_DRE;
    }
    if (event & SPI_EVENT_ERROR) {
        irq_mask |= SERCOM_SPI_INTFLAG_ERROR;
    }
    _SPI(obj).INTENSET.reg = irq_mask;
    /*} ** TEMP: Commented as DMA is not implemented now */
}

/** The asynchronous IRQ handler
 *
 * Reads the received values out of the RX FIFO, writes values into the TX FIFO and checks for transfer termination
 * conditions, such as buffer overflows or transfer complete.
 * @param[in] obj     The SPI object which holds the transfer information
 * @return event flags if a transfer termination condition was met or 0 otherwise.
 */
uint32_t spi_irq_handler_asynch(spi_t *obj)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    uint32_t transfer_event = 0;

    /*if (obj->spi.dma_usage == DMA_USAGE_NEVER) {** TEMP: Commented as DMA is not implemented now */
    /* IRQ method */
    if (STATUS_BUSY != _spi_transceive_buffer(obj)) {
        transfer_event = obj->spi.event & (obj->spi.mask | SPI_EVENT_INTERNAL_TRANSFER_COMPLETE);
    }
    /*}** TEMP: Commented as DMA is not implemented now */
    return transfer_event;
}

/** Attempts to determine if the SPI peripheral is already in use.
 * @param[in] obj The SPI object to check for activity
 * @return non-zero if the SPI port is active or zero if it is not.
 */
uint8_t spi_active(spi_t *obj)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    /* Check if the SPI module is busy with a job */
    return (obj->spi.status == STATUS_BUSY);
}

/** Abort an SPI transfer
 *
 * @param obj The SPI peripheral to stop
 */
void spi_abort_asynch(spi_t *obj)
{
    /* Sanity check arguments */
    MBED_ASSERT(obj);

    uint8_t sercom_index = _sercom_get_sercom_inst_index(obj->spi.spi);

    /* Clear all interrupts */
    _SPI(obj).INTENCLR.reg =
        SERCOM_SPI_INTFLAG_DRE |
        SERCOM_SPI_INTFLAG_TXC |
        SERCOM_SPI_INTFLAG_RXC |
        SERCOM_SPI_INTFLAG_ERROR;

    // TODO: Disable and remove irq handler
    NVIC_DisableIRQ(SERCOM0_IRQn + sercom_index);
    NVIC_SetVector((SERCOM0_IRQn + sercom_index), (uint32_t)NULL);

    obj->spi.status = STATUS_ABORTED;
}

#endif /* DEVICE_SPI_ASYNCH */