From 975e6b2fa5f9a066bafb631870cd1ca358e971e1 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Thu, 29 Aug 2024 01:22:44 -0700 Subject: [PATCH] Fix some DMA SPI issues with DMA IP v3 and/or word size > 8 bits (#332) * Fix some DMA SPI issues with DMA IP v3 and/or word size > 8 bits * Fix typo * oops fix naming --- .../TARGET_STM/TARGET_STM32WB/stm_dma_info.h | 2 +- targets/TARGET_STM/stm_dma_utils.c | 8 +-- targets/TARGET_STM/stm_spi_api.c | 72 ++++++++++++++++++- 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/targets/TARGET_STM/TARGET_STM32WB/stm_dma_info.h b/targets/TARGET_STM/TARGET_STM32WB/stm_dma_info.h index 4a73a48e4b1..f0a98da7682 100644 --- a/targets/TARGET_STM/TARGET_STM32WB/stm_dma_info.h +++ b/targets/TARGET_STM/TARGET_STM32WB/stm_dma_info.h @@ -35,7 +35,7 @@ static const DMALinkInfo SPITxDMALinks[] = { /// Mapping from SPI index to DMA link info for Rx static const DMALinkInfo SPIRxDMALinks[] = { {1, 2, DMA_REQUEST_SPI1_RX}, -#ifdef SPI4 +#ifdef SPI2 {1, 4, DMA_REQUEST_SPI2_RX} #endif }; diff --git a/targets/TARGET_STM/stm_dma_utils.c b/targets/TARGET_STM/stm_dma_utils.c index 481b5875858..c72dbaac12a 100644 --- a/targets/TARGET_STM/stm_dma_utils.c +++ b/targets/TARGET_STM/stm_dma_utils.c @@ -684,11 +684,11 @@ DMA_HandleTypeDef *stm_init_dma_link(const DMALinkInfo *dmaLink, uint32_t direct if(direction == DMA_PERIPH_TO_MEMORY || direction == DMA_MEMORY_TO_MEMORY) { // Destination is memory - dmaHandle->Init.DestInc = memInc ? DMA_SINC_INCREMENTED : DMA_SINC_FIXED; + dmaHandle->Init.DestInc = memInc ? DMA_DINC_INCREMENTED : DMA_DINC_FIXED; switch(memDataAlignment) { case 4: - dmaHandle->Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE; + dmaHandle->Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD; break; case 2: dmaHandle->Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD; @@ -702,11 +702,11 @@ DMA_HandleTypeDef *stm_init_dma_link(const DMALinkInfo *dmaLink, uint32_t direct } else { // Destination is a peripheral - dmaHandle->Init.DestInc = periphInc ? DMA_SINC_INCREMENTED : DMA_SINC_FIXED; + dmaHandle->Init.DestInc = periphInc ? DMA_DINC_INCREMENTED : DMA_DINC_FIXED; switch(periphDataAlignment) { case 4: - dmaHandle->Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE; + dmaHandle->Init.DestDataWidth = DMA_DEST_DATAWIDTH_WORD; break; case 2: dmaHandle->Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD; diff --git a/targets/TARGET_STM/stm_spi_api.c b/targets/TARGET_STM/stm_spi_api.c index d5c49fbcd08..fae3adbdf26 100644 --- a/targets/TARGET_STM/stm_spi_api.c +++ b/targets/TARGET_STM/stm_spi_api.c @@ -853,9 +853,8 @@ void spi_format(spi_t *obj, int bits, int mode, int slave) /* SPI slave implemtation in MBED does not support the 3 wires SPI. * (e.g. when MISO is not connected). So we're forcing slave in * 2LINES mode. As MISO is not connected, slave will only read - * from master, and cannot write to it. Inform user. + * from master, and cannot write to it. */ - debug("3 wires SPI slave not supported - slave will only read\r\n"); handle->Init.Direction = SPI_DIRECTION_2LINES; } @@ -1536,6 +1535,63 @@ typedef enum { SPI_TRANSFER_TYPE_TXRX = 3, } transfer_type_t; +/* + * Configure a DMA channel's transfer size to match the given SPI word size + */ +static void configure_dma_transfer_size(const uint32_t spiDataSize, DMA_HandleTypeDef * const dmaChannel) +{ +#if DMA_IP_VERSION_V3 + uint32_t * const transferSizePtr1 = &dmaChannel->Init.DestDataWidth; + uint32_t * const transferSizePtr2 = &dmaChannel->Init.SrcDataWidth; + uint32_t neededSizeVal1; + uint32_t neededSizeVal2; + + if(spiDataSize <= SPI_DATASIZE_8BIT) + { + neededSizeVal1 = DMA_DEST_DATAWIDTH_BYTE; + neededSizeVal2 = DMA_SRC_DATAWIDTH_BYTE; + } + else if(spiDataSize <= SPI_DATASIZE_16BIT) + { + neededSizeVal1 = DMA_DEST_DATAWIDTH_HALFWORD; + neededSizeVal2 = DMA_SRC_DATAWIDTH_HALFWORD; + } + else + { + neededSizeVal1 = DMA_DEST_DATAWIDTH_WORD; + neededSizeVal2 = DMA_SRC_DATAWIDTH_WORD; + } +#else + uint32_t * const transferSizePtr1 = &dmaChannel->Init.PeriphDataAlignment; + uint32_t * const transferSizePtr2 = &dmaChannel->Init.MemDataAlignment; + uint32_t neededSizeVal1; + uint32_t neededSizeVal2; + + if(spiDataSize <= SPI_DATASIZE_8BIT) + { + neededSizeVal1 = DMA_PDATAALIGN_BYTE; + neededSizeVal2 = DMA_MDATAALIGN_BYTE; + } + else if(spiDataSize <= SPI_DATASIZE_16BIT) + { + neededSizeVal1 = DMA_PDATAALIGN_HALFWORD; + neededSizeVal2 = DMA_MDATAALIGN_HALFWORD; + } + else + { + neededSizeVal1 = DMA_PDATAALIGN_WORD; + neededSizeVal2 = DMA_MDATAALIGN_WORD; + } +#endif + + // Check values and reinit DMA if needed + if(*transferSizePtr1 != neededSizeVal1 || *transferSizePtr2 != neededSizeVal2) + { + *transferSizePtr1 = neededSizeVal1; + *transferSizePtr2 = neededSizeVal2; + HAL_DMA_Init(dmaChannel); + } +} /// @returns True if DMA was used, false otherwise static bool spi_master_start_asynch_transfer(spi_t *obj, transfer_type_t transfer_type, const void *tx, void *rx, size_t length, DMAUsage hint) @@ -1580,6 +1636,18 @@ static bool spi_master_start_asynch_transfer(spi_t *obj, transfer_type_t transfe } useDMA = true; + + // Make sure that the DMA word size matches the SPI word size. Also check address alignment. + if(transfer_type == SPI_TRANSFER_TYPE_TXRX || transfer_type == SPI_TRANSFER_TYPE_TX) + { + MBED_ASSERT(((ptrdiff_t)tx) % (1 << bitshift) == 0); // <-- if you hit this assert you passed an unaligned pointer to an SPI async transfer + configure_dma_transfer_size(handle->Init.DataSize, handle->hdmatx); + } + if(transfer_type == SPI_TRANSFER_TYPE_TXRX || transfer_type == SPI_TRANSFER_TYPE_RX) + { + MBED_ASSERT(((ptrdiff_t)rx) % (1 << bitshift) == 0); // <-- if you hit this assert you passed an unaligned pointer to an SPI async transfer + configure_dma_transfer_size(handle->Init.DataSize, handle->hdmarx); + } } obj->spi.curr_transfer_uses_dma = useDMA;