From 35ae76ab7060364a2e343b59d58ec86d8e06990b Mon Sep 17 00:00:00 2001 From: Jett Rink Date: Tue, 12 Jun 2018 15:18:35 -0600 Subject: thermistor: move thermistor tables into common code The exact same 2 lookup tables are in multiple board files. We seem to reuse the 2 thermistor circuit enough that we should single source them in a common location. BRANCH=none BUG=none TEST=yorp sensors still function properly Change-Id: Ic393c609c78c8a51c55a67b639c1fb9e6c387d8a Signed-off-by: Jett Rink Reviewed-on: https://chromium-review.googlesource.com/1100943 Reviewed-by: Randall Spangler Reviewed-by: Furquan Shaikh Reviewed-by: Justin TerAvest --- driver/temp_sensor/thermistor.c | 147 ++++++++++++++++++++++++++++++++ driver/temp_sensor/thermistor.h | 42 ++++++++- driver/temp_sensor/thermistor_ncp15wb.c | 52 ----------- 3 files changed, 187 insertions(+), 54 deletions(-) create mode 100644 driver/temp_sensor/thermistor.c (limited to 'driver/temp_sensor') diff --git a/driver/temp_sensor/thermistor.c b/driver/temp_sensor/thermistor.c new file mode 100644 index 0000000000..d9c70f7386 --- /dev/null +++ b/driver/temp_sensor/thermistor.c @@ -0,0 +1,147 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Common thermistor code for Chrome EC */ + +#include "adc.h" +#include "common.h" +#include "thermistor.h" +#include "util.h" + +int thermistor_linear_interpolate(uint16_t mv, + const struct thermistor_info *info) +{ + const struct thermistor_data_pair *data = info->data; + int v_high = 0, v_low = 0, t_low, t_high, num_steps; + int head, tail, mid = 0; + + /* We need at least two points to form a line. */ + ASSERT(info->num_pairs >= 2); + + /* + * If input value is out of bounds return the lowest or highest + * value in the data sets provided. + */ + if (mv > data[0].mv * info->scaling_factor) + return data[0].temp; + else if (mv < data[info->num_pairs - 1].mv * info->scaling_factor) + return data[info->num_pairs - 1].temp; + + head = 0; + tail = info->num_pairs - 1; + while (head != tail) { + mid = (head + tail) / 2; + v_high = data[mid].mv * info->scaling_factor; + v_low = data[mid + 1].mv * info->scaling_factor; + + if ((mv <= v_high) && (mv >= v_low)) + break; + else if (mv > v_high) + tail = mid; + else if (mv < v_low) + head = mid + 1; + } + + t_low = data[mid].temp; + t_high = data[mid + 1].temp; + + /* + * The obvious way of doing this is to figure out how many mV per + * degree are in between the two points (mv_per_deg_c), and then how + * many of those exist between the input voltage and voltage of + * lower temperature : + * 1. mv_per_deg_c = (v_high - v_low) / (t_high - t_low) + * 2. num_steps = (v_high - mv) / mv_per_deg_c + * 3. result = t_low + num_steps + * + * Combine #1 and #2 to mitigate precision loss due to integer division. + */ + num_steps = ((v_high - mv) * (t_high - t_low)) / (v_high - v_low); + return t_low + num_steps; +} + +#ifdef CONFIG_STEINHART_HART_3V3_51K1_47K_4050B +/* + * Data derived from Steinhart-Hart equation in a resistor divider circuit with + * Vdd=3300mV, R = 51.1Kohm, and thermistor (B = 4050, T0 = 298.15 K, nominal + * resistance (R0) = 47Kohm). + */ +#define THERMISTOR_SCALING_FACTOR_51_47 11 +static const struct thermistor_data_pair thermistor_data_51_47[] = { + { 2512 / THERMISTOR_SCALING_FACTOR_51_47, 0 }, + { 2158 / THERMISTOR_SCALING_FACTOR_51_47, 10 }, + { 1772 / THERMISTOR_SCALING_FACTOR_51_47, 20 }, + { 1398 / THERMISTOR_SCALING_FACTOR_51_47, 30 }, + { 1070 / THERMISTOR_SCALING_FACTOR_51_47, 40 }, + { 803 / THERMISTOR_SCALING_FACTOR_51_47, 50 }, + { 597 / THERMISTOR_SCALING_FACTOR_51_47, 60 }, + { 443 / THERMISTOR_SCALING_FACTOR_51_47, 70 }, + { 329 / THERMISTOR_SCALING_FACTOR_51_47, 80 }, + { 285 / THERMISTOR_SCALING_FACTOR_51_47, 85 }, + { 247 / THERMISTOR_SCALING_FACTOR_51_47, 90 }, + { 214 / THERMISTOR_SCALING_FACTOR_51_47, 95 }, + { 187 / THERMISTOR_SCALING_FACTOR_51_47, 100 }, +}; + +static const struct thermistor_info thermistor_info_51_47 = { + .scaling_factor = THERMISTOR_SCALING_FACTOR_51_47, + .num_pairs = ARRAY_SIZE(thermistor_data_51_47), + .data = thermistor_data_51_47, +}; + +int get_temp_3v3_51k1_47k_4050b(int idx_adc, int *temp_ptr) +{ + int mv = adc_read_channel(idx_adc); + + if (mv < 0) + return EC_ERROR_UNKNOWN; + + *temp_ptr = thermistor_linear_interpolate(mv, &thermistor_info_51_47); + *temp_ptr = C_TO_K(*temp_ptr); + return EC_SUCCESS; +} +#endif /* CONFIG_STEINHART_HART_3V3_51K1_47K_4050B */ + +#ifdef CONFIG_STEINHART_HART_3V3_13K7_47K_4050B +/* + * Data derived from Steinhart-Hart equation in a resistor divider circuit with + * Vdd=3300mV, R = 13.7Kohm, and thermistor (B = 4050, T0 = 298.15 K, nominal + * resistance (R0) = 47Kohm). + */ +#define THERMISTOR_SCALING_FACTOR_13_47 13 +static const struct thermistor_data_pair thermistor_data_13_47[] = { + { 3044 / THERMISTOR_SCALING_FACTOR_13_47, 0 }, + { 2890 / THERMISTOR_SCALING_FACTOR_13_47, 10 }, + { 2680 / THERMISTOR_SCALING_FACTOR_13_47, 20 }, + { 2418 / THERMISTOR_SCALING_FACTOR_13_47, 30 }, + { 2117 / THERMISTOR_SCALING_FACTOR_13_47, 40 }, + { 1800 / THERMISTOR_SCALING_FACTOR_13_47, 50 }, + { 1490 / THERMISTOR_SCALING_FACTOR_13_47, 60 }, + { 1208 / THERMISTOR_SCALING_FACTOR_13_47, 70 }, + { 966 / THERMISTOR_SCALING_FACTOR_13_47, 80 }, + { 860 / THERMISTOR_SCALING_FACTOR_13_47, 85 }, + { 766 / THERMISTOR_SCALING_FACTOR_13_47, 90 }, + { 679 / THERMISTOR_SCALING_FACTOR_13_47, 95 }, + { 603 / THERMISTOR_SCALING_FACTOR_13_47, 100 }, +}; + +static const struct thermistor_info thermistor_info_13_47 = { + .scaling_factor = THERMISTOR_SCALING_FACTOR_13_47, + .num_pairs = ARRAY_SIZE(thermistor_data_13_47), + .data = thermistor_data_13_47, +}; + +int get_temp_3v3_13k7_47k_4050b(int idx_adc, int *temp_ptr) +{ + int mv = adc_read_channel(idx_adc); + + if (mv < 0) + return EC_ERROR_UNKNOWN; + + *temp_ptr = thermistor_linear_interpolate(mv, &thermistor_info_13_47); + *temp_ptr = C_TO_K(*temp_ptr); + return EC_SUCCESS; +} +#endif /* CONFIG_STEINHART_HART_3V3_13K7_47K_4050B */ diff --git a/driver/temp_sensor/thermistor.h b/driver/temp_sensor/thermistor.h index af3ea398c8..49edee5451 100644 --- a/driver/temp_sensor/thermistor.h +++ b/driver/temp_sensor/thermistor.h @@ -33,7 +33,7 @@ struct thermistor_info { }; /** - * @brief Calculate temperature using linear interpolation of data points. + * Calculate temperature using linear interpolation of data points. * * Given a set of datapoints, the algorithm will calculate the "step" in * between each one in order to interpolate missing entries. @@ -44,8 +44,9 @@ struct thermistor_info { * @return temperature in C */ int thermistor_linear_interpolate(uint16_t mv, - const struct thermistor_info *info); + const struct thermistor_info *info); +#ifdef CONFIG_THERMISTOR_NCP15WB /** * ncp15wb temperature conversion routine. * @@ -54,5 +55,42 @@ int thermistor_linear_interpolate(uint16_t mv, * @return temperature in C. */ int ncp15wb_calculate_temp(uint16_t adc); +#endif /* CONFIG_THERMISTOR_NCP15WB */ + +#ifdef CONFIG_STEINHART_HART_3V3_13K7_47K_4050B +/** + * Reads the specified ADC channel and uses a lookup table and interpolation to + * return a temperature in degrees K. + * + * The lookup table is based off of a resistor divider circuit on 3.3V with a + * 13.7K resistor in series with a thermistor with nominal value of 47K (at 25C) + * and a B (25/100) value of 4050. + * + * @param idx_adc The idx value from the temp_sensor_t struct, which is + * the ADC channel to read and convert to degrees K + * @param temp_ptr Destination for temperature (in degrees K) + * + * @return EC_SUCCESS, or non-zero if error. + */ +int get_temp_3v3_13k7_47k_4050b(int idx_adc, int *temp_ptr); +#endif + +#ifdef CONFIG_STEINHART_HART_3V3_51K1_47K_4050B +/** + * Reads the specified ADC channel and uses a lookup table and interpolation to + * return a temperature in degrees K. + * + * The lookup table is based off of a resistor divider circuit on 3.3V with a + * 51.1K resistor in series with a thermistor with nominal value of 47K (at 25C) + * and a B (25/100) value of 4050. + * + * @param idx_adc The idx value from the temp_sensor_t struct, which is + * the ADC channel to read and convert to degrees K + * @param temp_ptr Destination for temperature (in degrees K) + * + * @return EC_SUCCESS, or non-zero if error. + */ +int get_temp_3v3_51k1_47k_4050b(int idx_adc, int *temp_ptr); +#endif #endif /* __CROS_EC_TEMP_SENSOR_THERMISTOR_NCP15WB_H */ diff --git a/driver/temp_sensor/thermistor_ncp15wb.c b/driver/temp_sensor/thermistor_ncp15wb.c index dae8251575..51e884ed35 100644 --- a/driver/temp_sensor/thermistor_ncp15wb.c +++ b/driver/temp_sensor/thermistor_ncp15wb.c @@ -98,55 +98,3 @@ int ncp15wb_calculate_temp(uint16_t adc) return temp; } - -int thermistor_linear_interpolate(uint16_t mv, - const struct thermistor_info *info) -{ - const struct thermistor_data_pair *data = info->data; - int v_high = 0, v_low = 0, t_low, t_high, num_steps; - int head, tail, mid = 0; - - /* We need at least two points to form a line. */ - ASSERT(info->num_pairs >= 2); - - /* - * If input value is out of bounds return the lowest or highest - * value in the data sets provided. - */ - if (mv > data[0].mv * info->scaling_factor) - return data[0].temp; - else if (mv < data[info->num_pairs - 1].mv * info->scaling_factor) - return data[info->num_pairs - 1].temp; - - head = 0; - tail = info->num_pairs - 1; - while (head != tail) { - mid = (head + tail) / 2; - v_high = data[mid].mv * info->scaling_factor; - v_low = data[mid + 1].mv * info->scaling_factor; - - if ((mv <= v_high) && (mv >= v_low)) - break; - else if (mv > v_high) - tail = mid; - else if (mv < v_low) - head = mid + 1; - } - - t_low = data[mid].temp; - t_high = data[mid + 1].temp; - - /* - * The obvious way of doing this is to figure out how many mV per - * degree are in between the two points (mv_per_deg_c), and then how - * many of those exist between the input voltage and voltage of - * lower temperature : - * 1. mv_per_deg_c = (v_high - v_low) / (t_high - t_low) - * 2. num_steps = (v_high - mv) / mv_per_deg_c - * 3. result = t_low + num_steps - * - * Combine #1 and #2 to mitigate precision loss due to integer division. - */ - num_steps = ((v_high - mv) * (t_high - t_low)) / (v_high - v_low); - return t_low + num_steps; -} -- cgit v1.2.1