From e49cd106b4bbbb47b782f73b66000df6094f7afe Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 18 Jun 2018 17:36:52 +1000 Subject: [PATCH] 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 #3851, and thanks to @kwagyeman for the original fix. --- ports/stm32/spi.c | 62 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/ports/stm32/spi.c b/ports/stm32/spi.c index c578ac7da2..e8621b0ab3 100644 --- a/ports/stm32/spi.c +++ b/ports/stm32/spi.c @@ -401,8 +401,7 @@ void spi_deinit(const spi_t *spi_obj) { } } -STATIC HAL_StatusTypeDef spi_wait_dma_finished(const spi_t *spi, uint32_t timeout) { - uint32_t start = HAL_GetTick(); +STATIC HAL_StatusTypeDef spi_wait_dma_finished(const spi_t *spi, uint32_t t_start, uint32_t timeout) { volatile HAL_SPI_StateTypeDef *state = &spi->spi->State; for (;;) { // 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 } __WFI(); enable_irq(irq_state); - if (HAL_GetTick() - start >= timeout) { + if (HAL_GetTick() - t_start >= timeout) { return HAL_TIMEOUT; } } @@ -430,6 +429,8 @@ STATIC void spi_transfer(const spi_t *self, size_t len, const uint8_t *src, uint // time directly after the SPI/DMA is initialised. The cause of this is // unknown but we sidestep the issue by using polling for 1 byte transfer. + // Note: DMA transfers are limited to 65535 bytes at a time. + HAL_StatusTypeDef status; if (dest == NULL) { @@ -442,10 +443,20 @@ STATIC void spi_transfer(const spi_t *self, size_t len, const uint8_t *src, uint self->spi->hdmatx = &tx_dma; self->spi->hdmarx = NULL; MP_HAL_CLEAN_DCACHE(src, len); - status = HAL_SPI_Transmit_DMA(self->spi, (uint8_t*)src, len); - if (status == HAL_OK) { - status = spi_wait_dma_finished(self, timeout); - } + uint32_t t_start = HAL_GetTick(); + do { + uint32_t l = MIN(len, 65535); + status = HAL_SPI_Transmit_DMA(self->spi, (uint8_t*)src, l); + if (status != HAL_OK) { + break; + } + status = spi_wait_dma_finished(self, t_start, timeout); + if (status != HAL_OK) { + break; + } + len -= l; + src += l; + } while (len); dma_deinit(self->tx_dma_descr); } } else if (src == NULL) { @@ -464,10 +475,20 @@ STATIC void spi_transfer(const spi_t *self, size_t len, const uint8_t *src, uint dma_init(&rx_dma, self->rx_dma_descr, self->spi); self->spi->hdmarx = &rx_dma; MP_HAL_CLEANINVALIDATE_DCACHE(dest, len); - status = HAL_SPI_Receive_DMA(self->spi, dest, len); - if (status == HAL_OK) { - status = spi_wait_dma_finished(self, timeout); - } + uint32_t t_start = HAL_GetTick(); + do { + uint32_t l = MIN(len, 65535); + status = HAL_SPI_Receive_DMA(self->spi, dest, l); + if (status != HAL_OK) { + break; + } + status = spi_wait_dma_finished(self, t_start, timeout); + if (status != HAL_OK) { + break; + } + len -= l; + dest += l; + } while (len); if (self->spi->hdmatx != NULL) { dma_deinit(self->tx_dma_descr); } @@ -485,10 +506,21 @@ STATIC void spi_transfer(const spi_t *self, size_t len, const uint8_t *src, uint self->spi->hdmarx = &rx_dma; MP_HAL_CLEAN_DCACHE(src, len); MP_HAL_CLEANINVALIDATE_DCACHE(dest, len); - status = HAL_SPI_TransmitReceive_DMA(self->spi, (uint8_t*)src, dest, len); - if (status == HAL_OK) { - status = spi_wait_dma_finished(self, timeout); - } + uint32_t t_start = HAL_GetTick(); + do { + uint32_t l = MIN(len, 65535); + status = HAL_SPI_TransmitReceive_DMA(self->spi, (uint8_t*)src, dest, l); + if (status != HAL_OK) { + break; + } + status = spi_wait_dma_finished(self, t_start, timeout); + if (status != HAL_OK) { + break; + } + len -= l; + src += l; + dest += l; + } while (len); dma_deinit(self->tx_dma_descr); dma_deinit(self->rx_dma_descr); }