From 706a092bc2674f2ec6ebb05016f4c0b8c8603cb7 Mon Sep 17 00:00:00 2001 From: richardclli Date: Sat, 3 May 2025 18:26:10 +0800 Subject: [PATCH] fix(h7): common drivers for H7 support (#6149) Co-authored-by: raphaelcoeffic <1050031+raphaelcoeffic@users.noreply.github.com> --- .../targets/common/arm/stm32/CMakeLists.txt | 15 +- .../common/arm/stm32/audio_dac_driver.cpp | 137 ++++- .../arm/stm32/rotary_encoder_driver.cpp | 2 +- .../targets/common/arm/stm32/sdcard_spi.cpp | 12 +- .../targets/common/arm/stm32/stm32_hal_ll.h | 1 + .../targets/common/arm/stm32/stm32_spi.cpp | 149 +----- .../src/targets/common/arm/stm32/stm32_spi.h | 21 +- .../targets/common/arm/stm32/stm32_spi_h7.cpp | 493 ++++++++++++++++++ 8 files changed, 672 insertions(+), 158 deletions(-) create mode 100644 radio/src/targets/common/arm/stm32/stm32_spi_h7.cpp diff --git a/radio/src/targets/common/arm/stm32/CMakeLists.txt b/radio/src/targets/common/arm/stm32/CMakeLists.txt index ae324ecf1..a5a82a032 100644 --- a/radio/src/targets/common/arm/stm32/CMakeLists.txt +++ b/radio/src/targets/common/arm/stm32/CMakeLists.txt @@ -100,10 +100,23 @@ if(NOT NATIVE_BUILD) ${STM32_DRIVER_DIR}/stm32_timer.cpp ${STM32_DRIVER_DIR}/stm32_dma.cpp ${STM32_DRIVER_DIR}/stm32_gpio.cpp - ${STM32_DRIVER_DIR}/stm32_spi.cpp ${STM32_DRIVER_DIR}/stm32_adc.cpp ) + if(CPU_TYPE STREQUAL STM32F2) + target_sources(stm32_drivers PUBLIC + ${STM32_DRIVER_DIR}/stm32_spi.cpp + ) + elseif(CPU_TYPE STREQUAL STM32F4) + target_sources(stm32_drivers PUBLIC + ${STM32_DRIVER_DIR}/stm32_spi.cpp + ) + elseif(CPU_TYPE STREQUAL STM32H7 OR CPU_TYPE STREQUAL STM32H7RS) + target_sources(stm32_drivers PUBLIC + ${STM32_DRIVER_DIR}/stm32_spi_h7.cpp + ) + endif() + # HAL/LL drivers using TRACE add_library(stm32_drivers_w_dbg_fw OBJECT EXCLUDE_FROM_ALL ${STM32_DRIVER_DIR}/sdcard_spi.cpp diff --git a/radio/src/targets/common/arm/stm32/audio_dac_driver.cpp b/radio/src/targets/common/arm/stm32/audio_dac_driver.cpp index 80a49d0a9..ec611b9e5 100644 --- a/radio/src/targets/common/arm/stm32/audio_dac_driver.cpp +++ b/radio/src/targets/common/arm/stm32/audio_dac_driver.cpp @@ -29,8 +29,56 @@ #include "edgetx.h" + const AudioBuffer * nextBuffer = nullptr; +#if defined(STM32H5) || defined(STM32H7) || defined(STM32H7RS) +// 16 bit, 1 channels +#define DMA_BUFFER_HALF_LEN AUDIO_BUFFER_SIZE + +// 2 buffers +#define DMA_BUFFER_LEN (DMA_BUFFER_HALF_LEN * 2) + +static uint16_t _dma_buffer[DMA_BUFFER_LEN] __DMA_NO_CACHE; + +static volatile uint32_t _dma_buffer_offset = 0; + +static inline uint32_t _calc_offset(uint8_t tc) +{ + return tc * DMA_BUFFER_HALF_LEN; +} + +// the returned bool is true when there is no more data available +static bool audio_update_dma_buffer(uint8_t tc) +{ + _dma_buffer_offset = _calc_offset(tc); + + auto buffer = audioQueue.buffersFifo.getNextFilledBuffer(); + nextBuffer = buffer; + if (!buffer) { + unsigned idx = 0; + for (; idx < DMA_BUFFER_HALF_LEN; idx++) { + uint32_t offset = _dma_buffer_offset + idx; + _dma_buffer[offset] = AUDIO_DATA_SILENCE; + } + return true; + } else { + unsigned idx = 0; + for (; idx < buffer->size; idx++) { + uint32_t offset = _dma_buffer_offset + idx; + _dma_buffer[offset] = (uint16_t)buffer->data[idx]; + } + for (; idx < DMA_BUFFER_HALF_LEN; idx++) { + uint32_t offset = _dma_buffer_offset + idx; + _dma_buffer[offset] = AUDIO_DATA_SILENCE; + } + audioQueue.buffersFifo.freeNextFilledBuffer(); + return false; + } +} +#else +#define AUDIO_DAC DAC +#endif // Init timer that triggers the DAC conversion // at sampling frequency @@ -96,6 +144,26 @@ void dacInit() gpio_init_analog(AUDIO_OUTPUT_GPIO); stm32_dma_enable_clock(AUDIO_DMA); +#if defined(STM32H5) || defined(STM32H7) || defined(STM32H7RS) + LL_DMA_DeInit(AUDIO_DMA, AUDIO_DMA_Stream); + + LL_DMA_InitTypeDef dmaInit; + LL_DMA_StructInit(&dmaInit); + +#if defined(STM32H7) + dmaInit.Mode = LL_DMA_MODE_CIRCULAR; + dmaInit.PeriphRequest = AUDIO_DMA_Channel; + dmaInit.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dmaInit.Priority = LL_DMA_PRIORITY_VERYHIGH; + dmaInit.PeriphOrM2MSrcAddress = LL_DAC_DMA_GetRegAddr(DAC1, LL_DAC_CHANNEL_1, LL_DAC_DMA_REG_DATA_12BITS_LEFT_ALIGNED); + dmaInit.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD; + dmaInit.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dmaInit.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD; +#elif defined(STM32H7RS) + #error not implemented +#endif + LL_DMA_Init(AUDIO_DMA, AUDIO_DMA_Stream, &dmaInit); +#else // Disable DMA stream AUDIO_DMA_Stream->CR &= ~DMA_SxCR_EN; @@ -110,23 +178,34 @@ void dacInit() DMA_SxCR_CIRC; // write to DAC channel 1 (12 bits, left-aligned) - AUDIO_DMA_Stream->PAR = CONVERT_PTR_UINT(&DAC->DHR12L1); + AUDIO_DMA_Stream->PAR = CONVERT_PTR_UINT(&AUDIO_DAC->DHR12L1); // disable direct mode and set FIFO threshold to half AUDIO_DMA_Stream->FCR = DMA_SxFCR_DMDIS | DMA_SxFCR_FTH_0; +#endif +#if defined(LL_APB1_GRP1_PERIPH_DAC12) + LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_DAC12); +#else LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_DAC1); +#endif // set data registre to silence - DAC->DHR12L1 = AUDIO_DATA_SILENCE; + AUDIO_DAC->DHR12L1 = AUDIO_DATA_SILENCE; +#if defined(STM32H5) || defined(STM32H7) || defined(STM32H7RS) + LL_DAC_ClearFlag_DMAUDR1(AUDIO_DAC); + LL_DAC_SetTriggerSource(AUDIO_DAC, LL_DAC_CHANNEL_1, LL_DAC_TRIG_EXT_TIM6_TRGO); + LL_DAC_EnableTrigger(AUDIO_DAC, LL_DAC_CHANNEL_1); + LL_DAC_Enable(AUDIO_DAC, LL_DAC_CHANNEL_1); +#else // clear underrun flag - DAC->SR = DAC_SR_DMAUDR1; + AUDIO_DAC->SR = DAC_SR_DMAUDR1; // use TIM6 TRGO as trigger (TSEL1 = TSEL2 = 000) // enable DAC & channel 1 trigger - DAC->CR = DAC_CR_TEN1 | DAC_CR_EN1; - + AUDIO_DAC->CR = DAC_CR_TEN1 | DAC_CR_EN1; +#endif NVIC_EnableIRQ(AUDIO_DMA_Stream_IRQn); NVIC_SetPriority(AUDIO_DMA_Stream_IRQn, 7); } @@ -175,6 +254,25 @@ void audioUnmute() void audioConsumeCurrentBuffer() { +#if defined(STM32H5) || defined(STM32H7) || defined(STM32H7RS) + if (!LL_DMA_IsEnabledStream(AUDIO_DMA, AUDIO_DMA_Stream)) { + if (!audio_update_dma_buffer(0)) { +#if defined(AUDIO_MUTE_GPIO) + audioUnmute(); +#endif + + LL_DMA_DisableStream(AUDIO_DMA, AUDIO_DMA_Stream); + stm32_dma_check_tc_flag(AUDIO_DMA, AUDIO_DMA_Stream); + stm32_dma_check_ht_flag(AUDIO_DMA, AUDIO_DMA_Stream); + LL_DAC_ClearFlag_DMAUDR1(AUDIO_DAC); + LL_DAC_EnableDMAReq(AUDIO_DAC, LL_DAC_CHANNEL_1); + LL_DAC_Enable(AUDIO_DAC, LL_DAC_CHANNEL_1); + LL_DMA_SetMemoryAddress(AUDIO_DMA, AUDIO_DMA_Stream, (intptr_t)_dma_buffer); + LL_DMA_SetDataLength(AUDIO_DMA, AUDIO_DMA_Stream, DMA_BUFFER_LEN); + LL_DMA_EnableStream(AUDIO_DMA, AUDIO_DMA_Stream); + LL_DMA_EnableIT_HT(AUDIO_DMA, AUDIO_DMA_Stream); + LL_DMA_EnableIT_TC(AUDIO_DMA, AUDIO_DMA_Stream); +#else if (!nextBuffer) { nextBuffer = audioQueue.buffersFifo.getNextFilledBuffer(); if (nextBuffer) { @@ -182,7 +280,6 @@ void audioConsumeCurrentBuffer() #if defined(AUDIO_MUTE_GPIO) audioUnmute(); #endif - // Disable DMA stream AUDIO_DMA_Stream->CR &= ~DMA_SxCR_EN; @@ -199,11 +296,11 @@ void audioConsumeCurrentBuffer() AUDIO_DMA_Stream->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // clear underrun flag - DAC->SR = DAC_SR_DMAUDR1; + AUDIO_DAC->SR = DAC_SR_DMAUDR1; // enable DAC - DAC->CR |= DAC_CR_EN1 | DAC_CR_DMAEN1; - + AUDIO_DAC->CR |= DAC_CR_EN1 | DAC_CR_DMAEN1; +#endif } else { #if defined(AUDIO_MUTE_GPIO) audioMute(); @@ -220,7 +317,7 @@ void audioInit() void audioEnd() { - DAC->CR = 0; + AUDIO_DAC->CR = 0; AUDIO_TIMER->CR1 = 0; // Also need to turn off any possible interrupts @@ -230,6 +327,23 @@ void audioEnd() extern "C" void AUDIO_DMA_Stream_IRQHandler() { +#if defined(STM32H5) || defined(STM32H7) || defined(STM32H7RS) + bool stopDMA = false; + if(stm32_dma_check_ht_flag(AUDIO_DMA, AUDIO_DMA_Stream)) { + stopDMA = audio_update_dma_buffer(0); + } + + if(stm32_dma_check_tc_flag(AUDIO_DMA, AUDIO_DMA_Stream)) { + stopDMA |= audio_update_dma_buffer(1); + } + + if(stopDMA) { + LL_DMA_DisableIT_TC(AUDIO_DMA, AUDIO_DMA_Stream); + LL_DMA_DisableIT_HT(AUDIO_DMA, AUDIO_DMA_Stream); + LL_DMA_DisableStream(AUDIO_DMA, AUDIO_DMA_Stream); + } + +#else // disable transfer complete interrupt AUDIO_DMA_Stream->CR &= ~DMA_SxCR_TCIE; @@ -256,6 +370,7 @@ extern "C" void AUDIO_DMA_Stream_IRQHandler() AUDIO_DMA_Stream->CR |= DMA_SxCR_EN | DMA_SxCR_TCIE; // clear underrun flag - DAC->SR = DAC_SR_DMAUDR1; + AUDIO_DAC->SR = DAC_SR_DMAUDR1; } +#endif } diff --git a/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp b/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp index a26f768e9..634d2303d 100644 --- a/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp +++ b/radio/src/targets/common/arm/stm32/rotary_encoder_driver.cpp @@ -39,7 +39,7 @@ #define ON_DETENT(p) ((p == 3) || (p == 0)) #elif ROTARY_ENCODER_GRANULARITY == 4 #define ON_DETENT(p) (p == 3) -#elif +#else #error "Unknown ROTARY_ENCODER_GRANULARITY" #endif diff --git a/radio/src/targets/common/arm/stm32/sdcard_spi.cpp b/radio/src/targets/common/arm/stm32/sdcard_spi.cpp index 956700248..86586ee10 100644 --- a/radio/src/targets/common/arm/stm32/sdcard_spi.cpp +++ b/radio/src/targets/common/arm/stm32/sdcard_spi.cpp @@ -23,6 +23,9 @@ #include "stm32_spi.h" #include "timers_driver.h" #include "delays_driver.h" +#include "stm32_gpio.h" + +#include "stm32_hal_ll.h" #include "debug.h" #include "crc.h" @@ -244,6 +247,12 @@ static uint8_t sdcard_spi_send_acmd(const stm32_spi_t* spi, uint8_t sd_cmd_idx, static sd_rw_response_t _read_cid(const stm32_spi_t* spi, sdcard_info_t* card); static sd_rw_response_t _read_csd(const stm32_spi_t* spi, sdcard_info_t* card); +static inline void _set_miso_pullup(gpio_t miso) +{ + uint32_t pin = 1 << gpio_get_pin(miso); + LL_GPIO_SetPinPull(gpio_get_port(miso), pin, LL_GPIO_PULL_UP); +} + static sd_init_fsm_state_t _init_sd_fsm_step(const stm32_spi_t* spi, sdcard_info_t* card, sd_init_fsm_state_t state) @@ -251,8 +260,9 @@ static sd_init_fsm_state_t _init_sd_fsm_step(const stm32_spi_t* spi, switch (state) { case SD_INIT_START: TRACE("SD_INIT_START"); - stm32_spi_init(spi, LL_SPI_DATAWIDTH_8BIT, true); + stm32_spi_init(spi, LL_SPI_DATAWIDTH_8BIT); stm32_spi_set_max_baudrate(spi, SD_SPI_CLK_400K); + _set_miso_pullup(spi->MISO); return SD_INIT_SPI_POWER_SEQ; case SD_INIT_SPI_POWER_SEQ: diff --git a/radio/src/targets/common/arm/stm32/stm32_hal_ll.h b/radio/src/targets/common/arm/stm32/stm32_hal_ll.h index 496a845f3..ebcced487 100644 --- a/radio/src/targets/common/arm/stm32/stm32_hal_ll.h +++ b/radio/src/targets/common/arm/stm32/stm32_hal_ll.h @@ -64,6 +64,7 @@ extern "C" { #include "STM32F2xx_HAL_Driver/Inc/stm32f2xx_ll_usb.h" #elif defined(STM32H7) #include "STM32H7xx_HAL_Driver/Inc/stm32h7xx_ll_bus.h" + #include "STM32H7xx_HAL_Driver/Inc/stm32h7xx_ll_dac.h" #include "STM32H7xx_HAL_Driver/Inc/stm32h7xx_ll_fmc.h" #include "STM32H7xx_HAL_Driver/Inc/stm32h7xx_ll_dma2d.h" #include "STM32H7xx_HAL_Driver/Inc/stm32h7xx_ll_gpio.h" diff --git a/radio/src/targets/common/arm/stm32/stm32_spi.cpp b/radio/src/targets/common/arm/stm32/stm32_spi.cpp index 426eeab2e..ca32e5645 100644 --- a/radio/src/targets/common/arm/stm32/stm32_spi.cpp +++ b/radio/src/targets/common/arm/stm32/stm32_spi.cpp @@ -75,41 +75,11 @@ static inline uint32_t _get_spi_af(SPI_TypeDef *SPIx) return LL_GPIO_AF_5; } -#if defined(STM32H7) || defined(STM32H7RS) -static inline uint32_t _get_spi_clocksource(SPI_TypeDef* SPIx) -{ -#if defined(LL_RCC_SPI123_CLKSOURCE) - return LL_RCC_SPI123_CLKSOURCE; -#endif - -#if defined(LL_RCC_SPI1_CLKSOURCE) - if (SPIx == SPI1) return LL_RCC_SPI1_CLKSOURCE; -#endif - -#if defined(LL_RCC_SPI23_CLKSOURCE) - if (SPIx == SPI2 || SPIx == SPI3) return LL_RCC_SPI23_CLKSOURCE; -#endif - -#if defined(LL_RCC_SPI45_CLKSOURCE) - if (SPIx == SPI4 || SPIx == SPI5) return LL_RCC_SPI45_CLKSOURCE; -#endif - -#if defined(LL_RCC_SPI6_CLKSOURCE) - if (SPIx == SPI6) return LL_RCC_SPI6_CLKSOURCE; -#endif - - return 0; -} -#endif - static uint32_t _get_spi_prescaler(SPI_TypeDef *SPIx, uint32_t max_freq) { LL_RCC_ClocksTypeDef RCC_Clocks; LL_RCC_GetSystemClocksFreq(&RCC_Clocks); -#if defined(STM32H7) || defined(STM32H7RS) - uint32_t pclk = LL_RCC_GetSPIClockFreq(_get_spi_clocksource(SPIx)); -#else uint32_t pclk = RCC_Clocks.PCLK2_Frequency; #if defined(SPI2) if (SPIx == SPI2) { @@ -121,7 +91,6 @@ static uint32_t _get_spi_prescaler(SPI_TypeDef *SPIx, uint32_t max_freq) pclk = RCC_Clocks.PCLK1_Frequency; } #endif -#endif // STM32H7 || STM32H7RS uint32_t divider = (pclk + max_freq) / max_freq; uint32_t presc; if (divider > 128) { @@ -145,18 +114,11 @@ static uint32_t _get_spi_prescaler(SPI_TypeDef *SPIx, uint32_t max_freq) return presc; } -static void _init_gpios(const stm32_spi_t* spi, bool enableMisoPullUp) +static void _init_gpios(const stm32_spi_t* spi) { - if(enableMisoPullUp) - { - gpio_init(spi->MISO, GPIO_IN_PU, GPIO_PIN_SPEED_VERY_HIGH); - gpio_set_af(spi->MISO, _get_spi_af(spi->SPIx)); - } else { - gpio_init_af(spi->MISO, _get_spi_af(spi->SPIx), GPIO_PIN_SPEED_VERY_HIGH); - } + gpio_init_af(spi->MISO, _get_spi_af(spi->SPIx), GPIO_PIN_SPEED_VERY_HIGH); gpio_init_af(spi->SCK, _get_spi_af(spi->SPIx), GPIO_PIN_SPEED_VERY_HIGH); gpio_init_af(spi->MOSI, _get_spi_af(spi->SPIx), GPIO_PIN_SPEED_VERY_HIGH); - gpio_init(spi->CS, GPIO_OUT, GPIO_PIN_SPEED_HIGH); } @@ -170,55 +132,25 @@ static void _config_dma_streams(const stm32_spi_t* spi) LL_DMA_InitTypeDef dmaInit; LL_DMA_StructInit(&dmaInit); -#if defined(STM32F2) || defined(STM32F4) dmaInit.Channel = spi->DMA_Channel; dmaInit.PeriphOrM2MSrcAddress = LL_SPI_DMA_GetRegAddr(spi->SPIx); -#endif - -#if defined(STM32F2) || defined(STM32F4) || defined(STM32H7) dmaInit.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; dmaInit.Priority = LL_DMA_PRIORITY_VERYHIGH; dmaInit.FIFOMode = spi->DMA_FIFOMode; dmaInit.FIFOThreshold = spi->DMA_FIFOThreshold; dmaInit.MemoryOrM2MDstDataSize = spi->DMA_MemoryOrM2MDstDataSize; dmaInit.MemBurst = spi->DMA_MemBurst; -#elif defined(STM32H7RS) - dmaInit.Priority = LL_DMA_LOW_PRIORITY_HIGH_WEIGHT; -#endif - -#if defined(STM32H7) - dmaInit.PeriphRequest = spi->rxDMA_PeriphRequest; - dmaInit.PeriphOrM2MSrcAddress = LL_SPI_DMA_GetRxRegAddr(spi->SPIx); -#elif defined(STM32H7RS) - dmaInit.Request = spi->rxDMA_PeriphRequest; - dmaInit.SrcAddress = LL_SPI_DMA_GetRxRegAddr(spi->SPIx); - dmaInit.SrcIncMode = LL_DMA_SRC_FIXED; - dmaInit.DestIncMode = LL_DMA_DEST_INCREMENT; - dmaInit.DestDataWidth = LL_DMA_DEST_DATAWIDTH_HALFWORD; // TODO -#endif - dmaInit.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; LL_DMA_Init(spi->DMA, spi->rxDMA_Stream, &dmaInit); -#if defined(STM32H7) - dmaInit.PeriphRequest = spi->txDMA_PeriphRequest; - dmaInit.PeriphOrM2MSrcAddress = LL_SPI_DMA_GetTxRegAddr(spi->SPIx); -#elif defined(STM32H7RS) - dmaInit.Request = spi->rxDMA_PeriphRequest; - dmaInit.DestAddress = LL_SPI_DMA_GetTxRegAddr(spi->SPIx); - dmaInit.SrcIncMode = LL_DMA_SRC_INCREMENT; - dmaInit.DestIncMode = LL_DMA_DEST_FIXED; - dmaInit.SrcDataWidth = LL_DMA_SRC_DATAWIDTH_HALFWORD; // TODO -#endif - dmaInit.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; LL_DMA_Init(spi->DMA, spi->txDMA_Stream, &dmaInit); } #endif -void stm32_spi_init(const stm32_spi_t* spi, uint32_t data_width, bool misoPullUp) +void stm32_spi_init(const stm32_spi_t* spi, uint32_t data_width) { - _init_gpios(spi, misoPullUp); + _init_gpios(spi); auto SPIx = spi->SPIx; stm32_spi_enable_clock(SPIx); @@ -234,9 +166,6 @@ void stm32_spi_init(const stm32_spi_t* spi, uint32_t data_width, bool misoPullUp LL_SPI_Init(SPIx, &spiInit); LL_SPI_Enable(SPIx); -#if defined(STM32H7) || defined(STM32H7RS) - LL_SPI_StartMasterTransfer(SPIx); -#endif #if defined(USE_SPI_DMA) if (spi->DMA) { @@ -245,8 +174,6 @@ void stm32_spi_init(const stm32_spi_t* spi, uint32_t data_width, bool misoPullUp #endif } -// void stm32_spi_deinit(const stm32_spi_t* spi); - void stm32_spi_select(const stm32_spi_t* spi) { gpio_clear(spi->CS); @@ -261,36 +188,22 @@ void stm32_spi_set_max_baudrate(const stm32_spi_t* spi, uint32_t baudrate) { auto* SPIx = spi->SPIx; uint32_t presc = _get_spi_prescaler(SPIx, baudrate); -#if defined(STM32H7) || defined(STM32H7RS) - LL_SPI_Disable(SPIx); -#endif LL_SPI_SetBaudRatePrescaler(SPIx, presc); -#if defined(STM32H7) || defined(STM32H7RS) - LL_SPI_Enable(SPIx); - LL_SPI_StartMasterTransfer(SPIx); -#endif } uint8_t stm32_spi_transfer_byte(const stm32_spi_t* spi, uint8_t out) { auto* SPIx = spi->SPIx; -#if defined(STM32H7) || defined(STM32H7RS) - while (!LL_SPI_IsActiveFlag_TXP(SPIx)); -#else + while (!LL_SPI_IsActiveFlag_TXE(SPIx)); -#endif LL_SPI_TransmitData8(SPIx, out); -#if defined(STM32H7) || defined(STM32H7RS) - while (!LL_SPI_IsActiveFlag_RXP(SPIx)); -#else while (!LL_SPI_IsActiveFlag_RXNE(SPIx)); -#endif return LL_SPI_ReceiveData8(SPIx); } -uint16_t stm32_spi_transfer_bytes(const stm32_spi_t* spi, const uint8_t* out, - uint8_t* in, uint16_t length) +uint32_t stm32_spi_transfer_bytes(const stm32_spi_t* spi, const uint8_t* out, + uint8_t* in, uint32_t length) { unsigned trans_bytes = 0; uint8_t in_temp; @@ -312,18 +225,10 @@ uint16_t stm32_spi_transfer_bytes(const stm32_spi_t* spi, const uint8_t* out, uint16_t stm32_spi_transfer_word(const stm32_spi_t* spi, uint16_t out) { auto* SPIx = spi->SPIx; -#if defined(STM32H7) || defined(STM32H7RS) - while (!LL_SPI_IsActiveFlag_TXP(SPIx)); -#else while (!LL_SPI_IsActiveFlag_TXE(SPIx)); -#endif LL_SPI_TransmitData16(SPIx, out); -#if defined(STM32H7) || defined(STM32H7RS) - while (!LL_SPI_IsActiveFlag_RXP(SPIx)); -#else while (!LL_SPI_IsActiveFlag_RXNE(SPIx)); -#endif return LL_SPI_ReceiveData16(SPIx); } @@ -336,12 +241,6 @@ extern uint32_t _sram; extern uint32_t _eram; #define _IS_DMA_BUFFER(addr) \ ((intptr_t)(addr) >= (intptr_t)&_sram && (intptr_t)(addr) <= (intptr_t)&_eram) -#elif defined(STM32H7) || defined(STM32H7RS) -extern uint32_t _s_dram; -extern uint32_t _e_dram; -#define _IS_DMA_BUFFER(addr) \ - ((intptr_t)(addr) >= (intptr_t)&_s_dram && \ - (intptr_t)(addr) <= (intptr_t)&_e_dram) #else #define _IS_DMA_BUFFER(addr) (true) #endif @@ -349,19 +248,17 @@ extern uint32_t _e_dram; #define _IS_ALIGNED(addr) (((intptr_t)(addr) & 3U) == 0U) static void _dma_enable_stream(DMA_TypeDef* DMAx, uint32_t stream, - const void* data, uint32_t length) + const void* data, uint32_t length) { stm32_dma_check_tc_flag(DMAx, stream); -#if !defined(STM32H7RS) LL_DMA_SetMemoryAddress(DMAx, stream, (uintptr_t)data); LL_DMA_SetDataLength(DMAx, stream, length); LL_DMA_EnableStream(DMAx, stream); -#endif } #endif -uint16_t stm32_spi_dma_receive_bytes(const stm32_spi_t* spi, - uint8_t* data, uint16_t length) +uint32_t stm32_spi_dma_receive_bytes(const stm32_spi_t* spi, uint8_t* data, + uint32_t length) { #if defined(USE_SPI_DMA) if (!spi->DMA) { @@ -377,26 +274,18 @@ uint16_t stm32_spi_dma_receive_bytes(const stm32_spi_t* spi, LL_SPI_EnableDMAReq_RX(spi->SPIx); _scratch_byte = 0xFFFF; -#if !defined(STM32H7RS) LL_DMA_SetMemoryIncMode(spi->DMA, spi->txDMA_Stream, LL_DMA_MEMORY_NOINCREMENT); -#endif _dma_enable_stream(spi->DMA, spi->txDMA_Stream, &_scratch_byte, length); LL_SPI_EnableDMAReq_TX(spi->SPIx); // Wait for end of DMA transfer while(!stm32_dma_check_tc_flag(spi->DMA, spi->rxDMA_Stream)); -#if !defined(STM32H7) && !defined(STM32H7RS) // Wait for TXE=1 while (!LL_SPI_IsActiveFlag_TXE(spi->SPIx)); // Wait for BSY=0 while(LL_SPI_IsActiveFlag_BSY(spi->SPIx)); -#else - // Wait for EOT=1 - while (!LL_SPI_IsActiveFlag_EOT(spi->SPIx)); - LL_SPI_ClearFlag_EOT(spi->SPIx); -#endif // Disable SPI TX/RX DMA requests LL_SPI_DisableDMAReq_TX(spi->SPIx); @@ -412,8 +301,8 @@ uint16_t stm32_spi_dma_receive_bytes(const stm32_spi_t* spi, #endif } -uint16_t stm32_spi_dma_transmit_bytes(const stm32_spi_t* spi, - const uint8_t* data, uint16_t length) +uint32_t stm32_spi_dma_transmit_bytes(const stm32_spi_t* spi, + const uint8_t* data, uint32_t length) { #if defined(USE_SPI_DMA) if (!spi->DMA) { @@ -426,17 +315,13 @@ uint16_t stm32_spi_dma_transmit_bytes(const stm32_spi_t* spi, data = _scratch_buffer; } -#if !defined(STM32H7RS) LL_DMA_SetMemoryIncMode(spi->DMA, spi->txDMA_Stream, LL_DMA_MEMORY_INCREMENT); -#endif - _dma_enable_stream(spi->DMA, spi->txDMA_Stream, data, length); LL_SPI_EnableDMAReq_TX(spi->SPIx); // Wait for end of DMA transfer while (!stm32_dma_check_tc_flag(spi->DMA, spi->txDMA_Stream)); -#if !defined(STM32H7) && !defined(STM32H7RS) // Wait for TXE=1 while (!LL_SPI_IsActiveFlag_TXE(spi->SPIx)); @@ -447,16 +332,6 @@ uint16_t stm32_spi_dma_transmit_bytes(const stm32_spi_t* spi, if (LL_SPI_IsActiveFlag_RXNE(spi->SPIx)) { (void)LL_SPI_ReceiveData8(spi->SPIx); } -#else - // Wait for EOT=1 - while (!LL_SPI_IsActiveFlag_EOT(spi->SPIx)); - LL_SPI_ClearFlag_EOT(spi->SPIx); - - // Clear RX FIFO - while (LL_SPI_IsActiveFlag_RXP(spi->SPIx)) { - (void)LL_SPI_ReceiveData8(spi->SPIx); - } -#endif // Disable SPI TX DMA requests LL_SPI_DisableDMAReq_TX(spi->SPIx); diff --git a/radio/src/targets/common/arm/stm32/stm32_spi.h b/radio/src/targets/common/arm/stm32/stm32_spi.h index 510395d54..2fe3972c2 100644 --- a/radio/src/targets/common/arm/stm32/stm32_spi.h +++ b/radio/src/targets/common/arm/stm32/stm32_spi.h @@ -50,22 +50,29 @@ struct stm32_spi_t { void stm32_spi_enable_clock(SPI_TypeDef *SPIx); -void stm32_spi_init(const stm32_spi_t* spi, uint32_t data_width, bool misoPullUp = false); +void stm32_spi_init(const stm32_spi_t* spi, uint32_t data_width); void stm32_spi_deinit(const stm32_spi_t* spi); void stm32_spi_select(const stm32_spi_t* spi); void stm32_spi_unselect(const stm32_spi_t* spi); void stm32_spi_set_max_baudrate(const stm32_spi_t* spi, uint32_t baudrate); +void stm32_spi_set_data_width(const stm32_spi_t* spi, uint32_t data_width); uint8_t stm32_spi_transfer_byte(const stm32_spi_t* spi, uint8_t out); uint16_t stm32_spi_transfer_word(const stm32_spi_t* spi, uint16_t out); -uint16_t stm32_spi_transfer_bytes(const stm32_spi_t* spi, const uint8_t* out, - uint8_t* in, uint16_t length); +uint32_t stm32_spi_transfer_bytes(const stm32_spi_t* spi, const uint8_t* out, + uint8_t* in, uint32_t length); -uint16_t stm32_spi_dma_receive_bytes(const stm32_spi_t* spi, uint8_t* data, - uint16_t length); +uint32_t stm32_spi_transfer_words(const stm32_spi_t* spi, const uint16_t* out, + uint16_t* in, uint32_t length); -uint16_t stm32_spi_dma_transmit_bytes(const stm32_spi_t* spi, const uint8_t* data, - uint16_t length); +uint32_t stm32_spi_dma_receive_bytes(const stm32_spi_t* spi, uint8_t* data, + uint32_t length); + +uint32_t stm32_spi_dma_transmit_bytes(const stm32_spi_t* spi, + const uint8_t* data, uint32_t length); + +uint32_t stm32_spi_dma_transmit_words(const stm32_spi_t* spi, + const uint16_t* data, uint32_t length); diff --git a/radio/src/targets/common/arm/stm32/stm32_spi_h7.cpp b/radio/src/targets/common/arm/stm32/stm32_spi_h7.cpp new file mode 100644 index 000000000..5e6ad0a06 --- /dev/null +++ b/radio/src/targets/common/arm/stm32/stm32_spi_h7.cpp @@ -0,0 +1,493 @@ +/* + * Copyright (C) EdgeTx + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "stm32_spi.h" +#include "stm32_dma.h" +#include "stm32_gpio.h" + +#include "stm32_hal.h" +#include "stm32_hal_ll.h" + +#include + +#if !defined(SPI_DISABLE_DMA) +#define USE_SPI_DMA +#endif + +#define SPI_DUMMY_BYTE (0xFF) +#define SPI_MAX_XFER_SIZE (16*1024) + +void stm32_spi_enable_clock(SPI_TypeDef *SPIx) +{ + if (SPIx == SPI1) { + __HAL_RCC_SPI1_CLK_ENABLE(); + } +#if defined(SPI2) + else if (SPIx == SPI2) { + __HAL_RCC_SPI2_CLK_ENABLE(); + } +#endif +#if defined(SPI3) + else if (SPIx == SPI3) { + __HAL_RCC_SPI3_CLK_ENABLE(); + } +#endif +#if defined(SPI4) + else if (SPIx == SPI4) { + __HAL_RCC_SPI4_CLK_ENABLE(); + } +#endif +#if defined(SPI5) + else if (SPIx == SPI5) { + __HAL_RCC_SPI5_CLK_ENABLE(); + } +#endif +#if defined(SPI6) + else if (SPIx == SPI6) { + __HAL_RCC_SPI6_CLK_ENABLE(); + } +#endif +} + +static inline uint32_t _get_spi_af(SPI_TypeDef *SPIx) +{ +#if defined(SPI3) + if (SPIx == SPI3) return LL_GPIO_AF_6; +#endif + return LL_GPIO_AF_5; +} + +static inline uint32_t _get_spi_clocksource(SPI_TypeDef* SPIx) +{ +#if defined(LL_RCC_SPI123_CLKSOURCE) + return LL_RCC_SPI123_CLKSOURCE; +#endif + +#if defined(LL_RCC_SPI1_CLKSOURCE) + if (SPIx == SPI1) return LL_RCC_SPI1_CLKSOURCE; +#endif + +#if defined(LL_RCC_SPI23_CLKSOURCE) + if (SPIx == SPI2 || SPIx == SPI3) return LL_RCC_SPI23_CLKSOURCE; +#endif + +#if defined(LL_RCC_SPI45_CLKSOURCE) + if (SPIx == SPI4 || SPIx == SPI5) return LL_RCC_SPI45_CLKSOURCE; +#endif + +#if defined(LL_RCC_SPI6_CLKSOURCE) + if (SPIx == SPI6) return LL_RCC_SPI6_CLKSOURCE; +#endif + + return 0; +} + +static uint32_t _get_spi_prescaler(SPI_TypeDef *SPIx, uint32_t max_freq) +{ + LL_RCC_ClocksTypeDef RCC_Clocks; + LL_RCC_GetSystemClocksFreq(&RCC_Clocks); + + uint32_t pclk = LL_RCC_GetSPIClockFreq(_get_spi_clocksource(SPIx)); +#if defined(SPI2) + if (SPIx == SPI2) { + pclk = RCC_Clocks.PCLK1_Frequency; + } +#endif +#if defined(SPI3) + if (SPIx == SPI3) { + pclk = RCC_Clocks.PCLK1_Frequency; + } +#endif + uint32_t divider = (pclk + max_freq) / max_freq; + uint32_t presc; + if (divider > 128) { + presc = LL_SPI_BAUDRATEPRESCALER_DIV256; + } else if (divider > 64) { + presc = LL_SPI_BAUDRATEPRESCALER_DIV128; + } else if (divider > 32) { + presc = LL_SPI_BAUDRATEPRESCALER_DIV64; + } else if (divider > 16) { + presc = LL_SPI_BAUDRATEPRESCALER_DIV32; + } else if (divider > 8) { + presc = LL_SPI_BAUDRATEPRESCALER_DIV16; + } else if (divider > 4) { + presc = LL_SPI_BAUDRATEPRESCALER_DIV8; + } else if (divider > 2) { + presc = LL_SPI_BAUDRATEPRESCALER_DIV4; + } else { + presc = LL_SPI_BAUDRATEPRESCALER_DIV2; + } + + return presc; +} + +static void _init_gpios(const stm32_spi_t* spi) +{ + gpio_init_af(spi->MISO, _get_spi_af(spi->SPIx), GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(spi->SCK, _get_spi_af(spi->SPIx), GPIO_PIN_SPEED_VERY_HIGH); + gpio_init_af(spi->MOSI, _get_spi_af(spi->SPIx), GPIO_PIN_SPEED_VERY_HIGH); + gpio_init(spi->CS, GPIO_OUT, GPIO_PIN_SPEED_HIGH); +} + +#if defined(USE_SPI_DMA) +static void _config_dma_streams(const stm32_spi_t* spi) +{ + stm32_dma_enable_clock(spi->DMA); + LL_DMA_DeInit(spi->DMA, spi->rxDMA_Stream); + LL_DMA_DeInit(spi->DMA, spi->txDMA_Stream); + + LL_DMA_InitTypeDef dmaInit; + LL_DMA_StructInit(&dmaInit); + +#if defined(STM32H7) + dmaInit.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dmaInit.Priority = LL_DMA_PRIORITY_VERYHIGH; + dmaInit.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD; + dmaInit.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD; +#elif defined(STM32H7RS) + dmaInit.Priority = LL_DMA_LOW_PRIORITY_HIGH_WEIGHT; +#endif + +#if defined(STM32H7) + dmaInit.PeriphRequest = spi->rxDMA_PeriphRequest; + dmaInit.PeriphOrM2MSrcAddress = LL_SPI_DMA_GetRxRegAddr(spi->SPIx); +#elif defined(STM32H7RS) + dmaInit.Request = spi->rxDMA_PeriphRequest; + dmaInit.SrcAddress = LL_SPI_DMA_GetRxRegAddr(spi->SPIx); + dmaInit.SrcIncMode = LL_DMA_SRC_FIXED; + dmaInit.DestIncMode = LL_DMA_DEST_INCREMENT; + dmaInit.SrcDataWidth = LL_DMA_DEST_DATAWIDTH_HALFWORD; + dmaInit.DestDataWidth = LL_DMA_DEST_DATAWIDTH_HALFWORD; +#endif + + dmaInit.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + LL_DMA_Init(spi->DMA, spi->rxDMA_Stream, &dmaInit); + +#if defined(STM32H7) + dmaInit.PeriphRequest = spi->txDMA_PeriphRequest; + dmaInit.PeriphOrM2MSrcAddress = LL_SPI_DMA_GetTxRegAddr(spi->SPIx); +#elif defined(STM32H7RS) + dmaInit.Request = spi->rxDMA_PeriphRequest; + dmaInit.DestAddress = LL_SPI_DMA_GetTxRegAddr(spi->SPIx); + dmaInit.SrcIncMode = LL_DMA_SRC_INCREMENT; + dmaInit.DestIncMode = LL_DMA_DEST_FIXED; + dmaInit.SrcDataWidth = LL_DMA_SRC_DATAWIDTH_HALFWORD; + dmaInit.DestDataWidth = LL_DMA_SRC_DATAWIDTH_HALFWORD; +#endif + + dmaInit.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + LL_DMA_Init(spi->DMA, spi->txDMA_Stream, &dmaInit); +} +#endif + +void stm32_spi_init(const stm32_spi_t* spi, uint32_t data_width) +{ + _init_gpios(spi); + + auto SPIx = spi->SPIx; + stm32_spi_enable_clock(SPIx); + LL_SPI_DeInit(SPIx); + + LL_SPI_InitTypeDef spiInit; + LL_SPI_StructInit(&spiInit); + + spiInit.TransferDirection = LL_SPI_FULL_DUPLEX; + spiInit.Mode = LL_SPI_MODE_MASTER; + spiInit.NSS = LL_SPI_NSS_SOFT; + spiInit.DataWidth = data_width; + + LL_SPI_Init(SPIx, &spiInit); + +#if defined(USE_SPI_DMA) + if (spi->DMA) { + _config_dma_streams(spi); + } +#endif +} + +void stm32_spi_select(const stm32_spi_t* spi) +{ + gpio_clear(spi->CS); +} + +void stm32_spi_unselect(const stm32_spi_t* spi) +{ + gpio_set(spi->CS); +} + +void stm32_spi_set_max_baudrate(const stm32_spi_t* spi, uint32_t baudrate) +{ + auto* SPIx = spi->SPIx; + uint32_t presc = _get_spi_prescaler(SPIx, baudrate); + LL_SPI_SetBaudRatePrescaler(SPIx, presc); +} + +void stm32_spi_set_data_width(const stm32_spi_t* spi, uint32_t data_width) +{ + auto* SPIx = spi->SPIx; + LL_SPI_SetDataWidth(SPIx, data_width); +} + +static inline void spi_close_transfer(SPI_TypeDef* SPIx) +{ + while (!LL_SPI_IsActiveFlag_EOT(SPIx)) { + } + + LL_SPI_ClearFlag_EOT(SPIx); + LL_SPI_ClearFlag_TXTF(SPIx); + + LL_SPI_Disable(SPIx); + CLEAR_BIT(SPIx->CFG1, SPI_CFG1_TXDMAEN | SPI_CFG1_RXDMAEN); +} + +static void spi_transfer_8(SPI_TypeDef* SPIx, const uint8_t** tx_data, + unsigned tx_inc, uint8_t** rx_data, unsigned rx_inc, + uint16_t length) +{ + unsigned rx_len = length; + unsigned tx_len = length; + + LL_SPI_SetTransferSize(SPIx, length); + LL_SPI_Enable(SPIx); + + LL_SPI_StartMasterTransfer(SPIx); + + while (rx_len > 0 || tx_len > 0) { + if (LL_SPI_IsActiveFlag_TXP(SPIx) && (tx_len > 0)) { + LL_SPI_TransmitData8(SPIx, **tx_data); + *tx_data += tx_inc; + tx_len--; + } + + if (LL_SPI_IsActiveFlag_RXP(SPIx) && (rx_len > 0)) { + **rx_data = LL_SPI_ReceiveData8(SPIx); + *rx_data += rx_inc; + rx_len--; + } + } + spi_close_transfer(SPIx); +} + +uint32_t stm32_spi_transfer_bytes(const stm32_spi_t* spi, const uint8_t* out, + uint8_t* in, uint32_t length) +{ + auto* SPIx = spi->SPIx; + + unsigned rx_inc = 1; + unsigned tx_inc = 1; + + static uint8_t _scratch_data; + + const uint8_t* tx_data = out; + if (!tx_data) { + tx_data = &_scratch_data; + tx_inc = 0; + } + + uint8_t* rx_data = in; + if (!rx_data) { + rx_data = &_scratch_data; + rx_inc = 0; + } + + uint32_t xfer_len = length; + while (xfer_len > 0) { + uint16_t single_xfer_len = + xfer_len > SPI_MAX_XFER_SIZE ? SPI_MAX_XFER_SIZE : xfer_len; + spi_transfer_8(SPIx, &tx_data, tx_inc, &rx_data, rx_inc, single_xfer_len); + xfer_len -= single_xfer_len; + } + + return length; +} + +static void spi_transfer_16(SPI_TypeDef* SPIx, const uint16_t** tx_data, + unsigned tx_inc, uint16_t** rx_data, unsigned rx_inc, + uint16_t length) +{ + unsigned rx_len = length; + unsigned tx_len = length; + + LL_SPI_SetTransferSize(SPIx, length); + LL_SPI_Enable(SPIx); + + LL_SPI_StartMasterTransfer(SPIx); + + while (rx_len > 0 || tx_len > 0) { + if (LL_SPI_IsActiveFlag_TXP(SPIx) && (tx_len > 0)) { + LL_SPI_TransmitData16(SPIx, **tx_data); + *tx_data += tx_inc; + tx_len--; + } + + if (LL_SPI_IsActiveFlag_RXP(SPIx) && (rx_len > 0)) { + **rx_data = LL_SPI_ReceiveData16(SPIx); + *rx_data += rx_inc; + rx_len--; + } + } + spi_close_transfer(SPIx); +} + +uint32_t stm32_spi_transfer_words(const stm32_spi_t* spi, const uint16_t* out, + uint16_t* in, uint32_t length) +{ + auto* SPIx = spi->SPIx; + + unsigned rx_inc = 1; + unsigned tx_inc = 1; + + static uint16_t _scratch_data; + + const uint16_t* tx_data = out; + if (!tx_data) { + tx_data = &_scratch_data; + tx_inc = 0; + } + + uint16_t* rx_data = in; + if (!rx_data) { + rx_data = &_scratch_data; + rx_inc = 0; + } + + uint32_t xfer_len = length; + while (xfer_len > 0) { + uint16_t single_xfer_len = + xfer_len > SPI_MAX_XFER_SIZE ? SPI_MAX_XFER_SIZE : xfer_len; + spi_transfer_16(SPIx, &tx_data, tx_inc, &rx_data, rx_inc, single_xfer_len); + xfer_len -= single_xfer_len; + } + + return length; +} + +uint8_t stm32_spi_transfer_byte(const stm32_spi_t* spi, uint8_t out) +{ + uint8_t in; + stm32_spi_transfer_bytes(spi, &out, &in, 1); + return in; +} + +uint16_t stm32_spi_transfer_word(const stm32_spi_t* spi, uint16_t out) +{ + uint16_t in; + stm32_spi_transfer_words(spi, &out, &in, 1); + return in; +} + +#if defined(USE_SPI_DMA) +static void _dma_enable_stream(DMA_TypeDef* DMAx, uint32_t stream, + const void* data, uint32_t length) +{ + stm32_dma_check_tc_flag(DMAx, stream); +#if !defined(STM32H7RS) + LL_DMA_SetMemoryAddress(DMAx, stream, (uintptr_t)data); + LL_DMA_SetDataLength(DMAx, stream, length); + LL_DMA_EnableStream(DMAx, stream); +#endif +} +#endif + +static void spi_receive_dma(const stm32_spi_t* spi, void* data, uint16_t length) +{ + _dma_enable_stream(spi->DMA, spi->rxDMA_Stream, data, length); + + LL_SPI_SetTransferSize(spi->SPIx, length); + LL_SPI_EnableDMAReq_RX(spi->SPIx); + LL_SPI_Enable(spi->SPIx); + LL_SPI_StartMasterTransfer(spi->SPIx); + + // Wait for end of DMA transfer + while (!stm32_dma_check_tc_flag(spi->DMA, spi->rxDMA_Stream)); + spi_close_transfer(spi->SPIx); +} + +uint32_t stm32_spi_dma_receive_bytes(const stm32_spi_t* spi, uint8_t* data, + uint32_t length) +{ +#if !defined(USE_SPI_DMA) + return stm32_spi_transfer_bytes(spi, nullptr, data, length); +#else + uint32_t xfer_len = length / 2; + while (xfer_len > 0) { + uint16_t single_xfer_len = + xfer_len > SPI_MAX_XFER_SIZE ? SPI_MAX_XFER_SIZE : xfer_len; + spi_receive_dma(spi, data, single_xfer_len); + xfer_len -= single_xfer_len; + data += single_xfer_len * 2; + } + + return length; +#endif +} + +static void spi_transmit_dma(const stm32_spi_t* spi, const void* data, uint16_t length) +{ + _dma_enable_stream(spi->DMA, spi->txDMA_Stream, data, length); + + LL_SPI_SetTransferSize(spi->SPIx, length); + LL_SPI_EnableDMAReq_TX(spi->SPIx); + LL_SPI_Enable(spi->SPIx); + LL_SPI_StartMasterTransfer(spi->SPIx); + + // Wait for end of DMA transfer + while (!stm32_dma_check_tc_flag(spi->DMA, spi->txDMA_Stream)); + spi_close_transfer(spi->SPIx); +} + +uint32_t stm32_spi_dma_transmit_bytes(const stm32_spi_t* spi, + const uint8_t* data, uint32_t length) +{ +#if !defined(USE_SPI_DMA) + return stm32_spi_transfer_bytes(spi, data, nullptr, length); +#else + uint32_t xfer_len = length / 2; + while (xfer_len > 0) { + uint16_t single_xfer_len = + xfer_len > SPI_MAX_XFER_SIZE ? SPI_MAX_XFER_SIZE : xfer_len; + spi_transmit_dma(spi, data, single_xfer_len); + xfer_len -= single_xfer_len; + data += single_xfer_len * 2; + } + + return length; +#endif +} + +uint32_t stm32_spi_dma_transmit_words(const stm32_spi_t* spi, + const uint16_t* data, uint32_t length) +{ +#if !defined(USE_SPI_DMA) + return stm32_spi_transfer_words(spi, data, nullptr, length); +#else + uint32_t xfer_len = length; + while (xfer_len > 0) { + uint16_t single_xfer_len = + xfer_len > SPI_MAX_XFER_SIZE ? SPI_MAX_XFER_SIZE : xfer_len; + spi_transmit_dma(spi, data, single_xfer_len); + xfer_len -= single_xfer_len; + data += single_xfer_len; + } + + return length; +#endif +}