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.
pull/3881/head
Damien George 2018-06-18 17:36:52 +10:00
rodzic 338af99a7f
commit e49cd106b4
1 zmienionych plików z 47 dodań i 15 usunięć

Wyświetl plik

@ -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);
}