763 lines
28 KiB
C
763 lines
28 KiB
C
//
|
||
// Created by cfif on 02.12.2025.
|
||
//
|
||
#include "ADC_Temp.h"
|
||
#include <math.h>
|
||
#include <stdio.h>
|
||
#include <stdbool.h>
|
||
|
||
// Структура для хранения табличных данных
|
||
typedef struct {
|
||
int temp_c; // Температура (°C)
|
||
float r_nom; // Номинальное сопротивление (Ω)
|
||
} ntc_table_entry;
|
||
|
||
// Таблица из документа KST45 (Duct)
|
||
static const ntc_table_entry ntc_table_duct[] = {
|
||
{-40, 100950.0f},
|
||
{-35, 72777.0f},
|
||
{-30, 53100.0f},
|
||
{-25, 39111.0f},
|
||
{-20, 29121.0f},
|
||
{-15, 21879.0f},
|
||
{-10, 16599.0f},
|
||
{-5, 12695.0f},
|
||
{0, 9795.0f},
|
||
{5, 7616.0f},
|
||
{10, 5970.0f},
|
||
{15, 4712.0f},
|
||
{20, 3747.0f},
|
||
{25, 3000.0f},
|
||
{30, 2417.0f},
|
||
{35, 1959.0f},
|
||
{40, 1598.0f},
|
||
{45, 1311.0f},
|
||
{50, 1081.0f},
|
||
{55, 895.9f},
|
||
{60, 746.4f},
|
||
{65, 624.9f},
|
||
{70, 525.6f},
|
||
{75, 444.4f},
|
||
{80, 377.4f},
|
||
{85, 321.7f}
|
||
};
|
||
|
||
// Таблица из документа Incar
|
||
static const ntc_table_entry ntc_table_incar[] = {
|
||
{-40, 101000.0f},
|
||
{-35, 72600.0f},
|
||
{-30, 52720.0f},
|
||
{-25, 38660.0f},
|
||
{-20, 28620.0f},
|
||
{-15, 21390.0f},
|
||
{-10, 16120.0f},
|
||
{-5, 12260.0f},
|
||
{-4, 11620.0f},
|
||
{-3, 11010.0f},
|
||
{-2, 10440.0f},
|
||
{-1, 9907.0f},
|
||
{0, 9399.0f},
|
||
{1, 8924.0f},
|
||
{2, 8473.0f},
|
||
{3, 8048.0f},
|
||
{4, 7646.0f},
|
||
{5, 7267.0f},
|
||
{6, 6909.0f},
|
||
{7, 6570.0f},
|
||
{8, 6250.0f},
|
||
{9, 5947.0f},
|
||
{10, 5661.0f},
|
||
{15, 4441.0f},
|
||
{20, 3512.0f},
|
||
{25, 2795.0f},
|
||
{30, 2239.0f},
|
||
{35, 1806.0f},
|
||
{40, 1464.0f},
|
||
{45, 1195.0f},
|
||
{50, 980.0f},
|
||
{55, 809.0f},
|
||
{60, 670.0f},
|
||
{65, 559.0f},
|
||
{70, 468.0f},
|
||
{75, 394.0f},
|
||
{80, 333.0f},
|
||
{85, 283.0f}
|
||
};
|
||
|
||
// Таблица для NTC 10 кОм (Ambient)
|
||
static const ntc_table_entry ntc_table_ambient[] = {
|
||
{-40, 332776.0f},
|
||
{-35, 240264.0f},
|
||
{-30, 175427.0f},
|
||
{-25, 129449.0f},
|
||
{-20, 96481.0f},
|
||
{-15, 72592.0f},
|
||
{-10, 55109.0f},
|
||
{-5, 42193.0f},
|
||
{0, 32566.0f},
|
||
{5, 25338.0f},
|
||
{10, 19869.0f},
|
||
{15, 15695.0f},
|
||
{20, 12486.0f},
|
||
{25, 10000.0f},
|
||
{30, 8060.0f},
|
||
{35, 6536.0f},
|
||
{40, 5331.0f},
|
||
{45, 4373.0f},
|
||
{50, 3606.0f},
|
||
{55, 2990.0f},
|
||
{60, 2490.0f},
|
||
{65, 2085.0f},
|
||
{70, 1754.0f},
|
||
{75, 1482.0f},
|
||
{80, 1257.0f},
|
||
{85, 1071.0f},
|
||
{90, 916.4f},
|
||
{95, 786.9f},
|
||
{100, 678.1f},
|
||
{105, 586.5f},
|
||
{110, 509.1f},
|
||
{115, 443.3f},
|
||
{120, 387.3f},
|
||
{125, 339.5f},
|
||
{130, 298.4f},
|
||
{135, 263.1f},
|
||
{140, 232.6f},
|
||
{145, 206.1f},
|
||
{150, 183.2f}
|
||
};
|
||
|
||
// Глобальная структура для хранения таблиц быстрого доступа
|
||
fast_lookup_tables_t g_fast_tables = {
|
||
.duct_initialized = false,
|
||
.incar_initialized = false,
|
||
.ambient_initialized = false,
|
||
.duct_r1 = 3300.0f,
|
||
.incar_r1 = 3300.0f,
|
||
.ambient_r1 = 3300.0f,
|
||
.vcc_mv = VCC_DIVIDER_MV,
|
||
.vref_mv = VREF_MV
|
||
};
|
||
|
||
// Активная конфигурация
|
||
static ntc_config_t active_config = {
|
||
.table_type = TABLE_DUCT,
|
||
.r1 = 3300.0f,
|
||
.vcc_mv = VCC_DIVIDER_MV,
|
||
.vref_mv = VREF_MV,
|
||
.start_temp = -40,
|
||
.end_temp = 85,
|
||
.table_size = TABLE_SIZE_DUCT
|
||
};
|
||
|
||
|
||
// Вспомогательная функция для получения таблицы по типу
|
||
static const ntc_table_entry* get_table_by_type(eNtcTable table_type, uint16_t* size, int16_t* start_temp, int16_t* end_temp) {
|
||
const ntc_table_entry* table = NULL;
|
||
*size = 0;
|
||
|
||
switch(table_type) {
|
||
case TABLE_DUCT:
|
||
table = ntc_table_duct;
|
||
*size = TABLE_SIZE_DUCT;
|
||
*start_temp = -40;
|
||
*end_temp = 85;
|
||
break;
|
||
case TABLE_INCAR:
|
||
table = ntc_table_incar;
|
||
*size = TABLE_SIZE_INCAR;
|
||
*start_temp = -40;
|
||
*end_temp = 85;
|
||
break;
|
||
case TABLE_AMBIENT:
|
||
table = ntc_table_ambient;
|
||
*size = TABLE_SIZE_AMBIENT;
|
||
*start_temp = -40;
|
||
*end_temp = 150;
|
||
break;
|
||
default:
|
||
table = ntc_table_duct;
|
||
*size = TABLE_SIZE_DUCT;
|
||
*start_temp = -40;
|
||
*end_temp = 85;
|
||
break;
|
||
}
|
||
return table;
|
||
}
|
||
|
||
// Функция для получения указателя на таблицу быстрого доступа по типу
|
||
static adc_temp_lookup* get_fast_table_by_type(eNtcTable table_type) {
|
||
switch(table_type) {
|
||
case TABLE_DUCT:
|
||
return g_fast_tables.duct;
|
||
case TABLE_INCAR:
|
||
return g_fast_tables.incar;
|
||
case TABLE_AMBIENT:
|
||
return g_fast_tables.ambient;
|
||
default:
|
||
return g_fast_tables.duct;
|
||
}
|
||
}
|
||
|
||
// Функция для получения статуса инициализации таблицы
|
||
static bool is_table_initialized(eNtcTable table_type) {
|
||
switch(table_type) {
|
||
case TABLE_DUCT:
|
||
return g_fast_tables.duct_initialized;
|
||
case TABLE_INCAR:
|
||
return g_fast_tables.incar_initialized;
|
||
case TABLE_AMBIENT:
|
||
return g_fast_tables.ambient_initialized;
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// Расширенная функция расчёта сопротивления NTC с учетом VCC и VREF
|
||
static float calculate_resistance_full(uint16_t adc_value, float r1, float vcc_mv, float vref_mv) {
|
||
if (adc_value == 0) {
|
||
return INFINITY; // Бесконечное сопротивление при 0 ADC (обрыв)
|
||
}
|
||
|
||
// Защита от переполнения
|
||
if (adc_value >= (uint16_t) (ADC_MAX - 1)) {
|
||
return 0.0f; // Нулевое сопротивление при максимальном ADC (короткое замыкание)
|
||
}
|
||
|
||
// Напряжение на NTC (измеренное АЦП)
|
||
float v_ntc = (adc_value * vref_mv) / ADC_MAX;
|
||
|
||
// Напряжение на балластном резисторе
|
||
float v_r1 = vcc_mv - v_ntc;
|
||
|
||
// Проверка на корректность
|
||
if (v_r1 <= 0.0f) {
|
||
return 0.0f; // Короткое замыкание или ошибка
|
||
}
|
||
|
||
if (v_ntc <= 0.0f) {
|
||
return INFINITY; // Обрыв
|
||
}
|
||
|
||
// Сопротивление NTC (закон Ома)
|
||
float r_ntc = r1 * (v_ntc / v_r1);
|
||
|
||
// Ограничиваем разумными пределами (1 Ом - 10 МОм)
|
||
if (r_ntc < 1.0f) r_ntc = 1.0f;
|
||
if (r_ntc > 10000000.0f) r_ntc = 10000000.0f;
|
||
|
||
return r_ntc;
|
||
}
|
||
|
||
// Оптимизированная функция для случая VCC == VREF
|
||
static float calculate_resistance_optimized(uint16_t adc_value, float r1) {
|
||
if (adc_value == 0 || adc_value >= (uint16_t) ADC_MAX) {
|
||
return 0.0f;
|
||
}
|
||
return r1 * (float) adc_value / (ADC_MAX - (float) adc_value);
|
||
}
|
||
|
||
// Главная функция расчета сопротивления (использует константу времени компиляции)
|
||
static float calculate_resistance(uint16_t adc_value, float r1) {
|
||
#if VCC_EQUALS_VREF == 1
|
||
// Оптимизированный путь, когда напряжения равны
|
||
return calculate_resistance_optimized(adc_value, r1);
|
||
#else
|
||
// Точный путь с учетом разных напряжений
|
||
return calculate_resistance_full(adc_value, r1, VCC_DIVIDER_MV, VREF_MV);
|
||
#endif
|
||
}
|
||
|
||
|
||
// Бинарный поиск в таблице
|
||
static int find_interval_index(float resistance, const ntc_table_entry* table, uint16_t table_size) {
|
||
int left = 0;
|
||
int right = table_size - 1;
|
||
|
||
// Проверка границ
|
||
if (resistance >= table[0].r_nom) return 0;
|
||
if (resistance <= table[right].r_nom) return right - 1;
|
||
|
||
// Бинарный поиск
|
||
while (left <= right) {
|
||
int mid = left + (right - left) / 2;
|
||
|
||
if (mid < table_size - 1 &&
|
||
resistance <= table[mid].r_nom &&
|
||
resistance >= table[mid + 1].r_nom) {
|
||
return mid;
|
||
}
|
||
|
||
if (resistance > table[mid].r_nom) {
|
||
right = mid - 1;
|
||
} else {
|
||
left = mid + 1;
|
||
}
|
||
}
|
||
|
||
return -1; // Не найден
|
||
}
|
||
|
||
// Интерполяция с использованием уравнения Стейнхарта-Харта между точками
|
||
static float interpolate_steinhart(float resistance, int index, const ntc_table_entry* table) {
|
||
// Берем две соседние точки из таблицы
|
||
float t1 = (float) table[index].temp_c + 273.15f; // в Кельвинах
|
||
float t2 = (float) table[index + 1].temp_c + 273.15f;
|
||
float r1 = table[index].r_nom;
|
||
float r2 = table[index + 1].r_nom;
|
||
|
||
// Вычисляем коэффициент B для интервала
|
||
float B = logf(r1 / r2) / (1.0f / t1 - 1.0f / t2);
|
||
|
||
// Используем уравнение Стейнхарта-Харта для вычисления температуры
|
||
float steinhart = logf(resistance / r1) / B + 1.0f / t1;
|
||
float temp_k = 1.0f / steinhart;
|
||
|
||
return temp_k - 273.15f; // Конвертация в °C
|
||
}
|
||
|
||
// Линейная интерполяция в логарифмическом масштабе
|
||
static float interpolate_log_linear(float resistance, int index, const ntc_table_entry* table) {
|
||
float t1 = (float) table[index].temp_c;
|
||
float t2 = (float) table[index + 1].temp_c;
|
||
float r1 = table[index].r_nom;
|
||
float r2 = table[index + 1].r_nom;
|
||
|
||
float log_r1 = logf(r1);
|
||
float log_r2 = logf(r2);
|
||
float log_r = logf(resistance);
|
||
|
||
return t1 + (t2 - t1) * (log_r - log_r1) / (log_r2 - log_r1);
|
||
}
|
||
|
||
// Более надежная версия с проверкой параметров
|
||
static float interpolate_steinhart_full(float resistance, int index, const ntc_table_entry* table) {
|
||
// Проверка корректности входных данных
|
||
if (resistance <= 0.0f) {
|
||
return -273.15f; // Абсолютный ноль при некорректном сопротивлении
|
||
}
|
||
|
||
// Для повышения точности можно использовать таблицу как справочную,
|
||
// но основное вычисление - по уравнению с коэффициентами
|
||
double L = logf(resistance);
|
||
|
||
// Для термисторов NTC коэффициент C обычно очень маленький (порядка 1e-7...1e-8)
|
||
// Убедимся, что кубический член вычислен корректно
|
||
double L3 = L * L * L;
|
||
double inv_T = koef_A + koef_B * L + koef_C * L3;
|
||
|
||
// Проверка на физическую реализуемость (температура должна быть положительной в Кельвинах)
|
||
if (inv_T <= 0.0f || inv_T > 1.0f) { // 1/T не может быть <= 0 (T < 0K) или слишком большим
|
||
// В случае ошибки возвращаем температуру из таблицы по индексу
|
||
return (float) table[index].temp_c;
|
||
}
|
||
|
||
double temp_K = 1.0f / inv_T;
|
||
|
||
// Дополнительная проверка диапазона (например, для NTC обычно -50...+150°C)
|
||
if (temp_K < 223.15f || temp_K > 423.15f) { // -50°C...150°C в Кельвинах
|
||
// Возвращаем значение из таблицы как запасной вариант
|
||
return (float) table[index].temp_c;
|
||
}
|
||
|
||
return (float) (temp_K - 273.15f);
|
||
}
|
||
|
||
// Основная функция для получения температуры с указанием таблицы и R1
|
||
float get_temperature_from_adc_with_table(uint16_t adc_value, eAlg alg, eNtcTable table_type, float r1) {
|
||
float resistance = calculate_resistance(adc_value, r1);
|
||
|
||
if (resistance <= 0.0f || isinf(resistance)) {
|
||
return -273.15f; // Ошибка
|
||
}
|
||
|
||
uint16_t table_size;
|
||
int16_t start_temp, end_temp;
|
||
const ntc_table_entry* table = get_table_by_type(table_type, &table_size, &start_temp, &end_temp);
|
||
|
||
int index = find_interval_index(resistance, table, table_size);
|
||
|
||
if (index < 0 || index >= table_size - 1) {
|
||
// Вне диапазона таблицы
|
||
if (resistance >= table[0].r_nom) return (float) start_temp;
|
||
if (resistance <= table[table_size - 1].r_nom) return (float) end_temp;
|
||
return -273.15f; // Ошибка
|
||
}
|
||
|
||
if (alg == ALG_STEINHART) {
|
||
return interpolate_steinhart(resistance, index, table);
|
||
} else if (alg == ALG_STEINHART_FULL) {
|
||
return interpolate_steinhart_full(resistance, index, table);
|
||
} else {
|
||
return interpolate_log_linear(resistance, index, table);
|
||
}
|
||
}
|
||
|
||
// Функция для получения температуры с активной конфигурацией
|
||
float get_temperature_from_adc(uint16_t adc_value, eAlg alg) {
|
||
return get_temperature_from_adc_with_table(adc_value, alg, active_config.table_type, active_config.r1);
|
||
}
|
||
|
||
// Функция для получения сопротивления из значения АЦП с указанием таблицы
|
||
float get_resistance_from_adc_with_table(uint16_t adc_value, eNtcTable table_type, float r1) {
|
||
return calculate_resistance(adc_value, r1);
|
||
}
|
||
|
||
// Функция для получения сопротивления с активной конфигурацией
|
||
float get_resistance_from_adc(uint16_t adc_value) {
|
||
return calculate_resistance(adc_value, active_config.r1);
|
||
}
|
||
|
||
// Функция для получения напряжения из АЦП
|
||
uint16_t get_voltage_from_adc(uint16_t adc_value) {
|
||
return adc_to_voltage_mv(adc_value);
|
||
}
|
||
|
||
// Функция для получения точного напряжения на NTC
|
||
float get_ntc_voltage_fast(uint16_t adc_value) {
|
||
return (adc_value * VREF_MV) / ADC_MAX;
|
||
}
|
||
|
||
// Функция для получения напряжения на балластном резисторе
|
||
float get_r1_voltage_fast(uint16_t adc_value) {
|
||
return VCC_DIVIDER_MV - ((adc_value * VREF_MV) / ADC_MAX);
|
||
}
|
||
|
||
// Функция для получения напряжения из таблицы быстрого доступа
|
||
uint16_t get_voltage_fast_for_table(uint16_t adc_value, eNtcTable table_type) {
|
||
if (!is_table_initialized(table_type)) {
|
||
// Если таблица не инициализирована, вычисляем напрямую
|
||
return adc_to_voltage_mv(adc_value);
|
||
}
|
||
|
||
adc_temp_lookup* fast_table = get_fast_table_by_type(table_type);
|
||
|
||
// Поиск ближайшего значения в таблице
|
||
for (uint16_t i = 0; i < TABLE_SIZE_LOOKUP; i++) {
|
||
if (fast_table[i].adc_value == adc_value) {
|
||
return fast_table[i].voltage_mv;
|
||
}
|
||
|
||
if (i < TABLE_SIZE_LOOKUP - 1 &&
|
||
adc_value > fast_table[i].adc_value &&
|
||
adc_value < fast_table[i + 1].adc_value) {
|
||
// Линейная интерполяция напряжения
|
||
uint16_t adc1 = fast_table[i].adc_value;
|
||
uint16_t adc2 = fast_table[i + 1].adc_value;
|
||
uint16_t v1 = fast_table[i].voltage_mv;
|
||
uint16_t v2 = fast_table[i + 1].voltage_mv;
|
||
|
||
if (adc2 == adc1) return v1;
|
||
|
||
return v1 + (uint16_t)(((uint32_t)(v2 - v1) * (adc_value - adc1)) / (adc2 - adc1));
|
||
}
|
||
}
|
||
|
||
// Если не нашли, возвращаем ближайшее значение
|
||
if (adc_value <= fast_table[0].adc_value) {
|
||
return fast_table[0].voltage_mv;
|
||
} else {
|
||
return fast_table[TABLE_SIZE_LOOKUP - 1].voltage_mv;
|
||
}
|
||
}
|
||
|
||
void init_fast_lookup_table(eNtcTable table_type, float r1, eAlg use_alg) {
|
||
uint16_t table_size;
|
||
int16_t start_temp, end_temp;
|
||
const ntc_table_entry* table = get_table_by_type(table_type, &table_size, &start_temp, &end_temp);
|
||
adc_temp_lookup* fast_table = get_fast_table_by_type(table_type);
|
||
|
||
// Сохраняем значение R1 для этой таблицы
|
||
if (table_type == TABLE_DUCT) {
|
||
g_fast_tables.duct_r1 = r1;
|
||
} else if (table_type == TABLE_INCAR) {
|
||
g_fast_tables.incar_r1 = r1;
|
||
} else {
|
||
g_fast_tables.ambient_r1 = r1;
|
||
}
|
||
|
||
// Находим рабочий диапазон ADC
|
||
uint16_t min_valid_adc = 0;
|
||
uint16_t max_valid_adc = (uint16_t)(ADC_MAX - 1);
|
||
float max_resistance = table[0].r_nom; // Максимальное сопротивление (мин. температура)
|
||
float min_resistance = table[table_size - 1].r_nom; // Минимальное сопротивление (макс. температура)
|
||
|
||
// Ищем минимальное ADC, при котором сопротивление <= max_resistance
|
||
for (uint16_t adc = 1; adc < ADC_MAX; adc++) {
|
||
float resistance = calculate_resistance(adc, r1);
|
||
if (resistance <= max_resistance && !isinf(resistance)) {
|
||
min_valid_adc = adc;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Ищем максимальное ADC, при котором сопротивление >= min_resistance
|
||
for (uint16_t adc = (uint16_t)(ADC_MAX - 1); adc > 0; adc--) {
|
||
float resistance = calculate_resistance(adc, r1);
|
||
if (resistance >= min_resistance && resistance < INFINITY) {
|
||
max_valid_adc = adc;
|
||
break;
|
||
}
|
||
}
|
||
|
||
printf("DEBUG: %s - min_valid_adc=%u, max_valid_adc=%u\n",
|
||
table_type == TABLE_DUCT ? "DUCT" : (table_type == TABLE_INCAR ? "INCAR" : "AMBIENT"),
|
||
min_valid_adc, max_valid_adc);
|
||
|
||
// Заполняем таблицу
|
||
uint32_t adc_step = ((uint32_t)(ADC_MAX - 1) * 1000) / (TABLE_SIZE_LOOKUP - 1);
|
||
uint32_t current_adc = 0;
|
||
uint16_t prev_adc = 0;
|
||
|
||
for (uint16_t i = 0; i < TABLE_SIZE_LOOKUP; i++) {
|
||
uint16_t adc;
|
||
float resistance;
|
||
float temp;
|
||
|
||
// Определяем ADC для текущей записи
|
||
if (i == 0) {
|
||
adc = 0;
|
||
} else if (i == TABLE_SIZE_LOOKUP - 1) {
|
||
adc = (uint16_t)(ADC_MAX - 1);
|
||
} else {
|
||
current_adc += adc_step;
|
||
adc = (uint16_t)(current_adc / 1000);
|
||
if (adc <= prev_adc) adc = prev_adc + 1;
|
||
if (adc >= (uint16_t)(ADC_MAX - 1)) adc = (uint16_t)(ADC_MAX - 2);
|
||
}
|
||
|
||
// Расчет сопротивления и температуры в зависимости от зоны
|
||
if (adc <= min_valid_adc) {
|
||
// Зона низких ADC (высокая температура) - насыщение
|
||
resistance = min_resistance;
|
||
temp = (float)end_temp; // Максимальная температура (+85°C)
|
||
}
|
||
else if (adc >= max_valid_adc) {
|
||
// Зона высоких ADC (низкая температура) - насыщение
|
||
resistance = max_resistance;
|
||
temp = (float)start_temp; // Минимальная температура (-40°C)
|
||
}
|
||
else {
|
||
// Рабочая зона - нормальный расчет
|
||
resistance = calculate_resistance(adc, r1);
|
||
|
||
if (isinf(resistance) || resistance <= 0.0f) {
|
||
temp = (float)start_temp - 10.0f;
|
||
} else {
|
||
int index = find_interval_index(resistance, table, table_size);
|
||
|
||
if (index < 0 || index >= table_size - 1) {
|
||
if (resistance >= table[0].r_nom) {
|
||
temp = (float)start_temp;
|
||
} else if (resistance <= table[table_size - 1].r_nom) {
|
||
temp = (float)end_temp;
|
||
} else {
|
||
temp = 25.0f;
|
||
}
|
||
} else {
|
||
if (use_alg == ALG_STEINHART) {
|
||
temp = interpolate_steinhart(resistance, index, table);
|
||
} else if (use_alg == ALG_STEINHART_FULL) {
|
||
temp = interpolate_steinhart_full(resistance, index, table);
|
||
} else {
|
||
temp = interpolate_log_linear(resistance, index, table);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Заполняем запись
|
||
fast_table[i].adc_value = adc;
|
||
fast_table[i].temp_c = (int16_t)(temp * 10.0f);
|
||
fast_table[i].resistance_ohm = resistance;
|
||
fast_table[i].voltage_mv = adc_to_voltage_mv(adc);
|
||
fast_table[i].v_ntc_mv = (adc * VREF_MV) / ADC_MAX;
|
||
fast_table[i].v_r1_mv = VCC_DIVIDER_MV - fast_table[i].v_ntc_mv;
|
||
fast_table[i].table_type = table_type;
|
||
|
||
prev_adc = adc;
|
||
}
|
||
|
||
// Устанавливаем флаг инициализации
|
||
if (table_type == TABLE_DUCT) {
|
||
g_fast_tables.duct_initialized = true;
|
||
} else if (table_type == TABLE_INCAR) {
|
||
g_fast_tables.incar_initialized = true;
|
||
} else {
|
||
g_fast_tables.ambient_initialized = true;
|
||
}
|
||
}
|
||
|
||
// Инициализация таблицы Duct
|
||
void init_duct_table(float r1, eAlg use_alg) {
|
||
init_fast_lookup_table(TABLE_DUCT, r1, use_alg);
|
||
}
|
||
|
||
// Инициализация таблицы Incar
|
||
void init_incar_table(float r1, eAlg use_alg) {
|
||
init_fast_lookup_table(TABLE_INCAR, r1, use_alg);
|
||
}
|
||
|
||
// Инициализация таблицы Ambient
|
||
void init_ambient_table(float r1, eAlg use_alg) {
|
||
init_fast_lookup_table(TABLE_AMBIENT, r1, use_alg);
|
||
}
|
||
|
||
// Инициализация всех трех таблиц
|
||
void init_all_tables(float r1_duct, float r1_incar, float r1_ambient, eAlg use_alg) {
|
||
init_fast_lookup_table(TABLE_DUCT, r1_duct, use_alg);
|
||
init_fast_lookup_table(TABLE_INCAR, r1_incar, use_alg);
|
||
init_fast_lookup_table(TABLE_AMBIENT, r1_ambient, use_alg);
|
||
}
|
||
|
||
// Установка активной конфигурации
|
||
void set_active_config(eNtcTable table_type, float r1) {
|
||
active_config.table_type = table_type;
|
||
active_config.r1 = r1;
|
||
active_config.vcc_mv = VCC_DIVIDER_MV;
|
||
active_config.vref_mv = VREF_MV;
|
||
|
||
uint16_t table_size;
|
||
get_table_by_type(table_type, &table_size, &active_config.start_temp, &active_config.end_temp);
|
||
active_config.table_size = table_size;
|
||
}
|
||
|
||
// Получение активной конфигурации
|
||
const ntc_config_t* get_active_config(void) {
|
||
return &active_config;
|
||
}
|
||
|
||
// Получение таблиц быстрого доступа
|
||
const fast_lookup_tables_t* get_fast_tables(void) {
|
||
return &g_fast_tables;
|
||
}
|
||
|
||
// Функция для получения информации о конфигурации VCC/VREF
|
||
void get_vcc_vref_info(float* vcc_mv, float* vref_mv) {
|
||
if (vcc_mv) *vcc_mv = VCC_DIVIDER_MV;
|
||
if (vref_mv) *vref_mv = VREF_MV;
|
||
}
|
||
|
||
float get_vcc_divider_voltage(void) {
|
||
return VCC_DIVIDER_MV;
|
||
}
|
||
|
||
float get_vref_voltage(void) {
|
||
return VREF_MV;
|
||
}
|
||
|
||
bool is_vcc_equal_to_vref(void) {
|
||
return (VCC_DIVIDER_MV_INT == VREF_MV_INT);
|
||
}
|
||
|
||
// Быстрая функция для конкретной таблицы
|
||
int16_t get_temperature_log_fast_for_table(uint16_t adc_value, eNtcTable table_type) {
|
||
if (!is_table_initialized(table_type)) {
|
||
// Если таблица не инициализирована, используем обычный расчет
|
||
float r1 = (table_type == TABLE_DUCT) ? g_fast_tables.duct_r1 :
|
||
((table_type == TABLE_INCAR) ? g_fast_tables.incar_r1 : g_fast_tables.ambient_r1);
|
||
float temp = get_temperature_from_adc_with_table(adc_value, ALG_STEINHART, table_type, r1);
|
||
return (int16_t)(temp * 10.0f);
|
||
}
|
||
|
||
adc_temp_lookup* fast_table = get_fast_table_by_type(table_type);
|
||
|
||
// Простой линейный поиск (так как таблица небольшая - 4096 элементов)
|
||
for (uint16_t i = 0; i < TABLE_SIZE_LOOKUP - 1; i++) {
|
||
if (adc_value >= fast_table[i].adc_value && adc_value <= fast_table[i + 1].adc_value) {
|
||
// Линейная интерполяция
|
||
uint16_t adc1 = fast_table[i].adc_value;
|
||
uint16_t adc2 = fast_table[i + 1].adc_value;
|
||
int16_t temp1 = fast_table[i].temp_c;
|
||
int16_t temp2 = fast_table[i + 1].temp_c;
|
||
|
||
if (adc2 == adc1) {
|
||
return temp1;
|
||
}
|
||
|
||
return temp1 + ((int32_t)(temp2 - temp1) * (adc_value - adc1)) / (adc2 - adc1);
|
||
}
|
||
}
|
||
|
||
// Если не нашли, возвращаем ближайшее значение
|
||
if (adc_value <= fast_table[0].adc_value) {
|
||
return fast_table[0].temp_c;
|
||
} else {
|
||
return fast_table[TABLE_SIZE_LOOKUP - 1].temp_c;
|
||
}
|
||
}
|
||
|
||
// Быстрые функции с использованием активной таблицы
|
||
int16_t get_temperature_log_fast(uint16_t adc_value) {
|
||
return get_temperature_log_fast_for_table(adc_value, active_config.table_type);
|
||
}
|
||
|
||
// Альтернативная простая версия
|
||
int16_t get_temperature_linear_fast(uint16_t adc_value) {
|
||
return get_temperature_log_fast_for_table(adc_value, active_config.table_type);
|
||
}
|
||
|
||
// Функция для получения сопротивления из температуры для конкретной таблицы
|
||
float get_resistance_log_fast_for_table(int16_t temperature_c10, eNtcTable table_type) {
|
||
if (!is_table_initialized(table_type)) {
|
||
return 0.0f;
|
||
}
|
||
|
||
adc_temp_lookup* fast_table = get_fast_table_by_type(table_type);
|
||
|
||
// Защита от выхода за границы
|
||
if (temperature_c10 >= fast_table[0].temp_c) {
|
||
return fast_table[0].resistance_ohm;
|
||
}
|
||
if (temperature_c10 <= fast_table[TABLE_SIZE_LOOKUP - 1].temp_c) {
|
||
return fast_table[TABLE_SIZE_LOOKUP - 1].resistance_ohm;
|
||
}
|
||
|
||
// Бинарный поиск интервала по температуре
|
||
int left = 0;
|
||
int right = TABLE_SIZE_LOOKUP - 1;
|
||
|
||
while (left <= right) {
|
||
int mid = left + (right - left) / 2;
|
||
|
||
if (mid < TABLE_SIZE_LOOKUP - 1 &&
|
||
temperature_c10 <= fast_table[mid].temp_c &&
|
||
temperature_c10 >= fast_table[mid + 1].temp_c) {
|
||
// Нашли интервал
|
||
int16_t temp_high = fast_table[mid].temp_c;
|
||
int16_t temp_low = fast_table[mid + 1].temp_c;
|
||
float res_high = fast_table[mid].resistance_ohm;
|
||
float res_low = fast_table[mid + 1].resistance_ohm;
|
||
|
||
if (temp_high == temp_low) {
|
||
return res_high;
|
||
}
|
||
|
||
// Интерполяция в логарифмическом масштабе
|
||
float log_res_high = logf(res_high);
|
||
float log_res_low = logf(res_low);
|
||
float log_res = log_res_high + (log_res_low - log_res_high) *
|
||
(float)(temp_high - temperature_c10) / (float)(temp_high - temp_low);
|
||
|
||
return expf(log_res);
|
||
}
|
||
|
||
if (temperature_c10 > fast_table[mid].temp_c) {
|
||
right = mid - 1;
|
||
} else {
|
||
left = mid + 1;
|
||
}
|
||
}
|
||
|
||
return fast_table[TABLE_SIZE_LOOKUP - 1].resistance_ohm;
|
||
}
|
||
|
||
// Функция для получения сопротивления из температуры (обратное преобразование)
|
||
float get_resistance_log_fast(int16_t temperature_c10) {
|
||
return get_resistance_log_fast_for_table(temperature_c10, active_config.table_type);
|
||
}
|
||
|
||
// Упрощенная версия с линейным поиском
|
||
float get_resistance_fast_simple(int16_t temperature_c10) {
|
||
return get_resistance_log_fast_for_table(temperature_c10, active_config.table_type);
|
||
} |