Exemplo n.º 1
0
task main()
{
    // These will be used every loop, and are declared
    // here to save from declaring them every loop.
    int powerL = 0;
    int powerR = 0;
    //// Not implemented yet. Will implement when adding ring code.
    //MotorState isMotorStateL = MOTOR_JOYSTICK;
    //MotorState isMotorStateR = MOTOR_JOYSTICK;

    waitForStart();
    initializeRobot();



    // At "max capacity", each loop should do 8 checks and
    // 3 assignments: 2 x (2 joysticks + buttons + 1 D-pad).
    // The order is:
    // (D-pad)
    // Joysticks
    // Buttons

    // Default arguments are never passed if not needed (to optimize).

    // Using a `for` loop (instead of `while`) is more intuitive,
    // flexible, and makes code more readable (e.g. for indexing).

    for (int i=0; ; i++)	// `int i` is used in many included headers.
    {
        Joystick_UpdateData();

        // These should be zeroed after every loop, so if there
        // isn't any input, the motors will have no power.
        powerL = 0;
        powerR = 0;
        powerLift = 0;



        // CONTROLLER 2 INPUT:==================================================||>

        // JOYSTICKS INPUT:--------------------------------------------------|>
        // Input from the two joysticks will control the lift at
        // different speeds (fast for R, slow for L). These will
        // not override the input from the primary driver.
        // This is because the input from the primary driver is
        // processed last, therefore getting "the last say".

        // The signals are only processed if it is above a threshold.
        if ( abs(Joystick_Joystick(JOYSTICK_L, AXIS_Y, CONTROLLER_2))
                >g_JoystickThreshold )
        {
            isLiftState = LIFT_JOYSTICK;
            powerLift =
                Math_ToLogarithmic
                (Joystick_Joystick(JOYSTICK_L, AXIS_Y, CONTROLLER_2));
        }
        if ( abs(Joystick_Joystick(JOYSTICK_R, AXIS_Y, CONTROLLER_2))
                >g_JoystickThreshold )
        {
            isLiftState = LIFT_JOYSTICK;
            powerLift =
                Math_ToLogarithmic
                (Joystick_Joystick(JOYSTICK_R, AXIS_Y, CONTROLLER_2)
                 /g_FineTuneFactor);
        }


        // BUTTONS INPUT:--------------------------------------------------|>
        // This includes servo controls and the ramp release confirm.

        // Uncomment the next line and comment the one after if masking.
        // For an explanation of masking the buttons' input,
        // see the comments accompanying CONTROLLER_1's buttons.
        //if ( (g_ControllerMaskB & joystick.joy2_Buttons) != false )
        if ( joystick.joy2_Buttons != false )
        {
            if ( Joystick_Button(BUTTON_LT, CONTROLLER_2)==true )
            {
                Servo_Rotate(servo_IR, g_IRServoLowered);
            }
            if ( Joystick_Button(BUTTON_LB, CONTROLLER_2)==true )
            {
                Servo_Rotate(servo_IR, g_IRServoExtended);
            }
            if ( Joystick_Button(BUTTON_RT, CONTROLLER_2)==true )
            {
                Servo_Rotate(servo_IR, g_clawServoFolded);
            }
            if ( Joystick_Button(BUTTON_RB, CONTROLLER_2)==true )
            {
                Servo_Rotate(servo_IR, g_clawServoFolded);
            }

            // Both controllers need to press START to deploy ramp.
            // The code is in CONTROLLER_1's buttons code block.
            if ( Joystick_Button(BUTTON_BACK, CONTROLLER_2)==true )
            {
                Servo_Rotate(servo_ramp, g_rampServoHold);
            }
        }



        // CONTROLLER 1 INPUT:==================================================||>

        // D-PAD INPUT:--------------------------------------------------|>
        // Only if D-pad is pressed, test for direction.

        // Controls lift and has two unimplemented functions
        // for taking rings off/putting rings on.
        if ( Joystick_Direction() != DIRECTION_NONE )
        {
            switch ( Joystick_Direction() )
            {

            // Operate lift at full power if F/B.
            case DIRECTION_F:
                isLiftState = LIFT_JOYSTICK;
                powerLift = g_FullLiftPower;
                break;
            case DIRECTION_B:
                isLiftState = LIFT_JOYSTICK;
                powerLift = (-1)*g_FullLiftPower;
                break;

            case DIRECTION_L:
                sub_PutRingOn();
                break;
            case DIRECTION_R:
                sub_TakeRingOff();
                break;
            }
        }


        // JOYSTICKS INPUT:--------------------------------------------------|>
        // Controls most of the driving. We are using two separate
        // checks, because combining them into one check will execute
        // both even if only one joystick is pressed. The linking of
        // both checks (disjunction) checks both inputs anyways.

        // The signals are only processed if it is above a threshold.
        if ( abs(Joystick_Joystick(JOYSTICK_L, AXIS_Y)) > g_JoystickThreshold )
        {
            powerL = Math_ToLogarithmic(Joystick_Joystick(JOYSTICK_L, AXIS_Y));
        }
        if ( abs(Joystick_Joystick(JOYSTICK_R, AXIS_Y)) > g_JoystickThreshold )
        {
            powerR = Math_ToLogarithmic(Joystick_Joystick(JOYSTICK_R, AXIS_Y));
        }

        // I don't actually have any code for real arcade driving :P


        // BUTTONS INPUT:--------------------------------------------------|>
        // Everything other than the buttons used might be masked to
        // (possibly) increase speed.
        // Reasoning: `&` compares every bit, so we might as well mask,
        // in case something irrelevant is pressed.

        // A `0` value means no buttons (that we test for) are pressed.
        // Directly using the struct since this is the only time we
        // use it, and the code is meant to be low-level anyways.

        // Uncomment the next line and comment the one after if masking.
        // For an explanation of masking the buttons' input,
        // see the comments accompanying CONTROLLER_1's buttons.
        //if ( (g_ControllerMaskA & joystick.joy1_Buttons) != false )
        if ( joystick.joy1_Buttons != false )
        {
            // Buttons Y/B/A/X will control lift height.
            if ( Joystick_Button(BUTTON_Y)==true )
            {
                isLiftState = LIFT_TOP;
            }
            if ( Joystick_Button(BUTTON_B)==true )
            {
                isLiftState = LIFT_MIDDLE;
            }
            if ( Joystick_Button(BUTTON_A)==true )
            {
                isLiftState = LIFT_BOTTOM;
            }
            if ( Joystick_Button(BUTTON_X)==true )
            {
                isLiftState = LIFT_FETCH;
            }

            // Buttons LT/RT fine-tune the lift.
            if ( Joystick_Button(BUTTON_RT)==true )
            {
                isLiftState = LIFT_JOYSTICK;
                powerLift = g_FullLiftPower/g_FineTuneFactor;
            }
            if ( Joystick_Button(BUTTON_LT)==true )
            {
                isLiftState = LIFT_JOYSTICK;
                powerLift = (-1)*g_FullLiftPower/g_FineTuneFactor;
            }

            // If LB/RB is pressed, fine-tune the motors.
            if ( (Joystick_Button(BUTTON_LB)||
                    Joystick_Button(BUTTON_RB)) ==true )
            {
                powerL /= g_FineTuneFactor;
                powerR /= g_FineTuneFactor;
            }

            // Both controllers need to press START to deploy ramp.
            if ( (Joystick_Button(BUTTON_START)&&
                    Joystick_Button(BUTTON_START, CONTROLLER_2))==true )
            {
                Servo_Rotate(servo_ramp, g_rampServoDeployed);
            }
            if ( Joystick_Button(BUTTON_BACK)==true )
            {
                Servo_Rotate(servo_ramp, g_rampServoHold);
            }
        }



        // FINAL PROCESSING:==================================================||>

        // After preliminary processing of the controller data,
        // the actual motor/servo assignments happen here.

        switch (isLiftState)
        {
        case LIFT_BOTTOM:
            sub_LiftToHeight(g_BottomLiftAngle);
            break;
        case LIFT_MIDDLE:
            sub_LiftToHeight(g_MiddleLiftAngle);
            break;
        case LIFT_TOP:
            sub_LiftToHeight(g_TopLiftAngle);
            break;
        case LIFT_FETCH:
            sub_LiftToHeight(g_FetchLiftAngle);
        }

        Motor_SetPower(motor_L, powerL);
        Motor_SetPower(motor_R, powerR);
        Motor_SetPower(motor_lift, powerLift);
    }
}
Exemplo n.º 2
0
task PID()
{
	const float kP_up = 0.082;
	const float kI_up = 0.017;
	const float kD_up = 0.00;
	const float kP_down = 0.002;
	const float kI_down = 0.005;
	const float kD_down = 0.00;
	const float I_term_decay_rate = 0.87;
	const int I_term_threshold = 500;

	int timer_loop = 0;
	Time_ClearTimer(timer_loop);
	int dt = Time_GetTime(timer_loop);

	lift_pos = Motor_GetEncoder(encoder_lift);

	float error = 0.0;
	float error_prev = 0.0;
	float error_sum = 0.0;
	float error_rate = 0.0;

	Joystick_WaitForStart();
	Time_ClearTimer(timer_loop);

	while (true) {
		dt = Time_GetTime(timer_loop);
		Time_ClearTimer(timer_loop);
		error_prev = error;
		lift_pos = Motor_GetEncoder(encoder_lift);

		if (is_lift_manual == false) {
			if (lift_target < pos_lift_bottom) {
				lift_target = pos_lift_bottom;
			}
			if (lift_target > pos_lift_top) {
				lift_target = pos_lift_top;
			}
			error = lift_target - lift_pos;
			if (error > 0) {
				isDown = false;
			} else {
				isDown = true;
			}
			error_sum *= I_term_decay_rate;
			if (error < I_term_threshold) {
				error_sum += error * (float)dt;
			}
			error_rate = (error - error_prev) / (float)dt;

			term_P_lift = error;
			term_I_lift = error_sum;
			term_D_lift = error_rate;
			switch (isDown) {
				case true :
					term_P_lift *= kP_down;
					term_I_lift *= kI_down;
					term_D_lift *= kD_down;
					break;
				case false :
					term_P_lift *= kP_up;
					term_I_lift *= kI_up;
					term_D_lift *= kD_up;
					break;
			}
			power_lift = term_P_lift + term_I_lift + term_D_lift;
		} else {
			lift_target = lift_pos;
			power_lift = power_lift_temp;
		}
		if (abs(power_lift)<20) {
			power_lift = 0;
		}

		if (isLiftFrozen) {
			power_lift = 0;
		}

		if (isReset == false) {
			if (power_lift>0 && lift_pos>pos_lift_top) {
				power_lift = 0;
			} else if (power_lift<0 && lift_pos<pos_lift_bottom) {
				power_lift = 0;
			}
		} else {
			Motor_ResetEncoder(encoder_lift);
		}

		Motor_SetPower(power_lift, motor_lift_A);
		Motor_SetPower(power_lift, motor_lift_B);

		Time_Wait(2);
	}
}
Exemplo n.º 3
0
task main()
{
	typedef enum MotorDirection {
		DIRECTION_NONE = 0,
		DIRECTION_IN,
		DIRECTION_OUT
	};
	typedef enum PickupPos {
		PICKUP_RETRACT = 0,
		PICKUP_LARGE,
		PICKUP_SMALL,
		PICKUP_KICK
	};

	initializeGlobalVariables();
	initializeRobotVariables();

	MotorDirection pickup_I_direction		= DIRECTION_NONE;
	MotorDirection pickup_I_direction_prev	= DIRECTION_NONE;
	MotorDirection pickup_O_direction		= DIRECTION_NONE;
	MotorDirection pickup_O_direction_prev	= DIRECTION_NONE;

	MotorDirection clamp_direction			= DIRECTION_NONE;
	PickupPos pickup_pos	= PICKUP_LARGE;
	int servo_dump_pos		= pos_servo_dump_closed;

	float power_L			= 0.0;
	float power_R			= 0.0;
	float power_pickup_I	= 0.0;
	float power_pickup_O	= 0.0;
	float power_clamp		= 0.0;

	Task_Spawn(Gyro);
	Task_Spawn(PID);
	Task_Spawn(Display);
	Task_Spawn(Hopper);
	Joystick_WaitForStart();

	while (true) {
		Joystick_UpdateData();

		HTIRS2readAllACStrength(sensor_IR, IR_A, IR_B, IR_C, IR_D, IR_E);

		hopper_pos = encoderToHopper(Motor_GetEncoder(encoder_hopper));

		power_L = Joystick_GenericInput(JOYSTICK_L, AXIS_Y);
		power_R = Joystick_GenericInput(JOYSTICK_R, AXIS_Y);

		power_lift_temp = Joystick_GenericInput(JOYSTICK_L, AXIS_Y, CONTROLLER_2);
		if (abs(power_lift_temp) > 5) {
			is_lift_manual = true;
		} else {
			is_lift_manual = false;
		}
		if (Joystick_Button(BUTTON_JOYL, CONTROLLER_2) == true) {
			isReset = true;
		} else {
			isReset = false;
		}

		//servo_turntable_pos -= 0.004 * Joystick_GenericInput(JOYSTICK_R, AXIS_X, CONTROLLER_2);
		if (Joystick_Button(BUTTON_JOYR, CONTROLLER_2) || Joystick_ButtonReleased(BUTTON_JOYR, CONTROLLER_2)) {
			servo_turntable_pos = pos_servo_turntable_F;
			hopper_target = pos_servo_hopper_down;
		}
		if (abs(Joystick_GenericInput(JOYSTICK_R, AXIS_Y, CONTROLLER_2))>0) {
			hopper_target = hopper_pos;
			hopper_target += 0.6 * Joystick_GenericInput(JOYSTICK_R, AXIS_Y, CONTROLLER_2);
		}

		hopper_r = sqrt(hopper_x_target*hopper_x_target + hopper_y_target*hopper_y_target);
		hopper_theta = atan2(hopper_y_target, hopper_x_target);

		if (Joystick_ButtonPressed(BUTTON_B)) {
			pickup_I_direction_prev = pickup_I_direction;
			pickup_I_direction = DIRECTION_OUT;
		}
		if (Joystick_ButtonPressed(BUTTON_A)) {
			pickup_I_direction_prev = pickup_I_direction;
			pickup_I_direction = DIRECTION_IN;
		}
		if (Joystick_ButtonReleased(BUTTON_B)) {
			pickup_I_direction = pickup_I_direction_prev;
		}
		if (Joystick_ButtonReleased(BUTTON_A)) {
			pickup_I_direction = pickup_I_direction_prev;
		}

		if (Joystick_ButtonPressed(BUTTON_Y)) {
			pickup_O_direction_prev = pickup_O_direction;
			pickup_O_direction = DIRECTION_OUT;
		}
		if (Joystick_ButtonPressed(BUTTON_X)) {
			pickup_O_direction_prev = pickup_O_direction;
			pickup_O_direction = DIRECTION_IN;
		}
		if (Joystick_ButtonReleased(BUTTON_Y)) {
			pickup_O_direction = pickup_O_direction_prev;
		}
		if (Joystick_ButtonReleased(BUTTON_X)) {
			pickup_O_direction = pickup_O_direction_prev;
		}

		if (Joystick_ButtonPressed(BUTTON_RB)) {
			switch (pickup_O_direction) {
				case DIRECTION_NONE :
				case DIRECTION_OUT :
					pickup_O_direction = DIRECTION_IN;
					pickup_I_direction = DIRECTION_IN;
					break;
				case DIRECTION_IN :
					pickup_O_direction = DIRECTION_NONE;
					pickup_I_direction = DIRECTION_NONE;
					break;
			}
		}
		if (Joystick_ButtonPressed(BUTTON_RT)) {
			pickup_O_direction_prev = pickup_O_direction;
			pickup_I_direction_prev = pickup_I_direction;
			pickup_O_direction = DIRECTION_OUT;
			pickup_I_direction = DIRECTION_OUT;
		}
		if (Joystick_ButtonReleased(BUTTON_RT)) {
			pickup_O_direction = pickup_O_direction_prev;
			pickup_I_direction = pickup_I_direction_prev;
		}

		if (Joystick_DirectionPressed(DIRECTION_R)) {
			pickup_pos = PICKUP_RETRACT;
		} else if (Joystick_DirectionPressed(DIRECTION_F)) {
			pickup_pos = PICKUP_KICK;
		} else if (Joystick_DirectionPressed(DIRECTION_L)) {
			pickup_pos = PICKUP_LARGE;
		} else if (Joystick_DirectionPressed(DIRECTION_B)) {
			pickup_pos = PICKUP_SMALL;
		}

		if (Joystick_Button(BUTTON_LB)) {
			clamp_direction = DIRECTION_OUT;
		} else if (Joystick_Button(BUTTON_LT)) {
			clamp_direction = DIRECTION_IN;
		} else {
			clamp_direction = DIRECTION_NONE;
		}

		if (Joystick_Button(BUTTON_RB, CONTROLLER_2)) {
			servo_dump_pos = pos_servo_dump_open_small;
		} else if (Joystick_Button(BUTTON_RT, CONTROLLER_2)) {
			servo_dump_pos = pos_servo_dump_open_large;
		} else {
			servo_dump_pos = pos_servo_dump_closed;
		}
		if (pickup_I_direction==DIRECTION_IN && lift_pos<pos_dump_safety) {
			servo_dump_pos = pos_servo_dump_open_feed;
		}

		if (Joystick_Button(BUTTON_LB, CONTROLLER_2)) {
			clamp_direction = DIRECTION_IN;
		}
		if (Joystick_Button(BUTTON_LT, CONTROLLER_2)) {
			clamp_direction = DIRECTION_OUT;
		}

		if (Joystick_ButtonPressed(BUTTON_X, CONTROLLER_2)) {
			lift_target = pos_lift_bottom;
			is_lift_manual = false;
			hopper_target = pos_servo_hopper_down;
			servo_turntable_pos = pos_servo_turntable_F;
			isLiftFrozen = true;
			servo_dump_pos = pos_servo_dump_closed;
		} else if (Joystick_ButtonPressed(BUTTON_A, CONTROLLER_2)) {
			lift_target = pos_lift_low;
			is_lift_manual = false;
			hopper_target = pos_servo_hopper_goal;
			servo_turntable_pos = pos_servo_turntable_F;
			isHopperFrozen = true;
			servo_dump_pos = pos_servo_dump_closed;
		} else if (Joystick_ButtonPressed(BUTTON_B, CONTROLLER_2)) {
			lift_target = pos_lift_medium;
			is_lift_manual = false;
			hopper_target = pos_servo_hopper_goal;
			servo_turntable_pos = pos_servo_turntable_F;
			isHopperFrozen = true;
			servo_dump_pos = pos_servo_dump_closed;
		} else if (Joystick_ButtonPressed(BUTTON_Y, CONTROLLER_2)) {
			lift_target = pos_lift_high;
			is_lift_manual = false;
			hopper_target = pos_servo_hopper_goal;
			servo_turntable_pos = pos_servo_turntable_F;
			isHopperFrozen = true;
			servo_dump_pos = pos_servo_dump_closed;
		}

		if (Joystick_ButtonPressed(BUTTON_START)) {
			Servo_SetPosition(servo_hopper_A, pos_servo_hopper_goal);
			Servo_SetPosition(servo_hopper_B, pos_servo_hopper_goal);
			if (abs(hopper_target-pos_servo_hopper_center)<3) {
				hopper_target = pos_servo_hopper_down;
			} else {
				hopper_target = pos_servo_hopper_center;
			}
		}
		if (Joystick_ButtonPressed(BUTTON_BACK)) {
			Servo_SetPosition(servo_hopper_A, pos_servo_hopper_center);
			Servo_SetPosition(servo_hopper_B, pos_servo_hopper_center);
			if (abs(hopper_target-pos_servo_hopper_goal)<3) {
				hopper_target = pos_servo_hopper_down;
			} else {
				hopper_target = pos_servo_hopper_goal;
			}
		}

		if (isLiftFrozen && hopper_pos < pos_servo_hopper_up) {
			isLiftFrozen = false;
		}
		if (isHopperFrozen) {
			hopper_target = pos_servo_hopper_up;
			if (abs(lift_pos-lift_target)<300) {
				hopper_target = pos_servo_hopper_goal;
				isHopperFrozen = false;
			}
		}

		switch (pickup_I_direction) {
			case DIRECTION_NONE :
				power_pickup_I = 0;
				break;
			case DIRECTION_IN :
				power_pickup_I = 100;
				break;
			case DIRECTION_OUT :
				power_pickup_I = -100;
				break;
		}
		switch (pickup_O_direction) {
			case DIRECTION_NONE :
				power_pickup_O = 0;
				break;
			case DIRECTION_IN :
				power_pickup_O = 100;
				break;
			case DIRECTION_OUT :
				power_pickup_O = -100;
				break;
		}
		switch (clamp_direction) {
			case DIRECTION_NONE :
				power_clamp = 0;
				break;
			case DIRECTION_IN :
				power_clamp = 100;
				break;
			case DIRECTION_OUT :
				power_clamp = -100;
				break;
		}

		Motor_SetPower(power_L, motor_LT);
		Motor_SetPower(power_L, motor_LB);
		Motor_SetPower(power_R, motor_RT);
		Motor_SetPower(power_R, motor_RB);
		Motor_SetPower(power_pickup_I, motor_pickup_I);
		Motor_SetPower(power_pickup_O, motor_pickup_O);
		Motor_SetPower(power_clamp, motor_clamp_L);
		Motor_SetPower(power_clamp, motor_clamp_R);

		Servo_SetPosition(servo_dump, servo_dump_pos);
		// NOTE: Hopper and turntable servos should be set in the "Hopper" task.
		switch (pickup_pos) {
			case PICKUP_RETRACT :
				Servo_SetPosition(servo_pickup_L, 129 + pos_servo_pickup_retract);
				Servo_SetPosition(servo_pickup_R, 120 - pos_servo_pickup_retract);
				break;
			case PICKUP_LARGE :
				Servo_SetPosition(servo_pickup_L, 129 + pos_servo_pickup_large);
				Servo_SetPosition(servo_pickup_R, 120 - pos_servo_pickup_large);
				break;
			case PICKUP_SMALL :
				Servo_SetPosition(servo_pickup_L, 129 + pos_servo_pickup_small);
				Servo_SetPosition(servo_pickup_R, 120 - pos_servo_pickup_small);
				break;
			case PICKUP_KICK :
				Servo_SetPosition(servo_pickup_L, 129 + pos_servo_pickup_kick);
				Servo_SetPosition(servo_pickup_R, 120 - pos_servo_pickup_kick);
				break;
		}

		Time_Wait(5);
	}
}
Exemplo n.º 4
0
task main()
{
	// These will be used later and are declared here to save from having to
	// declare them every single loop.
	int powerL = 0;
	int powerR = 0;
	int powerPopcorn = 0;
	MotorState isMotorStateL = MOTOR_JOYSTICK;
	MotorState isMotorStateR = MOTOR_JOYSTICK;



	waitForStart();

	initializeRobot();



	while (true)
	{
		// Currently does (at least) 7 checks and 3 assignments per loop.
		Joystick_UpdateData();



		// These should be zeroed after every loop. In the case that there
		// isn't input, the motors won't keep moving at the last speed it had.
		powerL = 0;
		powerR = 0;
		powerLift = 0;
		powerPopcorn = 0;

		// POPCORN!!! (This comes first, obviously.)
		if ( Joystick_Button(BUTTON_B, CONTROLLER_2)==true )
		{
			powerPopcorn = g_FullDrivePower;
		}
		else if ( Joystick_Button(BUTTON_A, CONTROLLER_2)==true )
		{
			powerPopcorn = (-1)*g_FullDrivePower;
		}



		// See if a direction is being pressed, then test for the direction.
		// This is inside an `if` statement to optimize speed (less checking).
		// `JoystickController` arguments are not passed to increase speed.

		// Input from CONTROLLER_2 will be used to control the lift in
		// conjunction with CONTROLLER_1, but shouldn't override the driver,
		// since driver #1's input is processed last.

		// This is the code for CONTROLLER_2:
		if ( abs(joystick.joy2_y1)>g_JoystickThreshold )
		{
			isLiftState = LIFT_JOYSTICK;
			//powerLift = Math_ToLogarithmic(joystick.joy2_y1);
			powerLift = Math_ToLogarithmic(Joystick_Joystick(JOYSTICK_L, AXIS_Y, CONTROLLER_2));
		}
		if ( (	Joystick_Button(BUTTON_LB, CONTROLLER_2) ||
				Joystick_Button(BUTTON_RB, CONTROLLER_2)) ==true )
		{
			isLiftState = LIFT_JOYSTICK;
			powerLift /= g_FineTuneFactor;
		}

		// This is the code for CONTROLLER_1, along with two unimplemented
		// functions for putting rings on and taking rings off.
		if ( Joystick_Direction() != DIRECTION_NONE )
		{
			switch ( Joystick_Direction() )
			{

				// Operate lift at full power if F/B.
				case DIRECTION_F:
					isLiftState = LIFT_JOYSTICK;
					powerLift = g_FullLiftPower;
					break;
				case DIRECTION_B:
					isLiftState = LIFT_JOYSTICK;
					powerLift = (-1)*g_FullLiftPower;
					break;

				case DIRECTION_L:
					sub_PutRingOn();
					break;
				case DIRECTION_R:
					StartTask(sub_TakeRingOff);
					break;
			}
		}



		// See if a button (not masked) is being pressed, then react.
		// This is inside an `if` statement to optimize speed (less checking).

		// The argument to this first `if` statement is a masked version
		// of the "bitmap" of buttons directly from the controller.

		// Everything other than the buttons used are masked off, to increase
		// processing speed (possibly, just speculation). Reasoning:
		// `&` compares all bits of the variables, so we might as well mask
		// everything we won't need, in case something irrelevant is pressed.

		// A `0` value means no buttons (that we are testing for) are pressed.
		// Directly using the struct since this is the only possible time to
		// use it, and this is very low-level anyways.

		//if ( joystick.joy1_Buttons != false )
		//if ( (g_ControllerMaskA & joystick.joy1_Buttons) != false )
		{

			// Buttons Y/B/A will control lift height.
			if ( Joystick_Button(BUTTON_Y)==true )
			{
				isLiftState = LIFT_TOP;
			}
			if ( Joystick_Button(BUTTON_B)==true )
			{
				isLiftState = LIFT_MIDDLE;
			}
			if ( Joystick_Button(BUTTON_A)==true )
			{
				isLiftState = LIFT_BOTTOM;
			}

			// If only X is pressed, weigh the ring.
			// If JOYR is pressed as well, deploy ramp.
			if ( Joystick_Button(BUTTON_X)==true )
			{
				if ( Joystick_Button(BUTTON_JOYR) == true )
				{
					Servo_Rotate(servo_ramp, g_rampServoDeployed);
				}
				else
				{
					StartTask(sub_WeighRings);
				}
			}

			// Buttons LT/RT will fine-tune the lift.
			if ( Joystick_Button(BUTTON_RT)==true )
			{
				isLiftState = LIFT_JOYSTICK;
				powerLift = g_FullLiftPower/g_FineTuneFactor;
			}
			if ( Joystick_Button(BUTTON_LT)==true )
			{
				isLiftState = LIFT_JOYSTICK;
				powerLift = (-1)*g_FullLiftPower/g_FineTuneFactor;
			}

		}



		// L/R motor code. Only triggered when a joystick returns a
		// value greater than the "drive" threshold (`global vars.h`).

		// Logarithmic control probably won't be implemented anytime soon.
		// Also need to stop using the `joystick` struct and switch to the
		// encapsulated version (Joystick_Joystick(...)).



		// Y-axis code:
		if ( 	abs(Joystick_Joystick(JOYSTICK_L, AXIS_Y)) > g_JoystickThreshold ||
				abs(Joystick_Joystick(JOYSTICK_R, AXIS_Y)) > g_JoystickThreshold )
		{
			powerL = Math_ToLogarithmic(Joystick_Joystick(JOYSTICK_L, AXIS_Y));
			powerR = Math_ToLogarithmic(Joystick_Joystick(JOYSTICK_R, AXIS_Y));
		}

		// Last check: if LB/RB is pressed, fine-tune the power level.
		if ( (Joystick_Button(BUTTON_LB)||Joystick_Button(BUTTON_RB)) ==true )
		{
			powerL /= g_FineTuneFactor;
			powerR /= g_FineTuneFactor;
		}



		// CONTROLLER_2 has the same masking implementation as CONTROLLER_1.
		// For a detailed explanation of the mechanism, see those comments.

		// CONTROLLER_2 is only tested for button X (currently).

		//if ( joystick.joy2_Buttons != false )
		//if ( (g_ControllerMaskB & joystick.joy2_Buttons) != false )
		{

			// If X is pressed, the MOO shall be released!
			if ( Joystick_Button(BUTTON_X, CONTROLLER_2)==true )
			{
				//StartTask(sub_MOO);
				PlaySoundFile("moo.rso");
			}
			if ( Joystick_Button(BUTTON_Y, CONTROLLER_2)==true )
			{
				//StopTask(sub_MOO);
				sub_CowsWithGuns();
			}

		}



		switch (isLiftState)
		{
			case LIFT_BOTTOM:
				sub_LiftToBottom();
				break;
			case LIFT_MIDDLE:
				sub_LiftToMiddle();
				break;
			case LIFT_TOP:
				sub_LiftToTop();
				break;
		}



		// Flush the controller input buffer periodically (every 1/4 sec?)



		Motor_SetPower(motor_L, powerL);
		Motor_SetPower(motor_R, powerR);
		Motor_SetPower(motor_lift, powerLift);
		Motor_SetPower(motor_popcorn, powerPopcorn);



	}
}