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!
This commit is contained in:
Phil Hord 2016-04-14 18:57:50 -04:00 committed by Markus Hitter
parent 65857b17dc
commit fbe1832467
1 changed files with 60 additions and 60 deletions

52
temp.c
View File

@ -186,15 +186,24 @@ 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) {
// 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("pin:%d Raw ADC:%d table entry: %d"),
temp_sensors[sensor].temp_pin, temp, j);
temp_sensors[sensor].temp_pin, temp, hi);
if (sizeof(temptable[0][0]) == 2 * sizeof(uint16_t)) {
/**
@ -209,31 +218,31 @@ static uint16_t temp_table_lookup(uint16_t temp, uint8_t sensor) {
// 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]
// 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][j-1][0]))) *
pgm_read_word(&(temptable[table_num][j][1]))
((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][j][0])) - (uint32_t)temp) *
pgm_read_word(&(temptable[table_num][j - 1][1])))
(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][j][0])) -
pgm_read_word(&(temptable[table_num][j - 1][0])));
(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][j][0]))
#define Y1 pgm_read_word(&(temptable[table_num][j][1]))
#define D1 pgm_read_word(&(temptable[table_num][j][2]))
#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);
}
@ -241,18 +250,9 @@ static uint16_t temp_table_lookup(uint16_t temp, uint8_t sensor) {
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;
}
}
if (DEBUG_PID && (debug_flags & DEBUG_PID))
sersendf_P(PSTR(" Sensor:%d\n"), sensor);
// Clamp for overflows.
if (j == NUMTEMPS)
temp = temptable[table_num][NUMTEMPS - 1][1];
return temp;
}
#endif /* TEMP_THERMISTOR || TEMP_MCP3008 */