From fbe1832467246ad9473dc7f15014b1f3cf8e522c Mon Sep 17 00:00:00 2001 From: Phil Hord Date: Thu, 14 Apr 2016 18:57:50 -0400 Subject: [PATCH] Optimize ADC temperature search Use a binary search to find our target temperate in fewer comparisons. The search algorithm is ostensibly slower because it involves a division, but it's a div-by-two so should be optimized into a simple bit-shift. Fewer comparisons involves fewer pgm_read_words and should be faster overall, but the gain is arguably tiny. Note by Traumflug: According to @Wurstnase's performance measurements, gain is actually pretty huge. Temp conversions 0..1024 before this and the previous few commits: minimum: 1011.84 clock cycles. maximum: 1993.92 clock cycles. average: 1768.85 clock cycles. Now: minimum: 437.72 clock cycles. maximum: 494.76 clock cycles. average: 470.278 clock cycles. That's a speedup by factor 3.7 on average! --- temp.c | 120 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/temp.c b/temp.c index 41b80a9..236a6b3 100644 --- a/temp.c +++ b/temp.c @@ -186,72 +186,72 @@ static uint16_t mcp3008_read(uint8_t channel) { */ #if defined TEMP_THERMISTOR || defined TEMP_MCP3008 static uint16_t temp_table_lookup(uint16_t temp, uint8_t sensor) { - uint8_t j; + uint8_t lo, hi; uint8_t table_num = temp_sensors[sensor].additional; - for (j = 1; j < NUMTEMPS; j++) { - if (pgm_read_word(&(temptable[table_num][j][0])) > temp) { - - if (DEBUG_PID && (debug_flags & DEBUG_PID)) - sersendf_P(PSTR("pin:%d Raw ADC:%d table entry: %d"), - temp_sensors[sensor].temp_pin, temp, j); - - if (sizeof(temptable[0][0]) == 2 * sizeof(uint16_t)) { - /** - This code handles temptables with value pairs and is deprecated. - It's kept for compatibility with legacy, handcrafted tables, only. - - The new code expects tables with triples, nevertheless it's smaller - and also faster. Configtool was already changed to create tables - with triples, only. - */ - // 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]))); - } else - if (sizeof(temptable[0][0]) == 3 * sizeof(uint16_t)) { - // 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); - } - - if (DEBUG_PID && (debug_flags & DEBUG_PID)) - sersendf_P(PSTR(" temp:%d.%d"), temp / 4, (temp % 4) * 25); - - // Value found, no need to read the table further. - break; - } + // Binary search for table value bigger than our target. + // + // lo = index of highest entry less than target. + // hi = index of lowest entry greater than or equal to target. + for (lo = 0, hi = NUMTEMPS - 1; hi - lo > 1; ) { + uint8_t j = lo + (hi - lo) / 2 ; + if (pgm_read_word(&(temptable[table_num][j][0])) >= temp) + hi = j ; + else + lo = j ; } if (DEBUG_PID && (debug_flags & DEBUG_PID)) - sersendf_P(PSTR(" Sensor:%d\n"), sensor); + sersendf_P(PSTR("pin:%d Raw ADC:%d table entry: %d"), + temp_sensors[sensor].temp_pin, temp, hi); - // Clamp for overflows. - if (j == NUMTEMPS) - temp = temptable[table_num][NUMTEMPS - 1][1]; + if (sizeof(temptable[0][0]) == 2 * sizeof(uint16_t)) { + /** + This code handles temptables with value pairs and is deprecated. + It's kept for compatibility with legacy, handcrafted tables, only. + + The new code expects tables with triples, nevertheless it's smaller + and also faster. Configtool was already changed to create tables + with triples, only. + */ + // Wikipedia's example linear interpolation formula. + // y = ((x - x₀)y₁ + (x₁-x)y₀) / (x₁ - x₀) + // y = temp + // x = ADC reading + // x₀= temptable[lo][0] + // x₁= temptable[hi][0] + // y₀= temptable[lo][1] + // y₁= temptable[hi][1] + temp = ( + // ((x - x₀)y₁ + ((uint32_t)temp - pgm_read_word(&(temptable[table_num][lo][0]))) * + pgm_read_word(&(temptable[table_num][hi][1])) + // + + + + // (x₁-x)y₀) + (pgm_read_word(&(temptable[table_num][hi][0])) - (uint32_t)temp) * + pgm_read_word(&(temptable[table_num][lo][1]))) + // / + / + // (x₁ - x₀) + (pgm_read_word(&(temptable[table_num][hi][0])) - + pgm_read_word(&(temptable[table_num][lo][0]))); + } else + if (sizeof(temptable[0][0]) == 3 * sizeof(uint16_t)) { + // Linear interpolation using pre-computed slope. + // y = y₁ - (x - x₁) * d₁ + #define X1 pgm_read_word(&(temptable[table_num][hi][0])) + #define Y1 pgm_read_word(&(temptable[table_num][hi][1])) + #define D1 pgm_read_word(&(temptable[table_num][hi][2])) + + temp = Y1 - ((((int32_t)temp - X1) * D1 + (1 << 7)) >> 8); + } + + if (DEBUG_PID && (debug_flags & DEBUG_PID)) + sersendf_P(PSTR(" temp:%d.%d"), temp / 4, (temp % 4) * 25); + + if (DEBUG_PID && (debug_flags & DEBUG_PID)) + sersendf_P(PSTR(" Sensor:%d\n"), sensor); return temp; }