Skip to content

Commit 0db79fc

Browse files
FIx M480 word size issues, implement extra functionality
1 parent 6702976 commit 0db79fc

File tree

5 files changed

+138
-52
lines changed

5 files changed

+138
-52
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

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/objects.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ 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;

targets/TARGET_NUVOTON/TARGET_M480/spi_api.c

Lines changed: 118 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
#if DEVICE_SPI
2222

23+
#include <string.h>
24+
2325
#include "cmsis.h"
2426
#include "pinmap.h"
2527
#include "PeripheralPins.h"
@@ -124,6 +126,23 @@ __STATIC_INLINE void SPI_DISABLE_SYNC(SPI_T *spi_base)
124126
while (spi_base->STATUS & SPI_STATUS_SPIENSTS_Msk);
125127
}
126128

129+
/// Get the number of bytes of the Tx/Rx buffers that will be used to encode each word of data for the bus
130+
static uint8_t nu_spi_get_bytes_per_word(struct spi_s const * const nu_spi)
131+
{
132+
if(nu_spi->word_size_bits <= 8)
133+
{
134+
return 1;
135+
}
136+
else if(nu_spi->word_size_bits <= 16)
137+
{
138+
return 2;
139+
}
140+
else
141+
{
142+
return 4;
143+
}
144+
}
145+
127146
#if DEVICE_SPI_ASYNCH
128147
static void spi_enable_vector_interrupt(spi_t *obj, uint32_t handler, uint8_t enable);
129148
static void spi_master_enable_interrupt(spi_t *obj, uint8_t enable);
@@ -133,7 +152,6 @@ static uint32_t spi_event_check(spi_t *obj);
133152
static void spi_enable_event(spi_t *obj, uint32_t event, uint8_t enable);
134153
static void spi_buffer_set(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length);
135154
static void spi_check_dma_usage(DMAUsage *dma_usage, int *dma_ch_tx, int *dma_ch_rx);
136-
static uint8_t spi_get_data_width(spi_t *obj);
137155
static int spi_is_tx_complete(spi_t *obj);
138156
static int spi_is_rx_complete(spi_t *obj);
139157
static int spi_writeable(spi_t * obj);
@@ -157,6 +175,54 @@ static const struct nu_modinit_s spi_modinit_tab[] = {
157175
{NC, 0, 0, 0, 0, (IRQn_Type) 0, NULL}
158176
};
159177

