#ifdef __AVR_ATmega1280__ /* APM2_RC.cpp - Radio Control Library for Ardupilot Mega. Arduino Code by Jordi Muņoz and Jose Julio. DIYDrones.com This library is free software; you can redistribute it and / or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. RC Input : PPM signal on IC4 pin RC Output : 11 Servo outputs (standard 20ms frame) Methods: Init() : Initialization of interrupts an Timers OutpuCh(ch, pwm) : Output value to servos (range : 900 - 2100us) ch = 0..10 InputCh(ch) : Read a channel input value. ch = 0..7 GetState() : Returns the state of the input. 1 => New radio frame to process Automatically resets when we call InputCh to read channels */ #include "APM2_RC.h" #define REVERSE 3050 // Variable definition for Input Capture interrupt volatile uint16_t ICR4_old; volatile uint8_t PPM_Counter = 0; volatile uint16_t raw[8] = {1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200}; // Constructors //////////////////////////////////////////////////////////////// APM2_RC::APM2_RC() { _direction_mask = 255; // move to super class } void APM2_RC::init() { // Init PWM Timer 1 pinMode(11, OUTPUT); // (PB5 / OC1A) pinMode(12, OUTPUT); // OUT2 (PB6 / OC1B) pinMode(13, OUTPUT); // OUT3 (PB7 / OC1C) // Timer 3 pinMode(2, OUTPUT); // OUT7 (PE4 / OC3B) pinMode(3, OUTPUT); // OUT6 (PE5 / OC3C) pinMode(4, OUTPUT); // (PE3 / OC3A) // Timer 5 pinMode(44, OUTPUT); // OUT1 (PL5 / OC5C) pinMode(45, OUTPUT); // OUT0 (PL4 / OC5B) pinMode(46, OUTPUT); // (PL3 / OC5A) // Init PPM input and PWM Timer 4 pinMode(49, INPUT); // ICP4 pin (PL0) (PPM input) pinMode(7, OUTPUT); // OUT5 (PH4 / OC4B) pinMode(8, OUTPUT); // OUT4 (PH5 / OC4C) //Remember the registers not declared here remains zero by default... TCCR1A =((1 << WGM11) | (1 << COM1A1) | (1 << COM1B1) | (1 << COM1C1)); // Please read page 131 of DataSheet, we are changing the registers settings of WGM11, COM1B1, COM1A1 to 1 thats all... TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11); // Prescaler set to 8, that give us a resolution of 0.5us, read page 134 of data sheet OCR1A = 3000; // PB5, none //OCR1B = 3000; // PB6, OUT2 //OCR1C = 3000; // PB7 OUT3 ICR1 = 40000; // 50hz freq...Datasheet says (system_freq / prescaler) / target frequency. So (16000000hz / 8) / 50hz = 40000, // Init PWM Timer 3 TCCR3A =((1 << WGM31) | (1 << COM3A1) | (1 << COM3B1) | (1 << COM3C1)); TCCR3B = (1 << WGM33) | (1 << WGM32) | (1 << CS31); OCR3A = 3000; // PE3, NONE //OCR3B = 3000; // PE4, OUT7 //OCR3C = 3000; // PE5, OUT6 ICR3 = 40000; // 50hz freq // Init PWM Timer 5 TCCR5A =((1 << WGM51) | (1 << COM5A1) | (1 << COM5B1) | (1 << COM5C1)); TCCR5B = (1 << WGM53) | (1 << WGM52) | (1 << CS51); OCR5A = 3000; // PL3, //OCR5B = 3000; // PL4, OUT0 //OCR5C = 3000; // PL5, OUT1 ICR5 = 40000; // 50hz freq // Init PPM input and PWM Timer 4 TCCR4A = ((1 << WGM40) | (1 << WGM41) | (1 << COM4C1) | (1 << COM4B1) | (1 << COM4A1)); TCCR4B = ((1 << WGM43) | (1 << WGM42) | (1 << CS41) | (1 << ICES4)); OCR4A = 40000; // /50hz freq. //OCR4B = 3000; // PH4, OUT5 //OCR4C = 3000; // PH5, OUT4 //TCCR4B |=(1<= radio_trim[i]) servo_in[i] = (float)(radio_in[i] - radio_min[i]) / (float)(radio_max[i] - radio_min[i]) * 100.0; else servo_in[i] = (float)(radio_in[i] - radio_trim[i]) / (float)(radio_trim[i] - radio_min[i]) * 100.0; } servo_in[CH3] = (float)(radio_in[CH3] - radio_min[CH3]) / (float)(radio_max[CH3] - radio_min[CH3]) * 100.0; servo_in[CH3] = constrain(servo_out[CH3], 0, 100); } void APM2_RC::output() { uint16_t out; for (uint8_t i = 0; i < 8; i++){ if (i == 3) continue; if(radio_in[i] >= radio_trim[i]) out = ((servo_in[i] * (radio_max[i] - radio_trim[i])) / 100) + radio_trim[i]; else out = ((servo_in[i] * (radio_max[i] - radio_trim[i])) / 100) + radio_trim[i]; set_ch_pwm(i, out); } out = (servo_out[CH3] * (float)(radio_max[CH3] - radio_min[CH3])) / 100.0; out += radio_min[CH3]; set_ch_pwm(CH3, out); } void APM2_RC::trim() { uint8_t temp = _mix_mode; _mix_mode = 0; read(); _mix_mode = temp; // Store the trim values // --------------------- for (int y = 0; y < 8; y++) radio_trim[y] = radio_in[y]; } void APM2_RC::twitch_servos(uint8_t times) { // todo } void APM2_RC::set_ch_pwm(uint8_t ch, uint16_t pwm) { //pwm = constrain(pwm, MIN_PULSEWIDTH, MAX_PULSEWIDTH); switch(ch){ case 0: //Serial.print("\tpwm out "); //Serial.print(pwm,DEC); if((_direction_mask & 1) == 0 ) pwm = REVERSE - pwm; //Serial.print("\tpwm out "); //Serial.println(pwm,DEC); OCR5B = pwm << 1; break; // ch0 case 1: if((_direction_mask & 2) == 0 ) pwm = REVERSE - pwm; OCR5C = pwm << 1; break; // ch0 case 2: if((_direction_mask & 4) == 0 ) pwm = REVERSE - pwm; OCR1B = pwm << 1; break; // ch0 case 3: if((_direction_mask & 8) == 0 ) pwm = REVERSE - pwm; OCR1C = pwm << 1; break; // ch0 case 4: if((_direction_mask & 16) == 0 ) pwm = REVERSE - pwm; OCR4C = pwm << 1; break; // ch0 case 5: if((_direction_mask & 32) == 0 ) pwm = REVERSE - pwm; OCR4B = pwm << 1; break; // ch0 case 6: if((_direction_mask & 64) == 0 ) pwm = REVERSE - pwm; OCR3C = pwm << 1; break; // ch0 case 7: if((_direction_mask & 128) == 0 ) pwm = REVERSE - pwm; OCR3B = pwm << 1; break; // ch0 case 8: OCR5A = pwm << 1; break; // ch0 case 9: OCR1A = pwm << 1; break; // ch0 case 10: OCR3A = pwm << 1; break; // ch0 } } /**************************************************** Input Capture Interrupt ICP4 => PPM signal read ****************************************************/ ISR(TIMER4_CAPT_vect) { uint16_t pulse; uint16_t pulse_width; pulse = ICR4; if (pulse < ICR4_old){ // Take care of the overflow of Timer4 (TOP = 40000) pulse_width = (pulse + 40000) - ICR4_old; // Calculating pulse }else{ pulse_width = pulse - ICR4_old; // Calculating pulse } ICR4_old = pulse; if (pulse_width > 8000){ // SYNC pulse PPM_Counter = 0; } else { //PPM_Counter &= 0x07; // For safety only (limit PPM_Counter to 7) raw[PPM_Counter++] = pulse_width >> 1; // Saving pulse. } } // InstantPWM implementation // This function forces the PWM output (reset PWM) on Out0 and Out1 (Timer5). For quadcopters use void APM2_RC::force_out_0_1(void) { if (TCNT5 > 5000) // We take care that there are not a pulse in the output TCNT5 = 39990; // This forces the PWM output to reset in 5us (10 counts of 0.5us). The counter resets at 40000 } // This function forces the PWM output (reset PWM) on Out2 and Out3 (Timer1). For quadcopters use void APM2_RC::force_out_2_3(void) { if (TCNT1 > 5000) TCNT1 = 39990; } // This function forces the PWM output (reset PWM) on Out6 and Out7 (Timer3). For quadcopters use void APM2_RC::force_out_6_7(void) { if (TCNT3 > 5000) TCNT3 = 39990; } #endif