[MERGED] bring in advantages from branch to trunk; Performance optimizations

This combines now the changes from branch PerformanceOptimizations to trunk
Mainly this change should do following:
- Flash savings should be around 224 bytes for stock
- Added option ARITHMETIC_OVERFLOW_CHECK to prevent overflows.
  Still not bulletproof, but codecost is about 50 bytes
- Increases mix speed about 25 - 30% depending on task for stock
  Also increased for other boards, but they may not notice...
- Increased ranges for weight and offset from +-125% to +-245%
- Easier now to increase number of GVARS if needed; Just increase GVAR_MAX in myeeprom.h.
  But!!!! EEPROM layout changes and may not be compatible. Do this for own proposes.
  Till now we do not know how we handle this in future.
- Corrected Timer for stock if used together with AUDIO or VOICE option
- See comments below, what I potentially forgotten...

Be aware this version is currently not compatible to companion9x. But the version before wasn't either. Work is current in progress to tie this up.
This commit is contained in:
open.20.fsguruh@xoxy.net
2013-03-10 19:51:19 +00:00
7 changed files with 642 additions and 219 deletions
+16
View File
@@ -97,6 +97,14 @@ AUDIO = NO
# Values = YES, NO
VOICE = NO
# corrects different rounding for negative values. So instead of -99.9 you will see again -100.0
# 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
# HAPTIC Mods
# Values = YES, NO
HAPTIC = NO
@@ -423,6 +431,14 @@ ifeq ($(GVARS), YES)
EEPROM_VARIANT += ${GVARS_VARIANT}
endif
ifeq ($(CORRECT_NEGATIVE_VALUES), YES)
CPPDEFS += -DCORRECT_NEGATIVE_SHIFTS
endif
ifeq ($(ARITHMETIC_OVERFLOW_CHECK), YES)
CPPDEFS += -DPREVENT_ARITHMETIC_OVERFLOW
endif
# If STD-Support is enabled
ifeq ($(EXT), STD)
CPPDEFS += -DEXTSTD
+26 -11
View File
@@ -695,39 +695,54 @@ 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)
{
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) CHECK_INCDEC_MODELVAR(event, value, min, max);
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, 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
+2 -2
View File
@@ -218,10 +218,10 @@ 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);
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
+21 -29
View File
@@ -2157,20 +2157,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
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;
}
#endif
// #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, GV_RANGELARGE_NEG, GV_RANGELARGE, attr, event);
MD_SETWEIGHT(weight,md);
//#endif
}
void menuModelMixOne(uint8_t event)
@@ -2245,20 +2240,15 @@ void menuModelMixOne(uint8_t event)
case MIX_FIELD_OFFSET:
{
lcd_putsColumnLeft(COLUMN_X, y, NO_INDENT(STR_OFFSET));
#if defined(GVARS) && !defined(CPUARM)
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;
}
#else
md2->offset = gvarMenuItem(COLUMN_X+MIXES_2ND_COLUMN, y, md2->offset, -125, 125, attr|LEFT, event);
#endif
// #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, 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);
// #endif
break;
}
case MIX_FIELD_TRIM:
@@ -2575,7 +2565,9 @@ 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
// 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);
+82 -40
View File
@@ -56,15 +56,45 @@
#elif defined(PCBSKY9X)
#define EEPROM_VER 214
#elif defined(PCBGRUVIN9X)
#define EEPROM_VER 213
#define EEPROM_VER 214
#else
#define EEPROM_VER 213
#define EEPROM_VER 214
#endif
#ifndef PACK
#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
#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 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_TrainerMix {
uint8_t srcChn:6; // 0-7 = ch1-8
uint8_t mode:2; // off,add-mode,subst-mode
@@ -286,7 +316,24 @@ PACK(typedef struct t_MixData {
int16_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;
});
#define MD_OFFSET(md) (md->offset)
#define MD_GETOFFSET(var,md) var.word=md->offset;
#define MD_SETOFFSET(var,md) md->offset=var.word;
#else
#define DELAY_STEP 2
#define SLOW_STEP 2
@@ -312,17 +359,39 @@ PACK(typedef struct t_MixData {
int8_t curveParam;
int8_t offset;
}) MixData;
#if defined(GVARS)
#define MD_WEIGHT(md) (md->weightMode ? (int16_t)GV1_LARGE+md->weight : md->weight)
#else
#define MD_WEIGHT(md) (md->weight)
#endif
#endif
#if defined(GVARS) && !defined(CPUARM)
#define MD_OFFSET(md) (md->offsetMode ? (int16_t)GV1_LARGE+md->offset : md->offset)
#else
#define MD_OFFSET(md) (md->offset)
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
#endif
#if defined(CPUARM)
@@ -383,7 +452,7 @@ enum Functions {
#endif
#if defined(GVARS)
FUNC_ADJUST_GV1,
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
@@ -719,33 +788,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
#if defined(CPUARM)
PACK(typedef struct t_PhaseData {
TRIM_ARRAY;
+446 -128
View File
@@ -471,6 +471,17 @@ 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:
// ---------------
// kmplot
@@ -478,29 +489,79 @@ int16_t applyCurve(int16_t x, int8_t idx)
// 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;
// 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);
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);
@@ -511,6 +572,8 @@ int16_t expo(int16_t x, int16_t 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
@@ -546,6 +609,7 @@ int16_t Expo::expo(int16_t x)
return -expou(-x,c,-d) + drx;
}
#endif
*/
#ifdef BOLD_FONT
ACTIVE_EXPOS_TYPE activeExpos;
@@ -583,25 +647,75 @@ 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)
int16_t calc100toRESX(int8_t x)
// #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
// is done now in makefile
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;
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
}
int16_t calc100to256(int8_t x) // return x*2.56
{
return calc100to256_16Bits(x);
}
int16_t calc100toRESX_16Bits(int16_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
}
int8_t calcRESXto100(int16_t x)
int16_t calc100toRESX(int8_t x) // return x*10.24
{
return (x*25) >> 8;
return calc100toRESX_16Bits(x);
}
// return x*1.024
int16_t calc1000toRESX(int16_t x) // improve calc time by Pat MacKenzie
{
// return x + x/32 - x/128 + x/512;
@@ -612,46 +726,91 @@ 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));
}
int8_t calcRESXto100(int16_t x)
{
return (x*25) >> 8;
}
#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 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;
// value = outputvalue with 100 mulitplied usual range -102400 to 102400; output -1024 to 1024
// changed rescaling from *100 to *256 to optimize performance
// rescaled from -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)
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;
// 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.
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)
if (value) {
int32_t tmp;
if (limit->symetrical)
tmp = (value > 0) ? ((int32_t) lim_p) : ((int32_t) -lim_n);
int16_t tmp;
if (lim->symetrical)
tmp = (value > 0) ? (lim_p) : (-lim_n);
else
tmp = (value > 0) ? ((int32_t) lim_p - ofs) : ((int32_t) -lim_n + ofs);
value = (value * tmp) / 100000;
}
#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) {
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
#endif
#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,
}
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 (ofs > lim_p) ofs = lim_p;
if (ofs < lim_n) ofs = lim_n;
if (lim->revert) ofs = -ofs; // finally do the reverse.
if (safetyCh[channel] != -128) // if safety channel available for channel check
ofs = calc100toRESX(safetyCh[channel]);
@@ -661,7 +820,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;
#ifdef HELI
int16_t cyc_anas[3] = {0};
#endif
@@ -1116,8 +1275,8 @@ uint8_t s_gvar_last = 0;
#if defined(CPUM64)
int16_t getGVarValue(int16_t x, int16_t min, int16_t max)
{
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) {
@@ -1156,12 +1315,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;
}
@@ -1690,6 +1849,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
@@ -1721,6 +1894,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
@@ -1826,7 +2010,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)/(2*RESX);
int32_t vv = ((int32_t)trim-TRIM_MIN)*(RESX-v)>>(RESX_SHIFT+1);
trim = vv;
}
else if (trimsCheckTimer > 0) {
@@ -2511,7 +2695,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] / 100;
v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before
}
}
#else
@@ -2524,7 +2708,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] / 100;
v = chans[k-MIXSRC_CH1+1] >> 8; // remove factor 256 from old mix loop; was 100 before
}
}
#endif
@@ -2594,8 +2778,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), GV_RANGELARGE_NEG, GV_RANGELARGE, s_perout_flight_phase); // open.20.fsguruh
if (offset) v += calc100toRESX_16Bits(offset); // @@@ open.20.fsguruh
}
//========== TRIMS ===============
@@ -2609,58 +2793,73 @@ void perOut(uint8_t mode, uint8_t tick10ms)
mix_trim = -1;
if (mix_trim >= 0) v += trims[mix_trim];
}
// saves 12 bytes code if done here and not together with weight; unknown reason
int16_t weight = GET_GVAR(MD_WEIGHT(md), GV_RANGELARGE_NEG, GV_RANGELARGE, 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 - ((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
// 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
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=((int32_t) v<<DEL_MULT_SHIFT);
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 (newValue<currentValue) currentValue=newValue; // Endposition; prevent toggling around the destination
}
}
else { // if is <0 because ==0 is not possible
if (md->speedDown>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);
//========== SPEED ===============
if (mode == e_perout_mode_normal && (md->speedUp || md->speedDown)) { // there are delay values
#define DEL_MULT 256
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);
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) ;
}
{
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
}
v = act[i] / DEL_MULT;
}
}
//========== WEIGHT ===============
int32_t dv = (int32_t) v * weight;
//========== DIFFERENTIAL =========
if (md->curveMode == MODE_DIFFERENTIAL) {
int8_t curveParam = 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 * (100 - curveParam)) / 100;
dv = (dv * (256 - curveParam)) >> 8;
else if (curveParam < 0 && dv > 0)
dv = (dv * (100 + curveParam)) / 100;
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 this is the first calculation for the destination channel, initialize it with 0 (otherwise would be random)
switch (md->mltpx) {
case MLTPX_REP:
@@ -2671,16 +2870,57 @@ 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 >>= 8;
dv *= *ptr;
dv /= RESXl;
dv >>= RESX_SHIFT; // same as dv /= RESXl;
*ptr = dv;
break;
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
/*
// 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 {
int16_t lo;
int16_t hi;
} words_t;
int32_t dword;
});
u_int16int32_t tmp;
tmp.dword=*ptr;
if (tmp.dword<0) {
if ((tmp.words_t.hi&0xFF80)!=0xFF80) tmp.words_t.hi=0xFF86; // set to min nearly
} else {
if ((tmp.words_t.hi|0x007F)!=0x007F) tmp.words_t.hi=0x0079; // set to max nearly
}
}
*ptr=tmp.dword;
// this implementation saves 18bytes 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(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
tick10ms = 0;
dirtyChannels &= passDirtyChannels;
@@ -2688,7 +2928,7 @@ void perOut(uint8_t mode, uint8_t tick10ms)
} while (++pass < 5 && dirtyChannels);
mixWarning = lv_mixWarning;
}
} //endfunc perOut
#define TIME_TO_WRITE() (s_eeDirtyMsk && (tmr10ms_t)(get_tmr10ms() - s_eeDirtyTime10ms) >= (tmr10ms_t)WRITE_DELAY_10MS)
@@ -2710,7 +2950,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;
// therefore forget the exact calculation and use only 1 instead; good compromise
lastTMR = tmr10ms;
#if defined(BOLD_FONT)
@@ -2737,7 +2979,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);
@@ -2764,7 +3006,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<MAX_PHASES; p++) {
if (s_fade_flight_phases & ((ACTIVE_PHASES_TYPE)1 << p)) {
s_perout_flight_phase = p;
@@ -2781,26 +3023,27 @@ void doMixerCalculations()
s_perout_flight_phase = phase;
perOut(e_perout_mode_normal, tick10ms);
}
if (tick10ms) {
#if defined(CPUARM)
requiredSpeakerVolume = g_eeGeneral.speakerVolume + VOLUME_LEVEL_DEF;
#endif
evalFunctions();
}
// 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<NUM_CHNOUT; i++) {
// chans[i] holds data from mixer. chans[i] = v*weight => 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) * 16 : chans[i]);
ex_chans[i] = q / 100; // for the next perMain
int32_t q = (s_fade_flight_phases ? (sum_chans512[i] / weight) << 4 : chans[i]);
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
@@ -2828,15 +3071,30 @@ 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;
if (g_model.limitData[ch].revert)
val = -val + calc100toRESX(g_model.limitData[ch].max + 100);
val = -val + gModelMax;
else
val = val - calc100toRESX(g_model.limitData[ch].min - 100);
val = val - gModelMin;
#if defined(PPM_LIMITS_SYMETRICAL)
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 *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
// 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 {
val = RESX + calibratedStick[g_model.thrTraceSrc == 0 ? THR_STICK : g_model.thrTraceSrc+NUM_STICKS-1];
@@ -2854,6 +3112,7 @@ void doMixerCalculations()
#else
val >>= (RESX_SHIFT-4); // calibrate it
#endif
// Timers start
for (uint8_t i=0; i<MAX_TIMERS; i++) {
@@ -2953,7 +3212,7 @@ void doMixerCalculations()
}
}
}
};
} //endfor timer loop (only two)
static tmr10ms_t s_time_tot;
static uint8_t s_cnt_1s;
@@ -3030,7 +3289,7 @@ void doMixerCalculations()
}
}
}
}
} //endif s_fade_fligh_phases
#if defined(DSM2)
if (s_rangecheck_mode) AUDIO_PLAY(AU_FRSKY_CHEEP);
@@ -3052,8 +3311,35 @@ void perMain()
#if defined(PCBSTD) && defined(ROTARY_ENCODER_NAVIGATION)
rotencPoll();
#endif
// @@@ open.20.fsguruh
// SLEEP(); // wouldn't that make sense? should save a lot of battery power!!!
/* for future use; currently very very beta... */
#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|=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<MAX_MIXER_DELTA));
// reenabling of the hardware components needed here
MCUCR&=0x00; // disable sleep
ADCSRA|=0x80; // enable ADC
#endif
return;
}
}
nextMixerEndTime = t0 + MAX_MIXER_DELTA;
// this is a very tricky implementation; lastMixerEndTime is just like a default value not to stop mixcalculations totally;
@@ -3214,7 +3500,7 @@ void perMain()
#endif
}
}
}
} //endfunc void perMain()
int16_t g_ppmIns[8];
uint8_t ppmInState = 0; //0=unsync 1..8= wait for value i-1
@@ -3239,23 +3525,22 @@ uint16_t getTmr16KHz()
}
}
ISR(TIMER_10MS_VECT, ISR_NOBLOCK) // 10ms timer
{
cli();
PAUSE_10MS_INTERRUPT();
sei();
#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;
OCR2A += bump;
#elif defined(AUDIO) || defined(VOICE)
OCR0 += 2; // interrupt every 128us
#else
static uint8_t accuracyWarble = 4; // because 16M / 1024 / 100 = 156.25. So bump every 4.
uint8_t bump = (!(accuracyWarble++ & 0x03)) ? 157 : 156;
OCR0 += bump;
#endif
// new implementation
// This ISR is only called if timer matches OCR0. We updated OCR0 on each call to make ISR happen.
// If we do not do this, we have 16,385msec time before overflow causes an IRQ
// So it's so easy instead of preventing IRQ we
//just do not cause it! It's enough not to write to OCR0. This gives plenty of time to finish ISR.
// So instead of masking we write OCR0 at the end. Max would be ISR is caused here immediately, which would not do anything,
// 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
{
static uint8_t accuracyWarble; // because 16M / 1024 / 100 = 156.25. we need to correct the fault; no start value needed
#if defined(PCBSTD) && (defined(AUDIO) || defined(VOICE))
@@ -3267,7 +3552,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) {
@@ -3276,9 +3561,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; // 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();
@@ -3293,11 +3583,39 @@ ISR(TIMER_10MS_VECT, ISR_NOBLOCK) // 10ms timer
} // end 10ms event
#endif
// 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
// 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();
RESUME_10MS_INTERRUPT();
sei();
do {
cnt10ms--;
OCR0 += 2; // interrupt every 128us
} while ((OCR0<TCNT0) && (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
}
// 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),
+49 -9
View File
@@ -748,16 +748,15 @@ extern void setTrimValue(uint8_t phase, uint8_t idx, int16_t trim);
int16_t getGVarValue(int16_t x, int16_t min, int16_t max);
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 SET_GVAR(idx, val, p) setGVarValue(idx, val)
#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, int16_t value, int8_t phase);
void setGVarValue(uint8_t x, int16_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)
#define SET_GVAR(idx, val, p) setGVarValue(idx, val, p)
#endif
#define GV1_SMALL 123
#define GV1_LARGE 1024
#define GVAR_DISPLAY_TIME 100 /*1 second*/;
extern uint8_t s_gvar_timer;
extern uint8_t s_gvar_last;
@@ -765,6 +764,29 @@ extern void setTrimValue(uint8_t phase, uint8_t idx, int16_t trim);
#define GET_GVAR(x, ...) (x)
#endif
#if defined(CPUARM)
#define GV1_SMALL 128
// define here range for ARM based controllers; could be nearly unlimited, but could cause problems in mixer calculation (overflows)
#define GV1_LARGE 512
#else
#define GV1_SMALL 128
#define GV1_LARGE 256
#endif
#define GV_IS_GV_VALUE(x,min,max) ( (x>max) || (x<min) )
#define GV_GET_GV1_VALUE(max) ( (max<=GV_RANGESMALL) ? GV1_SMALL : GV1_LARGE )
#define GV_INDEX_CALCULATION(x,max) ( (max<=GV1_SMALL) ? (uint8_t) x-GV1_SMALL : \
( (x&(GV1_LARGE*2-1))-GV1_LARGE ) )
#define GV_INDEX_CALC_DELTA(x,delta) ((x&(delta*2-1)) - delta)
#define GV_CALC_VALUE_IDX_POS(idx,delta) (-delta+idx)
#define GV_CALC_VALUE_IDX_NEG(idx,delta) (delta+idx)
#define GV_RANGESMALL (GV1_SMALL - (RESERVE_RANGE_FOR_GVARS+1))
#define GV_RANGESMALL_NEG (-GV1_SMALL + (RESERVE_RANGE_FOR_GVARS+1))
#define GV_RANGELARGE (GV1_LARGE - (RESERVE_RANGE_FOR_GVARS+1))
#define GV_RANGELARGE_NEG (-GV1_LARGE + (RESERVE_RANGE_FOR_GVARS+1))
extern uint16_t s_timeCumTot;
extern uint16_t s_timeCumThr; //gewichtete laufzeit in 1/16 sec
extern uint16_t s_timeCum16ThrP; //gewichtete laufzeit in 1/16 sec
@@ -940,6 +962,21 @@ 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;
}
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;
@@ -961,10 +998,13 @@ inline int16_t calcRESXto100(register int32_t x)
}
#else
int16_t calc100toRESX(int8_t x);
int8_t calcRESXto100(int16_t x);
int16_t calc1000toRESX(int16_t x);
int16_t calcRESXto1000(int16_t x);
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);
extern int16_t calc1000toRESX(int16_t x);
extern int16_t calcRESXto1000(int16_t x);
extern int8_t calcRESXto100(int16_t x);
#endif
#define TMR_VAROFS 5