Skip to content

Commit 40a020a

Browse files
Nuvoton M480: Some SPI fixes using test shield (#467)
* FIx M480 word size issues, implement extra functionality * Fix spi_init() not freeing DMA channels, causing DMA to get stuck * Fix spi_abort_async leaving flags set that will cause the next transfer to fail * Fix incorrect spi active logic, add assert to catch timing issue * Fix comment * Style
1 parent 542309b commit 40a020a

File tree

9 files changed

+240
-113
lines changed

9 files changed

+240
-113
lines changed

drivers/include/drivers/SPI.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,12 @@ const use_gpio_ssel_t use_gpio_ssel;
179179
* applies to receiving data, so be sure to check examples in the datasheet to determine what frame
180180
* size to use and whether byte swapping is needed when working with an external chip.</p>
181181
*
182-
* <p>Note: Some Mbed targets support frame sizes that are not standard integer sizes, e.g. 4 bits, 7 bits, or
183-
* 24 bits. However, the behavior of these frame sizes is not well defined and may differ across targets
184-
* or across API calls (e.g. single-byte vs transaction vs async). More work is needed to make these consistent.</p>
182+
* \note Some Mbed targets support frame sizes that are not standard integer sizes, e.g. 4 bits, 7 bits, or
183+
* 24 bits. However, the behavior of these frame sizes is currently not tested, and you may need to test what works or
184+
* inspect your target's SPI driver code. More work is needed to make these consistent. When available, using these frame sizes
185+
* requires writing each word to be transmitted into the next-largest C integer type. For example, to send 24-bit frames, you
186+
* would create an array of uint32_ts and write each 24-bit integer into its own uint32_t.
187+
* Then you would pass in the byte length of the array (number of frames * 4, NOT number of frames * 3) to the SPI driver.
185188
*
186189
* <h1>Asynchronous API</h1>
187190
* <p>On many processors, Mbed OS also supports asynchronous %SPI. This feature allows you to run %SPI

drivers/source/SPI.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ void SPI::abort_all_transfers()
427427

428428
int SPI::set_dma_usage(DMAUsage usage)
429429
{
430-
if (spi_active(&_peripheral->spi)) {
430+
if (_peripheral->initialized && spi_active(&_peripheral->spi)) {
431431
return -1;
432432
}
433433
_usage = usage;

hal/include/hal/dma_api.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
* @brief Enumeration of possible DMA usage hints
3131
*/
3232
typedef enum {
33-
DMA_USAGE_NEVER, ///< Never use DMA
33+
DMA_USAGE_NEVER = 0, ///< Never use DMA
3434
DMA_USAGE_OPPORTUNISTIC, ///< Use DMA if possible but deallocate DMA resources when not being used.
3535
DMA_USAGE_ALWAYS, ///< Always use DMA when possible
3636
DMA_USAGE_TEMPORARY_ALLOCATED, // Seems to be used as an internal state indicator for "we need to deallocate these channels."

hal/include/hal/spi_api.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ SPIName spi_get_peripheral_name(PinName mosi, PinName miso, PinName mclk);
192192

193193
/**
194194
* Fills the given spi_capabilities_t structure with the capabilities of the given peripheral.
195+
*
196+
* @param ssel The CS pin being used, for checking the \c hw_cs_handle flag
197+
* @param slave True to get capabilities for slave mode, false to get capabilities for master mode
198+
* @param[out] cap Capabilities are returned here
195199
*/
196200
void spi_get_capabilities(PinName ssel, bool slave, spi_capabilities_t *cap);
197201

@@ -419,7 +423,7 @@ const PinMap *spi_slave_cs_pinmap(void);
419423
* @param[in] rx_length The number of bytes to receive
420424
* @param[in] bit_width The bit width of buffer words
421425
* @param[in] event The logical OR of events to be registered
422-
* @param[in] handler SPI interrupt handler
426+
* @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
423427
* @param[in] hint A suggestion for how to use DMA with this transfer
424428
*
425429
* @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);
429433
* after the transfer is complete. If this function returns true, the driver layer will cache invalidate the Rx buffer under
430434
* the assumption that the data needs to be re-read from main memory. Be careful, because if the read was not actually
431435
* done by DMA, and the rx data is in the CPU cache, this invalidation will corrupt it.
436+
*
437+
* @note The application layer will always acquire the SPI peripheral first before calling this, including setting the frequency and the bit width. So,
438+
* the \c bit_width argument will never be different from the SPI's currently set bit width, and can actually be ignored.
439+
* TODO remove this argument entirely.
432440
*/
433441
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);
434442

targets/TARGET_NUVOTON/TARGET_M480/PeripheralNames.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ typedef enum {
127127
SPI_5 = (int) NU_MODNAME(QSPI1_BASE, 5, 0),
128128
} SPIName;
129129

130+
#define DEVICE_SPI_COUNT 6
131+
130132
typedef enum {
131133
I2C_0 = (int) NU_MODNAME(I2C0_BASE, 0, 0),
132134
I2C_1 = (int) NU_MODNAME(I2C1_BASE, 1, 0),

targets/TARGET_NUVOTON/TARGET_M480/dma_api.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,19 @@ int dma_channel_allocate(uint32_t capabilities)
8484

8585
int dma_channel_free(int channelid)
8686
{
87+
PDMA_T * pdma = dma_modbase();
88+
89+
// Make sure channel is disabled
90+
pdma->CHCTL &= ~(1 << channelid);
91+
92+
// Also clear the request source for a channel in case still enabled.
93+
// This allows this request source to be assigned to a different channel later.
94+
PDMA_SetTransferMode(pdma,
95+
channelid,
96+
PDMA_MEM, // No peripheral
97+
0, // Scatter-gather disabled
98+
0); // Scatter-gather descriptor address
99+
87100
if (channelid != DMA_ERROR_OUT_OF_CHANNELS) {
88101
dma_chn_mask &= ~(1 << channelid);
89102
}

targets/TARGET_NUVOTON/TARGET_M480/objects.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,14 @@ struct spi_s {
8787
PinName pin_sclk;
8888
PinName pin_ssel;
8989

90+
// Current word size of the SPI
91+
uint8_t word_size_bits;
92+
9093
// Async transfer related fields
9194
DMAUsage dma_usage;
9295
int dma_chn_id_tx;
9396
int dma_chn_id_rx;
94-
uint32_t event;
97+
uint32_t event_mask;
9598
uint32_t txrx_rmn; // Track tx/rx frames remaining in interrupt way
9699
};
97100

0 commit comments

Comments
 (0)