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"
48 #include "msp/msp_serial.h"
50 #include "io/beeper.h"
51 #include "io/motors.h"
52 #include "io/servos.h"
53 #include "io/serial.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/altitudehold.h"
68 #include "config/config_profile.h"
69 #include "config/config_master.h"
70 #include "config/feature.h"
81 #define GYRO_WATCHDOG_DELAY 80 // delay for gyro sync
83 #define AIRMODE_THOTTLE_THRESHOLD 1350 // Make configurable in the future. ~35% throttle should be fine
86 int16_t headFreeModeHold
;
88 uint8_t motorControlEnable
= false;
90 int16_t telemTemperature1
; // gyro sensor temperature
91 static uint32_t disarmAt
; // Time of automatic disarm when "Don't spin the motors when armed" is enabled and auto_disarm_delay is nonzero
93 static float throttlePIDAttenuation
;
96 static bool armingCalibrationWasInitialised
;
97 static float setpointRate
[3], rcDeflection
[3], rcDeflectionAbs
[3];
99 float getSetpointRate(int axis
) {
100 return setpointRate
[axis
];
103 float getRcDeflection(int axis
) {
104 return rcDeflection
[axis
];
107 float getRcDeflectionAbs(int axis
) {
108 return rcDeflectionAbs
[axis
];
111 void applyAndSaveAccelerometerTrimsDelta(rollAndPitchTrims_t
*rollAndPitchTrimsDelta
)
113 accelerometerConfig()->accelerometerTrims
.values
.roll
+= rollAndPitchTrimsDelta
->values
.roll
;
114 accelerometerConfig()->accelerometerTrims
.values
.pitch
+= rollAndPitchTrimsDelta
->values
.pitch
;
116 saveConfigAndNotify();
122 if (sensors(SENSOR_BARO
) && !isBaroCalibrationComplete()) {
127 // Note: compass calibration is handled completely differently, outside of the main loop, see f.CALIBRATE_MAG
129 return (!isAccelerationCalibrationComplete() && sensors(SENSOR_ACC
)) || (!isGyroCalibrationComplete());
132 #define RC_RATE_INCREMENTAL 14.54f
134 void calculateSetpointRate(int axis
, int16_t rc
) {
135 float angleRate
, rcRate
, rcSuperfactor
, rcCommandf
;
139 rcExpo
= currentControlRateProfile
->rcExpo8
;
140 rcRate
= currentControlRateProfile
->rcRate8
/ 100.0f
;
142 rcExpo
= currentControlRateProfile
->rcYawExpo8
;
143 rcRate
= currentControlRateProfile
->rcYawRate8
/ 100.0f
;
146 if (rcRate
> 2.0f
) rcRate
= rcRate
+ (RC_RATE_INCREMENTAL
* (rcRate
- 2.0f
));
147 rcCommandf
= rc
/ 500.0f
;
148 rcDeflection
[axis
] = rcCommandf
;
149 rcDeflectionAbs
[axis
] = ABS(rcCommandf
);
152 float expof
= rcExpo
/ 100.0f
;
153 rcCommandf
= rcCommandf
* power3(rcDeflectionAbs
[axis
]) * expof
+ rcCommandf
* (1-expof
);
156 angleRate
= 200.0f
* rcRate
* rcCommandf
;
158 if (currentControlRateProfile
->rates
[axis
]) {
159 rcSuperfactor
= 1.0f
/ (constrainf(1.0f
- (ABS(rcCommandf
) * (currentControlRateProfile
->rates
[axis
] / 100.0f
)), 0.01f
, 1.00f
));
160 angleRate
*= rcSuperfactor
;
163 DEBUG_SET(DEBUG_ANGLERATE
, axis
, angleRate
);
165 setpointRate
[axis
] = constrainf(angleRate
, -1998.0f
, 1998.0f
); // Rate limit protection (deg/sec)
168 void scaleRcCommandToFpvCamAngle(void) {
169 //recalculate sin/cos only when rxConfig()->fpvCamAngleDegrees changed
170 static uint8_t lastFpvCamAngleDegrees
= 0;
171 static float cosFactor
= 1.0;
172 static float sinFactor
= 0.0;
174 if (lastFpvCamAngleDegrees
!= rxConfig()->fpvCamAngleDegrees
){
175 lastFpvCamAngleDegrees
= rxConfig()->fpvCamAngleDegrees
;
176 cosFactor
= cos_approx(rxConfig()->fpvCamAngleDegrees
* RAD
);
177 sinFactor
= sin_approx(rxConfig()->fpvCamAngleDegrees
* RAD
);
180 int16_t roll
= rcCommand
[ROLL
];
181 int16_t yaw
= rcCommand
[YAW
];
182 rcCommand
[ROLL
] = constrain(roll
* cosFactor
- yaw
* sinFactor
, -500, 500);
183 rcCommand
[YAW
] = constrain(yaw
* cosFactor
+ roll
* sinFactor
, -500, 500);
186 #define THROTTLE_BUFFER_MAX 20
187 #define THROTTLE_DELTA_MS 100
189 void checkForThrottleErrorResetState(uint16_t rxRefreshRate
) {
191 static int16_t rcCommandThrottlePrevious
[THROTTLE_BUFFER_MAX
];
192 const int rxRefreshRateMs
= rxRefreshRate
/ 1000;
193 const int indexMax
= constrain(THROTTLE_DELTA_MS
/ rxRefreshRateMs
, 1, THROTTLE_BUFFER_MAX
);
194 const int16_t throttleVelocityThreshold
= (feature(FEATURE_3D
)) ? currentProfile
->pidProfile
.itermThrottleThreshold
/ 2 : currentProfile
->pidProfile
.itermThrottleThreshold
;
196 rcCommandThrottlePrevious
[index
++] = rcCommand
[THROTTLE
];
197 if (index
>= indexMax
)
200 const int16_t rcCommandSpeed
= rcCommand
[THROTTLE
] - rcCommandThrottlePrevious
[index
];
202 if(ABS(rcCommandSpeed
) > throttleVelocityThreshold
)
203 pidResetErrorGyroState();
206 void processRcCommand(void)
208 static int16_t lastCommand
[4] = { 0, 0, 0, 0 };
209 static int16_t deltaRC
[4] = { 0, 0, 0, 0 };
210 static int16_t factor
, rcInterpolationFactor
;
211 static uint16_t currentRxRefreshRate
;
212 const uint8_t interpolationChannels
= rxConfig()->rcInterpolationChannels
+ 2;
213 uint16_t rxRefreshRate
;
214 bool readyToCalculateRate
= false;
215 uint8_t readyToCalculateRateAxisCnt
= 0;
218 currentRxRefreshRate
= constrain(getTaskDeltaTime(TASK_RX
),1000,20000);
219 checkForThrottleErrorResetState(currentRxRefreshRate
);
222 if (rxConfig()->rcInterpolation
|| flightModeFlags
) {
223 // Set RC refresh rate for sampling and channels to filter
224 switch(rxConfig()->rcInterpolation
) {
225 case(RC_SMOOTHING_AUTO
):
226 rxRefreshRate
= currentRxRefreshRate
+ 1000; // Add slight overhead to prevent ramps
228 case(RC_SMOOTHING_MANUAL
):
229 rxRefreshRate
= 1000 * rxConfig()->rcInterpolationInterval
;
231 case(RC_SMOOTHING_OFF
):
232 case(RC_SMOOTHING_DEFAULT
):
234 rxRefreshRate
= rxGetRefreshRate();
238 rcInterpolationFactor
= rxRefreshRate
/ targetPidLooptime
+ 1;
240 if (debugMode
== DEBUG_RC_INTERPOLATION
) {
241 for (int axis
= 0; axis
< 2; axis
++) debug
[axis
] = rcCommand
[axis
];
242 debug
[3] = rxRefreshRate
;
245 for (int channel
=ROLL
; channel
< interpolationChannels
; channel
++) {
246 deltaRC
[channel
] = rcCommand
[channel
] - (lastCommand
[channel
] - deltaRC
[channel
] * factor
/ rcInterpolationFactor
);
247 lastCommand
[channel
] = rcCommand
[channel
];
250 factor
= rcInterpolationFactor
- 1;
255 // Interpolate steps of rcCommand
258 for (channel
=ROLL
; channel
< interpolationChannels
; channel
++)
259 rcCommand
[channel
] = lastCommand
[channel
] - deltaRC
[channel
] * factor
/rcInterpolationFactor
;
263 readyToCalculateRateAxisCnt
= MAX(channel
, FD_YAW
); // throttle channel doesn't require rate calculation
264 readyToCalculateRate
= true;
266 factor
= 0; // reset factor in case of level modes flip flopping
269 if (readyToCalculateRate
|| isRXDataNew
) {
271 readyToCalculateRateAxisCnt
= FD_YAW
;
273 // Scaling of AngleRate to camera angle (Mixing Roll and Yaw)
274 if (rxConfig()->fpvCamAngleDegrees
&& IS_RC_MODE_ACTIVE(BOXFPVANGLEMIX
) && !FLIGHT_MODE(HEADFREE_MODE
))
275 scaleRcCommandToFpvCamAngle();
277 for (int axis
= 0; axis
<= readyToCalculateRateAxisCnt
; axis
++)
278 calculateSetpointRate(axis
, rcCommand
[axis
]);
284 void updateRcCommands(void)
286 // PITCH & ROLL only dynamic PID adjustment, depending on throttle value
288 if (rcData
[THROTTLE
] < currentControlRateProfile
->tpa_breakpoint
) {
290 throttlePIDAttenuation
= 1.0f
;
292 if (rcData
[THROTTLE
] < 2000) {
293 prop
= 100 - (uint16_t)currentControlRateProfile
->dynThrPID
* (rcData
[THROTTLE
] - currentControlRateProfile
->tpa_breakpoint
) / (2000 - currentControlRateProfile
->tpa_breakpoint
);
295 prop
= 100 - currentControlRateProfile
->dynThrPID
;
297 throttlePIDAttenuation
= prop
/ 100.0f
;
300 for (int axis
= 0; axis
< 3; axis
++) {
301 // non coupled PID reduction scaler used in PID controller 1 and PID controller 2.
303 int32_t tmp
= MIN(ABS(rcData
[axis
] - rxConfig()->midrc
), 500);
304 if (axis
== ROLL
|| axis
== PITCH
) {
305 if (tmp
> rcControlsConfig()->deadband
) {
306 tmp
-= rcControlsConfig()->deadband
;
310 rcCommand
[axis
] = tmp
;
312 if (tmp
> rcControlsConfig()->yaw_deadband
) {
313 tmp
-= rcControlsConfig()->yaw_deadband
;
317 rcCommand
[axis
] = tmp
* -rcControlsConfig()->yaw_control_direction
;
319 if (rcData
[axis
] < rxConfig()->midrc
) {
320 rcCommand
[axis
] = -rcCommand
[axis
];
325 if (feature(FEATURE_3D
)) {
326 tmp
= constrain(rcData
[THROTTLE
], PWM_RANGE_MIN
, PWM_RANGE_MAX
);
327 tmp
= (uint32_t)(tmp
- PWM_RANGE_MIN
);
329 tmp
= constrain(rcData
[THROTTLE
], rxConfig()->mincheck
, PWM_RANGE_MAX
);
330 tmp
= (uint32_t)(tmp
- rxConfig()->mincheck
) * PWM_RANGE_MIN
/ (PWM_RANGE_MAX
- rxConfig()->mincheck
);
332 rcCommand
[THROTTLE
] = rcLookupThrottle(tmp
);
334 if (feature(FEATURE_3D
) && IS_RC_MODE_ACTIVE(BOX3DDISABLESWITCH
) && !failsafeIsActive()) {
335 fix12_t throttleScaler
= qConstruct(rcCommand
[THROTTLE
] - 1000, 1000);
336 rcCommand
[THROTTLE
] = rxConfig()->midrc
+ qMultiply(throttleScaler
, PWM_RANGE_MAX
- rxConfig()->midrc
);
339 if (FLIGHT_MODE(HEADFREE_MODE
)) {
340 const float radDiff
= degreesToRadians(DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
) - headFreeModeHold
);
341 const float cosDiff
= cos_approx(radDiff
);
342 const float sinDiff
= sin_approx(radDiff
);
343 const int16_t rcCommand_PITCH
= rcCommand
[PITCH
] * cosDiff
+ rcCommand
[ROLL
] * sinDiff
;
344 rcCommand
[ROLL
] = rcCommand
[ROLL
] * cosDiff
- rcCommand
[PITCH
] * sinDiff
;
345 rcCommand
[PITCH
] = rcCommand_PITCH
;
349 void updateLEDs(void)
351 if (ARMING_FLAG(ARMED
)) {
354 if (IS_RC_MODE_ACTIVE(BOXARM
) == 0 || armingCalibrationWasInitialised
) {
355 ENABLE_ARMING_FLAG(OK_TO_ARM
);
358 if (!STATE(SMALL_ANGLE
)) {
359 DISABLE_ARMING_FLAG(OK_TO_ARM
);
362 if (isCalibrating() || (averageSystemLoadPercent
> 100)) {
364 DISABLE_ARMING_FLAG(OK_TO_ARM
);
366 if (ARMING_FLAG(OK_TO_ARM
)) {
379 armingCalibrationWasInitialised
= false;
381 if (ARMING_FLAG(ARMED
)) {
382 DISABLE_ARMING_FLAG(ARMED
);
385 if (feature(FEATURE_BLACKBOX
)) {
390 beeper(BEEPER_DISARMING
); // emit disarm tone
396 static bool firstArmingCalibrationWasCompleted
;
398 if (armingConfig()->gyro_cal_on_first_arm
&& !firstArmingCalibrationWasCompleted
) {
399 gyroSetCalibrationCycles();
400 armingCalibrationWasInitialised
= true;
401 firstArmingCalibrationWasCompleted
= true;
404 if (!isGyroCalibrationComplete()) return; // prevent arming before gyro is calibrated
406 if (ARMING_FLAG(OK_TO_ARM
)) {
407 if (ARMING_FLAG(ARMED
)) {
410 if (IS_RC_MODE_ACTIVE(BOXFAILSAFE
)) {
413 if (!ARMING_FLAG(PREVENT_ARMING
)) {
414 ENABLE_ARMING_FLAG(ARMED
);
415 ENABLE_ARMING_FLAG(WAS_EVER_ARMED
);
416 headFreeModeHold
= DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
);
419 if (feature(FEATURE_BLACKBOX
)) {
420 serialPort_t
*sharedBlackboxAndMspPort
= findSharedSerialPort(FUNCTION_BLACKBOX
, FUNCTION_MSP
);
421 if (sharedBlackboxAndMspPort
) {
422 mspSerialReleasePortIfAllocated(sharedBlackboxAndMspPort
);
427 disarmAt
= millis() + armingConfig()->auto_disarm_delay
* 1000; // start disarm timeout, will be extended when throttle is nonzero
429 //beep to indicate arming
431 if (feature(FEATURE_GPS
) && STATE(GPS_FIX
) && GPS_numSat
>= 5)
432 beeper(BEEPER_ARMING_GPS_FIX
);
434 beeper(BEEPER_ARMING
);
436 beeper(BEEPER_ARMING
);
443 if (!ARMING_FLAG(ARMED
)) {
444 beeperConfirmationBeeps(1);
448 // Automatic ACC Offset Calibration
449 bool AccInflightCalibrationArmed
= false;
450 bool AccInflightCalibrationMeasurementDone
= false;
451 bool AccInflightCalibrationSavetoEEProm
= false;
452 bool AccInflightCalibrationActive
= false;
453 uint16_t InflightcalibratingA
= 0;
455 void handleInflightCalibrationStickPosition(void)
457 if (AccInflightCalibrationMeasurementDone
) {
458 // trigger saving into eeprom after landing
459 AccInflightCalibrationMeasurementDone
= false;
460 AccInflightCalibrationSavetoEEProm
= true;
462 AccInflightCalibrationArmed
= !AccInflightCalibrationArmed
;
463 if (AccInflightCalibrationArmed
) {
464 beeper(BEEPER_ACC_CALIBRATION
);
466 beeper(BEEPER_ACC_CALIBRATION_FAIL
);
471 static void updateInflightCalibrationState(void)
473 if (AccInflightCalibrationArmed
&& ARMING_FLAG(ARMED
) && rcData
[THROTTLE
] > rxConfig()->mincheck
&& !IS_RC_MODE_ACTIVE(BOXARM
)) { // Copter is airborne and you are turning it off via boxarm : start measurement
474 InflightcalibratingA
= 50;
475 AccInflightCalibrationArmed
= false;
477 if (IS_RC_MODE_ACTIVE(BOXCALIB
)) { // Use the Calib Option to activate : Calib = TRUE measurement started, Land and Calib = 0 measurement stored
478 if (!AccInflightCalibrationActive
&& !AccInflightCalibrationMeasurementDone
)
479 InflightcalibratingA
= 50;
480 AccInflightCalibrationActive
= true;
481 } else if (AccInflightCalibrationMeasurementDone
&& !ARMING_FLAG(ARMED
)) {
482 AccInflightCalibrationMeasurementDone
= false;
483 AccInflightCalibrationSavetoEEProm
= true;
487 void updateMagHold(void)
489 if (ABS(rcCommand
[YAW
]) < 15 && FLIGHT_MODE(MAG_MODE
)) {
490 int16_t dif
= DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
) - magHold
;
495 dif
*= -rcControlsConfig()->yaw_control_direction
;
496 if (STATE(SMALL_ANGLE
))
497 rcCommand
[YAW
] -= dif
* currentProfile
->pidProfile
.P8
[PIDMAG
] / 30; // 18 deg
499 magHold
= DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
);
502 void processRx(timeUs_t currentTimeUs
)
504 static bool armedBeeperOn
= false;
505 static bool airmodeIsActivated
;
507 calculateRxChannelsAndUpdateFailsafe(currentTimeUs
);
509 // in 3D mode, we need to be able to disarm by switch at any time
510 if (feature(FEATURE_3D
)) {
511 if (!IS_RC_MODE_ACTIVE(BOXARM
))
515 updateRSSI(currentTimeUs
);
517 if (feature(FEATURE_FAILSAFE
)) {
519 if (currentTimeUs
> FAILSAFE_POWER_ON_DELAY_US
&& !failsafeIsMonitoring()) {
520 failsafeStartMonitoring();
523 failsafeUpdateState();
526 throttleStatus_e throttleStatus
= calculateThrottleStatus(&masterConfig
.rxConfig
, flight3DConfig()->deadband3d_throttle
);
528 if (isAirmodeActive() && ARMING_FLAG(ARMED
)) {
529 if (rcCommand
[THROTTLE
] >= rxConfig()->airModeActivateThreshold
) airmodeIsActivated
= true; // Prevent Iterm from being reset
531 airmodeIsActivated
= false;
534 /* In airmode Iterm should be prevented to grow when Low thottle and Roll + Pitch Centered.
535 This is needed to prevent Iterm winding on the ground, but keep full stabilisation on 0 throttle while in air */
536 if (throttleStatus
== THROTTLE_LOW
&& !airmodeIsActivated
) {
537 pidResetErrorGyroState();
538 if (currentProfile
->pidProfile
.pidAtMinThrottle
)
539 pidStabilisationState(PID_STABILISATION_ON
);
541 pidStabilisationState(PID_STABILISATION_OFF
);
543 pidStabilisationState(PID_STABILISATION_ON
);
546 // When armed and motors aren't spinning, do beeps and then disarm
547 // board after delay so users without buzzer won't lose fingers.
548 // mixTable constrains motor commands, so checking throttleStatus is enough
549 if (ARMING_FLAG(ARMED
)
550 && feature(FEATURE_MOTOR_STOP
)
551 && !STATE(FIXED_WING
)
552 && !feature(FEATURE_3D
)
553 && !isAirmodeActive()
555 if (isUsingSticksForArming()) {
556 if (throttleStatus
== THROTTLE_LOW
) {
557 if (armingConfig()->auto_disarm_delay
!= 0
558 && (int32_t)(disarmAt
- millis()) < 0
560 // auto-disarm configured and delay is over
562 armedBeeperOn
= false;
564 // still armed; do warning beeps while armed
565 beeper(BEEPER_ARMED
);
566 armedBeeperOn
= true;
569 // throttle is not low
570 if (armingConfig()->auto_disarm_delay
!= 0) {
571 // extend disarm time
572 disarmAt
= millis() + armingConfig()->auto_disarm_delay
* 1000;
577 armedBeeperOn
= false;
581 // arming is via AUX switch; beep while throttle low
582 if (throttleStatus
== THROTTLE_LOW
) {
583 beeper(BEEPER_ARMED
);
584 armedBeeperOn
= true;
585 } else if (armedBeeperOn
) {
587 armedBeeperOn
= false;
592 processRcStickPositions(&masterConfig
.rxConfig
, throttleStatus
, armingConfig()->disarm_kill_switch
);
594 if (feature(FEATURE_INFLIGHT_ACC_CAL
)) {
595 updateInflightCalibrationState();
598 updateActivatedModes(modeActivationProfile()->modeActivationConditions
);
601 updateAdjustmentStates(adjustmentProfile()->adjustmentRanges
);
602 processRcAdjustments(currentControlRateProfile
, rxConfig());
605 bool canUseHorizonMode
= true;
607 if ((IS_RC_MODE_ACTIVE(BOXANGLE
) || (feature(FEATURE_FAILSAFE
) && failsafeIsActive())) && (sensors(SENSOR_ACC
))) {
608 // bumpless transfer to Level mode
609 canUseHorizonMode
= false;
611 if (!FLIGHT_MODE(ANGLE_MODE
)) {
612 ENABLE_FLIGHT_MODE(ANGLE_MODE
);
615 DISABLE_FLIGHT_MODE(ANGLE_MODE
); // failsafe support
618 if (IS_RC_MODE_ACTIVE(BOXHORIZON
) && canUseHorizonMode
) {
620 DISABLE_FLIGHT_MODE(ANGLE_MODE
);
622 if (!FLIGHT_MODE(HORIZON_MODE
)) {
623 ENABLE_FLIGHT_MODE(HORIZON_MODE
);
626 DISABLE_FLIGHT_MODE(HORIZON_MODE
);
629 if (FLIGHT_MODE(ANGLE_MODE
) || FLIGHT_MODE(HORIZON_MODE
)) {
636 if (sensors(SENSOR_ACC
) || sensors(SENSOR_MAG
)) {
637 if (IS_RC_MODE_ACTIVE(BOXMAG
)) {
638 if (!FLIGHT_MODE(MAG_MODE
)) {
639 ENABLE_FLIGHT_MODE(MAG_MODE
);
640 magHold
= DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
);
643 DISABLE_FLIGHT_MODE(MAG_MODE
);
645 if (IS_RC_MODE_ACTIVE(BOXHEADFREE
)) {
646 if (!FLIGHT_MODE(HEADFREE_MODE
)) {
647 ENABLE_FLIGHT_MODE(HEADFREE_MODE
);
650 DISABLE_FLIGHT_MODE(HEADFREE_MODE
);
652 if (IS_RC_MODE_ACTIVE(BOXHEADADJ
)) {
653 headFreeModeHold
= DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
); // acquire new heading
659 if (sensors(SENSOR_GPS
)) {
660 updateGpsWaypointsAndMode();
664 if (IS_RC_MODE_ACTIVE(BOXPASSTHRU
)) {
665 ENABLE_FLIGHT_MODE(PASSTHRU_MODE
);
667 DISABLE_FLIGHT_MODE(PASSTHRU_MODE
);
670 if (mixerConfig()->mixerMode
== MIXER_FLYING_WING
|| mixerConfig()->mixerMode
== MIXER_AIRPLANE
) {
671 DISABLE_FLIGHT_MODE(HEADFREE_MODE
);
675 if (feature(FEATURE_TELEMETRY
)) {
676 if ((!telemetryConfig()->telemetry_switch
&& ARMING_FLAG(ARMED
)) ||
677 (telemetryConfig()->telemetry_switch
&& IS_RC_MODE_ACTIVE(BOXTELEMETRY
))) {
679 releaseSharedTelemetryPorts();
681 // the telemetry state must be checked immediately so that shared serial ports are released.
682 telemetryCheckState();
683 mspSerialAllocatePorts();
689 vtxUpdateActivatedChannel();
693 static void subTaskPidController(void)
696 if (debugMode
== DEBUG_PIDLOOP
) {startTime
= micros();}
697 // PID - note this is function pointer set by setPIDController()
698 pidController(¤tProfile
->pidProfile
, &accelerometerConfig()->accelerometerTrims
, throttlePIDAttenuation
);
699 DEBUG_SET(DEBUG_PIDLOOP
, 1, micros() - startTime
);
702 static void subTaskMainSubprocesses(timeUs_t currentTimeUs
)
705 if (debugMode
== DEBUG_PIDLOOP
) {startTime
= micros();}
707 // Read out gyro temperature if used for telemmetry
708 if (feature(FEATURE_TELEMETRY
) && gyro
.dev
.temperature
) {
709 gyro
.dev
.temperature(&gyro
.dev
, &telemTemperature1
);
713 if (sensors(SENSOR_MAG
)) {
718 #if defined(BARO) || defined(SONAR)
719 // updateRcCommands sets rcCommand, which is needed by updateAltHoldState and updateSonarAltHoldState
721 if (sensors(SENSOR_BARO
) || sensors(SENSOR_SONAR
)) {
722 if (FLIGHT_MODE(BARO_MODE
) || FLIGHT_MODE(SONAR_MODE
)) {
723 applyAltHold(&masterConfig
.airplaneConfig
);
728 // If we're armed, at minimum throttle, and we do arming via the
729 // sticks, do not process yaw input from the rx. We do this so the
730 // motors do not spin up while we are trying to arm or disarm.
731 // Allow yaw control for tricopters if the user wants the servo to move even when unarmed.
732 if (isUsingSticksForArming() && rcData
[THROTTLE
] <= rxConfig()->mincheck
733 #ifndef USE_QUAD_MIXER_ONLY
735 && !((mixerConfig()->mixerMode
== MIXER_TRI
|| mixerConfig()->mixerMode
== MIXER_CUSTOM_TRI
) && servoMixerConfig()->tri_unarmed_servo
)
737 && mixerConfig()->mixerMode
!= MIXER_AIRPLANE
738 && mixerConfig()->mixerMode
!= MIXER_FLYING_WING
742 setpointRate
[YAW
] = 0;
745 if (throttleCorrectionConfig()->throttle_correction_value
&& (FLIGHT_MODE(ANGLE_MODE
) || FLIGHT_MODE(HORIZON_MODE
))) {
746 rcCommand
[THROTTLE
] += calculateThrottleAngleCorrection(throttleCorrectionConfig()->throttle_correction_value
);
752 if (sensors(SENSOR_GPS
)) {
753 if ((FLIGHT_MODE(GPS_HOME_MODE
) || FLIGHT_MODE(GPS_HOLD_MODE
)) && STATE(GPS_FIX_HOME
)) {
754 updateGpsStateForHomeAndHoldMode();
764 if (!cliMode
&& feature(FEATURE_BLACKBOX
)) {
765 handleBlackbox(currentTimeUs
);
770 transponderUpdate(currentTimeUs
);
772 DEBUG_SET(DEBUG_PIDLOOP
, 2, micros() - startTime
);
775 static void subTaskMotorUpdate(void)
778 if (debugMode
== DEBUG_CYCLETIME
) {
779 startTime
= micros();
780 static uint32_t previousMotorUpdateTime
;
781 const uint32_t currentDeltaTime
= startTime
- previousMotorUpdateTime
;
782 debug
[2] = currentDeltaTime
;
783 debug
[3] = currentDeltaTime
- targetPidLooptime
;
784 previousMotorUpdateTime
= startTime
;
785 } else if (debugMode
== DEBUG_PIDLOOP
) {
786 startTime
= micros();
789 mixTable(¤tProfile
->pidProfile
);
792 // motor outputs are used as sources for servo mixing, so motors must be calculated using mixTable() before servos.
793 if (isMixerUsingServos()) {
800 if (motorControlEnable
) {
803 DEBUG_SET(DEBUG_PIDLOOP
, 3, micros() - startTime
);
806 uint8_t setPidUpdateCountDown(void)
808 if (gyroConfig()->gyro_soft_lpf_hz
) {
809 return pidConfig()->pid_process_denom
- 1;
815 // Function for loop trigger
816 void taskMainPidLoop(timeUs_t currentTimeUs
)
818 static bool runTaskMainSubprocesses
;
819 static uint8_t pidUpdateCountdown
;
821 if (debugMode
== DEBUG_CYCLETIME
) {
822 debug
[0] = getTaskDeltaTime(TASK_SELF
);
823 debug
[1] = averageSystemLoadPercent
;
826 if (runTaskMainSubprocesses
) {
827 subTaskMainSubprocesses(currentTimeUs
);
828 runTaskMainSubprocesses
= false;
831 // DEBUG_PIDLOOP, timings for:
833 // 1 - pidController()
834 // 2 - subTaskMainSubprocesses()
835 // 3 - subTaskMotorUpdate()
837 if (debugMode
== DEBUG_PIDLOOP
) {startTime
= micros();}
839 DEBUG_SET(DEBUG_PIDLOOP
, 0, micros() - startTime
);
841 if (pidUpdateCountdown
) {
842 pidUpdateCountdown
--;
844 pidUpdateCountdown
= setPidUpdateCountDown();
845 subTaskPidController();
846 subTaskMotorUpdate();
847 runTaskMainSubprocesses
= true;