mbed library sources
Fork of mbed-src by
Diff: targets/hal/TARGET_Atmel/TARGET_SAM21/spi_api.c
- Revision:
- 592:a274ee790e56
- Parent:
- 579:53297373a894
--- a/targets/hal/TARGET_Atmel/TARGET_SAM21/spi_api.c Wed Jul 15 08:15:08 2015 +0100 +++ b/targets/hal/TARGET_Atmel/TARGET_SAM21/spi_api.c Fri Jul 17 09:15:10 2015 +0100 @@ -22,34 +22,12 @@ #include "pinmap.h" #include "sercom.h" -/** Temporary definitions START - * Need to implement Pinmux APIs. For now, have hard coded to external SPIs available in SAM21 */ -#ifdef SAMR21 -#define EXT1_SPI_MODULE SERCOM5 -#define EXT1_SPI_SERCOM_MUX_SETTING ((0x1 << SERCOM_SPI_CTRLA_DOPO_Pos) | (0x0 << SERCOM_SPI_CTRLA_DIPO_Pos)) -#define EXT1_SPI_SERCOM_PINMUX_PAD0 PINMUX_PB02D_SERCOM5_PAD0 -#define EXT1_SPI_SERCOM_PINMUX_PAD1 PINMUX_PB03D_SERCOM5_PAD1 -#define EXT1_SPI_SERCOM_PINMUX_PAD2 PINMUX_PB22D_SERCOM5_PAD2 -#define EXT1_SPI_SERCOM_PINMUX_PAD3 PINMUX_PB23D_SERCOM5_PAD3 -#define EXT1_SPI_SERCOM_DMAC_ID_TX SERCOM5_DMAC_ID_TX -#define EXT1_SPI_SERCOM_DMAC_ID_RX SERCOM5_DMAC_ID_RX -#elif SAMD21 -#define EXT1_SPI_MODULE SERCOM0 -#define EXT1_SPI_SERCOM_MUX_SETTING ((0x1 << SERCOM_SPI_CTRLA_DOPO_Pos) | (0x0 << SERCOM_SPI_CTRLA_DIPO_Pos)) -#define EXT1_SPI_SERCOM_PINMUX_PAD0 PINMUX_PA04D_SERCOM0_PAD0 -#define EXT1_SPI_SERCOM_PINMUX_PAD1 PINMUX_PA05D_SERCOM0_PAD1 -#define EXT1_SPI_SERCOM_PINMUX_PAD2 PINMUX_PA06D_SERCOM0_PAD2 -#define EXT1_SPI_SERCOM_PINMUX_PAD3 PINMUX_PA07D_SERCOM0_PAD3 -#define EXT1_SPI_SERCOM_DMAC_ID_TX SERCOM0_DMAC_ID_TX -#define EXT1_SPI_SERCOM_DMAC_ID_RX SERCOM0_DMAC_ID_RX -#endif +#include "pinmap_function.h" -/** Default pinmux. */ -# define PINMUX_DEFAULT 0 - -/** Unused pinmux. */ -# define PINMUX_UNUSED 0xFFFFFFFF -/** Temporary definitions END */ +#define SPI_MOSI_INDEX 0 +#define SPI_MISO_INDEX 1 +#define SPI_SCLK_INDEX 2 +#define SPI_SSEL_INDEX 3 /** * \brief SPI modes enum @@ -82,31 +60,6 @@ extern uint8_t g_sys_init; uint16_t dummy_fill_word = 0xFFFF; -#if DEVICE_SPI_ASYNCH -/* Global variables */ -extern void *_sercom_instances[SERCOM_INST_NUM]; - -static void _spi_transceive_buffer(spi_t *obj); - -/** \internal - * Generates a SERCOM interrupt handler function for a given SERCOM index. - */ -#define _SERCOM_SPI_INTERRUPT_HANDLER(n, unused) \ -void SERCOM##n##_SPIHandler(void) \ -{ \ - _spi_transceive_buffer((spi_t *)_sercom_instances[n]); \ -} -#define _SERCOM_SPI_INTERRUPT_HANDLER_DECLR(n, unused) \ - (uint32_t)SERCOM##n##_SPIHandler, - -/** Auto-generate a set of interrupt handlers for each SERCOM SPI in the device */ -MREPEAT(SERCOM_INST_NUM, _SERCOM_SPI_INTERRUPT_HANDLER, ~) - -const uint32_t _sercom_handlers[SERCOM_INST_NUM] = { - MREPEAT(SERCOM_INST_NUM, _SERCOM_SPI_INTERRUPT_HANDLER_DECLR, ~) -}; -uint32_t _sercom_callbacks[SERCOM_INST_NUM] = {0}; -#endif /* DEVICE_SPI_ASYNCH */ static inline bool spi_is_syncing(spi_t *obj) { @@ -122,11 +75,6 @@ /* Sanity check arguments */ MBED_ASSERT(obj); -#if DEVICE_SPI_ASYNCH - /* Enable interrupt */ - NVIC_EnableIRQ(SERCOM0_IRQn + _sercom_get_sercom_inst_index(pSPI_SERCOM(obj))); -#endif - /* Wait until the synchronization is complete */ while (spi_is_syncing(obj)); @@ -139,10 +87,6 @@ /* Sanity check arguments */ MBED_ASSERT(obj); -#if DEVICE_SPI_ASYNCH - /* Disable interrupt */ - NVIC_DisableIRQ(SERCOM0_IRQn + _sercom_get_sercom_inst_index(pSPI_SERCOM(obj))); -#endif /* Wait until the synchronization is complete */ while (spi_is_syncing(obj)); @@ -221,6 +165,59 @@ 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 * @{ @@ -237,6 +234,10 @@ */ 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; @@ -247,9 +248,9 @@ g_sys_init = 1; } - /* TODO: Calculate SERCOM instance from pins */ - /* TEMP: Giving external SPI module value of SAMR21 for now */ - pSPI_SERCOM(obj) = EXT1_SPI_MODULE; + /* 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); @@ -258,7 +259,7 @@ if (_SPI(obj).CTRLA.reg & SERCOM_SPI_CTRLA_SWRST) { return; } - uint32_t sercom_index = _sercom_get_sercom_inst_index(pSPI_SERCOM(obj)); + uint32_t pm_index, gclk_index; #if (SAML21) if (sercom_index == 5) { @@ -292,14 +293,6 @@ system_gclk_chan_enable(gclk_index); sercom_set_gclk_generator(GCLK_GENERATOR_0, false); -#if DEVICE_SPI_ASYNCH - /* Save the object */ - _sercom_instances[sercom_index] = obj; - - /* Configure interrupt handler */ - NVIC_SetVector((SERCOM0_IRQn + sercom_index), (uint32_t)_sercom_handlers[sercom_index]); -#endif - /* Set the SERCOM in SPI master mode */ _SPI(obj).CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(0x3); pSPI_S(obj)->mode = SPI_MODE_MASTER; @@ -309,17 +302,18 @@ system_pinmux_get_config_defaults(&pin_conf); pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT; - uint32_t pad_pinmuxes[] = { - EXT1_SPI_SERCOM_PINMUX_PAD0, EXT1_SPI_SERCOM_PINMUX_PAD1, - EXT1_SPI_SERCOM_PINMUX_PAD2, EXT1_SPI_SERCOM_PINMUX_PAD3 - }; - + 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_pinmux = pad_pinmuxes[pad]; - if (current_pinmux != PINMUX_UNUSED) { - pin_conf.mux_position = current_pinmux & 0xFFFF; - system_pinmux_pin_set_config(current_pinmux >> 16, &pin_conf); + 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); + } } } @@ -333,8 +327,8 @@ } _SPI(obj).BAUD.reg = (uint8_t)baud; - /* Set MUX setting */ - ctrla |= EXT1_SPI_SERCOM_MUX_SETTING; /* TODO: Change this to appropriate Settings */ + /* TODO: Find MUX settings */ + ctrla |= spi_find_mux_settings(obj); /* Set SPI character size */ ctrlb |= SERCOM_SPI_CTRLB_CHSIZE(0); @@ -377,37 +371,40 @@ */ 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; - - struct system_pinmux_config pin_conf; - system_pinmux_get_config_defaults(&pin_conf); - pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT; - pin_conf.input_pull = SYSTEM_PINMUX_PIN_PULL_NONE; - - uint32_t pad_pinmuxes[] = { - EXT1_SPI_SERCOM_PINMUX_PAD0, EXT1_SPI_SERCOM_PINMUX_PAD1, - EXT1_SPI_SERCOM_PINMUX_PAD2, EXT1_SPI_SERCOM_PINMUX_PAD3 - }; - - /* Configure the SERCOM pins according to the user configuration */ - for (uint8_t pad = 0; pad < 4; pad++) { - uint32_t current_pinmux = pad_pinmuxes[pad]; - if (current_pinmux != PINMUX_UNUSED) { - pin_conf.mux_position = current_pinmux & 0xFFFF; - system_pinmux_pin_set_config(current_pinmux >> 16, &pin_conf); - } - } + pull_mode = PullNone; + /* Enable PLOADEN to avoid sending dummy character by slave */ + _SPI(obj).CTRLB.bit.PLOADEN = 1; } else { - /* Already in SPI master mode */ + /* Set the SERCOM in SPI mode */ + _SPI(obj).CTRLA.bit.MODE = 0x3; + pSPI_S(obj)->mode = SPI_MODE_MASTER; + pull_mode = PullUp; } - /* TODO: Change MUX settings to appropriate value */ + /* 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; @@ -432,6 +429,8 @@ void spi_frequency(spi_t *obj, int hz) { uint16_t baud = 0; + /* Sanity check arguments */ + MBED_ASSERT(obj); /* Disable SPI */ spi_disable(obj); @@ -711,6 +710,9 @@ */ 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 */ @@ -730,13 +732,10 @@ * \param[in,out] obj Pointer to SPI software instance struct * */ -static void _spi_transceive_buffer(spi_t *obj) +static enum status_code _spi_transceive_buffer(spi_t *obj) { /* Sanity check arguments */ MBED_ASSERT(obj); - void (*callback_func)(void); - - uint8_t sercom_index = _sercom_get_sercom_inst_index(obj->spi.spi); uint16_t interrupt_status = _SPI(obj).INTFLAG.reg; interrupt_status &= _SPI(obj).INTENSET.reg; @@ -780,11 +779,7 @@ } else { obj->spi.status = STATUS_ERR_BAD_DATA; } - callback_func = _sercom_callbacks[sercom_index]; - if (callback_func && (obj->spi.mask & (SPI_EVENT_ERROR | SPI_EVENT_RX_OVERFLOW))) { - callback_func(); - } - return; + 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)) { @@ -794,12 +789,9 @@ /* Transfer complete, invoke the callback function */ obj->spi.event = SPI_EVENT_INTERNAL_TRANSFER_COMPLETE; obj->spi.status = STATUS_OK; - callback_func = _sercom_callbacks[sercom_index]; - if (callback_func && (obj->spi.mask & SPI_EVENT_COMPLETE)) { - callback_func(); - } - return; } + + return obj->spi.status; } /** Begin the SPI transfer. Buffer pointers and lengths are specified in tx_buff and rx_buff @@ -856,7 +848,6 @@ dummy_read = _SPI(obj).DATA.reg; } - _sercom_callbacks[sercom_index] = handler; obj->spi.mask = event; obj->spi.dma_usage = hint; @@ -867,7 +858,7 @@ obj->spi.status = STATUS_BUSY; /* Enable interrupt */ - NVIC_SetVector((SERCOM0_IRQn + sercom_index), _sercom_handlers[sercom_index]); + NVIC_SetVector((SERCOM0_IRQn + sercom_index), handler); NVIC_EnableIRQ(SERCOM0_IRQn + sercom_index); /* Clear all interrupts */ @@ -895,53 +886,16 @@ */ uint32_t spi_irq_handler_asynch(spi_t *obj) { + /* Sanity check arguments */ + MBED_ASSERT(obj); + uint32_t transfer_event = 0; - uint32_t bytes_to_transfer = 0; - - uint8_t sercom_index = _sercom_get_sercom_inst_index(obj->spi.spi); /*if (obj->spi.dma_usage == DMA_USAGE_NEVER) {** TEMP: Commented as DMA is not implemented now */ /* IRQ method */ - if (obj->spi.event & SPI_EVENT_INTERNAL_TRANSFER_COMPLETE) { - obj->spi.event |= SPI_EVENT_COMPLETE; - transfer_event = obj->spi.event; - } else { - /* Data is still remaining to be transferred! */ - obj->spi.status = STATUS_BUSY; - - /* Read any pending data in RX buffer */ - while (spi_is_ready_to_read(obj)) { - _spi_read_async(obj); - } - - while (obj->tx_buff.pos < obj->tx_buff.length) { - /* Write data */ - _spi_write_async(obj); - /* Read if any */ - if ((obj->rx_buff.buffer) && (obj->rx_buff.pos < obj->rx_buff.length)) { - if (spi_is_ready_to_read(obj)) { - _spi_read_async(obj); - } - /* Extend TX buffer (with dummy) if there is more to receive */ - 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->spi.event & SPI_EVENT_ERROR) { - transfer_event = obj->spi.event; - obj->spi.status = STATUS_ERR_BAD_DATA; - break; - } - } - if ((obj->tx_buff.pos >= obj->tx_buff.length) && (obj->rx_buff.pos >= obj->rx_buff.length)) { - transfer_event = (SPI_EVENT_INTERNAL_TRANSFER_COMPLETE | SPI_EVENT_COMPLETE); - obj->spi.status = STATUS_OK; - } + if (STATUS_BUSY != _spi_transceive_buffer(obj)) { + transfer_event = obj->spi.event & (obj->spi.mask | SPI_EVENT_INTERNAL_TRANSFER_COMPLETE); } - transfer_event &= (obj->spi.mask | SPI_EVENT_INTERNAL_TRANSFER_COMPLETE); - /* Clear all interrupts */ - _spi_clear_interrupts(obj); /*}** TEMP: Commented as DMA is not implemented now */ return transfer_event; } @@ -952,6 +906,9 @@ */ 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); }