commit 10ee522b9bfd5ce8b4769d931d5d51c24d469d59 Author: cfif Date: Thu Dec 4 10:43:20 2025 +0300 Init diff --git a/APP/inc/ADC_Temp_KST45-14-2.h b/APP/inc/ADC_Temp_KST45-14-2.h new file mode 100644 index 0000000..c470a39 --- /dev/null +++ b/APP/inc/ADC_Temp_KST45-14-2.h @@ -0,0 +1,20 @@ +// +// Created by cfif on 04.12.2025. +// + +#ifndef MDF_ADC_TEMP_KST45_14_2_H +#define MDF_ADC_TEMP_KST45_14_2_H + +#include + +typedef enum { + ALG_STEINHART = 0, + ALG_STEINHART_FULL = 1, + ALG_LINEAR = 2 +} eAlg; + +void init_fast_lookup_table(eAlg use_alg); +float get_temperature_from_adc_KST45(uint16_t adc_value, eAlg alg); +int16_t get_temperature_fast_KST45(uint16_t adc_value); + +#endif //MDF_ADC_TEMP_KST45_14_2_H diff --git a/APP/inc/aterlux.h b/APP/inc/aterlux.h new file mode 100644 index 0000000..4fb0973 --- /dev/null +++ b/APP/inc/aterlux.h @@ -0,0 +1,12 @@ +// +// Created by cfif on 04.12.2025. +// + +#ifndef MDF_ATERLUX_H +#define MDF_ATERLUX_H + +#include + +int16_t calc_temperature(uint16_t adcsum); + +#endif //MDF_ATERLUX_H diff --git a/APP/main.c b/APP/main.c new file mode 100644 index 0000000..b26be13 --- /dev/null +++ b/APP/main.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include +#include "ADC_Temp_KST45-14-2.h" +#include "aterlux.h" + +int main() { + + init_fast_lookup_table(ALG_STEINHART); + uint16_t value = 4095 / 2; // Должно быть 25 градусов. Это середина диапазона АЦП + + float T_ALG_LINEAR = get_temperature_from_adc_KST45(value, ALG_LINEAR); + float T_ALG_STEINHART = get_temperature_from_adc_KST45(value, ALG_STEINHART); + float T_ALG_STEINHART_FULL = get_temperature_from_adc_KST45(value, ALG_STEINHART_FULL); + float T_FAST = get_temperature_fast_KST45(value); + float T_ATERLUX = calc_temperature(value); + + printf("T_ALG_LINEAR = %f \n", T_ALG_LINEAR); + printf("T_ALG_STEINHART = %f \n", T_ALG_STEINHART); + printf("T_ALG_STEINHART_FULL = %f \n", T_ALG_STEINHART_FULL); + + printf("T_FAST = %f \n", T_FAST / 10); + printf("T_ATERLUX = %f \n", T_ATERLUX / 10); + + + return 0; +} \ No newline at end of file diff --git a/APP/modular.json b/APP/modular.json new file mode 100644 index 0000000..2573066 --- /dev/null +++ b/APP/modular.json @@ -0,0 +1,13 @@ +{ + + "cmake": { + "inc_dirs": [ + "./inc/" + ], + "srcs": [ + "./main.c", + "./src/*.c", + "./src/*.c" + ] + } +} \ No newline at end of file diff --git a/APP/src/ADC_Temp_KST45-14-2.c b/APP/src/ADC_Temp_KST45-14-2.c new file mode 100644 index 0000000..64a757a --- /dev/null +++ b/APP/src/ADC_Temp_KST45-14-2.c @@ -0,0 +1,226 @@ +// +// 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(4095.0f / (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(4095.0f / (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); +} diff --git a/APP/src/aterlux.c b/APP/src/aterlux.c new file mode 100644 index 0000000..a039c4d --- /dev/null +++ b/APP/src/aterlux.c @@ -0,0 +1,76 @@ +// +// Created by cfif on 03.12.2025. +// +#include +#include + +// Значение температуры, возвращаемое если сумма результатов АЦП больше первого значения таблицы +#define TEMPERATURE_UNDER (-400) +// Значение температуры, возвращаемое если сумма результатов АЦП меньше последнего значения таблицы +#define TEMPERATURE_OVER 800 +// Значение температуры соответствующее первому значению таблицы +#define TEMPERATURE_TABLE_START (-400) +// Шаг таблицы +#define TEMPERATURE_TABLE_STEP 50 + +/* Таблица суммарного значения АЦП в зависимости от температуры. От большего значения к меньшему + Для построения таблицы использованы следующие параметры: + R1(T1): 3кОм(25°С) + Схема включения: A + Ra: 3кОм + Напряжения U0/Uref: 5В/5В +*/ +const uint16_t termo_table[] = { + 3978, 3934, 3877, 3804, 3713, 3602, 3469, 3313, + 3136, 2939, 2726, 2503, 2275, 2048, 1828, 1618, + 1424, 1246, 1085, 942, 816, 706, 611, 528, + 458, 397 +}; + +// Функция вычисляет значение температуры в десятых долях градусов Цельсия +// в зависимости от суммарного значения АЦП. +int16_t calc_temperature(uint16_t adcsum) { + uint8_t l = 0; + uint8_t r = (sizeof(termo_table) / sizeof(termo_table[0])) - 1; + uint16_t thigh = termo_table[r]; + + // Проверка выхода за пределы и граничных значений + if (adcsum <= thigh) { +#ifdef TEMPERATURE_UNDER + if (adcsum < thigh) + return TEMPERATURE_UNDER; +#endif + return TEMPERATURE_TABLE_STEP * r + TEMPERATURE_TABLE_START; + } + uint16_t tlow = termo_table[0]; + if (adcsum >= tlow) { +#ifdef TEMPERATURE_OVER + if (adcsum > tlow) + return TEMPERATURE_OVER; +#endif + return TEMPERATURE_TABLE_START; + } + + // Двоичный поиск по таблице + while ((r - l) > 1) { + uint8_t m = (l + r) >> 1; + uint16_t mid = termo_table[m]; + if (adcsum > mid) { + r = m; + } else { + l = m; + } + } + uint16_t vl = termo_table[l]; + if (adcsum >= vl) { + return l * TEMPERATURE_TABLE_STEP + TEMPERATURE_TABLE_START; + } + uint16_t vr = termo_table[r]; + uint16_t vd = vl - vr; + int16_t res = TEMPERATURE_TABLE_START + r * TEMPERATURE_TABLE_STEP; + if (vd) { + // Линейная интерполяция + res -= ((TEMPERATURE_TABLE_STEP * (int32_t)(adcsum - vr) + (vd >> 1)) / vd); + } + return res; +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..15bc671 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.17) +project(MODEL_ADC) + +set(CMAKE_CXX_STANDARD 17) + +include(modular.cmake) + +#link_directories(/usr/local/opt/libpq/lib/) +#include_directories(/usr/local/opt/libpq/include) +#link_directories(/usr/lib/x86_64-linux-gnu/) +#include_directories(/usr/include/postgresql) + +link_libraries(m) +#link_libraries(pq) +#link_libraries(tcp) +#link_libraries(pthread) + +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -funsigned-char -pipe -Wl,-O0 -std=gnu++2a -Wall -Wextra") + +add_executable(mdf ${SOURCES}) + + + diff --git a/modular.cmake b/modular.cmake new file mode 100644 index 0000000..bc4b615 --- /dev/null +++ b/modular.cmake @@ -0,0 +1,12 @@ +function(add_sources FILE_LIST FILES_PATH) + file(GLOB_RECURSE ADD_FILES_LIST ${FILES_PATH}) + list(APPEND ${FILE_LIST} ${ADD_FILES_LIST}) + set(${FILE_LIST} ${${FILE_LIST}} PARENT_SCOPE) +endfunction() + +include_directories("APP/inc/") + +add_sources(SOURCES "APP/main.c") +add_sources(SOURCES "APP/src/*.c") +add_sources(SOURCES "APP/src/*.c") + diff --git a/modular.json b/modular.json new file mode 100644 index 0000000..6034ef1 --- /dev/null +++ b/modular.json @@ -0,0 +1,14 @@ +{ + "dep": [ + { + "type": "local", + "dir": "APP" + } + ], + "cmake": { + "inc_dirs": [ + ], + "srcs": [ + ] + } +} \ No newline at end of file