mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-07-24 15:27:41 +08:00
fix(dshot): derive bit width per timer at runtime (#27505)
The DSHOT bit width was selected at compile time from a single timer clock (STM32_APB1_TIM5_CLKIN) and applied board-wide. On boards whose timers run on different clocks (e.g. APB1 vs APB2) the non-APB1 timers could emit DSHOT at the wrong rate; this was only flagged with a #pragma message rather than handled. Compute the bit width per timer from that timer's own clock_freq (which io_timers[] already carries), using a fixed DSHOT600 reference so the width depends only on the timer clock, not the selected DSHOT rate. The selection rule is unchanged, so register values are identical for every current board. Keeping the width rate-independent is what preserves the duty cycle: MOTOR_PWM_BIT_0/1 are absolute CCR counts calibrated for the fixed ARR, so a rate-varying ARR would distort them. Signed-off-by: Julian Oes <julian@oes.ch>
This commit is contained in:
@@ -38,30 +38,31 @@
|
||||
#include <stm32_dma.h>
|
||||
#include <arch/board/board.h>
|
||||
|
||||
// Pick WIDTH so floor(TIM_CLK / 600000) divides cleanly into it, keeping the
|
||||
// prescaler integer. Guard skips F1 IO co-processors (no TIM8, no DSHOT).
|
||||
#if defined(STM32_APB1_TIM5_CLKIN) && defined(STM32_APB2_TIM8_CLKIN)
|
||||
// Number of timer ticks per DSHOT bit (the timer's ARR). This is the per-bit
|
||||
// resolution, NOT a function of the DSHOT rate: the timer is configured with
|
||||
// ARR = WIDTH and PSC = floor(TIM_CLK / DSHOT_FREQ) / WIDTH - 1, so the prescaler
|
||||
// alone scales the rate while ARR stays fixed. Keeping ARR fixed across rates is
|
||||
// what lets the MOTOR_PWM_BIT_0/1 high-time constants hold their duty cycle at every
|
||||
// frequency. The width is derived from a fixed reference rate (DSHOT600) so that
|
||||
// floor(TIM_CLK / 600000) divides evenly, keeping the prescaler integer.
|
||||
//
|
||||
// This is computed per timer from that timer's own clock rather than from a single
|
||||
// board-wide define, so boards whose timers run on different clocks (e.g. APB1 vs
|
||||
// APB2) each get the correct width without any board-specific configuration.
|
||||
static inline uint32_t dshot_motor_pwm_bit_width(uint32_t timer_clock)
|
||||
{
|
||||
const uint32_t ticks = timer_clock / 600000u;
|
||||
|
||||
#define _DSHOT600_TICKS_APB1 (STM32_APB1_TIM5_CLKIN / 600000U)
|
||||
#define _DSHOT600_TICKS_APB2 (STM32_APB2_TIM8_CLKIN / 600000U)
|
||||
// Prefer the widest period that divides evenly (most timing resolution); fall
|
||||
// back to 20 when none does, matching the legacy default.
|
||||
for (uint32_t width = 20u; width >= 18u; --width) {
|
||||
if (ticks % width == 0u) {
|
||||
return width;
|
||||
}
|
||||
}
|
||||
|
||||
#if (_DSHOT600_TICKS_APB1 % 20U == 0U)
|
||||
# define DSHOT_MOTOR_PWM_BIT_WIDTH 20u
|
||||
#elif (_DSHOT600_TICKS_APB1 % 19U == 0U)
|
||||
# define DSHOT_MOTOR_PWM_BIT_WIDTH 19u
|
||||
#elif (_DSHOT600_TICKS_APB1 % 18U == 0U)
|
||||
# define DSHOT_MOTOR_PWM_BIT_WIDTH 18u
|
||||
#else
|
||||
# define DSHOT_MOTOR_PWM_BIT_WIDTH 20u
|
||||
#endif
|
||||
|
||||
#if (_DSHOT600_TICKS_APB1 % DSHOT_MOTOR_PWM_BIT_WIDTH) != (_DSHOT600_TICKS_APB2 % DSHOT_MOTOR_PWM_BIT_WIDTH)
|
||||
# pragma message "DSHOT bit width: APB1 and APB2 timer clocks emit DSHOT at different rates on this board"
|
||||
#endif
|
||||
|
||||
#else
|
||||
# define DSHOT_MOTOR_PWM_BIT_WIDTH 20u
|
||||
#endif
|
||||
return 20u;
|
||||
}
|
||||
|
||||
/* Configuration for each timer to setup DShot. Some timers have only one while others have two choices for the stream.
|
||||
*
|
||||
|
||||
@@ -601,8 +601,9 @@ int io_timer_set_dshot_burst_mode(uint8_t timer, unsigned dshot_pwm_freq, uint8_
|
||||
}
|
||||
|
||||
if (OK == ret_val) {
|
||||
rARR(timer) = DSHOT_MOTOR_PWM_BIT_WIDTH;
|
||||
rPSC(timer) = ((int)(io_timers[timer].clock_freq / dshot_pwm_freq) / DSHOT_MOTOR_PWM_BIT_WIDTH) - 1;
|
||||
const uint32_t bit_width = dshot_motor_pwm_bit_width(io_timers[timer].clock_freq);
|
||||
rARR(timer) = bit_width;
|
||||
rPSC(timer) = ((int)(io_timers[timer].clock_freq / dshot_pwm_freq) / bit_width) - 1;
|
||||
rEGR(timer) = ATIM_EGR_UG;
|
||||
|
||||
// find the lowest channel index for the timer (they are not necessarily in ascending order)
|
||||
@@ -639,7 +640,8 @@ int io_timer_set_dshot_capture_mode(uint8_t timer, uint8_t timer_channel_index,
|
||||
// Timer Autor Reload Register max value
|
||||
rARR(timer) = 0xFFFFFFFF;
|
||||
// Timer Prescalar
|
||||
rPSC(timer) = ((int)(io_timers[timer].clock_freq / (dshot_pwm_freq * 5 / 4)) / DSHOT_MOTOR_PWM_BIT_WIDTH) - 1;
|
||||
const uint32_t bit_width = dshot_motor_pwm_bit_width(io_timers[timer].clock_freq);
|
||||
rPSC(timer) = ((int)(io_timers[timer].clock_freq / (dshot_pwm_freq * 5 / 4)) / bit_width) - 1;
|
||||
|
||||
switch (timer_channel_index) {
|
||||
case 0:
|
||||
|
||||
Reference in New Issue
Block a user