HVAC_M7_ADC_TEMP/ADC_Temp.c

200 lines
7.7 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// Created by cfif on 02.12.2025.
//
#include "stdint.h"
#include "ADC_Temp.h"
#include <math.h>
adc_temp_lookup fast_lookup[TABLE_SIZE_LOOKUP]; // Таблица на TABLE_SIZE_LOOKUP значений
// Таблица из документа KST45-14-2
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(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);
}
}
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(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(uint16_t adc_value, adc_temp_lookup *fast_temp_lookup, uint16_t size_fast_temp_lookup) {
// Простой поиск в таблице с линейной интерполяцией
uint16_t index = adc_value /
(uint8_t) roundf(ADC_MAX / ((float) size_fast_temp_lookup - 1)); // Делим на 16 для TABLE_SIZE_LOOKUP = 256
if (index >= (size_fast_temp_lookup - 1)) return fast_temp_lookup[size_fast_temp_lookup - 1].temp_c;
uint16_t adc1 = fast_temp_lookup[index].adc_value;
uint16_t adc2 = fast_temp_lookup[index + 1].adc_value;
int16_t temp1 = fast_temp_lookup[index].temp_c;
int16_t temp2 = fast_temp_lookup[index + 1].temp_c;
// Линейная интерполяция
return temp1 + ((temp2 - temp1) * (adc_value - adc1)) / (adc2 - adc1);
}