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:
parent
65857b17dc
commit
fbe1832467
120
temp.c
120
temp.c
|
|
@ -186,72 +186,72 @@ static uint16_t mcp3008_read(uint8_t channel) {
|
||||||
*/
|
*/
|
||||||
#if defined TEMP_THERMISTOR || defined TEMP_MCP3008
|
#if defined TEMP_THERMISTOR || defined TEMP_MCP3008
|
||||||
static uint16_t temp_table_lookup(uint16_t temp, uint8_t sensor) {
|
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;
|
uint8_t table_num = temp_sensors[sensor].additional;
|
||||||
|
|
||||||
for (j = 1; j < NUMTEMPS; j++) {
|
// Binary search for table value bigger than our target.
|
||||||
if (pgm_read_word(&(temptable[table_num][j][0])) > temp) {
|
//
|
||||||
|
// lo = index of highest entry less than target.
|
||||||
if (DEBUG_PID && (debug_flags & DEBUG_PID))
|
// hi = index of lowest entry greater than or equal to target.
|
||||||
sersendf_P(PSTR("pin:%d Raw ADC:%d table entry: %d"),
|
for (lo = 0, hi = NUMTEMPS - 1; hi - lo > 1; ) {
|
||||||
temp_sensors[sensor].temp_pin, temp, j);
|
uint8_t j = lo + (hi - lo) / 2 ;
|
||||||
|
if (pgm_read_word(&(temptable[table_num][j][0])) >= temp)
|
||||||
if (sizeof(temptable[0][0]) == 2 * sizeof(uint16_t)) {
|
hi = j ;
|
||||||
/**
|
else
|
||||||
This code handles temptables with value pairs and is deprecated.
|
lo = j ;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG_PID && (debug_flags & DEBUG_PID))
|
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 (sizeof(temptable[0][0]) == 2 * sizeof(uint16_t)) {
|
||||||
if (j == NUMTEMPS)
|
/**
|
||||||
temp = temptable[table_num][NUMTEMPS - 1][1];
|
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;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue