#include #include "motors.h" #include "vehicleConfig.h" #include "rc_board.h" #include "radio.h" #include "lights.h" #include "steeringCurves.h" #include "balancing.h" #include "MPU6050.h" #include "MC34933.h" #include "PWMFrequency.h" bool isDriving; // is the vehicle driving? // Motor objects MC34933 Motor1; MC34933 Motor2; // This array is intended for the "vhctype_semi caterpillar" mode. The inner wheel can max. slow down to 60% of the // outer wheels RPM float curvevhctype_semi[][2] = { // see excel sheet! {0, 60} // {input value, output value} , {25, 70} , {50, 80} , {75, 90} , {100, 100} }; // This array is intended for the "Caterpillar" mode. The inner wheel can spin backwars up to 100% of the // outer wheels RPM. That allows for turning the vehicle "on place" float curveFull[][2] = { {0, -100} // {input value, output value} , {7, 0} , {20, 45} , {60, 85} , {100, 100} }; // This array is intended for the "Differential Thrust" mode. The inner motor can max. slow down to 20% of the // outer motors RPM float curveThrust[][2] = { // see excel sheet! {0, 20} // {input value, output value} , {25, 40} , {50, 60} , {75, 80} , {100, 100} }; // // ======================================================================================================= // MOTOR DRIVER SETUP // ======================================================================================================= // void setupMotors() { vehicleConfig* cfg = getConfigptr(); // MC34933 H-Bridge pins const byte motor1_in1 = PIN_MOTOR_M1A; const byte motor1_in2 = PIN_MOTOR_M1B; const byte motor2_in1 = PIN_MOTOR_M2A; const byte motor2_in2 = PIN_MOTOR_M2B; // SYNTAX: IN1, IN2, PWM, min. input value, max. input value, neutral position width // invert rotation direction true or false Motor2.begin(motor1_in1, motor1_in2, 0, 100, 4, false); // Drive motor Motor1.begin(motor2_in1, motor2_in2, 0, 100, 4, false); // Steering motor (Drive in "HP" version) // Motor PWM frequency prescalers (Requires the PWMFrequency.h library) // Differential steering vehicles: locked to 984Hz, to make sure, that both motors use 984Hz. if (cfg->getvehicleType() == vhctype_semi || cfg->getvehicleType() == vhctype_unknown || cfg->getvehicleType() == simpledualmotorplane) { cfg->setpwmPrescaler2(32); } // ----------- IMPORTANT!! -------------- // Motor 1 always runs @ 984Hz PWM frequency and can't be changed, because timers 0 an 1 are in use for other things! // Motor 2 (pin 3) can be changed to the following PWM frequencies: 32 = 984Hz, 8 = 3936Hz, 1 = 31488Hz setPWMPrescaler(3, cfg->getpwmPrescaler2()); // pin 3 is hardcoded, because we can't change all others anyway } MC34933* getMotorptr(motor_t motor) { switch(motor) { case MOTOR1: { return &Motor1; } break; case MOTOR2: { return &Motor2; } break; default: { return NULL; } } } // // ======================================================================================================= // DRIVE MOTORS CAR (for cars, one motor for driving, one for steering) // ======================================================================================================= // void motordrive(motor_t motor, int controlValue, int minPWM, int maxPWM, int rampTime, bool neutralBrake) { getMotorptr(motor)->drive( controlValue, minPWM, maxPWM, rampTime, neutralBrake); } bool motorBrakeActive(motor_t motor) { return getMotorptr(motor)->brakeActive(); } void drive(_mpu* mpu) { vehicleConfig* cfg = getConfigptr(); switch(cfg->getvehicleType()) { case car: { driveMotorsCar(); } break; case carwithMRSC: { mrsc(mpu); // Car with MSRC stabilty control } break; case forklift: { driveMotorsForklift(); // Forklift } break; case balancingthing: { balancing(mpu); // Self balancing robot } break; default: { driveMotorsSteering(); // Caterpillar and half caterpillar vecicles } break; } } void driveMotorsCar() { vehicleConfig* cfg = getConfigptr(); RcData* data = getDataptr(); //ackPayload* payload = getPayloadptr(); int maxPWM; byte maxAcceleration; // Speed limitation (max. is 255) if (data->mode1) { maxPWM = cfg->getmaxPWMlimited(); // Limited } else { maxPWM = cfg->getmaxPWMfull(); // Full } if (!data->batteryOk && cfg->getliPo()) data->axis3 = 50; // Stop the vehicle, if the battery is empty! // Acceleration & deceleration limitation (ms per 1 step input signal change) if (data->mode2) { maxAcceleration = cfg->getmaxAccelerationLimited(); // Limited } else { maxAcceleration = cfg->getmaxAccelerationFull(); // Full } // ***************** Note! The ramptime is intended to protect the gearbox! ******************* // SYNTAX: Input value, max PWM, ramptime in ms per 1 PWM increment // false = brake in neutral position inactive if (!cfg->getHP()) { // Two channel version: ---- if (Motor1.drive(data->axis3, cfg->getminPWM(), maxPWM, maxAcceleration, true) ) { // The drive motor (function returns true, if not in neutral) UpdatemillisLightOff(); // Reset the headlight delay timer, if the vehicle is driving! } if (cfg->getvehicleType() != carwithMRSC) { // If not car with MSRC stabilty control Motor2.drive(data->axis1, 0, cfg->getsteeringTorque(), 0, false); // The steering motor (if the original steering motor is reused instead of a servo) } } else { // High Power "HP" version. Motor 2 is the driving motor, no motor 1: ---- if (Motor2.drive(data->axis3, cfg->getminPWM(), maxPWM, maxAcceleration, true) ) { // The drive motor (function returns true, if not in neutral) UpdatemillisLightOff(); // Reset the headlight delay timer, if the vehicle is driving! } } } // // ======================================================================================================= // DRIVE MOTORS FORKLIFT (for forklifts, one motor for driving, one for lifting) // ======================================================================================================= // void driveMotorsForklift() { vehicleConfig* cfg = getConfigptr(); RcData* data = getDataptr(); //ackPayload* payload = getPayloadptr(); int maxPWM; byte maxAcceleration; // Speed limitation (max. is 255) if (data->mode1) { maxPWM = cfg->getmaxPWMlimited(); // Limited } else { maxPWM = cfg->getmaxPWMfull(); // Full } if (!data->batteryOk && cfg->getliPo()) data->axis3 = 50; // Stop the vehicle, if the battery is empty! // Acceleration & deceleration limitation (ms per 1 step input signal change) if (data->mode2) { maxAcceleration = cfg->getmaxAccelerationLimited(); // Limited } else { maxAcceleration = cfg->getmaxAccelerationFull(); // Full } // ***************** Note! The ramptime is intended to protect the gearbox! ******************* // SYNTAX: Input value, max PWM, ramptime in ms per 1 PWM increment // false = brake in neutral position inactive if (Motor1.drive(data->axis3, cfg->getminPWM(), maxPWM, maxAcceleration, true) ) { // The drive motor (function returns true, if not in neutral) UpdatemillisLightOff(); // Reset the headlight delay timer, if the vehicle is driving! } Motor2.drive(data->axis2, 0, cfg->getsteeringTorque(), 0, false); // The fork lifting motor (the steering is driven by servo 1) } // // ======================================================================================================= // "STEERING" MOTOR DRIVING FUNCTION (throttle & steering overlay for caterpillar vehicles) // ======================================================================================================= // bool getIsDriving() { return isDriving; } void setIsDriving(bool driveing) { isDriving = driveing; } void driveMotorsSteering() { int pwm[2]; vehicleConfig* cfg = getConfigptr(); RcData* data = getDataptr(); //ackPayload* payload = getPayloadptr(); // The steering overlay int steeringFactorLeft=0; int steeringFactorRight=0; int steeringFactorLeft2=0; int steeringFactorRight2 =0; // The input signal range const int servoMin = 0; const int servoMax = 100; const int servoNeutralMin = 48; const int servoNeutralMax = 52; // Compute steering overlay: // The steering signal is channel 1 = data.axis1 // 100% = wheel spins with 100% of the requested speed forward // -100% = wheel spins with 100% of the requested speed backward if (data->axis1 <= servoNeutralMin) { steeringFactorLeft = map(data->axis1, servoMin, servoNeutralMin, 0, 100); steeringFactorLeft = constrain(steeringFactorLeft, 0, 100); } else { steeringFactorLeft = 100; } if (data->axis1 >= servoNeutralMax) { steeringFactorRight = map(data->axis1, servoMax, servoNeutralMax, 0, 100); steeringFactorRight = constrain(steeringFactorRight, 0, 100); } else { steeringFactorRight = 100; } // Nonlinear steering overlay correction if (cfg->getvehicleType() == 6) { steeringFactorLeft2 = reMap(curveThrust, steeringFactorLeft); // Differential thrust mode steeringFactorRight2 = reMap(curveThrust, steeringFactorRight); data->axis3 = constrain(data->axis3, 50, 100); // reverse locked! } if (cfg->getvehicleType() == 2) { steeringFactorLeft2 = reMap(curveFull, steeringFactorLeft); // Caterpillar mode steeringFactorRight2 = reMap(curveFull, steeringFactorRight); } if (cfg->getvehicleType() == 1) { steeringFactorLeft2 = reMap(curvevhctype_semi, steeringFactorLeft); // vhctype_semi caterpillar mode steeringFactorRight2 = reMap(curvevhctype_semi, steeringFactorRight); } // Drive caterpillar motors // The throttle signal (for both caterpillars) is channel 3 = data.axis3 // -100 to 100% pwm[0] = map(data->axis3, servoMin, servoMax, 100, -100) * steeringFactorRight2 / 100; pwm[1] = map(data->axis3, servoMin, servoMax, 100, -100) * steeringFactorLeft2 / 100; pwm[0] = map(pwm[0], 100, -100, 100, 0); // convert -100 to 100% to 0-100 for motor control pwm[1] = map(pwm[1], 100, -100, 100, 0); if (!data->batteryOk && cfg->getliPo()) { // Both motors off, if LiPo battery is empty! pwm[0] = 0; pwm[1] = 0; } Motor1.drive(pwm[0], 0, 255, 0, false); // left caterpillar, 0ms ramp! Motor2.drive(pwm[1], 0, 255, 0, false); // right caterpillar if (pwm[0] < 40 || pwm[0] > 60 || pwm[1] < 40 || pwm[1] > 60) { UpdatemillisLightOff(); // Reset the headlight delay timer, if the vehicle is driving! } }