Repository navigation
Conversation
Ports bidirectional DShot (GCR) telemetry from Betaflight, enabling RPM-based gyro filtering without a dedicated ESC telemetry UART. After each DShot output frame the motor pin is switched to timer input capture mode via per-channel DMA to receive the ESC's GCR-encoded eRPM response. The decoded eRPM feeds the existing RPM filter infrastructure. New settings: - dshot_bidir_enabled: enable bidirectional DShot telemetry - dshot_edt_enabled: enable Extended DShot Telemetry (temperature, voltage, current in addition to eRPM) Changes: - drivers/dshot.c/.h: GCR decode, EDT frame parsing, eRPM-to-RPM conversion, motor frequency LPF (ported from Betaflight, GPLv3) - drivers/pwm_output.c: per-channel DMA direction switching between DShot output and GCR input capture on STM32 (StdPeriph/HAL) and AT32 - sensors/esc_sensor.c/.h: DShot bidir data path into ESC sensor framework (escSensorSetDshotData, escSensorIsActive) - flight/rpm_filter.c: DShot telemetry as frequency source alongside serial ESC sensor path - target/common_post.h: USE_RPM_FILTER enabled for any USE_DSHOT target - fc/fc_init.c: call initDshotTelemetry() at startup; fix RPM filter init condition to accept DShot bidir as a valid source - blackbox/blackbox.c: fix NULL dereference when escSensorGetData() returns NULL; fix uninitialized blackboxSlowState_t comparison - telemetry/srxl.c: replace Betaflight-specific USE_DSHOT_TELEMETRY guards with INAV's USE_DSHOT/USE_ESC_SENSOR; use escSensorGetData() Tested on SPEEDYBEEF405V4 (STM32F405, StdPeriph) with Bluejay ESC firmware. eRPM telemetry confirmed in blackbox; RPM gyro filter updating notch frequencies with motor speed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ht#11605 # Conflicts: # src/main/target/common_post.h
dshotDmaStream() is only called from the USE_HAL_DRIVER code paths, but was defined unconditionally with a dead fallback stub, so non-HAL F4 targets (e.g. FF_F35_LIGHTNING) built it with -Werror=unused-function. Scope the definition to USE_HAL_DRIVER to match its call sites.
pwm_output.c called LL_TIM_EnableDMAReq_CCx/LL_TIM_DisableDMAReq_CCx, which don't exist in ST's LL timer driver (only per-channel CC1..CC4 variants are provided). Add local static inline wrappers that set/clear the DIER CCxDE bits directly by mask, matching the equivalent helpers already used internally by timer_impl_hal.c.
…metry settings dshot_bidir_enabled and dshot_edt_enabled were added to settings.yaml but docs/Settings.md was never regenerated, failing CI's update_cli_docs.py check. Ran the generator; purely additive diff, no other drift.
loadDmaBufferDshot() was only defined when USE_DSHOT_DMAR was unset, but pwmCompleteMotorUpdate() also calls it as the per-channel DMA fallback on DMAR targets whenever bidir telemetry is enabled (burst DMA can't drive per-channel direction switching). Define it unconditionally; keep loadDmaBufferDshotStride() gated behind USE_DSHOT_DMAR since it's only used by the DMAR-specific burst path.
…cm-overflow fix: keep DShot telemetry decode out of ITCM
…lemetry, magic H7 workdown from betaflight
pwmSetMotorDMACircular() predates bidirectional DSHOT and corrupts its per-channel direction-switching state machine when a config save runs (e.g. servo autotrim saving on disarm): - On USE_DSHOT_DMAR targets the burst path is never armed with bidir, so dmaBurstBuffer stays NULL and loadDmaBufferDshotStride() writes the zero-throttle frame through the NULL pointer into 0x00000000 - which is ITCM RAM holding .tcm_code (FAST_CODE), corrupting the firmware. - On per-channel targets impl_timerPWMSetDMACircular() re-enables the stream without regard to the current transfer direction, and on exit disables the TIM DMA request before the stream: a FIFO-enabled mem-to-periph stream then cannot drain, EN sticks at 1 and every later reconfiguration is silently ignored - the output is dead until reboot. - Circular output also repeats frames back-to-back, colliding with the GCR reply the ESC drives onto the same line after every frame. Match Betaflight instead: with bidir telemetry active, leave motor DMA alone during the flash write and accept the frame gap. Saves only happen disarmed, the ESC holds zero throttle, and the existing decode path rebuilds the streams (full DeInit/Init) on the first update afterwards. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BB55zdRNZq3Q6hZYsDYwTc
…ot-fix BidirDshot - fix for H7, invertion of timer, reset DMA for reading te…
# Conflicts: # src/main/drivers/pwm_output.c # src/main/fc/settings.yaml # src/main/sensors/esc_sensor.c
Structure: - dshot.c/h -> bidir_dshot.c/h, compiled only with USE_DSHOT_BIDIR. The define comes from common_post.h: USE_DSHOT && STM32H7 && !USE_DSHOT_DMAR (direction switching is verified on H7; DMAR has no per-channel stream). dshot_bidir_enabled / dshot_edt_enabled are hidden elsewhere. - Every bidir path in pwm_output.c sits under USE_DSHOT_BIDIR and, at run time, under useDshotTelemetry: with the setting off, plain DSHOT keeps the timer driver's DMA handler, FIFO setup and channel config from maintenance-10.x. The DMAR fallback to per-channel DMA is gone (excluded by the define instead). - USE_RPM_FILTER follows USE_ESC_SENSOR || USE_DSHOT_BIDIR instead of USE_DSHOT, so targets without ESC sensor do not grow. - No circular keep-alive for bidir: the line idles high for the flash write, as in Betaflight. Fixes: - EDT voltage/current were 10x off (0.25 V and 1 A steps into 0.01 V / 0.01 A fields): x25 / x100 instead of x250 / x1000. - ESC sensor RPM taken from the already computed mechanical RPM instead of computeRpm((int16_t)eRPM/100); an EDT-only first frame no longer publishes rpm = 0. - GCR decoder: guard against len < 1 (shift by -1) on glitch edges and on an exactly filled frame, and against len > 21. - Bidir ESC sensor data is aged at the serial poll rate, so a silent ESC drops out of the combined values like a serial one. - useDshotTelemetry is set once, by pwmMotorPreconfigure(); the ESC sensor asks isDshotTelemetryActive() instead of re-deriving it. - pwmDshotDmaIrqHandler keeps tch->dmaState in step with the timer driver's handler (timerPWMDMAInProgress()). - srxl.c: RPM comes through escSensorGetData(), no dshot include needed; duplicate includes removed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…irdshot for rpi2350
With bidirectional DShot, a PID on the slowest motor's RPM raises the motors' low end as much as needed to keep dynamic_idle_min_rpm, up to dynamic_idle_max_increase. Off by default (dynamic_idle_min_rpm = 0).
|
RAM / Flash usage vs. base commit
See RAM/flash optimization guide for techniques to reduce usage. |
|
Test firmware build ready — commit Download firmware for PR #12134 251 targets built. Find your board's
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on bidirectional DShot, not merged yet. It is based on #12027's head (4a7d4e2): error414's current version of #11605, marked as testing and meant to be merged into #11605. Until one of the two is merged this PR's diff includes #12027's commits; I'll rebase onto whichever is merged. Please don't merge this before it.
Ported from Betaflight's dynamic idle. With bidirectional DShot, the motors' low end follows the slowest motor's RPM instead of staying at
throttle_idle: a PID on the slowest motor raises it as much as needed to keepdynamic_idle_min_rpm, up todynamic_idle_max_increase, and lets it fall back to the lowest DShot value when the motors turn fast enough on their own. Low idle where the props keep spinning, more idle where they would slow down too much, for example in a fast descent or a sharp flip, which is where desyncs start.Settings, in the motor group, on targets with
USE_DSHOT_BIDIR(dynamic_idle_min_rpm0, the default, keeps today's behaviour):dynamic_idle_min_rpmdyn_idle_min_rpmis in hundreds, so 35 there is 3500 heredynamic_idle_p_gain,_i_gain,_d_gaindynamic_idle_max_increaseHow it follows Betaflight, and where it differs:
rpm_filter_lpf_hz(150) and then takes the minimum; Bidir dshot - testing dont merge #12027 hands over the last eRPM as received and filters it only for the RPM filter's notches.airmode_throttle_thresholdonce after arming it goes no higher thanthrottle_idle: Betaflight waits for its airmode threshold too, and caps the start atdyn_idle_start_increase.throttle_idle(the throttle curve and navigation use that as the low end), so the mixer maps it onto the range above the dynamic idle; a motor value underthrottle_idleis then sent as a running DShot value, not as a stop, also in failsafe. Open-loop throttles (nav_mc_hover_thr,failsafe_throttlewithout an altitude source) map a little lower while the dynamic low end is underthrottle_idle; closed loops absorb it.dynamic_idle_max_increase, as in Betaflight.It runs only on a multirotor mixer with DShot and
dshot_bidir_enabled. Reversible motors, the logic-condition throttle override and fixed-wing mixers are left as they are. The newDYN_IDLEdebug mode logs P, I, D (x10000) and the slowest motor's frequency (0.1 Hz).The new fields go at the end of
motorConfig_t, which #12027 already moves from version 11 to 12, so there is no second bump: the motor group returns to its defaults once, with #12027. They are on every target, like #12120's, so turningUSE_DSHOT_BIDIRon for more targets later never moves the fields stored after them; only the settings and the controller are underUSE_DSHOT_BIDIR.Overlaps with other open PRs (trial merges against every open PR touching the same files): #12120 and #12121 (mine, throttle boost and thrust linearization) also add fields to
motorConfig_tand code tomixTable(), and #12022 adds settings next to these in the motor group. The overlaps are textual: whichever is merged second, I'll rebase it.Testing
On the bench, motors without props, PIDs at zero so that only the idle moves the motors, armed over MSP: 6 s at low stick, 1.5 s at 1300, 8 s at low stick. Read every 0.1 s: motor outputs, RPM, and the
DYN_IDLEdebug values.dynamic_idle_min_rpmthrottle_idle)On the F722s with airmode on and off; on the Kakute with airmode on and off. Disarming stops the motors each time.
getThrottlePercent()reads 0% at low stick under dynamic idle.Not flown. I'd be glad if someone who flies #12027 on a quad could try it, with
debug_mode = DYN_IDLEin the blackbox.Builds on F4, F7, H7 and AT32 (F7 without
USE_DSHOT_BIDIRin #12027, so without dynamic idle) and SITL. Against #12027's head: flash +1,240 to +1,368 B and RAM +32 to +48 B on F4 (MATEKF405, MATEKF405SE), H7 (MATEKH743, KAKUTEH7MINI) and AT32 (BLUEBERRYF435WING), ITCM +56 to +112 B on H7; on F7, where #12027 has no bidirectional DShot, flash +16 to +24 B (the debug mode's name), RAM +8 B (the fields), ITCM unchanged (KAKUTEF7 and KAKUTEF7HDV 16,096 of 16,384). Unit tests: 620 of 620.