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.
This commit is contained in:
parent
338af99a7f
commit
e49cd106b4
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user