UAVX Multicopter Flight Controller.

Dependencies:   mbed

rc.c

Committer:
gke
Date:
2011-04-26
Revision:
2:90292f8bd179
Parent:
0:62a1c91a859a

File content as of revision 2:90292f8bd179:

// ===============================================================================================
// =                              UAVXArm Quadrocopter Controller                                =
// =                           Copyright (c) 2008 by Prof. Greg Egan                             =
// =                 Original V3.15 Copyright (c) 2007 Ing. Wolfgang Mahringer                   =
// =                           http://code.google.com/p/uavp-mods/                               =
// ===============================================================================================

//    This is part of UAVXArm.

//    UAVXArm 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.

//    UAVXArm 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 this program.
//    If not, see http://www.gnu.org/licenses/

#include "UAVXArm.h"

void DoRxPolarity(void);
void InitRC(void);
void MapRC(void);
void CheckSticksHaveChanged(void);
void UpdateControls(void);
void CaptureTrims(void);
void CheckThrottleMoved(void);
void ReceiverTest(void);

const uint8 Map[CustomTxRx+1][CONTROLS] = {
    { 2,0,1,3,4,5,6 },     // Futaba Thr 3 Throttle
    { 1,0,3,2,4,5,6 },    // Futaba Thr 2 Throttle
    { 4,2,1,0,5,3,6 },    // Futaba 9C Spektrum DM8/AR7000
    { 0,1,2,3,4,5,6 },    // JR XP8103/PPM
    { 6,0,3,5,2,4,1 },    // JR 9XII Spektrum DM9 ?

    { 5,0,3,6,2,1,4 },    // JR DXS12 
    { 5,0,3,6,2,1,4 },    // Spektrum DX7/AR7000
    { 4,0,3,5,2,1,6 },    // Spektrum DX7/AR6200

    { 2,0,1,3,4,6,5 },     // Futaba Thr 3 Sw 6/7
    { 0,1,2,3,4,5,6 },    // Spektrum DX7/AR6000
    { 0,1,2,3,4,5,6 },    // Graupner MX16S
    { 4,0,2,3,1,5,6 },    // Spektrum DX6i/AR6200
    { 2,0,1,3,4,5,6 },    // Futaba Th 3/R617FS
    { 4,0,2,3,5,1,6 },    // Spektrum DX7a/AR7000
    { 2,0,1,3,4,6,5 },     // External decoder (Futaba Thr 3 6/7 swap)
    { 0,1,2,3,4,5,6 },    // FrSky DJT/D8R-SP
    { 5,0,3,6,2,1,4 },    // UNDEFINED Spektrum DX7/AR7000
    { 2,0,1,3,4,5,6 }    // Custom
//{ 4,0,2,1,3,5,6 }    // Custom
    };

// Rx signalling polarity used only for serial PPM frames usually
// by tapping internal Rx circuitry.
const boolean PPMPosPolarity[CustomTxRx+1] =
    {
        false,     // Futaba Ch3 Throttle
        false,    // Futaba Ch2 Throttle
        true,    // Futaba 9C Spektrum DM8/AR7000
        true,    // JR XP8103/PPM
        true,    // JR 9XII Spektrum DM9/AR7000

        true,    // JR DXS12
        true,    // Spektrum DX7/AR7000
        true,    // Spektrum DX7/AR6200
        false,    // Futaba Thr 3 Sw 6/7
        true,    // Spektrum DX7/AR6000
        true,    // Graupner MX16S
        true,    // Graupner DX6i/AR6200
        true,    // Futaba Thr 3/R617FS
        true,     // Spektrum DX7a/AR7000
        false,     // External decoder (Futaba Ch3 Throttle)
        true,    // FrSky DJT/D8R-SP
        true,    // UNKNOWN using Spektrum DX7/AR7000
        true    // custom Tx/Rx combination
    };

