summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien George <damien.p.george@gmail.com>2018-06-18 17:36:52 +1000
committerDamien George <damien.p.george@gmail.com>2018-06-18 17:41:14 +1000
commite49cd106b4bbbb47b782f73b66000df6094f7afe (patch)
tree5f96ca117f958b7393982cdc9c1676e598587033
parent338af99a7f3b8f2231be7475500a2bbf6a2bd723 (diff)
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.
-rw-r--r--ports/stm32/spi.c62
1 files changed, 47 insertions, 15 deletions
diff --git a/ports/stm32/spi.c b/ports/stm32/spi.c
index c578ac7da..e8621b0ab 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);
}