/*************************************************************************** * sam7fc - Flight Control * * * * Copyright (C) 03/2008 by Olaf Rempel * * razzor@kopf-tisch.de * * * * This program 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; version 2 of the License * * * * This program 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, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include /* abs() */ #include "AT91SAM7S256.h" #include "board.h" #include #include #include #include #include #include static uint32_t global_state; #define MOTOR_RUNNING 0x0001 #define MOTOR_MIXER_ENABLE 0x0002 #define STICK_CALIBRATION 0x0004 #define STICK_CALIBRATION_COMPLETE 0x0008 static struct pid_data pid_nick = { .kp = 1, .ki = 0, .err_sum_max = +1024, .err_sum_min = -1024, .out_max = +256, .out_min = -256, }; TDC_PTR(pid_nick_ki, &pid_nick.ki, "flctrl: pid nick ki", int32_t, TDC_SIGNED); TDC_PTR(pid_nick_errsum, &pid_nick.err_sum, "flctrl: pid nick errsum", int32_t, TDC_SIGNED | TDC_READONLY); static struct pid_data pid_roll = { .kp = 1, .ki = 0, .err_sum_max = +1024, .err_sum_min = -1024, .out_max = +256, .out_min = -256, }; TDC_PTR(pid_roll_ki, &pid_roll.ki, "flctrl: pid roll ki", int32_t, TDC_SIGNED); TDC_PTR(pid_roll_errsum, &pid_roll.err_sum, "flctrl: pid roll errsum", int32_t, TDC_SIGNED | TDC_READONLY); static struct pid_data pid_gier = { .kp = 1, .ki = 0, .err_sum_max = +1024, .err_sum_min = -1024, .out_max = +256, .out_min = -256, }; TDC_PTR(pid_gier_ki, &pid_gier.ki, "flctrl: pid gier ki", int32_t, TDC_SIGNED); TDC_PTR(pid_gier_errsum, &pid_gier.err_sum, "flctrl: pid gier errsum", int32_t, TDC_SIGNED | TDC_READONLY); static void motor_mixer(int32_t gas, int32_t nick, int32_t roll, int32_t gier) { static uint8_t pwm[4]; TDC_VALUE(pwm0, pwm[0], "Motor PWM 1", uint8_t, TDC_UNSIGNED | TDC_READONLY); TDC_VALUE(pwm1, pwm[1], "Motor PWM 2", uint8_t, TDC_UNSIGNED | TDC_READONLY); TDC_VALUE(pwm2, pwm[2], "Motor PWM 3", uint8_t, TDC_UNSIGNED | TDC_READONLY); TDC_VALUE(pwm3, pwm[3], "Motor PWM 4", uint8_t, TDC_UNSIGNED | TDC_READONLY); pwm[0] = LIMIT((gas - nick + gier), 0x0F, 0xFF); pwm[1] = LIMIT((gas + nick + gier), 0x0F, 0xFF); pwm[2] = LIMIT((gas + roll - gier), 0x0F, 0xFF); pwm[3] = LIMIT((gas - roll - gier), 0x0F, 0xFF); if (!(global_state & MOTOR_RUNNING)) { pwm[0] = 0x00; pwm[1] = 0x00; pwm[2] = 0x00; pwm[3] = 0x00; } else if (!(global_state & MOTOR_MIXER_ENABLE)) { pwm[0] = 0x0F; pwm[1] = 0x0F; pwm[2] = 0x0F; pwm[3] = 0x0F; } twi_setpwm(pwm); } static uint32_t base_ctrl_cb(struct pitc_timer *timer) { if (global_state & STICK_CALIBRATION) global_state |= STICK_CALIBRATION_COMPLETE; return PITC_REMOVE_TIMER; } static struct pitc_timer base_ctrl_timer = { .func = base_ctrl_cb, }; void base_ctrl(void) { static int32_t nick_integral, roll_integral, gier_integral; /* get stick switches & values */ struct rc_values rc_sw; uint32_t count = rcontrol_getswitches(&rc_sw); if (count < 4) { global_state &= ~MOTOR_RUNNING; } else if (global_state & STICK_CALIBRATION_COMPLETE) { // rcontrol_calibrate(RC_CAL_END); // rcontrol_calibrate(RC_CAL_SAVE); rcontrol_print_cal(); global_state &= ~(STICK_CALIBRATION | STICK_CALIBRATION_COMPLETE); } else if (global_state & STICK_CALIBRATION) { /* do nothing during calibration */ } else if (count >= 4) { /* Motor stop */ if (rc_sw.chan[2] < 0 && rc_sw.chan[3] > 0) global_state &= ~MOTOR_RUNNING; /* Motor start */ if (rc_sw.chan[2] < 0 && rc_sw.chan[3] < 0) global_state |= MOTOR_RUNNING; /* Gyro calibration */ if (rc_sw.chan[2] > 0 && rc_sw.chan[3] > 0) adc_calibrate(ADC_CAL_GYRO); /* ACC + Stick calibration */ if (rc_sw.chan[2] > 0 && rc_sw.chan[3] < 0) { adc_calibrate(ADC_CAL_ACC); // rcontrol_calibrate(RC_CAL_START); global_state |= STICK_CALIBRATION; base_ctrl_timer.interval = 1000; pitc_schedule_timer(&base_ctrl_timer); } } struct rc_values rc; rcontrol_getvalues(&rc); if (rc.chan[2] < 15) { global_state &= ~MOTOR_MIXER_ENABLE; /* reset integrals */ nick_integral = roll_integral = gier_integral = 0; pid_nick.err_sum = pid_roll.err_sum = pid_gier.err_sum = 0; } else { global_state |= MOTOR_MIXER_ENABLE; } /* get adc results */ static int16_t adc_result[7]; TDC_PTR(adc_result0, &adc_result[ADC_GYRO_NICK], "ADC_GYRO_NICK", int16_t, TDC_SIGNED | TDC_READONLY); TDC_PTR(adc_result1, &adc_result[ADC_GYRO_ROLL], "ADC_GYRO_ROLL", int16_t, TDC_SIGNED | TDC_READONLY); TDC_PTR(adc_result2, &adc_result[ADC_GYRO_GIER], "ADC_GYRO_GIER", int16_t, TDC_SIGNED | TDC_READONLY); TDC_PTR(adc_result3, &adc_result[ADC_ACC_NICK], "ADC_ACC_NICK", int16_t, TDC_SIGNED | TDC_READONLY); TDC_PTR(adc_result4, &adc_result[ADC_ACC_ROLL], "ADC_ACC_ROLL", int16_t, TDC_SIGNED | TDC_READONLY); TDC_PTR(adc_result5, &adc_result[ADC_ACC_GIER], "ADC_ACC_GIER", int16_t, TDC_SIGNED | TDC_READONLY); TDC_PTR(adc_result6, &adc_result[ADC_VOLTAGE], "ADC_VOLTAGE", int16_t, TDC_SIGNED | TDC_READONLY); adc_get_results(adc_result); if (count != 0 && adc_result[ADC_VOLTAGE] > 960) *AT91C_PIOA_CODR = LED_GREEN; else *AT91C_PIOA_SODR = LED_GREEN; nick_integral += adc_result[ADC_GYRO_NICK]; TDC_INT32(nick_integral, "flctrl: Base Integral Nick"); roll_integral += adc_result[ADC_GYRO_ROLL]; TDC_INT32(roll_integral, "flctrl: Base Integral Roll"); gier_integral += adc_result[ADC_GYRO_GIER]; TDC_INT32(gier_integral, "flctrl: Base Integral Gier"); static int32_t integral_gyro_mix = 1; TDC_INT32(integral_gyro_mix, "flctrl: Mix Integral/ACC (0-1024)"); static int32_t acc_faktor = 300; TDC_INT32(acc_faktor, "flctrl: Mix Faktor"); /* * 90° -> ADC_ACC_* ~210 * 90° -> Integral ~60000 */ int32_t mix_integral = 1024 - integral_gyro_mix; int32_t mix_acc = integral_gyro_mix * acc_faktor; nick_integral = ((nick_integral * mix_integral) + (adc_result[ADC_ACC_NICK] * mix_acc)) / 1024; roll_integral = ((roll_integral * mix_integral) + (adc_result[ADC_ACC_ROLL] * mix_acc)) / 1024; /* * hoher gyro_faktor -> bessere stabilität, aber langsames zurückkehren nach vollausschlag * niedriger integral_faktor -> geringere abweichungbei einseitigem anheben, aber schwingungsanfälliger */ static int32_t integral_faktor = 256; static int32_t gyro_faktor = 4; TDC_INT32(integral_faktor, "flctrl: Integral Divisior"); TDC_INT32(gyro_faktor, "flctrl: Integral Gyro Faktor /16"); static int32_t nick; static int32_t roll; static int32_t gier; nick = (nick_integral / integral_faktor) + (adc_result[ADC_GYRO_NICK] * gyro_faktor) / 16; roll = (roll_integral / integral_faktor) + (adc_result[ADC_GYRO_ROLL] * gyro_faktor) / 16; gier = (gier_integral / integral_faktor) + (adc_result[ADC_GYRO_GIER] * gyro_faktor) / 16; TDC_INT32_RO(nick, "flctrl: Integral + Gyro Nick"); TDC_INT32_RO(roll, "flctrl: Integral + Gyro Roll"); TDC_INT32_RO(gier, "flctrl: Integral + Gyro Gier"); static int32_t stick_kp = 8; TDC_INT32(stick_kp, "flctrl: Stick-P /16"); static int32_t stick_gas, stick_nick, stick_roll, stick_gier; stick_gas = rc.chan[2]; stick_nick = (rc.chan[0] * stick_kp) / 16; stick_roll = (rc.chan[1] * stick_kp) / 16; stick_gier = rc.chan[3] / 4; TDC_INT32_RO(stick_gas, "Stick Gas"); TDC_INT32_RO(stick_nick, "Stick Nick"); TDC_INT32_RO(stick_roll, "Stick Roll"); TDC_INT32_RO(stick_gier, "Stick Gier"); static int32_t gier_kp = 32; TDC_INT32(gier_kp, "flctrl: Gier-P (/256)"); gier_integral -= gier_kp * stick_gier * abs(stick_gier) / 256; static int32_t mixer_gas, mixer_nick, mixer_roll, mixer_gier; mixer_gas = stick_gas; mixer_nick = pid_ctrl(&pid_nick, stick_nick - nick); mixer_roll = pid_ctrl(&pid_roll, stick_roll - roll); mixer_gier = pid_ctrl(&pid_gier, stick_gier - gier); /* mix gas/nick/roll/gier -> 4 motors */ motor_mixer(mixer_gas, mixer_nick, mixer_roll, mixer_gier); TDC_INT32_RO(mixer_gas, "Mixer Gas"); TDC_INT32_RO(mixer_nick, "Mixer Nick"); TDC_INT32_RO(mixer_roll, "Mixer Roll"); TDC_INT32_RO(mixer_gier, "Mixer Gier"); }