From be527efc1ca14c4900e667aac806dacfb83d24e8 Mon Sep 17 00:00:00 2001 From: 3djc <5167938+3djc@users.noreply.github.com> Date: Thu, 24 Sep 2026 04:04:29 +0200 Subject: [PATCH] fix(gx15): no sound when recording mic on prod radio (#7813) --- .../boards/rm-h750/pdm_software_driver.cpp | 99 +++++++++++++------ radio/src/targets/gx15/hal.h | 10 +- 2 files changed, 72 insertions(+), 37 deletions(-) diff --git a/radio/src/boards/rm-h750/pdm_software_driver.cpp b/radio/src/boards/rm-h750/pdm_software_driver.cpp index 53373de4d..38756953c 100644 --- a/radio/src/boards/rm-h750/pdm_software_driver.cpp +++ b/radio/src/boards/rm-h750/pdm_software_driver.cpp @@ -28,11 +28,16 @@ #if defined(PDM_CAPTURE_DMA) -static constexpr uint32_t PDM_SAI_MCKDIV = +static constexpr uint32_t PDM_CLOCK_DIV = PDM_SAI_KER_FREQ / PDM_CLOCK_FREQ; -static_assert(PDM_SAI_MCKDIV >= 1 && PDM_SAI_MCKDIV <= 63, +#if defined(PDM_CLOCK_DFSDM) +static_assert(PDM_CLOCK_DIV >= 2 && PDM_CLOCK_DIV <= 256, + "PDM clock divider out of range for DFSDM_CHCFGR1_CKOUTDIV"); +#else +static_assert(PDM_CLOCK_DIV >= 1 && PDM_CLOCK_DIV <= 63, "PDM_SAI MCKDIV out of range for SAI_xCR1_MCKDIV (6 bits)"); +#endif // Burst size: ~6 ms of PDM at 1.6 MHz (multiple of 32 required for word packing). static constexpr uint32_t PDM_BURST_BITS = 10080; @@ -56,6 +61,63 @@ static uint32_t cic_bitsSeen = 0; static bool pdmRunning = false; +#if defined(PDM_CLOCK_DFSDM) +// DFSDM1 CKOUT driven from the audio clock (SAI1 kernel clock); only the +// clock output is used, capture stays on the timer + DMA path. +static void pdmClockStart() +{ + SET_BIT(RCC->APB2ENR, RCC_APB2ENR_SAI1EN | RCC_APB2ENR_DFSDM1EN); + (void)READ_BIT(RCC->APB2ENR, RCC_APB2ENR_DFSDM1EN); + + CLEAR_BIT(DFSDM1_Channel0->CHCFGR1, DFSDM_CHCFGR1_DFSDMEN); + DFSDM1_Channel0->CHCFGR1 = + DFSDM_CHCFGR1_CKOUTSRC | + ((PDM_CLOCK_DIV - 1U) << DFSDM_CHCFGR1_CKOUTDIV_Pos); + SET_BIT(DFSDM1_Channel0->CHCFGR1, DFSDM_CHCFGR1_DFSDMEN); +} + +static void pdmClockStop() +{ + CLEAR_BIT(DFSDM1_Channel0->CHCFGR1, DFSDM_CHCFGR1_DFSDMEN); +} +#else +static void pdmClockStart() +{ + SET_BIT(RCC->APB2ENR, RCC_APB2ENR_SAI1EN); + (void)READ_BIT(RCC->APB2ENR, RCC_APB2ENR_SAI1EN); + + SAI_Block_TypeDef* block = PDM_SAI_BLOCK; + + CLEAR_BIT(block->CR1, SAI_xCR1_SAIEN); + while (READ_BIT(block->CR1, SAI_xCR1_SAIEN)) {} + + block->CR1 = (0U << SAI_xCR1_MODE_Pos) // Master TX + | (0U << SAI_xCR1_PRTCFG_Pos) // Free protocol + | (4U << SAI_xCR1_DS_Pos) // 16-bit data + | SAI_xCR1_NODIV // BCLK = ker_ck / MCKDIV + | (PDM_CLOCK_DIV << SAI_xCR1_MCKDIV_Pos); + + block->CR2 = (1U << SAI_xCR2_FTH_Pos); // FIFO threshold = 1/4 full + + block->FRCR = (15U << 0) // FRL = 15 (16-bit frame) + | (7U << 8); // FSALL = 7 + block->SLOTR = (0U << 8) // NBSLOT = 0 -> 1 slot + | (1U << 16); // SLOTEN slot 0 + + // Prime the FIFO so the block starts clocking immediately (no underrun). + for (int i = 0; i < 4; ++i) block->DR = 0U; + + SET_BIT(block->CR1, SAI_xCR1_SAIEN); +} + +static void pdmClockStop() +{ + SAI_Block_TypeDef* block = PDM_SAI_BLOCK; + CLEAR_BIT(block->CR1, SAI_xCR1_SAIEN); + while (READ_BIT(block->CR1, SAI_xCR1_SAIEN)) {} +} +#endif + void pdmStart() { if (pdmRunning) return; @@ -71,31 +133,7 @@ void pdmStart() gpio_init_af(PDM_CLOCK, PDM_CLOCK_GPIO_AF, GPIO_PIN_SPEED_VERY_HIGH); gpio_init(PDM_DATA, GPIO_IN, GPIO_PIN_SPEED_VERY_HIGH); - SET_BIT(RCC->APB2ENR, RCC_APB2ENR_SAI1EN); - (void)READ_BIT(RCC->APB2ENR, RCC_APB2ENR_SAI1EN); - - SAI_Block_TypeDef* block = PDM_SAI_BLOCK; - - CLEAR_BIT(block->CR1, SAI_xCR1_SAIEN); - while (READ_BIT(block->CR1, SAI_xCR1_SAIEN)) {} - - block->CR1 = (0U << SAI_xCR1_MODE_Pos) // Master TX - | (0U << SAI_xCR1_PRTCFG_Pos) // Free protocol - | (4U << SAI_xCR1_DS_Pos) // 16-bit data - | SAI_xCR1_NODIV // BCLK = ker_ck / MCKDIV - | (PDM_SAI_MCKDIV << SAI_xCR1_MCKDIV_Pos); - - block->CR2 = (1U << SAI_xCR2_FTH_Pos); // FIFO threshold = 1/4 full - - block->FRCR = (15U << 0) // FRL = 15 (16-bit frame) - | (7U << 8); // FSALL = 7 - block->SLOTR = (0U << 8) // NBSLOT = 0 -> 1 slot - | (1U << 16); // SLOTEN slot 0 - - // Prime the FIFO so the block starts clocking immediately (no underrun). - for (int i = 0; i < 4; ++i) block->DR = 0U; - - SET_BIT(block->CR1, SAI_xCR1_SAIEN); + pdmClockStart(); // TIM15 is free because FLYSKY_GIMBAL is OFF. SET_BIT(RCC->APB2ENR, RCC_APB2ENR_TIM15EN); @@ -148,11 +186,8 @@ void pdmStop() LL_DMA_DisableStream(PDM_CAPTURE_DMA, PDM_CAPTURE_DMA_STREAM); while (LL_DMA_IsEnabledStream(PDM_CAPTURE_DMA, PDM_CAPTURE_DMA_STREAM)) {} - // Disable the SAI block — this stops the PDM clock to the mic, putting it - // into low-power mode. - SAI_Block_TypeDef* block = PDM_SAI_BLOCK; - CLEAR_BIT(block->CR1, SAI_xCR1_SAIEN); - while (READ_BIT(block->CR1, SAI_xCR1_SAIEN)) {} + // Stopping the PDM clock puts the mic into low-power mode. + pdmClockStop(); pdmRunning = false; } diff --git a/radio/src/targets/gx15/hal.h b/radio/src/targets/gx15/hal.h index 396f745d6..0fc8691f4 100644 --- a/radio/src/targets/gx15/hal.h +++ b/radio/src/targets/gx15/hal.h @@ -186,11 +186,11 @@ TIM17: ROTARY_ENCODER_TIMER #define I2S_DMA_Stream_IRQHandler DMA1_Stream4_IRQHandler #if !defined(FLYSKY_GIMBAL) - #define PDM_CLOCK GPIO_PIN(GPIOE, 5) - #define PDM_CLOCK_GPIO_AF LL_GPIO_AF_6 - #define PDM_SAI_BLOCK SAI1_Block_A - #define PDM_SAI_KER_FREQ 48000000 // PLL1Q (see system_clock.c) - #define PDM_CLOCK_FREQ 1600000 // 1.6 MHz → MCKDIV=30, R=100 → 16 kHz PCM direct + #define PDM_CLOCK GPIO_PIN(GPIOD, 3) // V1.5+ board, DFSDM1_CKOUT + #define PDM_CLOCK_GPIO_AF LL_GPIO_AF_3 + #define PDM_CLOCK_DFSDM + #define PDM_SAI_KER_FREQ 48000000 // PLL1Q, DFSDM1 audio clock (see system_clock.c) + #define PDM_CLOCK_FREQ 1600000 // 1.6 MHz → ÷30, R=100 → 16 kHz PCM direct #define PDM_DATA GPIO_PIN(GPIOE, 4) #define PDM_DATA_GPIO_PORT GPIOE #define PDM_DATA_GPIO_PIN 4