-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Firmware support for improved mixer wizard (MSP2_INAV_MOTOR_LOCATE command for motor identification) #11231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: maintenance-9.x
Are you sure you want to change the base?
Firmware support for improved mixer wizard (MSP2_INAV_MOTOR_LOCATE command for motor identification) #11231
Changes from all commits
7628c25
2bfa7ba
abf3a19
8d4c6ce
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,211 @@ | ||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||
| * This file is part of INAV. | ||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||
| * INAV is free software: you can redistribute it and/or modify | ||||||||||||||||||||||||||||||||
| * it under the terms of the GNU General Public License as published by | ||||||||||||||||||||||||||||||||
| * the Free Software Foundation, either version 3 of the License, or | ||||||||||||||||||||||||||||||||
| * (at your option) any later version. | ||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||
| * INAV is distributed in the hope that it will be useful, | ||||||||||||||||||||||||||||||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||||||||||||||||||||||||||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||||||||||||||||||||||||||||
| * GNU General Public License for more details. | ||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||
| * You should have received a copy of the GNU General Public License | ||||||||||||||||||||||||||||||||
| * along with INAV. If not, see <http://www.gnu.org/licenses/>. | ||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| #include <stdbool.h> | ||||||||||||||||||||||||||||||||
| #include <stdint.h> | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| #include "platform.h" | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| #ifdef USE_DSHOT | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| #include "drivers/pwm_output.h" | ||||||||||||||||||||||||||||||||
| #include "drivers/time.h" | ||||||||||||||||||||||||||||||||
| #include "flight/mixer.h" | ||||||||||||||||||||||||||||||||
| #include "fc/motor_locate.h" | ||||||||||||||||||||||||||||||||
| #include "fc/runtime_config.h" | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // Timing constants (in microseconds) | ||||||||||||||||||||||||||||||||
| #define LOCATE_JERK_DURATION_US 100000 // 100ms motor jerk | ||||||||||||||||||||||||||||||||
| #define LOCATE_JERK_PAUSE_US 100000 // 100ms pause after jerk | ||||||||||||||||||||||||||||||||
| #define LOCATE_BEEP_ON_US 80000 // 80ms beep on | ||||||||||||||||||||||||||||||||
| #define LOCATE_BEEP_OFF_US 80000 // 80ms beep off | ||||||||||||||||||||||||||||||||
| #define LOCATE_CYCLE_DURATION_US 2000000 // 2 seconds total cycle | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // Motor throttle for jerk (~12% throttle) | ||||||||||||||||||||||||||||||||
| // DShot range: 48 (0%) to 2047 (100%) | ||||||||||||||||||||||||||||||||
| #define LOCATE_JERK_THROTTLE 288 // 48 + (1999 * 0.12) ≈ 288 | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| typedef enum { | ||||||||||||||||||||||||||||||||
| LOCATE_STATE_IDLE, | ||||||||||||||||||||||||||||||||
| LOCATE_STATE_JERK, | ||||||||||||||||||||||||||||||||
| LOCATE_STATE_JERK_PAUSE, | ||||||||||||||||||||||||||||||||
| LOCATE_STATE_BEEP_ON, | ||||||||||||||||||||||||||||||||
| LOCATE_STATE_BEEP_OFF, | ||||||||||||||||||||||||||||||||
| } motorLocateState_e; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| #define LOCATE_NUM_BEEPS 4 | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // Global flag for fast inline check in FAST_CODE path | ||||||||||||||||||||||||||||||||
| bool motorLocateActive = false; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| static struct { | ||||||||||||||||||||||||||||||||
| motorLocateState_e state; | ||||||||||||||||||||||||||||||||
| uint8_t motorIndex; | ||||||||||||||||||||||||||||||||
| uint8_t beepCount; | ||||||||||||||||||||||||||||||||
| timeUs_t stateStartTime; | ||||||||||||||||||||||||||||||||
| timeUs_t cycleStartTime; | ||||||||||||||||||||||||||||||||
| } locateState = { | ||||||||||||||||||||||||||||||||
| .state = LOCATE_STATE_IDLE, | ||||||||||||||||||||||||||||||||
| .motorIndex = 0, | ||||||||||||||||||||||||||||||||
| .beepCount = 0, | ||||||||||||||||||||||||||||||||
| .stateStartTime = 0, | ||||||||||||||||||||||||||||||||
| .cycleStartTime = 0, | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // Forward declarations | ||||||||||||||||||||||||||||||||
| static void transitionToState(motorLocateState_e newState, timeUs_t now); | ||||||||||||||||||||||||||||||||
| static timeUs_t getStateDuration(motorLocateState_e state); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| bool motorLocateStart(uint8_t motorIndex) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| // Don't allow if already running | ||||||||||||||||||||||||||||||||
| if (locateState.state != LOCATE_STATE_IDLE) { | ||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // Validate motor index | ||||||||||||||||||||||||||||||||
| if (motorIndex >= getMotorCount()) { | ||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // Only allow when disarmed | ||||||||||||||||||||||||||||||||
| if (ARMING_FLAG(ARMED)) { | ||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // Only allow with DShot protocol | ||||||||||||||||||||||||||||||||
| if (!isMotorProtocolDshot()) { | ||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| timeUs_t now = micros(); | ||||||||||||||||||||||||||||||||
| locateState.motorIndex = motorIndex; | ||||||||||||||||||||||||||||||||
| locateState.beepCount = 0; | ||||||||||||||||||||||||||||||||
| locateState.cycleStartTime = now; | ||||||||||||||||||||||||||||||||
| transitionToState(LOCATE_STATE_JERK, now); | ||||||||||||||||||||||||||||||||
| motorLocateActive = true; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| void motorLocateStop(void) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| locateState.state = LOCATE_STATE_IDLE; | ||||||||||||||||||||||||||||||||
| motorLocateActive = false; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| bool motorLocateIsActive(void) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| return locateState.state != LOCATE_STATE_IDLE; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| static void transitionToState(motorLocateState_e newState, timeUs_t now) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| locateState.state = newState; | ||||||||||||||||||||||||||||||||
| locateState.stateStartTime = now; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| static timeUs_t getStateDuration(motorLocateState_e state) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| switch (state) { | ||||||||||||||||||||||||||||||||
| case LOCATE_STATE_JERK: | ||||||||||||||||||||||||||||||||
| return LOCATE_JERK_DURATION_US; | ||||||||||||||||||||||||||||||||
| case LOCATE_STATE_JERK_PAUSE: | ||||||||||||||||||||||||||||||||
| return LOCATE_JERK_PAUSE_US; | ||||||||||||||||||||||||||||||||
| case LOCATE_STATE_BEEP_ON: | ||||||||||||||||||||||||||||||||
| return LOCATE_BEEP_ON_US; | ||||||||||||||||||||||||||||||||
| case LOCATE_STATE_BEEP_OFF: | ||||||||||||||||||||||||||||||||
| return (locateState.beepCount >= LOCATE_NUM_BEEPS - 1) ? LOCATE_JERK_PAUSE_US : LOCATE_BEEP_OFF_US; | ||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||
| return 0; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| static motorLocateState_e advanceToNextState(motorLocateState_e state) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| switch (state) { | ||||||||||||||||||||||||||||||||
| case LOCATE_STATE_JERK: | ||||||||||||||||||||||||||||||||
| return LOCATE_STATE_JERK_PAUSE; | ||||||||||||||||||||||||||||||||
| case LOCATE_STATE_JERK_PAUSE: | ||||||||||||||||||||||||||||||||
| locateState.beepCount = 0; | ||||||||||||||||||||||||||||||||
| return LOCATE_STATE_BEEP_ON; | ||||||||||||||||||||||||||||||||
| case LOCATE_STATE_BEEP_ON: | ||||||||||||||||||||||||||||||||
| return LOCATE_STATE_BEEP_OFF; | ||||||||||||||||||||||||||||||||
| case LOCATE_STATE_BEEP_OFF: | ||||||||||||||||||||||||||||||||
| if (++locateState.beepCount >= LOCATE_NUM_BEEPS) { | ||||||||||||||||||||||||||||||||
| return LOCATE_STATE_JERK; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| return LOCATE_STATE_BEEP_ON; | ||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||
| return LOCATE_STATE_IDLE; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| static uint16_t getMotorValueForState(motorLocateState_e state, uint8_t motorIdx, uint8_t targetMotorIdx) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| if (motorIdx != targetMotorIdx) { | ||||||||||||||||||||||||||||||||
| return DSHOT_CMD_MOTOR_STOP; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| switch (state) { | ||||||||||||||||||||||||||||||||
| case LOCATE_STATE_JERK: | ||||||||||||||||||||||||||||||||
| return LOCATE_JERK_THROTTLE; | ||||||||||||||||||||||||||||||||
| case LOCATE_STATE_BEEP_ON: | ||||||||||||||||||||||||||||||||
| // Ascending tones help humans locate sound using multiple hearing mechanisms | ||||||||||||||||||||||||||||||||
| return DSHOT_CMD_BEACON1 + locateState.beepCount; | ||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||
| return DSHOT_CMD_MOTOR_STOP; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| bool motorLocateUpdate(void) | ||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||
| if (locateState.state == LOCATE_STATE_IDLE) { | ||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Comment on lines
+175
to
+179
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: Add a check in
Suggested change
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not needed. Motor protocol is configured at boot via |
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // Immediately stop if aircraft becomes armed | ||||||||||||||||||||||||||||||||
| if (ARMING_FLAG(ARMED)) { | ||||||||||||||||||||||||||||||||
| motorLocateStop(); | ||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| timeUs_t now = micros(); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // Check if total cycle time exceeded | ||||||||||||||||||||||||||||||||
| if (cmpTimeUs(now, locateState.cycleStartTime) >= LOCATE_CYCLE_DURATION_US) { | ||||||||||||||||||||||||||||||||
| motorLocateStop(); | ||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // Check for state transition | ||||||||||||||||||||||||||||||||
| timeUs_t stateDuration = getStateDuration(locateState.state); | ||||||||||||||||||||||||||||||||
| if (cmpTimeUs(now, locateState.stateStartTime) >= stateDuration) { | ||||||||||||||||||||||||||||||||
| transitionToState(advanceToNextState(locateState.state), now); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // Apply motor values for current state | ||||||||||||||||||||||||||||||||
| uint8_t motorCount = getMotorCount(); | ||||||||||||||||||||||||||||||||
| for (uint8_t i = 0; i < motorCount; i++) { | ||||||||||||||||||||||||||||||||
| uint16_t value = getMotorValueForState(locateState.state, i, locateState.motorIndex); | ||||||||||||||||||||||||||||||||
| pwmWriteMotor(i, value); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| #endif // USE_DSHOT | ||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| /* | ||
| * This file is part of INAV. | ||
| * | ||
| * INAV is free software: you can redistribute it and/or modify | ||
| * it under the terms of the GNU General Public License as published by | ||
| * the Free Software Foundation, either version 3 of the License, or | ||
| * (at your option) any later version. | ||
| * | ||
| * INAV is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| * GNU General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License | ||
| * along with INAV. If not, see <http://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <stdbool.h> | ||
| #include <stdint.h> | ||
|
|
||
| // Global flag for fast inline check - avoids function call in FAST_CODE path | ||
| extern bool motorLocateActive; | ||
|
|
||
| // Start motor locate cycle for specified motor index | ||
| // Runs jerk+beep pattern for ~2 seconds then stops automatically | ||
| // Returns false if locate is already running or motor index invalid | ||
| bool motorLocateStart(uint8_t motorIndex); | ||
|
|
||
| // Stop any running motor locate cycle | ||
| void motorLocateStop(void); | ||
|
|
||
| // Check if motor locate is currently active | ||
| bool motorLocateIsActive(void); | ||
|
|
||
| // Called from motor update loop to apply locate overrides | ||
| // Returns true if locate is active and motor values were overridden | ||
| bool motorLocateUpdate(void); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Mark the
locateStatestruct asvolatileto ensure its members are correctly accessed across different execution contexts and prevent potential compiler optimization issues. [possible issue, importance: 8]There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not needed. INAV uses cooperative scheduling (not preemptive). Both
writeMotors()(FAST_CODE) and MSP handlers run from main loop context via the scheduler - there's no interrupt contention.FAST_CODEis a linker section attribute that places code in fast RAM, not an indication of interrupt context. All accesses tolocateStateare single-threaded.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question:
Not needed. INAV uses cooperative scheduling (not preemptive). Both
writeMotors()(FAST_CODE) and MSP handlers run from main loop context via the scheduler - there's no interrupt contention.FAST_CODEis a linker section attribute that places code in fast RAM, not an indication of interrupt context. All accesses tolocateStateare single-threaded.Answer:
Add a project-specific instruction to stop the model from suggesting
volatile/atomic/thread-safety changes unless there is explicit evidence of true concurrency (ISR/preemption/multithreading), and to not infer “interrupt context” from attributes likeFAST_CODE.If you still see these false positives, you can also reduce overall “best-practices” style suggestions by keeping (or ensuring) the default:
Relevant Sources: