From 28815b13d31adb6f75818e1748bfde87751cb639 Mon Sep 17 00:00:00 2001 From: Jamie Smith Date: Thu, 2 Nov 2023 20:16:45 -0700 Subject: [PATCH] DMA SPI support for STM32 devices (#162) * Start on STM32 DMA SPI * Update all objects.hs, add interrupt function * Initial DMA code should be ready to test out... * Fix SPI interrupt-mode IRQ handlers, add SPI::transfer_and_wait * Fix CMake error when building for STM32WL processors * Now builds on all STM devices! * Properly support STM32U5 / DMA IP v3 * Start on STM32F4 support, fix hardfault on IP v1 and v3 due to incorrect indexing * Fix Rx-only transfers, add abort code, fix incorrect channel assignments for DMA IP v1 devices * Start on STM32H7 SPI DMA * Fixes for H7: Correctly manage data cache, keep SPI ISR enabled * Implement DMA SPI header constants for all remaining STM32 families. Also add support for freeing DMA channels * Try and fix build on STM32G0 * Fix build on STM32G0 * Add SPI_32BIT_WORDS label, start on fixing SPI docs * SPI: Implement reference counting so that DMA channels get freed properly * Fix issue where SPI data could get corrupted (by TI mode turning on) depending on memory layout (if your spis pointer & 0x10 was nonzero) * Mark DMA channels as unallocated when SPI bus is freed * Simplify spi_abort_asynch() * Fix some rebase issues, fix failing to allocate DMA channel on STM32U5 * Fix DMA getting stuck on STM32F4, F7, and F2 --- drivers/source/I2C.cpp | 2 +- hal/include/hal/spi_api.h | 3 + targets/TARGET_STM/CMakeLists.txt | 1 + targets/TARGET_STM/TARGET_STM32F0/objects.h | 14 - .../TARGET_STM/TARGET_STM32F0/spi_device.h | 3 + .../TARGET_STM/TARGET_STM32F0/stm_dma_info.h | 38 + targets/TARGET_STM/TARGET_STM32F1/objects.h | 14 - .../TARGET_STM/TARGET_STM32F1/spi_device.h | 3 + .../TARGET_STM/TARGET_STM32F1/stm_dma_info.h | 40 + .../stm32f2xx_hal_dma_ex.c | 13 +- targets/TARGET_STM/TARGET_STM32F2/objects.h | 14 - .../TARGET_STM/TARGET_STM32F2/spi_device.h | 3 + .../TARGET_STM/TARGET_STM32F2/stm_dma_info.h | 40 + targets/TARGET_STM/TARGET_STM32F3/objects.h | 14 - .../TARGET_STM/TARGET_STM32F3/spi_device.h | 3 + .../TARGET_STM/TARGET_STM32F3/stm_dma_info.h | 42 + .../stm32f4xx_hal_dma_ex.c | 13 +- targets/TARGET_STM/TARGET_STM32F4/objects.h | 14 - .../TARGET_STM/TARGET_STM32F4/spi_device.h | 3 + .../TARGET_STM/TARGET_STM32F4/stm_dma_info.h | 51 + .../STM32F7xx_HAL_Driver/stm32f7xx_hal_dma.c | 15 +- .../stm32f7xx_hal_dma_ex.c | 13 +- targets/TARGET_STM/TARGET_STM32F7/objects.h | 14 - .../TARGET_STM/TARGET_STM32F7/spi_device.h | 3 + .../TARGET_STM/TARGET_STM32F7/stm_dma_info.h | 51 + targets/TARGET_STM/TARGET_STM32G0/objects.h | 14 - .../TARGET_STM/TARGET_STM32G0/stm_dma_info.h | 52 + targets/TARGET_STM/TARGET_STM32G4/objects.h | 14 - .../TARGET_STM/TARGET_STM32G4/spi_device.h | 3 + .../TARGET_STM/TARGET_STM32G4/stm_dma_info.h | 48 + targets/TARGET_STM/TARGET_STM32H7/objects.h | 14 - .../TARGET_STM/TARGET_STM32H7/spi_device.h | 3 + .../TARGET_STM/TARGET_STM32H7/stm_dma_info.h | 46 + targets/TARGET_STM/TARGET_STM32L0/objects.h | 14 - .../TARGET_STM/TARGET_STM32L0/spi_device.h | 3 + .../TARGET_STM/TARGET_STM32L0/stm_dma_info.h | 38 + targets/TARGET_STM/TARGET_STM32L1/objects.h | 14 - .../TARGET_STM/TARGET_STM32L1/spi_device.h | 3 + .../TARGET_STM/TARGET_STM32L1/stm_dma_info.h | 40 + targets/TARGET_STM/TARGET_STM32L4/objects.h | 14 - .../TARGET_STM/TARGET_STM32L4/spi_device.h | 3 + .../TARGET_STM/TARGET_STM32L4/stm_dma_info.h | 65 + targets/TARGET_STM/TARGET_STM32L5/objects.h | 14 - .../TARGET_STM/TARGET_STM32L5/spi_device.h | 3 + .../TARGET_STM/TARGET_STM32L5/stm_dma_info.h | 42 + targets/TARGET_STM/TARGET_STM32U5/objects.h | 14 - .../TARGET_STM/TARGET_STM32U5/spi_device.h | 3 + .../TARGET_STM/TARGET_STM32U5/stm_dma_info.h | 42 + targets/TARGET_STM/TARGET_STM32WB/objects.h | 14 - .../TARGET_STM/TARGET_STM32WB/spi_device.h | 3 + .../TARGET_STM/TARGET_STM32WB/stm_dma_info.h | 44 + targets/TARGET_STM/TARGET_STM32WL/objects.h | 14 - .../TARGET_STM/TARGET_STM32WL/spi_device.h | 3 + .../TARGET_STM/TARGET_STM32WL/stm_dma_info.h | 40 + targets/TARGET_STM/device.h | 1 + targets/TARGET_STM/stm_dma_ip_v1.h | 53 + targets/TARGET_STM/stm_dma_ip_v2.h | 61 + targets/TARGET_STM/stm_dma_ip_v3.h | 37 + targets/TARGET_STM/stm_dma_utils.c | 1201 +++++++++++++++++ targets/TARGET_STM/stm_dma_utils.h | 109 ++ targets/TARGET_STM/stm_spi_api.c | 336 ++++- targets/TARGET_STM/stm_spi_api.h | 53 + targets/targets.json5 | 6 +- tools/cmake/mbed-run-greentea-test.in.cmake | 2 +- 64 files changed, 2642 insertions(+), 265 deletions(-) create mode 100644 targets/TARGET_STM/TARGET_STM32F0/stm_dma_info.h create mode 100644 targets/TARGET_STM/TARGET_STM32F1/stm_dma_info.h create mode 100644 targets/TARGET_STM/TARGET_STM32F2/stm_dma_info.h create mode 100644 targets/TARGET_STM/TARGET_STM32F3/stm_dma_info.h create mode 100644 targets/TARGET_STM/TARGET_STM32F4/stm_dma_info.h create mode 100644 targets/TARGET_STM/TARGET_STM32F7/stm_dma_info.h create mode 100644 targets/TARGET_STM/TARGET_STM32G0/stm_dma_info.h create mode 100644 targets/TARGET_STM/TARGET_STM32G4/stm_dma_info.h create mode 100644 targets/TARGET_STM/TARGET_STM32H7/stm_dma_info.h create mode 100644 targets/TARGET_STM/TARGET_STM32L0/stm_dma_info.h create mode 100644 targets/TARGET_STM/TARGET_STM32L1/stm_dma_info.h create mode 100644 targets/TARGET_STM/TARGET_STM32L4/stm_dma_info.h create mode 100644 targets/TARGET_STM/TARGET_STM32L5/stm_dma_info.h create mode 100644 targets/TARGET_STM/TARGET_STM32U5/stm_dma_info.h create mode 100644 targets/TARGET_STM/TARGET_STM32WB/stm_dma_info.h create mode 100644 targets/TARGET_STM/TARGET_STM32WL/stm_dma_info.h create mode 100644 targets/TARGET_STM/stm_dma_ip_v1.h create mode 100644 targets/TARGET_STM/stm_dma_ip_v2.h create mode 100644 targets/TARGET_STM/stm_dma_ip_v3.h create mode 100644 targets/TARGET_STM/stm_dma_utils.c create mode 100644 targets/TARGET_STM/stm_dma_utils.h create mode 100644 targets/TARGET_STM/stm_spi_api.h diff --git a/drivers/source/I2C.cpp b/drivers/source/I2C.cpp index 3718e8b8c0..ee68af0b89 100644 --- a/drivers/source/I2C.cpp +++ b/drivers/source/I2C.cpp @@ -245,7 +245,7 @@ void I2C::abort_transfer(void) I2C::Result I2C::transfer_and_wait(int address, const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length, rtos::Kernel::Clock::duration_u32 timeout, bool repeated) { // Use EventFlags to suspend the thread until the transfer finishes - rtos::EventFlags transferResultFlags("I2C::Result EvFlags"); + rtos::EventFlags transferResultFlags("I2C::transfer_and_wait EvFlags"); // Simple callback from the transfer that sets the EventFlags using the I2C result event event_callback_t transferCallback([&](int event) { diff --git a/hal/include/hal/spi_api.h b/hal/include/hal/spi_api.h index 5f78f9b7b0..65853c69b3 100644 --- a/hal/include/hal/spi_api.h +++ b/hal/include/hal/spi_api.h @@ -261,6 +261,9 @@ int spi_master_write(spi_t *obj, int value); * tx_length and rx_length. The bytes written will be padded with the * value 0xff. * + * Note: Even if the word size / bits per frame is not 8, \c rx_length and \c tx_length + * still give lengths in bytes of input data, not numbers of words. + * * @param[in] obj The SPI peripheral to use for sending * @param[in] tx_buffer Pointer to the byte-array of data to write to the device * @param[in] tx_length Number of bytes to write, may be zero diff --git a/targets/TARGET_STM/CMakeLists.txt b/targets/TARGET_STM/CMakeLists.txt index b9f2cb63c2..4d75ff4e46 100644 --- a/targets/TARGET_STM/CMakeLists.txt +++ b/targets/TARGET_STM/CMakeLists.txt @@ -52,6 +52,7 @@ target_sources(mbed-stm trng_api.c us_ticker.c watchdog_api.c + stm_dma_utils.c ) target_link_libraries(mbed-stm INTERFACE mbed-cmsis-cortex-m) diff --git a/targets/TARGET_STM/TARGET_STM32F0/objects.h b/targets/TARGET_STM/TARGET_STM32F0/objects.h index c8188eb4e2..ef188ba48b 100644 --- a/targets/TARGET_STM/TARGET_STM32F0/objects.h +++ b/targets/TARGET_STM/TARGET_STM32F0/objects.h @@ -44,20 +44,6 @@ struct pwmout_s { uint8_t inverted; }; -struct spi_s { - SPI_HandleTypeDef handle; - IRQn_Type spiIRQ; - SPIName spi; - PinName pin_miso; - PinName pin_mosi; - PinName pin_sclk; - PinName pin_ssel; -#if DEVICE_SPI_ASYNCH - uint32_t event; - uint8_t transfer_type; -#endif -}; - struct serial_s { UARTName uart; int index; // Used by irq diff --git a/targets/TARGET_STM/TARGET_STM32F0/spi_device.h b/targets/TARGET_STM/TARGET_STM32F0/spi_device.h index 0477325124..2a2da880c7 100644 --- a/targets/TARGET_STM/TARGET_STM32F0/spi_device.h +++ b/targets/TARGET_STM/TARGET_STM32F0/spi_device.h @@ -21,4 +21,7 @@ // Defines the word length capability of the device where Nth bit allows for N window size #define STM32_SPI_CAPABILITY_WORD_LENGTH (0x0000FFF8) +// We have DMA support +#define STM32_SPI_CAPABILITY_DMA 1 + #endif diff --git a/targets/TARGET_STM/TARGET_STM32F0/stm_dma_info.h b/targets/TARGET_STM/TARGET_STM32F0/stm_dma_info.h new file mode 100644 index 0000000000..0fa161ec68 --- /dev/null +++ b/targets/TARGET_STM/TARGET_STM32F0/stm_dma_info.h @@ -0,0 +1,38 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM_DMA_INFO_H +#define MBED_OS_STM_DMA_INFO_H + +#include "cmsis.h" +#include "stm_dma_utils.h" + +// See STM32F0 reference manual Table 26. + +/// Mapping from SPI index to DMA link info for Tx +static const DMALinkInfo SPITxDMALinks[] = { + {1, 3}, // SPI1 Tx is DMA1 Channel 3 + {1, 5}, // SPI2 Tx is DMA1 Channel 5 +}; + +/// Mapping from SPI index to DMA link info for Rx +static const DMALinkInfo SPIRxDMALinks[] = { + {1, 2}, // SPI1 Rx is DMA1 Channel 2 + {1, 4}, // SPI2 Rx is DMA1 Channel 4 +}; + +#endif //MBED_OS_STM_DMA_INFO_H diff --git a/targets/TARGET_STM/TARGET_STM32F1/objects.h b/targets/TARGET_STM/TARGET_STM32F1/objects.h index 491cdd2e09..94d93ad825 100644 --- a/targets/TARGET_STM/TARGET_STM32F1/objects.h +++ b/targets/TARGET_STM/TARGET_STM32F1/objects.h @@ -89,20 +89,6 @@ struct serial_s { #endif }; -struct spi_s { - SPI_HandleTypeDef handle; - IRQn_Type spiIRQ; - SPIName spi; - PinName pin_miso; - PinName pin_mosi; - PinName pin_sclk; - PinName pin_ssel; -#if DEVICE_SPI_ASYNCH - uint32_t event; - uint8_t transfer_type; -#endif -}; - struct i2c_s { /* The 1st 2 members I2CName i2c * and I2C_HandleTypeDef handle should diff --git a/targets/TARGET_STM/TARGET_STM32F1/spi_device.h b/targets/TARGET_STM/TARGET_STM32F1/spi_device.h index aa56a077c4..7ebf44d739 100644 --- a/targets/TARGET_STM/TARGET_STM32F1/spi_device.h +++ b/targets/TARGET_STM/TARGET_STM32F1/spi_device.h @@ -35,4 +35,7 @@ // Defines the word length capability of the device where Nth bit allows for N window size #define STM32_SPI_CAPABILITY_WORD_LENGTH (0x00008080) +// We have DMA support +#define STM32_SPI_CAPABILITY_DMA 1 + #endif diff --git a/targets/TARGET_STM/TARGET_STM32F1/stm_dma_info.h b/targets/TARGET_STM/TARGET_STM32F1/stm_dma_info.h new file mode 100644 index 0000000000..fe40accc7a --- /dev/null +++ b/targets/TARGET_STM/TARGET_STM32F1/stm_dma_info.h @@ -0,0 +1,40 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM_DMA_INFO_H +#define MBED_OS_STM_DMA_INFO_H + +#include "cmsis.h" +#include "stm_dma_utils.h" + +// See STM32F1 reference manual Tables 78 and 79. + +/// Mapping from SPI index to DMA link info for Tx +static const DMALinkInfo SPITxDMALinks[] = { + {1, 3}, // SPI1 Tx is DMA1 Channel 3 + {1, 5}, // SPI2 Tx is DMA1 Channel 5 + {2, 2}, // SPI3 Tx is DMA2 Channel 2 +}; + +/// Mapping from SPI index to DMA link info for Rx +static const DMALinkInfo SPIRxDMALinks[] = { + {1, 2}, // SPI1 Rx is DMA1 Channel 2 + {1, 4}, // SPI2 Rx is DMA1 Channel 4 + {2, 1}, // SPI3 Rx is DMA2 Channel 1 +}; + +#endif //MBED_OS_STM_DMA_INFO_H diff --git a/targets/TARGET_STM/TARGET_STM32F2/STM32Cube_FW/STM32F2xx_HAL_Driver/stm32f2xx_hal_dma_ex.c b/targets/TARGET_STM/TARGET_STM32F2/STM32Cube_FW/STM32F2xx_HAL_Driver/stm32f2xx_hal_dma_ex.c index 53bec8e2b8..0320e319e4 100644 --- a/targets/TARGET_STM/TARGET_STM32F2/STM32Cube_FW/STM32F2xx_HAL_Driver/stm32f2xx_hal_dma_ex.c +++ b/targets/TARGET_STM/TARGET_STM32F2/STM32Cube_FW/STM32F2xx_HAL_Driver/stm32f2xx_hal_dma_ex.c @@ -202,7 +202,18 @@ HAL_StatusTypeDef HAL_DMAEx_MultiBufferStart_IT(DMA_HandleTypeDef *hdma, uint32_ /* Enable Common interrupts*/ hdma->Instance->CR |= DMA_IT_TC | DMA_IT_TE | DMA_IT_DME; - hdma->Instance->FCR |= DMA_IT_FE; + + /* Mbed CE mod: Only enable the FIFO Error interrupt if the FIFO is actually enabled. + * If it's not enabled, then this interrupt can trigger spuriously from memory bus + * stalls that the DMA engine encounters, and this creates random DMA failures. + * Reference forum thread here: + * https://community.st.com/t5/stm32-mcus-products/spi-dma-fifo-error-issue-feifx/td-p/537074 + * also: https://community.st.com/t5/stm32-mcus-touch-gfx-and-gui/spi-dma-error-is-occurred-when-the-other-dma-memory-to-memory-is/td-p/191590 + */ + if(hdma->Instance->FCR & DMA_SxFCR_DMDIS) + { + hdma->Instance->FCR |= DMA_IT_FE; + } if((hdma->XferHalfCpltCallback != NULL) || (hdma->XferM1HalfCpltCallback != NULL)) { diff --git a/targets/TARGET_STM/TARGET_STM32F2/objects.h b/targets/TARGET_STM/TARGET_STM32F2/objects.h index e30629a09b..ef3a22493c 100644 --- a/targets/TARGET_STM/TARGET_STM32F2/objects.h +++ b/targets/TARGET_STM/TARGET_STM32F2/objects.h @@ -95,20 +95,6 @@ struct serial_s { #endif }; -struct spi_s { - SPI_HandleTypeDef handle; - IRQn_Type spiIRQ; - SPIName spi; - PinName pin_miso; - PinName pin_mosi; - PinName pin_sclk; - PinName pin_ssel; -#if DEVICE_SPI_ASYNCH - uint32_t event; - uint8_t transfer_type; -#endif -}; - struct i2c_s { /* The 1st 2 members I2CName i2c * and I2C_HandleTypeDef handle should diff --git a/targets/TARGET_STM/TARGET_STM32F2/spi_device.h b/targets/TARGET_STM/TARGET_STM32F2/spi_device.h index 4309567c01..5082713716 100644 --- a/targets/TARGET_STM/TARGET_STM32F2/spi_device.h +++ b/targets/TARGET_STM/TARGET_STM32F2/spi_device.h @@ -35,4 +35,7 @@ // Defines the word legnth capability of the device where Nth bit allows for N window size #define STM32_SPI_CAPABILITY_WORD_LENGTH (0x00008080) +// We have DMA support +#define STM32_SPI_CAPABILITY_DMA 1 + #endif diff --git a/targets/TARGET_STM/TARGET_STM32F2/stm_dma_info.h b/targets/TARGET_STM/TARGET_STM32F2/stm_dma_info.h new file mode 100644 index 0000000000..debda29e68 --- /dev/null +++ b/targets/TARGET_STM/TARGET_STM32F2/stm_dma_info.h @@ -0,0 +1,40 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM_DMA_INFO_H +#define MBED_OS_STM_DMA_INFO_H + +#include "cmsis.h" +#include "stm_dma_utils.h" + +// See STM32F2 reference manual Tables 22 and 23 + +/// Mapping from SPI index to DMA link info for Tx +static const DMALinkInfo SPITxDMALinks[] = { + {2, 3, 3}, // SPI1 Tx is DMA2 Stream 3 Channel 3 + {1, 4, 0}, // SPI2 Tx is DMA1 Stream 4 Channel 0 + {1, 5, 0} // SPI3 Tx is DMA1 Stream 5 Channel 0 +}; + +/// Mapping from SPI index to DMA link info for Rx +static const DMALinkInfo SPIRxDMALinks[] = { + {2, 0, 3}, // SPI1 Rx is DMA2 Stream 0 Channel 3 + {1, 3, 0}, // SPI2 Rx is DMA1 Stream 3 Channel 0 + {1, 0, 0} // SPI3 Rx is DMA2 Stream 0 Channel 0 +}; + +#endif //MBED_OS_STM_DMA_INFO_H diff --git a/targets/TARGET_STM/TARGET_STM32F3/objects.h b/targets/TARGET_STM/TARGET_STM32F3/objects.h index c95c440c87..96c9a1231e 100644 --- a/targets/TARGET_STM/TARGET_STM32F3/objects.h +++ b/targets/TARGET_STM/TARGET_STM32F3/objects.h @@ -57,20 +57,6 @@ struct pwmout_s { uint8_t inverted; }; -struct spi_s { - SPI_HandleTypeDef handle; - IRQn_Type spiIRQ; - SPIName spi; - PinName pin_miso; - PinName pin_mosi; - PinName pin_sclk; - PinName pin_ssel; -#if DEVICE_SPI_ASYNCH - uint32_t event; - uint8_t transfer_type; -#endif -}; - struct serial_s { UARTName uart; int index; // Used by irq diff --git a/targets/TARGET_STM/TARGET_STM32F3/spi_device.h b/targets/TARGET_STM/TARGET_STM32F3/spi_device.h index a61fbf5249..715740ca93 100644 --- a/targets/TARGET_STM/TARGET_STM32F3/spi_device.h +++ b/targets/TARGET_STM/TARGET_STM32F3/spi_device.h @@ -35,4 +35,7 @@ // Defines the word legnth capability of the device where Nth bit allows for N window size #define STM32_SPI_CAPABILITY_WORD_LENGTH (0x0000FFF8) +// We have DMA support +#define STM32_SPI_CAPABILITY_DMA 1 + #endif diff --git a/targets/TARGET_STM/TARGET_STM32F3/stm_dma_info.h b/targets/TARGET_STM/TARGET_STM32F3/stm_dma_info.h new file mode 100644 index 0000000000..d54393b7df --- /dev/null +++ b/targets/TARGET_STM/TARGET_STM32F3/stm_dma_info.h @@ -0,0 +1,42 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM_DMA_INFO_H +#define MBED_OS_STM_DMA_INFO_H + +#include "cmsis.h" +#include "stm_dma_utils.h" + +// See STM32F3 reference manual Tables 78 and 79. + +/// Mapping from SPI index to DMA link info for Tx +static const DMALinkInfo SPITxDMALinks[] = { + {1, 3}, // SPI1 Tx is DMA1 Channel 3 + {1, 5}, // SPI2 Tx is DMA1 Channel 5 + {2, 2}, // SPI3 Tx is DMA2 Channel 2 + {2, 5}, // SPI4 Tx is DMA2 Channel 5 +}; + +/// Mapping from SPI index to DMA link info for Rx +static const DMALinkInfo SPIRxDMALinks[] = { + {1, 2}, // SPI1 Rx is DMA1 Channel 2 + {1, 4}, // SPI2 Rx is DMA1 Channel 4 + {2, 1}, // SPI3 Rx is DMA2 Channel 1 + {2, 4}, // SPI4 Rx is DMA2 Channel 4 +}; + +#endif //MBED_OS_STM_DMA_INFO_H diff --git a/targets/TARGET_STM/TARGET_STM32F4/STM32Cube_FW/STM32F4xx_HAL_Driver/stm32f4xx_hal_dma_ex.c b/targets/TARGET_STM/TARGET_STM32F4/STM32Cube_FW/STM32F4xx_HAL_Driver/stm32f4xx_hal_dma_ex.c index 6e07376834..e8939dfded 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/STM32Cube_FW/STM32F4xx_HAL_Driver/stm32f4xx_hal_dma_ex.c +++ b/targets/TARGET_STM/TARGET_STM32F4/STM32Cube_FW/STM32F4xx_HAL_Driver/stm32f4xx_hal_dma_ex.c @@ -202,7 +202,18 @@ HAL_StatusTypeDef HAL_DMAEx_MultiBufferStart_IT(DMA_HandleTypeDef *hdma, uint32_ /* Enable Common interrupts*/ hdma->Instance->CR |= DMA_IT_TC | DMA_IT_TE | DMA_IT_DME; - hdma->Instance->FCR |= DMA_IT_FE; + + /* Mbed CE mod: Only enable the FIFO Error interrupt if the FIFO is actually enabled. + * If it's not enabled, then this interrupt can trigger spuriously from memory bus + * stalls that the DMA engine encounters, and this creates random DMA failures. + * Reference forum thread here: + * https://community.st.com/t5/stm32-mcus-products/spi-dma-fifo-error-issue-feifx/td-p/537074 + * also: https://community.st.com/t5/stm32-mcus-touch-gfx-and-gui/spi-dma-error-is-occurred-when-the-other-dma-memory-to-memory-is/td-p/191590 + */ + if(hdma->Instance->FCR & DMA_SxFCR_DMDIS) + { + hdma->Instance->FCR |= DMA_IT_FE; + } if((hdma->XferHalfCpltCallback != NULL) || (hdma->XferM1HalfCpltCallback != NULL)) { diff --git a/targets/TARGET_STM/TARGET_STM32F4/objects.h b/targets/TARGET_STM/TARGET_STM32F4/objects.h index a71b3030b9..320848eaa9 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/objects.h +++ b/targets/TARGET_STM/TARGET_STM32F4/objects.h @@ -76,20 +76,6 @@ struct serial_s { #endif }; -struct spi_s { - SPI_HandleTypeDef handle; - IRQn_Type spiIRQ; - SPIName spi; - PinName pin_miso; - PinName pin_mosi; - PinName pin_sclk; - PinName pin_ssel; -#if DEVICE_SPI_ASYNCH - uint32_t event; - uint8_t transfer_type; -#endif -}; - struct i2c_s { /* The 1st 2 members I2CName i2c * and I2C_HandleTypeDef handle should diff --git a/targets/TARGET_STM/TARGET_STM32F4/spi_device.h b/targets/TARGET_STM/TARGET_STM32F4/spi_device.h index f52d349871..d31c7f4934 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/spi_device.h +++ b/targets/TARGET_STM/TARGET_STM32F4/spi_device.h @@ -21,4 +21,7 @@ // Defines the word legnth capability of the device where Nth bit allows for N window size #define STM32_SPI_CAPABILITY_WORD_LENGTH (0x00008080) +// We have DMA support +#define STM32_SPI_CAPABILITY_DMA 1 + #endif diff --git a/targets/TARGET_STM/TARGET_STM32F4/stm_dma_info.h b/targets/TARGET_STM/TARGET_STM32F4/stm_dma_info.h new file mode 100644 index 0000000000..ea3c6a256e --- /dev/null +++ b/targets/TARGET_STM/TARGET_STM32F4/stm_dma_info.h @@ -0,0 +1,51 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM_DMA_INFO_H +#define MBED_OS_STM_DMA_INFO_H + +#include "cmsis.h" +#include "stm_dma_utils.h" + +// See STM32F4 reference manual Tables 42 and 43. +// Note: For each SPI and each direction, there are two possible assignments to a DMA channel. +// We need to assign them here so that no combination would ever conflict and use the same DMA channel. + +// Exception: SPI5 and SPI6 use the same DMA channels in hardware and there's no way to deconflict them. +// So, SPI5 and SPI6 cannot be used with DMA at the same time. + +/// Mapping from SPI index to DMA link info for Tx +static const DMALinkInfo SPITxDMALinks[] = { + {2, 3, 3}, // SPI1 Tx is DMA2 Stream 3 Channel 3 + {1, 4, 0}, // SPI2 Tx is DMA1 Stream 4 Channel 0 + {1, 5, 0}, // SPI3 Tx is DMA1 Stream 5 Channel 0 + {2, 1, 4}, // SPI4 Tx is DMA2 Stream 1 Channel 4 + {2, 6, 7}, // SPI5 Tx is DMA2 Stream 6 Channel 7 + {2, 5, 1}, // SPI6 Tx is DMA2 Stream 5 Channel 1 +}; + +/// Mapping from SPI index to DMA link info for Rx +static const DMALinkInfo SPIRxDMALinks[] = { + {2, 2, 3}, // SPI1 Rx is DMA2 Stream 2 Channel 3 + {1, 3, 0}, // SPI2 Rx is DMA1 Stream 3 Channel 0 + {1, 0, 0}, // SPI3 Rx is DMA1 Stream 0 Channel 0 + {2, 0, 4}, // SPI4 Rx is DMA2 Stream 0 Channel 4 + {2, 5, 7}, // SPI5 Rx is DMA2 Stream 5 Channel 7 + {2, 6, 1}, // SPI6 Rx is DMA2 Stream 6 Channel 1 +}; + +#endif //MBED_OS_STM_DMA_INFO_H diff --git a/targets/TARGET_STM/TARGET_STM32F7/STM32Cube_FW/STM32F7xx_HAL_Driver/stm32f7xx_hal_dma.c b/targets/TARGET_STM/TARGET_STM32F7/STM32Cube_FW/STM32F7xx_HAL_Driver/stm32f7xx_hal_dma.c index 9b3abe3e9d..c9568339fa 100644 --- a/targets/TARGET_STM/TARGET_STM32F7/STM32Cube_FW/STM32F7xx_HAL_Driver/stm32f7xx_hal_dma.c +++ b/targets/TARGET_STM/TARGET_STM32F7/STM32Cube_FW/STM32F7xx_HAL_Driver/stm32f7xx_hal_dma.c @@ -479,8 +479,19 @@ HAL_StatusTypeDef HAL_DMA_Start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, /* Enable Common interrupts*/ hdma->Instance->CR |= DMA_IT_TC | DMA_IT_TE | DMA_IT_DME; - hdma->Instance->FCR |= DMA_IT_FE; - + + /* Mbed CE mod: Only enable the FIFO Error interrupt if the FIFO is actually enabled. + * If it's not enabled, then this interrupt can trigger spuriously from memory bus + * stalls that the DMA engine encounters, and this creates random DMA failures. + * Reference forum thread here: + * https://community.st.com/t5/stm32-mcus-products/spi-dma-fifo-error-issue-feifx/td-p/537074 + * also: https://community.st.com/t5/stm32-mcus-touch-gfx-and-gui/spi-dma-error-is-occurred-when-the-other-dma-memory-to-memory-is/td-p/191590 + */ + if(hdma->Instance->FCR & DMA_SxFCR_DMDIS) + { + hdma->Instance->FCR |= DMA_IT_FE; + } + if(hdma->XferHalfCpltCallback != NULL) { hdma->Instance->CR |= DMA_IT_HT; diff --git a/targets/TARGET_STM/TARGET_STM32F7/STM32Cube_FW/STM32F7xx_HAL_Driver/stm32f7xx_hal_dma_ex.c b/targets/TARGET_STM/TARGET_STM32F7/STM32Cube_FW/STM32F7xx_HAL_Driver/stm32f7xx_hal_dma_ex.c index cb96197b9a..1fddde8f2c 100644 --- a/targets/TARGET_STM/TARGET_STM32F7/STM32Cube_FW/STM32F7xx_HAL_Driver/stm32f7xx_hal_dma_ex.c +++ b/targets/TARGET_STM/TARGET_STM32F7/STM32Cube_FW/STM32F7xx_HAL_Driver/stm32f7xx_hal_dma_ex.c @@ -197,7 +197,18 @@ HAL_StatusTypeDef HAL_DMAEx_MultiBufferStart_IT(DMA_HandleTypeDef *hdma, uint32_ /* Enable Common interrupts*/ hdma->Instance->CR |= DMA_IT_TC | DMA_IT_TE | DMA_IT_DME; - hdma->Instance->FCR |= DMA_IT_FE; + + /* Mbed CE mod: Only enable the FIFO Error interrupt if the FIFO is actually enabled. + * If it's not enabled, then this interrupt can trigger spuriously from memory bus + * stalls that the DMA engine encounters, and this creates random DMA failures. + * Reference forum thread here: + * https://community.st.com/t5/stm32-mcus-products/spi-dma-fifo-error-issue-feifx/td-p/537074 + * also: https://community.st.com/t5/stm32-mcus-touch-gfx-and-gui/spi-dma-error-is-occurred-when-the-other-dma-memory-to-memory-is/td-p/191590 + */ + if(hdma->Instance->FCR & DMA_SxFCR_DMDIS) + { + hdma->Instance->FCR |= DMA_IT_FE; + } if((hdma->XferHalfCpltCallback != NULL) || (hdma->XferM1HalfCpltCallback != NULL)) { diff --git a/targets/TARGET_STM/TARGET_STM32F7/objects.h b/targets/TARGET_STM/TARGET_STM32F7/objects.h index ed253af45d..b311d1f488 100644 --- a/targets/TARGET_STM/TARGET_STM32F7/objects.h +++ b/targets/TARGET_STM/TARGET_STM32F7/objects.h @@ -75,20 +75,6 @@ struct pwmout_s { uint8_t inverted; }; -struct spi_s { - SPI_HandleTypeDef handle; - IRQn_Type spiIRQ; - SPIName spi; - PinName pin_miso; - PinName pin_mosi; - PinName pin_sclk; - PinName pin_ssel; -#if DEVICE_SPI_ASYNCH - uint32_t event; - uint8_t transfer_type; -#endif -}; - struct serial_s { UARTName uart; int index; // Used by irq diff --git a/targets/TARGET_STM/TARGET_STM32F7/spi_device.h b/targets/TARGET_STM/TARGET_STM32F7/spi_device.h index b614b042b6..84168d94cd 100644 --- a/targets/TARGET_STM/TARGET_STM32F7/spi_device.h +++ b/targets/TARGET_STM/TARGET_STM32F7/spi_device.h @@ -35,4 +35,7 @@ // Defines the word legnth capability of the device where Nth bit allows for N window size #define STM32_SPI_CAPABILITY_WORD_LENGTH (0x0000FFF8) +// We have DMA support +#define STM32_SPI_CAPABILITY_DMA 1 + #endif diff --git a/targets/TARGET_STM/TARGET_STM32F7/stm_dma_info.h b/targets/TARGET_STM/TARGET_STM32F7/stm_dma_info.h new file mode 100644 index 0000000000..137f854d69 --- /dev/null +++ b/targets/TARGET_STM/TARGET_STM32F7/stm_dma_info.h @@ -0,0 +1,51 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM_DMA_INFO_H +#define MBED_OS_STM_DMA_INFO_H + +#include "cmsis.h" +#include "stm_dma_utils.h" + +// See STM32F7 reference manual Tables 27 and 28. +// Note: For each SPI and each direction, there are two possible assignments to a DMA channel. +// We need to assign them here so that no combination would ever conflict and use the same DMA channel. + +// Exception: SPI5 and SPI6 use the same DMA channels in hardware and there's no way to deconflict them. +// So, SPI5 and SPI6 cannot be used with DMA at the same time. + +/// Mapping from SPI index to DMA link info for Tx +static const DMALinkInfo SPITxDMALinks[] = { + {2, 3, 3}, // SPI1 Tx is DMA2 Stream 3 Channel 3 + {1, 4, 0}, // SPI2 Tx is DMA1 Stream 4 Channel 0 + {1, 5, 0}, // SPI3 Tx is DMA1 Stream 5 Channel 0 + {2, 1, 4}, // SPI4 Tx is DMA2 Stream 1 Channel 4 + {2, 6, 7}, // SPI5 Tx is DMA2 Stream 6 Channel 7 + {2, 5, 1}, // SPI6 Tx is DMA2 Stream 5 Channel 1 +}; + +/// Mapping from SPI index to DMA link info for Rx +static const DMALinkInfo SPIRxDMALinks[] = { + {2, 2, 3}, // SPI1 Rx is DMA2 Stream 2 Channel 3 + {1, 3, 0}, // SPI2 Rx is DMA1 Stream 3 Channel 0 + {1, 0, 0}, // SPI3 Rx is DMA1 Stream 0 Channel 0 + {2, 0, 4}, // SPI4 Rx is DMA2 Stream 0 Channel 4 + {2, 5, 7}, // SPI5 Rx is DMA2 Stream 5 Channel 7 + {2, 6, 1}, // SPI6 Rx is DMA2 Stream 6 Channel 1 +}; + +#endif //MBED_OS_STM_DMA_INFO_H diff --git a/targets/TARGET_STM/TARGET_STM32G0/objects.h b/targets/TARGET_STM/TARGET_STM32G0/objects.h index 0621162292..1d8672a308 100644 --- a/targets/TARGET_STM/TARGET_STM32G0/objects.h +++ b/targets/TARGET_STM/TARGET_STM32G0/objects.h @@ -56,20 +56,6 @@ struct pwmout_s { uint8_t inverted; }; -struct spi_s { - SPI_HandleTypeDef handle; - IRQn_Type spiIRQ; - SPIName spi; - PinName pin_miso; - PinName pin_mosi; - PinName pin_sclk; - PinName pin_ssel; -#if DEVICE_SPI_ASYNCH - uint32_t event; - uint8_t transfer_type; -#endif -}; - struct serial_s { UARTName uart; int index; // Used by irq diff --git a/targets/TARGET_STM/TARGET_STM32G0/stm_dma_info.h b/targets/TARGET_STM/TARGET_STM32G0/stm_dma_info.h new file mode 100644 index 0000000000..419f2f806b --- /dev/null +++ b/targets/TARGET_STM/TARGET_STM32G0/stm_dma_info.h @@ -0,0 +1,52 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM_DMA_INFO_H +#define MBED_OS_STM_DMA_INFO_H + +#include "cmsis.h" +#include "stm_dma_utils.h" + +// STM32G0 devices, with DMAMUX feature. +// On this device, the DMA channels may be chosen arbitrarily. + +/// Mapping from SPI index to DMA link info for Tx +static const DMALinkInfo SPITxDMALinks[] = { + {1, 1, DMA_REQUEST_SPI1_TX}, + {1, 3, DMA_REQUEST_SPI2_TX}, +#ifdef DMA2 + // For better performance, on devices with DMA2 (STM32G0Bxx/Cxx), put SPI3 on DMA2 + {2, 1, DMA_REQUEST_SPI3_TX} +#else + {1, 5, DMA_REQUEST_SPI3_TX} +#endif +}; + +/// Mapping from SPI index to DMA link info for Rx +static const DMALinkInfo SPIRxDMALinks[] = { + {1, 2, DMA_REQUEST_SPI1_RX}, + {1, 4, DMA_REQUEST_SPI2_RX}, +#ifdef DMA2 + // For better performance, on devices with DMA2 (STM32G0Bxx/Cxx), put SPI3 on DMA2 + {2, 2, DMA_REQUEST_SPI3_RX} +#else + {1, 6, DMA_REQUEST_SPI3_RX} +#endif +}; + + +#endif //MBED_OS_STM_DMA_INFO_H diff --git a/targets/TARGET_STM/TARGET_STM32G4/objects.h b/targets/TARGET_STM/TARGET_STM32G4/objects.h index 69db11a34e..368ec3c87f 100644 --- a/targets/TARGET_STM/TARGET_STM32G4/objects.h +++ b/targets/TARGET_STM/TARGET_STM32G4/objects.h @@ -55,20 +55,6 @@ struct pwmout_s { uint8_t inverted; }; -struct spi_s { - SPI_HandleTypeDef handle; - IRQn_Type spiIRQ; - SPIName spi; - PinName pin_miso; - PinName pin_mosi; - PinName pin_sclk; - PinName pin_ssel; -#if DEVICE_SPI_ASYNCH - uint32_t event; - uint8_t transfer_type; -#endif -}; - struct serial_s { UARTName uart; int index; // Used by irq diff --git a/targets/TARGET_STM/TARGET_STM32G4/spi_device.h b/targets/TARGET_STM/TARGET_STM32G4/spi_device.h index bff958e5cb..dd207f8a47 100644 --- a/targets/TARGET_STM/TARGET_STM32G4/spi_device.h +++ b/targets/TARGET_STM/TARGET_STM32G4/spi_device.h @@ -21,4 +21,7 @@ // Defines the word legnth capability of the device where Nth bit allows for N window size #define STM32_SPI_CAPABILITY_WORD_LENGTH (0x0000FFF8) +// We have DMA support +#define STM32_SPI_CAPABILITY_DMA 1 + #endif diff --git a/targets/TARGET_STM/TARGET_STM32G4/stm_dma_info.h b/targets/TARGET_STM/TARGET_STM32G4/stm_dma_info.h new file mode 100644 index 0000000000..9b7d1a1df6 --- /dev/null +++ b/targets/TARGET_STM/TARGET_STM32G4/stm_dma_info.h @@ -0,0 +1,48 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM_DMA_INFO_H +#define MBED_OS_STM_DMA_INFO_H + +#include "cmsis.h" +#include "stm_dma_utils.h" + +// STM32G4 devices, with DMAMUX feature. +// On this device, the DMA channels may be chosen arbitrarily. + +/// Mapping from SPI index to DMA link info for Tx +static const DMALinkInfo SPITxDMALinks[] = { + {1, 1, DMA_REQUEST_SPI1_TX}, + {1, 3, DMA_REQUEST_SPI2_TX}, + {1, 5, DMA_REQUEST_SPI3_TX}, +#ifdef SPI4 + {2, 1, DMA_REQUEST_SPI4_TX} +#endif +}; + +/// Mapping from SPI index to DMA link info for Rx +static const DMALinkInfo SPIRxDMALinks[] = { + {1, 2, DMA_REQUEST_SPI1_RX}, + {1, 4, DMA_REQUEST_SPI2_RX}, + {1, 6, DMA_REQUEST_SPI3_RX}, +#ifdef SPI4 + {2, 2, DMA_REQUEST_SPI4_RX} +#endif +}; + + +#endif //MBED_OS_STM_DMA_INFO_H diff --git a/targets/TARGET_STM/TARGET_STM32H7/objects.h b/targets/TARGET_STM/TARGET_STM32H7/objects.h index 5f94a8ecd3..8608dce0f2 100644 --- a/targets/TARGET_STM/TARGET_STM32H7/objects.h +++ b/targets/TARGET_STM/TARGET_STM32H7/objects.h @@ -64,20 +64,6 @@ struct pwmout_s { uint8_t inverted; }; -struct spi_s { - SPI_HandleTypeDef handle; - IRQn_Type spiIRQ; - SPIName spi; - PinName pin_miso; - PinName pin_mosi; - PinName pin_sclk; - PinName pin_ssel; -#if DEVICE_SPI_ASYNCH - uint32_t event; - uint8_t transfer_type; -#endif -}; - struct serial_s { UARTName uart; int index; // Used by irq diff --git a/targets/TARGET_STM/TARGET_STM32H7/spi_device.h b/targets/TARGET_STM/TARGET_STM32H7/spi_device.h index a3c8453ea2..120d3eba96 100644 --- a/targets/TARGET_STM/TARGET_STM32H7/spi_device.h +++ b/targets/TARGET_STM/TARGET_STM32H7/spi_device.h @@ -24,4 +24,7 @@ // Defines the word legnth capability of the device where Nth bit allows for N window size #define STM32_SPI_CAPABILITY_WORD_LENGTH (0xFFFFFFF8) +// We have DMA support +#define STM32_SPI_CAPABILITY_DMA 1 + #endif diff --git a/targets/TARGET_STM/TARGET_STM32H7/stm_dma_info.h b/targets/TARGET_STM/TARGET_STM32H7/stm_dma_info.h new file mode 100644 index 0000000000..8a79082419 --- /dev/null +++ b/targets/TARGET_STM/TARGET_STM32H7/stm_dma_info.h @@ -0,0 +1,46 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM_DMA_INFO_H +#define MBED_OS_STM_DMA_INFO_H + +#include "cmsis.h" +#include "stm_dma_utils.h" + +// STM32H7 devices, with DMAMUX feature. +// On this device, the DMA channels may be chosen arbitrarily. + +/// Mapping from SPI index to DMA link info for Tx +static const DMALinkInfo SPITxDMALinks[] = { + {1, 1, DMA_REQUEST_SPI1_TX}, + {1, 3, DMA_REQUEST_SPI2_TX}, + {1, 5, DMA_REQUEST_SPI3_TX}, + {1, 7, DMA_REQUEST_SPI4_TX}, + {2, 1, DMA_REQUEST_SPI5_TX}, +}; + +/// Mapping from SPI index to DMA link info for Rx +static const DMALinkInfo SPIRxDMALinks[] = { + {1, 0, DMA_REQUEST_SPI1_RX}, + {1, 2, DMA_REQUEST_SPI2_RX}, + {1, 4, DMA_REQUEST_SPI3_RX}, + {1, 6, DMA_REQUEST_SPI4_RX}, + {2, 0, DMA_REQUEST_SPI5_RX}, +}; + + +#endif //MBED_OS_STM_DMA_INFO_H diff --git a/targets/TARGET_STM/TARGET_STM32L0/objects.h b/targets/TARGET_STM/TARGET_STM32L0/objects.h index 4becf27ff6..93498419f7 100644 --- a/targets/TARGET_STM/TARGET_STM32L0/objects.h +++ b/targets/TARGET_STM/TARGET_STM32L0/objects.h @@ -58,20 +58,6 @@ struct pwmout_s { uint8_t inverted; }; -struct spi_s { - SPI_HandleTypeDef handle; - IRQn_Type spiIRQ; - SPIName spi; - PinName pin_miso; - PinName pin_mosi; - PinName pin_sclk; - PinName pin_ssel; -#if DEVICE_SPI_ASYNCH - uint32_t event; - uint8_t transfer_type; -#endif -}; - struct serial_s { UARTName uart; int index; // Used by irq diff --git a/targets/TARGET_STM/TARGET_STM32L0/spi_device.h b/targets/TARGET_STM/TARGET_STM32L0/spi_device.h index 34915bf9d6..96669eadfa 100644 --- a/targets/TARGET_STM/TARGET_STM32L0/spi_device.h +++ b/targets/TARGET_STM/TARGET_STM32L0/spi_device.h @@ -20,4 +20,7 @@ // Defines the word legnth capability of the device where Nth bit allows for N window size #define STM32_SPI_CAPABILITY_WORD_LENGTH (0x00008080) +// We have DMA support +#define STM32_SPI_CAPABILITY_DMA 1 + #endif diff --git a/targets/TARGET_STM/TARGET_STM32L0/stm_dma_info.h b/targets/TARGET_STM/TARGET_STM32L0/stm_dma_info.h new file mode 100644 index 0000000000..822ca4a687 --- /dev/null +++ b/targets/TARGET_STM/TARGET_STM32L0/stm_dma_info.h @@ -0,0 +1,38 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM_DMA_INFO_H +#define MBED_OS_STM_DMA_INFO_H + +#include "cmsis.h" +#include "stm_dma_utils.h" + +// See STM32L0 reference manual Table 51 + +/// Mapping from SPI index to DMA link info for Tx +static const DMALinkInfo SPITxDMALinks[] = { + {1, 3, 1}, // SPI1 Tx is DMA1 Channel 3 Request 1 + {1, 5, 2}, // SPI2 Tx is DMA1 Channel 5 Request 2 +}; + +/// Mapping from SPI index to DMA link info for Rx +static const DMALinkInfo SPIRxDMALinks[] = { + {1, 2, 1}, // SPI1 Rx is DMA1 Channel 2 Request 1 + {1, 4, 2}, // SPI2 Tx is DMA1 Channel 4 Request 2 +}; + +#endif //MBED_OS_STM_DMA_INFO_H diff --git a/targets/TARGET_STM/TARGET_STM32L1/objects.h b/targets/TARGET_STM/TARGET_STM32L1/objects.h index 148be365af..d98642b56b 100644 --- a/targets/TARGET_STM/TARGET_STM32L1/objects.h +++ b/targets/TARGET_STM/TARGET_STM32L1/objects.h @@ -76,20 +76,6 @@ struct serial_s { #endif }; -struct spi_s { - SPI_HandleTypeDef handle; - IRQn_Type spiIRQ; - SPIName spi; - PinName pin_miso; - PinName pin_mosi; - PinName pin_sclk; - PinName pin_ssel; -#if DEVICE_SPI_ASYNCH - uint32_t event; - uint8_t transfer_type; -#endif -}; - struct i2c_s { /* The 1st 2 members I2CName i2c * and I2C_HandleTypeDef handle should diff --git a/targets/TARGET_STM/TARGET_STM32L1/spi_device.h b/targets/TARGET_STM/TARGET_STM32L1/spi_device.h index 133918fd03..a81674faee 100644 --- a/targets/TARGET_STM/TARGET_STM32L1/spi_device.h +++ b/targets/TARGET_STM/TARGET_STM32L1/spi_device.h @@ -21,4 +21,7 @@ // Defines the word legnth capability of the device where Nth bit allows for N window size #define STM32_SPI_CAPABILITY_WORD_LENGTH (0x00008080) +// We have DMA support +#define STM32_SPI_CAPABILITY_DMA 1 + #endif diff --git a/targets/TARGET_STM/TARGET_STM32L1/stm_dma_info.h b/targets/TARGET_STM/TARGET_STM32L1/stm_dma_info.h new file mode 100644 index 0000000000..d5305ad2df --- /dev/null +++ b/targets/TARGET_STM/TARGET_STM32L1/stm_dma_info.h @@ -0,0 +1,40 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM_DMA_INFO_H +#define MBED_OS_STM_DMA_INFO_H + +#include "cmsis.h" +#include "stm_dma_utils.h" + +// See STM32L1 reference manual Tables 55 and 56 + +/// Mapping from SPI index to DMA link info for Tx +static const DMALinkInfo SPITxDMALinks[] = { + {1, 3}, // SPI1 Tx is DMA1 Channel 3 + {1, 5}, // SPI2 Tx is DMA1 Channel 5 + {2, 2}, // SPI3 Tx is DMA2 Channel 2 +}; + +/// Mapping from SPI index to DMA link info for Rx +static const DMALinkInfo SPIRxDMALinks[] = { + {1, 2}, // SPI1 Rx is DMA1 Channel 2 + {1, 4}, // SPI2 Rx is DMA1 Channel 4 + {2, 1}, // SPI3 Rx is DMA2 Channel 1 +}; + +#endif //MBED_OS_STM_DMA_INFO_H diff --git a/targets/TARGET_STM/TARGET_STM32L4/objects.h b/targets/TARGET_STM/TARGET_STM32L4/objects.h index d41e927384..8f1e182cf9 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/objects.h +++ b/targets/TARGET_STM/TARGET_STM32L4/objects.h @@ -54,20 +54,6 @@ struct pwmout_s { uint8_t inverted; }; -struct spi_s { - SPI_HandleTypeDef handle; - IRQn_Type spiIRQ; - SPIName spi; - PinName pin_miso; - PinName pin_mosi; - PinName pin_sclk; - PinName pin_ssel; -#if DEVICE_SPI_ASYNCH - uint32_t event; - uint8_t transfer_type; -#endif -}; - struct serial_s { UARTName uart; int index; // Used by irq diff --git a/targets/TARGET_STM/TARGET_STM32L4/spi_device.h b/targets/TARGET_STM/TARGET_STM32L4/spi_device.h index 6cc66275b0..7786f1bbbd 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/spi_device.h +++ b/targets/TARGET_STM/TARGET_STM32L4/spi_device.h @@ -23,4 +23,7 @@ // Defines the word legnth capability of the device where Nth bit allows for N window size #define STM32_SPI_CAPABILITY_WORD_LENGTH (0x0000FFF8) +// We have DMA support +#define STM32_SPI_CAPABILITY_DMA 1 + #endif diff --git a/targets/TARGET_STM/TARGET_STM32L4/stm_dma_info.h b/targets/TARGET_STM/TARGET_STM32L4/stm_dma_info.h new file mode 100644 index 0000000000..7a592a572f --- /dev/null +++ b/targets/TARGET_STM/TARGET_STM32L4/stm_dma_info.h @@ -0,0 +1,65 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM_DMA_INFO_H +#define MBED_OS_STM_DMA_INFO_H + +#include "cmsis.h" +#include "stm_dma_utils.h" + +#ifdef DMAMUX1 + +// STM32L4+ devices, with DMAMUX feature. +// On this device, the DMA channels may be chosen arbitrarily. + +/// Mapping from SPI index to DMA link info for Tx +static const DMALinkInfo SPITxDMALinks[] = { + {1, 1, DMA_REQUEST_SPI1_TX}, + {1, 3, DMA_REQUEST_SPI2_TX}, + {1, 5, DMA_REQUEST_SPI3_TX} +}; + +/// Mapping from SPI index to DMA link info for Rx +static const DMALinkInfo SPIRxDMALinks[] = { + {1, 2, DMA_REQUEST_SPI1_RX}, + {1, 4, DMA_REQUEST_SPI2_RX}, + {1, 6, DMA_REQUEST_SPI3_RX} +}; + +#else + + +// Base model STM32L4 devices, with fixed DMA line mapping +// See STM32L4 reference manual Tables 41 and 42. + +/// Mapping from SPI index to DMA link info for Tx +static const DMALinkInfo SPITxDMALinks[] = { + {1, 3, 1}, // SPI1 Tx is DMA1 Ch3 Request 1 + {1, 5, 1}, // SPI2 Tx is DMA1 Ch5 Request 1 + {2, 2, 3} // SPI3 Tx is DMA2 Ch2 Request 3 +}; + +/// Mapping from SPI index to DMA link info for Rx +static const DMALinkInfo SPIRxDMALinks[] = { + {1, 2, 1}, // SPI1 Rx is DMA1 Ch2 Request 1 + {1, 4, 1}, // SPI2 Rx is DMA1 Ch4 Request 1 + {2, 1, 3} // SPI3 Rx is DMA2 Ch1 Request 3 +}; + +#endif + +#endif //MBED_OS_STM_DMA_INFO_H diff --git a/targets/TARGET_STM/TARGET_STM32L5/objects.h b/targets/TARGET_STM/TARGET_STM32L5/objects.h index ed7d2a8dbe..ac1c39fd01 100644 --- a/targets/TARGET_STM/TARGET_STM32L5/objects.h +++ b/targets/TARGET_STM/TARGET_STM32L5/objects.h @@ -64,20 +64,6 @@ struct pwmout_s { uint8_t inverted; }; -struct spi_s { - SPI_HandleTypeDef handle; - IRQn_Type spiIRQ; - SPIName spi; - PinName pin_miso; - PinName pin_mosi; - PinName pin_sclk; - PinName pin_ssel; -#if DEVICE_SPI_ASYNCH - uint32_t event; - uint8_t transfer_type; -#endif -}; - struct serial_s { UARTName uart; int index; // Used by irq diff --git a/targets/TARGET_STM/TARGET_STM32L5/spi_device.h b/targets/TARGET_STM/TARGET_STM32L5/spi_device.h index 72461eab85..e261bc333b 100644 --- a/targets/TARGET_STM/TARGET_STM32L5/spi_device.h +++ b/targets/TARGET_STM/TARGET_STM32L5/spi_device.h @@ -21,4 +21,7 @@ // Defines the word legnth capability of the device where Nth bit allows for N window size #define STM32_SPI_CAPABILITY_WORD_LENGTH (0x0000FFF8) +// We have DMA support +#define STM32_SPI_CAPABILITY_DMA 1 + #endif diff --git a/targets/TARGET_STM/TARGET_STM32L5/stm_dma_info.h b/targets/TARGET_STM/TARGET_STM32L5/stm_dma_info.h new file mode 100644 index 0000000000..46ade14d37 --- /dev/null +++ b/targets/TARGET_STM/TARGET_STM32L5/stm_dma_info.h @@ -0,0 +1,42 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM_DMA_INFO_H +#define MBED_OS_STM_DMA_INFO_H + +#include "cmsis.h" +#include "stm_dma_utils.h" + +// STM32L5 devices, with DMAMUX feature. +// On this device, the DMA channels may be chosen arbitrarily. + +/// Mapping from SPI index to DMA link info for Tx +static const DMALinkInfo SPITxDMALinks[] = { + {1, 1, DMA_REQUEST_SPI1_TX}, + {1, 3, DMA_REQUEST_SPI2_TX}, + {1, 5, DMA_REQUEST_SPI3_TX}, +}; + +/// Mapping from SPI index to DMA link info for Rx +static const DMALinkInfo SPIRxDMALinks[] = { + {1, 2, DMA_REQUEST_SPI1_RX}, + {1, 4, DMA_REQUEST_SPI2_RX}, + {1, 6, DMA_REQUEST_SPI3_RX}, +}; + + +#endif //MBED_OS_STM_DMA_INFO_H diff --git a/targets/TARGET_STM/TARGET_STM32U5/objects.h b/targets/TARGET_STM/TARGET_STM32U5/objects.h index 69e9c5bc21..dd46048ac7 100644 --- a/targets/TARGET_STM/TARGET_STM32U5/objects.h +++ b/targets/TARGET_STM/TARGET_STM32U5/objects.h @@ -64,20 +64,6 @@ struct pwmout_s { uint8_t inverted; }; -struct spi_s { - SPI_HandleTypeDef handle; - IRQn_Type spiIRQ; - SPIName spi; - PinName pin_miso; - PinName pin_mosi; - PinName pin_sclk; - PinName pin_ssel; -#if DEVICE_SPI_ASYNCH - uint32_t event; - uint8_t transfer_type; -#endif -}; - struct serial_s { UARTName uart; int index; // Used by irq diff --git a/targets/TARGET_STM/TARGET_STM32U5/spi_device.h b/targets/TARGET_STM/TARGET_STM32U5/spi_device.h index 9bfdff7294..482d872b20 100644 --- a/targets/TARGET_STM/TARGET_STM32U5/spi_device.h +++ b/targets/TARGET_STM/TARGET_STM32U5/spi_device.h @@ -23,4 +23,7 @@ // Defines the word legnth capability of the device where Nth bit allows for N window size #define STM32_SPI_CAPABILITY_WORD_LENGTH (0x0000FFF8) +// We have DMA support +#define STM32_SPI_CAPABILITY_DMA 1 + #endif diff --git a/targets/TARGET_STM/TARGET_STM32U5/stm_dma_info.h b/targets/TARGET_STM/TARGET_STM32U5/stm_dma_info.h new file mode 100644 index 0000000000..8d170f3552 --- /dev/null +++ b/targets/TARGET_STM/TARGET_STM32U5/stm_dma_info.h @@ -0,0 +1,42 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM_DMA_INFO_H +#define MBED_OS_STM_DMA_INFO_H + +#include "cmsis.h" +#include "stm_dma_utils.h" + +// STM32U5+ devices. +// On this device, the DMA channels may be chosen arbitrarily. + +/// Mapping from SPI index to DMA link info for Tx +static const DMALinkInfo SPITxDMALinks[] = { + {1, 0, GPDMA1_REQUEST_SPI1_TX}, + {1, 2, GPDMA1_REQUEST_SPI2_TX}, + {1, 4, GPDMA1_REQUEST_SPI3_TX} +}; + +/// Mapping from SPI index to DMA link info for Rx +static const DMALinkInfo SPIRxDMALinks[] = { + {1, 1, GPDMA1_REQUEST_SPI1_RX}, + {1, 3, GPDMA1_REQUEST_SPI2_RX}, + {1, 5, GPDMA1_REQUEST_SPI3_TX} +}; + + +#endif //MBED_OS_STM_DMA_INFO_H diff --git a/targets/TARGET_STM/TARGET_STM32WB/objects.h b/targets/TARGET_STM/TARGET_STM32WB/objects.h index a3b501e896..65eb19e7a2 100644 --- a/targets/TARGET_STM/TARGET_STM32WB/objects.h +++ b/targets/TARGET_STM/TARGET_STM32WB/objects.h @@ -47,20 +47,6 @@ struct pwmout_s { uint8_t inverted; }; -struct spi_s { - SPI_HandleTypeDef handle; - IRQn_Type spiIRQ; - SPIName spi; - PinName pin_miso; - PinName pin_mosi; - PinName pin_sclk; - PinName pin_ssel; -#if DEVICE_SPI_ASYNCH - uint32_t event; - uint8_t transfer_type; -#endif -}; - struct serial_s { UARTName uart; int index; // Used by irq diff --git a/targets/TARGET_STM/TARGET_STM32WB/spi_device.h b/targets/TARGET_STM/TARGET_STM32WB/spi_device.h index 7f8f7b4d3e..0c287ce677 100644 --- a/targets/TARGET_STM/TARGET_STM32WB/spi_device.h +++ b/targets/TARGET_STM/TARGET_STM32WB/spi_device.h @@ -23,4 +23,7 @@ // Defines the word legnth capability of the device where Nth bit allows for N window size #define STM32_SPI_CAPABILITY_WORD_LENGTH (0x0000FFF8) +// We have DMA support +#define STM32_SPI_CAPABILITY_DMA 1 + #endif diff --git a/targets/TARGET_STM/TARGET_STM32WB/stm_dma_info.h b/targets/TARGET_STM/TARGET_STM32WB/stm_dma_info.h new file mode 100644 index 0000000000..4a73a48e4b --- /dev/null +++ b/targets/TARGET_STM/TARGET_STM32WB/stm_dma_info.h @@ -0,0 +1,44 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM_DMA_INFO_H +#define MBED_OS_STM_DMA_INFO_H + +#include "cmsis.h" +#include "stm_dma_utils.h" + +// STM32WB devices, with DMAMUX feature. +// On this device, the DMA channels may be chosen arbitrarily. + +/// Mapping from SPI index to DMA link info for Tx +static const DMALinkInfo SPITxDMALinks[] = { + {1, 1, DMA_REQUEST_SPI1_TX}, +#ifdef SPI2 + {1, 3, DMA_REQUEST_SPI2_TX} +#endif +}; + +/// Mapping from SPI index to DMA link info for Rx +static const DMALinkInfo SPIRxDMALinks[] = { + {1, 2, DMA_REQUEST_SPI1_RX}, +#ifdef SPI4 + {1, 4, DMA_REQUEST_SPI2_RX} +#endif +}; + + +#endif //MBED_OS_STM_DMA_INFO_H diff --git a/targets/TARGET_STM/TARGET_STM32WL/objects.h b/targets/TARGET_STM/TARGET_STM32WL/objects.h index d3c601b351..780971d2d9 100644 --- a/targets/TARGET_STM/TARGET_STM32WL/objects.h +++ b/targets/TARGET_STM/TARGET_STM32WL/objects.h @@ -50,20 +50,6 @@ struct pwmout_s { uint8_t inverted; }; -struct spi_s { - SPI_HandleTypeDef handle; - IRQn_Type spiIRQ; - SPIName spi; - PinName pin_miso; - PinName pin_mosi; - PinName pin_sclk; - PinName pin_ssel; -#if DEVICE_SPI_ASYNCH - uint32_t event; - uint8_t transfer_type; -#endif -}; - struct serial_s { UARTName uart; int index; // Used by irq diff --git a/targets/TARGET_STM/TARGET_STM32WL/spi_device.h b/targets/TARGET_STM/TARGET_STM32WL/spi_device.h index 6159bba817..1cfc215315 100644 --- a/targets/TARGET_STM/TARGET_STM32WL/spi_device.h +++ b/targets/TARGET_STM/TARGET_STM32WL/spi_device.h @@ -21,4 +21,7 @@ // Defines the word legnth capability of the device where Nth bit allows for N window size #define STM32_SPI_CAPABILITY_WORD_LENGTH (0x0000FFF8) +// We have DMA support +#define STM32_SPI_CAPABILITY_DMA 1 + #endif diff --git a/targets/TARGET_STM/TARGET_STM32WL/stm_dma_info.h b/targets/TARGET_STM/TARGET_STM32WL/stm_dma_info.h new file mode 100644 index 0000000000..a819d8b942 --- /dev/null +++ b/targets/TARGET_STM/TARGET_STM32WL/stm_dma_info.h @@ -0,0 +1,40 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_OS_STM_DMA_INFO_H +#define MBED_OS_STM_DMA_INFO_H + +#include "cmsis.h" +#include "stm_dma_utils.h" + +// STM32WB devices, with DMAMUX feature. +// On this device, the DMA channels may be chosen arbitrarily. + +/// Mapping from SPI index to DMA link info for Tx +static const DMALinkInfo SPITxDMALinks[] = { + {1, 1, DMA_REQUEST_SPI1_TX}, + {1, 3, DMA_REQUEST_SPI2_TX} +}; + +/// Mapping from SPI index to DMA link info for Rx +static const DMALinkInfo SPIRxDMALinks[] = { + {1, 2, DMA_REQUEST_SPI1_RX}, + {1, 4, DMA_REQUEST_SPI2_RX} +}; + + +#endif //MBED_OS_STM_DMA_INFO_H diff --git a/targets/TARGET_STM/device.h b/targets/TARGET_STM/device.h index b30041afd1..ff2c27bdfb 100644 --- a/targets/TARGET_STM/device.h +++ b/targets/TARGET_STM/device.h @@ -37,6 +37,7 @@ #include "objects.h" #include "stm_i2c_api.h" +#include "stm_spi_api.h" #if DEVICE_USTICKER #include "us_ticker_defines.h" diff --git a/targets/TARGET_STM/stm_dma_ip_v1.h b/targets/TARGET_STM/stm_dma_ip_v1.h new file mode 100644 index 0000000000..4ebac97a64 --- /dev/null +++ b/targets/TARGET_STM/stm_dma_ip_v1.h @@ -0,0 +1,53 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This header contains constants and defines specific to processors with the v1 DMA IP. + * The v1 IP has DMA controllers with multiple streams, where each "stream" has a "channel selection" + * to determine what triggers DMA requests. + */ + +#ifndef MBED_OS_STM_DMA_IP_V1_H +#define MBED_OS_STM_DMA_IP_V1_H + +// Devices with DMA IP v1 have at most 8 channels per controller. +#define MAX_DMA_CHANNELS_PER_CONTROLLER 8 + +// Count DMA controllers +#ifdef DMA1 +#ifdef DMA2 +#define NUM_DMA_CONTROLLERS 2 +#else +#define NUM_DMA_CONTROLLERS 1 +#endif +#else +#define NUM_DMA_CONTROLLERS 0 +#endif + +// Provide an alias so that code can always use the v2 name for this structure +#define DMA_Channel_TypeDef DMA_Stream_TypeDef + +// On some smaller devices, e.g. STM32L1 family, DMA channels are simply logically ORed rather than +// muxed, so we don't need the "sourceNumber" field. +// We can check if this is the case by the absence of specific peripherals/registers. +#if defined(DMAMUX1_BASE) || defined(DMA_SxCR_CHSEL_Msk) +#define STM_DEVICE_HAS_DMA_SOURCE_SELECTION 1 +#else +#define STM_DEVICE_HAS_DMA_SOURCE_SELECTION 0 +#endif + +#endif //MBED_OS_STM_DMA_IP_V1_H diff --git a/targets/TARGET_STM/stm_dma_ip_v2.h b/targets/TARGET_STM/stm_dma_ip_v2.h new file mode 100644 index 0000000000..8b96e4e7ad --- /dev/null +++ b/targets/TARGET_STM/stm_dma_ip_v2.h @@ -0,0 +1,61 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This header contains constants and defines specific to processors with the v2 DMA IP. + * + * The v2 DMA IP has DMA controllers with multiple channels, where each channel has a request source + * that determines what triggers DMA transactions + */ + +#ifndef MBED_OS_STM_DMA_IP_V2_H +#define MBED_OS_STM_DMA_IP_V2_H + +#ifdef TARGET_MCU_STM32F0 + +// STM32F0 is weird and does its own thing. +// Only 5 channels usable, the other 2 lack interrupts +#define MAX_DMA_CHANNELS_PER_CONTROLLER 5 + +#else + +// Devices with DMA IP v2 have at most 7 channels per controller. +#define MAX_DMA_CHANNELS_PER_CONTROLLER 7 + +#endif + +// Count DMA controllers +#ifdef DMA1 +#ifdef DMA2 +#define NUM_DMA_CONTROLLERS 2 +#else +#define NUM_DMA_CONTROLLERS 1 +#endif +#else +#define NUM_DMA_CONTROLLERS 0 +#endif + +// On some smaller devices, e.g. STM32L1 family, DMA channels are simply logically ORed rather than +// muxed, so we don't need the "sourceNumber" field. +// We can check if this is the case by the absence of specific peripherals/registers. +#if defined(DMA1_CSELR) || defined(DMAMUX1_BASE) +#define STM_DEVICE_HAS_DMA_SOURCE_SELECTION 1 +#else +#define STM_DEVICE_HAS_DMA_SOURCE_SELECTION 0 +#endif + +#endif //MBED_OS_STM_DMA_IP_V2_H diff --git a/targets/TARGET_STM/stm_dma_ip_v3.h b/targets/TARGET_STM/stm_dma_ip_v3.h new file mode 100644 index 0000000000..9de43774ea --- /dev/null +++ b/targets/TARGET_STM/stm_dma_ip_v3.h @@ -0,0 +1,37 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This header contains constants and defines specific to processors with the v3 DMA IP. + * + * The v3 DMA IP has one DMA controller with multiple channels, where each channel has a request source + * that determines what triggers DMA transactions. Any DMA channel can connect to any request source, + * unlike many other STM32 chips. + */ + +#ifndef MBED_OS_STM_DMA_IP_V3_H +#define MBED_OS_STM_DMA_IP_V3_H + +// Devices with DMA IP v3 have at most 16 channels per controller. +#define MAX_DMA_CHANNELS_PER_CONTROLLER 16 + +#define NUM_DMA_CONTROLLERS 1 + +// Currently all known IPv3 devices have source selection +#define STM_DEVICE_HAS_DMA_SOURCE_SELECTION 1 + +#endif //MBED_OS_STM_DMA_IP_V3_H diff --git a/targets/TARGET_STM/stm_dma_utils.c b/targets/TARGET_STM/stm_dma_utils.c new file mode 100644 index 0000000000..a1f6afb48a --- /dev/null +++ b/targets/TARGET_STM/stm_dma_utils.c @@ -0,0 +1,1201 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stm_dma_utils.h" + +#include "mbed_error.h" + +#include +#include +#include + +// Array to store pointer to DMA handle for each DMA channel. +// Note: arrays are 0-indexed, so DMA1 Channel2 is at stmDMAHandles[0][1]. +static DMA_HandleTypeDef * stmDMAHandles[NUM_DMA_CONTROLLERS][MAX_DMA_CHANNELS_PER_CONTROLLER]; + +DMA_Channel_TypeDef * stm_get_dma_channel(const DMALinkInfo *dmaLink) +{ + switch(dmaLink->dmaIdx) + { +#ifdef DMA1 + case 1: + switch(dmaLink->channelIdx) + { +#ifdef DMA1_Channel1 + case 1: + return DMA1_Channel1; +#endif +#ifdef DMA1_Channel2 + case 2: + return DMA1_Channel2; +#endif +#ifdef DMA1_Channel3 + case 3: + return DMA1_Channel3; +#endif +#ifdef DMA1_Channel4 + case 4: + return DMA1_Channel4; +#endif +#ifdef DMA1_Channel5 + case 5: + return DMA1_Channel5; +#endif +#ifdef DMA1_Channel6 + case 6: + return DMA1_Channel6; +#endif +#ifdef DMA1_Channel7 + case 7: + return DMA1_Channel7; +#endif +#ifdef DMA1_Stream0 + case 0: + return DMA1_Stream0; +#endif +#ifdef DMA1_Stream1 + case 1: + return DMA1_Stream1; +#endif +#ifdef DMA1_Stream2 + case 2: + return DMA1_Stream2; +#endif +#ifdef DMA1_Stream3 + case 3: + return DMA1_Stream3; +#endif +#ifdef DMA1_Stream4 + case 4: + return DMA1_Stream4; +#endif +#ifdef DMA1_Stream5 + case 5: + return DMA1_Stream5; +#endif +#ifdef DMA1_Stream6 + case 6: + return DMA1_Stream6; +#endif +#ifdef DMA1_Stream7 + case 7: + return DMA1_Stream7; +#endif + default: + mbed_error(MBED_ERROR_ITEM_NOT_FOUND, "Invalid DMA channel", dmaLink->channelIdx, MBED_FILENAME, __LINE__); + } +#endif + +#ifdef DMA2 + case 2: + switch(dmaLink->channelIdx) + { +#ifdef DMA2_Channel1 + case 1: + return DMA2_Channel1; +#endif +#ifdef DMA2_Channel2 + case 2: + return DMA2_Channel2; +#endif +#ifdef DMA2_Channel3 + case 3: + return DMA2_Channel3; +#endif +#ifdef DMA2_Channel4 + case 4: + return DMA2_Channel4; +#endif +#ifdef DMA2_Channel5 + case 5: + return DMA2_Channel5; +#endif +#ifdef DMA2_Channel6 + case 6: + return DMA2_Channel6; +#endif +#ifdef DMA2_Channel7 + case 7: + return DMA2_Channel7; +#endif +#ifdef DMA2_Stream0 + case 0: + return DMA2_Stream0; +#endif +#ifdef DMA2_Stream1 + case 1: + return DMA2_Stream1; +#endif +#ifdef DMA2_Stream2 + case 2: + return DMA2_Stream2; +#endif +#ifdef DMA2_Stream3 + case 3: + return DMA2_Stream3; +#endif +#ifdef DMA2_Stream4 + case 4: + return DMA2_Stream4; +#endif +#ifdef DMA2_Stream5 + case 5: + return DMA2_Stream5; +#endif +#ifdef DMA2_Stream6 + case 6: + return DMA2_Stream6; +#endif +#ifdef DMA2_Stream7 + case 7: + return DMA2_Stream7; +#endif + default: + mbed_error(MBED_ERROR_ITEM_NOT_FOUND, "Invalid DMA channel", dmaLink->channelIdx, MBED_FILENAME, __LINE__); + } +#endif +#ifdef GPDMA1 + case 1: + switch(dmaLink->channelIdx) + { +#ifdef GPDMA1_Channel0 + case 0: + return GPDMA1_Channel0; +#endif +#ifdef GPDMA1_Channel1 + case 1: + return GPDMA1_Channel1; +#endif +#ifdef GPDMA1_Channel2 + case 2: + return GPDMA1_Channel2; +#endif +#ifdef GPDMA1_Channel3 + case 3: + return GPDMA1_Channel3; +#endif +#ifdef GPDMA1_Channel4 + case 4: + return GPDMA1_Channel4; +#endif +#ifdef GPDMA1_Channel5 + case 5: + return GPDMA1_Channel5; +#endif +#ifdef GPDMA1_Channel6 + case 6: + return GPDMA1_Channel6; +#endif +#ifdef GPDMA1_Channel7 + case 7: + return GPDMA1_Channel7; +#endif +#ifdef GPDMA1_Channel8 + case 8: + return GPDMA1_Channel8; +#endif +#ifdef GPDMA1_Channel9 + case 9: + return GPDMA1_Channel9; +#endif +#ifdef GPDMA1_Channel10 + case 10: + return GPDMA1_Channel10; +#endif +#ifdef GPDMA1_Channel11 + case 11: + return GPDMA1_Channel11; +#endif +#ifdef GPDMA1_Channel12 + case 12: + return GPDMA1_Channel12; +#endif +#ifdef GPDMA1_Channel13 + case 13: + return GPDMA1_Channel13; +#endif +#ifdef GPDMA1_Channel14 + case 14: + return GPDMA1_Channel14; +#endif +#ifdef GPDMA1_Channel15 + case 15: + return GPDMA1_Channel15; +#endif + default: + mbed_error(MBED_ERROR_ITEM_NOT_FOUND, "Invalid DMA channel", dmaLink->channelIdx, MBED_FILENAME, __LINE__); + } +#endif + default: + mbed_error(MBED_ERROR_ITEM_NOT_FOUND, "Invalid DMA controller", dmaLink->dmaIdx, MBED_FILENAME, __LINE__); + + } +} + +IRQn_Type stm_get_dma_irqn(const DMALinkInfo *dmaLink) +{ + switch(dmaLink->dmaIdx) + { +#ifdef DMA1 + case 1: + switch(dmaLink->channelIdx) + { +#ifdef DMA1_Channel1 + case 1: + return DMA1_Channel1_IRQn; +#endif + +// STM32F0 has shared ISRs for Ch2-Ch3 and Ch4-Ch5, and NO ISRs for channels 6 and 7 +#ifdef TARGET_MCU_STM32F0 + case 2: + case 3: + return DMA1_Channel2_3_IRQn; + case 4: + case 5: + return DMA1_Channel4_5_IRQn; + +// STM32G0 has shared ISRs for Ch2-Ch3 and Ch4-Ch7 (and also all DMA2 channels on devices with DMA2) +#elif defined(TARGET_MCU_STM32G0) + case 2: + case 3: + return DMA1_Channel2_3_IRQn; + + // IRQ name for the remaining DMA channels depends on whether DMA2 exists or not + case 4: + case 5: + case 6: + case 7: +#ifdef DMA2 + return DMA1_Ch4_7_DMA2_Ch1_5_DMAMUX1_OVR_IRQn; +#elif defined(DMA1_Channel7) + return DMA1_Ch4_7_DMAMUX1_OVR_IRQn; +#else + return DMA1_Ch4_5_DMAMUX1_OVR_IRQn; +#endif + +// STM32L0 has shared ISRs for Ch2-Ch3 and Ch4-Ch7 +#elif defined(TARGET_MCU_STM32L0) + case 2: + case 3: + return DMA1_Channel2_3_IRQn; + + case 4: + case 5: + case 6: + case 7: + return DMA1_Channel4_5_6_7_IRQn; +#else +#ifdef DMA1_Channel2 + case 2: + return DMA1_Channel2_IRQn; +#endif +#ifdef DMA1_Channel3 + case 3: + return DMA1_Channel3_IRQn; +#endif +#ifdef DMA1_Channel4 + case 4: + return DMA1_Channel4_IRQn; +#endif +#ifdef DMA1_Channel5 + case 5: + return DMA1_Channel5_IRQn; +#endif +#ifdef DMA1_Channel6 + case 6: + return DMA1_Channel6_IRQn; +#endif +#ifdef DMA1_Channel7 + case 7: + return DMA1_Channel7_IRQn; +#endif +#endif + +#ifdef DMA1_Stream0 + case 0: + return DMA1_Stream0_IRQn; +#endif +#ifdef DMA1_Stream1 + case 1: + return DMA1_Stream1_IRQn; +#endif +#ifdef DMA1_Stream2 + case 2: + return DMA1_Stream2_IRQn; +#endif +#ifdef DMA1_Stream3 + case 3: + return DMA1_Stream3_IRQn; +#endif +#ifdef DMA1_Stream4 + case 4: + return DMA1_Stream4_IRQn; +#endif +#ifdef DMA1_Stream5 + case 5: + return DMA1_Stream5_IRQn; +#endif +#ifdef DMA1_Stream6 + case 6: + return DMA1_Stream6_IRQn; +#endif +#ifdef DMA1_Stream7 + case 7: + return DMA1_Stream7_IRQn; +#endif + + default: + mbed_error(MBED_ERROR_ITEM_NOT_FOUND, "Invalid DMA channel", dmaLink->channelIdx, MBED_FILENAME, __LINE__); + } +#endif + +#ifdef DMA2 + case 2: + switch(dmaLink->channelIdx) + { +#ifdef TARGET_MCU_STM32G0 + // STM32G0 does its own thing and has all DMA2 channels under 1 IRQ + case 1: + case 2: + case 3: + case 4: + case 5: + return DMA1_Ch4_7_DMA2_Ch1_5_DMAMUX1_OVR_IRQn; +#else + +#ifdef DMA2_Channel1 + case 1: + return DMA2_Channel1_IRQn; +#endif +#ifdef DMA2_Channel2 + case 2: + return DMA2_Channel2_IRQn; +#endif +#ifdef DMA2_Channel3 + case 3: + return DMA2_Channel3_IRQn; +#endif +#ifdef DMA2_Channel4 + case 4: + return DMA2_Channel4_IRQn; +#endif +#ifdef DMA2_Channel5 + case 5: + return DMA2_Channel5_IRQn; +#endif +#ifdef DMA2_Channel6 + case 6: + return DMA2_Channel6_IRQn; +#endif +#ifdef DMA2_Channel7 + case 7: + return DMA2_Channel7_IRQn; +#endif + +#ifdef DMA2_Stream0 + case 0: + return DMA2_Stream0_IRQn; +#endif +#ifdef DMA2_Stream1 + case 1: + return DMA2_Stream1_IRQn; +#endif +#ifdef DMA2_Stream2 + case 2: + return DMA2_Stream2_IRQn; +#endif +#ifdef DMA2_Stream3 + case 3: + return DMA2_Stream3_IRQn; +#endif +#ifdef DMA2_Stream4 + case 4: + return DMA2_Stream4_IRQn; +#endif +#ifdef DMA2_Stream5 + case 5: + return DMA2_Stream5_IRQn; +#endif +#ifdef DMA2_Stream6 + case 6: + return DMA2_Stream6_IRQn; +#endif +#ifdef DMA2_Stream7 + case 7: + return DMA2_Stream7_IRQn; +#endif +#endif + + default: + mbed_error(MBED_ERROR_ITEM_NOT_FOUND, "Invalid DMA channel", dmaLink->channelIdx, MBED_FILENAME, __LINE__); + } +#endif + +#ifdef GPDMA1 + case 1: + switch(dmaLink->channelIdx) + { +#ifdef GPDMA1_Channel0 + case 0: + return GPDMA1_Channel0_IRQn; +#endif +#ifdef GPDMA1_Channel1 + case 1: + return GPDMA1_Channel1_IRQn; +#endif +#ifdef GPDMA1_Channel2 + case 2: + return GPDMA1_Channel2_IRQn; +#endif +#ifdef GPDMA1_Channel3 + case 3: + return GPDMA1_Channel3_IRQn; +#endif +#ifdef GPDMA1_Channel4 + case 4: + return GPDMA1_Channel4_IRQn; +#endif +#ifdef GPDMA1_Channel5 + case 5: + return GPDMA1_Channel5_IRQn; +#endif +#ifdef GPDMA1_Channel6 + case 6: + return GPDMA1_Channel6_IRQn; +#endif +#ifdef GPDMA1_Channel7 + case 7: + return GPDMA1_Channel7_IRQn; +#endif +#ifdef GPDMA1_Channel8 + case 8: + return GPDMA1_Channel8_IRQn; +#endif +#ifdef GPDMA1_Channel9 + case 9: + return GPDMA1_Channel9_IRQn; +#endif +#ifdef GPDMA1_Channel10 + case 10: + return GPDMA1_Channel10_IRQn; +#endif +#ifdef GPDMA1_Channel11 + case 11: + return GPDMA1_Channel11_IRQn; +#endif +#ifdef GPDMA1_Channel12 + case 12: + return GPDMA1_Channel12_IRQn; +#endif +#ifdef GPDMA1_Channel13 + case 13: + return GPDMA1_Channel13_IRQn; +#endif +#ifdef GPDMA1_Channel14 + case 14: + return GPDMA1_Channel14_IRQn; +#endif +#ifdef GPDMA1_Channel15 + case 15: + return GPDMA1_Channel15_IRQn; +#endif + default: + mbed_error(MBED_ERROR_ITEM_NOT_FOUND, "Invalid DMA channel", dmaLink->channelIdx, MBED_FILENAME, __LINE__); + } +#endif + default: + mbed_error(MBED_ERROR_ITEM_NOT_FOUND, "Invalid DMA controller", dmaLink->dmaIdx, MBED_FILENAME, __LINE__); + + } +} + +DMA_HandleTypeDef *stm_init_dma_link(const DMALinkInfo *dmaLink, uint32_t direction, bool periphInc, bool memInc, + uint8_t periphDataAlignment, uint8_t memDataAlignment){ + +#ifdef DMA_IP_VERSION_V2 + // Channels start from 1 in IP v2 only + uint8_t channelIdx = dmaLink->channelIdx - 1; +#else + uint8_t channelIdx = dmaLink->channelIdx; +#endif + + if(stmDMAHandles[dmaLink->dmaIdx - 1][channelIdx] != NULL) + { + // Channel already allocated (e.g. two SPI busses which use the same DMA request tried to be initialized) + return NULL; + } + + // Enable DMA mux clock for devices with it +#ifdef __HAL_RCC_DMAMUX1_CLK_ENABLE + __HAL_RCC_DMAMUX1_CLK_ENABLE(); +#endif + + // Turn on clock for the DMA module + switch(dmaLink->dmaIdx) + { +#ifdef DMA1 + case 1: + __HAL_RCC_DMA1_CLK_ENABLE(); + break; +#endif +#ifdef DMA2 + case 2: + __HAL_RCC_DMA2_CLK_ENABLE(); + break; +#endif +#ifdef GPDMA1 + case 1: + __HAL_RCC_GPDMA1_CLK_ENABLE(); + break; +#endif +#ifdef BDMA + case 3: + __HAL_RCC_BDMA_CLK_ENABLE(); + break; +#endif + default: + mbed_error(MBED_ERROR_ITEM_NOT_FOUND, "Invalid DMA controller", dmaLink->dmaIdx, MBED_FILENAME, __LINE__); + } + + // Allocate DMA handle. + // Yes it's a little gross that we have to allocate on the heap, but this structure uses quite a lot of memory, + // so we don't want to allocate DMA handles until they're needed. + DMA_HandleTypeDef * dmaHandle = malloc(sizeof(DMA_HandleTypeDef)); + memset(dmaHandle, 0, sizeof(DMA_HandleTypeDef)); + stmDMAHandles[dmaLink->dmaIdx - 1][channelIdx] = dmaHandle; + + // Configure handle + dmaHandle->Instance = stm_get_dma_channel(dmaLink); +#if STM_DEVICE_HAS_DMA_SOURCE_SELECTION + + // Most devices with IP v1 call this member "Channel" and most with IP v2 call it "Request". + // But not STM32H7! +#if defined(DMA_IP_VERSION_V1) && !defined(TARGET_MCU_STM32H7) + dmaHandle->Init.Channel = dmaLink->sourceNumber << DMA_SxCR_CHSEL_Pos; +#else + dmaHandle->Init.Request = dmaLink->sourceNumber; +#endif + +#endif + dmaHandle->Init.Direction = direction; + + // IP v3 uses different fields for... basically everything in this struct +#ifdef DMA_IP_VERSION_V3 + if(direction == DMA_MEMORY_TO_PERIPH || direction == DMA_MEMORY_TO_MEMORY) + { + // Source is memory + dmaHandle->Init.SrcInc = memInc ? DMA_SINC_INCREMENTED : DMA_SINC_FIXED; + + switch(memDataAlignment) { + case 4: + dmaHandle->Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD; + break; + case 2: + dmaHandle->Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD; + break; + case 1: + default: + dmaHandle->Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE; + break; + + } + } + else { + // Source is a peripheral + dmaHandle->Init.SrcInc = periphInc ? DMA_SINC_INCREMENTED : DMA_SINC_FIXED; + + switch(periphDataAlignment) { + case 4: + dmaHandle->Init.SrcDataWidth = DMA_SRC_DATAWIDTH_WORD; + break; + case 2: + dmaHandle->Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD; + break; + case 1: + default: + dmaHandle->Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE; + break; + + } + } + + if(direction == DMA_PERIPH_TO_MEMORY || direction == DMA_MEMORY_TO_MEMORY) + { + // Destination is memory + dmaHandle->Init.DestInc = memInc ? DMA_SINC_INCREMENTED : DMA_SINC_FIXED; + + switch(memDataAlignment) { + case 4: + dmaHandle->Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE; + break; + case 2: + dmaHandle->Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD; + break; + case 1: + default: + dmaHandle->Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE; + break; + + } + } + else { + // Destination is a peripheral + dmaHandle->Init.DestInc = periphInc ? DMA_SINC_INCREMENTED : DMA_SINC_FIXED; + + switch(periphDataAlignment) { + case 4: + dmaHandle->Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE; + break; + case 2: + dmaHandle->Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD; + break; + case 1: + default: + dmaHandle->Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE; + break; + + } + } + + dmaHandle->Init.SrcBurstLength = 1; + dmaHandle->Init.DestBurstLength = 1; + dmaHandle->Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST; + dmaHandle->Init.Priority = DMA_LOW_PRIORITY_HIGH_WEIGHT; + dmaHandle->Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT1|DMA_DEST_ALLOCATED_PORT0; + dmaHandle->Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER; + +#else + dmaHandle->Init.PeriphInc = periphInc ? DMA_PINC_ENABLE : DMA_PINC_DISABLE; + dmaHandle->Init.MemInc = memInc ? DMA_MINC_ENABLE : DMA_MINC_DISABLE; + dmaHandle->Init.Priority = DMA_PRIORITY_MEDIUM; + + switch(periphDataAlignment) { + case 4: + dmaHandle->Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; + break; + case 2: + dmaHandle->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; + break; + case 1: + default: + dmaHandle->Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; + break; + + } + + switch(memDataAlignment) { + case 4: + dmaHandle->Init.MemDataAlignment = DMA_MDATAALIGN_WORD; + break; + case 2: + dmaHandle->Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; + break; + case 1: + default: + dmaHandle->Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; + break; + + } + +#endif + + dmaHandle->Init.Mode = DMA_NORMAL; + + HAL_DMA_Init(dmaHandle); + + // Set up interrupt + IRQn_Type irqNum = stm_get_dma_irqn(dmaLink); + NVIC_EnableIRQ(irqNum); + NVIC_SetPriority(irqNum, 7); + + return dmaHandle; +} + +void stm_free_dma_link(const DMALinkInfo *dmaLink) +{ + // Note: we can't disable the interrupt here, in case one ISR is shared by multiple DMA channels + // and another channel is still using the interrupt. + +#ifdef DMA_IP_VERSION_V2 + // Channels start from 1 in IP v2 only + uint8_t channelIdx = dmaLink->channelIdx - 1; +#else + uint8_t channelIdx = dmaLink->channelIdx; +#endif + + // Deinit hardware channel + HAL_DMA_DeInit(stmDMAHandles[dmaLink->dmaIdx - 1][channelIdx]); + + // Free memory + free(stmDMAHandles[dmaLink->dmaIdx - 1][channelIdx]); + stmDMAHandles[dmaLink->dmaIdx - 1][channelIdx] = NULL; +} + +#ifdef DMA_IP_VERSION_V2 + +#ifdef DMA1_Channel1 +void DMA1_Channel1_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][0]); +} +#endif + +// STM32F0 has shared ISRs for Ch2-Ch3 and Ch4-Ch5 +#ifdef TARGET_MCU_STM32F0 + +void DMA1_Channel2_3_IRQHandler(void) +{ + if(stmDMAHandles[0][1] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][1]); + } + if(stmDMAHandles[0][2] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][2]); + } +} + +void DMA1_Channel4_5_IRQHandler(void) +{ + if(stmDMAHandles[0][3] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][3]); + } + if(stmDMAHandles[0][4] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][4]); + } +} + +#elif defined(TARGET_MCU_STM32G0) + +void DMA1_Channel2_3_IRQHandler(void) +{ + if(stmDMAHandles[0][1] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][1]); + } + if(stmDMAHandles[0][2] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][2]); + } +} + +#ifdef DMA2 +void DMA1_Ch4_7_DMA2_Ch1_5_DMAMUX1_OVR_IRQHandler(void) +{ + if(stmDMAHandles[0][3] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][3]); + } + if(stmDMAHandles[0][4] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][4]); + } + if(stmDMAHandles[0][5] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][5]); + } + if(stmDMAHandles[0][6] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][6]); + } + if(stmDMAHandles[1][0] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[1][0]); + } + if(stmDMAHandles[1][1] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[1][1]); + } + if(stmDMAHandles[1][2] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[1][2]); + } + if(stmDMAHandles[1][3] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[1][3]); + } + if(stmDMAHandles[1][4] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[1][4]); + } +} +#elif defined(DMA1_Channel7) +void DMA1_Ch4_7_DMAMUX1_OVR_IRQHandler(void) +{ + if(stmDMAHandles[0][3] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][3]); + } + if(stmDMAHandles[0][4] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][4]); + } + if(stmDMAHandles[0][5] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][5]); + } + if(stmDMAHandles[0][6] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][6]); + } +} +#else +void DMA1_Ch4_5_DMAMUX1_OVR_IRQHandler(void) +{ + if(stmDMAHandles[0][3] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][3]); + } + if(stmDMAHandles[0][4] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][4]); + } +} +#endif + +#elif defined(TARGET_MCU_STM32L0) + +void DMA1_Channel2_3_IRQHandler(void) +{ + if(stmDMAHandles[0][1] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][1]); + } + if(stmDMAHandles[0][2] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][2]); + } +} + +void DMA1_Channel4_5_6_7_IRQHandler(void) +{ + if(stmDMAHandles[0][3] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][3]); + } + if(stmDMAHandles[0][4] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][4]); + } + if(stmDMAHandles[0][5] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][5]); + } + if(stmDMAHandles[0][6] != NULL) { + HAL_DMA_IRQHandler(stmDMAHandles[0][6]); + } +} + +#else + +#ifdef DMA1_Channel2 +void DMA1_Channel2_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][1]); +} +#endif + +#ifdef DMA1_Channel3 +void DMA1_Channel3_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][2]); +} +#endif + +#ifdef DMA1_Channel4 +void DMA1_Channel4_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][3]); +} +#endif + +#ifdef DMA1_Channel5 +void DMA1_Channel5_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][4]); +} +#endif + +#ifdef DMA1_Channel6 +void DMA1_Channel6_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][5]); +} +#endif + +#ifdef DMA1_Channel7 +void DMA1_Channel7_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][6]); +} +#endif +#endif + +#ifdef DMA2_Channel1 +void DMA2_Channel1_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[1][0]); +} +#endif + +#ifdef DMA2_Channel2 +void DMA2_Channel2_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[1][1]); +} +#endif + +#ifdef DMA2_Channel3 +void DMA2_Channel3_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[1][2]); +} +#endif + +#ifdef DMA2_Channel4 +void DMA2_Channel4_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[1][3]); +} +#endif + +#ifdef DMA2_Channel5 +void DMA2_Channel5_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[1][4]); +} +#endif + +#ifdef DMA2_Channel6 +void DMA2_Channel6_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[1][5]); +} +#endif + +#ifdef DMA2_Channel7 +void DMA2_Channel7_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[1][6]); +} +#endif +#endif // DMA_IP_VERSION_V2 + +#ifdef DMA_IP_VERSION_V1 +#ifdef DMA1_Stream0 +void DMA1_Stream0_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][0]); +} +#endif + +#ifdef DMA1_Stream1 +void DMA1_Stream1_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][1]); +} +#endif + +#ifdef DMA1_Stream2 +void DMA1_Stream2_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][2]); +} +#endif + +#ifdef DMA1_Stream3 +void DMA1_Stream3_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][3]); +} +#endif + +#ifdef DMA1_Stream4 +void DMA1_Stream4_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][4]); +} +#endif + +#ifdef DMA1_Stream5 +void DMA1_Stream5_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][5]); +} +#endif + +#ifdef DMA1_Stream6 +void DMA1_Stream6_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][6]); +} +#endif + +#ifdef DMA1_Stream7 +void DMA1_Stream7_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][7]); +} +#endif + +#ifdef DMA2_Stream0 +void DMA2_Stream0_IRQHandler(void) +{ + // Note: Unlike both IP v2 and IP v3, IP v1 channels are 0-indexed. + HAL_DMA_IRQHandler(stmDMAHandles[1][0]); +} +#endif + +#ifdef DMA2_Stream1 +void DMA2_Stream1_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[1][1]); +} +#endif + +#ifdef DMA2_Stream2 +void DMA2_Stream2_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[1][2]); +} +#endif + +#ifdef DMA2_Stream3 +void DMA2_Stream3_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[1][3]); +} +#endif + +#ifdef DMA2_Stream4 +void DMA2_Stream4_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[1][4]); +} +#endif + +#ifdef DMA2_Stream5 +void DMA2_Stream5_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[1][5]); +} +#endif + +#ifdef DMA2_Stream6 +void DMA2_Stream6_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[1][6]); +} +#endif + +#ifdef DMA2_Stream7 +void DMA2_Stream7_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[1][7]); +} +#endif +#endif // DMA_IP_VERSION_V1 + +#ifdef DMA_IP_VERSION_V3 +#ifdef GPDMA1_Channel0 +void GPDMA1_Channel0_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][0]); +} +#endif + +#ifdef GPDMA1_Channel1 +void GPDMA1_Channel1_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][1]); +} +#endif + +#ifdef GPDMA1_Channel2 +void GPDMA1_Channel2_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][2]); +} +#endif + +#ifdef GPDMA1_Channel3 +void GPDMA1_Channel3_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][3]); +} +#endif + +#ifdef GPDMA1_Channel4 +void GPDMA1_Channel4_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][4]); +} +#endif + +#ifdef GPDMA1_Channel5 +void GPDMA1_Channel5_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][5]); +} +#endif + +#ifdef GPDMA1_Channel6 +void GPDMA1_Channel6_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][6]); +} +#endif + +#ifdef GPDMA1_Channel7 +void GPDMA1_Channel7_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][7]); +} +#endif + +#ifdef GPDMA1_Channel8 +void GPDMA1_Channel8_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][8]); +} +#endif + +#ifdef GPDMA1_Channel9 +void GPDMA1_Channel9_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][9]); +} +#endif + +#ifdef GPDMA1_Channel10 +void GPDMA1_Channel10_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][10]); +} +#endif + +#ifdef GPDMA1_Channel11 +void GPDMA1_Channel11_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][11]); +} +#endif + +#ifdef GPDMA1_Channel12 +void GPDMA1_Channel12_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][12]); +} +#endif + +#ifdef GPDMA1_Channel13 +void GPDMA1_Channel13_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][13]); +} +#endif + +#ifdef GPDMA1_Channel14 +void GPDMA1_Channel14_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][14]); +} +#endif + +#ifdef GPDMA1_Channel15 +void GPDMA1_Channel15_IRQHandler(void) +{ + HAL_DMA_IRQHandler(stmDMAHandles[0][15]); +} +#endif +#endif // DMA_IP_VERSION_V3 \ No newline at end of file diff --git a/targets/TARGET_STM/stm_dma_utils.h b/targets/TARGET_STM/stm_dma_utils.h new file mode 100644 index 0000000000..1ddde2238b --- /dev/null +++ b/targets/TARGET_STM/stm_dma_utils.h @@ -0,0 +1,109 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef MBED_OS_STM_DMA_UTILS_H +#define MBED_OS_STM_DMA_UTILS_H + +#include +#include + +#include "cmsis.h" + +// determine DMA IP version using the available constants in the chip header +#if defined(GPDMA1) +#define DMA_IP_VERSION_V3 1 +#elif defined(DMA1_Channel1) +#define DMA_IP_VERSION_V2 1 +#else +#define DMA_IP_VERSION_V1 1 +#endif + +// Include correct header for the IP version +#ifdef DMA_IP_VERSION_V3 +#include "stm_dma_ip_v3.h" +#elif defined(DMA_IP_VERSION_V2) +#include "stm_dma_ip_v2.h" +#else +#include "stm_dma_ip_v1.h" +#endif + +/* + * Structure containing info about a peripheral's link to the DMA controller. + */ +typedef struct DMALinkInfo { + + /// Index of the DMA module that the DMA link uses. + /// Note: 1-indexed. + uint8_t dmaIdx; + + /// Index of the channel on the DMA module. + /// Note that some STMicro chips have a DMA mux allowing any DMA peripheral to be used with + /// any channel, and others have a semi-fixed architecture with just some basic multiplexing. + /// Note: May be 1 or 0 indexed depending on processor + uint8_t channelIdx; + +#if STM_DEVICE_HAS_DMA_SOURCE_SELECTION + /// Request source number. This is either a DMA mux input number, or a mux selection number + /// on devices without a DMA mux. + /// Note: 0-indexed. + uint8_t sourceNumber; +#endif +} DMALinkInfo; + +/** + * @brief Get the DMA channel instance for a DMA link + * + * @param dmaLink DMA link instance + */ +DMA_Channel_TypeDef * stm_get_dma_channel(DMALinkInfo const * dmaLink); + +/** + * @brief Get the interrupt number for a DMA link + * + * @param dmaLink DMA link instance + */ +IRQn_Type stm_get_dma_irqn(const DMALinkInfo *dmaLink); + +/** + * @brief Initialize a DMA link for use. + * + * This enables and sets up the interrupt, allocates a DMA handle, and returns the handle pointer. + * Arguments are based on the parameters used for the DMA_InitTypeDef structure. + * + * @param dmaLink DMA link instance + * @param direction \c DMA_PERIPH_TO_MEMORY, \c DMA_MEMORY_TO_PERIPH, or \c DMA_MEMORY_TO_MEMORY + * @param periphInc Whether the Peripheral address register should be incremented or not. + * @param memInc Whether the Memory address register should be incremented or not. + * @param periphDataAlignment Alignment value of the peripheral data. 1, 2, or 4. + * @param memDataAlignment \c DMA_MDATAALIGN_BYTE, \c DMA_MDATAALIGN_HALFWORD, or \c DMA_MDATAALIGN_WORD + * + * @return Pointer to DMA handle allocated by this module. + * @return NULL if the DMA channel used by the link has already been allocated by something else. + */ +DMA_HandleTypeDef * stm_init_dma_link(DMALinkInfo const * dmaLink, uint32_t direction, bool periphInc, bool memInc, uint8_t periphDataAlignment, uint8_t memDataAlignment); + +/** + * @brief Free a DMA link. + * + * This frees memory associated with it and unlocks the hardware DMA channel so that it can be used by somebody else. + * + * @param dmaLink DMA link ponter to free. + */ +void stm_free_dma_link(DMALinkInfo const * dmaLink); + +#endif //MBED_OS_STM_DMA_UTILS_H diff --git a/targets/TARGET_STM/stm_spi_api.c b/targets/TARGET_STM/stm_spi_api.c index 70f10557fd..f444afeb1e 100644 --- a/targets/TARGET_STM/stm_spi_api.c +++ b/targets/TARGET_STM/stm_spi_api.c @@ -42,6 +42,11 @@ #include "pinmap.h" #include "PeripheralPins.h" #include "spi_device.h" +#include "stm_spi_api.h" + +#ifdef STM32_SPI_CAPABILITY_DMA +#include "stm_dma_info.h" +#endif #if DEVICE_SPI_ASYNCH #define SPI_INST(obj) ((SPI_TypeDef *)(obj->spi.spi)) @@ -85,6 +90,55 @@ extern HAL_StatusTypeDef HAL_SPIEx_FlushRxFifo(SPI_HandleTypeDef *hspi); #define HAS_32BIT_SPI_TRANSFERS 1 #endif // SPI_DATASIZE_X +// SPI IRQ handlers +#if defined SPI1_BASE +static SPI_HandleTypeDef * spi1Handle; // Handle of whatever SPI structure is used for SPI1 +void SPI1_IRQHandler() +{ + HAL_SPI_IRQHandler(spi1Handle); +} +#endif + +#if defined SPI2_BASE +static SPI_HandleTypeDef * spi2Handle; // Handle of whatever SPI structure is used for SPI2 +void SPI2_IRQHandler() +{ + HAL_SPI_IRQHandler(spi2Handle); +} +#endif + +#if defined SPI3_BASE +static SPI_HandleTypeDef * spi3Handle; // Handle of whatever SPI structure is used for SPI3 +void SPI3_IRQHandler() +{ + HAL_SPI_IRQHandler(spi3Handle); +} +#endif + +#if defined SPI4_BASE +static SPI_HandleTypeDef * spi4Handle; // Handle of whatever SPI structure is used for SPI4 +void SPI4_IRQHandler() +{ + HAL_SPI_IRQHandler(spi4Handle); +} +#endif + +#if defined SPI5_BASE +static SPI_HandleTypeDef * spi5Handle; // Handle of whatever SPI structure is used for SPI5 +void SPI5_IRQHandler() +{ + HAL_SPI_IRQHandler(spi5Handle); +} +#endif + +#if defined SPI6_BASE +static SPI_HandleTypeDef * spi6Handle; // Handle of whatever SPI structure is used for SPI6 +void SPI6_IRQHandler() +{ + HAL_SPI_IRQHandler(spi6Handle); +} +#endif + /** * Flush RX FIFO/input register of SPI interface and clear overrun flag. */ @@ -96,6 +150,20 @@ static inline void spi_flush_rx(spi_t *obj) LL_SPI_ClearFlag_OVR(SPI_INST(obj)); } +// Store the spi_s * inside an SPI handle, for later retrieval in callbacks +static inline void store_spis_pointer(SPI_HandleTypeDef * spiHandle, struct spi_s * spis) { + // Annoyingly, STM neglected to provide any sort of "user data" pointer inside SPI_HandleTypeDef for use + // in callbacks. However, there are some variables in the Init struct that are never accessed after HAL_SPI_Init(). + // So, we can reuse those to store our pointer. + spiHandle->Init.TIMode = (uint32_t)spis; +} + +// Get spi_s * from SPI_HandleTypeDef +static inline struct spi_s * get_spis_pointer(SPI_HandleTypeDef * spiHandle) { + return (struct spi_s *) spiHandle->Init.TIMode; +} + + void spi_get_capabilities(PinName ssel, bool slave, spi_capabilities_t *cap) { if (slave) { @@ -149,10 +217,16 @@ void init_spi(spi_t *obj) __HAL_SPI_DISABLE(handle); + // Reset flag used by store_spis_pointer() + handle->Init.TIMode = SPI_TIMODE_DISABLE; + DEBUG_PRINTF("init_spi: instance=0x%8X\r\n", (int)handle->Instance); if (HAL_SPI_Init(handle) != HAL_OK) { error("Cannot initialize SPI"); } + + store_spis_pointer(handle, spiobj); + /* In some cases after SPI object re-creation SPI overrun flag may not * be cleared, so clear RX data explicitly to prevent any transmissions errors */ spi_flush_rx(obj); @@ -205,6 +279,10 @@ static void _spi_init_direct(spi_t *obj, const spi_pinmap_t *pinmap) RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; #endif /* SPI_IP_VERSION_V2 */ +#ifdef DEVICE_SPI_ASYNCH + spiobj->driverCallback = NULL; +#endif + #if defined SPI1_BASE // Enable SPI clock if (spiobj->spi == SPI_1) { @@ -224,6 +302,8 @@ static void _spi_init_direct(spi_t *obj, const spi_pinmap_t *pinmap) __HAL_RCC_SPI1_RELEASE_RESET(); __HAL_RCC_SPI1_CLK_ENABLE(); spiobj->spiIRQ = SPI1_IRQn; + spiobj->spiIndex = 1; + spi1Handle = &spiobj->handle; } #endif @@ -245,6 +325,8 @@ static void _spi_init_direct(spi_t *obj, const spi_pinmap_t *pinmap) __HAL_RCC_SPI2_RELEASE_RESET(); __HAL_RCC_SPI2_CLK_ENABLE(); spiobj->spiIRQ = SPI2_IRQn; + spiobj->spiIndex = 2; + spi2Handle = &spiobj->handle; } #endif @@ -266,6 +348,8 @@ static void _spi_init_direct(spi_t *obj, const spi_pinmap_t *pinmap) __HAL_RCC_SPI3_RELEASE_RESET(); __HAL_RCC_SPI3_CLK_ENABLE(); spiobj->spiIRQ = SPI3_IRQn; + spiobj->spiIndex = 3; + spi3Handle = &spiobj->handle; } #endif @@ -283,6 +367,8 @@ static void _spi_init_direct(spi_t *obj, const spi_pinmap_t *pinmap) __HAL_RCC_SPI4_RELEASE_RESET(); __HAL_RCC_SPI4_CLK_ENABLE(); spiobj->spiIRQ = SPI4_IRQn; + spiobj->spiIndex = 4; + spi4Handle = &spiobj->handle; } #endif @@ -300,6 +386,8 @@ static void _spi_init_direct(spi_t *obj, const spi_pinmap_t *pinmap) __HAL_RCC_SPI5_RELEASE_RESET(); __HAL_RCC_SPI5_CLK_ENABLE(); spiobj->spiIRQ = SPI5_IRQn; + spiobj->spiIndex = 5; + spi5Handle = &spiobj->handle; } #endif @@ -317,6 +405,8 @@ static void _spi_init_direct(spi_t *obj, const spi_pinmap_t *pinmap) __HAL_RCC_SPI6_RELEASE_RESET(); __HAL_RCC_SPI6_CLK_ENABLE(); spiobj->spiIRQ = SPI6_IRQn; + spiobj->spiIndex = 6; + spi6Handle = &spiobj->handle; } #endif @@ -368,7 +458,6 @@ static void _spi_init_direct(spi_t *obj, const spi_pinmap_t *pinmap) #endif handle->Init.DataSize = SPI_DATASIZE_8BIT; handle->Init.FirstBit = SPI_FIRSTBIT_MSB; - handle->Init.TIMode = SPI_TIMODE_DISABLE; #if defined (SPI_IP_VERSION_V2) handle->Init.NSSPolarity = SPI_NSS_POLARITY_LOW; @@ -418,6 +507,76 @@ void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel SPI_INIT_DIRECT(obj, &explicit_spi_pinmap); } +#ifdef STM32_SPI_CAPABILITY_DMA + +/** + * Initialize the DMA for an SPI object in the Tx direction. + * Does nothing if DMA is already initialized. + */ +static void spi_init_tx_dma(struct spi_s * obj) +{ + if(!obj->txDMAInitialized) + { +#ifdef TARGET_MCU_STM32H7 + // For STM32H7, SPI6 does not support DMA through the normal mechanism -- it would require use of the BDMA + // controller, which we don't currently support, and which can only access data in SRAM4. + if(obj->spiIndex == 6) + { + mbed_error(MBED_ERROR_UNSUPPORTED, "DMA not supported on SPI6!", 0, MBED_FILENAME, __LINE__); + } +#endif + + // Get DMA handle + DMALinkInfo const *dmaLink = &SPITxDMALinks[obj->spiIndex - 1]; + + // Initialize DMA channel + DMA_HandleTypeDef *dmaHandle = stm_init_dma_link(dmaLink, DMA_MEMORY_TO_PERIPH, false, true, 1, 1); + + if(dmaHandle == NULL) + { + mbed_error(MBED_ERROR_ALREADY_IN_USE, "Tx DMA channel already used by something else!", 0, MBED_FILENAME, __LINE__); + } + + __HAL_LINKDMA(&obj->handle, hdmatx, *dmaHandle); + obj->txDMAInitialized = true; + } +} + +/** + * Initialize the DMA for an SPI object in the Rx direction. + * Does nothing if DMA is already initialized. + */ +static void spi_init_rx_dma(struct spi_s * obj) +{ + if(!obj->rxDMAInitialized) + { +#ifdef TARGET_MCU_STM32H7 + // For STM32H7, SPI6 does not support DMA through the normal mechanism -- it would require use of the BDMA + // controller, which we don't currently support, and which can only access data in SRAM4. + if(obj->spiIndex == 6) + { + mbed_error(MBED_ERROR_UNSUPPORTED, "DMA not supported on SPI6!", 0, MBED_FILENAME, __LINE__); + } +#endif + + // Get DMA handle + DMALinkInfo const *dmaLink = &SPIRxDMALinks[obj->spiIndex - 1]; + + // Initialize DMA channel + DMA_HandleTypeDef *dmaHandle = stm_init_dma_link(dmaLink, DMA_PERIPH_TO_MEMORY, false, true, 1, 1); + + if(dmaHandle == NULL) + { + mbed_error(MBED_ERROR_ALREADY_IN_USE, "Rx DMA channel already used by something else!", 0, MBED_FILENAME, __LINE__); + } + + __HAL_LINKDMA(&obj->handle, hdmarx, *dmaHandle); + obj->rxDMAInitialized = true; + } +} + +#endif + void spi_free(spi_t *obj) { struct spi_s *spiobj = SPI_S(obj); @@ -425,6 +584,20 @@ void spi_free(spi_t *obj) DEBUG_PRINTF("spi_free\r\n"); +#if STM32_SPI_CAPABILITY_DMA + // Free DMA channels if allocated + if(spiobj->txDMAInitialized) + { + stm_free_dma_link(&SPITxDMALinks[spiobj->spiIndex - 1]); + spiobj->txDMAInitialized = false; + } + if(spiobj->rxDMAInitialized) + { + stm_free_dma_link(&SPIRxDMALinks[spiobj->spiIndex - 1]); + spiobj->rxDMAInitialized = false; + } +#endif + __HAL_SPI_DISABLE(handle); HAL_SPI_DeInit(handle); @@ -1362,7 +1535,7 @@ typedef enum { /// @returns the number of bytes transferred, or `0` if nothing transferred -static int spi_master_start_asynch_transfer(spi_t *obj, transfer_type_t transfer_type, const void *tx, void *rx, size_t length) +static int spi_master_start_asynch_transfer(spi_t *obj, transfer_type_t transfer_type, const void *tx, void *rx, size_t length, DMAUsage hint) { struct spi_s *spiobj = SPI_S(obj); SPI_HandleTypeDef *handle = &(spiobj->handle); @@ -1373,19 +1546,59 @@ static int spi_master_start_asynch_transfer(spi_t *obj, transfer_type_t transfer // so the number of transfers depends on the container size size_t words; - DEBUG_PRINTF("SPI inst=0x%8X Start: %u, %u\r\n", (int)handle->Instance, transfer_type, length); - obj->spi.transfer_type = transfer_type; words = length >> bitshift; - // enable the interrupt + bool useDMA = false; + +#if STM32_SPI_CAPABILITY_DMA + if (hint != DMA_USAGE_NEVER) + { + // Initialize DMA channel(s) needed + switch (transfer_type) + { + case SPI_TRANSFER_TYPE_TXRX: + spi_init_rx_dma(&obj->spi); + spi_init_tx_dma(&obj->spi); + break; + case SPI_TRANSFER_TYPE_TX: + spi_init_tx_dma(&obj->spi); + break; + case SPI_TRANSFER_TYPE_RX: + spi_init_rx_dma(&obj->spi); + if(handle->Init.Direction == SPI_DIRECTION_2LINES) { + // For 2 line SPI, doing an Rx-only transfer still requires a second DMA channel to send the fill + // bytes. + spi_init_tx_dma(&obj->spi); + } + break; + default: + break; + } + + useDMA = true; + } +#endif + + DEBUG_PRINTF("SPI inst=0x%8X Start: type=%u, length=%u, DMA=%d\r\n", (int) handle->Instance, transfer_type, length, !!useDMA); + + // Enable the interrupt. This might be needed even for DMA -- some HAL implementations (e.g. H7) have + // the DMA interrupt handler trigger the SPI interrupt. IRQn_Type irq_n = spiobj->spiIRQ; - NVIC_DisableIRQ(irq_n); NVIC_ClearPendingIRQ(irq_n); NVIC_SetPriority(irq_n, 1); NVIC_EnableIRQ(irq_n); +#if defined(STM32_SPI_CAPABILITY_DMA) && defined(__DCACHE_PRESENT) + if (useDMA && transfer_type != SPI_TRANSFER_TYPE_RX) + { + // For chips with a cache (e.g. Cortex-M7), we need to evict the Tx data from cache to main memory. + // This ensures that the DMA controller can see the most up-to-date copy of the data. + SCB_CleanDCache_by_Addr((volatile void*)tx, length); + } +#endif + // flush FIFO #if defined(SPI_FLAG_FRLVL) HAL_SPIEx_FlushRxFifo(handle); @@ -1400,21 +1613,52 @@ static int spi_master_start_asynch_transfer(spi_t *obj, transfer_type_t transfer #endif switch (transfer_type) { case SPI_TRANSFER_TYPE_TXRX: - rc = HAL_SPI_TransmitReceive_IT(handle, (uint8_t *)tx, (uint8_t *)rx, words); + if(useDMA) { + rc = HAL_SPI_TransmitReceive_DMA(handle, (uint8_t *)tx, (uint8_t *)rx, words); + } + else { + rc = HAL_SPI_TransmitReceive_IT(handle, (uint8_t *) tx, (uint8_t *) rx, words); + } break; case SPI_TRANSFER_TYPE_TX: - rc = HAL_SPI_Transmit_IT(handle, (uint8_t *)tx, words); + if (useDMA) { + rc = HAL_SPI_Transmit_DMA(handle, (uint8_t *)tx, words); + } + else { + rc = HAL_SPI_Transmit_IT(handle, (uint8_t *) tx, words); + } break; case SPI_TRANSFER_TYPE_RX: // the receive function also "transmits" the receive buffer so in order // to guarantee that 0xff is on the line, we explicitly memset it here memset(rx, SPI_FILL_CHAR, length); - rc = HAL_SPI_Receive_IT(handle, (uint8_t *)rx, words); + + if (useDMA) { +#if defined(STM32_SPI_CAPABILITY_DMA) && defined(__DCACHE_PRESENT) + // For chips with a cache (e.g. Cortex-M7), we need to evict the Tx data from cache to main memory. + // This ensures that the DMA controller can see the most up-to-date copy of the data. + SCB_CleanDCache_by_Addr(rx, length); +#endif + rc = HAL_SPI_Receive_DMA(handle, (uint8_t *)rx, words); + } + else { + rc = HAL_SPI_Receive_IT(handle, (uint8_t *)rx, words); + } + break; default: length = 0; } +#if defined(STM32_SPI_CAPABILITY_DMA) && defined(__DCACHE_PRESENT) + if (useDMA && transfer_type != SPI_TRANSFER_TYPE_TX) + { + // For chips with a cache (e.g. Cortex-M7), we need to invalidate the Rx data in cache. + // This ensures that the CPU will fetch the data from SRAM instead of using its cache. + SCB_InvalidateDCache_by_Addr(rx, length); + } +#endif + if (rc) { #if defined(SPI_IP_VERSION_V2) // enable SPI back in case of error @@ -1430,15 +1674,11 @@ static int spi_master_start_asynch_transfer(spi_t *obj, transfer_type_t transfer } // asynchronous API -// DMA support for SPI is currently not supported, hence asynchronous SPI does not support high speeds(MHZ range) void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint8_t bit_width, uint32_t handler, uint32_t event, DMAUsage hint) { struct spi_s *spiobj = SPI_S(obj); SPI_HandleTypeDef *handle = &(spiobj->handle); - // TODO: DMA usage is currently ignored - (void) hint; - // check which use-case we have bool use_tx = (tx != NULL && tx_length > 0); bool use_rx = (rx != NULL && rx_length > 0); @@ -1463,10 +1703,10 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, obj->spi.event = event; - // register the thunking handler - IRQn_Type irq_n = spiobj->spiIRQ; - NVIC_SetVector(irq_n, (uint32_t)handler); - DEBUG_PRINTF("SPI: Transfer: tx %u (%u), rx %u (%u), IRQ %u\n", use_tx, tx_length, use_rx, rx_length, irq_n); + // Register the callback. + // It's a function pointer, but it's passed as a uint32_t because of reasons. + spiobj->driverCallback = (void (*)(void))handler; + DEBUG_PRINTF("SPI: Transfer: tx %u (%u), rx %u (%u)\n", use_tx, tx_length, use_rx, rx_length); // enable the right hal transfer if (use_tx && use_rx) { @@ -1477,22 +1717,19 @@ void spi_master_transfer(spi_t *obj, const void *tx, size_t tx_length, void *rx, obj->tx_buff.length = size; obj->rx_buff.length = size; } - spi_master_start_asynch_transfer(obj, SPI_TRANSFER_TYPE_TXRX, tx, rx, size); + spi_master_start_asynch_transfer(obj, SPI_TRANSFER_TYPE_TXRX, tx, rx, size, hint); } else if (use_tx) { - spi_master_start_asynch_transfer(obj, SPI_TRANSFER_TYPE_TX, tx, NULL, tx_length); + spi_master_start_asynch_transfer(obj, SPI_TRANSFER_TYPE_TX, tx, NULL, tx_length, hint); } else if (use_rx) { - spi_master_start_asynch_transfer(obj, SPI_TRANSFER_TYPE_RX, NULL, rx, rx_length); + spi_master_start_asynch_transfer(obj, SPI_TRANSFER_TYPE_RX, NULL, rx, rx_length, hint); } } -inline uint32_t spi_irq_handler_asynch(spi_t *obj) +uint32_t spi_irq_handler_asynch(spi_t *obj) { int event = 0; SPI_HandleTypeDef *handle = &(SPI_S(obj)->handle); - // call the CubeF4 handler, this will update the handle - HAL_SPI_IRQHandler(handle); - if (handle->State == HAL_SPI_STATE_READY) { // When HAL SPI is back to READY state, check if there was an error int error = obj->spi.handle.ErrorCode; @@ -1537,6 +1774,36 @@ inline uint32_t spi_irq_handler_asynch(spi_t *obj) return (event & (obj->spi.event | SPI_EVENT_INTERNAL_TRANSFER_COMPLETE)); } +// Callback from STM32 HAL when a bidirectional SPI transfer completes (interrupt based or DMA) +void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) +{ + struct spi_s * spis = get_spis_pointer(hspi); + if(spis != NULL) + { + spis->driverCallback(); + } +} + +// Callback from STM32 HAL when a Rx-only SPI transfer completes (interrupt based or DMA) +void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) +{ + struct spi_s * spis = get_spis_pointer(hspi); + if(spis != NULL) + { + spis->driverCallback(); + } +} + +// Callback from STM32 HAL when a Tx-only SPI transfer completes (interrupt based or DMA) +void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) +{ + struct spi_s * spis = get_spis_pointer(hspi); + if(spis != NULL) + { + spis->driverCallback(); + } +} + uint8_t spi_active(spi_t *obj) { struct spi_s *spiobj = SPI_S(obj); @@ -1558,21 +1825,26 @@ void spi_abort_asynch(spi_t *obj) struct spi_s *spiobj = SPI_S(obj); SPI_HandleTypeDef *handle = &(spiobj->handle); - // disable interrupt + // disable interrupt if it was enabled IRQn_Type irq_n = spiobj->spiIRQ; NVIC_ClearPendingIRQ(irq_n); NVIC_DisableIRQ(irq_n); +#ifdef STM32_SPI_CAPABILITY_DMA + // Abort DMA transfers if DMA channels have been allocated + if(spiobj->txDMAInitialized) { + HAL_DMA_Abort_IT(spiobj->handle.hdmatx); + } + if(spiobj->rxDMAInitialized) { + HAL_DMA_Abort_IT(spiobj->handle.hdmarx); + } +#endif + // clean-up LL_SPI_Disable(SPI_INST(obj)); HAL_SPI_DeInit(handle); - HAL_SPI_Init(handle); - // cleanup input buffer - spi_flush_rx(obj); - // enable SPI back if it isn't 3-wire mode - if (handle->Init.Direction != SPI_DIRECTION_1LINE) { - LL_SPI_Enable(SPI_INST(obj)); - } + + init_spi(obj); } #endif //DEVICE_SPI_ASYNCH diff --git a/targets/TARGET_STM/stm_spi_api.h b/targets/TARGET_STM/stm_spi_api.h new file mode 100644 index 0000000000..6fb22c93c4 --- /dev/null +++ b/targets/TARGET_STM/stm_spi_api.h @@ -0,0 +1,53 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016-2023 STMicroelectronics + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef MBED_OS_STM_SPI_API_H +#define MBED_OS_STM_SPI_API_H + +#include "spi_device.h" + +#if STM32_SPI_CAPABILITY_DMA +#include "stm_dma_utils.h" +#endif + +struct spi_s { + SPI_HandleTypeDef handle; + IRQn_Type spiIRQ; + SPIName spi; + PinName pin_miso; + PinName pin_mosi; + PinName pin_sclk; + PinName pin_ssel; +#if DEVICE_SPI_ASYNCH + uint32_t event; + uint8_t transfer_type; + + // Callback function for when we get an interrupt on an async transfer. + // This will point, through a bit of indirection, to SPI::irq_handler_asynch() + // for the correct SPI instance. + void (*driverCallback)(void); + +#endif + uint8_t spiIndex; // Index of the SPI peripheral, from 1-6 +#if STM32_SPI_CAPABILITY_DMA + bool txDMAInitialized; + bool rxDMAInitialized; +#endif +}; + +#endif //MBED_OS_STM_SPI_API_H diff --git a/targets/targets.json5 b/targets/targets.json5 index 44627e8001..bb03d4cccd 100644 --- a/targets/targets.json5 +++ b/targets/targets.json5 @@ -3224,7 +3224,8 @@ "CRC", "TRNG", "FLASH", - "MPU" + "MPU", + "SPI_32BIT_WORDS" ] }, "MCU_STM32H723xG": { @@ -4849,7 +4850,8 @@ "FLASH", "MPU", "TRNG", - "SERIAL_ASYNCH" + "SERIAL_ASYNCH", + "SPI_32BIT_WORDS" ] }, "MCU_STM32U575xI": { diff --git a/tools/cmake/mbed-run-greentea-test.in.cmake b/tools/cmake/mbed-run-greentea-test.in.cmake index b5fdfe05c3..36739b2254 100644 --- a/tools/cmake/mbed-run-greentea-test.in.cmake +++ b/tools/cmake/mbed-run-greentea-test.in.cmake @@ -14,7 +14,7 @@ set(MBEDHTRUN_ARGS --skip-flashing @MBED_HTRUN_ARGUMENTS@) # filled in by config # Print out command string(REPLACE ";" " " MBEDHTRUN_ARGS_FOR_DISPLAY "${MBEDHTRUN_ARGS}") -message("Executing: mbedhtrun ${MBEDHTRUN_ARGS_FOR_DISPLAY}") +message("Executing: @Python3_EXECUTABLE@ -m mbed_host_tests.mbedhtrun ${MBEDHTRUN_ARGS_FOR_DISPLAY}") # Note: For this command, we need to survive mbedhtrun not being on the PATH, so we import the package and call the main function using "python -c" execute_process(