// Reference Internal Quadrocopter Channel Order
// 0 Throttle
// 1 Aileron
// 2 Elevator
// 3 Rudder
// 4 Gear
// 5 Aux1
// 6 Aux2

int8 RMap[CONTROLS];

int16x8x4Q PPMQ;
int16 PPMQSum[CONTROLS];
int16 RC[CONTROLS], RCp[CONTROLS];
int16 ThrLow, ThrNeutral, ThrHigh;

boolean RCPositiveEdge;

void DoRxPolarity(void) {

    RCInterrupt.rise(&RCNullISR);
    RCInterrupt.fall(&RCNullISR);

    if ( F.UsingSerialPPM  ) // serial PPM frame from within an Rx
        if  ( PPMPosPolarity[P[TxRxType]] )
            RCInterrupt.rise(&RCISR);
        else
            RCInterrupt.fall(&RCISR);
    else {
        RCInterrupt.rise(&RCISR);
        RCInterrupt.fall(&RCISR);
    }

}  // DoRxPolarity

void InitRC(void) {
    static int8 c, q;

    DoRxPolarity();

    SignalCount = -RC_GOOD_BUCKET_MAX;
    F.Signal = F.RCNewValues = false;

    for (c = 0; c < RC_CONTROLS; c++) {
        PPM[c] = 0;
        RC[c] = RCp[c] = RC_NEUTRAL;

        for (q = 0; q <= PPMQMASK; q++)
            PPMQ.B[q][c] = RC_NEUTRAL;
        PPMQSum[c] = RC_NEUTRAL * 4;
    }
    PPMQ.Head = 0;

    DesiredRoll = DesiredPitch = DesiredYaw = DesiredThrottle = StickThrottle = 0;
    Trim[Roll] = Trim[Pitch] = Trim[Yaw] = 0;

    PPM_Index = PrevEdge = RCGlitches = 0;
} // InitRC

void MapRC(void) { // re-maps captured PPM to Rx channel sequence
    static int8 c;
    static int16 LastThrottle, i;
    static uint16 Sum;

    LastThrottle = RC[ThrottleC];

    for ( c = 0 ; c < RC_CONTROLS ; c++ ) {
        Sum = PPM[c];
        PPMQSum[c] -= PPMQ.B[PPMQ.Head][c];
        PPMQ.B[PPMQ.Head][c] = Sum;
        PPMQSum[c] += Sum;
        PPMQ.Head = (PPMQ.Head + 1) & PPMQMASK;
    }

    for ( c = 0 ; c < RC_CONTROLS ; c++ ) {
        i = Map[P[TxRxType]][c];
        RC[c] = ( PPMQSum[i] * RC_MAXIMUM ) / 4000; // needs rethink - throwing away precision
    }

    if ( THROTTLE_SLEW_LIMIT > 0 )
        RC[ThrottleRC] = SlewLimit(LastThrottle, RC[ThrottleRC], THROTTLE_SLEW_LIMIT);

} // MapRC

