From c184efeb31b1221915b71eba2d84386042536538 Mon Sep 17 00:00:00 2001 From: "open.20.fsguruh@xoxy.net" Date: Mon, 18 Feb 2013 18:02:37 +0000 Subject: [PATCH 01/18] This branch holds changes to improve speed and code size of stock and other versions of open9x. Some of the changes might be dangerous, that's why it is in a branch. After proper testing this branch may die, and will be merged into main trunk From 63e2ae60a5368566cd9dd5f55cdb24715e3398cd Mon Sep 17 00:00:00 2001 From: "open.20.fsguruh@xoxy.net" Date: Mon, 18 Feb 2013 20:26:16 +0000 Subject: [PATCH 02/18] now does rescaling on 256base instead of 100; removes a lot of division by a shift right 8 Needs testing!!! Volunteer please come in. Benefits of change: -248 bytes code generated used gcc 4.7.2 -huge performance increase. My measurements: simple mix goes down from 2,87msec to 1.93msec middle complex mix goes down from 3,37msec to 2,00msec complex mix (with phase change) goes down from 6,87msec to 4,18msec --- src/menus.h | 2 + src/open9x.cpp | 203 +++++++++++++++++++++++++++++++++++++------------ src/open9x.h | 12 +++ 3 files changed, 168 insertions(+), 49 deletions(-) diff --git a/src/menus.h b/src/menus.h index 38c63af9a..2f2005e62 100644 --- a/src/menus.h +++ b/src/menus.h @@ -37,6 +37,8 @@ #define NO_HI_LEN 25 +// RESX range is used for internal calculation; The menu says -100.0 to 100.0; internally it is -1024 to 1024 to allow some optimizations +#define RESX_SHIFT 10 #define RESX 1024 #define RESXu 1024u #define RESXul 1024ul diff --git a/src/open9x.cpp b/src/open9x.cpp index c1b322cd0..c054fe871 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -291,7 +291,7 @@ void per10ms() // These moved here from perOut() to improve beep trigger reliability. #if defined(PWM_BACKLIGHT) - if (g_tmr10ms % 4 == 0) + if ((g_tmr10ms&0x03) == 0x00) // if (g_tmr10ms % 4 == 0) // (should be faster) fadeBacklight(); // increment or decrement brightness until target brightness is reached #endif @@ -337,8 +337,8 @@ CurveInfo curveinfo(uint8_t idx) result.crv = curveaddress(idx); int8_t *next = curveaddress(idx+1); uint8_t size = next - result.crv; - if (size % 2 == 0) { - result.points = (size / 2) + 1; + if ((size & 1)==0) { // if (size % 2 == 0) { + result.points = (size / 2) + 1; // result.points = (size >> 1) + 1; done by the compiler anyway result.custom = true; } else { @@ -470,6 +470,11 @@ int16_t applyCurve(int16_t x, int8_t idx) #define applyCurve(x, idx) (x) #endif +// maybe used in future? +uint16_t divu100(uint32_t A) { + return (((A * 0x47AF) >> 16) + A) >> 7; +} + // expo-funktion: // --------------- // kmplot @@ -491,7 +496,7 @@ uint16_t expou(uint16_t x, uint16_t k) value >>= 12; value += (uint32_t)(100-k)*x+50; - // return divu100(value); + // return divu100(value); // would speed up further by needs more code return value/100; } @@ -590,12 +595,35 @@ void applyExpos(int16_t *anas) } #if !defined(CPUARM) -int16_t calc100toRESX(int8_t x) + + +int16_t calc100to256(int8_t x) // return x*2.56 +{ + // y = 2*x + x/2 +x/16-x/512-x/2048 + // 512 and 2048 are out of scope from int8 input --> forget it + + return ((int16_t)x<<1)+(x>>1)+(x>>4); + // in case of negative values the routine above always generates a too small value, this could be corrected with this + // I think it is not needed anyway + // if (x<0) return -calc100to256(-x); + // else return ((int16_t)x<<1)+(x>>1)+(x>>4); +} + +// return x*10.24 +int16_t calc100toRESX_16Bits(int16_t x) // @@@ open.20.fsguruh +{ + // return (int16_t)x*10 + x/4 - x/64; + return ((x*41)>>2) - (x>>6); // this implementation saves a add but reduces valid range by factor 4!!! by carefull + // return (x*10)+(x>>2)-(x>>6); // this would be the saver implementation +} + +int16_t calc100toRESX(int8_t x) // return x*10.24 { // return (int16_t)x*10 + x/4 - x/64; return ((x*41)>>2) - x/64; } +// return x*1.024 int16_t calc1000toRESX(int16_t x) // improve calc time by Pat MacKenzie { // return x + x/32 - x/128 + x/512; @@ -606,45 +634,80 @@ int16_t calc1000toRESX(int16_t x) // improve calc time by Pat MacKenzie return x+(y>>2); } -int16_t calcRESXto1000(int16_t x) +int16_t calcRESXto1000(int16_t x) // return x/1.024 { // *1000/1024 = x - x/32 + x/128 return (x - (x>>5) + (x>>7)); } #endif + +// @@@2 open.20.fsguruh ; +// channel = channelnumber -1; +// value = outputvalue with 100 mulitplied usual range -102400 to 102400; output -1024 to 1024 +// changed rescaling from *100 to *256 to optimize performance +// rescaled to -262144)) to 262144)) int16_t applyLimits(uint8_t channel, int32_t value) { LimitData * limit = limitaddress(channel); - int16_t ofs = limit->offset; - int16_t lim_p = 10 * (limit->max + 100); - int16_t lim_n = 10 * (limit->min - 100); //multiply by 10 to get same range as ofs (-1000..1000) + int16_t ofs = calc1000toRESX(limit->offset); // multiply to 1.24 to get range (-1024..1024) + int16_t lim_p = calc100toRESX(limit->max + 100); + int16_t lim_n = calc100toRESX(limit->min - 100); //multiply by 10.24 to get same range (-1024..1024) + // int16_t ofs = limit->offset; + // int16_t lim_p = 10 * (limit->max + 100); + // int16_t lim_n = 10 * (limit->min - 100); //multiply by 10 to get same range as ofs (-1000..1000) if (ofs > lim_p) ofs = lim_p; if (ofs < lim_n) ofs = lim_n; #if defined(PPM_LIMITS_SYMETRICAL) if (value) { - int32_t tmp; + int16_t tmp; + if (limit->symetrical) + tmp = (value > 0) ? (lim_p) : (-lim_n); + else + tmp = (value > 0) ? (lim_p - ofs) : (-lim_n + ofs); + value = (int32_t) value * tmp; // div by 1024*256 -> output = -1024..1024 + tmp=value>>16; // that's quite tricky: the shiftright 16 operation is assmbled just with addressmove; just forget the two least significant bytes; + tmp>>=2; // now one simple shift right for two bytes does the rest +// too pedantic or really useful? +// if ((tmp==-1) && (value&0x00020000)) tmp+=1; // check if negativ and least significant bit was set + // if we are pedantic; a -1 result is wrong if we devide with 262144)) (-0x40000) That's why a +1 is needed + ofs+=tmp; // ofs can to added directly because already recalculated, + +/* int32_t tmp; if (limit->symetrical) tmp = (value > 0) ? ((int32_t) lim_p) : ((int32_t) -lim_n); else tmp = (value > 0) ? ((int32_t) lim_p - ofs) : ((int32_t) -lim_n + ofs); - value = (value * tmp) / 100000; + value = (value * tmp) / 100000; */ } #else if (value) { - int32_t tmp = (value > 0) ? ((int32_t) lim_p - ofs) : ((int32_t) -lim_n + ofs); - value = (value * tmp) / 100000; //div by 100000 -> output = -1024..1024 + int16_t tmp = (value > 0) ? (lim_p - ofs) : (-lim_n + ofs); + value = (int32_t) value * tmp; // div by 1024*256 -> output = -1024..1024 + tmp=value>>16; // that's quite tricky: the shiftright 16 operation is assmbled just with addressmove; just forget the two least significant bytes; + tmp>>=2; // now one simple shift right for two bytes does the rest +// too pedantic or really useful? +// if ((tmp==-1) && (value&0x00020000)) tmp+=1; // check if negativ and least significant bit was set + // if we are pedantic; a -1 result is wrong if we devide with 262144)) (-0x40000) That's why a +1 is needed + ofs+=tmp; // ofs can to added directly because already recalculated, + + // int32_t tmp = (value > 0) ? ((int32_t) lim_p - ofs) : ((int32_t) -lim_n + ofs); + // value = (value * tmp) / 100000; //div by 100000 -> output = -1024..1024 } #endif - - value += calc1000toRESX(ofs); - lim_p = calc1000toRESX(lim_p); - lim_n = calc1000toRESX(lim_n); - if (value > lim_p) value = lim_p; - if (value < lim_n) value = lim_n; - - ofs = value; // we convert value to a 16bit value and reuse ofs + // ofs+=value; // already done above + // lim_p, lim_n already recalculated + if (ofs > lim_p) ofs = lim_p; + if (ofs < lim_n) ofs = lim_n; + + // value += calc1000toRESX(ofs); + // lim_p = calc1000toRESX(lim_p); + // lim_n = calc1000toRESX(lim_n); + // if (value > lim_p) value = lim_p; + // if (value < lim_n) value = lim_n; + // ofs = value; // we convert value to a 16bit value and reuse ofs + if (limit->revert) ofs = -ofs; // finally do the reverse. if (safetyCh[channel] != -128) // if safety channel available for channel check @@ -1201,12 +1264,13 @@ void putsTelemetryValue(xcoord_t x, uint8_t y, lcdint_t val, uint8_t unit, uint8 } #endif +#define INAC_DEV_SHIFT 9 // @@@ shift right value for stick movement #define INAC_DEVISOR 512 // Bypass splash screen with stick movement uint16_t stickMoveValue() { uint16_t sum = 0; for (uint8_t i=0; i<4; i++) - sum += anaIn(i)/INAC_DEVISOR; + sum += (anaIn(i)>>INAC_DEV_SHIFT); // sum += anaIn(i)/INAC_DEVISOR; return sum; } @@ -1810,7 +1874,8 @@ FORCEINLINE void evalTrims() if (g_eeGeneral.throttleReversed) trim = -trim; int16_t v = anas[i]; - int32_t vv = ((int32_t)trim-TRIM_MIN)*(RESX-v)/(2*RESX); + int32_t vv = ((int32_t)trim-TRIM_MIN)*(RESX-v)>>(RESX_SHIFT+1); + // int32_t vv = ((int32_t)trim-TRIM_MIN)*(RESX-v)/(2*RESX); trim = vv; } else if (trimsCheckTimer > 0) { @@ -1830,7 +1895,8 @@ BeepANACenter evalSticks(uint8_t mode) if (g_model.swashR.value) { uint32_t v = (int32_t(calibratedStick[ELE_STICK])*calibratedStick[ELE_STICK] + int32_t(calibratedStick[AIL_STICK])*calibratedStick[AIL_STICK]); - uint32_t q = int32_t(RESX)*g_model.swashR.value/100; + uint32_t q = calc100toRESX(g_model.swashR.value); + // uint32_t q = int32_t(RESX)*g_model.swashR.value/100; q *= q; if (v>q) d = isqrt32(v); @@ -1908,7 +1974,8 @@ BeepANACenter evalSticks(uint8_t mode) #ifdef HELI if(d && (ch==ELE_STICK || ch==AIL_STICK)) - v = int32_t(v)*g_model.swashR.value*RESX/(int32_t(d)*100); + v = (int32_t(v)*calc100toRESX(g_model.swashR.value))/int32_t(d); + // v = int32_t(v)*g_model.swashR.value*RESX/(int32_t(d)*100); #endif rawAnas[ch] = v; @@ -2343,13 +2410,18 @@ void perOut(uint8_t mode, uint8_t tick10ms) if(g_model.swashR.value) { uint32_t v = ((int32_t)anas[ELE_STICK]*anas[ELE_STICK] + (int32_t)anas[AIL_STICK]*anas[AIL_STICK]); - uint32_t q = (int32_t)RESX*g_model.swashR.value/100; + // uint32_t q = (int32_t)RESX*g_model.swashR.value/100; + uint32_t q = calc100toRESX(g_model.swashR.value); q *= q; if(v>q) { uint16_t d = isqrt32(v); - anas[ELE_STICK] = (int32_t)anas[ELE_STICK]*g_model.swashR.value*RESX/((int32_t)d*100); - anas[AIL_STICK] = (int32_t)anas[AIL_STICK]*g_model.swashR.value*RESX/((int32_t)d*100); + // @@@ open.20.fsguruh + // anas[ELE_STICK] = (int32_t)anas[ELE_STICK]*g_model.swashR.value*RESX/((int32_t)d*100); + // anas[AIL_STICK] = (int32_t)anas[AIL_STICK]*g_model.swashR.value*RESX/((int32_t)d*100); + int16_t tmp = calc100toRESX(g_model.swashR.value); + anas[ELE_STICK] = (int32_t) anas[ELE_STICK]*tmp/d; + anas[AIL_STICK] = (int32_t) anas[AIL_STICK]*tmp/d; } } @@ -2455,7 +2527,8 @@ void perOut(uint8_t mode, uint8_t tick10ms) if (dirtyChannels & ((bitfield_channels_t)1 << (k-MIXSRC_CH1+1)) & (passDirtyChannels|~(((bitfield_channels_t) 1 << md->destCh)-1))) passDirtyChannels |= (bitfield_channels_t) 1 << md->destCh; if (k-MIXSRC_CH1+1 < md->destCh || pass > 0) - v = chans[k-MIXSRC_CH1+1] / 100; + v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before + // v = chans[k-MIXSRC_CH1+1] / 100; } } #else @@ -2469,7 +2542,8 @@ void perOut(uint8_t mode, uint8_t tick10ms) if (dirtyChannels & ((bitfield_channels_t)1 << (k-MIXSRC_CH1+1)) & (passDirtyChannels|~(((bitfield_channels_t) 1 << md->destCh)-1))) passDirtyChannels |= (bitfield_channels_t) 1 << md->destCh; if (k-MIXSRC_CH1+1 < md->destCh || pass > 0) - v = chans[k-MIXSRC_CH1+1] / 100; + v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before + // v = chans[k-MIXSRC_CH1+1] / 100; } } #endif @@ -2566,8 +2640,9 @@ void perOut(uint8_t mode, uint8_t tick10ms) if (mode == e_perout_mode_normal && (md->speedUp || md->speedDown)) { // there are delay values #define DEL_MULT 256 +#define DEL_MULT_SHIFT 8 - int16_t diff = v - act[i] / DEL_MULT; + int16_t diff = v - (act[i] >> DEL_MULT_SHIFT); // int16_t diff = v - act[i] / DEL_MULT; if (diff) { //rate = steps/sec => 32*1024/100*md->speedUp/Down @@ -2577,29 +2652,41 @@ void perOut(uint8_t mode, uint8_t tick10ms) int32_t rate = (int32_t) DEL_MULT * 2048 * 100 * tick10ms; if (weight) rate /= abs(weight); - act[i] = (diff>0) ? ((md->speedUp>0) ? act[i]+(rate)/((int16_t)(100/SLOW_STEP)*md->speedUp) : (int32_t)v*DEL_MULT) : - ((md->speedDown>0) ? act[i]-(rate)/((int16_t)(100/SLOW_STEP)*md->speedDown) : (int32_t)v*DEL_MULT) ; + act[i] = (diff>0) ? ((md->speedUp>0) ? act[i]+(rate)/((int16_t)(100/SLOW_STEP)*md->speedUp) : (int32_t)v<speedDown>0) ? act[i]-(rate)/((int16_t)(100/SLOW_STEP)*md->speedDown) : (int32_t)v<0) ? ((md->speedUp>0) ? act[i]+(rate)/((int16_t)(100/SLOW_STEP)*md->speedUp) : (int32_t)v*DEL_MULT) : + // ((md->speedDown>0) ? act[i]-(rate)/((int16_t)(100/SLOW_STEP)*md->speedDown) : (int32_t)v*DEL_MULT) ; } { - int32_t tmp = act[i] / DEL_MULT; - if (((diff > 0) && (v < tmp)) || ((diff < 0) && (v > tmp))) act[i] = (int32_t) v * DEL_MULT; //deal with overflow + int32_t tmp = act[i] >> DEL_MULT_SHIFT; + if (((diff > 0) && (v < tmp)) || ((diff < 0) && (v > tmp))) act[i] = (int32_t) v< 0) && (v < tmp)) || ((diff < 0) && (v > tmp))) act[i] = (int32_t) v * DEL_MULT; //deal with overflow } - v = act[i] / DEL_MULT; + v = act[i] >> DEL_MULT_SHIFT; // v = act[i] / DEL_MULT; } } //========== WEIGHT =============== - int32_t dv = (int32_t) v * weight; + // @@@2 recalculate weight to a 256 basis which ease the calculation later a lot + int32_t dv = (int32_t) v * calc100to256(weight); //========== DIFFERENTIAL ========= if (md->curveMode == MODE_DIFFERENTIAL) { - int8_t curveParam = GET_GVAR(md->curveParam, -100, 100, s_perout_flight_phase); +/* int8_t curveParam = GET_GVAR(md->curveParam, -100, 100, s_perout_flight_phase); if (curveParam > 0 && dv < 0) dv = (dv * (100 - curveParam)) / 100; else if (curveParam < 0 && dv > 0) - dv = (dv * (100 + curveParam)) / 100; + dv = (dv * (100 + curveParam)) / 100; */ + + // @@@2 also recalculate curveParam to a 256 basis which ease the calculation later a lot + int16_t curveParam=calc100to256(GET_GVAR(md->curveParam, -100, 100, s_perout_flight_phase)); + if (curveParam > 0 && dv < 0) + dv = (dv * (256 - curveParam)) >> 8; + else if (curveParam < 0 && dv > 0) + dv = (dv * (256 + curveParam)) >> 8; } int32_t *ptr = &chans[md->destCh]; // Save calculating address several times @@ -2616,9 +2703,11 @@ void perOut(uint8_t mode, uint8_t tick10ms) #endif break; case MLTPX_MUL: - dv /= 100; + // @@@2 we have to remove the weight factor of 256 in case of 100%; now we use the new base of 256 + // dv /= 100; + dv >>= 8; dv *= *ptr; - dv /= RESXl; + dv >>= RESX_SHIFT; // same as dv /= RESXl; *ptr = dv; break; default: // MLTPX_ADD @@ -2715,7 +2804,8 @@ void doMixerCalculations() s_perout_flight_phase = p; perOut(e_perout_mode_normal, tick10ms); for (uint8_t i=0; i>4) * fp_act[p]; // @@@ use shift right instead of division + // sum_chans512[i] += (chans[i] / 16) * fp_act[p]; weight += fp_act[p]; } } @@ -2734,10 +2824,13 @@ void doMixerCalculations() // at the end chans[i] = chans[i]/100 => -1024..1024 // interpolate value with min/max so we get smooth motion from center to stop // this limits based on v original values and min=-1024, max=1024 RESX=1024 - int32_t q = (s_fade_flight_phases ? (sum_chans512[i] / weight) * 16 : chans[i]); - ex_chans[i] = q / 100; // for the next perMain + + // int32_t q = (s_fade_flight_phases ? (sum_chans512[i] / weight) * 16 : chans[i]); + int32_t q = (s_fade_flight_phases ? (sum_chans512[i] / weight) << 4 : chans[i]); + // @@@2 now remove the internal 256 100% basis by a simple shift operation + ex_chans[i]=q>>8; // for the next perMain - int16_t value = applyLimits(i, q); + int16_t value = applyLimits(i, q); // applyLimits will remove the 256 100% basis cli(); g_chans512[i] = value; // copy consistent word to int-level @@ -2773,14 +2866,18 @@ void doMixerCalculations() if (g_model.limitData[ch].symetrical) val -= calc1000toRESX(g_model.limitData[ch].offset); #endif - val = val * 10 / (10+(g_model.limitData[ch].max-g_model.limitData[ch].min)/20); + // @@@ open.20.fsguruh optimized calculation; now *16 /16 instead of 10 base + val = (val << 4) / (16+((g_model.limitData[ch].max-g_model.limitData[ch].min)>>5)); + if (val<0) val=0; // prevent val be negative, which would corrupt throttle trace and timers; could occur if safetyswitch is smaller than limits + // val = val * 10 / (10+(g_model.limitData[ch].max-g_model.limitData[ch].min)/20); } else { val = RESX + calibratedStick[g_model.thrTraceSrc == 0 ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1]; } - val /= (RESX/16); // calibrate it - + val >>= (RESX_SHIFT-4); // calibrate it @@@ open.20.fsguruh + // val /= (RESX/16); // calibrate it + static tmr10ms_t s_time_tot; static uint8_t s_cnt_1s; static uint16_t s_sum_1s; @@ -2967,9 +3064,17 @@ void perMain() #elif !defined(CPUARM) uint16_t t0 = getTmr16KHz(); int16_t delta = (nextMixerEndTime - lastMixerDuration) - t0; - if (delta > 0 && delta < MAX_MIXER_DELTA) return; + if (delta > 0 && delta < MAX_MIXER_DELTA) { + // @@@ open.20.fsguruh + // SLEEP(); // wouldn't that make sense? should save a lot of battery power!!! + // asm volatile(" sleep \n\t"); // if _SLEEP() is not defined use this + return; + } nextMixerEndTime = t0 + MAX_MIXER_DELTA; + // this is a very tricky implementation; lastMixerEndTime is just like a default value not to stop mixcalculations totally; + // the real value for lastMixerEndTime is calculated inside pulses_XXX.cpp which aligns the timestamp to the pulses generated + // nextMixerEndTime is actually defined inside pulses_XXX.h doMixerCalculations(); diff --git a/src/open9x.h b/src/open9x.h index d4b454336..1c432ebab 100644 --- a/src/open9x.h +++ b/src/open9x.h @@ -1003,6 +1003,16 @@ void generalDefault(); void modelDefault(uint8_t id); #if defined(CPUARM) +inline int16_t calc100to256(register int8_t x) // @@@2 open.20.fsguruh: return x*2.56 +{ + return ((int16_t) x * 256) / 100; +} + +inline int16_t calc100toRESX_16Bits(register int16_t x) // @@@ open.20.fsguruh +{ + return x * 1024 / 100; +} + inline int32_t calc100toRESX(register int8_t x) { return x * 1024 / 100; @@ -1024,6 +1034,8 @@ inline int16_t calcRESXto100(register int32_t x) } #else +extern int16_t calc100to256(int8_t x); // @@@2 open.20.fsguruh: return x*2.56 +extern int16_t calc100toRESX_16Bits(int16_t x); // @@@ open.20.fsguruh extern int16_t calc100toRESX(int8_t x); extern int16_t calc1000toRESX(int16_t x); extern int16_t calcRESXto1000(int16_t x); From 35fc938b1e337132e56668d9597051ab04595630 Mon Sep 17 00:00:00 2001 From: "open.20.fsguruh@xoxy.net" Date: Mon, 18 Feb 2013 22:03:43 +0000 Subject: [PATCH 03/18] Safety channel fix This change should avoid setting all output channels to 0 for the first few outputs generated. Also this prevents the throttle triggered timers to start not intended after power up. This occurs when you set throttle timers to output channel instead of stick. (By the way, is there any reason, this should be the source at all?) Because reset of safety channels is only moved, this do not cost any additional bytes of flash. However the reinitialisation in evalFunctions() may be missing somewhere? Please comment... --- src/open9x.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/open9x.cpp b/src/open9x.cpp index c054fe871..a386d5786 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -1388,6 +1388,10 @@ void checkAll() checkLowEEPROM(); #endif +// @@@ will be done now here; otherwise cause safetyCh switch in with 0 output --> dangerous for throttle + for (uint8_t i=0; i dangerous for throttle for (uint8_t i=0; i Date: Tue, 19 Feb 2013 20:23:44 +0000 Subject: [PATCH 04/18] Safety channel fix; Switches fix; Slow fix (potential); Optimizations Let me explain the fixes: 1.) Safety channel fix should avoid throttle being pushed the first few PPM outputs. Also the throttle counters are affected. Mostly noticable, the THt starts, even no throttle is given during startup. This fix should prevent this. Its just a new position for evalFunctions as proposed by bsongis 2.) Slow fix Not so sure about this. I noticed the speed of Slow options in the mixer was always too fast for me. Wathever slow value I gave the movement was quite quick. Then I noticed I have always a small weight for that mixes. A bigger weight makes the movement faster. I thought what is the sense of this? Shouldn't it be vice versa? Then I realized the most useful way is just make it independ from weight. The speed just defines persentage of full movement per second. I think that is the most useful way to calculate it. And additionally this would simplify the mixcalculation a lot. 3.) Last but not least I found further code and data savings - Additional 198 bytes code saved with avr-gcc 4.7.2 compared to last release. So this is in addition! - Reduced data about 4 bytes - Potentially there is also a slight mixer speedup possible. Not measured yet. My first tests showed everything is now working as intended. Please test and report bug. --- src/open9x.cpp | 187 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 125 insertions(+), 62 deletions(-) diff --git a/src/open9x.cpp b/src/open9x.cpp index a386d5786..4daca3b15 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -1388,10 +1388,6 @@ void checkAll() checkLowEEPROM(); #endif -// @@@ will be done now here; otherwise cause safetyCh switch in with 0 output --> dangerous for throttle - for (uint8_t i=0; i dangerous for throttle for (uint8_t i=0; i0 ? (32768)/((int16_t)100*md->speedUp) : -(32768)/((int16_t)100*md->speedDown); //-100..100 => 32768 -> 100*83886/256 = 32768, For MAX we divide by 2 since it's asymmetrical if (tick10ms) { - int32_t rate = (int32_t) DEL_MULT * 2048 * 100 * tick10ms; - if (weight) rate /= abs(weight); + // int32_t rate = (int32_t) DEL_MULT * 2048 * 100 * tick10ms; + // if (weight) rate /= abs(weight); + // @@@ open.20.fsguruh: In my opinion the calculation above is wrong; It leeds to a slower movement if weight is big; What reason do we have to do so? + // Is it not enough if the speed of the movement is based on a percentage speed per second rather than slow speed by weight? + // if speed should be aligned to weight, the calculation should be the oposite; so instead of *100/weight it should be *weight/100; but is it intended at all? + int32_t rate = (int32_t) tick10ms << (DEL_MULT_SHIFT+11); // = DEL_MULT*2048*tick10ms + // codesaving 44 bytes + int32_t tmp=(int32_t)v<0) { + if (md->speedUp>0) { + int32_t tmp2 = act[i]+rate/((int16_t)(100/SLOW_STEP)*md->speedUp); + if (tmp2speedDown>0) { + int32_t tmp2 = act[i]-rate/((int16_t)(100/SLOW_STEP)*md->speedDown); + if (tmp2>tmp) tmp=tmp2; //deal with overflow; Endposition; prevent toggling around the destination + } + } + act[i] = tmp; + // open.20.fsguruh: this implementation would save about 50 bytes code + +/* act[i] = (diff>0) ? ((md->speedUp>0) ? act[i]+(rate)/((int16_t)(100/SLOW_STEP)*md->speedUp) : (int32_t)v<speedDown>0) ? act[i]-(rate)/((int16_t)(100/SLOW_STEP)*md->speedDown) : (int32_t)v<0) ? ((md->speedUp>0) ? act[i]+(rate)/((int16_t)(100/SLOW_STEP)*md->speedUp) : (int32_t)v*DEL_MULT) : // ((md->speedDown>0) ? act[i]-(rate)/((int16_t)(100/SLOW_STEP)*md->speedDown) : (int32_t)v*DEL_MULT) ; - } - - { + + // only needed if act changes at all int32_t tmp = act[i] >> DEL_MULT_SHIFT; - if (((diff > 0) && (v < tmp)) || ((diff < 0) && (v > tmp))) act[i] = (int32_t) v< 0) && (v < tmp)) || ((diff < 0) && (v > tmp))) act[i] = (int32_t) v * DEL_MULT; //deal with overflow - } - + if (((diff > 0) && (v < tmp)) || ((diff < 0) && (v > tmp))) act[i] = (int32_t) v<> DEL_MULT_SHIFT; // v = act[i] / DEL_MULT; } } @@ -2749,7 +2763,9 @@ void doMixerCalculations() static tmr10ms_t lastTMR; tmr10ms_t tmr10ms = get_tmr10ms(); - uint8_t tick10ms = (tmr10ms >= lastTMR ? tmr10ms - lastTMR : 1); + uint8_t tick10ms = (tmr10ms >= lastTMR ? tmr10ms - lastTMR : 1); // handle tick10ms overrun + //@@@ open.20.fsguruh: correct overflow handling costs a lot of code; happens only each 11 min; would be but costs too much code + // therefore forget the exact calculation and use only 1 instead; good compromise lastTMR = tmr10ms; #if defined(BOLD_FONT) @@ -2776,7 +2792,7 @@ void doMixerCalculations() static ACTIVE_PHASES_TYPE s_fade_flight_phases = 0; static uint8_t s_last_phase = 255; // TODO reinit everything here when the model changes, no??? uint8_t phase = getFlightPhase(); - int32_t weight = 0; + int32_t weight; // = 0; will be initilized below not needed here even compiler complains -> saves 6 bytes if (s_last_phase != phase) { if (s_last_phase != 255) PLAY_PHASE_OFF(s_last_phase); @@ -2803,7 +2819,7 @@ void doMixerCalculations() if (s_fade_flight_phases) { memclear(sum_chans512, sizeof(sum_chans512)); - weight = 0; + weight = 0; // unfortunately removing this instead of initialization do not save even 1 bytes of code, that's why it's here for (uint8_t p=0; p>= (RESX_SHIFT-6); // increase resolution by factor 4 +#else val >>= (RESX_SHIFT-4); // calibrate it @@@ open.20.fsguruh // val /= (RESX/16); // calibrate it +#endif - static tmr10ms_t s_time_tot; - static uint8_t s_cnt_1s; - static uint16_t s_sum_1s; -#if defined(THRTRACE) - static tmr10ms_t s_time_trace; - static uint16_t s_cnt_10s; - static uint16_t s_sum_10s; -#endif - - s_cnt_1s++; - s_sum_1s += val; - - if ((tmr10ms_t)(tmr10ms - s_time_tot) >= 100) { // 1sec - s_time_tot += 100; - s_timeCumTot += 1; - - val = s_sum_1s / s_cnt_1s; - s_timeCum16ThrP += val / 2; - if (val) s_timeCumThr += 1; - -#if defined(THRTRACE) - s_cnt_10s += s_cnt_1s; - s_sum_10s += s_sum_1s; - - if ((tmr10ms_t)(tmr10ms - s_time_trace) >= 1000) { // 10s - s_time_trace += 1000; - val = s_sum_10s / s_cnt_10s; - s_sum_10s = 0; - s_cnt_10s = 0; - - s_traceBuf[s_traceWr++] = val; - if (s_traceWr >= MAXTRACE) s_traceWr = 0; - if (s_traceCnt >= 0) s_traceCnt++; - } -#endif - - s_cnt_1s = 0; - s_sum_1s = 0; - } // Timers start for (uint8_t i=0; i<2; i++) { @@ -2933,7 +2927,7 @@ void doMixerCalculations() s_timerState[i] = TMR_RUNNING; s_cnt[i] = 0; s_sum[i] = 0; - s_time_cum_16[i] = 0; + // s_time_cum_16[i] = 0; open.20.fsguruh: we have s_sum, that's enough } uint8_t atm = (tm >= 0 ? tm : TMR_VAROFS-tm-1); @@ -2964,7 +2958,24 @@ void doMixerCalculations() if (val) s_timerVal[i]++; } else if (atm==TMRMODE_THR_REL) { - if (s_cnt[i]) { + // @@@ open.20.fsguruh: why so complicated? we have already a s_sum field; use it for the half seconds (not showable) as well + // check for s_cnt[i]==0 is not needed because we are shure it is at least 1 +#if defined(ACCURAT_THROTTLE_TIMER) + if ((s_sum[i]/s_cnt[i])>=128) { // throttle was normalized to 0 to 128 value (throttle/64*2 (because - range is added as well) + s_timerVal[i]++; // add second used of throttle + s_sum[i]-=128*s_cnt[i]; + } + s_cnt[i]=0; +#else + if ((s_sum[i]/s_cnt[i])>=32) { // throttle was normalized to 0 to 32 value (throttle/16*2 (because - range is added as well) + s_timerVal[i]++; // add second used of throttle + s_sum[i]-=32*s_cnt[i]; + } + s_cnt[i]=0; +#endif + // isn't that a lot shorter than the old code; should do exactly the same; except with double resolution + +/* if (s_cnt[i]) { old code val = s_sum[i]/s_cnt[i]; s_sum[i] -= val*s_cnt[i]; //rest s_cnt[i] = 0; @@ -2973,7 +2984,8 @@ void doMixerCalculations() s_timerVal[i] ++; s_time_cum_16[i] -= 16; } - } + } */ + } else if (atm==TMRMODE_THR_TRG) { if (val || s_timerVal[i] > 0) @@ -3001,6 +3013,55 @@ void doMixerCalculations() } }; + static tmr10ms_t s_time_tot; + static uint8_t s_cnt_1s; + static uint16_t s_sum_1s; +#if defined(THRTRACE) + static tmr10ms_t s_time_trace; + static uint16_t s_cnt_10s; + static uint16_t s_sum_10s; +#endif + + s_cnt_1s++; + s_sum_1s += val; + + // @@@ open.20.fsguruh: moved code here; at least val variable conflicts with caculations above, safer here? + // even reduced code size about 8 bytes, why ever? + if ((tmr10ms_t)(tmr10ms - s_time_tot) >= 100) { // 1sec + s_time_tot += 100; + s_timeCumTot += 1; + +#if defined(ACCURAT_THROTTLE_TIMER) + val = s_sum_1s / s_cnt_1s; + s_timeCum16ThrP += val >> 3; + if (val) s_timeCumThr += 1; + s_sum_1s>>=2; // correct better accuracy now, because trace graph can show this information +#else + val = s_sum_1s / s_cnt_1s; + s_timeCum16ThrP += (val>>1); // @@@ open.20.fsguruh + if (val) s_timeCumThr += 1; +#endif + +#if defined(THRTRACE) + s_cnt_10s += s_cnt_1s; + s_sum_10s += s_sum_1s; + + if ((tmr10ms_t)(tmr10ms - s_time_trace) >= 1000) { // 10s + s_time_trace += 1000; + val = s_sum_10s / s_cnt_10s; + s_sum_10s = 0; + s_cnt_10s = 0; + + s_traceBuf[s_traceWr++] = val; + if (s_traceWr >= MAXTRACE) s_traceWr = 0; + if (s_traceCnt >= 0) s_traceCnt++; + } +#endif + + s_cnt_1s = 0; + s_sum_1s = 0; + } + static int16_t last_tmr; if (last_tmr != s_timerVal[0]) { // beep only if seconds advance if (s_timerState[0] == TMR_RUNNING) { @@ -3047,11 +3108,13 @@ void doMixerCalculations() } } +/* as proposed by bsongis, this is done now shortly before limits #if defined(CPUARM) requiredSpeakerVolume = g_eeGeneral.speakerVolume + VOLUME_LEVEL_DEF; #endif evalFunctions(); +*/ #if defined(DSM2) if (s_rangecheck_mode) AUDIO_PLAY(AU_FRSKY_CHEEP); From 2b12eabcb9d5a221c03d5edbee64263d399aa89c Mon Sep 17 00:00:00 2001 From: "open.20.fsguruh@xoxy.net" Date: Wed, 20 Feb 2013 21:22:21 +0000 Subject: [PATCH 05/18] 2nd try for SLOW / speed fix / opinion needed brief description: - SLOW should be correct now, please test a lot - further small flash saving of 40 bytes - higher THROTTLE accuracy not for stock but all other boards as proposed by bsongis detailed: while first fix claimed to be correct, it wasn't because the original version was not wrong. I was confused because the simulator in companion9x simulates nearly half of real speed. Is this intended? You will see it also on the seconds measured. The original code was just a lot confusing and in a silly position. That's why I need your opinion! I moved now the speed calculation to the last position in mixing calculation. This causes the slow directly operate on the output values. This saves additional 40 bytes flash code. The original position was after trim, offset, curve, but before weight and differential. The before weight position makes this confusing weight correction necessary. A simple position change would avoid the weight division. My opinion for the best position for speed calculation is on the output, because whatever curve, diff, etc. calculation you had before the output will have the defined speed. However somebody want the output to change speed according a curve? This wasn't possible with the original implementation, but a change could make this be easily possible. As far as I know the implementation of the expensive Graupner remote controls do it that way. So speed is something like a controlled stickmovement speed and the rest is calculated on the base of it. I asked a lot, and most people do not really care if it is calculated on input or output. An advantage on the output is the easier calculation, which results in higher mixer speed and flash code savings. What's your opinion? Where is the best place for speed calculation? a) on the input b) somewhere in the middle (please specify) c) at the output --- src/open9x.cpp | 179 ++++++++++++++++++++++++++++--------------------- 1 file changed, 104 insertions(+), 75 deletions(-) diff --git a/src/open9x.cpp b/src/open9x.cpp index 4daca3b15..a3471f2d5 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -483,6 +483,11 @@ uint16_t divu100(uint32_t A) { // f(x,k)=x*x*k/10 + x*(1-k/10) ;P[0,1,2,3,4,5,6,7,8,9,10] // f(x,k)=1+(x-1)*(x-1)*(x-1)*k/10 + (x-1)*(1-k/10) ;P[0,1,2,3,4,5,6,7,8,9,10] +// input parameters; +// x 0 to 1024; +// k 0 to 100; +// return value = 1024 * (x/1024)^((101+k)/30) +// return value = exp(ln(x/1024)*exp(k/10))*1024 uint16_t expou(uint16_t x, uint16_t k) { // previous function was this one: @@ -597,30 +602,61 @@ void applyExpos(int16_t *anas) #if !defined(CPUARM) +// #define CORRECT_NEGATIVE_SHIFTS +// open.20.fsguruh; shift right operations do the rounding different for negative values compared to positive values +// so all negative divisions round always further down, which give absolute values bigger compared to a usual division +// this is noticable on the display, because instead of -100.0 -99.9 is shown; While in praxis I doublt somebody will notice a +// difference this is more a mental thing. Maybe people are distracted, because the easy calculations are obviously wrong +// this define would correct this, but costs 34 bytes code for stock version + int16_t calc100to256(int8_t x) // return x*2.56 { // y = 2*x + x/2 +x/16-x/512-x/2048 // 512 and 2048 are out of scope from int8 input --> forget it + +#ifdef CORRECT_NEGATIVE_SHIFTS + int16_t res=(int16_t)x<<1; + //int8_t sign=(uint8_t) x>>7; + int8_t sign=(x<0?1:0); + x-=sign; + res+=(x>>1); + res+=sign; + res+=(x>>4); + res+=sign; + return res; +#else return ((int16_t)x<<1)+(x>>1)+(x>>4); +#endif // in case of negative values the routine above always generates a too small value, this could be corrected with this // I think it is not needed anyway // if (x<0) return -calc100to256(-x); // else return ((int16_t)x<<1)+(x>>1)+(x>>4); } + // return x*10.24 int16_t calc100toRESX_16Bits(int16_t x) // @@@ open.20.fsguruh { // return (int16_t)x*10 + x/4 - x/64; return ((x*41)>>2) - (x>>6); // this implementation saves a add but reduces valid range by factor 4!!! by carefull // return (x*10)+(x>>2)-(x>>6); // this would be the saver implementation -} +} int16_t calc100toRESX(int8_t x) // return x*10.24 { +#ifdef CORRECT_NEGATIVE_SHIFTS + int16_t res= ((int16_t)x*41)>>2; + int8_t sign=(x<0?1:0); + //int8_t sign=(uint8_t) x>>7; + x-=sign; + res-=(x>>6); + res-=sign; + return res; +#else // return (int16_t)x*10 + x/4 - x/64; - return ((x*41)>>2) - x/64; + return ((x*41)>>2) - (x>>6); +#endif } // return x*1.024 @@ -2636,58 +2672,6 @@ void perOut(uint8_t mode, uint8_t tick10ms) int16_t weight = GET_GVAR(MD_WEIGHT(md), -500, 500, s_perout_flight_phase); - //========== SPEED =============== - if (mode == e_perout_mode_normal && (md->speedUp || md->speedDown)) { // there are delay values - -#define DEL_MULT 256 -#define DEL_MULT_SHIFT 8 - - int16_t diff = v - (act[i] >> DEL_MULT_SHIFT); // int16_t diff = v - act[i] / DEL_MULT; - - if (diff) { - //rate = steps/sec => 32*1024/100*md->speedUp/Down - //act[i] += diff>0 ? (32768)/((int16_t)100*md->speedUp) : -(32768)/((int16_t)100*md->speedDown); - //-100..100 => 32768 -> 100*83886/256 = 32768, For MAX we divide by 2 since it's asymmetrical - if (tick10ms) { - // int32_t rate = (int32_t) DEL_MULT * 2048 * 100 * tick10ms; - // if (weight) rate /= abs(weight); - // @@@ open.20.fsguruh: In my opinion the calculation above is wrong; It leeds to a slower movement if weight is big; What reason do we have to do so? - // Is it not enough if the speed of the movement is based on a percentage speed per second rather than slow speed by weight? - // if speed should be aligned to weight, the calculation should be the oposite; so instead of *100/weight it should be *weight/100; but is it intended at all? - int32_t rate = (int32_t) tick10ms << (DEL_MULT_SHIFT+11); // = DEL_MULT*2048*tick10ms - // codesaving 44 bytes - - int32_t tmp=(int32_t)v<0) { - if (md->speedUp>0) { - int32_t tmp2 = act[i]+rate/((int16_t)(100/SLOW_STEP)*md->speedUp); - if (tmp2speedDown>0) { - int32_t tmp2 = act[i]-rate/((int16_t)(100/SLOW_STEP)*md->speedDown); - if (tmp2>tmp) tmp=tmp2; //deal with overflow; Endposition; prevent toggling around the destination - } - } - act[i] = tmp; - // open.20.fsguruh: this implementation would save about 50 bytes code - -/* - act[i] = (diff>0) ? ((md->speedUp>0) ? act[i]+(rate)/((int16_t)(100/SLOW_STEP)*md->speedUp) : (int32_t)v<speedDown>0) ? act[i]-(rate)/((int16_t)(100/SLOW_STEP)*md->speedDown) : (int32_t)v<0) ? ((md->speedUp>0) ? act[i]+(rate)/((int16_t)(100/SLOW_STEP)*md->speedUp) : (int32_t)v*DEL_MULT) : - // ((md->speedDown>0) ? act[i]-(rate)/((int16_t)(100/SLOW_STEP)*md->speedDown) : (int32_t)v*DEL_MULT) ; - - // only needed if act changes at all - int32_t tmp = act[i] >> DEL_MULT_SHIFT; - if (((diff > 0) && (v < tmp)) || ((diff < 0) && (v > tmp))) act[i] = (int32_t) v<> DEL_MULT_SHIFT; // v = act[i] / DEL_MULT; - } - } - //========== WEIGHT =============== // @@@2 recalculate weight to a 256 basis which ease the calculation later a lot int32_t dv = (int32_t) v * calc100to256(weight); @@ -2708,6 +2692,48 @@ void perOut(uint8_t mode, uint8_t tick10ms) dv = (dv * (256 + curveParam)) >> 8; } + //========== SPEED =============== + if (mode == e_perout_mode_normal && (md->speedUp || md->speedDown)) { // there are delay values + +#define DEL_MULT_SHIFT 8 + // we are lucky, the MULT_SHIFT factor actually fits to the weight recalculation; Therefore no adaption needed + // so weight is 256 and MULT factor is the same + + int32_t diff=dv-act[i]; + if (diff) { + // open.20.fsguruh: speed is defined in % movement per second; In menu we specify the full movement (-100% to 100%) = 200% in total + // the unit of the stored value is the value from md->speedUp or md->speedDown divide SLOW_STEP seconds; e.g. value 4 means 4/SLOW_STEP = 2 seconds for CPU64 + // because we get a tick each 10msec, we need 100 ticks for one second + // the value in md->speedXXX gives the time it should take to do a full movement from -100 to 100 therefore 200%. This equals 2048 in recalculated internal range + if (tick10ms) { + // only if already time is passed add or substract a value according the speed configured + + // codesaving 44 bytes + int32_t rate = (int32_t) tick10ms << (DEL_MULT_SHIFT+11); // = DEL_MULT*2048*tick10ms + // rate equals a full range for one second; if less time is passed rate is accordingly smaller + // if one second passed, rate would be 2048 (full motion)*256(recalculated weight)*100(100 ticks needed for one second) + + int32_t currentValue=dv; + if (diff>0) { + if (md->speedUp>0) { + // if a speed upwards is defined recalculate the new value according configured speed; the higher the speed the smaller the add value is + int32_t newValue = act[i]+rate/((int16_t)(100/SLOW_STEP)*md->speedUp); + if (newValuespeedDown>0) { + // see explanation in speedUp + int32_t newValue = act[i]-rate/((int16_t)(100/SLOW_STEP)*md->speedDown); + if (newValue>currentValue) currentValue=newValue; // Endposition; prevent toggling around the destination + } + } //endif diff>0 + act[i] = currentValue; + // open.20.fsguruh: this implementation would save about 50 bytes code + } // endif tick10ms ; in case no time passed assign the old value, not the current value from source + dv = act[i]; + } //endif diff + } //endif speed + int32_t *ptr = &chans[md->destCh]; // Save calculating address several times if (i == 0 || md->destCh != (md - 1)->destCh) @@ -2723,7 +2749,6 @@ void perOut(uint8_t mode, uint8_t tick10ms) break; case MLTPX_MUL: // @@@2 we have to remove the weight factor of 256 in case of 100%; now we use the new base of 256 - // dv /= 100; dv >>= 8; dv *= *ptr; dv >>= RESX_SHIFT; // same as dv /= RESXl; @@ -2764,7 +2789,7 @@ void doMixerCalculations() tmr10ms_t tmr10ms = get_tmr10ms(); uint8_t tick10ms = (tmr10ms >= lastTMR ? tmr10ms - lastTMR : 1); // handle tick10ms overrun - //@@@ open.20.fsguruh: correct overflow handling costs a lot of code; happens only each 11 min; would be but costs too much code + //@@@ open.20.fsguruh: correct overflow handling costs a lot of code; happens only each 11 min; // therefore forget the exact calculation and use only 1 instead; good compromise lastTMR = tmr10ms; @@ -2906,8 +2931,13 @@ void doMixerCalculations() val = RESX + calibratedStick[g_model.thrTraceSrc == 0 ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1]; } -// #define ACCURAT_THROTTLE_TIMER -// code cost is about 18 bytes for higher throttle accuracy for timer +#ifndef CPUM64 + // code cost is about 16 bytes for higher throttle accuracy for timer + // would not be noticable anyway, because all version up to this change had only 16 steps; + // now it has already 32 steps; this define would increase to 128 steps + #define ACCURAT_THROTTLE_TIMER +#endif + #if defined(ACCURAT_THROTTLE_TIMER) val >>= (RESX_SHIFT-6); // increase resolution by factor 4 @@ -2973,19 +3003,6 @@ void doMixerCalculations() } s_cnt[i]=0; #endif - // isn't that a lot shorter than the old code; should do exactly the same; except with double resolution - -/* if (s_cnt[i]) { old code - val = s_sum[i]/s_cnt[i]; - s_sum[i] -= val*s_cnt[i]; //rest - s_cnt[i] = 0; - s_time_cum_16[i] += val/2; - if (s_time_cum_16[i] >= 16) { - s_timerVal[i] ++; - s_time_cum_16[i] -= 16; - } - } */ - } else if (atm==TMRMODE_THR_TRG) { if (val || s_timerVal[i] > 0) @@ -3033,16 +3050,19 @@ void doMixerCalculations() #if defined(ACCURAT_THROTTLE_TIMER) val = s_sum_1s / s_cnt_1s; - s_timeCum16ThrP += val >> 3; + s_timeCum16ThrP += (val>>3); // s_timeCum16ThrP would overrun if we would store throttle value with higher accuracy; therefore stay with 16 steps if (val) s_timeCumThr += 1; - s_sum_1s>>=2; // correct better accuracy now, because trace graph can show this information + s_sum_1s>>=2; // correct better accuracy now, because trace graph can show this information; in case thrtrace is not active, the compile should remove this #else val = s_sum_1s / s_cnt_1s; - s_timeCum16ThrP += (val>>1); // @@@ open.20.fsguruh + s_timeCum16ThrP += (val>>1); if (val) s_timeCumThr += 1; #endif #if defined(THRTRACE) + // throttle trace is done every 10 seconds; Tracebuffer is adjusted to screen size. + // in case buffer runs out, it wraps around + // resolution for y axis is only 32, therefore no higher value makes sense s_cnt_10s += s_cnt_1s; s_sum_10s += s_sum_1s; @@ -3135,7 +3155,16 @@ void perMain() if (delta > 0 && delta < MAX_MIXER_DELTA) { // @@@ open.20.fsguruh // SLEEP(); // wouldn't that make sense? should save a lot of battery power!!! - // asm volatile(" sleep \n\t"); // if _SLEEP() is not defined use this +/* for future use; currently very very beta... + ADCSRA&=0x7F; // disable ADC for power saving + ACSR&=0xF7; // disable ACIE Interrupts + ACSR|=0x80; // disable Analog Comparator + + MCUCR|=0x24; // enable Sleep (bit5) enable ADC Noise Reduction (bit2) + asm volatile(" sleep \n\t"); // if _SLEEP() is not defined use this + MCUCR&=0x00; // disable sleep + ADCSRA|=0x80; // enable ADC + */ return; } From 1e3c1d8ce2184a144effd6782a9d4463c60619a0 Mon Sep 17 00:00:00 2001 From: "open.20.fsguruh@xoxy.net" Date: Thu, 21 Feb 2013 20:17:54 +0000 Subject: [PATCH 06/18] Expo EXTENDED function added, SLOW moved to old position, 8 bytes code saved Now the expo function is explained properly. Expo execution speed should be again better. Codesaving this time only 8 bytes. Added EXTENDED_EXPO define for a wider range of expo. So around 0 the curve is more flat and the edges increase faster. This change costs about 84 bytes. I noticed the curve doesn't much change especially for smaller k values. Now the range is extended. From 0 to 80 it gives the old curves from 0 to 100. From 81 to 100 a higher raising curve is used. There is a small break between 80 to 81. If this is a problem I can change it to be smooth and even less code size, but loose the old curve form. I moved SLOW function back to old position, because it might be not a good idea if differential is actived, and the end positions are reached at different times. Therefore the smaller movement needs to be slower. Please try and test and report your opinion. Please also test more about overruns. I was able to generate a overrun by added about 4x a 125% value with 125% offset. (output each above 200%, in total more than 800%) The overruns occurs earlier compared to original version. Could this cause a problem? I can change the internal calculation to be more like the original version, but would cost more flash. --- src/open9x.cpp | 128 +++++++++++++++++++++++++++++++++++-------------- src/open9x.h | 2 +- 2 files changed, 93 insertions(+), 37 deletions(-) diff --git a/src/open9x.cpp b/src/open9x.cpp index a3471f2d5..6560ca6fa 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -470,10 +470,16 @@ int16_t applyCurve(int16_t x, int8_t idx) #define applyCurve(x, idx) (x) #endif +/* currently not needed // maybe used in future? uint16_t divu100(uint32_t A) { return (((A * 0x47AF) >> 16) + A) >> 7; } +*/ + + +// #define EXTENDED_EXPO +// increases range of expo curve but costs about 82 bytes flash // expo-funktion: // --------------- @@ -482,44 +488,90 @@ uint16_t divu100(uint32_t A) { // f(x,k)=x*x*x*k/10 + x*(1-k/10) ;P[0,1,2,3,4,5,6,7,8,9,10] // f(x,k)=x*x*k/10 + x*(1-k/10) ;P[0,1,2,3,4,5,6,7,8,9,10] // f(x,k)=1+(x-1)*(x-1)*(x-1)*k/10 + (x-1)*(1-k/10) ;P[0,1,2,3,4,5,6,7,8,9,10] +// don't know what this above should be, just confusing in my opinion, +// here is the real explanation +// actually the real formula is +/* + f(x) = exp( ln(x) * 10^k) + if it is 10^k or e^k or 2^k etc. just defines the max distortion of the expo curve; I think 10 is useful + this gives values from 0 to 1 for x and output; k must be between -1 and +1 + we do not like to calculate with floating point. Therefore we rescale for x from 0 to 1024 and for k from -100 to +100 + f(x) = 1024 * ( e^( ln(x/1024) * 10^(k/100) ) ) + This would be really hard to be calculated by such a microcontroller + Therefore Thomas Husterer compared a few usual function something like x^3, x^4*something, which look similar + Actually the formula + f(x) = k*x^3+x*(1-k) + gives a similar form and should have even advantages compared to a original exp curve. + This function again expect x from 0 to 1 and k only from 0 to 1 + Therefore rescaling is needed like before: + f(x) = 1024* ((k/100)*(x/1024)^3 + (x/1024)*(100-k)/100) + some mathematical tricks + f(x) = (k*x*x*x/(1024*1024) + x*(100-k)) / 100 + for better rounding results we add the 50 + f(x) = (k*x*x*x/(1024*1024) + x*(100-k) + 50) / 100 + + because we now understand the formula, we can optimize it further + --> calc100to256(k) --> eliminates /100 by replacing with /256 which is just a simple shift right 8 + k is now between 0 and 256 + f(x) = (k*x*x*x/(1024*1024) + x*(256-k) + 128) / 256 + */ + // input parameters; // x 0 to 1024; // k 0 to 100; -// return value = 1024 * (x/1024)^((101+k)/30) -// return value = exp(ln(x/1024)*exp(k/10))*1024 +// output between 0 and 1024 uint16_t expou(uint16_t x, uint16_t k) { - // previous function was this one: - // k*x*x*x + (1-k)*x - // return ((unsigned long)x*x*x/0x10000*k/(RESXul*RESXul/0x10000) + (RESKul-k)*x+RESKul/2)/RESKul; +#ifdef EXTENDED_EXPO + bool extended; + if (k>80) { + extended=true; + } else { + k+=(k>>2); // use bigger values before extend, because the effect is anyway very very low + extended=false; + } // endif k > 50 +#endif + k=calc100to256(k); + uint32_t value = (uint32_t) x*x; value *= (uint32_t)k; value >>= 8; value *= (uint32_t)x; - value >>= 12; - value += (uint32_t)(100-k)*x+50; - // return divu100(value); // would speed up further by needs more code - return value/100; +#ifdef EXTENDED_EXPO + if (extended) { // for higher values do more multiplications to get a stronger expo curve + value>>=16; + value*=(uint32_t)x; + value>>=4; + value*=(uint32_t)x; + } //endif extend +#endif + + value >>= 12; + value += (uint32_t)(256-k)*x+128; + + return value>>8; } int16_t expo(int16_t x, int16_t k) { if (k == 0) return x; int16_t y; - bool neg = x < 0; + bool neg = x < 0; + if (neg) x = -x; if (k<0) { y = RESXu-expou(RESXu-x, -k); - } - else { + } else { y = expou(x, k); } return neg? -y : y; } +// isn't the following useless? wouldn't be called even if EXTENDED_EXPO is defined +/* #ifdef EXTENDED_EXPO /// expo with y-offset class Expo @@ -555,6 +607,7 @@ int16_t Expo::expo(int16_t x) return -expou(-x,c,-d) + drx; } #endif +*/ #ifdef BOLD_FONT ACTIVE_EXPOS_TYPE activeExpos; @@ -592,11 +645,14 @@ void applyExpos(int16_t *anas) else v = expo(v, GET_GVAR(curveParam, -100, 100, s_perout_flight_phase)); } - v = ((int32_t)v * GET_GVAR(ed.weight, 0, 100, s_perout_flight_phase)) / 100; + int16_t weight=GET_GVAR(ed.weight, 0, 100, s_perout_flight_phase); + weight=calc100to256(weight); + v = ((int32_t)v * weight) >> 8; + // v = ((int32_t)v * GET_GVAR(ed.weight, 0, 100, s_perout_flight_phase)) / 100; anas[cur_chn] = v; - } - } - } + } //endif + } //endif getSwitch + } //endfor } #if !defined(CPUARM) @@ -2676,22 +2732,6 @@ void perOut(uint8_t mode, uint8_t tick10ms) // @@@2 recalculate weight to a 256 basis which ease the calculation later a lot int32_t dv = (int32_t) v * calc100to256(weight); - //========== DIFFERENTIAL ========= - if (md->curveMode == MODE_DIFFERENTIAL) { -/* int8_t curveParam = GET_GVAR(md->curveParam, -100, 100, s_perout_flight_phase); - if (curveParam > 0 && dv < 0) - dv = (dv * (100 - curveParam)) / 100; - else if (curveParam < 0 && dv > 0) - dv = (dv * (100 + curveParam)) / 100; */ - - // @@@2 also recalculate curveParam to a 256 basis which ease the calculation later a lot - int16_t curveParam=calc100to256(GET_GVAR(md->curveParam, -100, 100, s_perout_flight_phase)); - if (curveParam > 0 && dv < 0) - dv = (dv * (256 - curveParam)) >> 8; - else if (curveParam < 0 && dv > 0) - dv = (dv * (256 + curveParam)) >> 8; - } - //========== SPEED =============== if (mode == e_perout_mode_normal && (md->speedUp || md->speedDown)) { // there are delay values @@ -2733,11 +2773,27 @@ void perOut(uint8_t mode, uint8_t tick10ms) dv = act[i]; } //endif diff } //endif speed - + + //========== DIFFERENTIAL ========= + if (md->curveMode == MODE_DIFFERENTIAL) { +/* int8_t curveParam = GET_GVAR(md->curveParam, -100, 100, s_perout_flight_phase); + if (curveParam > 0 && dv < 0) + dv = (dv * (100 - curveParam)) / 100; + else if (curveParam < 0 && dv > 0) + dv = (dv * (100 + curveParam)) / 100; */ + + // @@@2 also recalculate curveParam to a 256 basis which ease the calculation later a lot + int16_t curveParam=calc100to256(GET_GVAR(md->curveParam, -100, 100, s_perout_flight_phase)); + if (curveParam > 0 && dv < 0) + dv = (dv * (256 - curveParam)) >> 8; + else if (curveParam < 0 && dv > 0) + dv = (dv * (256 + curveParam)) >> 8; + } + int32_t *ptr = &chans[md->destCh]; // Save calculating address several times - if (i == 0 || md->destCh != (md - 1)->destCh) - *ptr = 0; + if (i == 0 || md->destCh != (md - 1)->destCh) *ptr = 0; + // if this is the first calculation for the destination channel, initialize it with 0 (otherwise would be random) switch (md->mltpx) { case MLTPX_REP: @@ -2763,7 +2819,7 @@ void perOut(uint8_t mode, uint8_t tick10ms) tick10ms = 0; dirtyChannels &= passDirtyChannels; - } while (++pass < 5 && dirtyChannels); + } while (++pass < 7 && dirtyChannels); // we increased speed of mixer, therefore allow more passes; old value was 5 mixWarning = lv_mixWarning; } diff --git a/src/open9x.h b/src/open9x.h index 1c432ebab..fabfb7caf 100644 --- a/src/open9x.h +++ b/src/open9x.h @@ -1060,7 +1060,7 @@ extern int16_t ex_chans[NUM_CHNOUT]; // Outputs (before LIMITS) of th extern int16_t g_chans512[NUM_CHNOUT]; extern uint16_t BandGap; -extern uint16_t expou(uint16_t x, uint16_t k); +// extern uint16_t expou(uint16_t x, uint16_t k); extern int16_t expo(int16_t x, int16_t k); extern int16_t intpol(int16_t, uint8_t); extern int16_t applyCurve(int16_t, int8_t); From 2a9160fcab696de4dcac3bd56d4eb52d99e5abe3 Mon Sep 17 00:00:00 2001 From: "open.20.fsguruh@xoxy.net" Date: Thu, 21 Feb 2013 22:11:33 +0000 Subject: [PATCH 07/18] weight and offset range extension change (flash code cost 22bytes) This change makes both weight and offset range bigger for all 32 possible mixers. Instead of -125 to 125 now -245 to 245 is allowed for weight and offset in mixer. No additional EEPROM is needed. Conversion of EEPROM follow soon... --- src/menus.cpp | 4 +-- src/menus.h | 2 +- src/model_menus.cpp | 32 +++++++----------- src/myeeprom.h | 47 ++++++++++++++++++++++++-- src/open9x.cpp | 80 +++++++++++++++++++-------------------------- src/open9x.h | 9 ++++- 6 files changed, 100 insertions(+), 74 deletions(-) diff --git a/src/menus.cpp b/src/menus.cpp index f9348f9ce..a062cc6b9 100644 --- a/src/menus.cpp +++ b/src/menus.cpp @@ -677,7 +677,7 @@ int8_t switchMenuItem(uint8_t x, uint8_t y, int8_t value, LcdFlags attr, uint8_t } #if defined(GVARS) -int16_t gvarMenuItem(uint8_t x, uint8_t y, int16_t value, int8_t min, int8_t max, LcdFlags attr, uint8_t event) +int16_t gvarMenuItem(uint8_t x, uint8_t y, int16_t value, int16_t min, int16_t max, LcdFlags attr, uint8_t event) // @@@ open.20.fsguruh { uint8_t delta = (max <= 100 ? GV1_SMALL-1 : GV1_LARGE-1); bool invers = (attr & INVERS); @@ -701,7 +701,7 @@ int16_t gvarMenuItem(uint8_t x, uint8_t y, int16_t value, int8_t min, int8_t max } else { lcd_outdezAtt(x, y, value, attr); - if (invers) CHECK_INCDEC_MODELVAR(event, value, min, max); + if (invers) value = checkIncDec(event, value, min, max,EE_MODEL); // as proposed, no change to makro, but directly call this function } return value; } diff --git a/src/menus.h b/src/menus.h index 2f2005e62..da178a29d 100644 --- a/src/menus.h +++ b/src/menus.h @@ -211,7 +211,7 @@ uint8_t onoffMenuItem(uint8_t value, uint8_t x, uint8_t y, const pm_char *label, int8_t switchMenuItem(uint8_t x, uint8_t y, int8_t value, LcdFlags attr, uint8_t event); #if defined(GVARS) -int16_t gvarMenuItem(uint8_t x, uint8_t y, int16_t value, int8_t min, int8_t max, LcdFlags attr, uint8_t event); +int16_t gvarMenuItem(uint8_t x, uint8_t y, int16_t value, int16_t min, int16_t max, LcdFlags attr, uint8_t event); // @@@ open.20.fsguruh #define displayGVar(x, y, v, min, max) gvarMenuItem(x, y, v, min, max, 0, 0) #else int8_t gvarMenuItem(uint8_t x, uint8_t y, int8_t value, int8_t min, int8_t max, LcdFlags attr, uint8_t event); diff --git a/src/model_menus.cpp b/src/model_menus.cpp index 7e5b02a91..12a60cb3d 100644 --- a/src/model_menus.cpp +++ b/src/model_menus.cpp @@ -1904,16 +1904,11 @@ void gvarWeightItem(xcoord_t x, uint8_t y, MixData *md, uint8_t attr, uint8_t ev #if defined(CPUARM) || !defined(GVARS) md->weight = gvarMenuItem(x, y, md->weight, -125, 125, attr, event); #else - int16_t weight = (md->weightMode ? (int16_t)GV1_LARGE + md->weight : md->weight); - weight = gvarMenuItem(x, y, weight, -125, 125, attr, event); - if (weight <= 125) { - md->weightMode = 0; - md->weight = weight; - } - else { - md->weightMode = 1; - md->weight = weight - GV1_LARGE; - } + // @@@ open.20.fsguruh + u_int8int16_t weight; + MD_GETWEIGHT(weight,md); + weight.word = gvarMenuItem(x, y, weight.word, -245, 245, attr, event); + MD_SETWEIGHT(weight,md); #endif } @@ -1990,18 +1985,13 @@ void menuModelMixOne(uint8_t event) { lcd_putsColumnLeft(COLUMN_X, y, NO_INDENT(STR_OFFSET)); #if defined(GVARS) - int16_t offset = (md2->offsetMode ? (int16_t)GV1_LARGE + md2->offset : md2->offset); - offset = gvarMenuItem(COLUMN_X+MIXES_2ND_COLUMN, y, offset, -125, 125, attr|LEFT, event); - if (offset <= 125) { - md2->offsetMode = 0; - md2->offset = offset; - } - else { - md2->offsetMode = 1; - md2->offset = offset - GV1_LARGE; - } + // @@@ open.20.fsguruh + u_int8int16_t offset; + MD_GETOFFSET(offset,md2); + offset.word = gvarMenuItem(COLUMN_X+MIXES_2ND_COLUMN, y, offset.word, -245, 245, attr|LEFT, event); + MD_SETOFFSET(offset,md2); #else - md2->offset = gvarMenuItem(COLUMN_X+MIXES_2ND_COLUMN, y, md2->offset, -125, 125, attr|LEFT, event); + md2->offset = gvarMenuItem(COLUMN_X+MIXES_2ND_COLUMN, y, md2->offset, -125, 125, attr|LEFT, event); #endif break; } diff --git a/src/myeeprom.h b/src/myeeprom.h index 6608fc7e7..6a688ff8b 100644 --- a/src/myeeprom.h +++ b/src/myeeprom.h @@ -311,14 +311,57 @@ PACK(typedef struct t_MixData { int8_t offset; }) MixData; #if defined(GVARS) - #define MD_WEIGHT(md) (md->weightMode ? (int16_t)GV1_LARGE+md->weight : md->weight) + // @@@ open.20.fsguruh + PACK( union u_gvarint_t { + struct { + int8_t lo; + uint8_t hi; + } bytes_t; + int16_t word; + + u_gvarint_t(int8_t l, uint8_t h) {bytes_t.lo=l; bytes_t.hi=h?255:0;} // hi bit is negativ sign + + private: + // prevent unwanted constructors, also saves program + u_gvarint_t() {} + u_gvarint_t(const u_gvarint_t&) {} + }); + #define MD_WEIGHT(md) (u_gvarint_t(md->weight,md->weightMode).word) + + PACK( union u_int8int16_t { + struct { + int8_t lo; + uint8_t hi; + } bytes_t; + int16_t word; + }); + #define MD_GETWEIGHT(var,md) var.bytes_t.lo=md->weight; var.bytes_t.hi=md->weightMode?255:0 + #define MD_SETWEIGHT(var,md) md->weight = var.bytes_t.lo; \ + if (var.word<0) md->weightMode=1; else md->weightMode=0 // set negative sign + + #define MD_OFFSET(md) (u_gvarint_t(md->offset,md->offsetMode).word) + #define MD_GETOFFSET(var,md) var.bytes_t.lo=md->offset; var.bytes_t.hi=md->offsetMode?255:0 + #define MD_SETOFFSET(var,md) md->offset = var.bytes_t.lo; \ + if (var.word<0) md->offsetMode=1; else md->offsetMode=0 // set negative sign #else #define MD_WEIGHT(md) (md->weight) #endif #endif #if defined(GVARS) -#define MD_OFFSET(md) (md->offsetMode ? (int16_t)GV1_LARGE+md->offset : md->offset) +// @@@ open.20.fsguruh +#ifndef MD_OFFSET + PACK( union u_int8int16_t { + struct { + int8_t lo; + uint8_t hi; + } bytes_t; + int16_t word; + }); + #define MD_OFFSET(md) (md->offsetMode ? (int16_t)GV1_LARGE+md->offset : md->offset) + #define MD_GETOFFSET(var,md) var.word=md->offset + #define MD_SETOFFSET(var,md) md->offset=var.word +#endif #else #define MD_OFFSET(md) (md->offset) #endif diff --git a/src/open9x.cpp b/src/open9x.cpp index 6560ca6fa..071ecdbcd 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -657,7 +657,6 @@ void applyExpos(int16_t *anas) #if !defined(CPUARM) - // #define CORRECT_NEGATIVE_SHIFTS // open.20.fsguruh; shift right operations do the rounding different for negative values compared to positive values // so all negative divisions round always further down, which give absolute values bigger compared to a usual division @@ -665,11 +664,10 @@ void applyExpos(int16_t *anas) // difference this is more a mental thing. Maybe people are distracted, because the easy calculations are obviously wrong // this define would correct this, but costs 34 bytes code for stock version -int16_t calc100to256(int8_t x) // return x*2.56 +int16_t calc100to256_16Bits(int16_t x) // return x*2.56 { // y = 2*x + x/2 +x/16-x/512-x/2048 // 512 and 2048 are out of scope from int8 input --> forget it - #ifdef CORRECT_NEGATIVE_SHIFTS int16_t res=(int16_t)x<<1; //int8_t sign=(uint8_t) x>>7; @@ -684,22 +682,14 @@ int16_t calc100to256(int8_t x) // return x*2.56 #else return ((int16_t)x<<1)+(x>>1)+(x>>4); #endif - // in case of negative values the routine above always generates a too small value, this could be corrected with this - // I think it is not needed anyway - // if (x<0) return -calc100to256(-x); - // else return ((int16_t)x<<1)+(x>>1)+(x>>4); } - -// return x*10.24 -int16_t calc100toRESX_16Bits(int16_t x) // @@@ open.20.fsguruh +int16_t calc100to256(int8_t x) // return x*2.56 { - // return (int16_t)x*10 + x/4 - x/64; - return ((x*41)>>2) - (x>>6); // this implementation saves a add but reduces valid range by factor 4!!! by carefull - // return (x*10)+(x>>2)-(x>>6); // this would be the saver implementation -} + return calc100to256_16Bits(x); +} -int16_t calc100toRESX(int8_t x) // return x*10.24 +int16_t calc100toRESX_16Bits(int16_t x) // return x*10.24 { #ifdef CORRECT_NEGATIVE_SHIFTS int16_t res= ((int16_t)x*41)>>2; @@ -715,6 +705,11 @@ int16_t calc100toRESX(int8_t x) // return x*10.24 #endif } +int16_t calc100toRESX(int8_t x) // return x*10.24 +{ + return calc100toRESX_16Bits(x); +} + // return x*1.024 int16_t calc1000toRESX(int16_t x) // improve calc time by Pat MacKenzie { @@ -745,9 +740,7 @@ int16_t applyLimits(uint8_t channel, int32_t value) int16_t ofs = calc1000toRESX(limit->offset); // multiply to 1.24 to get range (-1024..1024) int16_t lim_p = calc100toRESX(limit->max + 100); int16_t lim_n = calc100toRESX(limit->min - 100); //multiply by 10.24 to get same range (-1024..1024) - // int16_t ofs = limit->offset; - // int16_t lim_p = 10 * (limit->max + 100); - // int16_t lim_n = 10 * (limit->min - 100); //multiply by 10 to get same range as ofs (-1000..1000) + if (ofs > lim_p) ofs = lim_p; if (ofs < lim_n) ofs = lim_n; @@ -759,47 +752,40 @@ int16_t applyLimits(uint8_t channel, int32_t value) else tmp = (value > 0) ? (lim_p - ofs) : (-lim_n + ofs); value = (int32_t) value * tmp; // div by 1024*256 -> output = -1024..1024 +#ifdef CORRECT_NEGATIVE_SHIFTS + int8_t sign=(value<0?1:0); + value-=sign; tmp=value>>16; // that's quite tricky: the shiftright 16 operation is assmbled just with addressmove; just forget the two least significant bytes; tmp>>=2; // now one simple shift right for two bytes does the rest -// too pedantic or really useful? -// if ((tmp==-1) && (value&0x00020000)) tmp+=1; // check if negativ and least significant bit was set - // if we are pedantic; a -1 result is wrong if we devide with 262144)) (-0x40000) That's why a +1 is needed + tmp+=sign; +#else + tmp=value>>16; // that's quite tricky: the shiftright 16 operation is assmbled just with addressmove; just forget the two least significant bytes; + tmp>>=2; // now one simple shift right for two bytes does the rest +#endif + ofs+=tmp; // ofs can to added directly because already recalculated, - -/* int32_t tmp; - if (limit->symetrical) - tmp = (value > 0) ? ((int32_t) lim_p) : ((int32_t) -lim_n); - else - tmp = (value > 0) ? ((int32_t) lim_p - ofs) : ((int32_t) -lim_n + ofs); - value = (value * tmp) / 100000; */ } #else if (value) { int16_t tmp = (value > 0) ? (lim_p - ofs) : (-lim_n + ofs); value = (int32_t) value * tmp; // div by 1024*256 -> output = -1024..1024 +#ifdef CORRECT_NEGATIVE_SHIFTS + int8_t sign=(value<0?1:0); + value-=sign; tmp=value>>16; // that's quite tricky: the shiftright 16 operation is assmbled just with addressmove; just forget the two least significant bytes; tmp>>=2; // now one simple shift right for two bytes does the rest -// too pedantic or really useful? -// if ((tmp==-1) && (value&0x00020000)) tmp+=1; // check if negativ and least significant bit was set - // if we are pedantic; a -1 result is wrong if we devide with 262144)) (-0x40000) That's why a +1 is needed + tmp+=sign; +#else + tmp=value>>16; // that's quite tricky: the shiftright 16 operation is assmbled just with addressmove; just forget the two least significant bytes; + tmp>>=2; // now one simple shift right for two bytes does the rest +#endif ofs+=tmp; // ofs can to added directly because already recalculated, - - // int32_t tmp = (value > 0) ? ((int32_t) lim_p - ofs) : ((int32_t) -lim_n + ofs); - // value = (value * tmp) / 100000; //div by 100000 -> output = -1024..1024 } #endif - // ofs+=value; // already done above - // lim_p, lim_n already recalculated + if (ofs > lim_p) ofs = lim_p; if (ofs < lim_n) ofs = lim_n; - // value += calc1000toRESX(ofs); - // lim_p = calc1000toRESX(lim_p); - // lim_n = calc1000toRESX(lim_n); - // if (value > lim_p) value = lim_p; - // if (value < lim_n) value = lim_n; - // ofs = value; // we convert value to a 16bit value and reuse ofs - if (limit->revert) ofs = -ofs; // finally do the reverse. if (safetyCh[channel] != -128) // if safety channel available for channel check @@ -1255,7 +1241,7 @@ uint8_t s_gvar_last = 0; #if defined(CPUM64) int16_t getGVarValue(int16_t x, int16_t min, int16_t max) { - if (x > max) { + if (x > (GV1_LARGE-6)) { // @@@ open.20.fsguruh int8_t idx = (max <= 100 ? x - GV1_SMALL : x - GV1_LARGE); int8_t mul = 1; @@ -2705,8 +2691,8 @@ void perOut(uint8_t mode, uint8_t tick10ms) //========== OFFSET =============== if (apply_offset_and_curve) { - int8_t offset = GET_GVAR(MD_OFFSET(md), -125, 125, s_perout_flight_phase); - if (offset) v += calc100toRESX(offset); + int16_t offset = GET_GVAR(MD_OFFSET(md), -245, 245, s_perout_flight_phase); // open.20.fsguruh + if (offset) v+=calc100toRESX_16Bits(offset); // @@@ open.20.fsguruh } //========== TRIMS =============== @@ -2730,7 +2716,7 @@ void perOut(uint8_t mode, uint8_t tick10ms) //========== WEIGHT =============== // @@@2 recalculate weight to a 256 basis which ease the calculation later a lot - int32_t dv = (int32_t) v * calc100to256(weight); + int32_t dv = (int32_t) v * calc100to256_16Bits(weight); //========== SPEED =============== if (mode == e_perout_mode_normal && (md->speedUp || md->speedDown)) { // there are delay values diff --git a/src/open9x.h b/src/open9x.h index fabfb7caf..491fd49e5 100644 --- a/src/open9x.h +++ b/src/open9x.h @@ -820,7 +820,8 @@ void incRotaryEncoder(uint8_t idx, int8_t inc); #define SET_GVAR(idx, val, p) setGVarValue(idx, val, p) #endif #define GV1_SMALL 123 - #define GV1_LARGE 1024 + // @@@ open.20.fsguruh + #define GV1_LARGE 251 #define GVAR_DISPLAY_TIME 100 /*1 second*/; extern uint8_t s_gvar_timer; extern uint8_t s_gvar_last; @@ -1003,6 +1004,11 @@ void generalDefault(); void modelDefault(uint8_t id); #if defined(CPUARM) +inline int16_t calc100to256_16Bits(register int16_t x) // @@@2 open.20.fsguruh: return x*2.56 +{ + return ((int16_t) x * 256) / 100; +} + inline int16_t calc100to256(register int8_t x) // @@@2 open.20.fsguruh: return x*2.56 { return ((int16_t) x * 256) / 100; @@ -1034,6 +1040,7 @@ inline int16_t calcRESXto100(register int32_t x) } #else +extern int16_t calc100to256_16Bits(int16_t x); // @@@2 open.20.fsguruh: return x*2.56 extern int16_t calc100to256(int8_t x); // @@@2 open.20.fsguruh: return x*2.56 extern int16_t calc100toRESX_16Bits(int16_t x); // @@@ open.20.fsguruh extern int16_t calc100toRESX(int8_t x); From f3a02f0f4ea617e79463a83710d91105486aad90 Mon Sep 17 00:00:00 2001 From: "open.20.fsguruh@xoxy.net" Date: Fri, 22 Feb 2013 20:57:31 +0000 Subject: [PATCH 08/18] Timer fixed for Audio/Voice; fixed throttle calc; small flash savings; SLOW change again brief: ------ * added compile options to makefile: - CORRECT_NEGATIVE_VALUES - EXTENDED_EXPO_CURVE * bugfix for Timer calculation if AUDIO or VOICE was selected for stock and Gruvin * bugfix for Throttle timer calculation * small flash savings about 28 bytes if SLOW wasn't moved, now only 20 bytes * SLOW calculation moved to beginning, but no weight compensation detailed -------- Makefile options: Like all other options this is set either to YES or NO. Both are default NO. This gives the user the possibility to choose themselves about which precision they want. Or if they want a greater EXPO range, both increases code of course. The code changes are explained in detail in source code and earlier version of this branch. Bugfix for Timer calculation if AUDIO or VOICE was selected for stock and Gruvin Need again some reports. I noticed there was a correction algorithm activated for 10msec call generated. For some unknown reasons, the correction was disabled, if AUDIO or VOICE was selected. This fix should make it working again. The clock was actually too fast. It's about 5 - 6 seconds per hours. Should be correct now, please test and report. Also the IRQ disabled the timer IRQ while it is on. This is wrong as well and saved code. Bugfix for Throttle timer calculation Actually the throttle calculations leads to an overrun if full throttle was given. Due to -1 calculation problem this issue was not seen, because it was 1 digit smaller. With the CORRECT_NEGATIVE_VALUE option actually the full range was used, and boom, throttle timer didn't work. This fix actually corrects this and made the code about 28 bytes smaller and faster. SLOW calculation moved to beginning, but no weight compensation As stated by bsongis I moved the SLOW calculation to the beginning. This matches to most other remote controls. However I still left the weight compensation out. This would cost 90 bytes flash and a slow division. Basically if we move the SLOW feature to the beginning it changes the meaning for SLOW. It is not actually defining the output speed in percentage per second. It is defining the input speed in % per second. In this case a mix with high weight will move faster as a mix with small weight. That's the same for other remote controls as well. Doing this compromise only costs 8 additional bytes of flash. --- src/Makefile | 14 +++++ src/myeeprom.h | 2 +- src/open9x.cpp | 158 ++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 151 insertions(+), 23 deletions(-) diff --git a/src/Makefile b/src/Makefile index d75de7d52..b36e6d431 100644 --- a/src/Makefile +++ b/src/Makefile @@ -97,6 +97,12 @@ AUDIO = NO # Values = YES, NO VOICE = NO +# corrects different rounding for negative values. So instead of -99.9 you will see again -100.0 +CORRECT_NEGATIVE_VALUES = NO + +# increases range of expo curve but costs about 82 bytes flash +EXTENDED_EXPO_CURVE = NO + # HAPTIC Mods # Values = YES, NO HAPTIC = NO @@ -410,6 +416,14 @@ ifeq ($(GVARS), YES) EEPROM_VARIANT += ${GVARS_VARIANT} endif +ifeq ($(CORRECT_NEGATIVE_VALUES), YES) + CPPDEFS += -DCORRECT_NEGATIVE_SHIFTS +endif + +ifeq ($(EXTENDED_EXPO_CURVE), YES) + CPPDEFS += -DEXTENDED_EXPO +endif + # If STD-Support is enabled ifeq ($(EXT), STD) CPPDEFS += -DEXTSTD diff --git a/src/myeeprom.h b/src/myeeprom.h index 6a688ff8b..01badeb58 100644 --- a/src/myeeprom.h +++ b/src/myeeprom.h @@ -58,7 +58,7 @@ #elif defined(PCBGRUVIN9X) #define EEPROM_VER 213 #else -#define EEPROM_VER 213 +#define EEPROM_VER 212 #endif #ifndef PACK diff --git a/src/open9x.cpp b/src/open9x.cpp index 071ecdbcd..a79f7291a 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -1816,6 +1816,20 @@ void getADC() uint16_t temp_ana; for (uint8_t adc_input=0; adc_input<8; adc_input++) { ADMUX = adc_input|ADC_VREF_TYPE; +/* + // ADCSRA|=0x08; // enable ADC Interrupt Enable should not be necessary? or is it + MCUCR|=0x28; // enable Sleep (bit5) enable ADC Noise Reduction (bit3) + asm volatile(" sleep \n\t"); // if _SLEEP() is not defined use this + while (ADCSRA & 0x40); // Wait for the AD conversion to complete + temp_ana = ADC; + + asm volatile(" sleep \n\t"); // if _SLEEP() is not defined use this + while (ADCSRA & 0x40); // Wait for the AD conversion to complete + s_anaFilt[adc_input] = temp_ana + ADC; + MCUCR&=0x08; // disable sleep + */ + + ADMUX = adc_input|ADC_VREF_TYPE; ADCSRA|=0x40; // Start the AD conversion while (ADCSRA & 0x40); // Wait for the AD conversion to complete temp_ana = ADC; @@ -1846,6 +1860,17 @@ void getADC_bandgap() #else // TODO is the next line needed (because it has been called before perMain)? ADMUX=0x1E|ADC_VREF_TYPE; // Switch MUX to internal 1.22V reference + +/* + MCUCR|=0x28; // enable Sleep (bit5) enable ADC Noise Reduction (bit2) + asm volatile(" sleep \n\t"); // if _SLEEP() is not defined use this + // ADCSRA|=0x40; + while ((ADCSRA & 0x10)==0); + ADCSRA|=0x10; // take sample clear flag? + BandGap=ADC; + MCUCR&=0x08; // disable sleep + */ + ADCSRA|=0x40; while ((ADCSRA & 0x10)==0); ADCSRA|=0x10; // take sample @@ -2706,18 +2731,70 @@ void perOut(uint8_t mode, uint8_t tick10ms) mix_trim = -1; if (mix_trim >= 0) v += trims[mix_trim]; } + + int16_t weight = GET_GVAR(MD_WEIGHT(md), -500, 500, s_perout_flight_phase); + weight=calc100to256_16Bits(weight); + + + //========== SPEED =============== + if (mode == e_perout_mode_normal && (md->speedUp || md->speedDown)) { // there are delay values + +#define DEL_MULT_SHIFT 8 + // we recale to a mult 256 higher value for calculation + + int16_t diff=v-(act[i]>>DEL_MULT_SHIFT); + if (diff) { + // open.20.fsguruh: speed is defined in % movement per second; In menu we specify the full movement (-100% to 100%) = 200% in total + // the unit of the stored value is the value from md->speedUp or md->speedDown divide SLOW_STEP seconds; e.g. value 4 means 4/SLOW_STEP = 2 seconds for CPU64 + // because we get a tick each 10msec, we need 100 ticks for one second + // the value in md->speedXXX gives the time it should take to do a full movement from -100 to 100 therefore 200%. This equals 2048 in recalculated internal range + if (tick10ms) { + // only if already time is passed add or substract a value according the speed configured + // codesaving 44 bytes +#ifndef WEIGHTSPEEDCORRECTION + int32_t rate = (int32_t) tick10ms << (DEL_MULT_SHIFT+11); // = DEL_MULT*2048*tick10ms +#else + // codecost 82 bytes + int32_t rate = (int32_t) tick10ms << (DEL_MULT_SHIFT+19); // = DEL_MULT*2048*tick10ms + rate/=abs(weight); +#endif + // rate equals a full range for one second; if less time is passed rate is accordingly smaller + // if one second passed, rate would be 2048 (full motion)*256(recalculated weight)*100(100 ticks needed for one second) + + int32_t currentValue=(v<0) { + if (md->speedUp>0) { + // if a speed upwards is defined recalculate the new value according configured speed; the higher the speed the smaller the add value is + int32_t newValue = act[i]+rate/((int16_t)(100/SLOW_STEP)*md->speedUp); + if (newValuespeedDown>0) { + // see explanation in speedUp + int32_t newValue = act[i]-rate/((int16_t)(100/SLOW_STEP)*md->speedDown); + if (newValue>currentValue) currentValue=newValue; // Endposition; prevent toggling around the destination + } + } //endif diff>0 + act[i] = currentValue; + // open.20.fsguruh: this implementation would save about 50 bytes code + } // endif tick10ms ; in case no time passed assign the old value, not the current value from source + v = (act[i] >> DEL_MULT_SHIFT); + } //endif diff + } //endif speed //========== CURVES =============== if (apply_offset_and_curve && md->curveParam && md->curveMode == MODE_CURVE) { v = applyCurve(v, md->curveParam); } - int16_t weight = GET_GVAR(MD_WEIGHT(md), -500, 500, s_perout_flight_phase); + // int16_t weight = GET_GVAR(MD_WEIGHT(md), -500, 500, s_perout_flight_phase); //========== WEIGHT =============== // @@@2 recalculate weight to a 256 basis which ease the calculation later a lot - int32_t dv = (int32_t) v * calc100to256_16Bits(weight); + // int32_t dv = (int32_t) v * calc100to256_16Bits(weight); + int32_t dv = (int32_t) v * weight; +#ifdef SPEED_AFTER_WEIGHT //========== SPEED =============== if (mode == e_perout_mode_normal && (md->speedUp || md->speedDown)) { // there are delay values @@ -2759,7 +2836,8 @@ void perOut(uint8_t mode, uint8_t tick10ms) dv = act[i]; } //endif diff } //endif speed - +#endif + //========== DIFFERENTIAL ========= if (md->curveMode == MODE_DIFFERENTIAL) { /* int8_t curveParam = GET_GVAR(md->curveParam, -100, 100, s_perout_flight_phase); @@ -2956,18 +3034,22 @@ void doMixerCalculations() if (g_model.thrTraceSrc > NUM_POTS) { uint8_t ch = g_model.thrTraceSrc-NUM_POTS-1; val = g_chans512[ch]; + int8_t gModelMax=g_model.limitData[ch].max; + int8_t gModelMin=g_model.limitData[ch].min; + if (g_model.limitData[ch].revert) - val = -val + calc100toRESX(g_model.limitData[ch].max + 100); + val = -val + calc100toRESX(gModelMax + 100); else - val = val - calc100toRESX(g_model.limitData[ch].min - 100); + val = val - calc100toRESX(gModelMin - 100); #if defined(PPM_LIMITS_SYMETRICAL) if (g_model.limitData[ch].symetrical) val -= calc1000toRESX(g_model.limitData[ch].offset); #endif - // @@@ open.20.fsguruh optimized calculation; now *16 /16 instead of 10 base - val = (val << 4) / (16+((g_model.limitData[ch].max-g_model.limitData[ch].min)>>5)); + // @@@ open.20.fsguruh optimized calculation; now *8 /8 instead of 10 base; unfortunately *16/16 causes overruns + gModelMax-=gModelMin; // we compare difference between Max and Mix for recaling needed; Max and Min are shifted to 0 by default + gModelMax>>=4; + if (gModelMax) val = (val << 3) / (8+gModelMax); // recaling only needed if Min, Max differs if (val<0) val=0; // prevent val be negative, which would corrupt throttle trace and timers; could occur if safetyswitch is smaller than limits - // val = val * 10 / (10+(g_model.limitData[ch].max-g_model.limitData[ch].min)/20); } else { val = RESX + calibratedStick[g_model.thrTraceSrc == 0 ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1]; @@ -3197,16 +3279,28 @@ void perMain() if (delta > 0 && delta < MAX_MIXER_DELTA) { // @@@ open.20.fsguruh // SLEEP(); // wouldn't that make sense? should save a lot of battery power!!! -/* for future use; currently very very beta... +/* for future use; currently very very beta... */ +#ifdef POWER_SAVE ADCSRA&=0x7F; // disable ADC for power saving ACSR&=0xF7; // disable ACIE Interrupts ACSR|=0x80; // disable Analog Comparator + // maybe we disable here a lot more hardware components in future to save even more power - MCUCR|=0x24; // enable Sleep (bit5) enable ADC Noise Reduction (bit2) - asm volatile(" sleep \n\t"); // if _SLEEP() is not defined use this + MCUCR|=0x20; // enable Sleep (bit5) + // MCUCR|=0x28; // enable Sleep (bit5) enable ADC Noise Reduction (bit3) + // first tests showed: simple sleep would reduce cpu current from 40.5mA to 32.0mA + // noise reduction sleep would reduce it down to 28.5mA; However this would break pulses in theory + // however with standard module, it will need about 95mA. Therefore the drop to 88mA is not much noticable + do { + asm volatile(" sleep \n\t"); // if _SLEEP() is not defined use this + t0=getTmr16KHz(); + delta= (nextMixerEndTime - lastMixerDuration) - t0; + } while ((delta>0) && (delta a count is not done which make the clock very inaccurate + Therefore in this case it is better the own ISR is called again and counts the clock. per10ms is not called twice, because the counters are resetted and on a high level. +Because this lines increases the CPU load of the ISR it makes it even worse + */ +/* cli(); PAUSE_10MS_INTERRUPT(); - sei(); + sei(); */ + static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; no start value needed + // without correction we are 0,16% too fast; that mean in one hour we are 5,76Sek too fast; we do not like that #if defined(PCBGRUVIN9X) - static uint8_t accuracyWarble = 4; // because 16M / 1024 / 100 = 156.25. So bump every 4. - uint8_t bump = (!(accuracyWarble++ & 0x03)) ? 157 : 156; + // static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; not start value needed + uint8_t bump = (!(++accuracyWarble & 0x03)) ? 157 : 156; OCR2A += bump; #elif defined(AUDIO) || defined(VOICE) OCR0 += 2; // interrupt every 128us + // warble is done later for AUDIO or VOICE don't do it here because it will destroy audio #else - static uint8_t accuracyWarble = 4; // because 16M / 1024 / 100 = 156.25. So bump every 4. - uint8_t bump = (!(accuracyWarble++ & 0x03)) ? 157 : 156; + // static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; not start value needed + // each tick we are 0,016msec too fast + // one tick more means 0,048 msec too slow = 3*0,016msec --> in sum we are correct!!! + // therefore every 4 round we need to slow down one tick + uint8_t bump = (!(++accuracyWarble & 0x03)) ? 157 : 156; OCR0 += bump; #endif @@ -3422,7 +3530,7 @@ ISR(TIMER_10MS_VECT, ISR_NOBLOCK) // 10ms timer VOICE_DRIVER(); #endif - static uint8_t cnt10ms = 77; // execute 10ms code once every 78 ISRs + static uint8_t cnt10ms; // no initialization needed here; execute 10ms code once every 78 ISRs; takes 16.38msec to overrun for first round --> no problem #if defined(FRSKY) || defined(MAVLINK) || defined(JETI) if (cnt10ms == 30) { @@ -3431,9 +3539,14 @@ ISR(TIMER_10MS_VECT, ISR_NOBLOCK) // 10ms timer } #endif - if (cnt10ms-- == 0) { // BEGIN { ... every 10ms ... } + if (--cnt10ms == 0) { // BEGIN { ... every 10ms ... } // Begin 10ms event - cnt10ms = 77; + // cnt10ms = 78; + cnt10ms = (!(++accuracyWarble &0x07)) ? 79 : 78; + // each per10ms() we are 0,016msec too fast + // one tick more means 0,112 msec too slow = 7*0,016msec --> in sum we are correct!!! + // therefore every 8. round we need to slow down one tick + #endif AUDIO_HEARTBEAT(); @@ -3448,9 +3561,10 @@ ISR(TIMER_10MS_VECT, ISR_NOBLOCK) // 10ms timer } // end 10ms event #endif +/* cli(); RESUME_10MS_INTERRUPT(); - sei(); + sei(); */ } // Timer3 used for PPM_IN pulse width capture. Counter running at 16MHz / 8 = 2MHz From 83dedd83bc5df9d6a03158b52d32f5997e39fb46 Mon Sep 17 00:00:00 2001 From: "open.20.fsguruh@xoxy.net" Date: Sun, 24 Feb 2013 14:11:12 +0000 Subject: [PATCH 09/18] changed ISR masking back to old solution; throttle rescale bugfix with limits changed; This change make ISR masking for 10msec call back to the old solution. Bugfix for throttle time calculation if limits are changed. Noticed my optimized recalculation routine was wrong. Now I changed it and recalculation is done on 32bit basis which cause 16bytes more flash. I also noticed the original version (in trunk) just does recalculation of 10 base. That means for full throttle range there would be only 10 steps noticed if limits are changed. That leads to a very unpresise calculation if your throttle limit do not match these steps. I can do something similar but this time only with 8 steps but save 16bytes flash. What's your opinion. --- src/myeeprom.h | 2 +- src/open9x.cpp | 127 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 106 insertions(+), 23 deletions(-) diff --git a/src/myeeprom.h b/src/myeeprom.h index 01badeb58..6a688ff8b 100644 --- a/src/myeeprom.h +++ b/src/myeeprom.h @@ -58,7 +58,7 @@ #elif defined(PCBGRUVIN9X) #define EEPROM_VER 213 #else -#define EEPROM_VER 212 +#define EEPROM_VER 213 #endif #ifndef PACK diff --git a/src/open9x.cpp b/src/open9x.cpp index a79f7291a..d338eb8a6 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -1815,8 +1815,8 @@ void getADC() { uint16_t temp_ana; for (uint8_t adc_input=0; adc_input<8; adc_input++) { +/* ADMUX = adc_input|ADC_VREF_TYPE; -/* // ADCSRA|=0x08; // enable ADC Interrupt Enable should not be necessary? or is it MCUCR|=0x28; // enable Sleep (bit5) enable ADC Noise Reduction (bit3) asm volatile(" sleep \n\t"); // if _SLEEP() is not defined use this @@ -3034,21 +3034,26 @@ void doMixerCalculations() if (g_model.thrTraceSrc > NUM_POTS) { uint8_t ch = g_model.thrTraceSrc-NUM_POTS-1; val = g_chans512[ch]; - int8_t gModelMax=g_model.limitData[ch].max; - int8_t gModelMin=g_model.limitData[ch].min; + int16_t gModelMax=calc100toRESX(g_model.limitData[ch].max)+1024; + int16_t gModelMin=calc100toRESX(g_model.limitData[ch].min)-1024; if (g_model.limitData[ch].revert) - val = -val + calc100toRESX(gModelMax + 100); + val = -val + gModelMax; else - val = val - calc100toRESX(gModelMin - 100); + val = val - gModelMin; #if defined(PPM_LIMITS_SYMETRICAL) if (g_model.limitData[ch].symetrical) val -= calc1000toRESX(g_model.limitData[ch].offset); #endif - // @@@ open.20.fsguruh optimized calculation; now *8 /8 instead of 10 base; unfortunately *16/16 causes overruns + // @@@ open.20.fsguruh optimized calculation; now *8 /8 instead of 10 base; (*16/16 already cause a overrun; unsigned calculation also not possible, because v may be negative) gModelMax-=gModelMin; // we compare difference between Max and Mix for recaling needed; Max and Min are shifted to 0 by default - gModelMax>>=4; - if (gModelMax) val = (val << 3) / (8+gModelMax); // recaling only needed if Min, Max differs + // usually max is 1024 min is -1024 --> max-min = 2048 full range / 128 = max 16 steps + + // gModelMax>>=(10-2); + // if (gModelMax!=8) val = (val << 3) / (gModelMax); // recaling only needed if Min, Max differs + + if (gModelMax!=2048) val = (int32_t) (val << 11) / (gModelMax); // recaling only needed if Min, Max differs + // val = val * 10 / (10+(g_model.limitData[ch].max-g_model.limitData[ch].min)/20); if (val<0) val=0; // prevent val be negative, which would corrupt throttle trace and timers; could occur if safetyswitch is smaller than limits } else { @@ -3488,19 +3493,97 @@ uint16_t getTmr16KHz() } } - -ISR(TIMER_10MS_VECT, ISR_NOBLOCK) // 10ms timer +/* +// ISR(TIMER_10MS_VECT, ISR_NOBLOCK) // 10ms timer +ISR(TIMER_10MS_VECT) +// 10ms timer { - /* open.20.fsguruh is this good for anything? What should that improve? - The best thing is it would make timers inaccurate. - If ISR is too fast, the whole thing doesn't make sense, so we assume the CPU is fast enough to handle usual turns - In case per10ms is called and it takes longer a IRQ is missed --> a count is not done which make the clock very inaccurate - Therefore in this case it is better the own ISR is called again and counts the clock. per10ms is not called twice, because the counters are resetted and on a high level. -Because this lines increases the CPU load of the ISR it makes it even worse - */ -/* cli(); + static uint8_t accuracyWarble; + +#if defined(PCBGRUVIN9X) + // static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; not start value needed + uint8_t bump = (!(++accuracyWarble & 0x03)) ? 157 : 156; + OCR2A += bump; +#elif defined(AUDIO) || defined(VOICE) + + // static uint8_t accuracyWarble; + // because in this compile option the ISR is called each 128usec we prevent reentrant calls with a check to highest bit if accuracyWarble + // this is not necessary for the other builds, because we expect there would not be a reentrant call in 10msec time + OCR0 += 2; // interrupt every 128us + // warble is done later for AUDIO or VOICE don't do it here because it will destroy audio + + static int8_t cnt10ms; // no initialization needed here; execute 10ms code once every 78 ISRs; takes 16.38msec to overrun for first round --> no problem + + cnt10ms--; + + if (accuracyWarble&0x80) return; // is we are already in ISR leave it as soon as possible to free CPU, at least we had the ISR counted + accuracyWarble|=0x80; // sign we are currently in ISR + + bool doPer10msCall=false; + + if (cnt10ms<=0) { // also allow smaller values + cnt10ms += (!(++accuracyWarble &0x07)) ? 79 : 78; // instead of assigning we add to take missed ISRs into account + accuracyWarble|=0x80; // reset sign bit, because ++accuracyWarble may deleted it because of overrun + doPer10msCall=true; + } + +#else + static uint8_t accuracyWarble; + // static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; not start value needed + // each tick we are 0,016msec too fast + // one tick more means 0,048 msec too slow = 3*0,016msec --> in sum we are correct!!! + // therefore every 4 round we need to slow down one tick + uint8_t bump = (!(++accuracyWarble & 0x03)) ? 157 : 156; + OCR0 += bump; +#endif + sei(); + +#if defined(PCBSTD) && (defined(AUDIO) || defined(VOICE)) + +#if defined(AUDIO) + AUDIO_DRIVER(); +#endif + +#if defined(VOICE) + VOICE_DRIVER(); +#endif + +#if defined(FRSKY) || defined(MAVLINK) || defined(JETI) + if (cnt10ms == 30) { + if (!IS_DSM2_SERIAL_PROTOCOL(s_current_protocol)) + telemetryPoll10ms(); + } +#endif + + if (doPer10msCall) { // BEGIN { ... every 10ms ... } + // Begin 10ms event +#endif + + AUDIO_HEARTBEAT(); + +#if defined(HAPTIC) + HAPTIC_HEARTBEAT(); +#endif + + per10ms(); + +#if defined(PCBSTD) && (defined(AUDIO) || defined(VOICE)) + } // end 10ms event +#endif + + + + accuracyWarble&=0x7F; + +} +*/ + + +ISR(TIMER_10MS_VECT, ISR_NOBLOCK) // 10ms timer +{ + cli(); PAUSE_10MS_INTERRUPT(); - sei(); */ + sei(); static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; no start value needed // without correction we are 0,16% too fast; that mean in one hour we are 5,76Sek too fast; we do not like that @@ -3561,10 +3644,10 @@ Because this lines increases the CPU load of the ISR it makes it even worse } // end 10ms event #endif -/* - cli(); + + // cli(); RESUME_10MS_INTERRUPT(); - sei(); */ + // sei(); } // Timer3 used for PPM_IN pulse width capture. Counter running at 16MHz / 8 = 2MHz From 5c8d5dbd950560aceae25c4d10b0590d820e4c60 Mon Sep 17 00:00:00 2001 From: "open.20.fsguruh@xoxy.net" Date: Mon, 25 Feb 2013 22:08:23 +0000 Subject: [PATCH 10/18] Now extended range for weight, offset also without GVARS, GVARS fix, overruns fix This version holds mainly bugfixes and prepares this branch for merge. Please test and report. - gvars didn't work for value ranges usually from -100 to 100 (e.g. diff function) - gvars shown wrong also in models menu (same in trunk as well) - gbirkus tested ranges with speed optimizations, overruns occured at half range of trunk version. Now this is fixed and a much bigger range can be used. Still overruns possible. Please test. - ISR 10msec now safe and code optimized. --- src/menus.cpp | 4 +- src/menus.h | 2 +- src/model_menus.cpp | 19 +++--- src/myeeprom.h | 108 ++++++++++++++++++--------------- src/open9x.cpp | 143 +++++++++++++++++++++++--------------------- 5 files changed, 147 insertions(+), 129 deletions(-) diff --git a/src/menus.cpp b/src/menus.cpp index a062cc6b9..0f49c925e 100644 --- a/src/menus.cpp +++ b/src/menus.cpp @@ -706,10 +706,10 @@ int16_t gvarMenuItem(uint8_t x, uint8_t y, int16_t value, int16_t min, int16_t m return value; } #else -int8_t gvarMenuItem(uint8_t x, uint8_t y, int8_t value, int8_t min, int8_t max, LcdFlags attr, uint8_t event) +int8_t gvarMenuItem(uint8_t x, uint8_t y, int8_t value, int16_t min, int16_t max, LcdFlags attr, uint8_t event) { lcd_outdezAtt(x, y, value, attr); - if (attr&INVERS) CHECK_INCDEC_MODELVAR(event, value, min, max); + if (attr&INVERS) value = checkIncDec(event, value, min, max,EE_MODEL); return value; } #endif diff --git a/src/menus.h b/src/menus.h index da178a29d..38b539898 100644 --- a/src/menus.h +++ b/src/menus.h @@ -214,7 +214,7 @@ int8_t switchMenuItem(uint8_t x, uint8_t y, int8_t value, LcdFlags attr, uint8_t int16_t gvarMenuItem(uint8_t x, uint8_t y, int16_t value, int16_t min, int16_t max, LcdFlags attr, uint8_t event); // @@@ open.20.fsguruh #define displayGVar(x, y, v, min, max) gvarMenuItem(x, y, v, min, max, 0, 0) #else -int8_t gvarMenuItem(uint8_t x, uint8_t y, int8_t value, int8_t min, int8_t max, LcdFlags attr, uint8_t event); +int8_t gvarMenuItem(uint8_t x, uint8_t y, int8_t value, int16_t min, int16_t max, LcdFlags attr, uint8_t event); #define displayGVar(x, y, v, min, max) lcd_outdez8(x, y, v) #endif diff --git a/src/model_menus.cpp b/src/model_menus.cpp index 12a60cb3d..562ffee53 100644 --- a/src/model_menus.cpp +++ b/src/model_menus.cpp @@ -1901,15 +1901,15 @@ enum MixFields { void gvarWeightItem(xcoord_t x, uint8_t y, MixData *md, uint8_t attr, uint8_t event) { -#if defined(CPUARM) || !defined(GVARS) - md->weight = gvarMenuItem(x, y, md->weight, -125, 125, attr, event); -#else +// #if defined(CPUARM) || !defined(GVARS) +// md->weight = gvarMenuItem(x, y, md->weight, -125, 125, attr, event); +// #else // @@@ open.20.fsguruh u_int8int16_t weight; MD_GETWEIGHT(weight,md); weight.word = gvarMenuItem(x, y, weight.word, -245, 245, attr, event); MD_SETWEIGHT(weight,md); -#endif +//#endif } void menuModelMixOne(uint8_t event) @@ -1984,15 +1984,15 @@ void menuModelMixOne(uint8_t event) case MIX_FIELD_OFFSET: { lcd_putsColumnLeft(COLUMN_X, y, NO_INDENT(STR_OFFSET)); -#if defined(GVARS) +// #if defined(GVARS) // @@@ open.20.fsguruh u_int8int16_t offset; MD_GETOFFSET(offset,md2); offset.word = gvarMenuItem(COLUMN_X+MIXES_2ND_COLUMN, y, offset.word, -245, 245, attr|LEFT, event); MD_SETOFFSET(offset,md2); -#else - md2->offset = gvarMenuItem(COLUMN_X+MIXES_2ND_COLUMN, y, md2->offset, -125, 125, attr|LEFT, event); -#endif +// #else +// md2->offset = gvarMenuItem(COLUMN_X+MIXES_2ND_COLUMN, y, md2->offset, -125, 125, attr|LEFT, event); +// #endif break; } case MIX_FIELD_TRIM: @@ -2305,7 +2305,8 @@ void menuModelExpoMix(uint8_t expo, uint8_t event) if (md->curveMode == MODE_CURVE) putsCurve(12*FW+2, y, md->curveParam); else - displayGVar(15*FW+2, y, md->curveParam, -125, 125); + displayGVar(15*FW+2, y, md->curveParam, -100, 100); // open.20.fsguruh + // displayGVar(15*FW+2, y, md->curveParam, -125, 125); // only -100 to +100 is allowed now } if (md->swtch) putsSwitches(16*FW, y, md->swtch); diff --git a/src/myeeprom.h b/src/myeeprom.h index 6a688ff8b..2eac0d9bc 100644 --- a/src/myeeprom.h +++ b/src/myeeprom.h @@ -284,7 +284,39 @@ PACK(typedef struct t_MixData { int8_t offset; char name[LEN_EXPOMIX_NAME]; }) MixData; + #define MD_WEIGHT(md) (md->weight) +#define MD_GETWEIGHT(var,md) var.word=md->weight +#define MD_SETWEIGHT(var,md) md->weight= var.word + +PACK( union u_int8int16_t { + struct { + int8_t lo; + uint8_t hi; + } bytes_t; + int16_t word; +}); + +PACK( union u_gvarint_t { + struct { + int8_t lo; + uint8_t hi; + } bytes_t; + int16_t word; + + u_gvarint_t(int8_t l, uint8_t h) {bytes_t.lo=l; bytes_t.hi=h?255:0;} // hi bit is negativ sign + +private: + // prevent unwanted constructors, also saves program + u_gvarint_t() {} + u_gvarint_t(const u_gvarint_t&) {} +}); + +#define MD_OFFSET(md) (u_gvarint_t(md->offset,md->offsetMode).word) +#define MD_GETOFFSET(var,md) var.bytes_t.lo=md->offset; var.bytes_t.hi=md->offsetMode?255:0 +#define MD_SETOFFSET(var,md) md->offset = var.bytes_t.lo; \ + if (var.word<0) md->offsetMode=1; else md->offsetMode=0 // set negative sign + #else #define DELAY_STEP 2 #define SLOW_STEP 2 @@ -310,60 +342,40 @@ PACK(typedef struct t_MixData { int8_t curveParam; int8_t offset; }) MixData; -#if defined(GVARS) - // @@@ open.20.fsguruh - PACK( union u_gvarint_t { - struct { - int8_t lo; - uint8_t hi; - } bytes_t; - int16_t word; - - u_gvarint_t(int8_t l, uint8_t h) {bytes_t.lo=l; bytes_t.hi=h?255:0;} // hi bit is negativ sign - private: - // prevent unwanted constructors, also saves program - u_gvarint_t() {} - u_gvarint_t(const u_gvarint_t&) {} - }); - #define MD_WEIGHT(md) (u_gvarint_t(md->weight,md->weightMode).word) +// @@@ open.20.fsguruh +PACK( union u_gvarint_t { + struct { + int8_t lo; + uint8_t hi; + } bytes_t; + int16_t word; + + u_gvarint_t(int8_t l, uint8_t h) {bytes_t.lo=l; bytes_t.hi=h?255:0;} // hi bit is negativ sign + +private: + // prevent unwanted constructors, also saves program + u_gvarint_t() {} + u_gvarint_t(const u_gvarint_t&) {} +}); +#define MD_WEIGHT(md) (u_gvarint_t(md->weight,md->weightMode).word) - PACK( union u_int8int16_t { - struct { - int8_t lo; - uint8_t hi; - } bytes_t; - int16_t word; - }); - #define MD_GETWEIGHT(var,md) var.bytes_t.lo=md->weight; var.bytes_t.hi=md->weightMode?255:0 - #define MD_SETWEIGHT(var,md) md->weight = var.bytes_t.lo; \ +PACK( union u_int8int16_t { + struct { + int8_t lo; + uint8_t hi; + } bytes_t; + int16_t word; +}); +#define MD_GETWEIGHT(var,md) var.bytes_t.lo=md->weight; var.bytes_t.hi=md->weightMode?255:0 +#define MD_SETWEIGHT(var,md) md->weight = var.bytes_t.lo; \ if (var.word<0) md->weightMode=1; else md->weightMode=0 // set negative sign - #define MD_OFFSET(md) (u_gvarint_t(md->offset,md->offsetMode).word) - #define MD_GETOFFSET(var,md) var.bytes_t.lo=md->offset; var.bytes_t.hi=md->offsetMode?255:0 - #define MD_SETOFFSET(var,md) md->offset = var.bytes_t.lo; \ +#define MD_OFFSET(md) (u_gvarint_t(md->offset,md->offsetMode).word) +#define MD_GETOFFSET(var,md) var.bytes_t.lo=md->offset; var.bytes_t.hi=md->offsetMode?255:0 +#define MD_SETOFFSET(var,md) md->offset = var.bytes_t.lo; \ if (var.word<0) md->offsetMode=1; else md->offsetMode=0 // set negative sign -#else - #define MD_WEIGHT(md) (md->weight) -#endif -#endif -#if defined(GVARS) -// @@@ open.20.fsguruh -#ifndef MD_OFFSET - PACK( union u_int8int16_t { - struct { - int8_t lo; - uint8_t hi; - } bytes_t; - int16_t word; - }); - #define MD_OFFSET(md) (md->offsetMode ? (int16_t)GV1_LARGE+md->offset : md->offset) - #define MD_GETOFFSET(var,md) var.word=md->offset - #define MD_SETOFFSET(var,md) md->offset=var.word -#endif -#else -#define MD_OFFSET(md) (md->offset) #endif #if defined(CPUARM) diff --git a/src/open9x.cpp b/src/open9x.cpp index d338eb8a6..f7911271c 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -733,7 +733,7 @@ int16_t calcRESXto1000(int16_t x) // return x/1.024 // channel = channelnumber -1; // value = outputvalue with 100 mulitplied usual range -102400 to 102400; output -1024 to 1024 // changed rescaling from *100 to *256 to optimize performance -// rescaled to -262144)) to 262144)) +// rescaled from -262144 to 262144 int16_t applyLimits(uint8_t channel, int32_t value) { LimitData * limit = limitaddress(channel); @@ -744,6 +744,12 @@ int16_t applyLimits(uint8_t channel, int32_t value) if (ofs > lim_p) ofs = lim_p; if (ofs < lim_n) ofs = lim_n; + // thanks to gbirkus, he motivated this change, which greatly reduces overruns + // unfortunately the constants and 32bit compares generates about 52 bytes codes; didn't find a way to get it down. + // But I think it is worth + if (value>RESXl*256) value = RESXl*256; // no bigger values allowed, prevent early overruns + if (value<-RESXl*256) value = -RESXl*256; // no smaller values allowed, prevent early overruns + #if defined(PPM_LIMITS_SYMETRICAL) if (value) { int16_t tmp; @@ -1241,7 +1247,7 @@ uint8_t s_gvar_last = 0; #if defined(CPUM64) int16_t getGVarValue(int16_t x, int16_t min, int16_t max) { - if (x > (GV1_LARGE-6)) { // @@@ open.20.fsguruh + if ((x > (GV1_LARGE-6)) || (x>max)) { // @@@ open.20.fsguruh int8_t idx = (max <= 100 ? x - GV1_SMALL : x - GV1_LARGE); int8_t mul = 1; @@ -1715,7 +1721,7 @@ uint8_t checkTrim(uint8_t event) after = TRIM_MAX; if (after < TRIM_MIN) after = TRIM_MIN; - after /= 4; + after>>=2; // open.20.fsguruh after/=4; after += 60; #endif @@ -2631,7 +2637,6 @@ void perOut(uint8_t mode, uint8_t tick10ms) passDirtyChannels |= (bitfield_channels_t) 1 << md->destCh; if (k-MIXSRC_CH1+1 < md->destCh || pass > 0) v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before - // v = chans[k-MIXSRC_CH1+1] / 100; } } #else @@ -2646,7 +2651,6 @@ void perOut(uint8_t mode, uint8_t tick10ms) passDirtyChannels |= (bitfield_channels_t) 1 << md->destCh; if (k-MIXSRC_CH1+1 < md->destCh || pass > 0) v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before - // v = chans[k-MIXSRC_CH1+1] / 100; } } #endif @@ -2737,6 +2741,8 @@ void perOut(uint8_t mode, uint8_t tick10ms) //========== SPEED =============== + // now its on input side, but without weight compensation. More like other remote controls + // lower weight causes slower movement if (mode == e_perout_mode_normal && (md->speedUp || md->speedDown)) { // there are delay values #define DEL_MULT_SHIFT 8 @@ -2750,14 +2756,7 @@ void perOut(uint8_t mode, uint8_t tick10ms) // the value in md->speedXXX gives the time it should take to do a full movement from -100 to 100 therefore 200%. This equals 2048 in recalculated internal range if (tick10ms) { // only if already time is passed add or substract a value according the speed configured - // codesaving 44 bytes -#ifndef WEIGHTSPEEDCORRECTION int32_t rate = (int32_t) tick10ms << (DEL_MULT_SHIFT+11); // = DEL_MULT*2048*tick10ms -#else - // codecost 82 bytes - int32_t rate = (int32_t) tick10ms << (DEL_MULT_SHIFT+19); // = DEL_MULT*2048*tick10ms - rate/=abs(weight); -#endif // rate equals a full range for one second; if less time is passed rate is accordingly smaller // if one second passed, rate would be 2048 (full motion)*256(recalculated weight)*100(100 ticks needed for one second) @@ -2795,6 +2794,7 @@ void perOut(uint8_t mode, uint8_t tick10ms) int32_t dv = (int32_t) v * weight; #ifdef SPEED_AFTER_WEIGHT +// todo remove is there is no need anymore... //========== SPEED =============== if (mode == e_perout_mode_normal && (md->speedUp || md->speedDown)) { // there are delay values @@ -2840,12 +2840,6 @@ void perOut(uint8_t mode, uint8_t tick10ms) //========== DIFFERENTIAL ========= if (md->curveMode == MODE_DIFFERENTIAL) { -/* int8_t curveParam = GET_GVAR(md->curveParam, -100, 100, s_perout_flight_phase); - if (curveParam > 0 && dv < 0) - dv = (dv * (100 - curveParam)) / 100; - else if (curveParam < 0 && dv > 0) - dv = (dv * (100 + curveParam)) / 100; */ - // @@@2 also recalculate curveParam to a 256 basis which ease the calculation later a lot int16_t curveParam=calc100to256(GET_GVAR(md->curveParam, -100, 100, s_perout_flight_phase)); if (curveParam > 0 && dv < 0) @@ -3049,10 +3043,10 @@ void doMixerCalculations() gModelMax-=gModelMin; // we compare difference between Max and Mix for recaling needed; Max and Min are shifted to 0 by default // usually max is 1024 min is -1024 --> max-min = 2048 full range / 128 = max 16 steps - // gModelMax>>=(10-2); - // if (gModelMax!=8) val = (val << 3) / (gModelMax); // recaling only needed if Min, Max differs + gModelMax>>=(10-2); + if (gModelMax!=8) val = (val << 3) / (gModelMax); // recaling only needed if Min, Max differs - if (gModelMax!=2048) val = (int32_t) (val << 11) / (gModelMax); // recaling only needed if Min, Max differs + // if (gModelMax!=2048) val = (int32_t) (val << 11) / (gModelMax); // recaling only needed if Min, Max differs // val = val * 10 / (10+(g_model.limitData[ch].max-g_model.limitData[ch].min)/20); if (val<0) val=0; // prevent val be negative, which would corrupt throttle trace and timers; could occur if safetyswitch is smaller than limits } @@ -3493,51 +3487,21 @@ uint16_t getTmr16KHz() } } -/* -// ISR(TIMER_10MS_VECT, ISR_NOBLOCK) // 10ms timer -ISR(TIMER_10MS_VECT) + + + + +ISR(TIMER_10MS_VECT, ISR_NOBLOCK) // 10ms timer { - static uint8_t accuracyWarble; - -#if defined(PCBGRUVIN9X) - // static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; not start value needed - uint8_t bump = (!(++accuracyWarble & 0x03)) ? 157 : 156; - OCR2A += bump; -#elif defined(AUDIO) || defined(VOICE) - // static uint8_t accuracyWarble; - // because in this compile option the ISR is called each 128usec we prevent reentrant calls with a check to highest bit if accuracyWarble - // this is not necessary for the other builds, because we expect there would not be a reentrant call in 10msec time - OCR0 += 2; // interrupt every 128us - // warble is done later for AUDIO or VOICE don't do it here because it will destroy audio +// cli(); +// PAUSE_10MS_INTERRUPT(); +// sei(); + + static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; no start value needed - static int8_t cnt10ms; // no initialization needed here; execute 10ms code once every 78 ISRs; takes 16.38msec to overrun for first round --> no problem - cnt10ms--; - - if (accuracyWarble&0x80) return; // is we are already in ISR leave it as soon as possible to free CPU, at least we had the ISR counted - accuracyWarble|=0x80; // sign we are currently in ISR - - bool doPer10msCall=false; - - if (cnt10ms<=0) { // also allow smaller values - cnt10ms += (!(++accuracyWarble &0x07)) ? 79 : 78; // instead of assigning we add to take missed ISRs into account - accuracyWarble|=0x80; // reset sign bit, because ++accuracyWarble may deleted it because of overrun - doPer10msCall=true; - } - -#else - static uint8_t accuracyWarble; - // static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; not start value needed - // each tick we are 0,016msec too fast - // one tick more means 0,048 msec too slow = 3*0,016msec --> in sum we are correct!!! - // therefore every 4 round we need to slow down one tick - uint8_t bump = (!(++accuracyWarble & 0x03)) ? 157 : 156; - OCR0 += bump; -#endif - sei(); - #if defined(PCBSTD) && (defined(AUDIO) || defined(VOICE)) #if defined(AUDIO) @@ -3548,6 +3512,8 @@ ISR(TIMER_10MS_VECT) VOICE_DRIVER(); #endif + static uint8_t cnt10ms; // no initialization needed here; execute 10ms code once every 78 ISRs; takes 16.38msec to overrun for first round --> no problem + #if defined(FRSKY) || defined(MAVLINK) || defined(JETI) if (cnt10ms == 30) { if (!IS_DSM2_SERIAL_PROTOCOL(s_current_protocol)) @@ -3555,8 +3521,14 @@ ISR(TIMER_10MS_VECT) } #endif - if (doPer10msCall) { // BEGIN { ... every 10ms ... } + if (--cnt10ms == 0) { // BEGIN { ... every 10ms ... } // Begin 10ms event + // cnt10ms = 78; + cnt10ms = (!(++accuracyWarble &0x07)) ? 79 : 78; // instead of assigning we add to take missed ISRs into account + // each per10ms() we are 0,016msec too fast + // one tick more means 0,112 msec too slow = 7*0,016msec --> in sum we are correct!!! + // therefore every 8. round we need to slow down one tick + #endif AUDIO_HEARTBEAT(); @@ -3570,14 +3542,45 @@ ISR(TIMER_10MS_VECT) #if defined(PCBSTD) && (defined(AUDIO) || defined(VOICE)) } // end 10ms event #endif - - - - accuracyWarble&=0x7F; - -} -*/ + + + // without correction we are 0,16% too fast; that mean in one hour we are 5,76Sek too fast; we do not like that +#if defined(PCBGRUVIN9X) + // static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; not start value needed + uint8_t bump = (!(++accuracyWarble & 0x03)) ? 157 : 156; + OCR2A += bump; +#elif defined(AUDIO) || defined(VOICE) + // simple solution; if ISR tooks longer this round is just ignored; The problem in this case is, it takes 16msec for the next ISR --> big gap in AUDIO, VOICE and a big fault in time calculaiton if it happens + // cnt10ms--; + OCR0 += 2; // interrupt every 128us +/* + // needs to change cnt10ms to int instead uint to allow cnt10ms to be negative + // code oost: 34 bytes + // this solutions all missed ISRs are counted and next ISR is in about 128usec (exception is wrap around) + cli(); + do { + cnt10ms--; + OCR0 += 2; // interrupt every 128us + } while ((OCR0=2)); // loop in case ISR tooks too long; If it laps around, ignore additional calcuation and stop anyway + // sei(); will be done anyway if ISR is left + */ +#else + // static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; not start value needed + // each tick we are 0,016msec too fast + // one tick more means 0,048 msec too slow = 3*0,016msec --> in sum we are correct!!! + // therefore every 4 round we need to slow down one tick + uint8_t bump = (!(++accuracyWarble & 0x03)) ? 157 : 156; + OCR0 += bump; +#endif + +} + + + +// #define OLD_VERSION + + #ifdef OLD_VERSION ISR(TIMER_10MS_VECT, ISR_NOBLOCK) // 10ms timer { @@ -3650,6 +3653,8 @@ ISR(TIMER_10MS_VECT, ISR_NOBLOCK) // 10ms timer // sei(); } +#endif + // Timer3 used for PPM_IN pulse width capture. Counter running at 16MHz / 8 = 2MHz // equating to one count every half millisecond. (2 counts = 1ms). Control channel // count delta values thus can range from about 1600 to 4400 counts (800us to 2200us), From 48a7090e6e126ec88147ea09132faff70ed66b4d Mon Sep 17 00:00:00 2001 From: bsongis Date: Tue, 26 Feb 2013 16:07:41 +0000 Subject: [PATCH 11/18] [all] Timers countdown / minute beep are configurable [stock] Port from a part of Andreas patch relative to timers. Thanks Andreas! It allowed me to implement the configurable beeps for timers at a almost 0 flash cost. --- src/menus.h | 12 ++-- src/open9x.cpp | 189 ++++++++++++++++--------------------------------- src/open9x.h | 3 +- 3 files changed, 68 insertions(+), 136 deletions(-) diff --git a/src/menus.h b/src/menus.h index 38b539898..8dfe59eea 100644 --- a/src/menus.h +++ b/src/menus.h @@ -35,15 +35,15 @@ #ifndef menus_h #define menus_h -#define NO_HI_LEN 25 +#define NO_HI_LEN 25 // RESX range is used for internal calculation; The menu says -100.0 to 100.0; internally it is -1024 to 1024 to allow some optimizations #define RESX_SHIFT 10 -#define RESX 1024 -#define RESXu 1024u -#define RESXul 1024ul -#define RESXl 1024l -#define RESKul 100ul +#define RESX 1024 +#define RESXu 1024u +#define RESXul 1024ul +#define RESXl 1024l +#define RESKul 100ul #define RESX_PLUS_TRIM (RESX+128) typedef void (*MenuFuncP)(uint8_t event); diff --git a/src/open9x.cpp b/src/open9x.cpp index f7911271c..f4c3fbe57 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -291,7 +291,7 @@ void per10ms() // These moved here from perOut() to improve beep trigger reliability. #if defined(PWM_BACKLIGHT) - if ((g_tmr10ms&0x03) == 0x00) // if (g_tmr10ms % 4 == 0) // (should be faster) + if ((g_tmr10ms&0x03) == 0x00) fadeBacklight(); // increment or decrement brightness until target brightness is reached #endif @@ -337,8 +337,8 @@ CurveInfo curveinfo(uint8_t idx) result.crv = curveaddress(idx); int8_t *next = curveaddress(idx+1); uint8_t size = next - result.crv; - if ((size & 1)==0) { // if (size % 2 == 0) { - result.points = (size / 2) + 1; // result.points = (size >> 1) + 1; done by the compiler anyway + if ((size & 1) == 0) { + result.points = (size / 2) + 1; result.custom = true; } else { @@ -564,7 +564,8 @@ int16_t expo(int16_t x, int16_t k) if (neg) x = -x; if (k<0) { y = RESXu-expou(RESXu-x, -k); - } else { + } + else { y = expou(x, k); } return neg? -y : y; @@ -1348,13 +1349,13 @@ void putsTelemetryValue(xcoord_t x, uint8_t y, lcdint_t val, uint8_t unit, uint8 } #endif -#define INAC_DEV_SHIFT 9 // @@@ shift right value for stick movement -#define INAC_DEVISOR 512 // Bypass splash screen with stick movement +#define INAC_DEVISOR 512 // bypass splash screen with stick movement +#define INAC_DEV_SHIFT 9 // shift right value for stick movement uint16_t stickMoveValue() { uint16_t sum = 0; - for (uint8_t i=0; i<4; i++) - sum += (anaIn(i)>>INAC_DEV_SHIFT); // sum += anaIn(i)/INAC_DEVISOR; + for (uint8_t i=0; i> INAC_DEV_SHIFT; return sum; } @@ -1721,7 +1722,7 @@ uint8_t checkTrim(uint8_t event) after = TRIM_MAX; if (after < TRIM_MIN) after = TRIM_MIN; - after>>=2; // open.20.fsguruh after/=4; + after >>= 2; after += 60; #endif @@ -1932,7 +1933,6 @@ static uint8_t lastSwPos[2] = {0, 0}; static uint16_t s_cnt[2] = {0, 0}; static uint16_t s_sum[2] = {0, 0}; static uint8_t sw_toggled[2] = {false, false}; -// static uint16_t s_time_cum_16[2] = {0, 0}; no longer needed using s_sum instead #if defined(THRTRACE) uint8_t s_traceBuf[MAXTRACE]; @@ -1983,8 +1983,7 @@ FORCEINLINE void evalTrims() if (g_eeGeneral.throttleReversed) trim = -trim; int16_t v = anas[i]; - int32_t vv = ((int32_t)trim-TRIM_MIN)*(RESX-v)>>(RESX_SHIFT+1); - // int32_t vv = ((int32_t)trim-TRIM_MIN)*(RESX-v)/(2*RESX); + int32_t vv = ((int32_t)trim-TRIM_MIN)*(RESX-v)>>(RESX_SHIFT+1); trim = vv; } else if (trimsCheckTimer > 0) { @@ -2004,8 +2003,7 @@ BeepANACenter evalSticks(uint8_t mode) if (g_model.swashR.value) { uint32_t v = (int32_t(calibratedStick[ELE_STICK])*calibratedStick[ELE_STICK] + int32_t(calibratedStick[AIL_STICK])*calibratedStick[AIL_STICK]); - uint32_t q = calc100toRESX(g_model.swashR.value); - // uint32_t q = int32_t(RESX)*g_model.swashR.value/100; + uint32_t q = calc100toRESX(g_model.swashR.value); q *= q; if (v>q) d = isqrt32(v); @@ -2082,9 +2080,8 @@ BeepANACenter evalSticks(uint8_t mode) } #ifdef HELI - if(d && (ch==ELE_STICK || ch==AIL_STICK)) - v = (int32_t(v)*calc100toRESX(g_model.swashR.value))/int32_t(d); - // v = int32_t(v)*g_model.swashR.value*RESX/(int32_t(d)*100); + if (d && (ch==ELE_STICK || ch==AIL_STICK)) + v = (int32_t(v)*calc100toRESX(g_model.swashR.value))/int32_t(d); #endif rawAnas[ch] = v; @@ -2519,18 +2516,13 @@ void perOut(uint8_t mode, uint8_t tick10ms) if(g_model.swashR.value) { uint32_t v = ((int32_t)anas[ELE_STICK]*anas[ELE_STICK] + (int32_t)anas[AIL_STICK]*anas[AIL_STICK]); - // uint32_t q = (int32_t)RESX*g_model.swashR.value/100; - uint32_t q = calc100toRESX(g_model.swashR.value); + uint32_t q = calc100toRESX(g_model.swashR.value); q *= q; - if(v>q) - { + if (v>q) { uint16_t d = isqrt32(v); - // @@@ open.20.fsguruh - // anas[ELE_STICK] = (int32_t)anas[ELE_STICK]*g_model.swashR.value*RESX/((int32_t)d*100); - // anas[AIL_STICK] = (int32_t)anas[AIL_STICK]*g_model.swashR.value*RESX/((int32_t)d*100); - int16_t tmp = calc100toRESX(g_model.swashR.value); - anas[ELE_STICK] = (int32_t) anas[ELE_STICK]*tmp/d; - anas[AIL_STICK] = (int32_t) anas[AIL_STICK]*tmp/d; + int16_t tmp = calc100toRESX(g_model.swashR.value); + anas[ELE_STICK] = (int32_t) anas[ELE_STICK]*tmp/d; + anas[AIL_STICK] = (int32_t) anas[AIL_STICK]*tmp/d; } } @@ -2636,7 +2628,7 @@ void perOut(uint8_t mode, uint8_t tick10ms) if (dirtyChannels & ((bitfield_channels_t)1 << (k-MIXSRC_CH1+1)) & (passDirtyChannels|~(((bitfield_channels_t) 1 << md->destCh)-1))) passDirtyChannels |= (bitfield_channels_t) 1 << md->destCh; if (k-MIXSRC_CH1+1 < md->destCh || pass > 0) - v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before + v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before } } #else @@ -2650,7 +2642,7 @@ void perOut(uint8_t mode, uint8_t tick10ms) if (dirtyChannels & ((bitfield_channels_t)1 << (k-MIXSRC_CH1+1)) & (passDirtyChannels|~(((bitfield_channels_t) 1 << md->destCh)-1))) passDirtyChannels |= (bitfield_channels_t) 1 << md->destCh; if (k-MIXSRC_CH1+1 < md->destCh || pass > 0) - v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before + v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before } } #endif @@ -2721,7 +2713,7 @@ void perOut(uint8_t mode, uint8_t tick10ms) //========== OFFSET =============== if (apply_offset_and_curve) { int16_t offset = GET_GVAR(MD_OFFSET(md), -245, 245, s_perout_flight_phase); // open.20.fsguruh - if (offset) v+=calc100toRESX_16Bits(offset); // @@@ open.20.fsguruh + if (offset) v += calc100toRESX_16Bits(offset); // @@@ open.20.fsguruh } //========== TRIMS =============== @@ -2737,7 +2729,7 @@ void perOut(uint8_t mode, uint8_t tick10ms) } int16_t weight = GET_GVAR(MD_WEIGHT(md), -500, 500, s_perout_flight_phase); - weight=calc100to256_16Bits(weight); + weight = calc100to256_16Bits(weight); //========== SPEED =============== @@ -2748,7 +2740,7 @@ void perOut(uint8_t mode, uint8_t tick10ms) #define DEL_MULT_SHIFT 8 // we recale to a mult 256 higher value for calculation - int16_t diff=v-(act[i]>>DEL_MULT_SHIFT); + int16_t diff = v - (act[i]>>DEL_MULT_SHIFT); if (diff) { // open.20.fsguruh: speed is defined in % movement per second; In menu we specify the full movement (-100% to 100%) = 200% in total // the unit of the stored value is the value from md->speedUp or md->speedDown divide SLOW_STEP seconds; e.g. value 4 means 4/SLOW_STEP = 2 seconds for CPU64 @@ -2756,7 +2748,7 @@ void perOut(uint8_t mode, uint8_t tick10ms) // the value in md->speedXXX gives the time it should take to do a full movement from -100 to 100 therefore 200%. This equals 2048 in recalculated internal range if (tick10ms) { // only if already time is passed add or substract a value according the speed configured - int32_t rate = (int32_t) tick10ms << (DEL_MULT_SHIFT+11); // = DEL_MULT*2048*tick10ms + int32_t rate = (int32_t) tick10ms << (DEL_MULT_SHIFT+11); // = DEL_MULT*2048*tick10ms // rate equals a full range for one second; if less time is passed rate is accordingly smaller // if one second passed, rate would be 2048 (full motion)*256(recalculated weight)*100(100 ticks needed for one second) @@ -2767,7 +2759,8 @@ void perOut(uint8_t mode, uint8_t tick10ms) int32_t newValue = act[i]+rate/((int16_t)(100/SLOW_STEP)*md->speedUp); if (newValuespeedDown>0) { // see explanation in speedUp int32_t newValue = act[i]-rate/((int16_t)(100/SLOW_STEP)*md->speedDown); @@ -2786,62 +2779,13 @@ void perOut(uint8_t mode, uint8_t tick10ms) v = applyCurve(v, md->curveParam); } - // int16_t weight = GET_GVAR(MD_WEIGHT(md), -500, 500, s_perout_flight_phase); - //========== WEIGHT =============== - // @@@2 recalculate weight to a 256 basis which ease the calculation later a lot - // int32_t dv = (int32_t) v * calc100to256_16Bits(weight); int32_t dv = (int32_t) v * weight; - -#ifdef SPEED_AFTER_WEIGHT -// todo remove is there is no need anymore... - //========== SPEED =============== - if (mode == e_perout_mode_normal && (md->speedUp || md->speedDown)) { // there are delay values - -#define DEL_MULT_SHIFT 8 - // we are lucky, the MULT_SHIFT factor actually fits to the weight recalculation; Therefore no adaption needed - // so weight is 256 and MULT factor is the same - - int32_t diff=dv-act[i]; - if (diff) { - // open.20.fsguruh: speed is defined in % movement per second; In menu we specify the full movement (-100% to 100%) = 200% in total - // the unit of the stored value is the value from md->speedUp or md->speedDown divide SLOW_STEP seconds; e.g. value 4 means 4/SLOW_STEP = 2 seconds for CPU64 - // because we get a tick each 10msec, we need 100 ticks for one second - // the value in md->speedXXX gives the time it should take to do a full movement from -100 to 100 therefore 200%. This equals 2048 in recalculated internal range - if (tick10ms) { - // only if already time is passed add or substract a value according the speed configured - - // codesaving 44 bytes - int32_t rate = (int32_t) tick10ms << (DEL_MULT_SHIFT+11); // = DEL_MULT*2048*tick10ms - // rate equals a full range for one second; if less time is passed rate is accordingly smaller - // if one second passed, rate would be 2048 (full motion)*256(recalculated weight)*100(100 ticks needed for one second) - - int32_t currentValue=dv; - if (diff>0) { - if (md->speedUp>0) { - // if a speed upwards is defined recalculate the new value according configured speed; the higher the speed the smaller the add value is - int32_t newValue = act[i]+rate/((int16_t)(100/SLOW_STEP)*md->speedUp); - if (newValuespeedDown>0) { - // see explanation in speedUp - int32_t newValue = act[i]-rate/((int16_t)(100/SLOW_STEP)*md->speedDown); - if (newValue>currentValue) currentValue=newValue; // Endposition; prevent toggling around the destination - } - } //endif diff>0 - act[i] = currentValue; - // open.20.fsguruh: this implementation would save about 50 bytes code - } // endif tick10ms ; in case no time passed assign the old value, not the current value from source - dv = act[i]; - } //endif diff - } //endif speed -#endif //========== DIFFERENTIAL ========= if (md->curveMode == MODE_DIFFERENTIAL) { - // @@@2 also recalculate curveParam to a 256 basis which ease the calculation later a lot - int16_t curveParam=calc100to256(GET_GVAR(md->curveParam, -100, 100, s_perout_flight_phase)); + // @@@2 also recalculate curveParam to a 256 basis which ease the calculation later a lot + int16_t curveParam = calc100to256(GET_GVAR(md->curveParam, -100, 100, s_perout_flight_phase)); if (curveParam > 0 && dv < 0) dv = (dv * (256 - curveParam)) >> 8; else if (curveParam < 0 && dv > 0) @@ -2850,7 +2794,8 @@ void perOut(uint8_t mode, uint8_t tick10ms) int32_t *ptr = &chans[md->destCh]; // Save calculating address several times - if (i == 0 || md->destCh != (md - 1)->destCh) *ptr = 0; + if (i == 0 || md->destCh != (md - 1)->destCh) + *ptr = 0; // if this is the first calculation for the destination channel, initialize it with 0 (otherwise would be random) switch (md->mltpx) { @@ -2862,10 +2807,10 @@ void perOut(uint8_t mode, uint8_t tick10ms) #endif break; case MLTPX_MUL: - // @@@2 we have to remove the weight factor of 256 in case of 100%; now we use the new base of 256 + // @@@2 we have to remove the weight factor of 256 in case of 100%; now we use the new base of 256 dv >>= 8; dv *= *ptr; - dv >>= RESX_SHIFT; // same as dv /= RESXl; + dv >>= RESX_SHIFT; // same as dv /= RESXl; *ptr = dv; break; default: // MLTPX_ADD @@ -2964,8 +2909,7 @@ void doMixerCalculations() s_perout_flight_phase = p; perOut(e_perout_mode_normal, tick10ms); for (uint8_t i=0; i>4) * fp_act[p]; // @@@ use shift right instead of division - // sum_chans512[i] += (chans[i] / 16) * fp_act[p]; + sum_chans512[i] += (chans[i] >> 4) * fp_act[p]; weight += fp_act[p]; } } @@ -2977,11 +2921,10 @@ void doMixerCalculations() perOut(e_perout_mode_normal, tick10ms); } - // @@@ open.20.fsguruh according to bsongis and also my this should be done here, hopefully tick10ms is set the first round; later on this should occur max each 10msec if (tick10ms) { - #if defined(CPUARM) +#if defined(CPUARM) requiredSpeakerVolume = g_eeGeneral.speakerVolume + VOLUME_LEVEL_DEF; - #endif +#endif // the reason this needs to be done before limits is the applyLimit function; it checks for safety switches which would be not initialized otherwise evalFunctions(); @@ -2994,11 +2937,9 @@ void doMixerCalculations() // at the end chans[i] = chans[i]/100 => -1024..1024 // interpolate value with min/max so we get smooth motion from center to stop // this limits based on v original values and min=-1024, max=1024 RESX=1024 - - // int32_t q = (s_fade_flight_phases ? (sum_chans512[i] / weight) * 16 : chans[i]); - int32_t q = (s_fade_flight_phases ? (sum_chans512[i] / weight) << 4 : chans[i]); - // @@@2 now remove the internal 256 100% basis by a simple shift operation - ex_chans[i]=q>>8; // for the next perMain + int32_t q = (s_fade_flight_phases ? (sum_chans512[i] / weight) << 4 : chans[i]); + // @@@2 now remove the internal 256 100% basis by a simple shift operation + ex_chans[i] = q>>8; // for the next perMain int16_t value = applyLimits(i, q); // applyLimits will remove the 256 100% basis @@ -3028,33 +2969,36 @@ void doMixerCalculations() if (g_model.thrTraceSrc > NUM_POTS) { uint8_t ch = g_model.thrTraceSrc-NUM_POTS-1; val = g_chans512[ch]; - int16_t gModelMax=calc100toRESX(g_model.limitData[ch].max)+1024; - int16_t gModelMin=calc100toRESX(g_model.limitData[ch].min)-1024; + + int16_t gModelMax = calc100toRESX(g_model.limitData[ch].max)+1024; + int16_t gModelMin = calc100toRESX(g_model.limitData[ch].min)-1024; if (g_model.limitData[ch].revert) val = -val + gModelMax; else val = val - gModelMin; + #if defined(PPM_LIMITS_SYMETRICAL) if (g_model.limitData[ch].symetrical) val -= calc1000toRESX(g_model.limitData[ch].offset); #endif // @@@ open.20.fsguruh optimized calculation; now *8 /8 instead of 10 base; (*16/16 already cause a overrun; unsigned calculation also not possible, because v may be negative) - gModelMax-=gModelMin; // we compare difference between Max and Mix for recaling needed; Max and Min are shifted to 0 by default + gModelMax -= gModelMin; // we compare difference between Max and Mix for recaling needed; Max and Min are shifted to 0 by default // usually max is 1024 min is -1024 --> max-min = 2048 full range / 128 = max 16 steps - gModelMax>>=(10-2); - if (gModelMax!=8) val = (val << 3) / (gModelMax); // recaling only needed if Min, Max differs + gModelMax >>= (10-2); + if (gModelMax != 8) + val = (val << 3) / (gModelMax); // recaling only needed if Min, Max differs // if (gModelMax!=2048) val = (int32_t) (val << 11) / (gModelMax); // recaling only needed if Min, Max differs // val = val * 10 / (10+(g_model.limitData[ch].max-g_model.limitData[ch].min)/20); - if (val<0) val=0; // prevent val be negative, which would corrupt throttle trace and timers; could occur if safetyswitch is smaller than limits + if (val<0) val=0; // prevent val be negative, which would corrupt throttle trace and timers; could occur if safetyswitch is smaller than limits } else { val = RESX + calibratedStick[g_model.thrTraceSrc == 0 ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1]; } -#ifndef CPUM64 +#if !defined(CPUM64) // code cost is about 16 bytes for higher throttle accuracy for timer // would not be noticable anyway, because all version up to this change had only 16 steps; // now it has already 32 steps; this define would increase to 128 steps @@ -3063,11 +3007,10 @@ void doMixerCalculations() #if defined(ACCURAT_THROTTLE_TIMER) - val >>= (RESX_SHIFT-6); // increase resolution by factor 4 + val >>= (RESX_SHIFT-6); // calibrate it (resolution increased by factor 4) #else - val >>= (RESX_SHIFT-4); // calibrate it @@@ open.20.fsguruh - // val /= (RESX/16); // calibrate it -#endif + val >>= (RESX_SHIFT-4); // calibrate it +#endif // Timers start @@ -3080,7 +3023,6 @@ void doMixerCalculations() s_timerState[i] = TMR_RUNNING; s_cnt[i] = 0; s_sum[i] = 0; - // s_time_cum_16[i] = 0; open.20.fsguruh: we have s_sum, that's enough } uint8_t atm = (tm >= 0 ? tm : TMR_VAROFS-tm-1); @@ -3118,14 +3060,13 @@ void doMixerCalculations() s_timerVal[i]++; // add second used of throttle s_sum[i]-=128*s_cnt[i]; } - s_cnt[i]=0; #else if ((s_sum[i]/s_cnt[i])>=32) { // throttle was normalized to 0 to 32 value (throttle/16*2 (because - range is added as well) s_timerVal[i]++; // add second used of throttle s_sum[i]-=32*s_cnt[i]; } - s_cnt[i]=0; #endif + s_cnt[i]=0; } else if (atm==TMRMODE_THR_TRG) { if (val || s_timerVal[i] > 0) @@ -3251,14 +3192,6 @@ void doMixerCalculations() } } -/* as proposed by bsongis, this is done now shortly before limits -#if defined(CPUARM) - requiredSpeakerVolume = g_eeGeneral.speakerVolume + VOLUME_LEVEL_DEF; -#endif - - evalFunctions(); -*/ - #if defined(DSM2) if (s_rangecheck_mode) AUDIO_PLAY(AU_FRSKY_CHEEP); #endif @@ -3279,13 +3212,13 @@ void perMain() // @@@ open.20.fsguruh // SLEEP(); // wouldn't that make sense? should save a lot of battery power!!! /* for future use; currently very very beta... */ -#ifdef POWER_SAVE - ADCSRA&=0x7F; // disable ADC for power saving - ACSR&=0xF7; // disable ACIE Interrupts - ACSR|=0x80; // disable Analog Comparator +#if defined(POWER_SAVE) + ADCSRA&=0x7F; // disable ADC for power saving + ACSR&=0xF7; // disable ACIE Interrupts + ACSR|=0x80; // disable Analog Comparator // maybe we disable here a lot more hardware components in future to save even more power - - MCUCR|=0x20; // enable Sleep (bit5) + + MCUCR|=0x20; // enable Sleep (bit5) // MCUCR|=0x28; // enable Sleep (bit5) enable ADC Noise Reduction (bit3) // first tests showed: simple sleep would reduce cpu current from 40.5mA to 32.0mA // noise reduction sleep would reduce it down to 28.5mA; However this would break pulses in theory @@ -3297,8 +3230,8 @@ void perMain() } while ((delta>0) && (delta #include -#if defined(PCBX9D) || defined(PCBACT) +#if defined(PCBX9D) #define IF_9X(x) #else #define IF_9X(x) x, @@ -1067,7 +1067,6 @@ extern int16_t ex_chans[NUM_CHNOUT]; // Outputs (before LIMITS) of th extern int16_t g_chans512[NUM_CHNOUT]; extern uint16_t BandGap; -// extern uint16_t expou(uint16_t x, uint16_t k); extern int16_t expo(int16_t x, int16_t k); extern int16_t intpol(int16_t, uint8_t); extern int16_t applyCurve(int16_t, int8_t); From 6fbbac60dc4e6d43286160ed5aced9123ad599bc Mon Sep 17 00:00:00 2001 From: "open.20.fsguruh@xoxy.net" Date: Tue, 26 Feb 2013 22:36:28 +0000 Subject: [PATCH 12/18] prevent arithmetic overflows This version should finally prevent nearly all arithmetic overflows. Unfortunately this costs over 100bytes flash. Please test and state your option. --- src/open9x.cpp | 108 ++++++++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 50 deletions(-) diff --git a/src/open9x.cpp b/src/open9x.cpp index f4c3fbe57..fa2c60e6d 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -1,6 +1,6 @@ /* * Authors (alphabetical order) - * - Andre Bernet + * - Andre Bernet * - Bertrand Songis * - Bryan J. Rentoul (Gruvin) * - Cameron Weeks @@ -730,6 +730,14 @@ int16_t calcRESXto1000(int16_t x) // return x/1.024 #endif +#define PREVENT_ARITHMETIC_OVERFLOW +// because of optimizations the reserves before overruns occurs is only the half +// this defines enables some checks the greatly improves this situation +// It should nearly prevent all overruns (is still a chance for it, but quite low) +// negative side is code cost 124 bytes flash + + + // @@@2 open.20.fsguruh ; // channel = channelnumber -1; // value = outputvalue with 100 mulitplied usual range -102400 to 102400; output -1024 to 1024 @@ -737,28 +745,37 @@ int16_t calcRESXto1000(int16_t x) // return x/1.024 // rescaled from -262144 to 262144 int16_t applyLimits(uint8_t channel, int32_t value) { - LimitData * limit = limitaddress(channel); - int16_t ofs = calc1000toRESX(limit->offset); // multiply to 1.24 to get range (-1024..1024) - int16_t lim_p = calc100toRESX(limit->max + 100); - int16_t lim_n = calc100toRESX(limit->min - 100); //multiply by 10.24 to get same range (-1024..1024) + LimitData * lim = limitaddress(channel); + int16_t ofs = calc1000toRESX(lim->offset); // multiply to 1.24 to get range (-1024..1024) + int16_t lim_p = calc100toRESX(lim->max + 100); + int16_t lim_n = calc100toRESX(lim->min - 100); //multiply by 10.24 to get same range (-1024..1024) if (ofs > lim_p) ofs = lim_p; if (ofs < lim_n) ofs = lim_n; +#ifdef PREVENT_ARITHMETIC_OVERFLOW // thanks to gbirkus, he motivated this change, which greatly reduces overruns - // unfortunately the constants and 32bit compares generates about 52 bytes codes; didn't find a way to get it down. + // unfortunately the constants and 32bit compares generates about 50 bytes codes; didn't find a way to get it down. // But I think it is worth - if (value>RESXl*256) value = RESXl*256; // no bigger values allowed, prevent early overruns - if (value<-RESXl*256) value = -RESXl*256; // no smaller values allowed, prevent early overruns + value = limit(int32_t(-RESXl*256),value,int32_t(RESXl*256)); // saves 2 bytes compared to solution below + // if (value>RESXl*256) value = RESXl*256; // no bigger values allowed, prevent early overruns + // if (value<-RESXl*256) value = -RESXl*256; // no smaller values allowed, prevent early overruns +#endif #if defined(PPM_LIMITS_SYMETRICAL) if (value) { int16_t tmp; - if (limit->symetrical) + if (lim->symetrical) tmp = (value > 0) ? (lim_p) : (-lim_n); else tmp = (value > 0) ? (lim_p - ofs) : (-lim_n + ofs); value = (int32_t) value * tmp; // div by 1024*256 -> output = -1024..1024 +#else + if (value) { + int16_t tmp = (value > 0) ? (lim_p - ofs) : (-lim_n + ofs); + value = (int32_t) value * tmp; // div by 1024*256 -> output = -1024..1024 +#endif + #ifdef CORRECT_NEGATIVE_SHIFTS int8_t sign=(value<0?1:0); value-=sign; @@ -772,28 +789,11 @@ int16_t applyLimits(uint8_t channel, int32_t value) ofs+=tmp; // ofs can to added directly because already recalculated, } -#else - if (value) { - int16_t tmp = (value > 0) ? (lim_p - ofs) : (-lim_n + ofs); - value = (int32_t) value * tmp; // div by 1024*256 -> output = -1024..1024 -#ifdef CORRECT_NEGATIVE_SHIFTS - int8_t sign=(value<0?1:0); - value-=sign; - tmp=value>>16; // that's quite tricky: the shiftright 16 operation is assmbled just with addressmove; just forget the two least significant bytes; - tmp>>=2; // now one simple shift right for two bytes does the rest - tmp+=sign; -#else - tmp=value>>16; // that's quite tricky: the shiftright 16 operation is assmbled just with addressmove; just forget the two least significant bytes; - tmp>>=2; // now one simple shift right for two bytes does the rest -#endif - ofs+=tmp; // ofs can to added directly because already recalculated, - } -#endif if (ofs > lim_p) ofs = lim_p; if (ofs < lim_n) ofs = lim_n; - if (limit->revert) ofs = -ofs; // finally do the reverse. + if (lim->revert) ofs = -ofs; // finally do the reverse. if (safetyCh[channel] != -128) // if safety channel available for channel check ofs = calc100toRESX(safetyCh[channel]); @@ -2728,18 +2728,16 @@ void perOut(uint8_t mode, uint8_t tick10ms) if (mix_trim >= 0) v += trims[mix_trim]; } - int16_t weight = GET_GVAR(MD_WEIGHT(md), -500, 500, s_perout_flight_phase); - weight = calc100to256_16Bits(weight); + // saves 12 bytes code if done here and not together with weight; unknown reason + int16_t weight = GET_GVAR(MD_WEIGHT(md), -500, 500, s_perout_flight_phase); + weight=calc100to256_16Bits(weight); - //========== SPEED =============== // now its on input side, but without weight compensation. More like other remote controls // lower weight causes slower movement if (mode == e_perout_mode_normal && (md->speedUp || md->speedDown)) { // there are delay values - #define DEL_MULT_SHIFT 8 // we recale to a mult 256 higher value for calculation - int16_t diff = v - (act[i]>>DEL_MULT_SHIFT); if (diff) { // open.20.fsguruh: speed is defined in % movement per second; In menu we specify the full movement (-100% to 100%) = 200% in total @@ -2751,7 +2749,6 @@ void perOut(uint8_t mode, uint8_t tick10ms) int32_t rate = (int32_t) tick10ms << (DEL_MULT_SHIFT+11); // = DEL_MULT*2048*tick10ms // rate equals a full range for one second; if less time is passed rate is accordingly smaller // if one second passed, rate would be 2048 (full motion)*256(recalculated weight)*100(100 ticks needed for one second) - int32_t currentValue=(v<0) { if (md->speedUp>0) { @@ -2793,7 +2790,7 @@ void perOut(uint8_t mode, uint8_t tick10ms) } int32_t *ptr = &chans[md->destCh]; // Save calculating address several times - + if (i == 0 || md->destCh != (md - 1)->destCh) *ptr = 0; // if this is the first calculation for the destination channel, initialize it with 0 (otherwise would be random) @@ -2816,8 +2813,18 @@ void perOut(uint8_t mode, uint8_t tick10ms) default: // MLTPX_ADD *ptr += dv; //Mixer output add up to the line (dv + (dv>0 ? 100/2 : -100/2))/(100); break; + } //endswitch md->mltpx +#ifdef PREVENT_ARITHMETIC_OVERFLOW + dv=*ptr>>8; + if (dv>(32767-RESXl)) { + *ptr=(32767-RESXl)<<8; + } else if (dv<(-32767+RESXl)) { + *ptr=(-32767+RESXl)<<8; } - } + //*ptr=limit( int32_t((-32767+RESXl)<<8), dv, int32_t((32767-RESXl)<<8)); // limit code cost 80 bytes +#endif + + } //endfor mixers tick10ms = 0; dirtyChannels &= passDirtyChannels; @@ -2825,7 +2832,7 @@ void perOut(uint8_t mode, uint8_t tick10ms) } while (++pass < 7 && dirtyChannels); // we increased speed of mixer, therefore allow more passes; old value was 5 mixWarning = lv_mixWarning; -} +} //endfunc perOut #define TIME_TO_WRITE() (s_eeDirtyMsk && (tmr10ms_t)(get_tmr10ms() - s_eeDirtyTime10ms) >= (tmr10ms_t)WRITE_DELAY_10MS) @@ -2928,7 +2935,7 @@ void doMixerCalculations() // the reason this needs to be done before limits is the applyLimit function; it checks for safety switches which would be not initialized otherwise evalFunctions(); - } + } //endif //========== LIMITS =============== for (uint8_t i=0; i Date: Wed, 27 Feb 2013 05:34:24 +0000 Subject: [PATCH 13/18] bugfix speed calculation for stock; flash saving 32bytes A speed calculation didn't work on stock, should be fixed now. Be aware SPEED is now calculated on the input side. Just to remember was already in earlier version as well. Found a way to reduce overflow protection and gives 32bytes back. Please test --- src/open9x.cpp | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/open9x.cpp b/src/open9x.cpp index fa2c60e6d..aa1f964c6 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -2738,7 +2738,7 @@ void perOut(uint8_t mode, uint8_t tick10ms) if (mode == e_perout_mode_normal && (md->speedUp || md->speedDown)) { // there are delay values #define DEL_MULT_SHIFT 8 // we recale to a mult 256 higher value for calculation - int16_t diff = v - (act[i]>>DEL_MULT_SHIFT); + int16_t diff = v - ((int32_t) act[i]>>DEL_MULT_SHIFT); if (diff) { // open.20.fsguruh: speed is defined in % movement per second; In menu we specify the full movement (-100% to 100%) = 200% in total // the unit of the stored value is the value from md->speedUp or md->speedDown divide SLOW_STEP seconds; e.g. value 4 means 4/SLOW_STEP = 2 seconds for CPU64 @@ -2749,7 +2749,7 @@ void perOut(uint8_t mode, uint8_t tick10ms) int32_t rate = (int32_t) tick10ms << (DEL_MULT_SHIFT+11); // = DEL_MULT*2048*tick10ms // rate equals a full range for one second; if less time is passed rate is accordingly smaller // if one second passed, rate would be 2048 (full motion)*256(recalculated weight)*100(100 ticks needed for one second) - int32_t currentValue=(v<0) { if (md->speedUp>0) { // if a speed upwards is defined recalculate the new value according configured speed; the higher the speed the smaller the add value is @@ -2815,12 +2815,35 @@ void perOut(uint8_t mode, uint8_t tick10ms) break; } //endswitch md->mltpx #ifdef PREVENT_ARITHMETIC_OVERFLOW - dv=*ptr>>8; + + PACK( union u_int16int32_t { + struct { + int16_t lo; + int16_t hi; + } words_t; + int32_t dword; + }); + u_int16int32_t tmp; + tmp.dword=*ptr; + + if (tmp.dword<0) { + tmp.words_t.hi|=0xFF80; + //tmp.bytes_t.h4=0xFF; + //tmp.bytes_t.h3|=0x80; + } else { + // tmp.bytes_t.h4=0x00; + // tmp.bytes_t.h3&=0x7F; + tmp.words_t.hi&=0x007F; + } + *ptr=tmp.dword; + // this implementation saves 32bytes flash + +/* dv=*ptr>>8; if (dv>(32767-RESXl)) { *ptr=(32767-RESXl)<<8; } else if (dv<(-32767+RESXl)) { *ptr=(-32767+RESXl)<<8; - } + }*/ //*ptr=limit( int32_t((-32767+RESXl)<<8), dv, int32_t((32767-RESXl)<<8)); // limit code cost 80 bytes #endif From 82783013fd601e237e47e11e91e01f7b7ee47706 Mon Sep 17 00:00:00 2001 From: "open.20.fsguruh@xoxy.net" Date: Thu, 28 Feb 2013 05:37:16 +0000 Subject: [PATCH 14/18] arithmetic overflow check improved: - Now it should be more unlikely to prevent arithmetic overflows. Please test. - This change costs about 20 bytes flash - Unfortunately still not bullet proof. A bullet proof check would cost additionally 100bytes flash, is it worth? - To get in dangerous areas, both weight and offset needs to be set very high. Without offset no more overruns should occur. --- src/open9x.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/open9x.cpp b/src/open9x.cpp index aa1f964c6..d80cbdb61 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -758,6 +758,9 @@ int16_t applyLimits(uint8_t channel, int32_t value) // unfortunately the constants and 32bit compares generates about 50 bytes codes; didn't find a way to get it down. // But I think it is worth value = limit(int32_t(-RESXl*256),value,int32_t(RESXl*256)); // saves 2 bytes compared to solution below + + // try also limit 0xFFFFF + // value = limit(int32_t(-0xFFFFF),value,int32_t(0xFFFFF)); // saves 2 bytes compared to solution below // if (value>RESXl*256) value = RESXl*256; // no bigger values allowed, prevent early overruns // if (value<-RESXl*256) value = -RESXl*256; // no smaller values allowed, prevent early overruns #endif @@ -2823,20 +2826,22 @@ void perOut(uint8_t mode, uint8_t tick10ms) } words_t; int32_t dword; }); + u_int16int32_t tmp; tmp.dword=*ptr; if (tmp.dword<0) { - tmp.words_t.hi|=0xFF80; + if ((tmp.words_t.hi&0xFF80)!=0xFF80) tmp.words_t.hi=0xFF86; // set to min nearly //tmp.bytes_t.h4=0xFF; //tmp.bytes_t.h3|=0x80; } else { + if ((tmp.words_t.hi|0x007F)!=0x007F) tmp.words_t.hi=0x00079; // set to max nearly // tmp.bytes_t.h4=0x00; // tmp.bytes_t.h3&=0x7F; - tmp.words_t.hi&=0x007F; + // tmp.words_t.hi&=0x007F; } *ptr=tmp.dword; - // this implementation saves 32bytes flash + // this implementation saves 18bytes flash /* dv=*ptr>>8; if (dv>(32767-RESXl)) { @@ -2844,7 +2849,8 @@ void perOut(uint8_t mode, uint8_t tick10ms) } else if (dv<(-32767+RESXl)) { *ptr=(-32767+RESXl)<<8; }*/ - //*ptr=limit( int32_t((-32767+RESXl)<<8), dv, int32_t((32767-RESXl)<<8)); // limit code cost 80 bytes + // *ptr=limit( int32_t(int32_t(-1)<<23), *ptr, int32_t(int32_t(1)<<23)); // limit code cost 72 bytes + // *ptr=limit( int32_t((-32767+RESXl)<<8), *ptr, int32_t((32767-RESXl)<<8)); // limit code cost 80 bytes #endif } //endfor mixers From 214bc9c4d47438bbdd556188d3f158c9334de91a Mon Sep 17 00:00:00 2001 From: "open.20.fsguruh@xoxy.net" Date: Sat, 2 Mar 2013 08:35:55 +0000 Subject: [PATCH 15/18] Small flash and data saving I need Bertrand for this change, but also testers. I removed ex_chans[] array, because I am the opinion it can be easily replaced by chans[]. According history ex_chans was there first, after that chans[] are introduced. Now we can use chans instead of ex_chans and get rid of this part to have savings. I am incorrect? My tests are all OK. The only area I can imagive this change is influenced are flight phases changes. And only if output channels are used as input. But I think this way is even better. Please test and report. --- src/open9x.cpp | 101 ++++++------------------------------------------- src/open9x.h | 2 +- 2 files changed, 13 insertions(+), 90 deletions(-) diff --git a/src/open9x.cpp b/src/open9x.cpp index d80cbdb61..7ef1180da 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -806,7 +806,7 @@ int16_t applyLimits(uint8_t channel, int32_t value) int16_t calibratedStick[NUM_STICKS+NUM_POTS]; int16_t g_chans512[NUM_CHNOUT] = {0}; -int16_t ex_chans[NUM_CHNOUT] = {0}; // Outputs (before LIMITS) of the last perMain +// int16_t ex_chans[NUM_CHNOUT] = {0}; // Outputs (before LIMITS) of the last perMain; @@@3 fsguruh #ifdef HELI int16_t cyc_anas[3] = {0}; #endif @@ -842,7 +842,7 @@ int16_t getValue(uint8_t i) #endif else if(i>8); //@@@3 fsguruh // return ex_chans[i-CSW_CHOUT_BASE]; else if(i=MIXSRC_CH1-1 && k<=MIXSRC_CHMAX-1 && md->destCh != k-MIXSRC_CH1+1) { if (dirtyChannels & ((bitfield_channels_t)1 << (k-MIXSRC_CH1+1)) & (passDirtyChannels|~(((bitfield_channels_t) 1 << md->destCh)-1))) passDirtyChannels |= (bitfield_channels_t) 1 << md->destCh; - if (k-MIXSRC_CH1+1 < md->destCh || pass > 0) - v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before + // @@@3 fsguruh should not be necessary + /*if (k-MIXSRC_CH1+1 < md->destCh || pass > 0) + v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before */ } } #else @@ -2644,8 +2645,9 @@ void perOut(uint8_t mode, uint8_t tick10ms) if (k>=MIXSRC_CH1-1 && k<=MIXSRC_CHMAX-1 && md->destCh != k-MIXSRC_CH1+1) { if (dirtyChannels & ((bitfield_channels_t)1 << (k-MIXSRC_CH1+1)) & (passDirtyChannels|~(((bitfield_channels_t) 1 << md->destCh)-1))) passDirtyChannels |= (bitfield_channels_t) 1 << md->destCh; - if (k-MIXSRC_CH1+1 < md->destCh || pass > 0) - v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before + // @@@3 fsguruh should not be necessary + /* if (k-MIXSRC_CH1+1 < md->destCh || pass > 0) + v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before */ } } #endif @@ -2968,14 +2970,13 @@ void doMixerCalculations() //========== LIMITS =============== for (uint8_t i=0; i 1024*100 + // chans[i] holds data from mixer. chans[i] = v*weight => 1024*256 // later we multiply by the limit (up to 100) and then we need to normalize - // at the end chans[i] = chans[i]/100 => -1024..1024 + // at the end chans[i] = chans[i]/256 => -1024..1024 // interpolate value with min/max so we get smooth motion from center to stop // this limits based on v original values and min=-1024, max=1024 RESX=1024 int32_t q = (s_fade_flight_phases ? (sum_chans512[i] / weight) << 4 : chans[i]); - // @@@2 now remove the internal 256 100% basis by a simple shift operation - ex_chans[i] = q>>8; // for the next perMain + // ex_chans[i] = q>>8; // for the next perMain @@@3 fsguruh int16_t value = applyLimits(i, q); // applyLimits will remove the 256 100% basis @@ -3468,7 +3469,6 @@ uint16_t getTmr16KHz() // because all critical pfathes are finished. It also would give us the chance to measure if we passed the next tick and count it. // It's already implemented but commented out, because of code cost. - ISR(TIMER_10MS_VECT, ISR_NOBLOCK) // 10ms timer { @@ -3526,6 +3526,7 @@ ISR(TIMER_10MS_VECT, ISR_NOBLOCK) OCR0 += 2; // interrupt every 128us /* // needs to change cnt10ms to int instead uint to allow cnt10ms to be negative + // also remove cnt10ms-- in if statement above, because substraction is done here // code oost: 34 bytes // this solutions all missed ISRs are counted and next ISR is in about 128usec (exception is wrap around) cli(); @@ -3547,84 +3548,6 @@ ISR(TIMER_10MS_VECT, ISR_NOBLOCK) } - -// #define OLD_VERSION - - #ifdef OLD_VERSION - -ISR(TIMER_10MS_VECT, ISR_NOBLOCK) // 10ms timer -{ - cli(); - PAUSE_10MS_INTERRUPT(); - sei(); - - static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; no start value needed - // without correction we are 0,16% too fast; that mean in one hour we are 5,76Sek too fast; we do not like that -#if defined(PCBGRUVIN9X) - // static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; not start value needed - uint8_t bump = (!(++accuracyWarble & 0x03)) ? 157 : 156; - OCR2A += bump; -#elif defined(AUDIO) || defined(VOICE) - OCR0 += 2; // interrupt every 128us - // warble is done later for AUDIO or VOICE don't do it here because it will destroy audio -#else - // static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; not start value needed - // each tick we are 0,016msec too fast - // one tick more means 0,048 msec too slow = 3*0,016msec --> in sum we are correct!!! - // therefore every 4 round we need to slow down one tick - uint8_t bump = (!(++accuracyWarble & 0x03)) ? 157 : 156; - OCR0 += bump; -#endif - -#if defined(PCBSTD) && (defined(AUDIO) || defined(VOICE)) - -#if defined(AUDIO) - AUDIO_DRIVER(); -#endif - -#if defined(VOICE) - VOICE_DRIVER(); -#endif - - static uint8_t cnt10ms; // no initialization needed here; execute 10ms code once every 78 ISRs; takes 16.38msec to overrun for first round --> no problem - -#if defined(FRSKY) || defined(MAVLINK) || defined(JETI) - if (cnt10ms == 30) { - if (!IS_DSM2_SERIAL_PROTOCOL(s_current_protocol)) - telemetryPoll10ms(); - } -#endif - - if (--cnt10ms == 0) { // BEGIN { ... every 10ms ... } - // Begin 10ms event - // cnt10ms = 78; - cnt10ms = (!(++accuracyWarble &0x07)) ? 79 : 78; - // each per10ms() we are 0,016msec too fast - // one tick more means 0,112 msec too slow = 7*0,016msec --> in sum we are correct!!! - // therefore every 8. round we need to slow down one tick - -#endif - - AUDIO_HEARTBEAT(); - -#if defined(HAPTIC) - HAPTIC_HEARTBEAT(); -#endif - - per10ms(); - -#if defined(PCBSTD) && (defined(AUDIO) || defined(VOICE)) - } // end 10ms event -#endif - - - // cli(); - RESUME_10MS_INTERRUPT(); - // sei(); -} - -#endif - // Timer3 used for PPM_IN pulse width capture. Counter running at 16MHz / 8 = 2MHz // equating to one count every half millisecond. (2 counts = 1ms). Control channel // count delta values thus can range from about 1600 to 4400 counts (800us to 2200us), diff --git a/src/open9x.h b/src/open9x.h index e51469a26..2c8027d5b 100644 --- a/src/open9x.h +++ b/src/open9x.h @@ -1063,7 +1063,7 @@ extern uint8_t g_beepVal[5]; extern uint8_t ppmInState; //0=unsync 1..8= wait for value i-1 extern int16_t g_ppmIns[8]; extern int32_t chans[NUM_CHNOUT]; -extern int16_t ex_chans[NUM_CHNOUT]; // Outputs (before LIMITS) of the last perMain +// extern int16_t ex_chans[NUM_CHNOUT]; // Outputs (before LIMITS) of the last perMain @@@3 fsguruh extern int16_t g_chans512[NUM_CHNOUT]; extern uint16_t BandGap; From 9f2b486152ce625145bce13433b0f893f15c2a91 Mon Sep 17 00:00:00 2001 From: "open.20.fsguruh@xoxy.net" Date: Sun, 3 Mar 2013 13:57:30 +0000 Subject: [PATCH 16/18] [BRANCH] Created possibility to increase GVARS number; is set to 9 Warning!!! ========== Be aware this version is in branch and is not supported currently by Companion9x. Whatever you configure will be lost in final version! Because EEPROM interpretation changes anyway because of higher offset and weight values possible in this branch, I tried to align the bits a little bit different to allow a higher number of GVARS. The code change is nearly flash neutral. It costs only 18 bytes flash, but would us allow to either increase range for weight and offset further, or allow more GVARS. I set it to 9 for this branch now. Please try and report. It's very easy now to adjust the GVARS number by changing the constant in myeeprom.h. Just change the MAX_GVARS constant for your build in myeeprom.h in the beginning of the file. The lower MAX_GVARS the higher is the possible number for weight and offset and vice versa. That is quite likely too complicated for conversion so we need to agree maximum for GVARS. Not sure in the moment, but this is a branch only and is just to play with it. Not sure how Bertrand would like this anyway, so may be removed at all. --- src/menus.cpp | 33 +++++++++++++++++++------- src/model_menus.cpp | 5 ++-- src/myeeprom.h | 58 +++++++++++++++++++++++---------------------- src/open9x.cpp | 37 ++++++++++++++--------------- src/open9x.h | 34 +++++++++++++++++++++++--- 5 files changed, 106 insertions(+), 61 deletions(-) diff --git a/src/menus.cpp b/src/menus.cpp index 0f49c925e..d28a3b1be 100644 --- a/src/menus.cpp +++ b/src/menus.cpp @@ -677,34 +677,49 @@ int8_t switchMenuItem(uint8_t x, uint8_t y, int8_t value, LcdFlags attr, uint8_t } #if defined(GVARS) -int16_t gvarMenuItem(uint8_t x, uint8_t y, int16_t value, int16_t min, int16_t max, LcdFlags attr, uint8_t event) // @@@ open.20.fsguruh +int16_t gvarMenuItem(uint8_t x, uint8_t y, int16_t value, int16_t min, int16_t max, LcdFlags attr, uint8_t event) { - uint8_t delta = (max <= 100 ? GV1_SMALL-1 : GV1_LARGE-1); + uint16_t delta = GV_GET_GV1_VALUE(max); bool invers = (attr & INVERS); if (invers && event == EVT_KEY_LONG(KEY_ENTER)) { s_editMode = !s_editMode; - value = (value > max ? GET_GVAR(value, min, max, s_perout_flight_phase) : delta+1); + + value = ( GV_IS_GV_VALUE(value,min,max) ? GET_GVAR(value, min, max, s_perout_flight_phase) : delta); + eeDirty(EE_MODEL); } - if (value > max) { + if ( GV_IS_GV_VALUE(value,min,max) ) { if (attr & LEFT) attr -= LEFT; /* because of ZCHAR */ else x -= 2*FW+FWNUM; - int8_t idx = value - delta; + + int8_t idx = (int16_t) GV_INDEX_CALC_DELTA(value,delta); if (invers) { - CHECK_INCDEC_MODELVAR(event, idx, -4, +5); - value = (int16_t)idx + delta; + CHECK_INCDEC_MODELVAR(event, idx, -MAX_GVARS, MAX_GVARS-1); } - if (idx <= 0) { idx = 1-idx; lcd_putcAtt(x-6, y, '-', attr); } + +/* sometimes this implementation was smaller, but sometimes not.... + value=delta+(int16_t) idx; + if (idx < 0) {idx = -1-idx; lcd_putcAtt(x-6, y, '-', attr); } + else value|=~(delta-1); + idx++; */ + + if (idx < 0) { + value=(int16_t) GV_CALC_VALUE_IDX_NEG(idx,delta); idx=-idx; lcd_putcAtt(x-6, y, '-', attr); + } else { + value = (int16_t) GV_CALC_VALUE_IDX_POS(idx,delta); idx++; + } + putsStrIdx(x, y, STR_GV, idx, attr); } else { lcd_outdezAtt(x, y, value, attr); - if (invers) value = checkIncDec(event, value, min, max,EE_MODEL); // as proposed, no change to makro, but directly call this function + if (invers) value = checkIncDec(event, value, min, max,EE_MODEL); } return value; } + #else int8_t gvarMenuItem(uint8_t x, uint8_t y, int8_t value, int16_t min, int16_t max, LcdFlags attr, uint8_t event) { diff --git a/src/model_menus.cpp b/src/model_menus.cpp index 562ffee53..d75910522 100644 --- a/src/model_menus.cpp +++ b/src/model_menus.cpp @@ -1907,7 +1907,7 @@ void gvarWeightItem(xcoord_t x, uint8_t y, MixData *md, uint8_t attr, uint8_t ev // @@@ open.20.fsguruh u_int8int16_t weight; MD_GETWEIGHT(weight,md); - weight.word = gvarMenuItem(x, y, weight.word, -245, 245, attr, event); + weight.word = gvarMenuItem(x, y, weight.word, GV_RANGELARGE_NEG, GV_RANGELARGE, attr, event); MD_SETWEIGHT(weight,md); //#endif } @@ -1988,7 +1988,7 @@ void menuModelMixOne(uint8_t event) // @@@ open.20.fsguruh u_int8int16_t offset; MD_GETOFFSET(offset,md2); - offset.word = gvarMenuItem(COLUMN_X+MIXES_2ND_COLUMN, y, offset.word, -245, 245, attr|LEFT, event); + offset.word = gvarMenuItem(COLUMN_X+MIXES_2ND_COLUMN, y, offset.word, GV_RANGELARGE_NEG, GV_RANGELARGE, attr|LEFT, event); MD_SETOFFSET(offset,md2); // #else // md2->offset = gvarMenuItem(COLUMN_X+MIXES_2ND_COLUMN, y, md2->offset, -125, 125, attr|LEFT, event); @@ -2307,6 +2307,7 @@ void menuModelExpoMix(uint8_t expo, uint8_t event) else displayGVar(15*FW+2, y, md->curveParam, -100, 100); // open.20.fsguruh // displayGVar(15*FW+2, y, md->curveParam, -125, 125); // only -100 to +100 is allowed now + // could be increased now, but is it useful? differentiate with more the +-100% is a fault, correct? } if (md->swtch) putsSwitches(16*FW, y, md->swtch); diff --git a/src/myeeprom.h b/src/myeeprom.h index 2eac0d9bc..3a30457e1 100644 --- a/src/myeeprom.h +++ b/src/myeeprom.h @@ -65,6 +65,34 @@ #define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__)) #endif + +typedef int16_t gvar_t; + +#if !defined(CPUM64) +typedef char gvar_name_t[6]; +#define GVAR_MAX 1024 +#endif + +#if defined(CPUM64) && defined(GVARS) + #define MAX_GVARS 9 + #define MODEL_GVARS_DATA gvar_t gvars[MAX_GVARS]; + #define PHASE_GVARS_DATA + #define GVAR_VALUE(x, p) g_model.gvars[x] +#elif defined(CPUM64) + #define MAX_GVARS 0 + #define MODEL_GVARS_DATA + #define PHASE_GVARS_DATA +#elif defined(GVARS) + #define MAX_GVARS 9 + #define MODEL_GVARS_DATA gvar_name_t gvarsNames[MAX_GVARS]; + #define PHASE_GVARS_DATA gvar_t gvars[MAX_GVARS] + #define GVAR_VALUE(x, p) g_model.phaseData[p].gvars[x] +#else + #define MAX_GVARS 0 + #define MODEL_GVARS_DATA gvar_name_t gvarsNames[5]; + #define PHASE_GVARS_DATA gvar_t gvars[5] +#endif + PACK(typedef struct t_TrainerMix { uint8_t srcChn:6; // 0-7 = ch1-8 uint8_t mode:2; // off,add-mode,subst-mode @@ -427,7 +455,8 @@ enum Functions { #endif #if defined(GVARS) FUNC_ADJUST_GV1, - FUNC_ADJUST_GV5 = FUNC_ADJUST_GV1 + 4, + // FUNC_ADJUST_GV5 = FUNC_ADJUST_GV1 + 4, + FUNC_ADJUST_GVLAST = (FUNC_ADJUST_GV1 + (MAX_GVARS-1)), #endif #if defined(DEBUG) FUNC_TEST, // should remain the last before MAX as not added in companion9x @@ -693,33 +722,6 @@ PACK(typedef struct t_SwashRingData { // Swash Ring data #define TRIM_ARRAY int16_t trim[4] #endif -typedef int16_t gvar_t; - -#if !defined(CPUM64) -typedef char gvar_name_t[6]; -#define GVAR_MAX 1024 -#endif - -#if defined(CPUM64) && defined(GVARS) -#define MAX_GVARS 5 -#define MODEL_GVARS_DATA gvar_t gvars[MAX_GVARS]; -#define PHASE_GVARS_DATA -#define GVAR_VALUE(x, p) g_model.gvars[x] -#elif defined(CPUM64) -#define MAX_GVARS 0 -#define MODEL_GVARS_DATA -#define PHASE_GVARS_DATA -#elif defined(GVARS) -#define MAX_GVARS 5 -#define MODEL_GVARS_DATA gvar_name_t gvarsNames[MAX_GVARS]; -#define PHASE_GVARS_DATA gvar_t gvars[MAX_GVARS] -#define GVAR_VALUE(x, p) g_model.phaseData[p].gvars[x] -#else -#define MAX_GVARS 0 -#define MODEL_GVARS_DATA gvar_name_t gvarsNames[5]; -#define PHASE_GVARS_DATA gvar_t gvars[5] -#endif - PACK(typedef struct t_PhaseData { TRIM_ARRAY; int8_t swtch; // swtch of phase[0] is not used diff --git a/src/open9x.cpp b/src/open9x.cpp index 7ef1180da..d840dd176 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -757,12 +757,7 @@ int16_t applyLimits(uint8_t channel, int32_t value) // thanks to gbirkus, he motivated this change, which greatly reduces overruns // unfortunately the constants and 32bit compares generates about 50 bytes codes; didn't find a way to get it down. // But I think it is worth - value = limit(int32_t(-RESXl*256),value,int32_t(RESXl*256)); // saves 2 bytes compared to solution below - - // try also limit 0xFFFFF - // value = limit(int32_t(-0xFFFFF),value,int32_t(0xFFFFF)); // saves 2 bytes compared to solution below - // if (value>RESXl*256) value = RESXl*256; // no bigger values allowed, prevent early overruns - // if (value<-RESXl*256) value = -RESXl*256; // no smaller values allowed, prevent early overruns + value = limit(int32_t(-RESXl*256),value,int32_t(RESXl*256)); // saves 2 bytes compared to other solutions up to now #endif #if defined(PPM_LIMITS_SYMETRICAL) @@ -1251,8 +1246,8 @@ uint8_t s_gvar_last = 0; #if defined(CPUM64) int16_t getGVarValue(int16_t x, int16_t min, int16_t max) { - if ((x > (GV1_LARGE-6)) || (x>max)) { // @@@ open.20.fsguruh - int8_t idx = (max <= 100 ? x - GV1_SMALL : x - GV1_LARGE); + if (GV_IS_GV_VALUE(x,min,max)) { + int8_t idx = GV_INDEX_CALCULATION(x,max); int8_t mul = 1; if (idx < 0) { @@ -1291,12 +1286,12 @@ uint8_t getGVarFlightPhase(uint8_t phase, uint8_t idx) int16_t getGVarValue(int16_t x, int16_t min, int16_t max, int8_t phase) { - if (x > max) { - int8_t idx = (max <= 100 ? x - GV1_SMALL : x - GV1_LARGE); + if (GV_IS_GV_VALUE(x,min,max)) { + int8_t idx = GV_INDEX_CALCULATION(x,max); int8_t mul = 1; if (idx < 0) { - idx = 1-idx; + idx = -1-idx; mul = -1; } @@ -2717,7 +2712,7 @@ void perOut(uint8_t mode, uint8_t tick10ms) //========== OFFSET =============== if (apply_offset_and_curve) { - int16_t offset = GET_GVAR(MD_OFFSET(md), -245, 245, s_perout_flight_phase); // open.20.fsguruh + int16_t offset = GET_GVAR(MD_OFFSET(md), GV_RANGELARGE_NEG, GV_RANGELARGE, s_perout_flight_phase); // open.20.fsguruh if (offset) v += calc100toRESX_16Bits(offset); // @@@ open.20.fsguruh } @@ -2734,7 +2729,7 @@ void perOut(uint8_t mode, uint8_t tick10ms) } // saves 12 bytes code if done here and not together with weight; unknown reason - int16_t weight = GET_GVAR(MD_WEIGHT(md), -500, 500, s_perout_flight_phase); + int16_t weight = GET_GVAR(MD_WEIGHT(md), GV_RANGELARGE_NEG, GV_RANGELARGE, s_perout_flight_phase); weight=calc100to256_16Bits(weight); //========== SPEED =============== @@ -2820,6 +2815,15 @@ void perOut(uint8_t mode, uint8_t tick10ms) break; } //endswitch md->mltpx #ifdef PREVENT_ARITHMETIC_OVERFLOW +/* + // a lot of assumptions must be true, for this kind of check; not really worth for only 4 bytes flash savings + // this solution would save again 4 bytes flash + int8_t testVar=(*ptr<<1)>>24; + if ( (testVar!=-1) && (testVar!=0 ) ) { + // this devices by 64 which should give a good balance between still over 100% but lower then 32x100%; should be OK + *ptr >>= 6; // this is quite tricky, reduces the value a lot but should be still over 100% and reduces flash need + } */ + PACK( union u_int16int32_t { struct { @@ -2834,13 +2838,8 @@ void perOut(uint8_t mode, uint8_t tick10ms) if (tmp.dword<0) { if ((tmp.words_t.hi&0xFF80)!=0xFF80) tmp.words_t.hi=0xFF86; // set to min nearly - //tmp.bytes_t.h4=0xFF; - //tmp.bytes_t.h3|=0x80; } else { - if ((tmp.words_t.hi|0x007F)!=0x007F) tmp.words_t.hi=0x00079; // set to max nearly - // tmp.bytes_t.h4=0x00; - // tmp.bytes_t.h3&=0x7F; - // tmp.words_t.hi&=0x007F; + if ((tmp.words_t.hi|0x007F)!=0x007F) tmp.words_t.hi=0x0079; // set to max nearly } *ptr=tmp.dword; // this implementation saves 18bytes flash diff --git a/src/open9x.h b/src/open9x.h index 2c8027d5b..ddd1fa49f 100644 --- a/src/open9x.h +++ b/src/open9x.h @@ -812,21 +812,49 @@ void incRotaryEncoder(uint8_t idx, int8_t inc); void setGVarValue(uint8_t x, int8_t value); #define GET_GVAR(x, min, max, p) getGVarValue(x, min, max) #define SET_GVAR(idx, val, p) setGVarValue(idx, val) + #define GV1_SMALL 128 + #define GV1_LARGE 256 #else uint8_t getGVarFlightPhase(uint8_t phase, uint8_t idx); int16_t getGVarValue(int16_t x, int16_t min, int16_t max, int8_t phase); void setGVarValue(uint8_t x, int8_t value, int8_t phase); #define GET_GVAR(x, min, max, p) getGVarValue(x, min, max, p) #define SET_GVAR(idx, val, p) setGVarValue(idx, val, p) + // @@@ open.20.fsguruh may have different recalculation formula in future to allow higher ranges? + #define GV1_SMALL 128 + #define GV1_LARGE 256 #endif - #define GV1_SMALL 123 - // @@@ open.20.fsguruh - #define GV1_LARGE 251 + + // depends if we want free ranges for use, if no MAX_GVARS are smaller + // if not this would assume a max for GVARS instead using MAX_GVARS + #define GV_RANGESMALL (GV1_SMALL - (MAX_GVARS+1)) + #define GV_RANGESMALL_NEG (-GV1_SMALL + (MAX_GVARS+1)) + #define GV_RANGELARGE (GV1_LARGE - (MAX_GVARS+1)) + #define GV_RANGELARGE_NEG (-GV1_LARGE + (MAX_GVARS+1)) + + #define GV_IS_GV_VALUE(x,min,max) ( (x>max) || (x Date: Fri, 8 Mar 2013 18:10:16 +0000 Subject: [PATCH 17/18] removed code and memory optimization, because it breaks output as input This version reverts r2029. It also activates CORRECT_NEGATIVE_SHIFTS by default and activates ARITHMETIC_OVERFLOW halfway. Unfortunately this removes a lot of flash optimization but is necessary to work properly. Maybe we find later a better solution. --- src/open9x.cpp | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/open9x.cpp b/src/open9x.cpp index d840dd176..727805925 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -658,13 +658,15 @@ void applyExpos(int16_t *anas) #if !defined(CPUARM) -// #define CORRECT_NEGATIVE_SHIFTS +#define CORRECT_NEGATIVE_SHIFTS // open.20.fsguruh; shift right operations do the rounding different for negative values compared to positive values // so all negative divisions round always further down, which give absolute values bigger compared to a usual division // this is noticable on the display, because instead of -100.0 -99.9 is shown; While in praxis I doublt somebody will notice a // difference this is more a mental thing. Maybe people are distracted, because the easy calculations are obviously wrong // this define would correct this, but costs 34 bytes code for stock version +// currently we set this to active always, because it might cause a fault about 1% compared positive and negative values + int16_t calc100to256_16Bits(int16_t x) // return x*2.56 { // y = 2*x + x/2 +x/16-x/512-x/2048 @@ -730,13 +732,19 @@ int16_t calcRESXto1000(int16_t x) // return x/1.024 #endif -#define PREVENT_ARITHMETIC_OVERFLOW +// #define PREVENT_ARITHMETIC_OVERFLOW // because of optimizations the reserves before overruns occurs is only the half // this defines enables some checks the greatly improves this situation // It should nearly prevent all overruns (is still a chance for it, but quite low) -// negative side is code cost 124 bytes flash - +// negative side is code cost 96 bytes flash +// we do it now half way, only in applyLimits, which costs currently 50bytes +// according opinion poll this topic is currently not very important +// the change below improves already the situation +// the check inside mixer would slow down mix a little bit and costs additionally flash +// also the check inside mixer still is not bulletproof, there may be still situations a overflow could occur +// a bulletproof implementation would take about additional 100bytes flash +// therefore with go with this compromize, interested people could activate this define // @@@2 open.20.fsguruh ; // channel = channelnumber -1; @@ -753,12 +761,15 @@ int16_t applyLimits(uint8_t channel, int32_t value) if (ofs > lim_p) ofs = lim_p; if (ofs < lim_n) ofs = lim_n; -#ifdef PREVENT_ARITHMETIC_OVERFLOW +// because the recaling optimization would reduce the calculation reserve we activate this for all builds +// it increases the caculation reserve from factor 20,25x to 32x, which it slightly better as original +// without it we would only have 16x which is slightly worse as original, we should not do this +// #ifdef PREVENT_ARITHMETIC_OVERFLOW // thanks to gbirkus, he motivated this change, which greatly reduces overruns // unfortunately the constants and 32bit compares generates about 50 bytes codes; didn't find a way to get it down. - // But I think it is worth + value = limit(int32_t(-RESXl*256),value,int32_t(RESXl*256)); // saves 2 bytes compared to other solutions up to now -#endif +// #endif #if defined(PPM_LIMITS_SYMETRICAL) if (value) { @@ -801,7 +812,7 @@ int16_t applyLimits(uint8_t channel, int32_t value) int16_t calibratedStick[NUM_STICKS+NUM_POTS]; int16_t g_chans512[NUM_CHNOUT] = {0}; -// int16_t ex_chans[NUM_CHNOUT] = {0}; // Outputs (before LIMITS) of the last perMain; @@@3 fsguruh +int16_t ex_chans[NUM_CHNOUT] = {0}; // Outputs (before LIMITS) of the last perMain; #ifdef HELI int16_t cyc_anas[3] = {0}; #endif @@ -837,7 +848,7 @@ int16_t getValue(uint8_t i) #endif else if(i>8); //@@@3 fsguruh // return ex_chans[i-CSW_CHOUT_BASE]; + else if(i=MIXSRC_CH1-1 && k<=MIXSRC_CHMAX-1 && md->destCh != k-MIXSRC_CH1+1) { if (dirtyChannels & ((bitfield_channels_t)1 << (k-MIXSRC_CH1+1)) & (passDirtyChannels|~(((bitfield_channels_t) 1 << md->destCh)-1))) passDirtyChannels |= (bitfield_channels_t) 1 << md->destCh; - // @@@3 fsguruh should not be necessary - /*if (k-MIXSRC_CH1+1 < md->destCh || pass > 0) - v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before */ + if (k-MIXSRC_CH1+1 < md->destCh || pass > 0) + v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before } } #else @@ -2640,9 +2650,8 @@ void perOut(uint8_t mode, uint8_t tick10ms) if (k>=MIXSRC_CH1-1 && k<=MIXSRC_CHMAX-1 && md->destCh != k-MIXSRC_CH1+1) { if (dirtyChannels & ((bitfield_channels_t)1 << (k-MIXSRC_CH1+1)) & (passDirtyChannels|~(((bitfield_channels_t) 1 << md->destCh)-1))) passDirtyChannels |= (bitfield_channels_t) 1 << md->destCh; - // @@@3 fsguruh should not be necessary - /* if (k-MIXSRC_CH1+1 < md->destCh || pass > 0) - v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before */ + if (k-MIXSRC_CH1+1 < md->destCh || pass > 0) + v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before } } #endif @@ -2975,7 +2984,7 @@ void doMixerCalculations() // interpolate value with min/max so we get smooth motion from center to stop // this limits based on v original values and min=-1024, max=1024 RESX=1024 int32_t q = (s_fade_flight_phases ? (sum_chans512[i] / weight) << 4 : chans[i]); - // ex_chans[i] = q>>8; // for the next perMain @@@3 fsguruh + ex_chans[i] = q>>8; // for the next perMain int16_t value = applyLimits(i, q); // applyLimits will remove the 256 100% basis From 67689c8f7148a02ad47ee1f76999d0a6c5293c9a Mon Sep 17 00:00:00 2001 From: "open.20.fsguruh@xoxy.net" Date: Sat, 9 Mar 2013 17:19:56 +0000 Subject: [PATCH 18/18] [BRANCH] preperation for merge to trunk; GVARS 5; introduced RANGE constant I would like Bertrand to check this version. It's currently my proposal for the merge. It holds following changes: - Introduced constant RESERVE_RANGE_FOR_GVARS to make number of MAX_GVARS independent. - NEGATIVE_SHIFT_CORRECTION is now set by makefile by default instead inside the code The would at least give the option to save code and have less precise calculation. - ex_chans is included like in trunk. No change here, because it would break output as input. Actually this version would reduce the effort to merge: - We stay with the GVARS defines we had till now - We reserve for ranges space for 10 GVARS, which do not hurt, because we doubled ranges anyway. - We stay with 5 GVARS in the moment. According my poll not many people currently use GVARS. Therefore there is plenty of time finding here a better solution. I expect the number of people will increase soon, but up to this moment 5 GVARS are more than enough. - The layout of EEPROM for the values is already the new format. That means when we allow more GVARS in future, at least the parameters like weight, offset, etc. needs no additional conversion. What do you think. Should we go this way? --- src/Makefile | 12 +++++++++++- src/myeeprom.h | 6 ++++-- src/open9x.cpp | 3 ++- src/open9x.h | 23 ++++++----------------- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/Makefile b/src/Makefile index b36e6d431..c2ddf89e4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -98,9 +98,15 @@ AUDIO = NO VOICE = NO # corrects different rounding for negative values. So instead of -99.9 you will see again -100.0 -CORRECT_NEGATIVE_VALUES = NO +# Values = YES, NO +CORRECT_NEGATIVE_VALUES = YES + +# adds additional code to prevent arithmetic overflows. MAX or MIN values stays. Not yet guaranteed!! Just improved +# Values = YES, NO +ARITHMETIC_OVERFLOW_CHECK = NO # increases range of expo curve but costs about 82 bytes flash +# Values = YES, NO EXTENDED_EXPO_CURVE = NO # HAPTIC Mods @@ -420,6 +426,10 @@ ifeq ($(CORRECT_NEGATIVE_VALUES), YES) CPPDEFS += -DCORRECT_NEGATIVE_SHIFTS endif +ifeq ($(ARITHMETIC_OVERFLOW_CHECK), YES) + CPPDEFS += -DPREVENT_ARITHMETIC_OVERFLOW +endif + ifeq ($(EXTENDED_EXPO_CURVE), YES) CPPDEFS += -DEXTENDED_EXPO endif diff --git a/src/myeeprom.h b/src/myeeprom.h index 3a30457e1..3a794d27d 100644 --- a/src/myeeprom.h +++ b/src/myeeprom.h @@ -73,8 +73,10 @@ typedef char gvar_name_t[6]; #define GVAR_MAX 1024 #endif +#define RESERVE_RANGE_FOR_GVARS 10 +// even we do not spend space in EEPROM for 10 GVARS, we reserve the space inside the range of values, like offset, weight, etc. #if defined(CPUM64) && defined(GVARS) - #define MAX_GVARS 9 + #define MAX_GVARS 5 #define MODEL_GVARS_DATA gvar_t gvars[MAX_GVARS]; #define PHASE_GVARS_DATA #define GVAR_VALUE(x, p) g_model.gvars[x] @@ -83,7 +85,7 @@ typedef char gvar_name_t[6]; #define MODEL_GVARS_DATA #define PHASE_GVARS_DATA #elif defined(GVARS) - #define MAX_GVARS 9 + #define MAX_GVARS 5 #define MODEL_GVARS_DATA gvar_name_t gvarsNames[MAX_GVARS]; #define PHASE_GVARS_DATA gvar_t gvars[MAX_GVARS] #define GVAR_VALUE(x, p) g_model.phaseData[p].gvars[x] diff --git a/src/open9x.cpp b/src/open9x.cpp index 727805925..a39c12abb 100644 --- a/src/open9x.cpp +++ b/src/open9x.cpp @@ -658,7 +658,7 @@ void applyExpos(int16_t *anas) #if !defined(CPUARM) -#define CORRECT_NEGATIVE_SHIFTS +// #define CORRECT_NEGATIVE_SHIFTS // open.20.fsguruh; shift right operations do the rounding different for negative values compared to positive values // so all negative divisions round always further down, which give absolute values bigger compared to a usual division // this is noticable on the display, because instead of -100.0 -99.9 is shown; While in praxis I doublt somebody will notice a @@ -666,6 +666,7 @@ void applyExpos(int16_t *anas) // this define would correct this, but costs 34 bytes code for stock version // currently we set this to active always, because it might cause a fault about 1% compared positive and negative values +// is done now in makefile int16_t calc100to256_16Bits(int16_t x) // return x*2.56 { diff --git a/src/open9x.h b/src/open9x.h index ddd1fa49f..2b489b83e 100644 --- a/src/open9x.h +++ b/src/open9x.h @@ -825,16 +825,9 @@ void incRotaryEncoder(uint8_t idx, int8_t inc); #define GV1_LARGE 256 #endif - // depends if we want free ranges for use, if no MAX_GVARS are smaller - // if not this would assume a max for GVARS instead using MAX_GVARS - #define GV_RANGESMALL (GV1_SMALL - (MAX_GVARS+1)) - #define GV_RANGESMALL_NEG (-GV1_SMALL + (MAX_GVARS+1)) - #define GV_RANGELARGE (GV1_LARGE - (MAX_GVARS+1)) - #define GV_RANGELARGE_NEG (-GV1_LARGE + (MAX_GVARS+1)) - #define GV_IS_GV_VALUE(x,min,max) ( (x>max) || (x