@@ -51,13 +51,6 @@ enum {
5151 MAP_TO_LED_OUTPUT
5252};
5353
54- typedef struct {
55- int maxTimMotorCount ;
56- int maxTimServoCount ;
57- const timerHardware_t * timMotors [MAX_PWM_OUTPUTS ];
58- const timerHardware_t * timServos [MAX_PWM_OUTPUTS ];
59- } timMotorServoHardware_t ;
60-
6154static pwmInitError_e pwmInitError = PWM_INIT_ERROR_NONE ;
6255
6356static const char * pwmInitErrorMsg [] = {
@@ -300,14 +293,31 @@ static void pwmAssignOutput(timMotorServoHardware_t *timOutputs, timerHardware_t
300293 }
301294}
302295
303- void pwmBuildTimerOutputList (timMotorServoHardware_t * timOutputs , bool isMixerUsingServos )
296+ void pwmBuildTimerOutputList (timMotorServoHardware_t * timOutputs )
304297{
305- UNUSED (isMixerUsingServos );
306298 timOutputs -> maxTimMotorCount = 0 ;
307299 timOutputs -> maxTimServoCount = 0 ;
308300
309301 const uint8_t motorCount = getMotorCount ();
310302
303+ // Count servo outputs needed across all mixer profiles, matching
304+ // computeServoCount() / getServoCount() which also walk all profiles.
305+ // Using only the active profile would under-count on targets with a second
306+ // profile that has more servos, causing the simulation to diverge from
307+ // the real pwmInitServos() result.
308+ uint8_t minServo = 255 , maxServo = 0 ;
309+ bool anyServo = false;
310+ for (int j = 0 ; j < MAX_MIXER_PROFILE_COUNT ; j ++ ) {
311+ for (int i = 0 ; i < MAX_SERVO_RULES ; i ++ ) {
312+ if (mixerServoMixersByIndex (j )[i ].rate == 0 ) break ;
313+ uint8_t ch = mixerServoMixersByIndex (j )[i ].targetChannel ;
314+ if (ch < minServo ) minServo = ch ;
315+ if (ch > maxServo ) maxServo = ch ;
316+ anyServo = true;
317+ }
318+ }
319+ uint8_t servoCount = anyServo ? (uint8_t )(1 + maxServo - minServo ) : 0 ;
320+
311321 // Apply all timerOverrides upfront so flag state is stable for both passes
312322 for (int idx = 0 ; idx < timerHardwareCount ; idx ++ ) {
313323 timerHardwareOverride (& timerHardware [idx ]);
@@ -341,7 +351,7 @@ void pwmBuildTimerOutputList(timMotorServoHardware_t *timOutputs, bool isMixerUs
341351 }
342352
343353 // Servos: dedicated (OUTPUT_MODE_SERVOS) first, then auto
344- if (TIM_IS_SERVO (timHw -> usageFlags )
354+ if (TIM_IS_SERVO (timHw -> usageFlags ) && timOutputs -> maxTimServoCount < servoCount
345355 && !pwmHasMotorOnTimer (timOutputs , timHw -> tim )
346356 && (isDedicated ? mode == OUTPUT_MODE_SERVOS : mode != OUTPUT_MODE_SERVOS )) {
347357 pwmAssignOutput (timOutputs , timHw , MAP_TO_SERVO_OUTPUT );
@@ -459,19 +469,61 @@ static void pwmInitServos(timMotorServoHardware_t * timOutputs)
459469}
460470
461471
472+ static timMotorServoHardware_t timOutputsStatic ;
473+
462474bool pwmMotorAndServoInit (void )
463475{
464- timMotorServoHardware_t timOutputs ;
476+ pwmBuildTimerOutputList (& timOutputsStatic );
477+ pwmInitMotors (& timOutputsStatic );
478+ pwmInitServos (& timOutputsStatic );
479+ return (pwmInitError == PWM_INIT_ERROR_NONE );
480+ }
465481
466- // Build temporary timer mappings for motor and servo
467- pwmBuildTimerOutputList (& timOutputs , isMixerUsingServos ());
482+ const timMotorServoHardware_t * pwmGetOutputAssignment (void )
483+ {
484+ return & timOutputsStatic ;
485+ }
468486
469- // At this point we have built tables of timers suitable for motor and servo mappings
470- // Now we can actually initialize them according to motor/servo count from mixer
471- pwmInitMotors ( & timOutputs );
472- pwmInitServos ( & timOutputs );
487+ // Upper bound for timerHardware[] size across all supported targets.
488+ // timerHardwareCount is a runtime value; this constant prevents a VLA on the MSP
489+ // task stack. Targets with 22+ entries (e.g. OMNIBUSF4) need more than MAX_PWM_OUTPUTS.
490+ #define TIMER_HW_MAX 64
473491
474- return (pwmInitError == PWM_INIT_ERROR_NONE );
492+ // Simulate pwmBuildTimerOutputList() with proposed overrides without modifying live state.
493+ // IMPORTANT: Must only be called from the main loop / MSP task — not ISR-safe.
494+ // timerHardware[].usageFlags are modified in-place during the simulation; this function
495+ // saves and restores them so hardware state is identical on exit.
496+ void pwmCalculateAssignment (timMotorServoHardware_t * out , const uint8_t * proposedModes )
497+ {
498+ if (timerHardwareCount > TIMER_HW_MAX ) {
499+ return ; // Safety guard: target exceeds the buffer — increase TIMER_HW_MAX
500+ }
501+
502+ // Snapshot timerHardware flags — pwmBuildTimerOutputList() modifies them in-place
503+ // via timerHardwareOverride() and pwmClaimTimer().
504+ uint32_t savedFlags [TIMER_HW_MAX ];
505+ for (int i = 0 ; i < timerHardwareCount ; i ++ ) {
506+ savedFlags [i ] = timerHardware [i ].usageFlags ;
507+ }
508+
509+ // Snapshot timerOverrides config so the proposed values can be applied temporarily.
510+ uint8_t savedModes [HARDWARE_TIMER_DEFINITION_COUNT ];
511+ for (int i = 0 ; i < HARDWARE_TIMER_DEFINITION_COUNT ; i ++ ) {
512+ savedModes [i ] = timerOverrides (i )-> outputMode ;
513+ }
514+
515+ for (int i = 0 ; i < HARDWARE_TIMER_DEFINITION_COUNT ; i ++ ) {
516+ timerOverridesMutable (i )-> outputMode = proposedModes [i ];
517+ }
518+
519+ pwmBuildTimerOutputList (out );
520+
521+ for (int i = 0 ; i < HARDWARE_TIMER_DEFINITION_COUNT ; i ++ ) {
522+ timerOverridesMutable (i )-> outputMode = savedModes [i ];
523+ }
524+ for (int i = 0 ; i < timerHardwareCount ; i ++ ) {
525+ timerHardware [i ].usageFlags = savedFlags [i ];
526+ }
475527}
476528
477529#endif
0 commit comments