void CheckSticksHaveChanged(void) {
    static boolean Change;
    static uint8 c;

    if ( F.ReturnHome || F.Navigate  ) {
        if ( mSClock() > mS[StickChangeUpdate] ) {
            mS[StickChangeUpdate] = mSClock() + 500;
            if ( !F.ForceFailsafe && ( State == InFlight )) {
                Stats[RCFailsafesS]++;
                mS[NavStateTimeout] = mSClock() + NAV_RTH_LAND_TIMEOUT_MS;
                mS[DescentUpdate]  = mSClock() + ALT_DESCENT_UPDATE_MS;
                DescentComp = 0; // for no Baro case
            }

            F.ForceFailsafe = State == InFlight; // abort if not navigating
        }
    } else {
        if ( mSClock() > mS[StickChangeUpdate] ) {
            mS[StickChangeUpdate] = mSClock() + 500;

            Change = false;
            for ( c = ThrottleC; c <= (uint8)RTHC; c++ ) {
                Change |= abs( RC[c] - RCp[c]) > RC_STICK_MOVEMENT;
                RCp[c] = RC[c];
            }
        }

        if ( Change ) {
            mS[RxFailsafeTimeout] = mSClock() + RC_NO_CHANGE_TIMEOUT_MS;
            mS[NavStateTimeout] = mSClock();
            F.ForceFailsafe = false;
            if ( FailState == MonitoringRx ) {
                if ( F.LostModel ) {
                    Beeper_OFF;
                    F.LostModel = false;
                    DescentComp = 0;
                }
            }
        } else
            if ( mSClock() > mS[RxFailsafeTimeout] ) {
                if ( !F.ForceFailsafe && ( ( State == InFlight ) || ( ( mSClock() - mS[RxFailsafeTimeout])  > 120000 ) ) ) {
                    Stats[RCFailsafesS]++;
                    mS[NavStateTimeout] = mSClock() + NAV_RTH_LAND_TIMEOUT_MS;
                    mS[DescentUpdate]  = mSClock() + ALT_DESCENT_UPDATE_MS;
                    DescentComp = 0; // for no Baro case
                    F.ForceFailsafe = true;
                }

                //    F.ForceFailsafe = State == InFlight; // abort if not navigating
            }
    }

} // CheckSticksHaveChanged

void UpdateControls(void) {
    static int16 HoldRoll, HoldPitch, RollPitchScale;
    static boolean NewCh5Active;

    F.RCNewValues = false;

    MapRC();                                // remap channel order for specific Tx/Rx

    StickThrottle = RC[ThrottleRC];

    //_________________________________________________________________________________________

    // Navigation

    F.ReturnHome = F.Navigate = F.UsingPolar = false;
    NewCh5Active = RC[RTHRC] > RC_NEUTRAL;

    if ( F.UsingPositionHoldLock )
        if ( NewCh5Active & !F.Ch5Active )
            F.AllowTurnToWP = true;
        else
            F.AllowTurnToWP = SaveAllowTurnToWP;
    else
        if ( RC[RTHRC] > ((3L*RC_MAXIMUM)/4) )
            F.ReturnHome = true;
        else
            if ( RC[RTHRC] > (RC_NEUTRAL/2) )
                F.Navigate = true;

    F.Ch5Active = NewCh5Active;

#ifdef RX6CH
    DesiredCamPitchTrim = RC_NEUTRAL;
    // NavSensitivity set in ReadParametersPX
#else
    DesiredCamPitchTrim = RC[CamPitchRC] - RC_NEUTRAL;
    NavSensitivity = RC[NavGainRC];
    NavSensitivity = Limit(NavSensitivity, 0, RC_MAXIMUM);
#endif // !RX6CH

    //_________________________________________________________________________________________

    // Altitude Hold

    F.AltHoldEnabled = NavSensitivity > NAV_SENS_ALTHOLD_THRESHOLD;

    if ( NavState == HoldingStation ) { // Manual
        if ( StickThrottle < RC_THRES_STOP )    // to deal with usual non-zero EPA
            StickThrottle = 0;
    } else // Autonomous
        if ( F.AllowNavAltitudeHold &&  F.AltHoldEnabled )
            StickThrottle = CruiseThrottle;

    if ( (! F.HoldingAlt) && (!(F.Navigate || F.ReturnHome )) ) // cancel any current altitude hold setting
        DesiredAltitude = Altitude;

    //_________________________________________________________________________________________

    // Attitude

#ifdef ATTITUDE_NO_LIMITS
    DesiredRoll = RC[RollRC] - RC_NEUTRAL;
    DesiredPitch = RC[PitchRC] - RC_NEUTRAL;
#else
    RollPitchScale = MAX_ROLL_PITCH - (NavSensitivity >> 2);
    DesiredRoll = SRS16((RC[RollRC] - RC_NEUTRAL) * RollPitchScale, 7);
    DesiredPitch = SRS16((RC[PitchRC] - RC_NEUTRAL) * RollPitchScale, 7);
#endif // ATTITUDE_NO_LIMITS
    DesiredYaw = RC[YawRC] - RC_NEUTRAL;
    
    AdaptiveYawLPFreq();

    HoldRoll = abs(DesiredRoll - Trim[Roll]);
    HoldPitch = abs(DesiredPitch - Trim[Pitch]);
    CurrMaxRollPitch = Max(HoldRoll, HoldPitch);

    if ( CurrMaxRollPitch > ATTITUDE_HOLD_LIMIT )
        if ( AttitudeHoldResetCount > ATTITUDE_HOLD_RESET_INTERVAL )
            F.AttitudeHold = false;
        else {
            AttitudeHoldResetCount++;
            F.AttitudeHold = true;
        }
    else {
        F.AttitudeHold = true;
        if ( AttitudeHoldResetCount > 1 )
            AttitudeHoldResetCount -= 2;        // Faster decay
    }

    //_________________________________________________________________________________________

    // Rx has gone to failsafe

    //zzz  CheckSticksHaveChanged();

    F.NewCommands = true;

} // UpdateControls