178+
SPIName spi_get_peripheral_name(PinName mosi, PinName miso, PinName sclk) {
179+
SPIName spi_mosi = (SPIName)pinmap_peripheral(mosi, PinMap_SPI_MOSI);
180+
SPIName spi_miso = (SPIName)pinmap_peripheral(miso, PinMap_SPI_MISO);
181+
SPIName spi_sclk = (SPIName)pinmap_peripheral(sclk, PinMap_SPI_SCLK);
182+
183+
SPIName spi_data = (SPIName)pinmap_merge(spi_mosi, spi_miso);
184+
SPIName spi_per = (SPIName)pinmap_merge(spi_data, spi_sclk);
185+
186+
return spi_per;
187+
}
188+
189+
void spi_get_capabilities(PinName ssel, bool slave, spi_capabilities_t *cap) {
190+
if (slave) {
191+
cap->minimum_frequency = 1;
192+
cap->maximum_frequency = 48000000; // Per the datasheet, max slave SCLK freq is 48MHz
193+
cap->word_length = 0xFFFFFF80; // Word lengths 32 bits through 8 bits
194+
cap->support_slave_mode = false; // to be determined later based on ssel
195+
cap->hw_cs_handle = false; // irrelevant in slave mode
196+
cap->slave_delay_between_symbols_ns = 2500; // 2.5 us - TODO update, this is currently not used for anything
197+
cap->clk_modes = 0x0f; // all clock modes
198+
cap->tx_rx_buffers_equal_length = false; // rx/tx buffers can have different sizes
199+
cap->async_mode = false;
200+
} else {
201+
cap->minimum_frequency = 375000; // Slowest clock is PCLK0/1 / 256
202+
cap->maximum_frequency = 96000000; // With clock divider 1, SCLK = PCLK0/1 clock, which is 96MHz
203+
cap->word_length = 0xFFFFFF80; // Word lengths 32 bits through 8 bits
204+
cap->support_slave_mode = false; // to be determined later based on ssel
205+
cap->hw_cs_handle = false; // to be determined later based on ssel
206+
cap->slave_delay_between_symbols_ns = 0; // irrelevant in master mode
207+
cap->clk_modes = 0x0f; // all clock modes
208+
cap->tx_rx_buffers_equal_length = false; // rx/tx buffers can have different sizes
209+
cap->async_mode = true;
210+
}
211+
212+
// check if given ssel pin is in the cs pinmap
213+
const PinMap *cs_pins = spi_master_cs_pinmap();
214+
while (cs_pins->pin != NC) {
215+
if (cs_pins->pin == ssel) {
216+
#if DEVICE_SPISLAVE
217+
cap->support_slave_mode = true;
218+
#endif
219+
cap->hw_cs_handle = true;
220+
break;
221+
}
222+
cs_pins++;
223+
}
224+
}
225+
160226
void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel)
161227
{
162228
// Determine which SPI_x the pins are used for
@@ -261,6 +327,8 @@ void spi_format(spi_t *obj, int bits, int mode, int slave)
261327

262328
SPI_DISABLE_SYNC(spi_base);
263329

330+
obj->spi.word_size_bits = bits;
331+
264332
if (spi_is_qspi(obj)) {
265333
QSPI_Open((QSPI_T *) spi_base,
266334
slave ? QSPI_SLAVE : QSPI_MASTER,
@@ -351,17 +419,34 @@ int spi_master_write(spi_t *obj, int value)
351419

352420
int spi_master_block_write(spi_t *obj, const char *tx_buffer, int tx_length,
353421
char *rx_buffer, int rx_length, char write_fill) {
354-
int total = (tx_length > rx_length) ? tx_length : rx_length;
355422

356-
for (int i = 0; i < total; i++) {
357-
char out = (i < tx_length) ? tx_buffer[i] : write_fill;
358-
char in = spi_master_write(obj, out);
359-
if (i < rx_length) {
360-
rx_buffer[i] = in;
423+
// Length is passed in bytes so we need to convert to words
424+
const uint8_t word_size_bytes = nu_spi_get_bytes_per_word(&obj->spi);
425+
MBED_ASSERT(tx_length % word_size_bytes == 0);
426+
MBED_ASSERT(rx_length % word_size_bytes == 0);
427+
428+
const int tx_words = tx_length / word_size_bytes;
429+
const int rx_words = rx_length / word_size_bytes;
430+
const int total_words = (tx_words > rx_words) ? tx_words : rx_words;
431+
432+
for (int word_idx = 0; word_idx < total_words; word_idx++) {
433+
434+
int out = 0;
435+
if(word_idx >= tx_length){
436+
// Use fill char
437+
memset(&out, write_fill, word_size_bytes);
438+
}
439+
else{
440+
memcpy(&out, tx_buffer + (word_idx * word_size_bytes), word_size_bytes);
441+
}
442+
443+
int in = spi_master_write(obj, out);
444+
if (word_idx < rx_words) {
445+
memcpy(rx_buffer + (word_idx * word_size_bytes), &in, word_size_bytes);
361446
}
362447
}
363448

364-
return total;
449+
return total_words * word_size_bytes;
365450
}
366451

367452
const PinMap *spi_master_mosi_pinmap()
@@ -442,15 +527,18 @@ void spi_slave_write(spi_t *obj, int value)
442527
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)
443528
{
444529
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
445-
SPI_SET_DATA_WIDTH(spi_base, bit_width);
530+
531+
// Make sure Tx and Rx lengths are sane
532+
const uint8_t word_size_bytes = nu_spi_get_bytes_per_word(&obj->spi);
533+
MBED_ASSERT(tx_length % word_size_bytes == 0);
534+
MBED_ASSERT(rx_length % word_size_bytes == 0);
446535

447536
obj->spi.dma_usage = hint;
448537
spi_check_dma_usage(&obj->spi.dma_usage, &obj->spi.dma_chn_id_tx, &obj->spi.dma_chn_id_rx);
449-
uint32_t data_width = spi_get_data_width(obj);
450538
// Conditions to go DMA way:
451539
// (1) No DMA support for non-8 multiple data width.
452540
// (2) tx length >= rx length. Otherwise, as tx DMA is done, no bus activity for remaining rx.
453-
if ((data_width % 8) ||
541+
if (((obj->spi.word_size_bits % 8) != 0) ||
454542
(tx_length < rx_length)) {
455543
obj->spi.dma_usage = DMA_USAGE_NEVER;
456544
dma_channel_free(obj->spi.dma_chn_id_tx);
@@ -490,8 +578,8 @@ bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
490578
0); // Scatter-gather descriptor address
491579
PDMA_SetTransferCnt(pdma_base,
492580
obj->spi.dma_chn_id_tx,
493-
(data_width == 8) ? PDMA_WIDTH_8 : (data_width == 16) ? PDMA_WIDTH_16 : PDMA_WIDTH_32,
494-
tx_length);
581+
(word_size_bytes == 1) ? PDMA_WIDTH_8 : (word_size_bytes == 2) ? PDMA_WIDTH_16 : PDMA_WIDTH_32,
582+
tx_length / word_size_bytes);
495583
PDMA_SetTransferAddr(pdma_base,
496584
obj->spi.dma_chn_id_tx,
497585
(uint32_t) tx, // NOTE:
@@ -519,8 +607,8 @@ bool spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx,
519607
0); // Scatter-gather descriptor address
520608
PDMA_SetTransferCnt(pdma_base,
521609
obj->spi.dma_chn_id_rx,
522-
(data_width == 8) ? PDMA_WIDTH_8 : (data_width == 16) ? PDMA_WIDTH_16 : PDMA_WIDTH_32,
523-
rx_length);
610+
(word_size_bytes == 1) ? PDMA_WIDTH_8 : (word_size_bytes == 2) ? PDMA_WIDTH_16 : PDMA_WIDTH_32,
611+
rx_length / word_size_bytes);
524612
PDMA_SetTransferAddr(pdma_base,
525613
obj->spi.dma_chn_id_rx,
526614
(uint32_t) &spi_base->RX, // Source address
@@ -747,35 +835,31 @@ static uint32_t spi_event_check(spi_t *obj)
747835
static uint32_t spi_master_write_asynch(spi_t *obj, uint32_t tx_limit)
748836
{
749837
uint32_t n_words = 0;
750-
uint8_t data_width = spi_get_data_width(obj);
751-
uint8_t bytes_per_word = (data_width + 7) / 8;
752-
uint8_t *tx = (uint8_t *)(obj->tx_buff.buffer) + bytes_per_word * obj->tx_buff.pos;
838+
const uint8_t word_size_bytes = nu_spi_get_bytes_per_word(&obj->spi);
753839
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
754840

755841
while (obj->spi.txrx_rmn && spi_writeable(obj)) {
756842
if (spi_is_tx_complete(obj)) {
757843
// Transmit dummy as transmit buffer is empty
758844
SPI_WRITE_TX(spi_base, 0);
759845
} else {
760-
switch (bytes_per_word) {
846+
uint8_t *tx = (uint8_t *)(obj->tx_buff.buffer) + obj->tx_buff.pos;
847+
switch (word_size_bytes) {
761848
case 4:
762849
SPI_WRITE_TX(spi_base, nu_get32_le(tx));
763-
tx += 4;
764850
break;
765851
case 2:
766852
SPI_WRITE_TX(spi_base, nu_get16_le(tx));
767-
tx += 2;
768853
break;
769854
case 1:
770-
SPI_WRITE_TX(spi_base, *((uint8_t *) tx));
771-
tx += 1;
855+
SPI_WRITE_TX(spi_base, *tx);
772856
break;
773857
}
774858

775-
obj->tx_buff.pos ++;
859+
obj->tx_buff.pos += word_size_bytes;
776860
}
777861
n_words ++;
778-
obj->spi.txrx_rmn --;
862+
obj->spi.txrx_rmn -= word_size_bytes;
779863
}
780864

781865
//Return the number of words that have been sent
@@ -796,35 +880,33 @@ static uint32_t spi_master_write_asynch(spi_t *obj, uint32_t tx_limit)
796880
static uint32_t spi_master_read_asynch(spi_t *obj)
797881
{
798882
uint32_t n_words = 0;
799-
uint8_t data_width = spi_get_data_width(obj);
800-
uint8_t bytes_per_word = (data_width + 7) / 8;
801-
uint8_t *rx = (uint8_t *)(obj->rx_buff.buffer) + bytes_per_word * obj->rx_buff.pos;
883+
const uint8_t word_size_bytes = nu_spi_get_bytes_per_word(&obj->spi);
884+
802885
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
803886

804887
while (spi_readable(obj)) {
805888
if (spi_is_rx_complete(obj)) {
806889
// Disregard as receive buffer is full
807890
SPI_READ_RX(spi_base);
808891
} else {
809-
switch (bytes_per_word) {
892+
uint8_t *rx = (uint8_t *)(obj->rx_buff.buffer) + obj->rx_buff.pos;
893+
switch (word_size_bytes) {
810894
case 4: {
811895
uint32_t val = SPI_READ_RX(spi_base);
812896
nu_set32_le(rx, val);
813-
rx += 4;
814897
break;
815898
}
816899
case 2: {
817900
uint16_t val = SPI_READ_RX(spi_base);
818901
nu_set16_le(rx, val);
819-
rx += 2;
820902
break;
821903
}
822904
case 1:
823-
*rx ++ = SPI_READ_RX(spi_base);
905+
*rx = SPI_READ_RX(spi_base);
824906
break;
825907
}
826908

827-
obj->rx_buff.pos ++;
909+
obj->rx_buff.pos += word_size_bytes;
828910
}
829911
n_words ++;
830912
}
@@ -838,11 +920,11 @@ static void spi_buffer_set(spi_t *obj, const void *tx, size_t tx_length, void *r
838920
obj->tx_buff.buffer = (void *) tx;
839921
obj->tx_buff.length = tx_length;
840922
obj->tx_buff.pos = 0;
841-
obj->tx_buff.width = spi_get_data_width(obj);
923+
obj->tx_buff.width = nu_spi_get_bytes_per_word(&obj->spi) * 8;
842924
obj->rx_buff.buffer = rx;
843925
obj->rx_buff.length = rx_length;
844926
obj->rx_buff.pos = 0;
845-
obj->rx_buff.width = spi_get_data_width(obj);
927+
obj->rx_buff.width = nu_spi_get_bytes_per_word(&obj->spi) * 8;
846928
}
847929

848930
static void spi_check_dma_usage(DMAUsage *dma_usage, int *dma_ch_tx, int *dma_ch_rx)
@@ -868,18 +950,6 @@ static void spi_check_dma_usage(DMAUsage *dma_usage, int *dma_ch_tx, int *dma_ch
868950
}
869951
}
870952

871-
static uint8_t spi_get_data_width(spi_t *obj)
872-
{
873-
SPI_T *spi_base = (SPI_T *) NU_MODBASE(obj->spi.spi);
874-
875-
uint32_t data_width = ((spi_base->CTL & SPI_CTL_DWIDTH_Msk) >> SPI_CTL_DWIDTH_Pos);
876-
if (data_width == 0) {
877-
data_width = 32;
878-
}
879-
880-
return data_width;
881-
}
882-
883953
static int spi_is_tx_complete(spi_t *obj)
884954
{
885955
return (obj->tx_buff.pos == obj->tx_buff.length);
@@ -948,7 +1018,7 @@ static uint32_t spi_fifo_depth(spi_t *obj)
9481018
return 8;
9491019
}
9501020

951-
return (spi_get_data_width(obj) <= 16) ? 8 : 4;
1021+
return (obj->spi.word_size_bits <= 16) ? 8 : 4;
9521022
}
9531023

9541024
#endif

0 commit comments

Comments
 (0)