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/>.
24 #include "scheduler.h"
27 #include "common/maths.h"
28 #include "common/axis.h"
29 #include "common/color.h"
30 #include "common/utils.h"
31 #include "common/filter.h"
33 #include "drivers/sensor.h"
34 #include "drivers/accgyro.h"
35 #include "drivers/compass.h"
36 #include "drivers/light_led.h"
38 #include "drivers/gpio.h"
39 #include "drivers/system.h"
40 #include "drivers/serial.h"
41 #include "drivers/timer.h"
42 #include "drivers/pwm_rx.h"
43 #include "drivers/gyro_sync.h"
45 #include "sensors/sensors.h"
46 #include "sensors/boardalignment.h"
47 #include "sensors/sonar.h"
48 #include "sensors/compass.h"
49 #include "sensors/acceleration.h"
50 #include "sensors/barometer.h"
51 #include "sensors/gyro.h"
52 #include "sensors/battery.h"
54 #include "io/beeper.h"
55 #include "io/display.h"
56 #include "io/escservo.h"
57 #include "io/rc_controls.h"
58 #include "io/rc_curves.h"
59 #include "io/gimbal.h"
61 #include "io/ledstrip.h"
62 #include "io/serial.h"
63 #include "io/serial_cli.h"
64 #include "io/serial_msp.h"
65 #include "io/statusindicator.h"
66 #include "io/asyncfatfs/asyncfatfs.h"
67 #include "io/transponder_ir.h"
73 #include "telemetry/telemetry.h"
74 #include "blackbox/blackbox.h"
76 #include "flight/mixer.h"
77 #include "flight/pid.h"
78 #include "flight/imu.h"
79 #include "flight/altitudehold.h"
80 #include "flight/failsafe.h"
81 #include "flight/navigation.h"
83 #include "config/runtime_config.h"
84 #include "config/config.h"
85 #include "config/config_profile.h"
86 #include "config/config_master.h"
96 //#define JITTER_DEBUG 0 // Specify debug value for jitter debug
98 /* VBAT monitoring interval (in microseconds) - 1s*/
99 #define VBATINTERVAL (6 * 3500)
100 /* IBat monitoring interval (in microseconds) - 6 default looptimes */
101 #define IBATINTERVAL (6 * 3500)
103 #define GYRO_WATCHDOG_DELAY 80 // delay for gyro sync
104 #define JITTER_BUFFER_TIME 20 // cycleTime jitter buffer time
106 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
111 int16_t headFreeModeHold
;
113 uint8_t motorControlEnable
= false;
115 int16_t telemTemperature1
; // gyro sensor temperature
116 static uint32_t disarmAt
; // Time of automatic disarm when "Don't spin the motors when armed" is enabled and auto_disarm_delay is nonzero
118 extern uint32_t currentTime
;
119 extern uint8_t dynP8
[3], dynI8
[3], dynD8
[3], PIDweight
[3];
120 extern bool antiWindupProtection
;
122 uint16_t filteredCycleTime
;
123 static bool isRXDataNew
;
125 typedef void (*pidControllerFuncPtr
)(pidProfile_t
*pidProfile
, controlRateConfig_t
*controlRateConfig
,
126 uint16_t max_angle_inclination
, rollAndPitchTrims_t
*angleTrim
, rxConfig_t
*rxConfig
); // pid controller function prototype
128 extern pidControllerFuncPtr pid_controller
;
130 void applyAndSaveAccelerometerTrimsDelta(rollAndPitchTrims_t
*rollAndPitchTrimsDelta
)
132 masterConfig
.accelerometerTrims
.values
.roll
+= rollAndPitchTrimsDelta
->values
.roll
;
133 masterConfig
.accelerometerTrims
.values
.pitch
+= rollAndPitchTrimsDelta
->values
.pitch
;
135 saveConfigAndNotify();
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());
152 static int16_t lastCommand
[4] = { 0, 0, 0, 0 };
153 static int16_t deltaRC
[4] = { 0, 0, 0, 0 };
154 static int16_t factor
, rcInterpolationFactor
;
155 uint16_t rxRefreshRate
;
157 // Set RC refresh rate for sampling and channels to filter
158 initRxRefreshRate(&rxRefreshRate
);
160 rcInterpolationFactor
= rxRefreshRate
/ targetLooptime
+ 1;
163 for (int channel
=0; channel
< 4; channel
++) {
164 deltaRC
[channel
] = rcCommand
[channel
] - (lastCommand
[channel
] - deltaRC
[channel
] * factor
/ rcInterpolationFactor
);
165 lastCommand
[channel
] = rcCommand
[channel
];
169 factor
= rcInterpolationFactor
- 1;
174 // Interpolate steps of rcCommand
176 for (int channel
=0; channel
< 4; channel
++) {
177 rcCommand
[channel
] = lastCommand
[channel
] - deltaRC
[channel
] * factor
/rcInterpolationFactor
;
184 void scaleRcCommandToFpvCamAngle(void) {
185 int16_t roll
= rcCommand
[ROLL
];
186 int16_t yaw
= rcCommand
[YAW
];
187 rcCommand
[ROLL
] = constrain(cos(masterConfig
.rxConfig
.fpvCamAngleDegrees
*RAD
) * roll
- sin(masterConfig
.rxConfig
.fpvCamAngleDegrees
*RAD
) * yaw
, -500, 500);
188 rcCommand
[YAW
] = constrain(cos(masterConfig
.rxConfig
.fpvCamAngleDegrees
*RAD
) * yaw
+ sin(masterConfig
.rxConfig
.fpvCamAngleDegrees
*RAD
) * roll
, -500, 500);
194 int32_t axis
, prop1
= 0, prop2
;
196 // PITCH & ROLL only dynamic PID adjustment, depending on throttle value
197 if (rcData
[THROTTLE
] < currentControlRateProfile
->tpa_breakpoint
) {
200 if (rcData
[THROTTLE
] < 2000) {
201 prop2
= 100 - (uint16_t)currentControlRateProfile
->dynThrPID
* (rcData
[THROTTLE
] - currentControlRateProfile
->tpa_breakpoint
) / (2000 - currentControlRateProfile
->tpa_breakpoint
);
203 prop2
= 100 - currentControlRateProfile
->dynThrPID
;
207 for (axis
= 0; axis
< 3; axis
++) {
208 tmp
= MIN(ABS(rcData
[axis
] - masterConfig
.rxConfig
.midrc
), 500);
209 if (axis
== ROLL
|| axis
== PITCH
) {
210 if (masterConfig
.rcControlsConfig
.deadband
) {
211 if (tmp
> masterConfig
.rcControlsConfig
.deadband
) {
212 tmp
-= masterConfig
.rcControlsConfig
.deadband
;
219 rcCommand
[axis
] = lookupPitchRollRC
[tmp2
] + (tmp
- tmp2
* 100) * (lookupPitchRollRC
[tmp2
+ 1] - lookupPitchRollRC
[tmp2
]) / 100;
220 prop1
= 100 - (uint16_t)currentControlRateProfile
->rates
[axis
] * tmp
/ 500;
221 prop1
= (uint16_t)prop1
* prop2
/ 100;
222 } else if (axis
== YAW
) {
223 if (masterConfig
.rcControlsConfig
.yaw_deadband
) {
224 if (tmp
> masterConfig
.rcControlsConfig
.yaw_deadband
) {
225 tmp
-= masterConfig
.rcControlsConfig
.yaw_deadband
;
231 rcCommand
[axis
] = (lookupYawRC
[tmp2
] + (tmp
- tmp2
* 100) * (lookupYawRC
[tmp2
+ 1] - lookupYawRC
[tmp2
]) / 100) * -masterConfig
.yaw_control_direction
;
232 prop1
= 100 - (uint16_t)currentControlRateProfile
->rates
[axis
] * ABS(tmp
) / 500;
234 // FIXME axis indexes into pids. use something like lookupPidIndex(rc_alias_e alias) to reduce coupling.
235 dynP8
[axis
] = (uint16_t)currentProfile
->pidProfile
.P8
[axis
] * prop1
/ 100;
236 dynI8
[axis
] = (uint16_t)currentProfile
->pidProfile
.I8
[axis
] * prop1
/ 100;
237 dynD8
[axis
] = (uint16_t)currentProfile
->pidProfile
.D8
[axis
] * prop1
/ 100;
239 // non coupled PID reduction scaler used in PID controller 1 and PID controller 2. YAW TPA disabled. 100 means 100% of the pids
241 PIDweight
[axis
] = 100;
244 PIDweight
[axis
] = prop2
;
247 if (rcData
[axis
] < masterConfig
.rxConfig
.midrc
)
248 rcCommand
[axis
] = -rcCommand
[axis
];
251 tmp
= constrain(rcData
[THROTTLE
], masterConfig
.rxConfig
.mincheck
, PWM_RANGE_MAX
);
252 tmp
= (uint32_t)(tmp
- masterConfig
.rxConfig
.mincheck
) * PWM_RANGE_MIN
/ (PWM_RANGE_MAX
- masterConfig
.rxConfig
.mincheck
); // [MINCHECK;2000] -> [0;1000]
254 rcCommand
[THROTTLE
] = lookupThrottleRC
[tmp2
] + (tmp
- tmp2
* 100) * (lookupThrottleRC
[tmp2
+ 1] - lookupThrottleRC
[tmp2
]) / 100; // [0;1000] -> expo -> [MINTHROTTLE;MAXTHROTTLE]
256 if (FLIGHT_MODE(HEADFREE_MODE
)) {
257 float radDiff
= degreesToRadians(DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
) - headFreeModeHold
);
258 float cosDiff
= cos_approx(radDiff
);
259 float sinDiff
= sin_approx(radDiff
);
260 int16_t rcCommand_PITCH
= rcCommand
[PITCH
] * cosDiff
+ rcCommand
[ROLL
] * sinDiff
;
261 rcCommand
[ROLL
] = rcCommand
[ROLL
] * cosDiff
- rcCommand
[PITCH
] * sinDiff
;
262 rcCommand
[PITCH
] = rcCommand_PITCH
;
265 if (masterConfig
.rxConfig
.rcSmoothing
|| flightModeFlags
) {
269 // experimental scaling of RC command to FPV cam angle
270 if (masterConfig
.rxConfig
.fpvCamAngleDegrees
&& !FLIGHT_MODE(HEADFREE_MODE
)) {
271 scaleRcCommandToFpvCamAngle();
275 if (ARMING_FLAG(ARMED
)) {
278 if (IS_RC_MODE_ACTIVE(BOXARM
) == 0) {
279 ENABLE_ARMING_FLAG(OK_TO_ARM
);
282 if (!STATE(SMALL_ANGLE
)) {
283 DISABLE_ARMING_FLAG(OK_TO_ARM
);
286 if (isCalibrating() || (averageSystemLoadPercent
> 100)) {
288 DISABLE_ARMING_FLAG(OK_TO_ARM
);
290 if (ARMING_FLAG(OK_TO_ARM
)) {
300 // Read out gyro temperature. can use it for something somewhere. maybe get MCU temperature instead? lots of fun possibilities.
301 if (gyro
.temperature
)
302 gyro
.temperature(&telemTemperature1
);
307 if (ARMING_FLAG(ARMED
)) {
308 DISABLE_ARMING_FLAG(ARMED
);
311 if (feature(FEATURE_BLACKBOX
)) {
316 beeper(BEEPER_DISARMING
); // emit disarm tone
320 #define TELEMETRY_FUNCTION_MASK (FUNCTION_TELEMETRY_FRSKY | FUNCTION_TELEMETRY_HOTT | FUNCTION_TELEMETRY_LTM | FUNCTION_TELEMETRY_SMARTPORT)
322 void releaseSharedTelemetryPorts(void) {
323 serialPort_t
*sharedPort
= findSharedSerialPort(TELEMETRY_FUNCTION_MASK
, FUNCTION_MSP
);
325 mspReleasePortIfAllocated(sharedPort
);
326 sharedPort
= findNextSharedSerialPort(TELEMETRY_FUNCTION_MASK
, FUNCTION_MSP
);
332 if (ARMING_FLAG(OK_TO_ARM
)) {
333 if (ARMING_FLAG(ARMED
)) {
336 if (IS_RC_MODE_ACTIVE(BOXFAILSAFE
)) {
339 if (!ARMING_FLAG(PREVENT_ARMING
)) {
340 ENABLE_ARMING_FLAG(ARMED
);
341 headFreeModeHold
= DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
);
344 if (feature(FEATURE_BLACKBOX
)) {
345 serialPort_t
*sharedBlackboxAndMspPort
= findSharedSerialPort(FUNCTION_BLACKBOX
, FUNCTION_MSP
);
346 if (sharedBlackboxAndMspPort
) {
347 mspReleasePortIfAllocated(sharedBlackboxAndMspPort
);
352 disarmAt
= millis() + masterConfig
.auto_disarm_delay
* 1000; // start disarm timeout, will be extended when throttle is nonzero
354 //beep to indicate arming
356 if (feature(FEATURE_GPS
) && STATE(GPS_FIX
) && GPS_numSat
>= 5)
357 beeper(BEEPER_ARMING_GPS_FIX
);
359 beeper(BEEPER_ARMING
);
361 beeper(BEEPER_ARMING
);
368 if (!ARMING_FLAG(ARMED
)) {
369 beeperConfirmationBeeps(1);
373 // Automatic ACC Offset Calibration
374 bool AccInflightCalibrationArmed
= false;
375 bool AccInflightCalibrationMeasurementDone
= false;
376 bool AccInflightCalibrationSavetoEEProm
= false;
377 bool AccInflightCalibrationActive
= false;
378 uint16_t InflightcalibratingA
= 0;
380 void handleInflightCalibrationStickPosition(void)
382 if (AccInflightCalibrationMeasurementDone
) {
383 // trigger saving into eeprom after landing
384 AccInflightCalibrationMeasurementDone
= false;
385 AccInflightCalibrationSavetoEEProm
= true;
387 AccInflightCalibrationArmed
= !AccInflightCalibrationArmed
;
388 if (AccInflightCalibrationArmed
) {
389 beeper(BEEPER_ACC_CALIBRATION
);
391 beeper(BEEPER_ACC_CALIBRATION_FAIL
);
396 void updateInflightCalibrationState(void)
398 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
399 InflightcalibratingA
= 50;
400 AccInflightCalibrationArmed
= false;
402 if (IS_RC_MODE_ACTIVE(BOXCALIB
)) { // Use the Calib Option to activate : Calib = TRUE measurement started, Land and Calib = 0 measurement stored
403 if (!AccInflightCalibrationActive
&& !AccInflightCalibrationMeasurementDone
)
404 InflightcalibratingA
= 50;
405 AccInflightCalibrationActive
= true;
406 } else if (AccInflightCalibrationMeasurementDone
&& !ARMING_FLAG(ARMED
)) {
407 AccInflightCalibrationMeasurementDone
= false;
408 AccInflightCalibrationSavetoEEProm
= true;
412 void updateMagHold(void)
414 if (ABS(rcCommand
[YAW
]) < 15 && FLIGHT_MODE(MAG_MODE
)) {
415 int16_t dif
= DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
) - magHold
;
420 dif
*= -masterConfig
.yaw_control_direction
;
421 if (STATE(SMALL_ANGLE
))
422 rcCommand
[YAW
] -= dif
* currentProfile
->pidProfile
.P8
[PIDMAG
] / 30; // 18 deg
424 magHold
= DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
);
429 static bool armedBeeperOn
= false;
431 calculateRxChannelsAndUpdateFailsafe(currentTime
);
433 // in 3D mode, we need to be able to disarm by switch at any time
434 if (feature(FEATURE_3D
)) {
435 if (!IS_RC_MODE_ACTIVE(BOXARM
))
439 updateRSSI(currentTime
);
441 if (feature(FEATURE_FAILSAFE
)) {
443 if (currentTime
> FAILSAFE_POWER_ON_DELAY_US
&& !failsafeIsMonitoring()) {
444 failsafeStartMonitoring();
447 failsafeUpdateState();
450 throttleStatus_e throttleStatus
= calculateThrottleStatus(&masterConfig
.rxConfig
, masterConfig
.flight3DConfig
.deadband3d_throttle
);
451 rollPitchStatus_e rollPitchStatus
= calculateRollPitchCenterStatus(&masterConfig
.rxConfig
);
453 /* In airmode Iterm should be prevented to grow when Low thottle and Roll + Pitch Centered.
454 This is needed to prevent Iterm winding on the ground, but keep full stabilisation on 0 throttle while in air */
455 if (throttleStatus
== THROTTLE_LOW
) {
456 if (IS_RC_MODE_ACTIVE(BOXAIRMODE
) && !failsafeIsActive() && ARMING_FLAG(ARMED
)) {
457 if (rollPitchStatus
== CENTERED
) {
458 antiWindupProtection
= true;
460 antiWindupProtection
= false;
463 if (IS_RC_MODE_ACTIVE(BOXAIRMODE
)) {
464 pidResetErrorGyroState(RESET_ITERM
);
466 pidResetErrorGyroState(RESET_ITERM_AND_REDUCE_PID
);
468 pidResetErrorAngle();
471 pidResetErrorGyroState(RESET_DISABLE
);
472 antiWindupProtection
= false;
475 // When armed and motors aren't spinning, do beeps and then disarm
476 // board after delay so users without buzzer won't lose fingers.
477 // mixTable constrains motor commands, so checking throttleStatus is enough
478 if (ARMING_FLAG(ARMED
)
479 && feature(FEATURE_MOTOR_STOP
)
480 && !STATE(FIXED_WING
)
482 if (isUsingSticksForArming()) {
483 if (throttleStatus
== THROTTLE_LOW
) {
484 if (masterConfig
.auto_disarm_delay
!= 0
485 && (int32_t)(disarmAt
- millis()) < 0
487 // auto-disarm configured and delay is over
489 armedBeeperOn
= false;
491 // still armed; do warning beeps while armed
492 beeper(BEEPER_ARMED
);
493 armedBeeperOn
= true;
496 // throttle is not low
497 if (masterConfig
.auto_disarm_delay
!= 0) {
498 // extend disarm time
499 disarmAt
= millis() + masterConfig
.auto_disarm_delay
* 1000;
504 armedBeeperOn
= false;
508 // arming is via AUX switch; beep while throttle low
509 if (throttleStatus
== THROTTLE_LOW
) {
510 beeper(BEEPER_ARMED
);
511 armedBeeperOn
= true;
512 } else if (armedBeeperOn
) {
514 armedBeeperOn
= false;
519 processRcStickPositions(&masterConfig
.rxConfig
, throttleStatus
, masterConfig
.retarded_arm
, masterConfig
.disarm_kill_switch
);
521 if (feature(FEATURE_INFLIGHT_ACC_CAL
)) {
522 updateInflightCalibrationState();
525 updateActivatedModes(masterConfig
.modeActivationConditions
);
528 updateAdjustmentStates(masterConfig
.adjustmentRanges
);
529 processRcAdjustments(currentControlRateProfile
, &masterConfig
.rxConfig
);
532 bool canUseHorizonMode
= true;
534 if ((IS_RC_MODE_ACTIVE(BOXANGLE
) || (feature(FEATURE_FAILSAFE
) && failsafeIsActive())) && (sensors(SENSOR_ACC
))) {
535 // bumpless transfer to Level mode
536 canUseHorizonMode
= false;
538 if (!FLIGHT_MODE(ANGLE_MODE
)) {
539 ENABLE_FLIGHT_MODE(ANGLE_MODE
);
542 DISABLE_FLIGHT_MODE(ANGLE_MODE
); // failsafe support
545 if (IS_RC_MODE_ACTIVE(BOXHORIZON
) && canUseHorizonMode
) {
547 DISABLE_FLIGHT_MODE(ANGLE_MODE
);
549 if (!FLIGHT_MODE(HORIZON_MODE
)) {
550 ENABLE_FLIGHT_MODE(HORIZON_MODE
);
553 DISABLE_FLIGHT_MODE(HORIZON_MODE
);
556 if (FLIGHT_MODE(ANGLE_MODE
) || FLIGHT_MODE(HORIZON_MODE
)) {
563 if (sensors(SENSOR_ACC
) || sensors(SENSOR_MAG
)) {
564 if (IS_RC_MODE_ACTIVE(BOXMAG
)) {
565 if (!FLIGHT_MODE(MAG_MODE
)) {
566 ENABLE_FLIGHT_MODE(MAG_MODE
);
567 magHold
= DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
);
570 DISABLE_FLIGHT_MODE(MAG_MODE
);
572 if (IS_RC_MODE_ACTIVE(BOXHEADFREE
)) {
573 if (!FLIGHT_MODE(HEADFREE_MODE
)) {
574 ENABLE_FLIGHT_MODE(HEADFREE_MODE
);
577 DISABLE_FLIGHT_MODE(HEADFREE_MODE
);
579 if (IS_RC_MODE_ACTIVE(BOXHEADADJ
)) {
580 headFreeModeHold
= DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
); // acquire new heading
586 if (sensors(SENSOR_GPS
)) {
587 updateGpsWaypointsAndMode();
591 if (IS_RC_MODE_ACTIVE(BOXPASSTHRU
)) {
592 ENABLE_FLIGHT_MODE(PASSTHRU_MODE
);
594 DISABLE_FLIGHT_MODE(PASSTHRU_MODE
);
597 if (masterConfig
.mixerMode
== MIXER_FLYING_WING
|| masterConfig
.mixerMode
== MIXER_AIRPLANE
) {
598 DISABLE_FLIGHT_MODE(HEADFREE_MODE
);
602 if (feature(FEATURE_TELEMETRY
)) {
603 if ((!masterConfig
.telemetryConfig
.telemetry_switch
&& ARMING_FLAG(ARMED
)) ||
604 (masterConfig
.telemetryConfig
.telemetry_switch
&& IS_RC_MODE_ACTIVE(BOXTELEMETRY
))) {
606 releaseSharedTelemetryPorts();
608 // the telemetry state must be checked immediately so that shared serial ports are released.
609 telemetryCheckState();
610 mspAllocateSerialPorts(&masterConfig
.serialConfig
);
617 #if defined(BARO) || defined(SONAR)
618 static bool haveProcessedAnnexCodeOnce
= false;
621 void taskMainPidLoop(void)
623 imuUpdateGyroAndAttitude();
627 #if defined(BARO) || defined(SONAR)
628 haveProcessedAnnexCodeOnce
= true;
632 if (sensors(SENSOR_MAG
)) {
637 #if defined(BARO) || defined(SONAR)
638 if (sensors(SENSOR_BARO
) || sensors(SENSOR_SONAR
)) {
639 if (FLIGHT_MODE(BARO_MODE
) || FLIGHT_MODE(SONAR_MODE
)) {
640 applyAltHold(&masterConfig
.airplaneConfig
);
645 // If we're armed, at minimum throttle, and we do arming via the
646 // sticks, do not process yaw input from the rx. We do this so the
647 // motors do not spin up while we are trying to arm or disarm.
648 // Allow yaw control for tricopters if the user wants the servo to move even when unarmed.
649 if (isUsingSticksForArming() && rcData
[THROTTLE
] <= masterConfig
.rxConfig
.mincheck
650 #ifndef USE_QUAD_MIXER_ONLY
652 && !((masterConfig
.mixerMode
== MIXER_TRI
|| masterConfig
.mixerMode
== MIXER_CUSTOM_TRI
) && masterConfig
.mixerConfig
.tri_unarmed_servo
)
654 && masterConfig
.mixerMode
!= MIXER_AIRPLANE
655 && masterConfig
.mixerMode
!= MIXER_FLYING_WING
661 if (masterConfig
.throttle_correction_value
&& (FLIGHT_MODE(ANGLE_MODE
) || FLIGHT_MODE(HORIZON_MODE
))) {
662 rcCommand
[THROTTLE
] += calculateThrottleAngleCorrection(masterConfig
.throttle_correction_value
);
666 if (sensors(SENSOR_GPS
)) {
667 if ((FLIGHT_MODE(GPS_HOME_MODE
) || FLIGHT_MODE(GPS_HOLD_MODE
)) && STATE(GPS_FIX_HOME
)) {
668 updateGpsStateForHomeAndHoldMode();
673 // PID - note this is function pointer set by setPIDController()
675 ¤tProfile
->pidProfile
,
676 currentControlRateProfile
,
677 masterConfig
.max_angle_inclination
,
678 &masterConfig
.accelerometerTrims
,
679 &masterConfig
.rxConfig
689 if (motorControlEnable
) {
698 if (!cliMode
&& feature(FEATURE_BLACKBOX
)) {
708 // Function for loop trigger
709 void taskMainPidLoopCheck(void) {
710 static uint32_t previousTime
;
712 cycleTime
= micros() - previousTime
;
713 previousTime
= micros();
715 // Debugging parameters
716 debug
[0] = cycleTime
;
717 debug
[1] = cycleTime
- targetLooptime
;
718 debug
[2] = averageSystemLoadPercent
;
721 if (gyroSyncCheckUpdate() || ((cycleTime
+ (micros() - previousTime
)) >= (targetLooptime
+ GYRO_WATCHDOG_DELAY
))) {
723 if (micros() >= JITTER_BUFFER_TIME
+ previousTime
) break;
732 void taskUpdateAccelerometer(void)
734 imuUpdateAccelerometer(&masterConfig
.accelerometerTrims
);
737 void taskHandleSerial(void)
742 void taskUpdateBeeper(void)
744 beeperUpdate(); //call periodic beeper handler
747 void taskUpdateBattery(void)
749 static uint32_t vbatLastServiced
= 0;
750 static uint32_t ibatLastServiced
= 0;
752 if (feature(FEATURE_VBAT
)) {
753 if (cmp32(currentTime
, vbatLastServiced
) >= VBATINTERVAL
) {
754 vbatLastServiced
= currentTime
;
759 if (feature(FEATURE_CURRENT_METER
)) {
760 int32_t ibatTimeSinceLastServiced
= cmp32(currentTime
, ibatLastServiced
);
762 if (ibatTimeSinceLastServiced
>= IBATINTERVAL
) {
763 ibatLastServiced
= currentTime
;
764 updateCurrentMeter(ibatTimeSinceLastServiced
, &masterConfig
.rxConfig
, masterConfig
.flight3DConfig
.deadband3d_throttle
);
769 bool taskUpdateRxCheck(void)
771 updateRx(currentTime
);
772 return shouldProcessRx(currentTime
);
775 void taskUpdateRxMain(void)
781 // the 'annexCode' initialses rcCommand, updateAltHoldState depends on valid rcCommand data.
782 if (haveProcessedAnnexCodeOnce
) {
783 if (sensors(SENSOR_BARO
)) {
784 updateAltHoldState();
790 // the 'annexCode' initialses rcCommand, updateAltHoldState depends on valid rcCommand data.
791 if (haveProcessedAnnexCodeOnce
) {
792 if (sensors(SENSOR_SONAR
)) {
793 updateSonarAltHoldState();
800 void taskProcessGPS(void)
802 // if GPS feature is enabled, gpsThread() will be called at some intervals to check for stuck
803 // hardware, wrong baud rates, init GPS if needed, etc. Don't use SENSOR_GPS here as gpsThread() can and will
804 // change this based on available hardware
805 if (feature(FEATURE_GPS
)) {
809 if (sensors(SENSOR_GPS
)) {
810 updateGpsIndicator(currentTime
);
816 void taskUpdateCompass(void)
818 if (sensors(SENSOR_MAG
)) {
819 updateCompass(&masterConfig
.magZero
);
825 void taskUpdateBaro(void)
827 if (sensors(SENSOR_BARO
)) {
828 uint32_t newDeadline
= baroUpdate();
829 rescheduleTask(TASK_SELF
, newDeadline
);
835 void taskUpdateSonar(void)
837 if (sensors(SENSOR_SONAR
)) {
843 #if defined(BARO) || defined(SONAR)
844 void taskCalculateAltitude(void)
848 || (sensors(SENSOR_BARO
) && isBaroReady())
851 || sensors(SENSOR_SONAR
)
854 calculateEstimatedAltitude(currentTime
);
859 void taskUpdateDisplay(void)
861 if (feature(FEATURE_DISPLAY
)) {
868 void taskTelemetry(void)
870 telemetryCheckState();
872 if (!cliMode
&& feature(FEATURE_TELEMETRY
)) {
873 telemetryProcess(&masterConfig
.rxConfig
, masterConfig
.flight3DConfig
.deadband3d_throttle
);
879 void taskLedStrip(void)
881 if (feature(FEATURE_LED_STRIP
)) {