diff --git a/src/Makefile b/src/Makefile index 0ab74f6ef..0b76a9784 100644 --- a/src/Makefile +++ b/src/Makefile @@ -72,6 +72,22 @@ DECIMALS = NO # Values = YES, NO TRANSLATIONS = YES +# DISPLAY_USER_DATA to display on screen data from FrSky module (testing/demo purpose) +# Values = YES, NO +DISPLAY_USER_DATA = NO + +# PXX (FrSky PCM) protocol +PXX = NO + +# DSM2 (Spektrum) protocol +DSM2 = YES + +# Silver protocol +SILVER = NO + +# CTP-1009 protocol +CTP1009 = NO + #------- END BUILD OPTIONS --------------------------- # MCU name @@ -98,16 +114,12 @@ TARGET = gruvin9x OBJDIR = obj # List C++ source files here. (C dependencies are automatically generated.) -CPPSRC = gruvin9x.cpp stamp.cpp menus.cpp model_menus.cpp general_menus.cpp main_views.cpp statistics_views.cpp pers.cpp file.cpp lcd.cpp drivers.cpp +CPPSRC = gruvin9x.cpp pulses.cpp stamp.cpp menus.cpp model_menus.cpp general_menus.cpp main_views.cpp statistics_views.cpp pers.cpp file.cpp lcd.cpp drivers.cpp ifeq ($(EXT), JETI) CPPSRC += jeti.cpp endif -ifeq ($(EXT), FRSKY) - CPPSRC += frsky.cpp -endif - # Disk IO support (PCB V2+ only) ifneq ($(PCB), STD) CPPSRC += time.cpp @@ -166,10 +178,13 @@ CPPDEFS = -DF_CPU=$(F_CPU)UL # NOTE: PCB version now overrides all the earlier individual settings # These individual settings work only for PCB=STD +CPPDEFS += -DEEPROM_ASYNC_WRITE + ifeq ($(PCB), STD) # STD PCB, so ... - CPPDEFS += -DPCBSTD -DEEPROM_ASYNC_WRITE + CPPDEFS += -DPCBSTD + # If Hardware PPM mode ( PB0<->BP7) switch the Backlight output with the original PPM to use hardware facility to generate precise PPM (hardware mods) # G: TODO This prevents HARDPPM being used with FRSKY. HARDPPM needs its own option XXX ifeq ($(EXT), HARDPPM) @@ -184,11 +199,13 @@ ifeq ($(PCB), STD) # If FRSKY-Support is enabled ifeq ($(EXT), FRSKY) CPPDEFS += -DFRSKY -DFRSKY_HUB + CPPSRC += frsky.cpp endif # If FRSKY-Support is enabled ifeq ($(EXT), FRSKY_NOHUB) CPPDEFS += -DFRSKY + CPPSRC += frsky.cpp endif # gruvin: If buzzer speaker replacment supported @@ -254,6 +271,25 @@ ifeq ($(TEMPLATES), YES) CPPSRC += templates.cpp endif +ifeq ($(DISPLAY_USER_DATA), YES) + CPPDEFS += -DDISPLAY_USER_DATA +endif + +ifeq ($(PXX), YES) + CPPDEFS += -DPXX +endif + +ifeq ($(DSM2), YES) + CPPDEFS += -DDSM2 +endif + +ifeq ($(SILVER), YES) + CPPDEFS += -DSILVER +endif + +ifeq ($(CTP1009), YES) + CPPDEFS += -DCTP1009 +endif #---------------- Compiler Options C ---------------- # -g*: generate debugging information @@ -661,7 +697,10 @@ else endif simu: $(CPPSRC) Makefile simu.cpp $(CPPSRC) simpgmspace.cpp *.h *.lbm eeprom.bin - g++ simu.cpp $(CPPFLAGS) $(CPPSRC) simpgmspace.cpp $(ARCH) -o simu $(FOXINC) $(FOXLIB) -MD -DSIMU + g++ simu.cpp $(CPPFLAGS) $(CPPSRC) simpgmspace.cpp $(ARCH) -MD -DSIMU -o simu $(FOXINC) $(FOXLIB) + +gruvin9x.so: $(CPPSRC) Makefile export.cpp $(CPPSRC) simpgmspace.cpp *.h *.lbm + g++ export.cpp $(CPPFLAGS) $(CPPSRC) simpgmspace.cpp $(ARCH) -g -MD -DSIMU -shared -fPIC -Wl,-soname,$@ -o $@ eeprom.bin: dd if=/dev/zero of=$@ bs=1 count=2048 diff --git a/src/export.cpp b/src/export.cpp new file mode 100644 index 000000000..31261c7e5 --- /dev/null +++ b/src/export.cpp @@ -0,0 +1,61 @@ +/* + * Authors (alphabetical order) + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * + * Original contributors + * - Philip Moss Adapted first frsky functions from jeti.cpp code by + * - Karl Szmutny + + * 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 "simpgmspace.h" +#include "lcd.h" +#include "gruvin9x.h" +#include "menus.h" + +int16_t g_anas[NUM_STICKS+NUM_POTS]; +uint8_t *g_switches[MAX_PSWITCH]; +extern uint8_t eeprom[EESIZE]; + +extern "C" void initGruvin9x(uint8_t *_eeprom) +{ + eepromFile = NULL; // in memory + memcpy(eeprom, _eeprom, sizeof(eeprom)); + + g_menuStack[0] = menuMainView; + g_menuStack[1] = menuProcModelSelect; + eeReadAll(); //load general setup and selected model + // TODO + //checkLowEEPROM(); //enough eeprom free? + //checkTHR(); + //checkSwitches(); //must be last + //state = 2; +} + +extern "C" void runGruvin9x(int16_t *anas, uint8_t *switches, int16_t *chans, uint8_t *display) +{ + memcpy(g_anas, anas, sizeof(uint16_t) * NUM_STICKS+NUM_POTS); + memcpy(g_switches, switches, sizeof(uint8_t) * MAX_PSWITCH); + + g_tmr10ms++; + perMain(); + + memcpy(chans, g_chans512, sizeof(g_chans512)); + memcpy(display, displayBuf, sizeof(displayBuf)); +} + +uint16_t anaIn(uint8_t chan) +{ + // printf("anaIn(%d) => %d\n", chan, g_anas[chan]); fflush(stdout); + return g_anas[chan]; +} diff --git a/src/gruvin9x.cpp b/src/gruvin9x.cpp index 646db5dbd..b40450d7b 100644 --- a/src/gruvin9x.cpp +++ b/src/gruvin9x.cpp @@ -55,6 +55,8 @@ uint8_t toneOn = false; bool warble = false; +uint8_t heartbeat; + const prog_char APM modi12x3[]= "RUD ELE THR AIL " "RUD THR ELE AIL " @@ -295,8 +297,8 @@ void applyExpos(int16_t *anas) } } -static bool s_noStickInputs = false; -inline int16_t getValue(uint8_t i) +bool s_noStickInputs = false; +inline int16_t __attribute__ ((always_inline)) getValue(uint8_t i) { if(i CHOUT_BASE+NUM_CHNOUT) + y = 125+cs.v2; + else +#endif + y = calc100toRESX(cs.v2); + + switch (cs.func) { + case CS_VPOS: + result = (x>y); + break; + case CS_VNEG: + result = (xy); + break; + // case CS_ANEG: + default: + result = (abs(x)y); + break; + case CS_LESS: + result = (x=y); + break; + // case CS_ELESS: + default: + result = (x<=y); + } + } + } + } + + return swtch > 0 ? result : !result; +} + +bool getSwitch(int8_t swtch, bool nc) +{ + s_last_switch_used = (nc<<15); + return __getSwitch(swtch); +} + uint8_t getFlightPhase() { for (uint8_t i=1; i5) return false; //prevent recursive loop going too deep - - switch(swtch){ - case 0: return nc; - case MAX_SWITCH: return true; - case -MAX_SWITCH: return false; - } - - uint8_t dir = swtch>0; - if(abs(swtch)<(MAX_SWITCH-NUM_CSW)) { - if(!dir) return ! keyState((EnumKeys)(SW_BASE-swtch-1)); - return keyState((EnumKeys)(SW_BASE+swtch-1)); - } - - //custom switch, Issue 78 - //use putsChnRaw - //input -> 1..4 -> sticks, 5..8 pots - //MAX,FULL - disregard - //ppm - CustomSwData &cs = g_model.customSw[abs(swtch)-(MAX_SWITCH-NUM_CSW)]; - if(!cs.func) return false; - - - int8_t a = cs.v1; - int8_t b = cs.v2; - int16_t x = 0; - int16_t y = 0; - - // init values only if needed - uint8_t s = CS_STATE(cs.func); - if(s == CS_VOFS) - { - x = getValue(cs.v1-1); -#ifdef FRSKY - if (cs.v1 > CHOUT_BASE+NUM_CHNOUT) - y = 125+cs.v2; - else -#endif - y = calc100toRESX(cs.v2); - } - else if(s == CS_VCOMP) - { - x = getValue(cs.v1-1); - y = getValue(cs.v2-1); - } - - switch (cs.func) { - case (CS_VPOS): - return swtch>0 ? (x>y) : !(x>y); - break; - case (CS_VNEG): - return swtch>0 ? (xy) ; - return swtch>0 ? res : !res ; - } - break; - case (CS_ANEG): - { - bool res = (abs(x)0 ? res : !res ; - } - break; - - case (CS_AND): - case (CS_OR): - case (CS_XOR): - { - bool res1 = getSwitch(a,0,level+1) ; - bool res2 = getSwitch(b,0,level+1) ; - if ( cs.func == CS_AND ) - { - return res1 && res2 ; - } - else if ( cs.func == CS_OR ) - { - return res1 || res2 ; - } - else // CS_XOR - { - return res1 ^ res2 ; - } - } - break; - case (CS_EQUAL): - return (x==y); - case (CS_NEQUAL): - return (x!=y); - case (CS_GREATER): - return (x>y); - case (CS_LESS): - return (x=y); - case (CS_ELESS): - return (x<=y); - default: - return false; - } -} - - -//#define CS_EQUAL 8 -//#define CS_NEQUAL 9 -//#define CS_GREATER 10 -//#define CS_LESS 11 -//#define CS_EGREATER 12 -//#define CS_ELESS 13 - #if defined (PCBV3) && !defined (PCBV4) // The ugly scanned keys thing should be gone for PCBV4+. In the meantime ... uint8_t keyDown() @@ -1084,7 +1085,6 @@ uint16_t isqrt32(uint32_t n) // static variables used in perOut - moved here so they don't interfere with the stack // It's also easier to initialize them here. -uint16_t pulses2MHz[120] = {0}; int16_t anas [NUM_XCHNRAW] = {0}; int16_t trims[NUM_STICKS] = {0}; int32_t chans[NUM_CHNOUT] = {0}; @@ -1654,70 +1654,6 @@ uint8_t ppmInState = 0; //0=unsync 1..8= wait for value i-1 #ifndef SIMU -#define HEART_TIMER2Mhz 1; -#define HEART_TIMER10ms 2; - -uint8_t heartbeat; - - -ISR(TIMER1_COMPA_vect) //2MHz pulse generation -{ - static uint8_t pulsePol; - static uint16_t *pulsePtr = pulses2MHz; - - // TODO understand what Bryan did here - uint8_t i = 0; - while((TCNT1L < 10) && (++i < 50)) // Timer does not read too fast, so i - ; - uint16_t dt=TCNT1;//-OCR1A; - -//vinceofdrink@gmail harwared ppm -//Orginal bitbang for PPM -#ifndef DPPMPB7_HARDWARE - if(pulsePol) - { - PORTB |= (1< inline t abs(t a){ return a>0?a:-a; } +template inline t __attribute__ ((always_inline)) abs(t a){ return a>0?a:-a; } /// liefert das Minimum der Argumente template inline t min(t a, t b){ return a inline t max(t a, t b){ return a>b?a:b; } template inline int8_t sgn(t a){ return a>0 ? 1 : (a < 0 ? -1 : 0); } +template inline t limit(t mi, t x, t ma){ return min(max(mi,x),ma); } /// Markiert einen EEPROM-Bereich als dirty. der Bereich wird dann in /// eeCheck ins EEPROM zurueckgeschrieben. @@ -602,9 +609,6 @@ extern inline uint16_t get_tmr10ms() #define SUB_MODE_H_DBL 3 void setupPulses(); -void setupPulsesPPM(); -void setupPulsesSilver(); -void setupPulsesTracerCtp1009(); void initTemplates(); @@ -638,7 +642,6 @@ extern volatile uint8_t g_blinkTmr10ms; extern uint8_t g_beepCnt; extern uint8_t g_beepVal[5]; extern const PROGMEM char modi12x3[]; -extern uint16_t pulses2MHz[120]; extern int16_t g_ppmIns[8]; extern int16_t g_chans512[NUM_CHNOUT]; extern volatile uint8_t tick10ms; diff --git a/src/gtests.cpp b/src/gtests.cpp index ac6427b32..744666994 100644 --- a/src/gtests.cpp +++ b/src/gtests.cpp @@ -234,6 +234,22 @@ TEST(FrSky, dateNtime) { EXPECT_EQ(frskyHubData.sec, 50); } +TEST(getSwitch, undefCSW) { + memset(&g_model, 0, sizeof(g_model)); + EXPECT_EQ(getSwitch(MAX_SWITCH-NUM_CSW, 0), false); + EXPECT_EQ(getSwitch(-(MAX_SWITCH-NUM_CSW), 0), false); +} + +TEST(getSwitch, circularCSW) { + memset(&g_model, 0, sizeof(g_model)); + g_model.customSw[0] = { MAX_SWITCH-NUM_CSW, MAX_SWITCH-NUM_CSW, CS_OR }; + g_model.customSw[1] = { MAX_SWITCH-NUM_CSW, MAX_SWITCH-NUM_CSW, CS_AND }; + EXPECT_EQ(getSwitch(MAX_SWITCH-NUM_CSW, 0), false); + EXPECT_EQ(getSwitch(-(MAX_SWITCH-NUM_CSW), 0), true); + EXPECT_EQ(getSwitch(1+MAX_SWITCH-NUM_CSW, 0), false); + EXPECT_EQ(getSwitch(-(1+MAX_SWITCH-NUM_CSW), 0), true); +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/src/lcd.cpp b/src/lcd.cpp index 1b5db196c..80b8f319c 100644 --- a/src/lcd.cpp +++ b/src/lcd.cpp @@ -275,7 +275,7 @@ void lcd_outdezNAtt(uint8_t x, uint8_t y, int16_t val, uint8_t flags, uint8_t le void lcd_mask(uint8_t *p, uint8_t mask, uint8_t att) { - assert(p < DISPLAY_END); + assert(p >= displayBuf && p < DISPLAY_END); if (att & BLACK) *p |= mask; @@ -326,13 +326,13 @@ void lcd_vlineStip(uint8_t x, int8_t y, int8_t h, uint8_t pat) uint8_t *p = &displayBuf[ y / 8 * DISPLAY_W + x ]; y = y % 8; if (y) { - assert(p < DISPLAY_END); + assert(p >= displayBuf && p < DISPLAY_END); *p ^= ~(BITMASK(y)-1) & pat; p += DISPLAY_W; h -= 8-y; } while (h>0) { - assert(p < DISPLAY_END); + assert(p >= displayBuf && p < DISPLAY_END); *p ^= pat; p += DISPLAY_W; h -= 8; @@ -340,7 +340,7 @@ void lcd_vlineStip(uint8_t x, int8_t y, int8_t h, uint8_t pat) h = (h+8) % 8; if (h) { p -= DISPLAY_W; - assert(p < DISPLAY_END); + assert(p >= displayBuf && p < DISPLAY_END); *p ^= ~(BITMASK(h)-1) & pat; } } @@ -360,10 +360,11 @@ void lcd_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t pat, uint8_t a } } -void lcd_filled_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t att) +void lcd_filled_rect(uint8_t x, int8_t y, uint8_t w, uint8_t h, uint8_t att) { - for (uint8_t i=y; i=0 && i<64) lcd_hline(x, i, w, att); + } } void putsTime(uint8_t x,uint8_t y,int16_t tme,uint8_t att,uint8_t att2) diff --git a/src/lcd.h b/src/lcd.h index 8b76358ca..991d0b265 100644 --- a/src/lcd.h +++ b/src/lcd.h @@ -81,6 +81,8 @@ extern void lcd_outdezAtt(uint8_t x, uint8_t y, int16_t val, uint8_t mode=0); extern void lcd_outdezNAtt(uint8_t x, uint8_t y, int16_t val, uint8_t mode=0, uint8_t len=0); extern void lcd_outdez8(uint8_t x, uint8_t y, int8_t val); +extern void putsStrIdx(uint8_t x, uint8_t y, const prog_char *str, uint8_t idx, uint8_t att=0); + extern void putsModelName(uint8_t x, uint8_t y, char *name, uint8_t id, uint8_t att); extern void putsSwitches(uint8_t x, uint8_t y, int8_t swtch, uint8_t att=0); extern void putsFlightPhase(uint8_t x, uint8_t y, int8_t idx, uint8_t att=0); @@ -107,7 +109,7 @@ extern void lcd_vline(uint8_t x, int8_t y, int8_t h); extern void lcd_vlineStip(uint8_t x, int8_t y, int8_t h, uint8_t pat); extern void lcd_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t pat=0xff, uint8_t att=0); -extern void lcd_filled_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t att=0); +extern void lcd_filled_rect(uint8_t x, int8_t y, uint8_t w, uint8_t h, uint8_t att=0); inline void lcd_square(uint8_t x, uint8_t y, uint8_t w, uint8_t att=0) { lcd_rect(x, y, w, w, 0xff, att); } #define DO_CROSS(xx,yy,ww) \ diff --git a/src/menus.cpp b/src/menus.cpp index e9c7af6bc..d9cd84d82 100644 --- a/src/menus.cpp +++ b/src/menus.cpp @@ -303,215 +303,3 @@ void pushMenu(MenuFuncP newMenu) g_menuStack[g_menuStackPtr] = newMenu; (*newMenu)(EVT_ENTRY); } - -/****************************************************************************** - the functions below are from int-level - the functions below are from int-level - the functions below are from int-level -******************************************************************************/ - -void setupPulses() -{ - switch(g_model.protocol) - { - case PROTO_PPM: - setupPulsesPPM(); - break; - case PROTO_SILV_A: - case PROTO_SILV_B: - case PROTO_SILV_C: - setupPulsesSilver(); - break; - case PROTO_TRACER_CTP1009: - setupPulsesTracerCtp1009(); - break; - } -} - -//inline int16_t reduceRange(int16_t x) // for in case we want to have room for subtrims -//{ -// return x-(x/4); //512+128 =? 640, 640 - 640/4 == 640 * 3/4 => 480 (just below 500msec - it can still reach 500 with offset) -//} - -void setupPulsesPPM() // changed 10/05/2010 by dino Issue 128 -{ -#define PPM_CENTER 1200*2 - int16_t PPM_range = g_model.extendedLimits ? 640*2 : 512*2; //range of 0.7..1.7msec - - //Total frame length = 22.5msec - //each pulse is 0.7..1.7ms long with a 0.3ms stop tail - //The pulse ISR is 2mhz so everything is multiplied by 2 - - // G: Found the following reference at th9x. The below code does not seem - // to produce quite exactly this, to my eye. *shrug* - // http://www.aerodesign.de/peter/2000/PCM/frame_ppm.gif - - uint8_t j=0; - uint8_t p=8+g_model.ppmNCH*2; //Channels *2 - uint16_t q=(g_model.ppmDelay*50+300)*2; //Stoplen *2 - uint16_t rest=22500u*2-q; //Minimum Framelen=22.5 ms - if(p>9) rest=p*(1720u*2 + q) + 4000u*2; //for more than 9 channels, frame must be longer - for(uint8_t i=0;i>2 -m1 -m2>>2 -m2 -//<= 500us Probleme -//>= 650us Probleme -//periode orig: 450ms -void setupPulsesSilver() -{ - int8_t chan=1; //chan 1=C 2=B 0=A? - - switch(g_model.protocol) - { - case PROTO_SILV_A: chan=0; break; - case PROTO_SILV_B: chan=2; break; - case PROTO_SILV_C: chan=1; break; - } - - int8_t m1 = (uint16_t)(g_chans512[0]+1024)*2 / 256; - int8_t m2 = (uint16_t)(g_chans512[1]+1024)*2 / 256; - if (m1 < 0) m1=0; - if (m2 < 0) m2=0; - if (m1 > 15) m1=15; - if (m2 > 15) m2=15; - if (m2 > m1+9) m1=m2-9; - if (m1 > m2+9) m2=m1-9; - //uint8_t i=0; - pulses2MHzPtr=pulses2MHz; - send_hilo_silv(5,1); //idx 0 erzeugt pegel=0 am Ausgang, wird als high gesendet - send2BitsSilv(0); - send_hilo_silv(2,1); - send_hilo_silv(2,1); - - send2BitsSilv(chan); //chan 1=C 2=B 0=A? - uint8_t sum = 0 - chan; - - send2BitsSilv(m1>>2); //m1 - sum-=m1>>2; - send2BitsSilv(m1); - sum-=m1; - - send2BitsSilv(m2>>2); //m2 - sum-=m2>>2; - send2BitsSilv(m2); - sum-=m2; - - send2BitsSilv(sum); //chk - - sendBitSilv(0); - pulses2MHzPtr--; - send_hilo_silv(50,0); //low-impuls (pegel=1) ueberschreiben - - -} - - - -/* - TRACE CTP-1009 - - = send 45MHz - _ = send nix - start1 0 1 start2 - -------__ --_ -__ -----__ - 7ms 2 .8 .4 .4 .8 5 2 - - frame: - start1 24Bits_1 start2 24_Bits2 - - 24Bits_1: - 7 x Bits Throttle lsb first - 1 x 0 - - 6 x Bits rotate lsb first - 1 x Bit 1=rechts - 1 x 0 - - 4 x Bits chk5 = nib2 ^ nib4 - 4 x Bits chk6 = nib1 ^ nib3 - - 24Bits_2: - 7 x Bits Vorwaets lsb first 0x3f = mid - 1 x 1 - - 7 x Bits 0x0e lsb first - 1 x 1 - - 4 x Bits chk5 = nib2 ^ nib4 - 4 x Bits chk6 = nib1 ^ nib3 - - */ - -#define BIT_TRA (400u*2) -void sendBitTra(uint8_t val) -{ - if(val) _send_hilo( BIT_TRA*1 , BIT_TRA*2 ); - else _send_hilo( BIT_TRA*2 , BIT_TRA*1 ); -} -void sendByteTra(uint8_t val) -{ - for(uint8_t i=0; i<8; i++, val>>=1) sendBitTra(val&1); -} -void setupPulsesTracerCtp1009() -{ - pulses2MHzPtr=pulses2MHz; - static bool phase; - if( (phase=!phase) ){ - uint8_t thr = min(127u,(uint16_t)(g_chans512[0]+1024+8) / 16u); - uint8_t rot; - if (g_chans512[1] >= 0) - { - rot = min(63u,(uint16_t)( g_chans512[1]+16) / 32u) | 0x40; - }else{ - rot = min(63u,(uint16_t)(-g_chans512[1]+16) / 32u); - } - sendByteTra(thr); - sendByteTra(rot); - uint8_t chk=thr^rot; - sendByteTra( (chk>>4) | (chk<<4) ); - _send_hilo( 5000*2, 2000*2 ); - }else{ - uint8_t fwd = min(127u,(uint16_t)(g_chans512[2]+1024) / 16u) | 0x80; - sendByteTra(fwd); - sendByteTra(0x8e); - uint8_t chk=fwd^0x8e; - sendByteTra( (chk>>4) | (chk<<4) ); - _send_hilo( 7000*2, 2000*2 ); - } - *pulses2MHzPtr++=0; - if((pulses2MHzPtr-pulses2MHz) >= (signed)DIM(pulses2MHz)) alert(PSTR("pulse tab overflow")); -} diff --git a/src/model_menus.cpp b/src/model_menus.cpp index 8483c8a9c..b13589854 100644 --- a/src/model_menus.cpp +++ b/src/model_menus.cpp @@ -67,6 +67,7 @@ void menuProcTelemetry(uint8_t event); #ifdef TEMPLATES void menuProcTemplates(uint8_t event); #endif +void menuProcExpoOne(uint8_t event); MenuFuncP_PROGMEM APM menuTabModel[] = { menuProcModelSelect, @@ -678,99 +679,137 @@ void menuProcHeli(uint8_t event) static uint8_t s_curveChan; +typedef int16_t (*FnFuncP) (int16_t x); + +int16_t expoFn(int16_t x) +{ + ExpoData *ed = expoaddress(s_currIdx); + int16_t anas[NUM_STICKS] = {0}; + anas[ed->chn] = x; + applyExpos(anas); + return anas[ed->chn]; +} + +int16_t curveFn(int16_t x) +{ + return intpol(x, s_curveChan); +} + +void DrawCurve(FnFuncP fn) +{ + lcd_vlineStip(X0, 0, DISPLAY_H, 0xee); + lcd_hlineStip(X0-WCHART, Y0, WCHART*2, 0xee); + + for (int8_t xv=-WCHART+1; xv= MAX_CURVE5; + uint8_t points; + int8_t *crv; + static int8_t dfltCrv; + static uint8_t autoThrStep; - SUBMENU("CURVE", 2+(cv9 ? 9 : 5), { 9, 0/*repeated...*/}); - lcd_outdezAtt(6*FW, 0, s_curveChan+1, INVERS); + TITLE("CURVE"); + lcd_outdezAtt(5*FW+1, 0, s_curveChan+1, INVERS|LEFT); - int8_t *crv = cv9 ? g_model.curves9[s_curveChan-MAX_CURVE5] : g_model.curves5[s_curveChan]; + if (s_curveChan >= MAX_CURVE5) { + points = 9; + crv = g_model.curves9[s_curveChan-MAX_CURVE5]; + } + else { + points = 5; + crv = g_model.curves5[s_curveChan]; + } - int8_t sub = m_posVert-1; - int8_t subSub = m_posHorz; - - switch(event){ + switch(event) { + case EVT_ENTRY: + dfltCrv = 0; + autoThrStep = 0; + break; + case EVT_KEY_FIRST(KEY_MENU): + if (!s_editMode) { + switch (m_posHorz) { + case 0: + s_editMode = true; + break; + case 1: + if (++dfltCrv > 4) + dfltCrv = -4; + for (uint8_t i=0; i0) m_posHorz--; + if (!autoThrStep && m_posHorz>0) m_posHorz--; break; case EVT_KEY_REPT(KEY_RIGHT): case EVT_KEY_FIRST(KEY_RIGHT): - if(s_editMode && subSub<(cv9 ? 9 : 5)) m_posHorz++; + if (!autoThrStep && m_posHorz<(s_editMode ? points-1 : ((g_menuStack[g_menuStackPtr-1] == menuProcExpoOne && IS_THROTTLE(expoaddress(s_currIdx)->chn)) ? 2 : 1))) m_posHorz++; break; } - s_editMode = m_posHorz; - - for (uint8_t i = 0; i < 5; i++) { - uint8_t y = i * FH + 16; - uint8_t attr = sub == i ? INVERS : 0; - lcd_outdezAtt(4 * FW, y, crv[i], attr); - } - if(cv9) - for (uint8_t i = 0; i < 4; i++) { - uint8_t y = i * FH + 16; - uint8_t attr = sub == i + 5 ? INVERS : 0; - lcd_outdezAtt(8 * FW, y, crv[i + 5], attr); + for (uint8_t i = 0; i < points; i++) { + uint8_t x, y; + if (i>4) { + x = 8*FW; y = (i-4) * FH; } - lcd_putsAtt( 2*FW, 1*FH,PSTR("EDIT->"),((sub == -1) && (subSub == 0)) ? INVERS : 0); - lcd_putsAtt( 2*FW, 7*FH,PSTR("PRESET"),sub == (cv9 ? 9 : 5) ? INVERS : 0); - - static int8_t dfltCrv; - if((sub<(cv9 ? 9 : 5)) && (sub>-1)) CHECK_INCDEC_MODELVAR( event, crv[sub], -100,100); - else if(sub>0){ //make sure we're not on "EDIT" - dfltCrv = checkIncDec(event, dfltCrv, -4, 4, EE_MODEL); - if (checkIncDec_Ret) { - if(cv9) for (uint8_t i = 0; i < 9; i++) crv[i] = (i-4)*dfltCrv* 100 / 16; - else for (uint8_t i = 0; i < 5; i++) crv[i] = (i-2)*dfltCrv* 100 / 8; + else { + x = 4*FW; y = (i+1) * FH; } + uint8_t attr = (s_editMode && m_posHorz == i) ? INVERS : 0; + lcd_outdezAtt(x, y, crv[i], attr); } - if(s_editMode) - { - for(uint8_t i=0; i<(cv9 ? 9 : 5); i++) - { - uint8_t xx = XD-1-WCHART+i*WCHART/(cv9 ? 4 : 2); - uint8_t yy = Y0-crv[i]*WCHART/100; + lcd_puts_P(0*FW, 7*FH, PSTR("MODE")); + lcd_putsnAtt(5*FW-2, 7*FH, PSTR("EDIT ""PRSET""A.THR")+5*(!s_editMode)*m_posHorz, 5, s_editMode || autoThrStep ? 0 : INVERS); + if (s_editMode || autoThrStep) { + for (uint8_t i=0; ichn] = xv*(RESX/WCHART); - applyExpos(anas); - uint16_t yv = (RESX + anas[ed->chn]) / 2; - yv = (DISPLAY_H-1) - yv * (DISPLAY_H-1) / RESX; - lcd_plot(X0+xv, yv, BLACK); - } + DrawCurve(expoFn); int16_t x512 = calibratedStick[ed->chn]; + int16_t anas[NUM_STICKS] = {0}; anas[ed->chn] = x512; applyExpos(anas); int16_t y512 = anas[ed->chn]; @@ -1123,7 +1153,6 @@ static uint8_t s_maxLines = 8; static uint8_t s_copySrcIdx; static uint8_t s_copySrcCh; -#define FIRST 0x10 inline void displayMixerLine(uint8_t row, uint8_t mix, uint8_t ch, uint8_t idx, uint8_t cur, uint8_t event) { uint8_t y = (row-s_pgOfs)*FH; @@ -1173,7 +1202,7 @@ inline void displayExpoLine(uint8_t row, uint8_t expo, uint8_t ch, uint8_t idx, lcd_outdezAtt(9*FW+1, y, ed->expo, 0); putsFlightPhase(10*FW, y, ed->negPhase ? -ed->phase : +ed->phase); putsSwitches(13*FW+4, y, ed->swtch, 0); // normal switches - if (ed->mode!=3) lcd_putc(17*FW, y, ed->mode == 2 ? 127 : 126);//'|' : (stkVal[i] ? '<' : '>'),0);*/ + if (ed->mode!=3) lcd_putc(17*FW, y, ed->mode == 2 ? 126 : 127);//'|' : (stkVal[i] ? '<' : '>'),0);*/ if (ed->curve) putsCurve(18*FW+2, y, ed->curve+(ed->curve >= CURVE_BASE+4 ? 4 : 0)); if (s_copyMode) { @@ -1394,31 +1423,25 @@ void menuProcLimits(uint8_t event) static bool swVal[NUM_CHNOUT]; - uint8_t y = 0; - uint8_t k = 0; - int8_t sub = m_posVert - 1; + int8_t sub = m_posVert - 1; - switch(event) - { - case EVT_KEY_LONG(KEY_MENU): - if(sub>=0 && suboffset = (ld->revert) ? -v : v; - STORE_MODELVARS; - break; - } + for (uint8_t i=0; i<7; i++) { + uint8_t y = (i+1)*FH; + uint8_t k = i+s_pgOfs; + + if (k==NUM_CHNOUT) { + //last line available - add the "copy trim menu" line + uint8_t attr = (sub==NUM_CHNOUT) ? INVERS : 0; + lcd_putsAtt(3*FW, y, PSTR("COPY TRIM [MENU]"), s_noHi ? 0 : attr); + if (attr && event==EVT_KEY_LONG(KEY_MENU)) { + s_noHi = NO_HI_LEN; + killEvents(event); + moveTrimsToOffsets(); // if highlighted and menu pressed - copy trims } - break; - } + return; + } - for(uint8_t i=0; i<7; i++){ - y=(i+1)*FH; - k=i+s_pgOfs; - if(k==NUM_CHNOUT) break; - LimitData *ld = limitaddress( k ) ; + LimitData *ld = limitaddress(k) ; int16_t v = (ld->revert) ? -ld->offset : ld->offset; if((g_chans512[k] - v) > 50) swVal[k] = (true==ld->revert);// Switch to raw inputs? - remove trim! if((g_chans512[k] - v) < -50) swVal[k] = (false==ld->revert); @@ -1434,6 +1457,12 @@ void menuProcLimits(uint8_t event) if (active) { ld->offset = checkIncDec(event, ld->offset, -1000, 1000, EE_MODEL); } + else if (attr && event==EVT_KEY_LONG(KEY_MENU)) { + int16_t zero = g_chans512[k]; + ld->offset = (ld->revert) ? -zero : zero; + s_editMode = false; + STORE_MODELVARS; + } break; case 1: lcd_outdezAtt( 12*FW, y, (int8_t)(ld->min-100), attr); @@ -1466,16 +1495,6 @@ void menuProcLimits(uint8_t event) } } } - if(k==NUM_CHNOUT){ - //last line available - add the "copy trim menu" line - uint8_t attr = (sub==NUM_CHNOUT) ? INVERS : 0; - lcd_putsAtt(3*FW,y,PSTR("COPY TRIM [MENU]"),s_noHi ? 0 : attr); - if(attr && event==EVT_KEY_LONG(KEY_MENU)) { - s_noHi = NO_HI_LEN; - killEvents(event); - moveTrimsToOffsets(); // if highlighted and menu pressed - copy trims - } - } } void menuProcCurvesAll(uint8_t event) @@ -1505,17 +1524,15 @@ void menuProcCurvesAll(uint8_t event) if(cv9 && (yd>6)) break; if(yd>7) break; if(!m) m = attr; - lcd_putsAtt( FW*0, y,PSTR("CV"),attr); - lcd_outdezAtt( (k<9) ? FW*3 : FW*4-1, y,k+1 ,attr); - + putsStrIdx(0, y, PSTR("CV"), k+1, attr); int8_t *crv = cv9 ? g_model.curves9[k-MAX_CURVE5] : g_model.curves5[k]; for (uint8_t j = 0; j < (5); j++) { - lcd_outdezAtt( j*(3*FW+3) + 7*FW, y, crv[j], 0); + lcd_outdezAtt( j*(3*FW+3) + 7*FW + 2, y, crv[j], 0); } y += FH;yd++; if(cv9){ for (uint8_t j = 0; j < 4; j++) { - lcd_outdezAtt( j*(3*FW+3) + 7*FW, y, crv[j+5], 0); + lcd_outdezAtt( j*(3*FW+3) + 7*FW + 2, y, crv[j+5], 0); } y += FH;yd++; } @@ -1541,8 +1558,8 @@ void menuProcCustomSwitches(uint8_t event) //write SW names here lcd_putsnAtt( 0*FW , y, PSTR("SW"),2,0); - lcd_putc( 2*FW , y, k + (k>8 ? 'A'-9: '1')); - lcd_putsnAtt( 4*FW , y, PSTR(CSWITCH_STR)+CSW_LEN_FUNC*cs.func,CSW_LEN_FUNC,m_posHorz==0 ? attr : 0); + lcd_putc( 2*FW, y, k + (k>8 ? 'A'-9: '1')); + lcd_putsnAtt( 4*FW - 1, y, PSTR(CSWITCH_STR)+CSW_LEN_FUNC*cs.func,CSW_LEN_FUNC,m_posHorz==0 ? attr : 0); uint8_t cstate = CS_STATE(cs.func); diff --git a/src/myeeprom.h b/src/myeeprom.h index 924c39555..93cfb4fed 100644 --- a/src/myeeprom.h +++ b/src/myeeprom.h @@ -232,11 +232,10 @@ typedef struct t_ModelData { char name[10]; // 10 must be first for eeLoadModelName TimerData timer1; // TODO timers array uint8_t protocol:3; - int8_t ppmNCH:3; uint8_t thrTrim:1; // Enable Throttle Trim - uint8_t thrExpo:1; // Enable Throttle Expo + int8_t ppmNCH:4; uint8_t trimInc:3; // Trim Increments - uint8_t spare1:1; + uint8_t thrExpo:1; // Enable Throttle Expo uint8_t pulsePol:1; uint8_t extendedLimits:1; uint8_t extendedTrims:1; diff --git a/src/pers.cpp b/src/pers.cpp index dfb838647..a5c684040 100644 --- a/src/pers.cpp +++ b/src/pers.cpp @@ -114,7 +114,6 @@ uint8_t Translate() g_model.thrTrim = v3->thrTrim; g_model.thrExpo = v3->thrExpo; g_model.trimInc = v3->trimInc; - g_model.spare1 = 0; g_model.pulsePol = v3->pulsePol; if (g_eeGeneral.myVers == EEPROM_VER_r584) g_model.extendedLimits = 0; diff --git a/src/pulses.cpp b/src/pulses.cpp new file mode 100644 index 000000000..47d0509b8 --- /dev/null +++ b/src/pulses.cpp @@ -0,0 +1,444 @@ +/* + * Authors (alphabetical order) + * - Bertrand Songis + * - Bryan J. Rentoul (Gruvin) + * + * gruvin9x is based on code named er9x by + * Author - Erez Raviv , which is in turn + * was based on 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 "gruvin9x.h" + +#ifndef SIMU + +#ifdef CTP1009 +uint16_t pulses2MHz[50] = {0}; +#else +uint16_t pulses2MHz[40] = {0}; +#endif + +uint16_t *pulses2MHzRPtr = pulses2MHz; +uint16_t *pulses2MHzWPtr = pulses2MHz; + +#define CTRL_END 0 +#define CTRL_CNT 1 +#define CTRL_REP_1CMD -3 +#define CTRL_REP_2CMD -6 + +ISR(TIMER1_COMPA_vect) //2MHz pulse generation +{ + static uint8_t pulsePol; // TODO strange, it's always 0 at first, shouldn't it be initialized properly in setupPulses? + + // Latency -- how far further on from interrupt trigger has the timer counted? + // (or -- how long did it take to get to this function) + uint8_t dt = TCNT1L; + if (dt > g_tmr1Latency_max) g_tmr1Latency_max = dt; + if (dt < g_tmr1Latency_min) g_tmr1Latency_min = dt; + + // vinceofdrink@gmail harwared ppm + // Orginal bitbang for PPM +#if !defined (DPPMPB7_HARDWARE) && !defined (PCBV4) + if (pulsePol) { + PORTB |= (1< 480 (just below 500msec - it can still reach 500 with offset) +//} +//int16_t PPM_range = 512*2; //range of 0.7..1.7msec +//uint16_t PPM_gap = 300 * 2; //Stoplen *2 +//uint16_t PPM_frame ; + +void setupPulsesPPM() // changed 10/05/2010 by dino Issue 128 +{ +#define PPM_CENTER 1200*2 + int16_t PPM_range = g_model.extendedLimits ? 640*2 : 512*2; //range of 0.7..1.7msec + + //Total frame length = 22.5msec + //each pulse is 0.7..1.7ms long with a 0.3ms stop tail + //The pulse ISR is 2mhz that's why everything is multiplied by 2 + + // G: Found the following reference at th9x. The below code does not seem + // to produce quite exactly this, to my eye. *shrug* + // http://www.aerodesign.de/peter/2000/PCM/frame_ppm.gif + uint16_t *ptr = pulses2MHz ; // TODO check this saves flash + uint8_t p = 8+(g_model.ppmNCH*2); // channels count + uint16_t q = (g_model.ppmDelay*50+300)*2; //Stoplen *2 + uint16_t rest = 22500u*2-q; //Minimum Framelen=22.5 ms + // TODO in er9x this line replaces the next one: rest += (int16_t(g_model.ppmFrameLength))*1000; + if(p>9) rest=p*(1720u*2 + q) + 4000u*2; //for more than 9 channels, frame must be longer + for (uint8_t i=0; i>1) | 0x80; //shift in stop bit + } + _send_1(len + 10*BITLEN_DSM2 -1); //some more space-time for security +} + + +void setupPulsesDsm2(uint8_t chns) +{ + static uint8_t dsmDat[2+6*2]={0x80,0, 0x00,0xAA, 0x05,0xFF, 0x09,0xFF, 0x0D,0xFF, 0x13,0x54, 0x14,0xAA}; + + static uint8_t state = 0; + + if(state==0){ + + if((dsmDat[0] == 0) || ! keyState(SW_Trainer) ){ //init - bind! + dsmDat[0]=0; dsmDat[1]=0; //DSM2_Header = 0,0; + for(uint8_t i=0; i>8)&0x03); + dsmDat[3+2*i] = pulse & 0xff; + } + } + } + sendByteDsm2(dsmDat[state++]); + sendByteDsm2(dsmDat[state++]); + if(state >= 2+chns*2){ + pulses2MHzWPtr--; //remove last stopbits and + _send_1(20000u*2 -1); //prolong them + state=0; + } +} + +#endif + +#if defined(SILVER) || defined(CTP1009) +void _send_hilo(uint16_t hi,uint16_t lo) +{ + *pulses2MHzWPtr++=hi; + *pulses2MHzWPtr++=lo; +} +#endif + +#ifdef SILVER + +#define BITLEN (600u*2) +#define send_hilo_silv( hi, lo) _send_hilo( (hi)*BITLEN,(lo)*BITLEN ) + +void sendBitSilv(uint8_t val) +{ + send_hilo_silv((val)?2:1,(val)?2:1); +} +void send2BitsSilv(uint8_t val) +{ + sendBitSilv(val&2);sendBitSilv(val&1); +} +// _ oder - je 0.6ms (gemessen 0.7ms) +// +//____-----_-_-_--_--_ -_--__ -_-_-_-_ -_-_-_-_ --__--__-_______ +// trailer chan m1 m2 +// +//see /home/thus/txt/silverlit/thus.txt +//m1, m2 most significant bit first |m1-m2| <= 9 +//chan: 01=C 10=B +//chk = 0 - chan -m1>>2 -m1 -m2>>2 -m2 +//<= 500us Probleme +//>= 650us Probleme +//periode orig: 450ms +void setupPulsesSilver() +{ + int8_t chan=1; //chan 1=C 2=B 0=A? + + switch(g_model.protocol) + { + case PROTO_SILV_A: chan=0; break; + case PROTO_SILV_B: chan=2; break; + case PROTO_SILV_C: chan=1; break; + } + + int8_t m1 = (uint16_t)(g_chans512[0]+1024)*2 / 256; + int8_t m2 = (uint16_t)(g_chans512[1]+1024)*2 / 256; + if (m1 < 0) m1=0; + if (m2 < 0) m2=0; + if (m1 > 15) m1=15; + if (m2 > 15) m2=15; + if (m2 > m1+9) m1=m2-9; + if (m1 > m2+9) m2=m1-9; + //uint8_t i=0; + + send_hilo_silv(5,1); //idx 0 erzeugt pegel=0 am Ausgang, wird als high gesendet + send2BitsSilv(0); + send_hilo_silv(2,1); + send_hilo_silv(2,1); + + send2BitsSilv(chan); //chan 1=C 2=B 0=A? + uint8_t sum = 0 - chan; + + send2BitsSilv(m1>>2); //m1 + sum-=m1>>2; + send2BitsSilv(m1); + sum-=m1; + + send2BitsSilv(m2>>2); //m2 + sum-=m2>>2; + send2BitsSilv(m2); + sum-=m2; + + send2BitsSilv(sum); //chk + + sendBitSilv(0); + pulses2MHzWPtr--; + send_hilo_silv(50,0); //low-impuls (pegel=1) ueberschreiben +} + +#endif + +#ifdef CTP1009 + +/* + TRACE CTP-1009 + - = send 45MHz + _ = send nix + start1 0 1 start2 + -------__ --_ -__ -----__ + 7ms 2 .8 .4 .4 .8 5 2 + + frame: + start1 24Bits_1 start2 24_Bits2 + + 24Bits_1: + 7 x Bits Throttle lsb first + 1 x 0 + + 6 x Bits rotate lsb first + 1 x Bit 1=rechts + 1 x 0 + + 4 x Bits chk5 = nib2 ^ nib4 + 4 x Bits chk6 = nib1 ^ nib3 + + 24Bits_2: + 7 x Bits Vorwaets lsb first 0x3f = mid + 1 x 1 + + 7 x Bits 0x0e lsb first + 1 x 1 + + 4 x Bits chk5 = nib2 ^ nib4 + 4 x Bits chk6 = nib1 ^ nib3 + + */ + +#define BIT_TRA (400u*2) +void sendBitTra(uint8_t val) +{ + if(val) _send_hilo( BIT_TRA*1 , BIT_TRA*2 ); + else _send_hilo( BIT_TRA*2 , BIT_TRA*1 ); +} +void sendByteTra(uint8_t val) +{ + for(uint8_t i=0; i<8; i++, val>>=1) sendBitTra(val&1); +} +void setupPulsesTracerCtp1009() +{ + static bool phase; + if( (phase=!phase) ){ + uint8_t thr = min(127u,(uint16_t)(g_chans512[0]+1024+8) / 16u); + uint8_t rot; + if (g_chans512[1] >= 0) + { + rot = min(63u,(uint16_t)( g_chans512[1]+16) / 32u) | 0x40; + }else{ + rot = min(63u,(uint16_t)(-g_chans512[1]+16) / 32u); + } + sendByteTra(thr); + sendByteTra(rot); + uint8_t chk=thr^rot; + sendByteTra( (chk>>4) | (chk<<4) ); + _send_hilo( 5000*2, 2000*2 ); + }else{ + uint8_t fwd = min(127u,(uint16_t)(g_chans512[2]+1024) / 16u) | 0x80; + sendByteTra(fwd); + sendByteTra(0x8e); + uint8_t chk=fwd^0x8e; + sendByteTra( (chk>>4) | (chk<<4) ); + _send_hilo( 7000*2, 2000*2 ); + } + if((pulses2MHzWPtr-pulses2MHz) >= (signed)DIM(pulses2MHz)) alert(PSTR("pulse tab overflow")); +} + +#endif + +void setupPulses() +{ + pulses2MHzWPtr = pulses2MHz; + pulses2MHzRPtr = pulses2MHz; + + switch(g_model.protocol) { + case PROTO_PPM: + setupPulsesPPM(); + break; +#ifdef SILVER + case PROTO_SILV_A: + case PROTO_SILV_B: + case PROTO_SILV_C: + setupPulsesSilver(); + break; +#endif +#ifdef CTP1009 + case PROTO_CTP1009: + setupPulsesTracerCtp1009(); + break; +#endif +#ifdef PXX + case PROTO_PXX: + setupPulsesPXX(); + break; +#endif +#ifdef DSM2 + case PROTO_DSM2: + setupPulsesDsm2(6); + break; +#endif + } +} +#endif +