221 lines
8.6 KiB
C
221 lines
8.6 KiB
C
//
|
||
// Created by cfif on 02.12.2025.
|
||
//
|
||
#include "stdint.h"
|
||
#include <math.h>
|
||
|
||
// Константы
|
||
#define ADC_MAX 4095.0f // 12-битный АЦП
|
||
#define R1 3000.0f // Сопротивление делителя напряжения
|
||
#define TABLE_START_TEMP (-40)
|
||
#define TABLE_END_TEMP 85
|
||
#define TABLE_SIZE 26
|
||
|
||
// Структура для хранения табличных данных
|
||
typedef struct {
|
||
int temp_c; // Температура (°C)
|
||
float r_nom; // Номинальное сопротивление (Ω)
|
||
float r_min; // Минимальное сопротивление (Ω)
|
||
float r_max; // Максимальное сопротивление (Ω)
|
||
} ntc_table_entry;
|
||
|
||
// Таблица из документа
|
||
static const ntc_table_entry ntc_table[] = {
|
||
{-40, 100950.0f, 98626.0f, 103274.0f},
|
||
{-35, 72777.0f, 71232.0f, 74322.0f},
|
||
{-30, 53100.0f, 52064.0f, 54136.0f},
|
||
{-25, 39111.0f, 38413.0f, 39809.0f},
|
||
{-20, 29121.0f, 28647.0f, 29595.0f},
|
||
{-15, 21879.0f, 21556.0f, 22201.0f},
|
||
{-10, 16599.0f, 16379.0f, 16819.0f},
|
||
{-5, 12695.0f, 12544.0f, 12845.0f},
|
||
{0, 9795.0f, 9697.0f, 9893.0f},
|
||
{5, 7616.0f, 7526.0f, 7706.0f},
|
||
{10, 5970.0f, 5892.0f, 6048.0f},
|
||
{15, 4712.0f, 4645.0f, 4780.0f},
|
||
{20, 3747.0f, 3689.0f, 3805.0f},
|
||
{25, 3000.0f, 2950.0f, 3050.0f},
|
||
{30, 2417.0f, 2374.0f, 2460.0f},
|
||
{35, 1959.0f, 1923.0f, 1996.0f},
|
||
{40, 1598.0f, 1566.0f, 1630.0f},
|
||
{45, 1311.0f, 1283.0f, 1338.0f},
|
||
{50, 1081.0f, 1057.0f, 1104.0f},
|
||
{55, 895.9f, 875.5f, 916.2f},
|
||
{60, 746.4f, 728.7f, 764.1f},
|
||
{65, 624.9f, 609.6f, 640.2f},
|
||
{70, 525.6f, 512.3f, 538.9f},
|
||
{75, 444.4f, 432.8f, 456.1f},
|
||
{80, 377.4f, 367.2f, 387.6f},
|
||
{85, 321.7f, 312.8f, 330.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);
|
||
}
|
||
|
||
// Основная функция для получения температуры
|
||
float get_temperature_from_adc_KST45(uint16_t adc_value, int use_steinhart) {
|
||
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_steinhart) {
|
||
return interpolate_steinhart(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[256]; // Таблица на 256 значений
|
||
|
||
static void init_fast_lookup_table(void) {
|
||
// Создаем таблицу для быстрого преобразования АЦП->температура
|
||
for (int i = 0; i < 256; i++) {
|
||
uint16_t adc = i * 16; // Для 12-битного АЦП (0-4095)
|
||
float temp = get_temperature_from_adc_KST45(adc, 0);
|
||
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) {
|
||
// Простой поиск в таблице с линейной интерполяцией
|
||
uint8_t index = adc_value >> 4; // Делим на 16
|
||
if (index >= 255) return fast_lookup[255].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);
|
||
}
|
||
|
||
|
||
// Константы для датчика
|
||
#define R25 3000.0f // Сопротивление при 25°C
|
||
#define B 3930.0f // Коэффициент B25/50
|
||
#define T25 298.15f // 25°C в Кельвинах
|
||
|
||
// Функция расчёта температуры из сопротивления
|
||
static float calculate_temperature(float resistance) {
|
||
|
||
if (resistance <= 0) return -273.15f; // Абсолютный ноль
|
||
|
||
// Расчёт по уравнению Стейнхарта-Харта
|
||
float t_kelvin = 1.0f / (1.0f / T25 + (1.0f / B) * logf(resistance / R25));
|
||
|
||
return t_kelvin - 273.15f;
|
||
}
|
||
|
||
// Прямой расчёт температуры из значения АЦП
|
||
float calculate_temperature_direct_KST45(uint16_t adc_value) {
|
||
float resistance = calculate_resistance(adc_value);
|
||
return calculate_temperature(resistance);
|
||
}
|
||
|
||
// сигнал АЦП, (R резистора / R термистора), B термистора, t термистора, разрешение АЦП
|
||
static float NTC_computeRR(float analog, float baseDiv, uint16_t BB, uint8_t t, uint8_t res) {
|
||
if (analog <= 0 || isnan(analog)) return INFINITY;
|
||
analog = baseDiv / ((float) ((1 << res) - 1) / analog - 1.0f);
|
||
analog = (logf(analog) / (float)BB) + 1.0f / ((float)t + 273.15f);
|
||
return (1.0f / analog - 273.15f);
|
||
}
|
||
|
||
// сигнал АЦП, R резистора, B термистора, t термистора, R термистора, разрешение АЦП
|
||
float NTC_compute_KST45(float analog, uint32_t R, uint16_t BB, uint8_t t, uint32_t Rt, uint8_t res) {
|
||
return NTC_computeRR(analog, (float) R / (float)Rt, BB, t, res);
|
||
}
|
||
|
||
void zz_AAS_KST45() {
|
||
NTC_compute_KST45(0, R25, B, 25, (uint32_t)R25, 12);
|
||
} |