diff --git a/radio/src/Makefile b/radio/src/Makefile index f3dcf7d74..23ab423ab 100644 --- a/radio/src/Makefile +++ b/radio/src/Makefile @@ -1058,7 +1058,7 @@ ifeq ($(GUI), YES) CPPDEFS += -DGUI endif -CPPSRC += opentx.cpp functions.cpp strhelpers.cpp $(PULSESSRC) switches.cpp curves.cpp mixer.cpp stamp.cpp $(GUISRC) $(EEPROMSRC) timers.cpp +CPPSRC += opentx.cpp functions.cpp strhelpers.cpp $(PULSESSRC) switches.cpp curves.cpp mixer.cpp stamp.cpp $(GUISRC) $(EEPROMSRC) timers.cpp trainer_input.cpp ifeq ($(GUI), YES) GUISRC += gui/$(GUIDIRECTORY)/lcd.cpp gui/$(GUIDIRECTORY)/splash.cpp gui/$(GUIDIRECTORY)/fonts.cpp diff --git a/radio/src/audio_arm.cpp b/radio/src/audio_arm.cpp index f6e20e8c1..721fe71d0 100644 --- a/radio/src/audio_arm.cpp +++ b/radio/src/audio_arm.cpp @@ -169,6 +169,8 @@ const char * const audioFilenames[] = { "swr_red", "telemko", "telemok", + "trainko", + "trainok", #if defined(PCBSKY9X) "highmah", "hightemp", diff --git a/radio/src/audio_arm.h b/radio/src/audio_arm.h index 27ba3c8b2..c60f774cf 100644 --- a/radio/src/audio_arm.h +++ b/radio/src/audio_arm.h @@ -331,6 +331,8 @@ void audioStart(); #define AUDIO_RXBATT_RED() audioEvent(AU_RXBATT_RED) #define AUDIO_TELEMETRY_LOST() audioEvent(AU_TELEMETRY_LOST) #define AUDIO_TELEMETRY_BACK() audioEvent(AU_TELEMETRY_BACK) +#define AUDIO_TRAINER_LOST() audioEvent(AU_TRAINER_LOST) +#define AUDIO_TRAINER_BACK() audioEvent(AU_TRAINER_BACK) #define AUDIO_HEARTBEAT() diff --git a/radio/src/mixer.cpp b/radio/src/mixer.cpp index 8ea95926a..133d89909 100644 --- a/radio/src/mixer.cpp +++ b/radio/src/mixer.cpp @@ -455,7 +455,7 @@ void evalInputs(uint8_t mode) } #endif - if (mode <= e_perout_mode_inactive_flight_mode && isFunctionActive(FUNCTION_TRAINER+ch) && ppmInValid) { + if (mode <= e_perout_mode_inactive_flight_mode && isFunctionActive(FUNCTION_TRAINER+ch) && IS_TRAINER_INPUT_VALID()) { // trainer mode TrainerMix* td = &g_eeGeneral.trainer.mix[ch]; if (td->mode) { @@ -662,7 +662,7 @@ void evalFlightModeMixes(uint8_t mode, uint8_t tick10ms) #define MIXER_LINE_DISABLE() (mixCondition = true, mixEnabled = 0) - if (mixEnabled && md->srcRaw >= MIXSRC_FIRST_TRAINER && md->srcRaw <= MIXSRC_LAST_TRAINER && !ppmInValid) { + if (mixEnabled && md->srcRaw >= MIXSRC_FIRST_TRAINER && md->srcRaw <= MIXSRC_LAST_TRAINER && !IS_TRAINER_INPUT_VALID()) { MIXER_LINE_DISABLE(); } diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 52e509be2..ec6db1eed 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -155,7 +155,7 @@ void per10ms() #endif if (trimsCheckTimer) trimsCheckTimer--; - if (ppmInValid) ppmInValid--; + if (g_ppmInputValidityTimer) g_ppmInputValidityTimer--; #if defined(CPUARM) if (trimsDisplayTimer) @@ -1714,6 +1714,7 @@ void doMixerCalculations() s_cnt_1s += 1; logicalSwitchesTimerTick(); + checkTrainerSignalWarning(); if (s_cnt_1s >= 10) { // 1sec s_cnt_1s -= 10; @@ -2068,10 +2069,6 @@ void checkBattery() } } -int16_t g_ppmIns[NUM_TRAINER]; -uint8_t ppmInState = 0; // 0=unsync 1..8= wait for value i-1 -uint8_t ppmInValid = 0; - #if !defined(SIMU) && !defined(CPUARM) volatile uint8_t g_tmr16KHz; //continuous timer 16ms (16MHz/1024/256) -- 8-bit counter overflow @@ -2153,27 +2150,8 @@ ISR(TIMER3_CAPT_vect) // G: High frequency noise can cause stack overflo with IS PAUSE_PPMIN_INTERRUPT(); sei(); // enable other interrupts - uint16_t val = (capture - lastCapt) / 2; - - // G: We process g_ppmIns immediately here, to make servo movement as smooth as possible - // while under trainee control - if (val>4000 && val < 16000) { // G: Prioritize reset pulse. (Needed when less than 8 incoming pulses) - ppmInState = 1; // triggered - } - else { - if (ppmInState>0 && ppmInState<=8) { - if (val>800 && val<2200) { // if valid pulse-width range - ppmInValid = PPM_IN_VALID_TIMEOUT; - g_ppmIns[ppmInState++ - 1] = (int16_t)(val - 1500) * (uint8_t)(g_eeGeneral.PPM_Multiplier+10)/10; //+-500 != 512, but close enough. - } - else { - ppmInState = 0; // not triggered - } - } - } - - lastCapt = capture; - + captureTrainerPulses(capture); + cli(); // disable other interrupts for stack pops before this function's RETI RESUME_PPMIN_INTERRUPT(); } diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 49bb990ae..087798f40 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -1127,10 +1127,8 @@ extern uint8_t g_vbat100mV; extern uint8_t g_beepCnt; extern uint8_t g_beepVal[5]; -extern uint8_t ppmInState; //0=unsync 1..8= wait for value i-1 -extern uint8_t ppmInValid; -#define PPM_IN_VALID_TIMEOUT 100 -extern int16_t g_ppmIns[NUM_TRAINER]; +#include "trainer_input.h" + extern int32_t chans[NUM_CHNOUT]; extern int16_t ex_chans[NUM_CHNOUT]; // Outputs (before LIMITS) of the last perMain extern int16_t channelOutputs[NUM_CHNOUT]; @@ -1392,6 +1390,8 @@ enum AUDIO_SOUNDS { AU_SWR_RED, AU_TELEMETRY_LOST, AU_TELEMETRY_BACK, + AU_TRAINER_LOST, + AU_TRAINER_BACK, #endif #if defined(PCBSKY9X) AU_TX_MAH_HIGH, diff --git a/radio/src/sbus.cpp b/radio/src/sbus.cpp index 1cb813481..19d2e9465 100644 --- a/radio/src/sbus.cpp +++ b/radio/src/sbus.cpp @@ -87,7 +87,7 @@ void processSbusFrame(uint8_t *sbus, int16_t *pulses, uint32_t size) inputbits >>= SBUS_CH_BITS; } - ppmInValid = PPM_IN_VALID_TIMEOUT; + g_ppmInputValidityTimer = PPM_IN_VALID_TIMEOUT; } void processSbusInput() diff --git a/radio/src/targets/sky9x/board_sky9x.cpp b/radio/src/targets/sky9x/board_sky9x.cpp index dcbdac3bf..c7b1ab771 100644 --- a/radio/src/targets/sky9x/board_sky9x.cpp +++ b/radio/src/targets/sky9x/board_sky9x.cpp @@ -248,31 +248,10 @@ void start_timer4() extern "C" void TC3_IRQHandler() //capture ppm in at 2MHz { - static uint16_t lastCapt ; - uint16_t capture = TC1->TC_CHANNEL[0].TC_RA ; (void) TC1->TC_CHANNEL[0].TC_SR ; // Acknowledge the interrupt - uint16_t val = ((uint16_t)(capture - lastCapt)) / 2; - - // G: We process g_ppmIns immediately here, to make servo movement as smooth as possible - // while under trainee control - if (val>4000 && val<19000) { // G: Prioritize reset pulse. (Needed when less than 16 incoming pulses) - ppmInState = 1; // triggered - } - else { - if (ppmInState>0 && ppmInState<=16) { - if (val>800 && val<2200) { // if valid pulse-width range - ppmInValid = PPM_IN_VALID_TIMEOUT; - g_ppmIns[ppmInState++ - 1] = (int16_t)(val - 1500)*(g_eeGeneral.PPM_Multiplier+10)/10; //+-500 != 512, but close enough. - } - else { - ppmInState = 0; // not triggered - } - } - } - - lastCapt = capture; + captureTrainerPulses(capture); } void init_trainer_capture() diff --git a/radio/src/targets/taranis/trainer_driver.cpp b/radio/src/targets/taranis/trainer_driver.cpp index 701cfdfdc..295738e8b 100644 --- a/radio/src/targets/taranis/trainer_driver.cpp +++ b/radio/src/targets/taranis/trainer_driver.cpp @@ -121,8 +121,6 @@ void stop_trainer_capture() extern "C" void TIM3_IRQHandler() { uint16_t capture = 0; - static uint16_t lastCapt ; - uint16_t val ; bool doCapture = false ; // What mode? in or out? @@ -139,25 +137,7 @@ extern "C" void TIM3_IRQHandler() } if (doCapture) { - val = (uint16_t)(capture - lastCapt) / 2 ; - lastCapt = capture; - - // We process g_ppmInsright here to make servo movement as smooth as possible - // while under trainee control - if (val>4000 && val<19000) { // G: Prioritize reset pulse. (Needed when less than 16 incoming pulses) - ppmInState = 1; // triggered - } - else { - if (ppmInState>0 && ppmInState<=16) { - if (val>800 && val<2200) { - ppmInValid = PPM_IN_VALID_TIMEOUT; - g_ppmIns[ppmInState++ - 1] = (int16_t)(val - 1500)*(g_eeGeneral.PPM_Multiplier+10)/10; //+-500 != 512, but close enough. - } - else { - ppmInState = 0; // not triggered - } - } - } + captureTrainerPulses(capture); } // PPM out compare interrupt diff --git a/radio/src/trainer_input.cpp b/radio/src/trainer_input.cpp new file mode 100644 index 000000000..83e80cadc --- /dev/null +++ b/radio/src/trainer_input.cpp @@ -0,0 +1,69 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#include "trainer_input.h" + +int16_t g_ppmIns[NUM_TRAINER]; +uint8_t g_ppmInputValidityTimer; + + +#if defined(CPUARM) +#include "audio_arm.h" + +void checkTrainerSignalWarning() +{ + enum PpmInValidState_t { + PPM_IN_IS_NOT_USED=0, + PPM_IN_IS_VALID, + PPM_IN_INVALID + }; + + static uint8_t ppmInputValidState = PPM_IN_IS_NOT_USED; + + if(g_ppmInputValidityTimer && (ppmInputValidState == PPM_IN_IS_NOT_USED)) { + ppmInputValidState = PPM_IN_IS_VALID; + } + else if (!g_ppmInputValidityTimer && (ppmInputValidState == PPM_IN_IS_VALID)) { + ppmInputValidState = PPM_IN_INVALID; + AUDIO_TRAINER_LOST(); + } + else if (g_ppmInputValidityTimer && (ppmInputValidState == PPM_IN_INVALID)) { + ppmInputValidState = PPM_IN_IS_VALID; + AUDIO_TRAINER_BACK(); + } +} + +#endif diff --git a/radio/src/trainer_input.h b/radio/src/trainer_input.h new file mode 100644 index 000000000..9f869a5d3 --- /dev/null +++ b/radio/src/trainer_input.h @@ -0,0 +1,89 @@ +/* + * Authors (alphabetical order) + * - Andre Bernet + * - Andreas Weitl + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * - Cameron Weeks + * - Erez Raviv + * - Gabriel Birkus + * - Jean-Pierre Parisy + * - Karl Szmutny + * - Michael Blandford + * - Michal Hlavinka + * - Pat Mackenzie + * - Philip Moss + * - Rob Thomson + * - Romolo Manfredini + * - Thomas Husterer + * + * opentx is based on code named + * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, + * er9x by Erez Raviv: http://code.google.com/p/er9x/, + * and the original (and ongoing) project by + * Thomas Husterer, th9x: http://code.google.com/p/th9x/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef trainer_input_h +#define trainer_input_h + +#include "myeeprom.h" + +// Trainer input channels +extern int16_t g_ppmIns[NUM_TRAINER]; + +// Timer gets decremented in per10ms() +#define PPM_IN_VALID_TIMEOUT 100 // 1s +extern uint8_t g_ppmInputValidityTimer; + +#define IS_TRAINER_INPUT_VALID() (g_ppmInputValidityTimer != 0) + +#if defined(CPUARM) +void checkTrainerSignalWarning(); +#else +#define checkTrainerSignalWarning() +#endif + +// Needs to be inlined to avoid slow function calls in ISR routines +inline void captureTrainerPulses(uint16_t capture) +{ + static uint16_t lastCapt=0; + static uint8_t channelNumber=0; + + uint16_t val = (uint16_t)(capture - lastCapt) / 2; + lastCapt = capture; + + // We process g_ppmInsright here to make servo movement as smooth as possible + // while under trainee control + // + // G: Prioritize reset pulse. (Needed when less than 16 incoming pulses) + // + if (val>4000 && val<19000) { + channelNumber = 1; // triggered + } + else { + if ((channelNumber > 0) && (channelNumber <= NUM_TRAINER)) { + if (val>800 && val<2200) { + g_ppmInputValidityTimer = PPM_IN_VALID_TIMEOUT; + g_ppmIns[channelNumber++ - 1] = + //+-500 != 512, but close enough. + (int16_t)(val - 1500)*(g_eeGeneral.PPM_Multiplier+10)/10; + } + else { + channelNumber = 0; // not triggered + } + } + } +} + +#endif diff --git a/radio/util/tts.py b/radio/util/tts.py index 0491aa6a7..d0cf30f72 100644 --- a/radio/util/tts.py +++ b/radio/util/tts.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python # -*- coding: utf-8 -*- @@ -186,6 +186,8 @@ def ttsEn(): (u"radio antenna defective", "swr_red", NO_ALTERNATE), (u"telemetry lost", "telemko", NO_ALTERNATE), (u"telemetry recovered", "telemok", NO_ALTERNATE), + (u"trainer signal lost", "trainko", NO_ALTERNATE), + (u"trainer signal recovered", "trainok", NO_ALTERNATE), ]: systemSounds.append((s, filename(f, a))) for i, (s, f) in enumerate([ @@ -282,6 +284,8 @@ def ttsFr(): (u"Antenne défectueuse", "swr_red", NO_ALTERNATE), (u"Plus de télémétrie", "telemko", NO_ALTERNATE), (u"Télémétrie retrouvée", "telemok", NO_ALTERNATE), + (u"Signal écolage perdu", "trainko", NO_ALTERNATE), + (u"Signal écolage retrouvé", "trainok", NO_ALTERNATE), ]: systemSounds.append((s, filename(f, a))) for i, (s, f) in enumerate([ @@ -599,6 +603,10 @@ def ttsDe(): (u"Funksignal schlecht!", "rssi_org", NO_ALTERNATE), (u"Funksignal kritisch!", "rssi_red", NO_ALTERNATE), (u"Problem mit der sender Antenna", "swr_red", NO_ALTERNATE), + (u"Telemetrie verloren", "telemko", NO_ALTERNATE), + (u"Telemetrie wiederhergestellt", "telemok", NO_ALTERNATE), + (u"Schülersignal verloren", "trainko", NO_ALTERNATE), + (u"Schülersignal wiederhergestellt", "trainok", NO_ALTERNATE), ]: systemSounds.append((s, filename(f, a))) for i, s in enumerate(["Timer", "Timer", "Sendung", "Empfang", "A1", "A2", "Hoehe", "Motor",