mbed library sources
Fork of mbed-src by
Diff: targets/hal/TARGET_Atmel/TARGET_SAM21/spi_api.c
- Revision:
- 613:bc40b8d2aec4
- Parent:
- 612:fba1c7dc54c0
- Child:
- 614:9d86c2ae5de0
--- a/targets/hal/TARGET_Atmel/TARGET_SAM21/spi_api.c Tue Aug 18 15:00:09 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,941 +0,0 @@ -/* 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 */