From a6ba1168fa693d176ba1324982d05ff5351a7841 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Sat, 17 Jan 2026 21:32:53 -0600 Subject: [PATCH 1/8] WIP: Attempt circular DShot DMA during settings save This approach doesn't prevent DShot interruption during EEPROM writes. Committing for potential future refinement. Changes: - Added impl_timerPWMSetDMACircular() to switch DMA mode at runtime - Modified processDelayedSave() to use circular mode during writeEEPROM() - Called pwmCompleteMotorUpdate() 3x to latch DShot 0 packets Issue: DShot still shows gaps during settings save on oscilloscope. Next approach: Test with simple GPIO high instead of DShot. --- src/main/drivers/pwm_output.c | 14 ++++++++++++++ src/main/drivers/pwm_output.h | 1 + src/main/drivers/timer_impl.h | 1 + src/main/drivers/timer_impl_hal.c | 23 +++++++++++++++++++++++ src/main/drivers/timer_impl_stdperiph.c | 20 ++++++++++++++++++++ src/main/fc/config.c | 14 ++++++++++++++ 6 files changed, 73 insertions(+) diff --git a/src/main/drivers/pwm_output.c b/src/main/drivers/pwm_output.c index 619f4b95db5..42debf6cb48 100644 --- a/src/main/drivers/pwm_output.c +++ b/src/main/drivers/pwm_output.c @@ -226,6 +226,20 @@ void pwmEnableMotors(void) pwmMotorsEnabled = true; } +void pwmSetMotorDMACircular(bool circular) +{ +#ifdef USE_DSHOT + // Set DMA circular mode for all motor outputs + for (int i = 0; i < getMotorCount(); i++) { + if (motors[i].pwmPort && motors[i].pwmPort->tch) { + impl_timerPWMSetDMACircular(motors[i].pwmPort->tch, circular); + } + } +#else + UNUSED(circular); +#endif +} + bool isMotorBrushed(uint16_t motorPwmRateHz) { return (motorPwmRateHz > 500); diff --git a/src/main/drivers/pwm_output.h b/src/main/drivers/pwm_output.h index 1041ace04fa..1dc644f7f4e 100644 --- a/src/main/drivers/pwm_output.h +++ b/src/main/drivers/pwm_output.h @@ -50,6 +50,7 @@ void pwmWriteServo(uint8_t index, uint16_t value); void pwmDisableMotors(void); void pwmEnableMotors(void); +void pwmSetMotorDMACircular(bool circular); struct timerHardware_s; void pwmMotorPreconfigure(void); diff --git a/src/main/drivers/timer_impl.h b/src/main/drivers/timer_impl.h index 4d0a87f9aa5..83a7a93f2b1 100644 --- a/src/main/drivers/timer_impl.h +++ b/src/main/drivers/timer_impl.h @@ -84,6 +84,7 @@ bool impl_timerPWMConfigChannelDMA(TCH_t * tch, void * dmaBuffer, uint8_t dmaBuf void impl_timerPWMPrepareDMA(TCH_t * tch, uint32_t dmaBufferElementCount); void impl_timerPWMStartDMA(TCH_t * tch); void impl_timerPWMStopDMA(TCH_t * tch); +void impl_timerPWMSetDMACircular(TCH_t * tch, bool circular); #ifdef USE_DSHOT_DMAR bool impl_timerPWMConfigDMABurst(burstDmaTimer_t *burstDmaTimer, TCH_t * tch, void * dmaBuffer, uint8_t dmaBufferElementSize, uint32_t dmaBufferElementCount); diff --git a/src/main/drivers/timer_impl_hal.c b/src/main/drivers/timer_impl_hal.c index 8df0f7024d3..6390b4beef5 100644 --- a/src/main/drivers/timer_impl_hal.c +++ b/src/main/drivers/timer_impl_hal.c @@ -580,3 +580,26 @@ void impl_timerPWMStopDMA(TCH_t * tch) HAL_TIM_Base_Start(tch->timCtx->timHandle); } + +void impl_timerPWMSetDMACircular(TCH_t * tch, bool circular) +{ + if (!tch->dma || !tch->dma->dma) { + return; + } + + const uint32_t streamLL = lookupDMALLStreamTable[DMATAG_GET_STREAM(tch->timHw->dmaTag)]; + DMA_TypeDef *dmaBase = tch->dma->dma; + + // Temporarily disable DMA while modifying configuration + LL_DMA_DisableStream(dmaBase, streamLL); + + // Modify the DMA mode + if (circular) { + LL_DMA_SetMode(dmaBase, streamLL, LL_DMA_MODE_CIRCULAR); + } else { + LL_DMA_SetMode(dmaBase, streamLL, LL_DMA_MODE_NORMAL); + } + + // Re-enable DMA + LL_DMA_EnableStream(dmaBase, streamLL); +} diff --git a/src/main/drivers/timer_impl_stdperiph.c b/src/main/drivers/timer_impl_stdperiph.c index d2bd35dd521..2c82b052890 100644 --- a/src/main/drivers/timer_impl_stdperiph.c +++ b/src/main/drivers/timer_impl_stdperiph.c @@ -519,3 +519,23 @@ void impl_timerPWMStopDMA(TCH_t * tch) tch->dmaState = TCH_DMA_IDLE; TIM_Cmd(tch->timHw->tim, ENABLE); } + +void impl_timerPWMSetDMACircular(TCH_t * tch, bool circular) +{ + if (!tch->dma || !tch->dma->ref) { + return; + } + + // Temporarily disable DMA while modifying configuration + DMA_Cmd(tch->dma->ref, DISABLE); + + // Modify the DMA mode + if (circular) { + tch->dma->ref->CR |= DMA_SxCR_CIRC; // Set circular bit + } else { + tch->dma->ref->CR &= ~DMA_SxCR_CIRC; // Clear circular bit + } + + // Re-enable DMA + DMA_Cmd(tch->dma->ref, ENABLE); +} diff --git a/src/main/fc/config.c b/src/main/fc/config.c index d3021317ae5..b2e29caf4e0 100755 --- a/src/main/fc/config.c +++ b/src/main/fc/config.c @@ -420,7 +420,21 @@ void processDelayedSave(void) saveState = SAVESTATE_NONE; } else if (saveState == SAVESTATE_SAVEONLY) { suspendRxSignal(); + + // Prevent ESC spinup during settings save + // Switch to circular mode first + pwmSetMotorDMACircular(true); + // Force motor updates to latch current (zero) throttle into circular DMA buffer + pwmCompleteMotorUpdate(); + delayMicroseconds(200); + pwmCompleteMotorUpdate(); + delayMicroseconds(200); + pwmCompleteMotorUpdate(); + writeEEPROM(); + + pwmSetMotorDMACircular(false); + resumeRxSignal(); saveState = SAVESTATE_NONE; } From d32cf254e68c718e61680acaa6f6917e8cdccc9a Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Sun, 18 Jan 2026 00:04:47 -0600 Subject: [PATCH 2/8] Fix ESC spinup during MSP settings save (#10913) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move circular DShot DMA code from processDelayedSave() to writeConfigToEEPROM(). This ensures the fix works for MSP_EEPROM_WRITE commands, not just delayed saves. The MSP call path is: MSP_EEPROM_WRITE → writeEEPROM() → writeConfigToEEPROM() → writeSettingsToEEPROM() The previous commit (a6ba1168f) had circular DMA in processDelayedSave(), which is only called for delayed saves (on disarm), not MSP commands. Changes: - Move circular DMA setup to writeConfigToEEPROM() in config_eeprom.c - Remove unused pwmSetMotorPinsHigh() function - Add pwm_output.h include to config_eeprom.c Test method: - MSP_EEPROM_WRITE command sent once per second - DShot signal monitored on oscilloscope - Confirmed: DShot no longer interrupted during settings save Issue: #10913 Related: #9441 --- src/main/config/config_eeprom.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/config/config_eeprom.c b/src/main/config/config_eeprom.c index 6c1dfc3dc7e..38b35937019 100755 --- a/src/main/config/config_eeprom.c +++ b/src/main/config/config_eeprom.c @@ -33,6 +33,7 @@ #include "drivers/system.h" #include "drivers/flash.h" +#include "drivers/pwm_output.h" #include "fc/config.h" @@ -321,6 +322,16 @@ static bool writeSettingsToEEPROM(void) void writeConfigToEEPROM(void) { + // Prevent ESC spinup during settings save using circular DMA + pwmSetMotorDMACircular(true); + + // Force motor updates to latch current (zero) throttle into circular DMA buffer + pwmCompleteMotorUpdate(); + delayMicroseconds(200); + pwmCompleteMotorUpdate(); + delayMicroseconds(200); + pwmCompleteMotorUpdate(); + bool success = false; // write it for (int attempt = 0; attempt < 3 && !success; attempt++) { @@ -333,6 +344,9 @@ void writeConfigToEEPROM(void) } } + // Restore normal DMA mode + pwmSetMotorDMACircular(false); + if (success && isEEPROMContentValid()) { return; } From 4249b5576587211f32fd06ce85aa7b00c34a04f6 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Sun, 18 Jan 2026 15:35:17 -0600 Subject: [PATCH 3/8] Address code review feedback for ESC spinup fix Based on code-reviewer agent feedback: 1. Add missing AT32 platform implementation - Implement impl_timerPWMSetDMACircular() for AT32F43x targets - Uses AT32 loop_mode (ctrl_bit.lm) instead of DMA_SxCR_CIRC 2. Remove duplicate circular DMA code from config.c - processDelayedSave() calls writeEEPROM() which calls writeConfigToEEPROM() - writeConfigToEEPROM() already has circular DMA protection - Removed redundant nested enable/disable from config.c 3. Add ATOMIC_BLOCK protection to DMA mode switch - Consistent with existing impl_timerPWMStopDMA() pattern - Prevents interrupt interference during DMA reconfiguration - Applied to HAL, StdPeriph, and AT32 implementations Issue: #10913 --- src/main/drivers/timer_impl_hal.c | 23 +++++++++++--------- src/main/drivers/timer_impl_stdperiph.c | 23 +++++++++++--------- src/main/drivers/timer_impl_stdperiph_at32.c | 23 ++++++++++++++++++++ src/main/fc/config.c | 16 +------------- 4 files changed, 50 insertions(+), 35 deletions(-) diff --git a/src/main/drivers/timer_impl_hal.c b/src/main/drivers/timer_impl_hal.c index 6390b4beef5..0036a6d50fb 100644 --- a/src/main/drivers/timer_impl_hal.c +++ b/src/main/drivers/timer_impl_hal.c @@ -590,16 +590,19 @@ void impl_timerPWMSetDMACircular(TCH_t * tch, bool circular) const uint32_t streamLL = lookupDMALLStreamTable[DMATAG_GET_STREAM(tch->timHw->dmaTag)]; DMA_TypeDef *dmaBase = tch->dma->dma; - // Temporarily disable DMA while modifying configuration - LL_DMA_DisableStream(dmaBase, streamLL); + // Protect DMA reconfiguration from interrupt interference + ATOMIC_BLOCK(NVIC_PRIO_MAX) { + // Temporarily disable DMA while modifying configuration + LL_DMA_DisableStream(dmaBase, streamLL); - // Modify the DMA mode - if (circular) { - LL_DMA_SetMode(dmaBase, streamLL, LL_DMA_MODE_CIRCULAR); - } else { - LL_DMA_SetMode(dmaBase, streamLL, LL_DMA_MODE_NORMAL); - } + // Modify the DMA mode + if (circular) { + LL_DMA_SetMode(dmaBase, streamLL, LL_DMA_MODE_CIRCULAR); + } else { + LL_DMA_SetMode(dmaBase, streamLL, LL_DMA_MODE_NORMAL); + } - // Re-enable DMA - LL_DMA_EnableStream(dmaBase, streamLL); + // Re-enable DMA + LL_DMA_EnableStream(dmaBase, streamLL); + } } diff --git a/src/main/drivers/timer_impl_stdperiph.c b/src/main/drivers/timer_impl_stdperiph.c index 2c82b052890..d3bb0555004 100644 --- a/src/main/drivers/timer_impl_stdperiph.c +++ b/src/main/drivers/timer_impl_stdperiph.c @@ -526,16 +526,19 @@ void impl_timerPWMSetDMACircular(TCH_t * tch, bool circular) return; } - // Temporarily disable DMA while modifying configuration - DMA_Cmd(tch->dma->ref, DISABLE); + // Protect DMA reconfiguration from interrupt interference + ATOMIC_BLOCK(NVIC_PRIO_MAX) { + // Temporarily disable DMA while modifying configuration + DMA_Cmd(tch->dma->ref, DISABLE); - // Modify the DMA mode - if (circular) { - tch->dma->ref->CR |= DMA_SxCR_CIRC; // Set circular bit - } else { - tch->dma->ref->CR &= ~DMA_SxCR_CIRC; // Clear circular bit - } + // Modify the DMA mode + if (circular) { + tch->dma->ref->CR |= DMA_SxCR_CIRC; // Set circular bit + } else { + tch->dma->ref->CR &= ~DMA_SxCR_CIRC; // Clear circular bit + } - // Re-enable DMA - DMA_Cmd(tch->dma->ref, ENABLE); + // Re-enable DMA + DMA_Cmd(tch->dma->ref, ENABLE); + } } diff --git a/src/main/drivers/timer_impl_stdperiph_at32.c b/src/main/drivers/timer_impl_stdperiph_at32.c index 0cc194897d9..b74fd4f18a4 100644 --- a/src/main/drivers/timer_impl_stdperiph_at32.c +++ b/src/main/drivers/timer_impl_stdperiph_at32.c @@ -407,3 +407,26 @@ void impl_timerPWMStopDMA(TCH_t * tch) tch->dmaState = TCH_DMA_IDLE; tmr_counter_enable(tch->timHw->tim, TRUE); } + +void impl_timerPWMSetDMACircular(TCH_t * tch, bool circular) +{ + if (!tch->dma || !tch->dma->ref) { + return; + } + + // Protect DMA reconfiguration from interrupt interference + ATOMIC_BLOCK(NVIC_PRIO_MAX) { + // Temporarily disable DMA while modifying configuration + dma_channel_enable(tch->dma->ref, FALSE); + + // Modify the DMA loop mode (AT32's equivalent of circular mode) + if (circular) { + tch->dma->ref->ctrl_bit.lm = TRUE; // Enable loop mode + } else { + tch->dma->ref->ctrl_bit.lm = FALSE; // Disable loop mode + } + + // Re-enable DMA + dma_channel_enable(tch->dma->ref, TRUE); + } +} diff --git a/src/main/fc/config.c b/src/main/fc/config.c index b2e29caf4e0..fcc40d530e0 100755 --- a/src/main/fc/config.c +++ b/src/main/fc/config.c @@ -420,21 +420,7 @@ void processDelayedSave(void) saveState = SAVESTATE_NONE; } else if (saveState == SAVESTATE_SAVEONLY) { suspendRxSignal(); - - // Prevent ESC spinup during settings save - // Switch to circular mode first - pwmSetMotorDMACircular(true); - // Force motor updates to latch current (zero) throttle into circular DMA buffer - pwmCompleteMotorUpdate(); - delayMicroseconds(200); - pwmCompleteMotorUpdate(); - delayMicroseconds(200); - pwmCompleteMotorUpdate(); - - writeEEPROM(); - - pwmSetMotorDMACircular(false); - + writeEEPROM(); // Circular DMA protection is inside writeConfigToEEPROM() resumeRxSignal(); saveState = SAVESTATE_NONE; } From ebcd802ff1724af418065d613e05edf90d113361 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Sun, 18 Jan 2026 23:59:18 -0600 Subject: [PATCH 4/8] Fix H7 circular DMA implementation - wait for stream disable Critical fixes for STM32H7 DMA circular mode: - Wait for EN bit to actually clear before changing mode (was the primary bug) - Disable/re-enable timer DMA requests during reconfiguration - Reload DMA transfer count after mode change - Clear pending DMA flags Without these changes, the mode change was being ignored because the DMA stream was still active when we tried to modify the configuration. --- src/main/drivers/timer_impl_hal.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/main/drivers/timer_impl_hal.c b/src/main/drivers/timer_impl_hal.c index 0036a6d50fb..5d62fc42128 100644 --- a/src/main/drivers/timer_impl_hal.c +++ b/src/main/drivers/timer_impl_hal.c @@ -590,11 +590,27 @@ void impl_timerPWMSetDMACircular(TCH_t * tch, bool circular) const uint32_t streamLL = lookupDMALLStreamTable[DMATAG_GET_STREAM(tch->timHw->dmaTag)]; DMA_TypeDef *dmaBase = tch->dma->dma; + // Save the current transfer count before disabling + uint32_t dataLength = LL_DMA_GetDataLength(dmaBase, streamLL); + // Protect DMA reconfiguration from interrupt interference ATOMIC_BLOCK(NVIC_PRIO_MAX) { - // Temporarily disable DMA while modifying configuration + // Disable timer DMA request first to stop new transfer triggers + LL_TIM_DisableDMAReq_CCx(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex]); + + // Disable the DMA stream LL_DMA_DisableStream(dmaBase, streamLL); + // CRITICAL: Wait for stream to actually become disabled + // The EN bit doesn't clear immediately, especially if transfer is in progress + uint32_t timeout = 10000; + while (LL_DMA_IsEnabledStream(dmaBase, streamLL) && timeout--) { + __NOP(); + } + + // Clear any pending transfer complete flags + DMA_CLEAR_FLAG(tch->dma, DMA_IT_TCIF); + // Modify the DMA mode if (circular) { LL_DMA_SetMode(dmaBase, streamLL, LL_DMA_MODE_CIRCULAR); @@ -602,7 +618,16 @@ void impl_timerPWMSetDMACircular(TCH_t * tch, bool circular) LL_DMA_SetMode(dmaBase, streamLL, LL_DMA_MODE_NORMAL); } - // Re-enable DMA + // Reload the transfer count (required after mode change) + // If dataLength was 0 (transfer completed), keep it at 0 - the next motor update will reload it + if (dataLength > 0) { + LL_DMA_SetDataLength(dmaBase, streamLL, dataLength); + } + + // Re-enable DMA stream LL_DMA_EnableStream(dmaBase, streamLL); + + // Re-enable timer DMA requests + LL_TIM_EnableDMAReq_CCx(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex]); } } From 9c2c1f4483edf7c7beaadcff68ec3725647c8252 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Mon, 19 Jan 2026 01:13:42 -0600 Subject: [PATCH 5/8] Fix SITL build: Guard DShot circular DMA calls with SITL_BUILD check SITL doesn't have real PWM/motor hardware, so pwmSetMotorDMACircular() and pwmCompleteMotorUpdate() don't exist in SITL builds. Wrap these calls with #if \!defined(SITL_BUILD) to allow SITL builds to compile while preserving the ESC spinup fix for hardware builds. --- src/main/config/config_eeprom.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/config/config_eeprom.c b/src/main/config/config_eeprom.c index 38b35937019..1efadd2a3fd 100755 --- a/src/main/config/config_eeprom.c +++ b/src/main/config/config_eeprom.c @@ -322,6 +322,7 @@ static bool writeSettingsToEEPROM(void) void writeConfigToEEPROM(void) { +#if !defined(SITL_BUILD) // Prevent ESC spinup during settings save using circular DMA pwmSetMotorDMACircular(true); @@ -331,6 +332,7 @@ void writeConfigToEEPROM(void) pwmCompleteMotorUpdate(); delayMicroseconds(200); pwmCompleteMotorUpdate(); +#endif bool success = false; // write it @@ -344,8 +346,10 @@ void writeConfigToEEPROM(void) } } +#if !defined(SITL_BUILD) // Restore normal DMA mode pwmSetMotorDMACircular(false); +#endif if (success && isEEPROMContentValid()) { return; From eda9f2207fc9014419f2f754b007c23e9dece925 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Mon, 19 Jan 2026 15:21:05 -0600 Subject: [PATCH 6/8] Add DMA disable timeout handling to all platforms Address qodo-code-review feedback: Add defensive timeout checks when waiting for DMA streams/channels to disable before reconfiguring. Changes: - H7 (timer_impl_hal.c): Check if timeout expired and abort if DMA still enabled - F4/F7 (timer_impl_stdperiph.c): Add wait loop for EN bit to clear with timeout check - AT32 (timer_impl_stdperiph_at32.c): Add wait loop for chen bit to clear with timeout check This prevents potential race conditions where DMA configuration could be modified while the stream is still active, which could cause unstable behavior. --- src/main/drivers/timer_impl_hal.c | 7 +++++++ src/main/drivers/timer_impl_stdperiph.c | 13 +++++++++++++ src/main/drivers/timer_impl_stdperiph_at32.c | 13 +++++++++++++ 3 files changed, 33 insertions(+) diff --git a/src/main/drivers/timer_impl_hal.c b/src/main/drivers/timer_impl_hal.c index 5d62fc42128..85a8429f8f9 100644 --- a/src/main/drivers/timer_impl_hal.c +++ b/src/main/drivers/timer_impl_hal.c @@ -608,6 +608,13 @@ void impl_timerPWMSetDMACircular(TCH_t * tch, bool circular) __NOP(); } + // If timeout occurred, DMA stream is still enabled - abort reconfiguration + if (timeout == 0 && LL_DMA_IsEnabledStream(dmaBase, streamLL)) { + // Re-enable timer DMA request and return to avoid unstable state + LL_TIM_EnableDMAReq_CCx(tch->timHw->tim, lookupDMASourceTable[tch->timHw->channelIndex]); + return; + } + // Clear any pending transfer complete flags DMA_CLEAR_FLAG(tch->dma, DMA_IT_TCIF); diff --git a/src/main/drivers/timer_impl_stdperiph.c b/src/main/drivers/timer_impl_stdperiph.c index d3bb0555004..af685d0a5ba 100644 --- a/src/main/drivers/timer_impl_stdperiph.c +++ b/src/main/drivers/timer_impl_stdperiph.c @@ -531,6 +531,19 @@ void impl_timerPWMSetDMACircular(TCH_t * tch, bool circular) // Temporarily disable DMA while modifying configuration DMA_Cmd(tch->dma->ref, DISABLE); + // Wait for DMA stream to actually be disabled + // The EN bit doesn't clear immediately, especially if transfer is in progress + uint32_t timeout = 10000; + while ((tch->dma->ref->CR & DMA_SxCR_EN) && timeout--) { + __NOP(); + } + + // If timeout occurred, DMA stream is still enabled - abort reconfiguration + if (timeout == 0 && (tch->dma->ref->CR & DMA_SxCR_EN)) { + DMA_Cmd(tch->dma->ref, ENABLE); // Re-enable and return + return; + } + // Modify the DMA mode if (circular) { tch->dma->ref->CR |= DMA_SxCR_CIRC; // Set circular bit diff --git a/src/main/drivers/timer_impl_stdperiph_at32.c b/src/main/drivers/timer_impl_stdperiph_at32.c index b74fd4f18a4..e39cef7b9ca 100644 --- a/src/main/drivers/timer_impl_stdperiph_at32.c +++ b/src/main/drivers/timer_impl_stdperiph_at32.c @@ -419,6 +419,19 @@ void impl_timerPWMSetDMACircular(TCH_t * tch, bool circular) // Temporarily disable DMA while modifying configuration dma_channel_enable(tch->dma->ref, FALSE); + // Wait for DMA channel to actually be disabled + // The enable bit doesn't clear immediately, especially if transfer is in progress + uint32_t timeout = 10000; + while (tch->dma->ref->ctrl_bit.chen && timeout--) { + __NOP(); + } + + // If timeout occurred, DMA channel is still enabled - abort reconfiguration + if (timeout == 0 && tch->dma->ref->ctrl_bit.chen) { + dma_channel_enable(tch->dma->ref, TRUE); // Re-enable and return + return; + } + // Modify the DMA loop mode (AT32's equivalent of circular mode) if (circular) { tch->dma->ref->ctrl_bit.lm = TRUE; // Enable loop mode From c7b342df488428a43a513ecdb779a123aa2348fc Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Mon, 19 Jan 2026 19:39:55 -0600 Subject: [PATCH 7/8] Fix build for targets without DShot: Add USE_DSHOT guard Some hardware targets don't compile DShot support, causing linker errors when trying to call pwmSetMotorDMACircular() and pwmCompleteMotorUpdate(). Change guard from: #if \!defined(SITL_BUILD) To: #if \!defined(SITL_BUILD) && defined(USE_DSHOT) This ensures the functions are only called on targets that actually have DShot compiled in, fixing build failures on targets like BEEROTORF4. --- src/main/config/config_eeprom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/config/config_eeprom.c b/src/main/config/config_eeprom.c index 1efadd2a3fd..e4a22aaa581 100755 --- a/src/main/config/config_eeprom.c +++ b/src/main/config/config_eeprom.c @@ -322,7 +322,7 @@ static bool writeSettingsToEEPROM(void) void writeConfigToEEPROM(void) { -#if !defined(SITL_BUILD) +#if !defined(SITL_BUILD) && defined(USE_DSHOT) // Prevent ESC spinup during settings save using circular DMA pwmSetMotorDMACircular(true); @@ -346,7 +346,7 @@ void writeConfigToEEPROM(void) } } -#if !defined(SITL_BUILD) +#if !defined(SITL_BUILD) && defined(USE_DSHOT) // Restore normal DMA mode pwmSetMotorDMACircular(false); #endif From e7075c5b5044f81eb1799d7ffb0659eb3a3e3deb Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Mon, 19 Jan 2026 21:05:30 -0600 Subject: [PATCH 8/8] Remove unnecessary comments - config.c: Remove comment about circular DMA protection location (obvious from context) - timer_impl_stdperiph_at32.c: Remove redundant comment about loop mode (already clear from 'Enable loop mode' / 'Disable loop mode' comments) --- src/main/drivers/timer_impl_stdperiph_at32.c | 1 - src/main/fc/config.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/drivers/timer_impl_stdperiph_at32.c b/src/main/drivers/timer_impl_stdperiph_at32.c index e39cef7b9ca..d9433ba1f66 100644 --- a/src/main/drivers/timer_impl_stdperiph_at32.c +++ b/src/main/drivers/timer_impl_stdperiph_at32.c @@ -432,7 +432,6 @@ void impl_timerPWMSetDMACircular(TCH_t * tch, bool circular) return; } - // Modify the DMA loop mode (AT32's equivalent of circular mode) if (circular) { tch->dma->ref->ctrl_bit.lm = TRUE; // Enable loop mode } else { diff --git a/src/main/fc/config.c b/src/main/fc/config.c index fcc40d530e0..d3021317ae5 100755 --- a/src/main/fc/config.c +++ b/src/main/fc/config.c @@ -420,7 +420,7 @@ void processDelayedSave(void) saveState = SAVESTATE_NONE; } else if (saveState == SAVESTATE_SAVEONLY) { suspendRxSignal(); - writeEEPROM(); // Circular DMA protection is inside writeConfigToEEPROM() + writeEEPROM(); resumeRxSignal(); saveState = SAVESTATE_NONE; }