blob: 347156fb3350ccc3fe581933467be8cf69a9aaf8 [file] [log] [blame]
/*
* Copyright (c) 2023 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <zephyr/devicetree.h>
#include "ntc_thermistor.h"
/**
* fixp_linear_interpolate() - interpolates a value from two known points
*
* @x0: x value of point 0
* @y0: y value of point 0
* @x1: x value of point 1
* @y1: y value of point 1
* @x: the linear interpolant
*/
static int ntc_fixp_linear_interpolate(int x0, int y0, int x1, int y1, int x)
{
if (y0 == y1 || x == x0) {
return y0;
}
if (x1 == x0 || x == x1) {
return y1;
}
return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
}
/**
* ntc_compensation_compare_ohm() - Helper comparison function for bsearch
*
* Ohms are sorted in descending order, perform comparison to find
* interval indexes where key falls between
*
* @type: Pointer to ntc_type table info
* @key: Key value bsearch is looking for
* @element: Array element bsearch is searching
*/
int ntc_compensation_compare_ohm(const struct ntc_type *type, const void *key, const void *element)
{
int sgn = 0;
const struct ntc_compensation *ntc_key = key;
const struct ntc_compensation *element_val = element;
int element_idx = element_val - type->comp;
if (ntc_key->ohm > element_val->ohm) {
if (element_idx == 0) {
sgn = 0;
} else {
sgn = -1;
}
} else if (ntc_key->ohm == element_val->ohm) {
sgn = 0;
} else if (ntc_key->ohm < element_val->ohm) {
if (element_idx == (type->n_comp / 2) - 1) {
sgn = 0;
} else {
if (element_idx != (type->n_comp / 2) - 1 &&
ntc_key->ohm > type->comp[element_idx + 1].ohm) {
sgn = 0;
} else {
sgn = 1;
}
}
}
return sgn;
}
/**
* ntc_lookup_comp() - Finds indicies where ohm falls between
*
* @ohm: key value search is looking for
* @i_low: return Lower interval index value
* @i_high: return Higher interval index value
*/
static void ntc_lookup_comp(const struct ntc_type *type, unsigned int ohm, int *i_low, int *i_high)
{
const struct ntc_compensation *ptr;
struct ntc_compensation search_ohm_key = {.ohm = ohm};
ptr = bsearch(&search_ohm_key, type->comp, type->n_comp, sizeof(type->comp[0]),
type->ohm_cmp);
if (ptr) {
*i_low = ptr - type->comp;
*i_high = *i_low + 1;
} else {
*i_low = 0;
*i_high = 0;
}
}
/**
* ntc_get_ohm_of_thermistor() - Calculate the resistance read from NTC Thermistor
*
* @cfg: NTC Thermistor configuration
* @max_adc: Max ADC value
* @raw_adc: Raw ADC value read
*/
uint32_t ntc_get_ohm_of_thermistor(const struct ntc_config *cfg, uint32_t max_adc, int16_t raw_adc)
{
uint32_t ohm;
if (cfg->connected_positive) {
ohm = cfg->pulldown_ohm * max_adc / (raw_adc - 1);
} else {
ohm = cfg->pullup_ohm * (raw_adc - 1) / max_adc;
}
return ohm;
}
int32_t ntc_get_temp_mc(const struct ntc_type *type, unsigned int ohm)
{
int low, high;
int temp;
ntc_lookup_comp(type, ohm, &low, &high);
/*
* First multiplying the table temperatures with 1000 to get to
* millicentigrades (which is what we want) and then interpolating
* will give the best precision.
*/
temp = ntc_fixp_linear_interpolate(type->comp[low].ohm, type->comp[low].temp_c * 1000,
type->comp[high].ohm, type->comp[high].temp_c * 1000,
ohm);
return temp;
}