diff --git a/drivers/include/drivers/SPI.h b/drivers/include/drivers/SPI.h index 86dc4be757f..e05bac4801f 100644 --- a/drivers/include/drivers/SPI.h +++ b/drivers/include/drivers/SPI.h @@ -179,9 +179,12 @@ const use_gpio_ssel_t use_gpio_ssel; * applies to receiving data, so be sure to check examples in the datasheet to determine what frame * size to use and whether byte swapping is needed when working with an external chip.

* - *

Note: Some Mbed targets support frame sizes that are not standard integer sizes, e.g. 4 bits, 7 bits, or - * 24 bits. However, the behavior of these frame sizes is not well defined and may differ across targets - * or across API calls (e.g. single-byte vs transaction vs async). More work is needed to make these consistent.

+ * \note Some Mbed targets support frame sizes that are not standard integer sizes, e.g. 4 bits, 7 bits, or + * 24 bits. However, the behavior of these frame sizes is currently not tested, and you may need to test what works or + * inspect your target's SPI driver code. More work is needed to make these consistent. When available, using these frame sizes + * requires writing each word to be transmitted into the next-largest C integer type. For example, to send 24-bit frames, you + * would create an array of uint32_ts and write each 24-bit integer into its own uint32_t. + * Then you would pass in the byte length of the array (number of frames * 4, NOT number of frames * 3) to the SPI driver. * *

Asynchronous API

*

