commit 35f07b60ab494bd4068f80d7882310bed851c0ec Author: cfif Date: Mon Mar 30 11:43:46 2026 +0300 Init diff --git a/APP/inc/ADC_Temp.h b/APP/inc/ADC_Temp.h new file mode 100644 index 0000000..7695932 --- /dev/null +++ b/APP/inc/ADC_Temp.h @@ -0,0 +1,58 @@ +// +// Created by cfif on 04.12.2025. +// + +#ifndef MDF_ADC_TEMP_KST45_14_2_H +#define MDF_ADC_TEMP_KST45_14_2_H + +#include + +// Константы +#define ADC_MAX 4095.0f // 12-битный АЦП + +// Типы термисторов +typedef enum { + NTC_TYPE_1 = 0, // Первая таблица (100950 Ом при -40°C) + NTC_TYPE_2 = 1 // Вторая таблица (101000 Ом при -40°C) +} eNTC_Type; + +// Параметры Steinhart-Hart для термисторов +typedef struct { + float A; + float B; + float C; +} SteinhartParams; + +// Структура для хранения табличных данных +typedef struct { + int temp_c; // Температура (°C) + float r_nom; // Номинальное сопротивление (Ω) +} ntc_table_entry; + +typedef enum { + ALG_STEINHART = 0, + ALG_STEINHART_FULL = 1, + ALG_LINEAR = 2 +} eAlg; + +// Предварительно вычисленная таблица для быстрого доступа +typedef struct { + uint16_t adc_value; // Значение АЦП + int16_t temp_c; // Температура в °C * 10 (для фиксированной точки) +} adc_temp_lookup; + +// Функции инициализации +void init_ntc(eNTC_Type ntc_type); +void set_r1(float r1_value); // Функция для установки сопротивления делителя +void init_fast_lookup_table(eAlg use_alg); + +// Основные функции +float get_temperature_from_adc(uint16_t adc_value, eAlg alg); +int16_t get_temperature_fast(uint16_t adc_value); + +// Функция для получения информации о текущем термисторе +const char* get_ntc_name(void); +uint16_t get_table_size(void); +float get_r1_value(void); // Функция для получения текущего R1 + +#endif //MDF_ADC_TEMP_KST45_14_2_H \ No newline at end of file diff --git a/APP/main.c b/APP/main.c new file mode 100644 index 0000000..31bb9f4 --- /dev/null +++ b/APP/main.c @@ -0,0 +1,133 @@ +#include +#include "ADC_Temp.h" + +void print_table_comparison(void) { + printf("\n=== Сравнение таблиц термисторов ===\n\n"); + + printf("Тестирование %s:\n", get_ntc_name()); + printf("Сопротивление делителя R1: %.1f Ом\n", get_r1_value()); + printf("Размер таблицы: %d точек\n\n", get_table_size()); + + printf("ADC\tСопротивление(Ом)\tLinear\tSteinhart\tFull\tFast\n"); + printf("---------------------------------------------------------------\n"); + + // Тестируем разные значения АЦП + uint16_t test_adc[] = {500, 1000, 1500, 2000, 2500, 3000, 3500, 4000}; + + for (int i = 0; i < 8; i++) { + uint16_t adc = test_adc[i]; + float resistance = get_r1_value() * adc / (4095.0f - adc); + float linear = get_temperature_from_adc(adc, ALG_LINEAR); + float steinhart = get_temperature_from_adc(adc, ALG_STEINHART); + float full = get_temperature_from_adc(adc, ALG_STEINHART_FULL); + float fast = get_temperature_fast(adc) / 10.0f; + + printf("%d\t%.1f\t\t%.2f\t%.2f\t%.2f\t%.2f\n", + adc, resistance, linear, steinhart, full, fast); + } +} + +void test_temperature_range(void) { + printf("\n=== Тестирование температурного диапазона ===\n\n"); + printf("Примечание: Для точного тестирования необходимы\n"); + printf("функции доступа к таблице термистора\n\n"); + + // Просто показываем несколько значений + uint16_t test_adc[] = {500, 1000, 1500, 2000, 2500, 3000, 3500, 4000}; + + printf("ADC\tРасчетная температура\n"); + printf("------------------------\n"); + + for (int i = 0; i < 8; i++) { + uint16_t adc = test_adc[i]; + float temp = get_temperature_from_adc(adc, ALG_STEINHART); + printf("%d\t%.2f°C\n", adc, temp); + } +} + +void compare_both_ntc_types(void) { + printf("\n=== Сравнение двух типов термисторов ===\n\n"); + printf("ADC\tNTC Type 1\tNTC Type 2\tРазница\n"); + printf("----------------------------------------\n"); + + uint16_t test_adc[] = {500, 1000, 1500, 2000, 2500, 3000, 3500, 4000}; + + for (int i = 0; i < 8; i++) { + uint16_t adc = test_adc[i]; + + // Тестируем первый тип + init_ntc(NTC_TYPE_1); + init_fast_lookup_table(ALG_STEINHART); + float temp1 = get_temperature_from_adc(adc, ALG_STEINHART); + + // Тестируем второй тип + init_ntc(NTC_TYPE_2); + init_fast_lookup_table(ALG_STEINHART); + float temp2 = get_temperature_from_adc(adc, ALG_STEINHART); + + printf("%d\t%.2f°C\t\t%.2f°C\t\t%.2f°C\n", + adc, temp1, temp2, (temp2 - temp1)); + } +} + +int main() { + printf("=== Программа для работы с NTC термисторами ===\n"); + printf("Автор: cfif\n"); + printf("Дата: 04.12.2025\n\n"); + + // Выбираем первый тип термистора для тестирования + printf("Инициализация термистора Type 1...\n"); + init_ntc(NTC_TYPE_1); + init_fast_lookup_table(ALG_STEINHART); + + // Выводим информацию + printf("Текущий термистор: %s\n", get_ntc_name()); + printf("Сопротивление делителя R1: %.1f Ом\n", get_r1_value()); + printf("Количество точек в таблице: %d\n\n", get_table_size()); + + // Тестируем различные значения АЦП + print_table_comparison(); + + // Тестируем температурный диапазон + test_temperature_range(); + + // Сравниваем оба типа термисторов + compare_both_ntc_types(); + + // Пример работы с конкретным значением АЦП + printf("\n=== Пример работы с конкретным значением АЦП ===\n"); + uint16_t test_value = 1980; + + init_ntc(NTC_TYPE_2); + init_fast_lookup_table(ALG_STEINHART); + + float T_ALG_LINEAR = get_temperature_from_adc(test_value, ALG_LINEAR); + float T_ALG_STEINHART = get_temperature_from_adc(test_value, ALG_STEINHART); + float T_ALG_STEINHART_FULL = get_temperature_from_adc(test_value, ALG_STEINHART_FULL); + float T_FAST = get_temperature_fast(test_value) / 10.0f; + + printf("Для ADC = %d:\n", test_value); + printf(" Линейная интерполяция: %.2f°C\n", T_ALG_LINEAR); + printf(" Steinhart-Hart: %.2f°C\n", T_ALG_STEINHART); + printf(" Steinhart-Hart полная: %.2f°C\n", T_ALG_STEINHART_FULL); + printf(" Быстрый метод: %.2f°C\n", T_FAST); + + // Пример изменения сопротивления делителя + printf("\n=== Пример изменения сопротивления делителя ===\n"); + printf("Текущий R1: %.1f Ом\n", get_r1_value()); + + set_r1(4700.0f); // Изменяем на 4.7 кОм + init_fast_lookup_table(ALG_STEINHART); + float new_temp = get_temperature_from_adc(test_value, ALG_STEINHART); + printf("После изменения R1 на %.1f Ом, температура: %.2f°C\n", + get_r1_value(), new_temp); + + set_r1(3300.0f); // Возвращаем обратно + init_fast_lookup_table(ALG_STEINHART); + float original_temp = get_temperature_from_adc(test_value, ALG_STEINHART); + printf("После возврата R1 на %.1f Ом, температура: %.2f°C\n", + get_r1_value(), original_temp); + + printf("\n=== Программа завершена ===\n"); + 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.c b/APP/src/ADC_Temp.c new file mode 100644 index 0000000..c9fda1d --- /dev/null +++ b/APP/src/ADC_Temp.c @@ -0,0 +1,347 @@ +// +// Created by cfif on 02.12.2025. +// +#include "ADC_Temp.h" +#include +#include + +// Таблица 1: Первый термистор (100950 Ом при -40°C) +static const ntc_table_entry ntc_table_type1[] = { + {-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} +}; + +#define TABLE1_SIZE (sizeof(ntc_table_type1) / sizeof(ntc_table_entry)) +#define TABLE1_START_TEMP -40 +#define TABLE1_END_TEMP 85 +#define TABLE1_R1 3300.0f // Сопротивление делителя для первого термистора + +// Таблица 2: Второй термистор (101000 Ом при -40°C) - KST-45 +static const ntc_table_entry ntc_table_type2[] = { + {-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} +}; + +#define TABLE2_SIZE (sizeof(ntc_table_type2) / sizeof(ntc_table_entry)) +#define TABLE2_START_TEMP -40 +#define TABLE2_END_TEMP 85 +#define TABLE2_R1 3300.0f // Сопротивление делителя для второго термистора (можно изменить при необходимости) + +// Параметры Steinhart-Hart для различных термисторов +static const SteinhartParams steinhart_type1 = { + 0.001752169f, // A + 0.000171234f, // B + 0.000000512345f // C (рассчитаны приблизительно) +}; + +static const SteinhartParams steinhart_type2 = { + 0.001741624168166423f, + 0.00017003940268680147f, + 0.0000004890545443703666f +}; + +// Структура для хранения информации о текущем термисторе +typedef struct { + eNTC_Type type; + const ntc_table_entry* table; + uint16_t table_size; + int start_temp; + int end_temp; + float r1; // Сопротивление делителя для данного термистора + SteinhartParams params; + const char* name; +} NTC_Config; + +// Текущая активная конфигурация +static NTC_Config active_ntc; + +// Быстрая таблица поиска +#define FAST_TABLE_SIZE 512 +static adc_temp_lookup fast_lookup[FAST_TABLE_SIZE]; + +// Инициализация с выбором типа термистора +void init_ntc(eNTC_Type ntc_type) { + switch(ntc_type) { + case NTC_TYPE_1: + active_ntc.type = NTC_TYPE_1; + active_ntc.table = ntc_table_type1; + active_ntc.table_size = TABLE1_SIZE; + active_ntc.start_temp = TABLE1_START_TEMP; + active_ntc.end_temp = TABLE1_END_TEMP; + active_ntc.r1 = TABLE1_R1; + active_ntc.params = steinhart_type1; + active_ntc.name = "NTC Type 1 (100950 Ohm @ -40C)"; + break; + + case NTC_TYPE_2: + active_ntc.type = NTC_TYPE_2; + active_ntc.table = ntc_table_type2; + active_ntc.table_size = TABLE2_SIZE; + active_ntc.start_temp = TABLE2_START_TEMP; + active_ntc.end_temp = TABLE2_END_TEMP; + active_ntc.r1 = TABLE2_R1; + active_ntc.params = steinhart_type2; + active_ntc.name = "NTC Type 2 (101000 Ohm @ -40C) - KST-45"; + break; + } +} + +// Функция для установки сопротивления делителя +void set_r1(float r1_value) { + if (r1_value > 0.0f) { + active_ntc.r1 = r1_value; + } +} + +// Функция для получения текущего R1 +float get_r1_value(void) { + return active_ntc.r1; +} + +// Функция расчёта сопротивления NTC из значения АЦП +static float calculate_resistance(uint16_t adc_value) { + if (adc_value == 0) { + return active_ntc.table[0].r_nom * 1.1f; // Экстраполяция + } + if (adc_value >= (uint16_t) ADC_MAX) { + return active_ntc.table[active_ntc.table_size - 1].r_nom * 0.9f; // Экстраполяция + } + + float R_ntc = active_ntc.r1 * (float)adc_value / (ADC_MAX - (float)adc_value); + + // Ограничиваем физически возможные значения + if (R_ntc < 10.0f) R_ntc = 10.0f; + if (R_ntc > 200000.0f) R_ntc = 200000.0f; + + return R_ntc; +} + +// Бинарный поиск в активной таблице +static int find_interval_index(float resistance) { + int left = 0; + int right = active_ntc.table_size - 1; + const ntc_table_entry* table = active_ntc.table; + + // Проверка границ + 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 < active_ntc.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 = active_ntc.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 = active_ntc.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); +} + +// Полная версия Steinhart-Hart с коэффициентами для данного термистора +static float interpolate_steinhart_full(float resistance, int index) { + // Проверка корректности входных данных + if (resistance <= 0.0f) { + return -273.15f; + } + + double L = logf(resistance); + double L3 = L * L * L; + double inv_T = active_ntc.params.A + active_ntc.params.B * L + active_ntc.params.C * L3; + + // Проверка на физическую реализуемость + if (inv_T <= 0.0f || inv_T > 1.0f) { + return (float)active_ntc.table[index].temp_c; + } + + double temp_K = 1.0f / inv_T; + + // Проверка диапазона + if (temp_K < (active_ntc.start_temp + 273.15f) || + temp_K > (active_ntc.end_temp + 273.15f)) { + return (float)active_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 >= active_ntc.table_size - 1) { + // Вне диапазона таблицы + const ntc_table_entry* table = active_ntc.table; + if (resistance >= table[0].r_nom) return active_ntc.start_temp; + if (resistance <= table[active_ntc.table_size - 1].r_nom) return active_ntc.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) { + uint16_t step = (uint16_t)(ADC_MAX / (FAST_TABLE_SIZE - 1)); + + for (uint16_t i = 0; i < FAST_TABLE_SIZE; i++) { + uint16_t adc = i * step; + if (adc > (uint16_t)ADC_MAX) adc = (uint16_t)ADC_MAX; + + float temp = get_temperature_from_adc(adc, use_alg); + fast_lookup[i].adc_value = adc; + + // Ограничиваем температуру разумными пределами + if (temp < active_ntc.start_temp) temp = active_ntc.start_temp; + if (temp > active_ntc.end_temp) temp = active_ntc.end_temp; + + fast_lookup[i].temp_c = (int16_t)(temp * 10.0f); + } +} + +// Быстрое получение температуры с линейной интерполяцией +int16_t get_temperature_fast(uint16_t adc_value) { + uint16_t step = (uint16_t)(ADC_MAX / (FAST_TABLE_SIZE - 1)); + uint16_t index = adc_value / step; + + if (index >= FAST_TABLE_SIZE - 1) { + return fast_lookup[FAST_TABLE_SIZE - 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; + + // Линейная интерполяция + if (adc2 != adc1) { + return temp1 + ((temp2 - temp1) * (adc_value - adc1)) / (adc2 - adc1); + } + + return temp1; +} + +// Вспомогательные функции +const char* get_ntc_name(void) { + return active_ntc.name; +} + +uint16_t get_table_size(void) { + return active_ntc.table_size; +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..cd68162 --- /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(MODEL_ADC ${SOURCES}) + + + 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