void CaptureTrims(void)
{     // only used in detecting movement from neutral in hold GPS position
    // Trims are invalidated if Nav sensitivity is changed - Answer do not use trims ?
    #ifndef TESTING
    Trim[Roll] = Limit1(DesiredRoll, NAV_MAX_TRIM);
    Trim[Yaw] = Limit1(DesiredPitch, NAV_MAX_TRIM);
    Trim[Yaw] = Limit1(DesiredYaw, NAV_MAX_TRIM);
    #endif // TESTING

    HoldYaw = 0;
} // CaptureTrims

void CheckThrottleMoved(void) {
    if ( mSClock() < mS[ThrottleUpdate] )
        ThrNeutral = DesiredThrottle;
    else {
        ThrLow = ThrNeutral - THROTTLE_MIDDLE;
        ThrLow = Max(ThrLow, THROTTLE_MIN_ALT_HOLD);
        ThrHigh = ThrNeutral + THROTTLE_MIDDLE;
        if ( ( DesiredThrottle <= ThrLow ) || ( DesiredThrottle >= ThrHigh ) ) {
            mS[ThrottleUpdate] = mSClock() + THROTTLE_UPDATE_MS;
            F.ThrottleMoving = true;
        } else
            F.ThrottleMoving = false;
    }
} // CheckThrottleMoved

void ReceiverTest(void) {
    static int8 s;

    TxString("\r\nRx Test \r\n");

    TxString("\r\nRx: ");
    ShowRxSetup();
    TxString("\r\n");

    TxString("RAW Rx frame values - neutrals NOT applied\r\n");
    TxString("Channel order is: ");
    for ( s = 0; s < RC_CONTROLS; s++)
        TxChar(RxChMnem[RMap[s]]);

    if ( F.Signal )
        TxString("\r\nSignal OK ");
    else
        TxString("\r\nSignal FAIL ");
    TxVal32(mSClock() - mS[LastValidRx], 0, 0);
    TxString(" mS ago\r\n");

    // Be wary as new RC frames are being received as this
    // is being displayed so data may be from overlapping frames

    for ( s = 0; s < RC_CONTROLS ; s++ ) {
        TxChar(s+'1');
        TxString(": ");
        TxChar(RxChMnem[RMap[s]]);
        TxString(":\t");

        TxVal32((int32)PPM[s] + 1000, 3, 0);
        TxChar(HT);
        TxVal32(PPM[s] / 10, 0, '%');
        if ( ( PPM[s] < -200 ) || ( PPM[s] > 1200 ) )
            TxString(" FAIL");

        TxNextLine();
    }

    TxString("Glitches:\t");
    TxVal32(RCGlitches,0,0);
    TxNextLine();

} // ReceiverTest