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);