// // Created by cfif on 02.12.2025. // #include "stdint.h" #include "ADC_Temp_KST45-14-2.h" #include // Константы #define ADC_MAX 4095.0f // 12-битный АЦП #define R1 3000.0f // Сопротивление делителя напряжения #define TABLE_START_TEMP (-40) #define TABLE_END_TEMP 85 #define TABLE_SIZE 26 // Параметры Steinhart-Hart для термистора #define koef_A 0.001741624168166423 #define koef_B 0.00017003940268680147 #define koef_C 0.0000004890545443703666 #define TABLE_SIZE_LOOKUP 32 // Структура для хранения табличных данных typedef struct { int temp_c; // Температура (°C) float r_nom; // Номинальное сопротивление (Ω) } ntc_table_entry; // Таблица из документа static const ntc_table_entry ntc_table[] = { {-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} }; // Функция расчёта сопротивления NTC из значения АЦП static float calculate_resistance(uint16_t adc_value) { if (adc_value == 0 || adc_value >= (uint16_t) ADC_MAX) { return 0.0f; } // Формула делителя напряжения: R_ntc = R1 * (ADC_MAX / adc_value - 1) // float R_ntc = R1 * (ADC_MAX / (float)adc_value - 1.0f); float R_ntc = R1 * (float) adc_value / (ADC_MAX - (float) adc_value); return R_ntc; } // Бинарный поиск в таблице static int find_interval_index(float resistance) { int left = 0; int right = TABLE_SIZE - 1; // Проверка границ if (resistance >= ntc_table[0].r_nom) return 0; if (resistance <= ntc_table[right].r_nom) return right - 1; // Бинарный поиск while (left <= right) { int mid = left + (right - left) / 2; if (mid < TABLE_SIZE - 1 && resistance <= ntc_table[mid].r_nom && resistance >= ntc_table[mid + 1].r_nom) { return mid; } if (resistance > ntc_table[mid].r_nom) { right = mid - 1; } else { left = mid + 1; } } return -1; // Не найден } // Интерполяция с использованием уравнения Стейнхарта-Харта между точками static float interpolate_steinhart(float resistance, int index) { // Берем две соседние точки из таблицы float t1 = (float) ntc_table[index].temp_c + 273.15f; // в Кельвинах float t2 = (float) ntc_table[index + 1].temp_c + 273.15f; float r1 = ntc_table[index].r_nom; float r2 = ntc_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) { float t1 = (float) ntc_table[index].temp_c; float t2 = (float) ntc_table[index + 1].temp_c; float r1 = ntc_table[index].r_nom; float r2 = ntc_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) { // Проверка корректности входных данных 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)ntc_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)ntc_table[index].temp_c; } return (float)(temp_K - 273.15f); } // Основная функция для получения температуры float get_temperature_from_adc_KST45(uint16_t adc_value, eAlg use_alg) { float resistance = calculate_resistance(adc_value); if (resistance <= 0.0f) { return -273.15f; // Ошибка } int index = find_interval_index(resistance); if (index < 0 || index >= TABLE_SIZE - 1) { // Вне диапазона таблицы if (resistance >= ntc_table[0].r_nom) return TABLE_START_TEMP; if (resistance <= ntc_table[TABLE_SIZE - 1].r_nom) return TABLE_END_TEMP; return -273.15f; // Ошибка } if (use_alg == ALG_STEINHART) { return interpolate_steinhart(resistance, index); } else if (use_alg == ALG_STEINHART_FULL) { return interpolate_steinhart_full(resistance, index); } else { return interpolate_log_linear(resistance, index); } } // Предварительно вычисленная таблица для быстрого доступа typedef struct { uint16_t adc_value; // Значение АЦП int16_t temp_c; // Температура в °C * 10 (для фиксированной точки) } adc_temp_lookup; static adc_temp_lookup fast_lookup[TABLE_SIZE_LOOKUP]; // Таблица на TABLE_SIZE_LOOKUP значений void init_fast_lookup_table(eAlg use_alg) { // Создаем таблицу для быстрого преобразования АЦП->температура for (uint16_t i = 0; i < TABLE_SIZE_LOOKUP; i++) { uint16_t adc = i * (uint8_t)roundf(ADC_MAX / (TABLE_SIZE_LOOKUP - 1)); // Для 12-битного АЦП (0-4095) float temp = get_temperature_from_adc_KST45(adc, use_alg); fast_lookup[i].adc_value = adc; fast_lookup[i].temp_c = (int16_t) (temp * 10.0f); // Храним с точностью 0.1°C } } int16_t get_temperature_fast_KST45(uint16_t adc_value) { // Простой поиск в таблице с линейной интерполяцией uint16_t index = adc_value / (uint8_t)roundf(ADC_MAX / (TABLE_SIZE_LOOKUP - 1)); // Делим на 16 для TABLE_SIZE_LOOKUP = 256 if (index >= (TABLE_SIZE_LOOKUP - 1)) return fast_lookup[TABLE_SIZE_LOOKUP - 1].temp_c; uint16_t adc1 = fast_lookup[index].adc_value; uint16_t adc2 = fast_lookup[index + 1].adc_value; int16_t temp1 = fast_lookup[index].temp_c; int16_t temp2 = fast_lookup[index + 1].temp_c; // Линейная интерполяция return temp1 + ((temp2 - temp1) * (adc_value - adc1)) / (adc2 - adc1); }