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
128147static void spi_enable_vector_interrupt (spi_t * obj , uint32_t handler , uint8_t enable );
129148static void spi_master_enable_interrupt (spi_t * obj , uint8_t enable );
@@ -133,7 +152,6 @@ static uint32_t spi_event_check(spi_t *obj);
133152static void spi_enable_event (spi_t * obj , uint32_t event , uint8_t enable );
134153static void spi_buffer_set (spi_t * obj , const void * tx , size_t tx_length , void * rx , size_t rx_length );
135154static 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 );
137155static int spi_is_tx_complete (spi_t * obj );
138156static int spi_is_rx_complete (spi_t * obj );
139157static 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+
160226void 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
352420int 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
367452const PinMap * spi_master_mosi_pinmap ()
@@ -442,15 +527,18 @@ void spi_slave_write(spi_t *obj, int value)
442527bool 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)
747835static 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)
796880static 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
848930static 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-
883953static 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