742 lines
22 KiB
C
742 lines
22 KiB
C
/**
|
|
* \file
|
|
*
|
|
* \brief SAM Serial Peripheral Interface Driver
|
|
*
|
|
* Copyright (c) 2013-2016 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
|
|
*
|
|
*/
|
|
/*
|
|
* Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
|
|
*/
|
|
#include "spi_interrupt.h"
|
|
|
|
/**
|
|
* \internal
|
|
*
|
|
* Dummy byte to send when reading in master mode.
|
|
*/
|
|
uint16_t dummy_write;
|
|
|
|
/**
|
|
* \internal
|
|
* Starts transceive of buffers with a given length
|
|
*
|
|
* \param[in] module Pointer to SPI software instance struct
|
|
* \param[in] rx_data Pointer to data to be received
|
|
* \param[in] tx_data Pointer to data to be transmitted
|
|
* \param[in] length Length of data buffer
|
|
*
|
|
*/
|
|
static void _spi_transceive_buffer(
|
|
struct spi_module *const module,
|
|
uint8_t *tx_data,
|
|
uint8_t *rx_data,
|
|
uint16_t length)
|
|
{
|
|
Assert(module);
|
|
Assert(tx_data);
|
|
|
|
/* Write parameters to the device instance */
|
|
module->remaining_tx_buffer_length = length;
|
|
module->remaining_rx_buffer_length = length;
|
|
module->rx_buffer_ptr = rx_data;
|
|
module->tx_buffer_ptr = tx_data;
|
|
module->status = STATUS_BUSY;
|
|
|
|
module->dir = SPI_DIRECTION_BOTH;
|
|
|
|
/* Get a pointer to the hardware module instance */
|
|
SercomSpi *const hw = &(module->hw->SPI);
|
|
|
|
/* Enable the Data Register Empty and RX Complete Interrupt */
|
|
hw->INTENSET.reg = (SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY |
|
|
SPI_INTERRUPT_FLAG_RX_COMPLETE);
|
|
|
|
# if CONF_SPI_SLAVE_ENABLE == true
|
|
if (module->mode == SPI_MODE_SLAVE) {
|
|
/* Clear TXC flag if set */
|
|
hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE;
|
|
/* Enable transmit complete interrupt for slave */
|
|
hw->INTENSET.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE;
|
|
}
|
|
# endif
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* Starts write of a buffer with a given length
|
|
*
|
|
* \param[in] module Pointer to SPI software instance struct
|
|
* \param[in] tx_data Pointer to data to be transmitted
|
|
* \param[in] length Length of data buffer
|
|
*
|
|
*/
|
|
static void _spi_write_buffer(
|
|
struct spi_module *const module,
|
|
uint8_t *tx_data,
|
|
uint16_t length)
|
|
{
|
|
Assert(module);
|
|
Assert(tx_data);
|
|
|
|
/* Write parameters to the device instance */
|
|
module->remaining_tx_buffer_length = length;
|
|
module->remaining_dummy_buffer_length = length;
|
|
module->tx_buffer_ptr = tx_data;
|
|
module->status = STATUS_BUSY;
|
|
|
|
module->dir = SPI_DIRECTION_WRITE;
|
|
|
|
/* Get a pointer to the hardware module instance */
|
|
SercomSpi *const hw = &(module->hw->SPI);
|
|
|
|
# if CONF_SPI_SLAVE_ENABLE == true
|
|
if (module->mode == SPI_MODE_SLAVE) {
|
|
/* Clear TXC flag if set */
|
|
hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE;
|
|
/* Enable transmit complete interrupt for slave */
|
|
hw->INTENSET.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE;
|
|
}
|
|
# endif
|
|
|
|
if (module->receiver_enabled) {
|
|
/* Enable the Data Register Empty and RX Complete interrupt */
|
|
hw->INTENSET.reg = (SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY |
|
|
SPI_INTERRUPT_FLAG_RX_COMPLETE);
|
|
} else {
|
|
/* Enable the Data Register Empty interrupt */
|
|
hw->INTENSET.reg = SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
* Setup SPI to read a buffer with a given length
|
|
*
|
|
* \param[in] module Pointer to SPI software instance struct
|
|
* \param[in] rx_data Pointer to data to be received
|
|
* \param[in] length Length of data buffer
|
|
*
|
|
*/
|
|
static void _spi_read_buffer(
|
|
struct spi_module *const module,
|
|
uint8_t *rx_data,
|
|
uint16_t length)
|
|
{
|
|
Assert(module);
|
|
Assert(rx_data);
|
|
|
|
uint8_t tmp_intenset = 0;
|
|
|
|
/* Set length for the buffer and the pointer, and let
|
|
* the interrupt handler do the rest */
|
|
module->remaining_rx_buffer_length = length;
|
|
module->remaining_dummy_buffer_length = length;
|
|
module->rx_buffer_ptr = rx_data;
|
|
module->status = STATUS_BUSY;
|
|
|
|
module->dir = SPI_DIRECTION_READ;
|
|
|
|
/* Get a pointer to the hardware module instance */
|
|
SercomSpi *const hw = &(module->hw->SPI);
|
|
|
|
/* Enable the RX Complete Interrupt */
|
|
tmp_intenset = SPI_INTERRUPT_FLAG_RX_COMPLETE;
|
|
|
|
# if CONF_SPI_MASTER_ENABLE == true
|
|
if (module->mode == SPI_MODE_MASTER && module->dir == SPI_DIRECTION_READ) {
|
|
/* Enable Data Register Empty interrupt for master */
|
|
tmp_intenset |= SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY;
|
|
}
|
|
# endif
|
|
# if CONF_SPI_SLAVE_ENABLE == true
|
|
if (module->mode == SPI_MODE_SLAVE) {
|
|
/* Clear TXC flag if set */
|
|
hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE;
|
|
/* Enable transmit complete interrupt for slave */
|
|
tmp_intenset |= SPI_INTERRUPT_FLAG_TX_COMPLETE;
|
|
|
|
/* Workaround for SSL flag enable */
|
|
#ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT
|
|
/* Clear SSL flag if set */
|
|
hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW;
|
|
/* Enable Slave Select Low Interrupt for slave */
|
|
tmp_intenset |= SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW;
|
|
#endif
|
|
}
|
|
# endif
|
|
|
|
/* Enable all interrupts simultaneously */
|
|
hw->INTENSET.reg = tmp_intenset;
|
|
}
|
|
|
|
/**
|
|
* \brief Registers a SPI callback function
|
|
*
|
|
* Registers a callback function which is implemented by the user.
|
|
*
|
|
* \note The callback must be enabled by \ref spi_enable_callback, in order
|
|
* for the interrupt handler to call it when the conditions for the
|
|
* callback type are met.
|
|
*
|
|
* \param[in] module Pointer to USART software instance struct
|
|
* \param[in] callback_func Pointer to callback function
|
|
* \param[in] callback_type Callback type given by an enum
|
|
*
|
|
*/
|
|
void spi_register_callback(
|
|
struct spi_module *const module,
|
|
spi_callback_t callback_func,
|
|
enum spi_callback callback_type)
|
|
{
|
|
/* Sanity check arguments */
|
|
Assert(module);
|
|
Assert(callback_func);
|
|
|
|
/* Register callback function */
|
|
module->callback[callback_type] = callback_func;
|
|
|
|
/* Set the bit corresponding to the callback_type */
|
|
module->registered_callback |= (1 << callback_type);
|
|
}
|
|
|
|
/**
|
|
* \brief Unregisters a SPI callback function
|
|
*
|
|
* Unregisters a callback function which is implemented by the user.
|
|
*
|
|
* \param[in] module Pointer to SPI software instance struct
|
|
* \param[in] callback_type Callback type given by an enum
|
|
*
|
|
*/
|
|
void spi_unregister_callback(
|
|
struct spi_module *const module,
|
|
enum spi_callback callback_type)
|
|
{
|
|
/* Sanity check arguments */
|
|
Assert(module);
|
|
|
|
/* Unregister callback function */
|
|
module->callback[callback_type] = NULL;
|
|
|
|
/* Clear the bit corresponding to the callback_type */
|
|
module->registered_callback &= ~(1 << callback_type);
|
|
}
|
|
|
|
/**
|
|
* \brief Asynchronous buffer write
|
|
*
|
|
* Sets up the driver to write to the SPI from a given buffer. If registered
|
|
* and enabled, a callback function will be called when the write is finished.
|
|
*
|
|
* \param[in] module Pointer to SPI software instance struct
|
|
* \param[out] tx_data Pointer to data buffer to receive
|
|
* \param[in] length Data buffer length
|
|
*
|
|
* \returns Status of the write request operation.
|
|
* \retval STATUS_OK If the operation completed successfully
|
|
* \retval STATUS_ERR_BUSY If the SPI was already busy with a write
|
|
* operation
|
|
* \retval STATUS_ERR_INVALID_ARG If requested write length was zero
|
|
*/
|
|
enum status_code spi_write_buffer_job(
|
|
struct spi_module *const module,
|
|
uint8_t *tx_data,
|
|
uint16_t length)
|
|
{
|
|
Assert(module);
|
|
Assert(tx_data);
|
|
|
|
if (length == 0) {
|
|
return STATUS_ERR_INVALID_ARG;
|
|
}
|
|
|
|
/* Check if the SPI is busy transmitting or slave waiting for TXC*/
|
|
if (module->status == STATUS_BUSY) {
|
|
return STATUS_BUSY;
|
|
}
|
|
|
|
/* Issue internal write */
|
|
_spi_write_buffer(module, tx_data, length);
|
|
|
|
return STATUS_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Asynchronous buffer read
|
|
*
|
|
* Sets up the driver to read from the SPI to a given buffer. If registered
|
|
* and enabled, a callback function will be called when the read is finished.
|
|
*
|
|
* \note If address matching is enabled for the slave, the first character
|
|
* received and placed in the RX buffer will be the address.
|
|
*
|
|
* \param[in] module Pointer to SPI software instance struct
|
|
* \param[out] rx_data Pointer to data buffer to receive
|
|
* \param[in] length Data buffer length
|
|
* \param[in] dummy Dummy character to send when reading in master mode
|
|
*
|
|
* \returns Status of the operation.
|
|
* \retval STATUS_OK If the operation completed successfully
|
|
* \retval STATUS_ERR_BUSY If the SPI was already busy with a read
|
|
* operation
|
|
* \retval STATUS_ERR_DENIED If the receiver is not enabled
|
|
* \retval STATUS_ERR_INVALID_ARG If requested read length was zero
|
|
*/
|
|
enum status_code spi_read_buffer_job(
|
|
struct spi_module *const module,
|
|
uint8_t *rx_data,
|
|
uint16_t length,
|
|
uint16_t dummy)
|
|
{
|
|
/* Sanity check arguments */
|
|
Assert(module);
|
|
Assert(rx_data);
|
|
|
|
if (length == 0) {
|
|
return STATUS_ERR_INVALID_ARG;
|
|
}
|
|
|
|
if (!(module->receiver_enabled)) {
|
|
return STATUS_ERR_DENIED;
|
|
}
|
|
|
|
/* Check if the SPI is busy transmitting or slave waiting for TXC*/
|
|
if (module->status == STATUS_BUSY) {
|
|
return STATUS_BUSY;
|
|
}
|
|
|
|
dummy_write = dummy;
|
|
/* Issue internal read */
|
|
_spi_read_buffer(module, rx_data, length);
|
|
return STATUS_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Asynchronous buffer write and read
|
|
*
|
|
* Sets up the driver to write and read to and from given buffers. If registered
|
|
* and enabled, a callback function will be called when the transfer is finished.
|
|
*
|
|
* \note If address matching is enabled for the slave, the first character
|
|
* received and placed in the RX buffer will be the address.
|
|
*
|
|
* \param[in] module Pointer to SPI software instance struct
|
|
* \param[in] tx_data Pointer to data buffer to send
|
|
* \param[out] rx_data Pointer to data buffer to receive
|
|
* \param[in] length Data buffer length
|
|
*
|
|
* \returns Status of the operation.
|
|
* \retval STATUS_OK If the operation completed successfully
|
|
* \retval STATUS_ERR_BUSY If the SPI was already busy with a read
|
|
* operation
|
|
* \retval STATUS_ERR_DENIED If the receiver is not enabled
|
|
* \retval STATUS_ERR_INVALID_ARG If requested read length was zero
|
|
*/
|
|
enum status_code spi_transceive_buffer_job(
|
|
struct spi_module *const module,
|
|
uint8_t *tx_data,
|
|
uint8_t *rx_data,
|
|
uint16_t length)
|
|
{
|
|
/* Sanity check arguments */
|
|
Assert(module);
|
|
Assert(rx_data);
|
|
|
|
if (length == 0) {
|
|
return STATUS_ERR_INVALID_ARG;
|
|
}
|
|
|
|
if (!(module->receiver_enabled)) {
|
|
return STATUS_ERR_DENIED;
|
|
}
|
|
|
|
/* Check if the SPI is busy transmitting or slave waiting for TXC*/
|
|
if (module->status == STATUS_BUSY) {
|
|
return STATUS_BUSY;
|
|
}
|
|
|
|
/* Issue internal transceive */
|
|
_spi_transceive_buffer(module, tx_data, rx_data, length);
|
|
|
|
return STATUS_OK;
|
|
}
|
|
/**
|
|
* \brief Aborts an ongoing job
|
|
*
|
|
* This function will abort the specified job type.
|
|
*
|
|
* \param[in] module Pointer to SPI software instance struct
|
|
*/
|
|
void spi_abort_job(
|
|
struct spi_module *const module)
|
|
{
|
|
/* Pointer to the hardware module instance */
|
|
SercomSpi *const spi_hw
|
|
= &(module->hw->SPI);
|
|
|
|
/* Abort ongoing job */
|
|
|
|
/* Disable interrupts */
|
|
spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_RX_COMPLETE |
|
|
SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY |
|
|
SPI_INTERRUPT_FLAG_TX_COMPLETE;
|
|
|
|
module->status = STATUS_ABORTED;
|
|
module->remaining_rx_buffer_length = 0;
|
|
module->remaining_dummy_buffer_length = 0;
|
|
module->remaining_tx_buffer_length = 0;
|
|
|
|
module->dir = SPI_DIRECTION_IDLE;
|
|
}
|
|
|
|
# if CONF_SPI_SLAVE_ENABLE == true || CONF_SPI_MASTER_ENABLE == true
|
|
/**
|
|
* \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(
|
|
struct spi_module *const module)
|
|
{
|
|
/* Pointer to the hardware module instance */
|
|
SercomSpi *const spi_hw = &(module->hw->SPI);
|
|
|
|
/* Write value will be at least 8-bits long */
|
|
uint16_t data_to_send = *(module->tx_buffer_ptr);
|
|
/* Increment 8-bit pointer */
|
|
(module->tx_buffer_ptr)++;
|
|
|
|
if (module->character_size == SPI_CHARACTER_SIZE_9BIT) {
|
|
data_to_send |= ((*(module->tx_buffer_ptr)) << 8);
|
|
/* Increment 8-bit pointer */
|
|
(module->tx_buffer_ptr)++;
|
|
}
|
|
|
|
/* Write the data to send*/
|
|
spi_hw->DATA.reg = data_to_send & SERCOM_SPI_DATA_MASK;
|
|
|
|
/* Decrement remaining buffer length */
|
|
(module->remaining_tx_buffer_length)--;
|
|
}
|
|
# endif
|
|
|
|
# if CONF_SPI_MASTER_ENABLE == true
|
|
/**
|
|
* \internal
|
|
* Writes a dummy character to the Data register.
|
|
*
|
|
* \param[in,out] module Pointer to SPI software instance struct
|
|
*/
|
|
static void _spi_write_dummy(
|
|
struct spi_module *const module)
|
|
{
|
|
/* Pointer to the hardware module instance */
|
|
SercomSpi *const spi_hw = &(module->hw->SPI);
|
|
|
|
/* Write dummy byte */
|
|
spi_hw->DATA.reg = dummy_write;
|
|
|
|
/* Decrement remaining dummy buffer length */
|
|
module->remaining_dummy_buffer_length--;
|
|
}
|
|
# endif
|
|
|
|
/**
|
|
* \internal
|
|
* Writes a dummy character from the to the Data register.
|
|
*
|
|
* \param[in,out] module Pointer to SPI software instance struct
|
|
*/
|
|
static void _spi_read_dummy(
|
|
struct spi_module *const module)
|
|
{
|
|
/* Pointer to the hardware module instance */
|
|
SercomSpi *const spi_hw = &(module->hw->SPI);
|
|
uint16_t flush = 0;
|
|
|
|
/* Read dummy byte */
|
|
flush = spi_hw->DATA.reg;
|
|
UNUSED(flush);
|
|
|
|
/* Decrement remaining dummy buffer length */
|
|
module->remaining_dummy_buffer_length--;
|
|
}
|
|
|
|
/**
|
|
* \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(
|
|
struct spi_module *const module)
|
|
{
|
|
/* Pointer to the hardware module instance */
|
|
SercomSpi *const spi_hw = &(module->hw->SPI);
|
|
|
|
uint16_t received_data = (spi_hw->DATA.reg & SERCOM_SPI_DATA_MASK);
|
|
|
|
/* Read value will be at least 8-bits long */
|
|
*(module->rx_buffer_ptr) = received_data;
|
|
/* Increment 8-bit pointer */
|
|
module->rx_buffer_ptr += 1;
|
|
|
|
if(module->character_size == SPI_CHARACTER_SIZE_9BIT) {
|
|
/* 9-bit data, write next received byte to the buffer */
|
|
*(module->rx_buffer_ptr) = (received_data >> 8);
|
|
/* Increment 8-bit pointer */
|
|
module->rx_buffer_ptr += 1;
|
|
}
|
|
|
|
/* Decrement length of the remaining buffer */
|
|
module->remaining_rx_buffer_length--;
|
|
}
|
|
|
|
/**
|
|
* \internal
|
|
*
|
|
* Handles interrupts as they occur, and it will run callback functions
|
|
* which are registered and enabled.
|
|
*
|
|
* \note This function will be called by the Sercom_Handler, and should
|
|
* not be called directly from any application code.
|
|
*
|
|
* \param[in] instance ID of the SERCOM instance calling the interrupt
|
|
* handler.
|
|
*/
|
|
void _spi_interrupt_handler(
|
|
uint8_t instance)
|
|
{
|
|
/* Get device instance from the look-up table */
|
|
struct spi_module *module
|
|
= (struct spi_module *)_sercom_instances[instance];
|
|
|
|
/* Pointer to the hardware module instance */
|
|
SercomSpi *const spi_hw = &(module->hw->SPI);
|
|
|
|
/* Combine callback registered and enabled masks. */
|
|
uint8_t callback_mask =
|
|
module->enabled_callback & module->registered_callback;
|
|
|
|
/* Read and mask interrupt flag register */
|
|
uint16_t interrupt_status = spi_hw->INTFLAG.reg;
|
|
interrupt_status &= spi_hw->INTENSET.reg;
|
|
|
|
/* Data register empty interrupt */
|
|
if (interrupt_status & SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY) {
|
|
# if CONF_SPI_MASTER_ENABLE == true
|
|
if ((module->mode == SPI_MODE_MASTER) &&
|
|
(module->dir == SPI_DIRECTION_READ)) {
|
|
/* Send dummy byte when reading in master mode */
|
|
_spi_write_dummy(module);
|
|
if (module->remaining_dummy_buffer_length == 0) {
|
|
/* Disable the Data Register Empty Interrupt */
|
|
spi_hw->INTENCLR.reg
|
|
= SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY;
|
|
}
|
|
}
|
|
# endif
|
|
|
|
if (0
|
|
# if CONF_SPI_MASTER_ENABLE == true
|
|
|| ((module->mode == SPI_MODE_MASTER) &&
|
|
(module->dir != SPI_DIRECTION_READ))
|
|
# endif
|
|
# if CONF_SPI_SLAVE_ENABLE == true
|
|
|| ((module->mode == SPI_MODE_SLAVE) &&
|
|
(module->dir != SPI_DIRECTION_READ))
|
|
# endif
|
|
) {
|
|
/* Write next byte from buffer */
|
|
_spi_write(module);
|
|
if (module->remaining_tx_buffer_length == 0) {
|
|
/* Disable the Data Register Empty Interrupt */
|
|
spi_hw->INTENCLR.reg
|
|
= SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY;
|
|
|
|
if (module->dir == SPI_DIRECTION_WRITE &&
|
|
!(module->receiver_enabled)) {
|
|
/* Enable the Data Register transmit complete Interrupt */
|
|
spi_hw->INTENSET.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Receive complete interrupt*/
|
|
if (interrupt_status & SPI_INTERRUPT_FLAG_RX_COMPLETE) {
|
|
/* Check for overflow */
|
|
if (spi_hw->STATUS.reg & SERCOM_SPI_STATUS_BUFOVF) {
|
|
if (module->dir != SPI_DIRECTION_WRITE) {
|
|
/* Store the error code */
|
|
module->status = STATUS_ERR_OVERFLOW;
|
|
|
|
/* End transaction */
|
|
module->dir = SPI_DIRECTION_IDLE;
|
|
|
|
spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_RX_COMPLETE |
|
|
SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY;
|
|
/* Run callback if registered and enabled */
|
|
if (callback_mask & (1 << SPI_CALLBACK_ERROR)) {
|
|
(module->callback[SPI_CALLBACK_ERROR])(module);
|
|
}
|
|
}
|
|
/* Flush */
|
|
uint16_t flush = spi_hw->DATA.reg;
|
|
UNUSED(flush);
|
|
/* Clear overflow flag */
|
|
spi_hw->STATUS.reg = SERCOM_SPI_STATUS_BUFOVF;
|
|
} else {
|
|
if (module->dir == SPI_DIRECTION_WRITE) {
|
|
/* Flush receive buffer when writing */
|
|
_spi_read_dummy(module);
|
|
if (module->remaining_dummy_buffer_length == 0) {
|
|
spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_RX_COMPLETE;
|
|
module->status = STATUS_OK;
|
|
module->dir = SPI_DIRECTION_IDLE;
|
|
/* Run callback if registered and enabled */
|
|
if (callback_mask &
|
|
(1 << SPI_CALLBACK_BUFFER_TRANSMITTED)){
|
|
(module->callback[SPI_CALLBACK_BUFFER_TRANSMITTED])(module);
|
|
}
|
|
}
|
|
} else {
|
|
/* Read data register */
|
|
_spi_read(module);
|
|
|
|
/* Check if the last character have been received */
|
|
if (module->remaining_rx_buffer_length == 0) {
|
|
module->status = STATUS_OK;
|
|
/* Disable RX Complete Interrupt and set status */
|
|
spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_RX_COMPLETE;
|
|
if(module->dir == SPI_DIRECTION_BOTH) {
|
|
if (callback_mask & (1 << SPI_CALLBACK_BUFFER_TRANSCEIVED)) {
|
|
(module->callback[SPI_CALLBACK_BUFFER_TRANSCEIVED])(module);
|
|
}
|
|
} else if (module->dir == SPI_DIRECTION_READ) {
|
|
if (callback_mask & (1 << SPI_CALLBACK_BUFFER_RECEIVED)) {
|
|
(module->callback[SPI_CALLBACK_BUFFER_RECEIVED])(module);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Transmit complete */
|
|
if (interrupt_status & SPI_INTERRUPT_FLAG_TX_COMPLETE) {
|
|
# if CONF_SPI_SLAVE_ENABLE == true
|
|
if (module->mode == SPI_MODE_SLAVE) {
|
|
/* Transaction ended by master */
|
|
|
|
/* Disable interrupts */
|
|
spi_hw->INTENCLR.reg =
|
|
SPI_INTERRUPT_FLAG_TX_COMPLETE |
|
|
SPI_INTERRUPT_FLAG_RX_COMPLETE |
|
|
SPI_INTERRUPT_FLAG_DATA_REGISTER_EMPTY;
|
|
/* Clear interrupt flag */
|
|
spi_hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE;
|
|
|
|
|
|
/* Reset all status information */
|
|
module->dir = SPI_DIRECTION_IDLE;
|
|
module->remaining_tx_buffer_length = 0;
|
|
module->remaining_rx_buffer_length = 0;
|
|
module->status = STATUS_OK;
|
|
|
|
if (callback_mask &
|
|
(1 << SPI_CALLBACK_SLAVE_TRANSMISSION_COMPLETE)) {
|
|
(module->callback[SPI_CALLBACK_SLAVE_TRANSMISSION_COMPLETE])
|
|
(module);
|
|
}
|
|
|
|
}
|
|
# endif
|
|
# if CONF_SPI_MASTER_ENABLE == true
|
|
if ((module->mode == SPI_MODE_MASTER) &&
|
|
(module->dir == SPI_DIRECTION_WRITE) && !(module->receiver_enabled)) {
|
|
/* Clear interrupt flag */
|
|
spi_hw->INTENCLR.reg
|
|
= SPI_INTERRUPT_FLAG_TX_COMPLETE;
|
|
/* Buffer sent with receiver disabled */
|
|
module->dir = SPI_DIRECTION_IDLE;
|
|
module->status = STATUS_OK;
|
|
/* Run callback if registered and enabled */
|
|
if (callback_mask & (1 << SPI_CALLBACK_BUFFER_TRANSMITTED)){
|
|
(module->callback[SPI_CALLBACK_BUFFER_TRANSMITTED])
|
|
(module);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
# ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT
|
|
# if CONF_SPI_SLAVE_ENABLE == true
|
|
/* When a high to low transition is detected on the _SS pin in slave mode */
|
|
if (interrupt_status & SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW) {
|
|
if (module->mode == SPI_MODE_SLAVE) {
|
|
/* Disable interrupts */
|
|
spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW;
|
|
/* Clear interrupt flag */
|
|
spi_hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_SLAVE_SELECT_LOW;
|
|
|
|
if (callback_mask & (1 << SPI_CALLBACK_SLAVE_SELECT_LOW)) {
|
|
(module->callback[SPI_CALLBACK_SLAVE_SELECT_LOW])(module);
|
|
}
|
|
}
|
|
}
|
|
# endif
|
|
# endif
|
|
|
|
# ifdef FEATURE_SPI_ERROR_INTERRUPT
|
|
/* When combined error happen */
|
|
if (interrupt_status & SPI_INTERRUPT_FLAG_COMBINED_ERROR) {
|
|
/* Disable interrupts */
|
|
spi_hw->INTENCLR.reg = SPI_INTERRUPT_FLAG_COMBINED_ERROR;
|
|
/* Clear interrupt flag */
|
|
spi_hw->INTFLAG.reg = SPI_INTERRUPT_FLAG_COMBINED_ERROR;
|
|
|
|
if (callback_mask & (1 << SPI_CALLBACK_COMBINED_ERROR)) {
|
|
(module->callback[SPI_CALLBACK_COMBINED_ERROR])(module);
|
|
}
|
|
}
|
|
# endif
|
|
}
|