Skip to content

Commit e49cd10

Browse files
committed
stm32/spi: Fix SPI driver so it can send/recv more than 65535 bytes.
The DMA peripheral is limited to transferring 65535 elements at a time so in order to send more than that the SPI driver must split the transfers up. The user must be aware of this limit if they are relying on precise timing of the entire SPI transfer, because there might be a small delay between the split transfers. Fixes issue adafruit#3851, and thanks to @kwagyeman for the original fix.
1 parent 338af99 commit e49cd10

1 file changed

Lines changed: 47 additions & 15 deletions

File tree

ports/stm32/spi.c

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -401,8 +401,7 @@ void spi_deinit(const spi_t *spi_obj) {
401401
}
402402
}
403403

404-
STATIC HAL_StatusTypeDef spi_wait_dma_finished(const spi_t *spi, uint32_t timeout) {
405-
uint32_t start = HAL_GetTick();
404+
STATIC HAL_StatusTypeDef spi_wait_dma_finished(const spi_t *spi, uint32_t t_start, uint32_t timeout) {
406405
volatile HAL_SPI_StateTypeDef *state = &spi->spi->State;
407406
for (;;) {
408407
// Do an atomic check of the state; WFI will exit even if IRQs are disabled
@@ -413,7 +412,7 @@ STATIC HAL_StatusTypeDef spi_wait_dma_finished(const spi_t *spi, uint32_t timeou
413412
}
414413
__WFI();
415414
enable_irq(irq_state);
416-
if (HAL_GetTick() - start >= timeout) {
415+
if (HAL_GetTick() - t_start >= timeout) {
417416
return HAL_TIMEOUT;
418417
}
419418
}
@@ -430,6 +429,8 @@ STATIC void spi_transfer(const spi_t *self, size_t len, const uint8_t *src, uint
430429
// time directly after the SPI/DMA is initialised. The cause of this is
431430
// unknown but we sidestep the issue by using polling for 1 byte transfer.
432431

432+
// Note: DMA transfers are limited to 65535 bytes at a time.
433+
433434
HAL_StatusTypeDef status;
434435

435436
if (dest == NULL) {
@@ -442,10 +443,20 @@ STATIC void spi_transfer(const spi_t *self, size_t len, const uint8_t *src, uint
442443
self->spi->hdmatx = &tx_dma;
443444
self->spi->hdmarx = NULL;
444445
MP_HAL_CLEAN_DCACHE(src, len);
445-
status = HAL_SPI_Transmit_DMA(self->spi, (uint8_t*)src, len);
446-
if (status == HAL_OK) {
447-
status = spi_wait_dma_finished(self, timeout);
448-
}
446+
uint32_t t_start = HAL_GetTick();
447+
do {
448+
uint32_t l = MIN(len, 65535);
449+
status = HAL_SPI_Transmit_DMA(self->spi, (uint8_t*)src, l);
450+
if (status != HAL_OK) {
451+
break;
452+
}
453+
status = spi_wait_dma_finished(self, t_start, timeout);
454+
if (status != HAL_OK) {
455+
break;
456+
}
457+
len -= l;
458+
src += l;
459+
} while (len);
449460
dma_deinit(self->tx_dma_descr);
450461
}
451462
} else if (src == NULL) {
@@ -464,10 +475,20 @@ STATIC void spi_transfer(const spi_t *self, size_t len, const uint8_t *src, uint
464475
dma_init(&rx_dma, self->rx_dma_descr, self->spi);
465476
self->spi->hdmarx = &rx_dma;
466477
MP_HAL_CLEANINVALIDATE_DCACHE(dest, len);
467-
status = HAL_SPI_Receive_DMA(self->spi, dest, len);
468-
if (status == HAL_OK) {
469-
status = spi_wait_dma_finished(self, timeout);
470-
}
478+
uint32_t t_start = HAL_GetTick();
479+
do {
480+
uint32_t l = MIN(len, 65535);
481+
status = HAL_SPI_Receive_DMA(self->spi, dest, l);
482+
if (status != HAL_OK) {
483+
break;
484+
}
485+
status = spi_wait_dma_finished(self, t_start, timeout);
486+
if (status != HAL_OK) {
487+
break;
488+
}
489+
len -= l;
490+
dest += l;
491+
} while (len);
471492
if (self->spi->hdmatx != NULL) {
472493
dma_deinit(self->tx_dma_descr);
473494
}
@@ -485,10 +506,21 @@ STATIC void spi_transfer(const spi_t *self, size_t len, const uint8_t *src, uint
485506
self->spi->hdmarx = &rx_dma;
486507
MP_HAL_CLEAN_DCACHE(src, len);
487508
MP_HAL_CLEANINVALIDATE_DCACHE(dest, len);
488-
status = HAL_SPI_TransmitReceive_DMA(self->spi, (uint8_t*)src, dest, len);
489-
if (status == HAL_OK) {
490-
status = spi_wait_dma_finished(self, timeout);
491-
}
509+
uint32_t t_start = HAL_GetTick();
510+
do {
511+
uint32_t l = MIN(len, 65535);
512+
status = HAL_SPI_TransmitReceive_DMA(self->spi, (uint8_t*)src, dest, l);
513+
if (status != HAL_OK) {
514+
break;
515+
}
516+
status = spi_wait_dma_finished(self, t_start, timeout);
517+
if (status != HAL_OK) {
518+
break;
519+
}
520+
len -= l;
521+
src += l;
522+
dest += l;
523+
} while (len);
492524
dma_deinit(self->tx_dma_descr);
493525
dma_deinit(self->rx_dma_descr);
494526
}

0 commit comments

Comments
 (0)