// Receive ISR callback static void sumdDataReceive(uint16_t c) { timeUs_t sumdTime; static timeUs_t sumdTimeLast; static uint8_t sumdIndex; sumdTime = micros(); if (cmpTimeUs(sumdTime, sumdTimeLast) > 4000) sumdIndex = 0; sumdTimeLast = sumdTime; if (sumdIndex == 0) { if (c != SUMD_SYNCBYTE) return; else { sumdFrameDone = false; // lazy main loop didnt fetch the stuff crc = 0; } } if (sumdIndex == 2) sumdChannelCount = (uint8_t)c; if (sumdIndex < SUMD_BUFFSIZE) sumd[sumdIndex] = (uint8_t)c; sumdIndex++; if (sumdIndex < sumdChannelCount * 2 + 4) CRC16((uint8_t)c); else if (sumdIndex == sumdChannelCount * 2 + 5) { sumdIndex = 0; sumdFrameDone = true; } }
static FAST_CODE_NOINLINE void handleYawSpin(gyroSensor_t *gyroSensor, timeUs_t currentTimeUs) { const float yawSpinResetRate = gyroConfig()->yaw_spin_threshold - 100.0f; if (abs(gyroSensor->gyroDev.gyroADCf[Z]) < yawSpinResetRate) { // testing whether 20ms of consecutive OK gyro yaw values is enough if (cmpTimeUs(currentTimeUs, gyroSensor->yawSpinTimeUs) > 20000) { gyroSensor->yawSpinDetected = false; } } else { // reset the yaw spin time gyroSensor->yawSpinTimeUs = currentTimeUs; } }
void taskUpdateBattery(timeUs_t currentTimeUs) { static timeUs_t vbatLastServiced = 0; static timeUs_t ibatLastServiced = 0; if (feature(FEATURE_VBAT)) { if (cmpTimeUs(currentTimeUs, vbatLastServiced) >= VBATINTERVAL) { timeUs_t vbatTimeDelta = currentTimeUs - vbatLastServiced; vbatLastServiced = currentTimeUs; updateBattery(vbatTimeDelta); } } if (feature(FEATURE_CURRENT_METER)) { timeUs_t ibatTimeSinceLastServiced = cmpTimeUs(currentTimeUs, ibatLastServiced); if (ibatTimeSinceLastServiced >= IBATINTERVAL) { ibatLastServiced = currentTimeUs; updateCurrentMeter(ibatTimeSinceLastServiced, &masterConfig.rxConfig, flight3DConfig()->deadband3d_throttle); } } }
void ledStripUpdate(timeUs_t currentTimeUs) { if (!(ledStripInitialised && isWS2811LedStripReady())) { return; } if (IS_RC_MODE_ACTIVE(BOXLEDLOW) && !(ledStripConfig()->ledstrip_visual_beeper && isBeeperOn())) { if (ledStripEnabled) { ledStripDisable(); ledStripEnabled = false; } return; } ledStripEnabled = true; const uint32_t now = currentTimeUs; // test all led timers, setting corresponding bits uint32_t timActive = 0; for (timId_e timId = 0; timId < timTimerCount; timId++) { if (!(disabledTimerMask & (1 << timId))) { // sanitize timer value, so that it can be safely incremented. Handles inital timerVal value. const timeDelta_t delta = cmpTimeUs(now, timerVal[timId]); // max delay is limited to 5s if (delta < 0 && delta > -MAX_TIMER_DELAY) continue; // not ready yet timActive |= 1 << timId; if (delta >= 100 * 1000 || delta < 0) { timerVal[timId] = now; } } } if (!timActive) return; // no change this update, keep old state // apply all layers; triggered timed functions has to update timers scaledThrottle = ARMING_FLAG(ARMED) ? scaleRange(rcData[THROTTLE], PWM_RANGE_MIN, PWM_RANGE_MAX, 0, 100) : 0; auxInput = rcData[ledStripConfig()->ledstrip_aux_channel]; applyLedFixedLayers(); for (timId_e timId = 0; timId < ARRAYLEN(layerTable); timId++) { uint32_t *timer = &timerVal[timId]; bool updateNow = timActive & (1 << timId); (*layerTable[timId])(updateNow, timer); } ws2811UpdateStrip((ledStripFormatRGB_e)ledStripConfig()->ledstrip_grb_rgb); }
static FAST_CODE_NOINLINE void handleOverflow(gyroSensor_t *gyroSensor, timeUs_t currentTimeUs) { const float gyroOverflowResetRate = GYRO_OVERFLOW_RESET_THRESHOLD * gyroSensor->gyroDev.scale; if ((abs(gyroSensor->gyroDev.gyroADCf[X]) < gyroOverflowResetRate) && (abs(gyroSensor->gyroDev.gyroADCf[Y]) < gyroOverflowResetRate) && (abs(gyroSensor->gyroDev.gyroADCf[Z]) < gyroOverflowResetRate)) { // if we have 50ms of consecutive OK gyro vales, then assume yaw readings are OK again and reset overflowDetected // reset requires good OK values on all axes if (cmpTimeUs(currentTimeUs, gyroSensor->overflowTimeUs) > 50000) { gyroSensor->overflowDetected = false; } } else { // not a consecutive OK value, so reset the overflow time gyroSensor->overflowTimeUs = currentTimeUs; } }
rx_spi_received_e frSkyDHandlePacket(uint8_t * const packet, uint8_t * const protocolState) { static timeUs_t lastPacketReceivedTime = 0; static timeUs_t telemetryTimeUs; rx_spi_received_e ret = RX_SPI_RECEIVED_NONE; const timeUs_t currentPacketReceivedTime = micros(); switch (*protocolState) { case STATE_STARTING: listLength = 47; initialiseData(0); *protocolState = STATE_UPDATE; nextChannel(1); cc2500Strobe(CC2500_SRX); break; case STATE_UPDATE: lastPacketReceivedTime = currentPacketReceivedTime; *protocolState = STATE_DATA; if (rxSpiCheckBindRequested(false)) { lastPacketReceivedTime = 0; timeoutUs = 50; missingPackets = 0; *protocolState = STATE_INIT; break; } FALLTHROUGH; //!!TODO -check this fall through is correct // here FS code could be case STATE_DATA: if (cc2500getGdo()) { uint8_t ccLen = cc2500ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F; if (ccLen >= 20) { cc2500ReadFifo(packet, 20); if (packet[19] & 0x80) { missingPackets = 0; timeoutUs = 1; if (packet[0] == 0x11) { if ((packet[1] == rxFrSkySpiConfig()->bindTxId[0]) && (packet[2] == rxFrSkySpiConfig()->bindTxId[1])) { rxSpiLedOn(); nextChannel(1); cc2500setRssiDbm(packet[18]); #if defined(USE_RX_FRSKY_SPI_TELEMETRY) if ((packet[3] % 4) == 2) { telemetryTimeUs = micros(); buildTelemetryFrame(packet); *protocolState = STATE_TELEMETRY; } else #endif { cc2500Strobe(CC2500_SRX); *protocolState = STATE_UPDATE; } ret = RX_SPI_RECEIVED_DATA; lastPacketReceivedTime = currentPacketReceivedTime; } } } } } if (cmpTimeUs(currentPacketReceivedTime, lastPacketReceivedTime) > (timeoutUs * SYNC_DELAY_MAX)) { #if defined(USE_RX_CC2500_SPI_PA_LNA) cc2500TxDisable(); #endif if (timeoutUs == 1) { #if defined(USE_RX_CC2500_SPI_PA_LNA) && defined(USE_RX_CC2500_SPI_DIVERSITY) // SE4311 chip if (missingPackets >= 2) { cc2500switchAntennae(); } #endif if (missingPackets > MAX_MISSING_PKT) { timeoutUs = 50; setRssiDirect(0, RSSI_SOURCE_RX_PROTOCOL); } missingPackets++; nextChannel(1); } else { rxSpiLedToggle(); setRssi(0, RSSI_SOURCE_RX_PROTOCOL); nextChannel(13); } cc2500Strobe(CC2500_SRX); *protocolState = STATE_UPDATE; } break; #if defined(USE_RX_FRSKY_SPI_TELEMETRY) case STATE_TELEMETRY: if (cmpTimeUs(micros(), telemetryTimeUs) >= 1380) { cc2500Strobe(CC2500_SIDLE); cc2500SetPower(6); cc2500Strobe(CC2500_SFRX); #if defined(USE_RX_CC2500_SPI_PA_LNA) cc2500TxEnable(); #endif cc2500Strobe(CC2500_SIDLE); cc2500WriteFifo(frame, frame[0] + 1); *protocolState = STATE_DATA; ret = RX_SPI_RECEIVED_DATA; lastPacketReceivedTime = currentPacketReceivedTime; } break; #endif } return ret; }
/* * processRx called from taskUpdateRxMain */ bool processRx(timeUs_t currentTimeUs) { static bool armedBeeperOn = false; if (!calculateRxChannelsAndUpdateFailsafe(currentTimeUs)) { return false; } // in 3D mode, we need to be able to disarm by switch at any time if (feature(FEATURE_3D)) { if (!IS_RC_MODE_ACTIVE(BOXARM)) disarm(); } updateRSSI(currentTimeUs); if (currentTimeUs > FAILSAFE_POWER_ON_DELAY_US && !failsafeIsMonitoring()) { failsafeStartMonitoring(); } failsafeUpdateState(); const throttleStatus_e throttleStatus = calculateThrottleStatus(); const uint8_t throttlePercent = calculateThrottlePercent(); if (isAirmodeActive() && ARMING_FLAG(ARMED)) { if (throttlePercent >= rxConfig()->airModeActivateThreshold) { airmodeIsActivated = true; // Prevent Iterm from being reset } } else { airmodeIsActivated = false; } /* In airmode Iterm should be prevented to grow when Low thottle and Roll + Pitch Centered. This is needed to prevent Iterm winding on the ground, but keep full stabilisation on 0 throttle while in air */ if (throttleStatus == THROTTLE_LOW && !airmodeIsActivated) { pidResetITerm(); if (currentPidProfile->pidAtMinThrottle) pidStabilisationState(PID_STABILISATION_ON); else pidStabilisationState(PID_STABILISATION_OFF); } else { pidStabilisationState(PID_STABILISATION_ON); } #ifdef USE_RUNAWAY_TAKEOFF // If runaway_takeoff_prevention is enabled, accumulate the amount of time that throttle // is above runaway_takeoff_deactivate_throttle with the any of the R/P/Y sticks deflected // to at least runaway_takeoff_stick_percent percent while the pidSum on all axis is kept low. // Once the amount of accumulated time exceeds runaway_takeoff_deactivate_delay then disable // prevention for the remainder of the battery. if (ARMING_FLAG(ARMED) && pidConfig()->runaway_takeoff_prevention && !runawayTakeoffCheckDisabled && !flipOverAfterCrashMode && !runawayTakeoffTemporarilyDisabled && !STATE(FIXED_WING)) { // Determine if we're in "flight" // - motors running // - throttle over runaway_takeoff_deactivate_throttle_percent // - sticks are active and have deflection greater than runaway_takeoff_deactivate_stick_percent // - pidSum on all axis is less then runaway_takeoff_deactivate_pidlimit bool inStableFlight = false; if (!feature(FEATURE_MOTOR_STOP) || isAirmodeActive() || (throttleStatus != THROTTLE_LOW)) { // are motors running? const uint8_t lowThrottleLimit = pidConfig()->runaway_takeoff_deactivate_throttle; const uint8_t midThrottleLimit = constrain(lowThrottleLimit * 2, lowThrottleLimit * 2, RUNAWAY_TAKEOFF_HIGH_THROTTLE_PERCENT); if ((((throttlePercent >= lowThrottleLimit) && areSticksActive(RUNAWAY_TAKEOFF_DEACTIVATE_STICK_PERCENT)) || (throttlePercent >= midThrottleLimit)) && (fabsf(pidData[FD_PITCH].Sum) < RUNAWAY_TAKEOFF_DEACTIVATE_PIDSUM_LIMIT) && (fabsf(pidData[FD_ROLL].Sum) < RUNAWAY_TAKEOFF_DEACTIVATE_PIDSUM_LIMIT) && (fabsf(pidData[FD_YAW].Sum) < RUNAWAY_TAKEOFF_DEACTIVATE_PIDSUM_LIMIT)) { inStableFlight = true; if (runawayTakeoffDeactivateUs == 0) { runawayTakeoffDeactivateUs = currentTimeUs; } } } // If we're in flight, then accumulate the time and deactivate once it exceeds runaway_takeoff_deactivate_delay milliseconds if (inStableFlight) { if (runawayTakeoffDeactivateUs == 0) { runawayTakeoffDeactivateUs = currentTimeUs; } uint16_t deactivateDelay = pidConfig()->runaway_takeoff_deactivate_delay; // at high throttle levels reduce deactivation delay by 50% if (throttlePercent >= RUNAWAY_TAKEOFF_HIGH_THROTTLE_PERCENT) { deactivateDelay = deactivateDelay / 2; } if ((cmpTimeUs(currentTimeUs, runawayTakeoffDeactivateUs) + runawayTakeoffAccumulatedUs) > deactivateDelay * 1000) { runawayTakeoffCheckDisabled = true; } } else { if (runawayTakeoffDeactivateUs != 0) { runawayTakeoffAccumulatedUs += cmpTimeUs(currentTimeUs, runawayTakeoffDeactivateUs); } runawayTakeoffDeactivateUs = 0; } if (runawayTakeoffDeactivateUs == 0) { DEBUG_SET(DEBUG_RUNAWAY_TAKEOFF, DEBUG_RUNAWAY_TAKEOFF_DEACTIVATING_DELAY, DEBUG_RUNAWAY_TAKEOFF_FALSE); DEBUG_SET(DEBUG_RUNAWAY_TAKEOFF, DEBUG_RUNAWAY_TAKEOFF_DEACTIVATING_TIME, runawayTakeoffAccumulatedUs / 1000); } else { DEBUG_SET(DEBUG_RUNAWAY_TAKEOFF, DEBUG_RUNAWAY_TAKEOFF_DEACTIVATING_DELAY, DEBUG_RUNAWAY_TAKEOFF_TRUE); DEBUG_SET(DEBUG_RUNAWAY_TAKEOFF, DEBUG_RUNAWAY_TAKEOFF_DEACTIVATING_TIME, (cmpTimeUs(currentTimeUs, runawayTakeoffDeactivateUs) + runawayTakeoffAccumulatedUs) / 1000); } } else { DEBUG_SET(DEBUG_RUNAWAY_TAKEOFF, DEBUG_RUNAWAY_TAKEOFF_DEACTIVATING_DELAY, DEBUG_RUNAWAY_TAKEOFF_FALSE); DEBUG_SET(DEBUG_RUNAWAY_TAKEOFF, DEBUG_RUNAWAY_TAKEOFF_DEACTIVATING_TIME, DEBUG_RUNAWAY_TAKEOFF_FALSE); } #endif // When armed and motors aren't spinning, do beeps and then disarm // board after delay so users without buzzer won't lose fingers. // mixTable constrains motor commands, so checking throttleStatus is enough if (ARMING_FLAG(ARMED) && feature(FEATURE_MOTOR_STOP) && !STATE(FIXED_WING) && !feature(FEATURE_3D) && !isAirmodeActive() ) { if (isUsingSticksForArming()) { if (throttleStatus == THROTTLE_LOW) { if (armingConfig()->auto_disarm_delay != 0 && (int32_t)(disarmAt - millis()) < 0 ) { // auto-disarm configured and delay is over disarm(); armedBeeperOn = false; } else { // still armed; do warning beeps while armed beeper(BEEPER_ARMED); armedBeeperOn = true; } } else { // throttle is not low if (armingConfig()->auto_disarm_delay != 0) { // extend disarm time disarmAt = millis() + armingConfig()->auto_disarm_delay * 1000; } if (armedBeeperOn) { beeperSilence(); armedBeeperOn = false; } } } else { // arming is via AUX switch; beep while throttle low if (throttleStatus == THROTTLE_LOW) { beeper(BEEPER_ARMED); armedBeeperOn = true; } else if (armedBeeperOn) { beeperSilence(); armedBeeperOn = false; } } } processRcStickPositions(); if (feature(FEATURE_INFLIGHT_ACC_CAL)) { updateInflightCalibrationState(); } updateActivatedModes(); #ifdef USE_DSHOT /* Enable beep warning when the crash flip mode is active */ if (isMotorProtocolDshot() && isModeActivationConditionPresent(BOXFLIPOVERAFTERCRASH) && IS_RC_MODE_ACTIVE(BOXFLIPOVERAFTERCRASH)) { beeper(BEEPER_CRASH_FLIP_MODE); } #endif if (!cliMode) { updateAdjustmentStates(); processRcAdjustments(currentControlRateProfile); } bool canUseHorizonMode = true; if ((IS_RC_MODE_ACTIVE(BOXANGLE) || failsafeIsActive()) && (sensors(SENSOR_ACC))) { // bumpless transfer to Level mode canUseHorizonMode = false; if (!FLIGHT_MODE(ANGLE_MODE)) { ENABLE_FLIGHT_MODE(ANGLE_MODE); } } else { DISABLE_FLIGHT_MODE(ANGLE_MODE); // failsafe support } if (IS_RC_MODE_ACTIVE(BOXHORIZON) && canUseHorizonMode) { DISABLE_FLIGHT_MODE(ANGLE_MODE); if (!FLIGHT_MODE(HORIZON_MODE)) { ENABLE_FLIGHT_MODE(HORIZON_MODE); } } else { DISABLE_FLIGHT_MODE(HORIZON_MODE); } #ifdef USE_GPS_RESCUE if (IS_RC_MODE_ACTIVE(BOXGPSRESCUE) || (failsafeIsActive() && failsafeConfig()->failsafe_procedure == FAILSAFE_PROCEDURE_GPS_RESCUE)) { if (!FLIGHT_MODE(GPS_RESCUE_MODE)) { ENABLE_FLIGHT_MODE(GPS_RESCUE_MODE); } } else { DISABLE_FLIGHT_MODE(GPS_RESCUE_MODE); } #endif if (FLIGHT_MODE(ANGLE_MODE) || FLIGHT_MODE(HORIZON_MODE)) { LED1_ON; // increase frequency of attitude task to reduce drift when in angle or horizon mode rescheduleTask(TASK_ATTITUDE, TASK_PERIOD_HZ(500)); } else { LED1_OFF; rescheduleTask(TASK_ATTITUDE, TASK_PERIOD_HZ(100)); } if (!IS_RC_MODE_ACTIVE(BOXPREARM) && ARMING_FLAG(WAS_ARMED_WITH_PREARM)) { DISABLE_ARMING_FLAG(WAS_ARMED_WITH_PREARM); } #if defined(USE_ACC) || defined(USE_MAG) if (sensors(SENSOR_ACC) || sensors(SENSOR_MAG)) { #if defined(USE_GPS) || defined(USE_MAG) if (IS_RC_MODE_ACTIVE(BOXMAG)) { if (!FLIGHT_MODE(MAG_MODE)) { ENABLE_FLIGHT_MODE(MAG_MODE); magHold = DECIDEGREES_TO_DEGREES(attitude.values.yaw); } } else { DISABLE_FLIGHT_MODE(MAG_MODE); } #endif if (IS_RC_MODE_ACTIVE(BOXHEADFREE)) { if (!FLIGHT_MODE(HEADFREE_MODE)) { ENABLE_FLIGHT_MODE(HEADFREE_MODE); } } else { DISABLE_FLIGHT_MODE(HEADFREE_MODE); } if (IS_RC_MODE_ACTIVE(BOXHEADADJ)) { if (imuQuaternionHeadfreeOffsetSet()){ beeper(BEEPER_RX_SET); } } } #endif if (IS_RC_MODE_ACTIVE(BOXPASSTHRU)) { ENABLE_FLIGHT_MODE(PASSTHRU_MODE); } else { DISABLE_FLIGHT_MODE(PASSTHRU_MODE); } if (mixerConfig()->mixerMode == MIXER_FLYING_WING || mixerConfig()->mixerMode == MIXER_AIRPLANE) { DISABLE_FLIGHT_MODE(HEADFREE_MODE); } #ifdef USE_TELEMETRY static bool sharedPortTelemetryEnabled = false; if (feature(FEATURE_TELEMETRY)) { bool enableSharedPortTelemetry = (!isModeActivationConditionPresent(BOXTELEMETRY) && ARMING_FLAG(ARMED)) || (isModeActivationConditionPresent(BOXTELEMETRY) && IS_RC_MODE_ACTIVE(BOXTELEMETRY)); if (enableSharedPortTelemetry && !sharedPortTelemetryEnabled) { mspSerialReleaseSharedTelemetryPorts(); telemetryCheckState(); sharedPortTelemetryEnabled = true; } else if (!enableSharedPortTelemetry && sharedPortTelemetryEnabled) { // the telemetry state must be checked immediately so that shared serial ports are released. telemetryCheckState(); mspSerialAllocatePorts(); sharedPortTelemetryEnabled = false; } } #endif #ifdef USE_VTX_CONTROL vtxUpdateActivatedChannel(); if (canUpdateVTX()) { handleVTXControlButton(); } #endif #ifdef USE_ACRO_TRAINER pidSetAcroTrainerState(IS_RC_MODE_ACTIVE(BOXACROTRAINER) && sensors(SENSOR_ACC)); #endif // USE_ACRO_TRAINER #ifdef USE_RC_SMOOTHING_FILTER if (ARMING_FLAG(ARMED) && !rcSmoothingInitializationComplete()) { beeper(BEEPER_RC_SMOOTHING_INIT_FAIL); } #endif pidSetAntiGravityState(IS_RC_MODE_ACTIVE(BOXANTIGRAVITY) || feature(FEATURE_ANTI_GRAVITY)); return true; }
// Receive ISR callback static void sbusDataReceive(uint16_t c, void *data) { static uint16_t sbusDesyncCounter = 0; sbusFrameData_t *sbusFrameData = data; const timeUs_t currentTimeUs = micros(); const timeDelta_t timeSinceLastByteUs = cmpTimeUs(currentTimeUs, sbusFrameData->lastActivityTimeUs); sbusFrameData->lastActivityTimeUs = currentTimeUs; // Handle inter-frame gap. We dwell in STATE_SBUS_WAIT_SYNC state ignoring all incoming bytes until we get long enough quite period on the wire if (sbusFrameData->state == STATE_SBUS_WAIT_SYNC && timeSinceLastByteUs >= rxConfig()->sbusSyncInterval) { DEBUG_SET(DEBUG_SBUS, DEBUG_SBUS_INTERFRAME_TIME, timeSinceLastByteUs); sbusFrameData->state = STATE_SBUS_SYNC; } switch (sbusFrameData->state) { case STATE_SBUS_SYNC: if (c == SBUS_FRAME_BEGIN_BYTE) { sbusFrameData->position = 0; sbusFrameData->buffer[sbusFrameData->position++] = (uint8_t)c; sbusFrameData->state = STATE_SBUS_PAYLOAD; } break; case STATE_SBUS_PAYLOAD: sbusFrameData->buffer[sbusFrameData->position++] = (uint8_t)c; if (sbusFrameData->position == SBUS_FRAME_SIZE) { const sbusFrame_t * frame = (sbusFrame_t *)&sbusFrameData->buffer[0]; bool frameValid = false; // Do some sanity check switch (frame->endByte) { case 0x00: // This is S.BUS 1 case 0x04: // S.BUS 2 receiver voltage case 0x14: // S.BUS 2 GPS/baro case 0x24: // Unknown SBUS2 data case 0x34: // Unknown SBUS2 data frameValid = true; sbusFrameData->state = STATE_SBUS_WAIT_SYNC; break; default: // Failed end marker sbusFrameData->state = STATE_SBUS_WAIT_SYNC; sbusDesyncCounter++; DEBUG_SET(DEBUG_SBUS, DEBUG_SBUS_DESYNC_COUNTER, sbusDesyncCounter); break; } // Frame seems sane, pass data to decoder if (!sbusFrameData->frameDone && frameValid) { DEBUG_SET(DEBUG_SBUS, DEBUG_SBUS_FRAME_FLAGS, frame->channels.flags); memcpy((void *)&sbusFrameData->frame, (void *)&sbusFrameData->buffer[0], SBUS_FRAME_SIZE); sbusFrameData->frameDone = true; } } break; case STATE_SBUS_WAIT_SYNC: // Stay at this state and do nothing. Exit will be handled before byte is processed if the // inter-frame gap is long enough break; } }