On many processors, Mbed OS also supports asynchronous %SPI. This feature allows you to run %SPI diff --git a/drivers/source/SPI.cpp b/drivers/source/SPI.cpp index a0339487215..848f7e80390 100644 --- a/drivers/source/SPI.cpp +++ b/drivers/source/SPI.cpp @@ -427,7 +427,7 @@ void SPI::abort_all_transfers() int SPI::set_dma_usage(DMAUsage usage) { - if (spi_active(&_peripheral->spi)) { + if (_peripheral->initialized && spi_active(&_peripheral->spi)) { return -1; } _usage = usage; diff --git a/hal/include/hal/dma_api.h b/hal/include/hal/dma_api.h index d12be44d7f4..699d89d64d8 100644 --- a/hal/include/hal/dma_api.h +++ b/hal/include/hal/dma_api.h @@ -30,7 +30,7 @@ * @brief Enumeration of possible DMA usage hints */ typedef enum { - DMA_USAGE_NEVER, ///< Never use DMA + DMA_USAGE_NEVER = 0, ///< Never use DMA DMA_USAGE_OPPORTUNISTIC, ///< Use DMA if possible but deallocate DMA resources when not being used. DMA_USAGE_ALWAYS, ///< Always use DMA when possible DMA_USAGE_TEMPORARY_ALLOCATED, // Seems to be used as an internal state indicator for "we need to deallocate these channels." diff --git a/hal/include/hal/spi_api.h b/hal/include/hal/spi_api.h index 8bf50347b9b..90e836f52ab 100644 --- a/hal/include/hal/spi_api.h +++ b/hal/include/hal/spi_api.h @@ -192,6 +192,10 @@ SPIName spi_get_peripheral_name(PinName mosi, PinName miso, PinName mclk); /** * Fills the given spi_capabilities_t structure with the capabilities of the given peripheral. + * + * @param ssel The CS pin being used, for checking the \c hw_cs_handle flag + * @param slave True to get capabilities for slave mode, false to get capabilities for master mode + * @param[out] cap Capabilities are returned here */ void spi_get_capabilities(PinName ssel, bool slave, spi_capabilities_t *cap); @@ -419,7 +423,7 @@ const PinMap *spi_slave_cs_pinmap(void); * @param[in] rx_length The number of bytes 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] handler SPI interrupt handler. This will point, through a bit of indirection, to \c SPI::irq_handler_asynch() for the correct SPI instance * @param[in] hint A suggestion for how to use DMA with this transfer * * @return True if DMA was actually used for the transfer, false otherwise (if interrupts or another CPU-based @@ -429,6 +433,10 @@ const PinMap *spi_slave_cs_pinmap(void); * after the transfer is complete. If this function returns true, the driver layer will cache invalidate the Rx buffer under * the assumption that the data needs to be re-read from main memory. Be careful, because if the read was not actually * done by DMA, and the rx data is in the CPU cache, this invalidation will corrupt it. + * + * @note The application layer will always acquire the SPI peripheral first before calling this, including setting the frequency and the bit width. So, + * the \c bit_width argument will never be different from the SPI's currently set bit width, and can actually be ignored. + * TODO remove this argument entirely. */ bool 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); diff --git a/targets/TARGET_NUVOTON/TARGET_M480/PeripheralNames.h b/targets/TARGET_NUVOTON/TARGET_M480/PeripheralNames.h index 9bf6d76141b..d139cfede69 100644 --- a/targets/TARGET_NUVOTON/TARGET_M480/PeripheralNames.h +++ b/targets/TARGET_NUVOTON/TARGET_M480/PeripheralNames.h @@ -127,6 +127,8 @@ typedef enum { SPI_5 = (int) NU_MODNAME(QSPI1_BASE, 5, 0), } SPIName; +#define DEVICE_SPI_COUNT 6 + typedef enum { I2C_0 = (int) NU_MODNAME(I2C0_BASE, 0, 0), I2C_1 = (int) NU_MODNAME(I2C1_BASE, 1, 0), diff --git a/targets/TARGET_NUVOTON/TARGET_M480/dma_api.c b/targets/TARGET_NUVOTON/TARGET_M480/dma_api.c index 51f0e686b48..11fd9ad31b1 100644 --- a/targets/TARGET_NUVOTON/TARGET_M480/dma_api.c +++ b/targets/TARGET_NUVOTON/TARGET_M480/dma_api.c @@ -84,6 +84,19 @@ int dma_channel_allocate(uint32_t capabilities) int dma_channel_free(int channelid) { + PDMA_T * pdma = dma_modbase(); + + // Make sure channel is disabled + pdma->CHCTL &= ~(1 << channelid); + + // Also clear the request source for a channel in case still enabled. + // This allows this request source to be assigned to a different channel later. + PDMA_SetTransferMode(pdma, + channelid, + PDMA_MEM, // No peripheral + 0, // Scatter-gather disabled + 0); // Scatter-gather descriptor address + if (channelid != DMA_ERROR_OUT_OF_CHANNELS) { dma_chn_mask &= ~(1 << channelid); } diff --git a/targets/TARGET_NUVOTON/TARGET_M480/objects.h b/targets/TARGET_NUVOTON/TARGET_M480/objects.h index e89de49b3e5..50f2722afc3 100644 --- a/targets/TARGET_NUVOTON/TARGET_M480/objects.h +++ b/targets/TARGET_NUVOTON/TARGET_M480/objects.h @@ -87,11 +87,14 @@ struct spi_s { PinName pin_sclk; PinName pin_ssel; + // Current word size of the SPI + uint8_t word_size_bits; + // Async transfer related fields DMAUsage dma_usage; int dma_chn_id_tx; int dma_chn_id_rx; - uint32_t event; + uint32_t event_mask; uint32_t txrx_rmn; // Track tx/rx frames remaining in interrupt way }; diff --git a/targets/TARGET_NUVOTON/TARGET_M480/spi_api.c b/targets/TARGET_NUVOTON/TARGET_M480/spi_api.c index e3d2b552e11..cf0cc593b63 100644 --- a/targets/TARGET_NUVOTON/TARGET_M480/spi_api.c +++ b/targets/TARGET_NUVOTON/TARGET_M480/spi_api.c @@ -20,6 +20,8 @@ #if DEVICE_SPI +#include + #include "cmsis.h" #include "pinmap.h" #include "PeripheralPins.h" @@ -124,6 +126,65 @@ __STATIC_INLINE void SPI_DISABLE_SYNC(SPI_T *spi_base) while (spi_base->STATUS & SPI_STATUS_SPIENSTS_Msk); } +/// Get the number of bytes of the Tx/Rx buffers that will be used to encode each word of data for the bus +static uint8_t nu_spi_get_bytes_per_word(struct spi_s const * const nu_spi) +{ + if(nu_spi->word_size_bits <= 8) + { + return 1; + } + else if(nu_spi->word_size_bits <= 16) + { + return 2; + } + else + { + return 4; + } +} + +// Set the DMA usage of this SPI instance. +// Allocates or deallocates channels as necessary. +// If no DMA channels are available, sets DMA usage to DMA_USAGE_NEVER +static void nu_spi_set_dma_usage(struct spi_s * const spi, DMAUsage new_dma_usage) +{ + if(new_dma_usage == DMA_USAGE_NEVER) + { + if(spi->dma_usage != DMA_USAGE_NEVER) + { + // Free channels + dma_channel_free(spi->dma_chn_id_tx); + spi->dma_chn_id_tx = DMA_ERROR_OUT_OF_CHANNELS; + dma_channel_free(spi->dma_chn_id_rx); + spi->dma_chn_id_rx = DMA_ERROR_OUT_OF_CHANNELS; + } + } + else + { + // Temporary or permanent DMA usage + if(spi->dma_usage == DMA_USAGE_NEVER) + { + // Need to allocate channels + spi->dma_chn_id_tx = dma_channel_allocate(DMA_CAP_NONE); + if(spi->dma_chn_id_tx == DMA_ERROR_OUT_OF_CHANNELS) + { + new_dma_usage = DMA_USAGE_NEVER; + } + else + { + spi->dma_chn_id_rx = dma_channel_allocate(DMA_CAP_NONE); + if(spi->dma_chn_id_rx == DMA_ERROR_OUT_OF_CHANNELS) + { + new_dma_usage = DMA_USAGE_NEVER; + dma_channel_free(spi->dma_chn_id_tx); + } + } + } + } + + spi->dma_usage = new_dma_usage; +} + #if DEVICE_SPI_ASYNCH static void spi_enable_vector_interrupt(spi_t *obj, uint32_t handler, uint8_t enable); static void spi_master_enable_interrupt(spi_t *obj, uint8_t enable); @@ -132,8 +193,7 @@ static uint32_t spi_master_read_asynch(spi_t *obj); static uint32_t spi_event_check(spi_t *obj); static void spi_enable_event(spi_t *obj, uint32_t event, uint8_t enable); static void spi_buffer_set(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length); -static void spi_check_dma_usage(DMAUsage *dma_usage, int *dma_ch_tx, int *dma_ch_rx); -static uint8_t spi_get_data_width(spi_t *obj); +static void nu_spi_set_dma_usage(struct spi_s * const spi, DMAUsage new_dma_usage); static int spi_is_tx_complete(spi_t *obj); static int spi_is_rx_complete(spi_t *obj); static int spi_writeable(spi_t * obj); @@ -157,6 +217,54 @@ static const struct nu_modinit_s spi_modinit_tab[] = { {NC, 0, 0, 0, 0, (IRQn_Type) 0, NULL} }; +SPIName spi_get_peripheral_name(PinName mosi, PinName miso, PinName sclk) { + SPIName spi_mosi = (SPIName)pinmap_peripheral(mosi, PinMap_SPI_MOSI); + SPIName spi_miso = (SPIName)pinmap_peripheral(miso, PinMap_SPI_MISO); + SPIName spi_sclk = (SPIName)pinmap_peripheral(sclk, PinMap_SPI_SCLK); + + SPIName spi_data = (SPIName)pinmap_merge(spi_mosi, spi_miso); + SPIName spi_per = (SPIName)pinmap_merge(spi_data, spi_sclk); + + return spi_per; +} + +void spi_get_capabilities(PinName ssel, bool slave, spi_capabilities_t *cap) { + if (slave) { + cap->minimum_frequency = 1; + cap->maximum_frequency = 48000000; // Per the datasheet, max slave SCLK freq is 48MHz + cap->word_length = 0xFFFFFF80; // Word lengths 32 bits through 8 bits + cap->support_slave_mode = false; // to be determined later based on ssel + cap->hw_cs_handle = false; // irrelevant in slave mode + cap->slave_delay_between_symbols_ns = 2500; // 2.5 us - TODO update, this is currently not used for anything + cap->clk_modes = 0x0f; // all clock modes + cap->tx_rx_buffers_equal_length = false; // rx/tx buffers can have different sizes + cap->async_mode = false; + } else { + cap->minimum_frequency = 375000; // Slowest clock is PCLK0/1 / 256 + cap->maximum_frequency = 96000000; // With clock divider 1, SCLK = PCLK0/1 clock, which is 96MHz + cap->word_length = 0xFFFFFF80; // Word lengths 32 bits through 8 bits + cap->support_slave_mode = false; // to be determined later based on ssel + cap->hw_cs_handle = false; // to be determined later based on ssel + cap->slave_delay_between_symbols_ns = 0; // irrelevant in master mode + cap->clk_modes = 0x0f; // all clock modes + cap->tx_rx_buffers_equal_length = false; // rx/tx buffers can have different sizes + cap->async_mode = true; + } + + // check if given ssel pin is in the cs pinmap + const PinMap *cs_pins = spi_master_cs_pinmap(); + while (cs_pins->pin != NC) { + if (cs_pins->pin == ssel) { +#if DEVICE_SPISLAVE + cap->support_slave_mode = true; +#endif + cap->hw_cs_handle = true; + break; + } + cs_pins++; + } +} + void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel) { // Determine which SPI_x the pins are used for @@ -193,10 +301,8 @@ void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel SYS_ResetModule(modinit->rsetidx); #if DEVICE_SPI_ASYNCH - obj->spi.dma_usage = DMA_USAGE_NEVER; - obj->spi.event = 0; - obj->spi.dma_chn_id_tx = DMA_ERROR_OUT_OF_CHANNELS; - obj->spi.dma_chn_id_rx = DMA_ERROR_OUT_OF_CHANNELS; + // Note: We don't want to touch the DMA usage here, because either this is a completely new SPI and the DMA usage is already set to 0 (NEVER), + // or it's a re-initialization of an existing SPI and we can allow it to keep its existing DMA settings. /* NOTE: We use vector to judge if asynchronous transfer is on-going (spi_active). * At initial time, asynchronous transfer is not on-going and so vector must @@ -212,14 +318,8 @@ void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel void spi_free(spi_t *obj) { #if DEVICE_SPI_ASYNCH - if (obj->spi.dma_chn_id_tx != DMA_ERROR_OUT_OF_CHANNELS) { - dma_channel_free(obj->spi.dma_chn_id_tx); - obj->spi.dma_chn_id_tx = DMA_ERROR_OUT_OF_CHANNELS; - } - if (obj->spi.dma_chn_id_rx != DMA_ERROR_OUT_OF_CHANNELS) { - dma_channel_free(obj->spi.dma_chn_id_rx); - obj->spi.dma_chn_id_rx = DMA_ERROR_OUT_OF_CHANNELS; - } + // Free DMA channels + nu_spi_set_dma_usage(&obj->spi, DMA_USAGE_NEVER); #endif if (spi_is_qspi(obj)) { @@ -261,6 +361,8 @@ void spi_format(spi_t *obj, int bits, int mode, int slave) SPI_DISABLE_SYNC(spi_base); + obj->spi.word_size_bits = bits; + if (spi_is_qspi(obj)) { QSPI_Open((QSPI_T *) spi_base, slave ? QSPI_SLAVE : QSPI_MASTER, @@ -351,17 +453,34 @@ int spi_master_write(spi_t *obj, int value) int spi_master_block_write(spi_t *obj, const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length, char write_fill) { - int total = (tx_length > rx_length) ? tx_length : rx_length; - for (int i = 0; i < total; i++) { - char out = (i < tx_length) ? tx_buffer[i] : write_fill; - char in = spi_master_write(obj, out); - if (i < rx_length) { - rx_buffer[i] = in; + // Length is passed in bytes so we need to convert to words + const uint8_t word_size_bytes = nu_spi_get_bytes_per_word(&obj->spi); + MBED_ASSERT(tx_length % word_size_bytes == 0); + MBED_ASSERT(rx_length % word_size_bytes == 0); + + const int tx_words = tx_length / word_size_bytes; + const int rx_words = rx_length / word_size_bytes; + const int total_words = (tx_words > rx_words) ? tx_words : rx_words; + + for (int word_idx = 0; word_idx < total_words; word_idx++) { + + int out = 0; + if(word_idx >= tx_length){ + // Use fill char + memset(&out, write_fill, word_size_bytes); + } + else{ + memcpy(&out, tx_buffer + (word_idx * word_size_bytes), word_size_bytes); + } + + int in = spi_master_write(obj, out); + if (word_idx < rx_words) { + memcpy(rx_buffer + (word_idx * word_size_bytes), &in, word_size_bytes); } } - return total; + return total_words * word_size_bytes; } const PinMap *spi_master_mosi_pinmap() @@ -442,24 +561,29 @@ void spi_slave_write(spi_t *obj, int value) bool 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) { SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); - SPI_SET_DATA_WIDTH(spi_base, bit_width); - obj->spi.dma_usage = hint; - spi_check_dma_usage(&obj->spi.dma_usage, &obj->spi.dma_chn_id_tx, &obj->spi.dma_chn_id_rx); - uint32_t data_width = spi_get_data_width(obj); + // Make sure Tx and Rx lengths are sane + const uint8_t word_size_bytes = nu_spi_get_bytes_per_word(&obj->spi); + MBED_ASSERT(tx_length % word_size_bytes == 0); + MBED_ASSERT(rx_length % word_size_bytes == 0); + // Conditions to go DMA way: // (1) No DMA support for non-8 multiple data width. // (2) tx length >= rx length. Otherwise, as tx DMA is done, no bus activity for remaining rx. - if ((data_width % 8) || + if (((obj->spi.word_size_bits % 8) != 0) || (tx_length < rx_length)) { - obj->spi.dma_usage = DMA_USAGE_NEVER; - dma_channel_free(obj->spi.dma_chn_id_tx); - obj->spi.dma_chn_id_tx = DMA_ERROR_OUT_OF_CHANNELS; - dma_channel_free(obj->spi.dma_chn_id_rx); - obj->spi.dma_chn_id_rx = DMA_ERROR_OUT_OF_CHANNELS; + hint = DMA_USAGE_NEVER; } - // SPI IRQ is necessary for both interrupt way and DMA way + // Set DMA usage, allocating or releasing DMA channels + nu_spi_set_dma_usage(&obj->spi, hint); + + // SPI IRQ is necessary for both interrupt way and DMA way. + // However, if we are using DMA then overflows can happen if Tx length > Rx length, so ignore them + if(obj->spi.dma_usage != DMA_USAGE_NEVER) + { + event &= ~(SPI_EVENT_RX_OVERFLOW); + } spi_enable_event(obj, event, 1); spi_buffer_set(obj, tx, tx_length, rx, rx_length); @@ -490,8 +614,8 @@ bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, 0); // Scatter-gather descriptor address PDMA_SetTransferCnt(pdma_base, obj->spi.dma_chn_id_tx, - (data_width == 8) ? PDMA_WIDTH_8 : (data_width == 16) ? PDMA_WIDTH_16 : PDMA_WIDTH_32, - tx_length); + (word_size_bytes == 1) ? PDMA_WIDTH_8 : (word_size_bytes == 2) ? PDMA_WIDTH_16 : PDMA_WIDTH_32, + tx_length / word_size_bytes); PDMA_SetTransferAddr(pdma_base, obj->spi.dma_chn_id_tx, (uint32_t) tx, // NOTE: @@ -519,8 +643,8 @@ bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, 0); // Scatter-gather descriptor address PDMA_SetTransferCnt(pdma_base, obj->spi.dma_chn_id_rx, - (data_width == 8) ? PDMA_WIDTH_8 : (data_width == 16) ? PDMA_WIDTH_16 : PDMA_WIDTH_32, - rx_length); + (word_size_bytes == 1) ? PDMA_WIDTH_8 : (word_size_bytes == 2) ? PDMA_WIDTH_16 : PDMA_WIDTH_32, + rx_length / word_size_bytes); PDMA_SetTransferAddr(pdma_base, obj->spi.dma_chn_id_rx, (uint32_t) &spi_base->RX, // Source address @@ -601,6 +725,12 @@ void spi_abort_asynch(spi_t *obj) pdma_base->CHCTL &= ~(1 << obj->spi.dma_chn_id_rx); } SPI_DISABLE_RX_PDMA(((SPI_T *) NU_MODBASE(obj->spi.spi))); + + // If DMA was temporary, free its channels + if(obj->spi.dma_usage == DMA_USAGE_TEMPORARY_ALLOCATED || obj->spi.dma_usage == DMA_USAGE_OPPORTUNISTIC) + { + nu_spi_set_dma_usage(&obj->spi, DMA_USAGE_NEVER); + } } // Necessary for both interrupt way and DMA way @@ -612,6 +742,21 @@ void spi_abort_asynch(spi_t *obj) SPI_ClearRxFIFO(spi_base); SPI_ClearTxFIFO(spi_base); + + // Clear any events which may have been triggered by the transfer or the abort + if (spi_base->STATUS & SPI_STATUS_RXOVIF_Msk) { + spi_base->STATUS = SPI_STATUS_RXOVIF_Msk; + } + + // Receive Time-Out + if (spi_base->STATUS & SPI_STATUS_RXTOIF_Msk) { + spi_base->STATUS = SPI_STATUS_RXTOIF_Msk; + } + + // Transmit FIFO Under-Run + if (spi_base->STATUS & SPI_STATUS_TXUFIF_Msk) { + spi_base->STATUS = SPI_STATUS_TXUFIF_Msk; + } } /** @@ -629,7 +774,7 @@ uint32_t spi_irq_handler_asynch(spi_t *obj) spi_abort_asynch(obj); } - return (obj->spi.event & event) | ((event & SPI_EVENT_COMPLETE) ? SPI_EVENT_INTERNAL_TRANSFER_COMPLETE : 0); + return (obj->spi.event_mask & event) | ((event & SPI_EVENT_COMPLETE) ? SPI_EVENT_INTERNAL_TRANSFER_COMPLETE : 0); } uint8_t spi_active(spi_t *obj) @@ -664,8 +809,8 @@ static int spi_readable(spi_t * obj) static void spi_enable_event(spi_t *obj, uint32_t event, uint8_t enable) { - obj->spi.event &= ~SPI_EVENT_ALL; - obj->spi.event |= (event & SPI_EVENT_ALL); + obj->spi.event_mask &= ~SPI_EVENT_ALL; + obj->spi.event_mask |= (event & SPI_EVENT_ALL); if (event & SPI_EVENT_RX_OVERFLOW) { SPI_EnableInt((SPI_T *) NU_MODBASE(obj->spi.spi), SPI_FIFO_RXOV_INT_MASK); } @@ -716,21 +861,11 @@ static uint32_t spi_event_check(spi_t *obj) // Receive FIFO Overrun if (spi_base->STATUS & SPI_STATUS_RXOVIF_Msk) { - spi_base->STATUS = SPI_STATUS_RXOVIF_Msk; - // In case of tx length > rx length on DMA way - if (obj->spi.dma_usage == DMA_USAGE_NEVER) { - event |= SPI_EVENT_RX_OVERFLOW; - } + event |= SPI_EVENT_RX_OVERFLOW; } - // Receive Time-Out - if (spi_base->STATUS & SPI_STATUS_RXTOIF_Msk) { - spi_base->STATUS = SPI_STATUS_RXTOIF_Msk; - // Not using this IF. Just clear it. - } // Transmit FIFO Under-Run if (spi_base->STATUS & SPI_STATUS_TXUFIF_Msk) { - spi_base->STATUS = SPI_STATUS_TXUFIF_Msk; event |= SPI_EVENT_ERROR; } @@ -747,9 +882,7 @@ static uint32_t spi_event_check(spi_t *obj) static uint32_t spi_master_write_asynch(spi_t *obj, uint32_t tx_limit) { uint32_t n_words = 0; - uint8_t data_width = spi_get_data_width(obj); - uint8_t bytes_per_word = (data_width + 7) / 8; - uint8_t *tx = (uint8_t *)(obj->tx_buff.buffer) + bytes_per_word * obj->tx_buff.pos; + const uint8_t word_size_bytes = nu_spi_get_bytes_per_word(&obj->spi); SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); while (obj->spi.txrx_rmn && spi_writeable(obj)) { @@ -757,25 +890,23 @@ static uint32_t spi_master_write_asynch(spi_t *obj, uint32_t tx_limit) // Transmit dummy as transmit buffer is empty SPI_WRITE_TX(spi_base, 0); } else { - switch (bytes_per_word) { + uint8_t *tx = (uint8_t *)(obj->tx_buff.buffer) + obj->tx_buff.pos; + switch (word_size_bytes) { case 4: SPI_WRITE_TX(spi_base, nu_get32_le(tx)); - tx += 4; break; case 2: SPI_WRITE_TX(spi_base, nu_get16_le(tx)); - tx += 2; break; case 1: - SPI_WRITE_TX(spi_base, *((uint8_t *) tx)); - tx += 1; + SPI_WRITE_TX(spi_base, *tx); break; } - obj->tx_buff.pos ++; + obj->tx_buff.pos += word_size_bytes; } n_words ++; - obj->spi.txrx_rmn --; + obj->spi.txrx_rmn -= word_size_bytes; } //Return the number of words that have been sent @@ -796,9 +927,8 @@ static uint32_t spi_master_write_asynch(spi_t *obj, uint32_t tx_limit) static uint32_t spi_master_read_asynch(spi_t *obj) { uint32_t n_words = 0; - uint8_t data_width = spi_get_data_width(obj); - uint8_t bytes_per_word = (data_width + 7) / 8; - uint8_t *rx = (uint8_t *)(obj->rx_buff.buffer) + bytes_per_word * obj->rx_buff.pos; + const uint8_t word_size_bytes = nu_spi_get_bytes_per_word(&obj->spi); + SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); while (spi_readable(obj)) { @@ -806,25 +936,24 @@ static uint32_t spi_master_read_asynch(spi_t *obj) // Disregard as receive buffer is full SPI_READ_RX(spi_base); } else { - switch (bytes_per_word) { + uint8_t *rx = (uint8_t *)(obj->rx_buff.buffer) + obj->rx_buff.pos; + switch (word_size_bytes) { case 4: { uint32_t val = SPI_READ_RX(spi_base); nu_set32_le(rx, val); - rx += 4; break; } case 2: { uint16_t val = SPI_READ_RX(spi_base); nu_set16_le(rx, val); - rx += 2; break; } case 1: - *rx ++ = SPI_READ_RX(spi_base); + *rx = SPI_READ_RX(spi_base); break; } - obj->rx_buff.pos ++; + obj->rx_buff.pos += word_size_bytes; } n_words ++; } @@ -838,46 +967,11 @@ static void spi_buffer_set(spi_t *obj, const void *tx, size_t tx_length, void *r obj->tx_buff.buffer = (void *) tx; obj->tx_buff.length = tx_length; obj->tx_buff.pos = 0; - obj->tx_buff.width = spi_get_data_width(obj); + obj->tx_buff.width = nu_spi_get_bytes_per_word(&obj->spi) * 8; obj->rx_buff.buffer = rx; obj->rx_buff.length = rx_length; obj->rx_buff.pos = 0; - obj->rx_buff.width = spi_get_data_width(obj); -} - -static void spi_check_dma_usage(DMAUsage *dma_usage, int *dma_ch_tx, int *dma_ch_rx) -{ - if (*dma_usage != DMA_USAGE_NEVER) { - if (*dma_ch_tx == DMA_ERROR_OUT_OF_CHANNELS) { - *dma_ch_tx = dma_channel_allocate(DMA_CAP_NONE); - } - if (*dma_ch_rx == DMA_ERROR_OUT_OF_CHANNELS) { - *dma_ch_rx = dma_channel_allocate(DMA_CAP_NONE); - } - - if (*dma_ch_tx == DMA_ERROR_OUT_OF_CHANNELS || *dma_ch_rx == DMA_ERROR_OUT_OF_CHANNELS) { - *dma_usage = DMA_USAGE_NEVER; - } - } - - if (*dma_usage == DMA_USAGE_NEVER) { - dma_channel_free(*dma_ch_tx); - *dma_ch_tx = DMA_ERROR_OUT_OF_CHANNELS; - dma_channel_free(*dma_ch_rx); - *dma_ch_rx = DMA_ERROR_OUT_OF_CHANNELS; - } -} - -static uint8_t spi_get_data_width(spi_t *obj) -{ - SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi); - - uint32_t data_width = ((spi_base->CTL & SPI_CTL_DWIDTH_Msk) >> SPI_CTL_DWIDTH_Pos); - if (data_width == 0) { - data_width = 32; - } - - return data_width; + obj->rx_buff.width = nu_spi_get_bytes_per_word(&obj->spi) * 8; } static int spi_is_tx_complete(spi_t *obj) @@ -910,6 +1004,7 @@ static void spi_dma_handler_tx(uint32_t id, uint32_t event_dma) MBED_ASSERT(modinit->modname == (int) obj->spi.spi); void (*vec)(void) = (void (*)(void)) NVIC_GetVector(modinit->irq_n); + MBED_ASSERT(vec != NULL); vec(); } @@ -933,6 +1028,7 @@ static void spi_dma_handler_rx(uint32_t id, uint32_t event_dma) MBED_ASSERT(modinit->modname == (int) obj->spi.spi); void (*vec)(void) = (void (*)(void)) NVIC_GetVector(modinit->irq_n); + MBED_ASSERT(vec != NULL); vec(); } @@ -948,7 +1044,7 @@ static uint32_t spi_fifo_depth(spi_t *obj) return 8; } - return (spi_get_data_width(obj) <= 16) ? 8 : 4; + return (obj->spi.word_size_bits <= 16) ? 8 : 4; } #endif diff --git a/targets/TARGET_NUVOTON/nu_bitutil.h b/targets/TARGET_NUVOTON/nu_bitutil.h index 270ab4924ea..afd68efae3e 100644 --- a/targets/TARGET_NUVOTON/nu_bitutil.h +++ b/targets/TARGET_NUVOTON/nu_bitutil.h @@ -34,12 +34,14 @@ __STATIC_INLINE int nu_clo(uint32_t x) return nu_clz(~x); } +// Count Trailing Zeroes in an integer __STATIC_INLINE int nu_ctz(uint32_t x) { int c = __CLZ(x & -x); return x ? 31 - c : c; } +// Count Trailing Ones in an integer __STATIC_INLINE int nu_cto(uint32_t x) { return nu_ctz(~x);