2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
23 #include "build/debug.h"
25 #include "blackbox/blackbox.h"
27 #include "common/maths.h"
28 #include "common/axis.h"
29 #include "common/utils.h"
30 #include "common/filter.h"
32 #include "drivers/light_led.h"
33 #include "drivers/system.h"
34 #include "drivers/gyro_sync.h"
36 #include "sensors/sensors.h"
37 #include "sensors/boardalignment.h"
38 #include "sensors/acceleration.h"
39 #include "sensors/gyro.h"
40 #include "sensors/battery.h"
42 #include "fc/config.h"
43 #include "fc/rc_controls.h"
44 #include "fc/rc_curves.h"
45 #include "fc/runtime_config.h"
47 #include "msp/msp_serial.h"
49 #include "io/beeper.h"
50 #include "io/motors.h"
51 #include "io/servos.h"
52 #include "io/serial.h"
53 #include "io/serial_cli.h"
54 #include "io/statusindicator.h"
55 #include "io/transponder_ir.h"
56 #include "io/asyncfatfs/asyncfatfs.h"
60 #include "scheduler/scheduler.h"
62 #include "flight/mixer.h"
63 #include "flight/servos.h"
64 #include "flight/pid.h"
65 #include "flight/failsafe.h"
66 #include "flight/gtune.h"
67 #include "flight/altitudehold.h"
69 #include "config/config_profile.h"
70 #include "config/config_master.h"
71 #include "config/feature.h"
82 #define GYRO_WATCHDOG_DELAY 80 // delay for gyro sync
84 #define AIRMODE_THOTTLE_THRESHOLD 1350 // Make configurable in the future. ~35% throttle should be fine
86 uint16_t cycleTime
= 0; // this is the number in micro second to achieve a full loop, it can differ a little and is taken into account in the PID loop
89 int16_t headFreeModeHold
;
91 uint8_t motorControlEnable
= false;
93 int16_t telemTemperature1
; // gyro sensor temperature
94 static uint32_t disarmAt
; // Time of automatic disarm when "Don't spin the motors when armed" is enabled and auto_disarm_delay is nonzero
96 extern uint8_t PIDweight
[3];
98 uint16_t filteredCycleTime
;
100 static bool armingCalibrationWasInitialised
;
101 float setpointRate
[3];
104 extern pidControllerFuncPtr pid_controller
;
106 void applyAndSaveAccelerometerTrimsDelta(rollAndPitchTrims_t
*rollAndPitchTrimsDelta
)
108 masterConfig
.accelerometerTrims
.values
.roll
+= rollAndPitchTrimsDelta
->values
.roll
;
109 masterConfig
.accelerometerTrims
.values
.pitch
+= rollAndPitchTrimsDelta
->values
.pitch
;
111 saveConfigAndNotify();
116 void updateGtuneState(void)
118 static bool GTuneWasUsed
= false;
120 if (IS_RC_MODE_ACTIVE(BOXGTUNE
)) {
121 if (!FLIGHT_MODE(GTUNE_MODE
) && ARMING_FLAG(ARMED
)) {
122 ENABLE_FLIGHT_MODE(GTUNE_MODE
);
123 init_Gtune(¤tProfile
->pidProfile
);
126 if (!FLIGHT_MODE(GTUNE_MODE
) && !ARMING_FLAG(ARMED
) && GTuneWasUsed
) {
127 saveConfigAndNotify();
128 GTuneWasUsed
= false;
131 if (FLIGHT_MODE(GTUNE_MODE
) && ARMING_FLAG(ARMED
)) {
132 DISABLE_FLIGHT_MODE(GTUNE_MODE
);
141 if (sensors(SENSOR_BARO
) && !isBaroCalibrationComplete()) {
146 // Note: compass calibration is handled completely differently, outside of the main loop, see f.CALIBRATE_MAG
148 return (!isAccelerationCalibrationComplete() && sensors(SENSOR_ACC
)) || (!isGyroCalibrationComplete());
151 #define RC_RATE_INCREMENTAL 14.54f
152 #define RC_EXPO_POWER 3
154 void calculateSetpointRate(int axis
, int16_t rc
) {
155 float angleRate
, rcRate
, rcSuperfactor
, rcCommandf
;
159 rcExpo
= currentControlRateProfile
->rcExpo8
;
160 rcRate
= currentControlRateProfile
->rcRate8
/ 100.0f
;
162 rcExpo
= currentControlRateProfile
->rcYawExpo8
;
163 rcRate
= currentControlRateProfile
->rcYawRate8
/ 100.0f
;
166 if (rcRate
> 2.0f
) rcRate
= rcRate
+ (RC_RATE_INCREMENTAL
* (rcRate
- 2.0f
));
167 rcCommandf
= rc
/ 500.0f
;
168 rcInput
[axis
] = ABS(rcCommandf
);
171 float expof
= rcExpo
/ 100.0f
;
172 rcCommandf
= rcCommandf
* powerf(rcInput
[axis
], RC_EXPO_POWER
) * expof
+ rcCommandf
* (1-expof
);
175 angleRate
= 200.0f
* rcRate
* rcCommandf
;
177 if (currentControlRateProfile
->rates
[axis
]) {
178 rcSuperfactor
= 1.0f
/ (constrainf(1.0f
- (ABS(rcCommandf
) * (currentControlRateProfile
->rates
[axis
] / 100.0f
)), 0.01f
, 1.00f
));
179 angleRate
*= rcSuperfactor
;
182 if (debugMode
== DEBUG_ANGLERATE
) {
183 debug
[axis
] = angleRate
;
186 setpointRate
[axis
] = constrainf(angleRate
, -1998.0f
, 1998.0f
); // Rate limit protection (deg/sec)
189 void scaleRcCommandToFpvCamAngle(void) {
190 //recalculate sin/cos only when masterConfig.rxConfig.fpvCamAngleDegrees changed
191 static uint8_t lastFpvCamAngleDegrees
= 0;
192 static float cosFactor
= 1.0;
193 static float sinFactor
= 0.0;
195 if (lastFpvCamAngleDegrees
!= masterConfig
.rxConfig
.fpvCamAngleDegrees
){
196 lastFpvCamAngleDegrees
= masterConfig
.rxConfig
.fpvCamAngleDegrees
;
197 cosFactor
= cos_approx(masterConfig
.rxConfig
.fpvCamAngleDegrees
* RAD
);
198 sinFactor
= sin_approx(masterConfig
.rxConfig
.fpvCamAngleDegrees
* RAD
);
201 int16_t roll
= rcCommand
[ROLL
];
202 int16_t yaw
= rcCommand
[YAW
];
203 rcCommand
[ROLL
] = constrain(roll
* cosFactor
- yaw
* sinFactor
, -500, 500);
204 rcCommand
[YAW
] = constrain(yaw
* cosFactor
+ roll
* sinFactor
, -500, 500);
207 void processRcCommand(void)
209 static int16_t lastCommand
[4] = { 0, 0, 0, 0 };
210 static int16_t deltaRC
[4] = { 0, 0, 0, 0 };
211 static int16_t factor
, rcInterpolationFactor
;
212 uint16_t rxRefreshRate
;
213 bool readyToCalculateRate
= false;
215 if (masterConfig
.rxConfig
.rcInterpolation
|| flightModeFlags
) {
217 // Set RC refresh rate for sampling and channels to filter
218 switch (masterConfig
.rxConfig
.rcInterpolation
) {
219 case(RC_SMOOTHING_AUTO
):
220 rxRefreshRate
= constrain(getTaskDeltaTime(TASK_RX
), 1000, 20000) + 1000; // Add slight overhead to prevent ramps
222 case(RC_SMOOTHING_MANUAL
):
223 rxRefreshRate
= 1000 * masterConfig
.rxConfig
.rcInterpolationInterval
;
225 case(RC_SMOOTHING_OFF
):
226 case(RC_SMOOTHING_DEFAULT
):
228 rxRefreshRate
= rxGetRefreshRate();
231 rcInterpolationFactor
= rxRefreshRate
/ targetPidLooptime
+ 1;
233 if (debugMode
== DEBUG_RC_INTERPOLATION
) {
234 for (int axis
= 0; axis
< 2; axis
++) debug
[axis
] = rcCommand
[axis
];
235 debug
[3] = rxRefreshRate
;
238 for (int channel
=0; channel
< 4; channel
++) {
239 deltaRC
[channel
] = rcCommand
[channel
] - (lastCommand
[channel
] - deltaRC
[channel
] * factor
/ rcInterpolationFactor
);
240 lastCommand
[channel
] = rcCommand
[channel
];
243 factor
= rcInterpolationFactor
- 1;
248 // Interpolate steps of rcCommand
250 for (int channel
=0; channel
< 4; channel
++) rcCommand
[channel
] = lastCommand
[channel
] - deltaRC
[channel
] * factor
/rcInterpolationFactor
;
255 readyToCalculateRate
= true;
257 factor
= 0; // reset factor in case of level modes flip flopping
260 if (readyToCalculateRate
|| isRXDataNew
) {
261 // Scaling of AngleRate to camera angle (Mixing Roll and Yaw)
262 if (masterConfig
.rxConfig
.fpvCamAngleDegrees
&& IS_RC_MODE_ACTIVE(BOXFPVANGLEMIX
) && !FLIGHT_MODE(HEADFREE_MODE
))
263 scaleRcCommandToFpvCamAngle();
265 for (int axis
= 0; axis
< 3; axis
++) calculateSetpointRate(axis
, rcCommand
[axis
]);
271 void updateRcCommands(void)
273 // PITCH & ROLL only dynamic PID adjustment, depending on throttle value
275 if (rcData
[THROTTLE
] < currentControlRateProfile
->tpa_breakpoint
) {
278 if (rcData
[THROTTLE
] < 2000) {
279 prop
= 100 - (uint16_t)currentControlRateProfile
->dynThrPID
* (rcData
[THROTTLE
] - currentControlRateProfile
->tpa_breakpoint
) / (2000 - currentControlRateProfile
->tpa_breakpoint
);
281 prop
= 100 - currentControlRateProfile
->dynThrPID
;
285 for (int axis
= 0; axis
< 3; axis
++) {
286 // non coupled PID reduction scaler used in PID controller 1 and PID controller 2.
287 PIDweight
[axis
] = prop
;
289 int32_t tmp
= MIN(ABS(rcData
[axis
] - masterConfig
.rxConfig
.midrc
), 500);
290 if (axis
== ROLL
|| axis
== PITCH
) {
291 if (tmp
> masterConfig
.rcControlsConfig
.deadband
) {
292 tmp
-= masterConfig
.rcControlsConfig
.deadband
;
296 rcCommand
[axis
] = tmp
;
297 } else if (axis
== YAW
) {
298 if (tmp
> masterConfig
.rcControlsConfig
.yaw_deadband
) {
299 tmp
-= masterConfig
.rcControlsConfig
.yaw_deadband
;
303 rcCommand
[axis
] = tmp
* -masterConfig
.yaw_control_direction
;
305 if (rcData
[axis
] < masterConfig
.rxConfig
.midrc
) {
306 rcCommand
[axis
] = -rcCommand
[axis
];
311 if (feature(FEATURE_3D
)) {
312 tmp
= constrain(rcData
[THROTTLE
], PWM_RANGE_MIN
, PWM_RANGE_MAX
);
313 tmp
= (uint32_t)(tmp
- PWM_RANGE_MIN
);
315 tmp
= constrain(rcData
[THROTTLE
], masterConfig
.rxConfig
.mincheck
, PWM_RANGE_MAX
);
316 tmp
= (uint32_t)(tmp
- masterConfig
.rxConfig
.mincheck
) * PWM_RANGE_MIN
/ (PWM_RANGE_MAX
- masterConfig
.rxConfig
.mincheck
);
318 rcCommand
[THROTTLE
] = rcLookupThrottle(tmp
);
320 if (feature(FEATURE_3D
) && IS_RC_MODE_ACTIVE(BOX3DDISABLESWITCH
) && !failsafeIsActive()) {
321 fix12_t throttleScaler
= qConstruct(rcCommand
[THROTTLE
] - 1000, 1000);
322 rcCommand
[THROTTLE
] = masterConfig
.rxConfig
.midrc
+ qMultiply(throttleScaler
, PWM_RANGE_MAX
- masterConfig
.rxConfig
.midrc
);
325 if (FLIGHT_MODE(HEADFREE_MODE
)) {
326 const float radDiff
= degreesToRadians(DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
) - headFreeModeHold
);
327 const float cosDiff
= cos_approx(radDiff
);
328 const float sinDiff
= sin_approx(radDiff
);
329 const int16_t rcCommand_PITCH
= rcCommand
[PITCH
] * cosDiff
+ rcCommand
[ROLL
] * sinDiff
;
330 rcCommand
[ROLL
] = rcCommand
[ROLL
] * cosDiff
- rcCommand
[PITCH
] * sinDiff
;
331 rcCommand
[PITCH
] = rcCommand_PITCH
;
335 void updateLEDs(void)
337 if (ARMING_FLAG(ARMED
)) {
340 if (IS_RC_MODE_ACTIVE(BOXARM
) == 0 || armingCalibrationWasInitialised
) {
341 ENABLE_ARMING_FLAG(OK_TO_ARM
);
344 if (!STATE(SMALL_ANGLE
)) {
345 DISABLE_ARMING_FLAG(OK_TO_ARM
);
348 if (isCalibrating() || (averageSystemLoadPercent
> 100)) {
350 DISABLE_ARMING_FLAG(OK_TO_ARM
);
352 if (ARMING_FLAG(OK_TO_ARM
)) {
365 armingCalibrationWasInitialised
= false;
367 if (ARMING_FLAG(ARMED
)) {
368 DISABLE_ARMING_FLAG(ARMED
);
371 if (feature(FEATURE_BLACKBOX
)) {
376 beeper(BEEPER_DISARMING
); // emit disarm tone
380 #define TELEMETRY_FUNCTION_MASK (FUNCTION_TELEMETRY_FRSKY | FUNCTION_TELEMETRY_HOTT | FUNCTION_TELEMETRY_LTM | FUNCTION_TELEMETRY_SMARTPORT | FUNCTION_TELEMETRY_CRSF)
382 void releaseSharedTelemetryPorts(void) {
383 serialPort_t
*sharedPort
= findSharedSerialPort(TELEMETRY_FUNCTION_MASK
, FUNCTION_MSP
);
385 mspSerialReleasePortIfAllocated(sharedPort
);
386 sharedPort
= findNextSharedSerialPort(TELEMETRY_FUNCTION_MASK
, FUNCTION_MSP
);
392 static bool firstArmingCalibrationWasCompleted
;
394 if (masterConfig
.gyro_cal_on_first_arm
&& !firstArmingCalibrationWasCompleted
) {
395 gyroSetCalibrationCycles();
396 armingCalibrationWasInitialised
= true;
397 firstArmingCalibrationWasCompleted
= true;
400 if (!isGyroCalibrationComplete()) return; // prevent arming before gyro is calibrated
402 if (ARMING_FLAG(OK_TO_ARM
)) {
403 if (ARMING_FLAG(ARMED
)) {
406 if (IS_RC_MODE_ACTIVE(BOXFAILSAFE
)) {
409 if (!ARMING_FLAG(PREVENT_ARMING
)) {
410 ENABLE_ARMING_FLAG(ARMED
);
411 ENABLE_ARMING_FLAG(WAS_EVER_ARMED
);
412 headFreeModeHold
= DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
);
415 if (feature(FEATURE_BLACKBOX
)) {
416 serialPort_t
*sharedBlackboxAndMspPort
= findSharedSerialPort(FUNCTION_BLACKBOX
, FUNCTION_MSP
);
417 if (sharedBlackboxAndMspPort
) {
418 mspSerialReleasePortIfAllocated(sharedBlackboxAndMspPort
);
423 disarmAt
= millis() + masterConfig
.auto_disarm_delay
* 1000; // start disarm timeout, will be extended when throttle is nonzero
425 //beep to indicate arming
427 if (feature(FEATURE_GPS
) && STATE(GPS_FIX
) && GPS_numSat
>= 5)
428 beeper(BEEPER_ARMING_GPS_FIX
);
430 beeper(BEEPER_ARMING
);
432 beeper(BEEPER_ARMING
);
439 if (!ARMING_FLAG(ARMED
)) {
440 beeperConfirmationBeeps(1);
444 // Automatic ACC Offset Calibration
445 bool AccInflightCalibrationArmed
= false;
446 bool AccInflightCalibrationMeasurementDone
= false;
447 bool AccInflightCalibrationSavetoEEProm
= false;
448 bool AccInflightCalibrationActive
= false;
449 uint16_t InflightcalibratingA
= 0;
451 void handleInflightCalibrationStickPosition(void)
453 if (AccInflightCalibrationMeasurementDone
) {
454 // trigger saving into eeprom after landing
455 AccInflightCalibrationMeasurementDone
= false;
456 AccInflightCalibrationSavetoEEProm
= true;
458 AccInflightCalibrationArmed
= !AccInflightCalibrationArmed
;
459 if (AccInflightCalibrationArmed
) {
460 beeper(BEEPER_ACC_CALIBRATION
);
462 beeper(BEEPER_ACC_CALIBRATION_FAIL
);
467 static void updateInflightCalibrationState(void)
469 if (AccInflightCalibrationArmed
&& ARMING_FLAG(ARMED
) && rcData
[THROTTLE
] > masterConfig
.rxConfig
.mincheck
&& !IS_RC_MODE_ACTIVE(BOXARM
)) { // Copter is airborne and you are turning it off via boxarm : start measurement
470 InflightcalibratingA
= 50;
471 AccInflightCalibrationArmed
= false;
473 if (IS_RC_MODE_ACTIVE(BOXCALIB
)) { // Use the Calib Option to activate : Calib = TRUE measurement started, Land and Calib = 0 measurement stored
474 if (!AccInflightCalibrationActive
&& !AccInflightCalibrationMeasurementDone
)
475 InflightcalibratingA
= 50;
476 AccInflightCalibrationActive
= true;
477 } else if (AccInflightCalibrationMeasurementDone
&& !ARMING_FLAG(ARMED
)) {
478 AccInflightCalibrationMeasurementDone
= false;
479 AccInflightCalibrationSavetoEEProm
= true;
483 void updateMagHold(void)
485 if (ABS(rcCommand
[YAW
]) < 15 && FLIGHT_MODE(MAG_MODE
)) {
486 int16_t dif
= DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
) - magHold
;
491 dif
*= -masterConfig
.yaw_control_direction
;
492 if (STATE(SMALL_ANGLE
))
493 rcCommand
[YAW
] -= dif
* currentProfile
->pidProfile
.P8
[PIDMAG
] / 30; // 18 deg
495 magHold
= DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
);
498 void processRx(uint32_t currentTime
)
500 static bool armedBeeperOn
= false;
501 static bool airmodeIsActivated
;
503 calculateRxChannelsAndUpdateFailsafe(currentTime
);
505 // in 3D mode, we need to be able to disarm by switch at any time
506 if (feature(FEATURE_3D
)) {
507 if (!IS_RC_MODE_ACTIVE(BOXARM
))
511 updateRSSI(currentTime
);
513 if (feature(FEATURE_FAILSAFE
)) {
515 if (currentTime
> FAILSAFE_POWER_ON_DELAY_US
&& !failsafeIsMonitoring()) {
516 failsafeStartMonitoring();
519 failsafeUpdateState();
522 throttleStatus_e throttleStatus
= calculateThrottleStatus(&masterConfig
.rxConfig
, masterConfig
.flight3DConfig
.deadband3d_throttle
);
524 if (isAirmodeActive() && ARMING_FLAG(ARMED
)) {
525 if (rcCommand
[THROTTLE
] >= masterConfig
.rxConfig
.airModeActivateThreshold
) airmodeIsActivated
= true; // Prevent Iterm from being reset
527 airmodeIsActivated
= false;
530 /* In airmode Iterm should be prevented to grow when Low thottle and Roll + Pitch Centered.
531 This is needed to prevent Iterm winding on the ground, but keep full stabilisation on 0 throttle while in air */
532 if (throttleStatus
== THROTTLE_LOW
&& !airmodeIsActivated
) {
533 pidResetErrorGyroState();
534 if (currentProfile
->pidProfile
.pidAtMinThrottle
)
535 pidStabilisationState(PID_STABILISATION_ON
);
537 pidStabilisationState(PID_STABILISATION_OFF
);
539 pidStabilisationState(PID_STABILISATION_ON
);
542 // When armed and motors aren't spinning, do beeps and then disarm
543 // board after delay so users without buzzer won't lose fingers.
544 // mixTable constrains motor commands, so checking throttleStatus is enough
545 if (ARMING_FLAG(ARMED
)
546 && feature(FEATURE_MOTOR_STOP
)
547 && !STATE(FIXED_WING
)
548 && !feature(FEATURE_3D
)
549 && !isAirmodeActive()
551 if (isUsingSticksForArming()) {
552 if (throttleStatus
== THROTTLE_LOW
) {
553 if (masterConfig
.auto_disarm_delay
!= 0
554 && (int32_t)(disarmAt
- millis()) < 0
556 // auto-disarm configured and delay is over
558 armedBeeperOn
= false;
560 // still armed; do warning beeps while armed
561 beeper(BEEPER_ARMED
);
562 armedBeeperOn
= true;
565 // throttle is not low
566 if (masterConfig
.auto_disarm_delay
!= 0) {
567 // extend disarm time
568 disarmAt
= millis() + masterConfig
.auto_disarm_delay
* 1000;
573 armedBeeperOn
= false;
577 // arming is via AUX switch; beep while throttle low
578 if (throttleStatus
== THROTTLE_LOW
) {
579 beeper(BEEPER_ARMED
);
580 armedBeeperOn
= true;
581 } else if (armedBeeperOn
) {
583 armedBeeperOn
= false;
588 processRcStickPositions(&masterConfig
.rxConfig
, throttleStatus
, masterConfig
.disarm_kill_switch
);
590 if (feature(FEATURE_INFLIGHT_ACC_CAL
)) {
591 updateInflightCalibrationState();
594 updateActivatedModes(masterConfig
.modeActivationConditions
);
597 updateAdjustmentStates(masterConfig
.adjustmentRanges
);
598 processRcAdjustments(currentControlRateProfile
, &masterConfig
.rxConfig
);
601 bool canUseHorizonMode
= true;
603 if ((IS_RC_MODE_ACTIVE(BOXANGLE
) || (feature(FEATURE_FAILSAFE
) && failsafeIsActive())) && (sensors(SENSOR_ACC
))) {
604 // bumpless transfer to Level mode
605 canUseHorizonMode
= false;
607 if (!FLIGHT_MODE(ANGLE_MODE
)) {
608 ENABLE_FLIGHT_MODE(ANGLE_MODE
);
611 DISABLE_FLIGHT_MODE(ANGLE_MODE
); // failsafe support
614 if (IS_RC_MODE_ACTIVE(BOXHORIZON
) && canUseHorizonMode
) {
616 DISABLE_FLIGHT_MODE(ANGLE_MODE
);
618 if (!FLIGHT_MODE(HORIZON_MODE
)) {
619 ENABLE_FLIGHT_MODE(HORIZON_MODE
);
622 DISABLE_FLIGHT_MODE(HORIZON_MODE
);
625 if (FLIGHT_MODE(ANGLE_MODE
) || FLIGHT_MODE(HORIZON_MODE
)) {
632 if (sensors(SENSOR_ACC
) || sensors(SENSOR_MAG
)) {
633 if (IS_RC_MODE_ACTIVE(BOXMAG
)) {
634 if (!FLIGHT_MODE(MAG_MODE
)) {
635 ENABLE_FLIGHT_MODE(MAG_MODE
);
636 magHold
= DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
);
639 DISABLE_FLIGHT_MODE(MAG_MODE
);
641 if (IS_RC_MODE_ACTIVE(BOXHEADFREE
)) {
642 if (!FLIGHT_MODE(HEADFREE_MODE
)) {
643 ENABLE_FLIGHT_MODE(HEADFREE_MODE
);
646 DISABLE_FLIGHT_MODE(HEADFREE_MODE
);
648 if (IS_RC_MODE_ACTIVE(BOXHEADADJ
)) {
649 headFreeModeHold
= DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
); // acquire new heading
655 if (sensors(SENSOR_GPS
)) {
656 updateGpsWaypointsAndMode();
660 if (IS_RC_MODE_ACTIVE(BOXPASSTHRU
)) {
661 ENABLE_FLIGHT_MODE(PASSTHRU_MODE
);
663 DISABLE_FLIGHT_MODE(PASSTHRU_MODE
);
666 if (masterConfig
.mixerMode
== MIXER_FLYING_WING
|| masterConfig
.mixerMode
== MIXER_AIRPLANE
) {
667 DISABLE_FLIGHT_MODE(HEADFREE_MODE
);
671 if (feature(FEATURE_TELEMETRY
)) {
672 if ((!masterConfig
.telemetryConfig
.telemetry_switch
&& ARMING_FLAG(ARMED
)) ||
673 (masterConfig
.telemetryConfig
.telemetry_switch
&& IS_RC_MODE_ACTIVE(BOXTELEMETRY
))) {
675 releaseSharedTelemetryPorts();
677 // the telemetry state must be checked immediately so that shared serial ports are released.
678 telemetryCheckState();
679 mspSerialAllocatePorts();
685 vtxUpdateActivatedChannel();
689 void subTaskPidController(void)
692 if (debugMode
== DEBUG_PIDLOOP
) {startTime
= micros();}
693 // PID - note this is function pointer set by setPIDController()
695 ¤tProfile
->pidProfile
,
696 masterConfig
.max_angle_inclination
,
697 &masterConfig
.accelerometerTrims
,
698 &masterConfig
.rxConfig
700 if (debugMode
== DEBUG_PIDLOOP
) {debug
[2] = micros() - startTime
;}
703 void subTaskMainSubprocesses(void)
706 const uint32_t startTime
= micros();
708 // Read out gyro temperature. can use it for something somewhere. maybe get MCU temperature instead? lots of fun possibilities.
709 if (gyro
.temperature
) {
710 gyro
.temperature(&telemTemperature1
);
714 if (sensors(SENSOR_MAG
)) {
723 #if defined(BARO) || defined(SONAR)
724 // updateRcCommands sets rcCommand, which is needed by updateAltHoldState and updateSonarAltHoldState
726 if (sensors(SENSOR_BARO
) || sensors(SENSOR_SONAR
)) {
727 if (FLIGHT_MODE(BARO_MODE
) || FLIGHT_MODE(SONAR_MODE
)) {
728 applyAltHold(&masterConfig
.airplaneConfig
);
733 // If we're armed, at minimum throttle, and we do arming via the
734 // sticks, do not process yaw input from the rx. We do this so the
735 // motors do not spin up while we are trying to arm or disarm.
736 // Allow yaw control for tricopters if the user wants the servo to move even when unarmed.
737 if (isUsingSticksForArming() && rcData
[THROTTLE
] <= masterConfig
.rxConfig
.mincheck
738 #ifndef USE_QUAD_MIXER_ONLY
740 && !((masterConfig
.mixerMode
== MIXER_TRI
|| masterConfig
.mixerMode
== MIXER_CUSTOM_TRI
) && masterConfig
.servoMixerConfig
.tri_unarmed_servo
)
742 && masterConfig
.mixerMode
!= MIXER_AIRPLANE
743 && masterConfig
.mixerMode
!= MIXER_FLYING_WING
747 setpointRate
[YAW
] = 0;
750 if (masterConfig
.throttle_correction_value
&& (FLIGHT_MODE(ANGLE_MODE
) || FLIGHT_MODE(HORIZON_MODE
))) {
751 rcCommand
[THROTTLE
] += calculateThrottleAngleCorrection(masterConfig
.throttle_correction_value
);
757 if (sensors(SENSOR_GPS
)) {
758 if ((FLIGHT_MODE(GPS_HOME_MODE
) || FLIGHT_MODE(GPS_HOLD_MODE
)) && STATE(GPS_FIX_HOME
)) {
759 updateGpsStateForHomeAndHoldMode();
769 if (!cliMode
&& feature(FEATURE_BLACKBOX
)) {
770 handleBlackbox(startTime
);
775 transponderUpdate(startTime
);
777 if (debugMode
== DEBUG_PIDLOOP
) {debug
[1] = micros() - startTime
;}
780 void subTaskMotorUpdate(void)
782 const uint32_t startTime
= micros();
783 if (debugMode
== DEBUG_CYCLETIME
) {
784 static uint32_t previousMotorUpdateTime
;
785 const uint32_t currentDeltaTime
= startTime
- previousMotorUpdateTime
;
786 debug
[2] = currentDeltaTime
;
787 debug
[3] = currentDeltaTime
- targetPidLooptime
;
788 previousMotorUpdateTime
= startTime
;
791 mixTable(¤tProfile
->pidProfile
);
794 // motor outputs are used as sources for servo mixing, so motors must be calculated using mixTable() before servos.
800 if (motorControlEnable
) {
803 if (debugMode
== DEBUG_PIDLOOP
) {debug
[3] = micros() - startTime
;}
806 uint8_t setPidUpdateCountDown(void)
808 if (masterConfig
.gyro_soft_lpf_hz
) {
809 return masterConfig
.pid_process_denom
- 1;
815 // Function for loop trigger
816 void taskMainPidLoopCheck(uint32_t currentTime
)
820 static bool runTaskMainSubprocesses
;
821 static uint8_t pidUpdateCountdown
;
823 cycleTime
= getTaskDeltaTime(TASK_SELF
);
825 if (debugMode
== DEBUG_CYCLETIME
) {
826 debug
[0] = cycleTime
;
827 debug
[1] = averageSystemLoadPercent
;
830 if (runTaskMainSubprocesses
) {
831 subTaskMainSubprocesses();
832 runTaskMainSubprocesses
= false;
837 if (pidUpdateCountdown
) {
838 pidUpdateCountdown
--;
840 pidUpdateCountdown
= setPidUpdateCountDown();
841 subTaskPidController();
842 subTaskMotorUpdate();
843 runTaskMainSubprocesses
= true;