blob: b823737b7a4f1b56143eb6ae527bb9b8508f11c9 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_DEVICES_LIB_THERMAL_INCLUDE_LIB_THERMAL_NTC_H_
#define SRC_DEVICES_LIB_THERMAL_INCLUDE_LIB_THERMAL_NTC_H_
#include <zircon/assert.h>
#include <zircon/types.h>
#include <algorithm>
#include <vector>
#include <ddk/metadata.h>
#define NTC_CHANNELS_METADATA_PRIVATE (0x4e544300 | DEVICE_METADATA_PRIVATE) // NTCd
#define NTC_PROFILE_METADATA_PRIVATE (0x4e545000 | DEVICE_METADATA_PRIVATE) // NTPd
namespace thermal {
static constexpr uint32_t kMaxProfileLen = 50;
static constexpr uint32_t kMaxNameLen = 50;
struct NtcTable {
float temperature_c;
uint32_t resistance_ohm;
};
struct NtcChannel {
uint32_t adc_channel;
uint32_t pullup_ohms;
uint32_t profile_idx;
char name[kMaxNameLen];
};
struct NtcInfo {
char part[kMaxNameLen];
NtcTable profile[kMaxProfileLen]; // profile table should be sorted in decreasing resistance
};
class Ntc {
public:
Ntc(NtcInfo ntc_info, uint32_t pullup_ohms)
: profile_(ntc_info.profile, ntc_info.profile + kMaxProfileLen), pullup_ohms_(pullup_ohms) {
// Sort profile table descending by resistance to ensure proper lookup
auto sort_compare = [](NtcTable const& x, NtcTable const& y) -> bool {
return x.resistance_ohm > y.resistance_ohm;
};
std::sort(profile_.begin(), profile_.end(), sort_compare);
}
// we use a normalized sample [0-1] to prevent having to worry about adc resolution
// in this library. This assumes the call site will normalize teh value appropriately
// Since the thermistor is in series with a pullup resistor, we must convert our sample
// value to a resistance then lookup in the profile table.
zx_status_t GetTemperatureCelsius(float norm_sample, float* out) const {
if ((norm_sample < 0) || (norm_sample > 1.0)) {
return ZX_ERR_INVALID_ARGS;
}
float ratio = -(norm_sample) / (norm_sample - 1);
float resistance_f = ratio * static_cast<float>(pullup_ohms_);
if ((resistance_f > static_cast<float>(std::numeric_limits<uint32_t>::max())) ||
(resistance_f < static_cast<float>(std::numeric_limits<uint32_t>::min()))) {
return ZX_ERR_INVALID_ARGS;
}
return LookupCelsius(static_cast<uint32_t>(resistance_f), out);
}
zx_status_t LookupCelsius(uint32_t resistance, float* out) const {
auto lb_compare = [](NtcTable const& lhs, uint32_t val) -> bool {
return lhs.resistance_ohm > val;
};
auto low = std::lower_bound(profile_.begin(), profile_.end(), resistance, lb_compare);
size_t idx = (low - profile_.begin());
if ((idx == 0) || (idx == kMaxProfileLen)) {
return ZX_ERR_INVALID_ARGS;
}
// Since all entries in profile table may not have been used (passed as metadata)
// check to make sure lookup wasn't out of range of the table.
if (profile_.at(idx).resistance_ohm == kInvalidResistance) {
return ZX_ERR_INVALID_ARGS;
}
float span =
static_cast<float>(profile_.at(idx - 1).resistance_ohm - profile_.at(idx).resistance_ohm);
float scale = static_cast<float>(resistance - profile_.at(idx).resistance_ohm) / span;
*out = profile_.at(idx).temperature_c -
scale * (profile_.at(idx).temperature_c - profile_.at(idx - 1).temperature_c);
return ZX_OK;
}
private:
static constexpr uint32_t kInvalidResistance = 0;
std::vector<NtcTable> profile_;
uint32_t pullup_ohms_ = 0;
};
} // namespace thermal
#endif // SRC_DEVICES_LIB_THERMAL_INCLUDE_LIB_THERMAL_NTC_H_