From df3d827a361075a976c3c98767db001dc3f008df Mon Sep 17 00:00:00 2001 From: Phil Hord Date: Thu, 14 Apr 2016 18:20:41 -0400 Subject: [PATCH] Expand thermistortable to include precomputed slope Save a division at runtime by pre-calculating the slope between each pair of adjacent thermistortable values. Since we use the larger value each time, save the slope between two values A and B in the table with the B data. Therefore the slope is that between each value and its predecessor in the list. Store this new value in the third element of the now 3-integers-wide array which makes up the table. Use fixed-point 6.10 format to store the slope. This is almost too narrow for some slopes and maybe it should be changed to 8.8 fixed-point. In practice this presents a loss in accuracy, but it is still significantly better than the previous fixed-sample-size table production method. In particular no provision is made to handle values which scale over 65535, and it seems we should at least warn about this if not simply fail before letting the user go off compiling his code. Add a new flag TEMPTABLE_FORMAT and define it as 1 to tell the code that we are using this new and incompatible format. This lets us tolerate old hand-crafted thermistor tables by keeping the slower algorithm in case one is still used. New thermistor tables should be defined with this new format and with the FORMAT define set accordingly. With the default 25 samples this adds 100 bytes to the flash image for the thermistortable storage for two different thermistors. But the code is simplified and saves me 134 bytes in the bargain for a net decrease in flash size of 34 bytes. --- configtool/thermistortablefile.py | 19 +++++++---- temp.c | 56 +++++++++++++++++++------------ 2 files changed, 47 insertions(+), 28 deletions(-) diff --git a/configtool/thermistortablefile.py b/configtool/thermistortablefile.py index e03355a55..9f8e0c294 100644 --- a/configtool/thermistortablefile.py +++ b/configtool/thermistortablefile.py @@ -54,6 +54,7 @@ def generateTempTables(sensors, settings): ofp.output("#define NUMTABLES %d" % len(tl)) ofp.output("#define NUMTEMPS %d" % N) + ofp.output("#define TEMPTABLE_FORMAT 1") ofp.output(""); for i in range(len(tl)): @@ -65,7 +66,7 @@ def generateTempTables(sensors, settings): ofp.close(); return True - ofp.output("const uint16_t PROGMEM temptable[NUMTABLES][NUMTEMPS][2] = {") + ofp.output("const uint16_t PROGMEM temptable[NUMTABLES][NUMTEMPS][3] = {") tcount = 0 for tn in tl: @@ -102,6 +103,7 @@ def BetaTable(ofp, params, names, settings, finalTable): samples = optimizeTempTable(thrm, N, hiadc) + prev = samples[0] for i in samples: t = thrm.temp(i) if t is None: @@ -117,10 +119,12 @@ def BetaTable(ofp, params, names, settings, finalTable): c = " " else: c = "," - ostr = (" {%4s, %5s}%s // %4d C, %6.0f ohms, %0.3f V," - " %0.2f mW") % (i, int(t * 4), c, int(t), int(round(r)), - vTherm, ptherm * 1000) + delta = (t-thrm.temp(prev))/(prev-i) if i!=prev else 0 + ostr = (" {%4s, %5s, %5s}%s // %4d C, %6.0f ohms, %0.3f V," + " %0.2f mW, m=%0.4f") % (i, int(t * 4), int(delta*4*256), c, + int(t), int(round(r)), vTherm, ptherm * 1000, delta) ofp.output(ostr) + prev = i if finalTable: ofp.output(" }") @@ -145,6 +149,7 @@ def SteinhartHartTable(ofp, params, names, settings, finalTable): samples = optimizeTempTable(thrm, N, hiadc) + prev = samples[0] for i in samples: t = thrm.temp(i) if t is None: @@ -157,8 +162,10 @@ def SteinhartHartTable(ofp, params, names, settings, finalTable): c = " " else: c = "," - ofp.output(" {%4d, %5d}%s // %4d C, %6d ohms" % - (i, int(t * 4), c, int(t), int(round(r)))) + delta = (t-thrm.temp(prev))/(prev-i) if i!=prev else 0 + ofp.output(" {%4d, %5d, %5d}%s // %4d C, %6d ohms, m=%0.4f" % + (i, int(t * 4), int(delta*4*256), c, int(t), int(round(r))), delta) + prev = i if finalTable: ofp.output(" }") diff --git a/temp.c b/temp.c index e0162cf5b..659fa39e8 100644 --- a/temp.c +++ b/temp.c @@ -196,28 +196,40 @@ static uint16_t temp_table_lookup(uint16_t temp, uint8_t sensor) { sersendf_P(PSTR("pin:%d Raw ADC:%d table entry: %d"), temp_sensors[sensor].temp_pin, temp, j); - // Wikipedia's example linear interpolation formula. - // y = ((x - x₀)y₁ + (x₁-x)y₀) / (x₁ - x₀) - // y = temp - // x = ADC reading - // x₀= temptable[j-1][0] - // x₁= temptable[j][0] - // y₀= temptable[j-1][1] - // y₁= temptable[j][1] - temp = ( - // ((x - x₀)y₁ - ((uint32_t)temp - pgm_read_word(&(temptable[table_num][j-1][0]))) * - pgm_read_word(&(temptable[table_num][j][1])) - // + - + - // (x₁-x)y₀) - (pgm_read_word(&(temptable[table_num][j][0])) - (uint32_t)temp) * - pgm_read_word(&(temptable[table_num][j - 1][1]))) - // / - / - // (x₁ - x₀) - (pgm_read_word(&(temptable[table_num][j][0])) - - pgm_read_word(&(temptable[table_num][j - 1][0]))); + #if (TEMPTABLE_FORMAT == 0) + // Wikipedia's example linear interpolation formula. + // y = ((x - x₀)y₁ + (x₁-x)y₀) / (x₁ - x₀) + // y = temp + // x = ADC reading + // x₀= temptable[j-1][0] + // x₁= temptable[j][0] + // y₀= temptable[j-1][1] + // y₁= temptable[j][1] + temp = ( + // ((x - x₀)y₁ + ((uint32_t)temp - pgm_read_word(&(temptable[table_num][j-1][0]))) * + pgm_read_word(&(temptable[table_num][j][1])) + // + + + + // (x₁-x)y₀) + (pgm_read_word(&(temptable[table_num][j][0])) - (uint32_t)temp) * + pgm_read_word(&(temptable[table_num][j - 1][1]))) + // / + / + // (x₁ - x₀) + (pgm_read_word(&(temptable[table_num][j][0])) - + pgm_read_word(&(temptable[table_num][j - 1][0]))); + #elif (TEMPTABLE_FORMAT == 1) + // Linear interpolation using pre-computed slope + // y = y₁ - (x-x₁)*d₁ + #define X1 pgm_read_word(&(temptable[table_num][j][0])) + #define Y1 pgm_read_word(&(temptable[table_num][j][1])) + #define D1 pgm_read_word(&(temptable[table_num][j][2])) + + temp = Y1 - ((((int32_t)temp - X1) * D1 + (1<<7)) >> 8); + #else + #error "temptable format unrecognized" + #endif if (DEBUG_PID && (debug_flags & DEBUG_PID)) sersendf_P(PSTR(" temp:%d.%d"), temp / 4, (temp % 4) * 25);