From 35a477a7ddcc2f75d92cea79378f99dd5ce90e4e Mon Sep 17 00:00:00 2001 From: cfif Date: Wed, 1 Apr 2026 10:37:02 +0300 Subject: [PATCH] Init --- APP/inc/ADC_Temp.h | 39 ++----- APP/main.c | 48 +++------ APP/src/ADC_Temp.c | 259 +++++++++++++++++++++------------------------ 3 files changed, 148 insertions(+), 198 deletions(-) diff --git a/APP/inc/ADC_Temp.h b/APP/inc/ADC_Temp.h index 3cff4c1..c8af0d3 100644 --- a/APP/inc/ADC_Temp.h +++ b/APP/inc/ADC_Temp.h @@ -9,6 +9,10 @@ // Константы #define ADC_MAX 4095.0f // 12-битный АЦП +#define R1 3300.0f // Сопротивление делителя напряжения +#define TABLE_START_TEMP (-40) +#define TABLE_END_TEMP 85 +#define TABLE_SIZE 38 // Параметры Steinhart-Hart для термистора #define koef_A 0.001741624168166423 @@ -17,19 +21,6 @@ #define TABLE_SIZE_LOOKUP 1024 -// Типы таблиц NTC -typedef enum { - NTC_TYPE_KST45 = 0, // Таблица из документа KST45 (25°C = 3kΩ) - NTC_TYPE_INCAR = 1, // Таблица из документа Incar (25°C = 2795Ω) - NTC_TYPE_CUSTOM = 2 // Пользовательская таблица -} eNtcTableType; - -// Структура для хранения табличных данных -typedef struct { - int16_t temp_c; // Температура (°C) - float r_nom; // Номинальное сопротивление (Ω) -} ntc_table_entry; - typedef enum { ALG_STEINHART = 0, ALG_STEINHART_FULL = 1, @@ -38,24 +29,16 @@ typedef enum { // Предварительно вычисленная таблица для быстрого доступа typedef struct { - uint16_t adc_value; // Значение АЦП - int16_t temp_c; // Температура в °C * 10 (для фиксированной точки) - float resistance; // Сопротивление термистора в Омах + uint16_t adc_value; // Значение АЦП + int16_t temp_c; // Температура в °C * 10 (для фиксированной точки) + float resistance_ohm; // Сопротивление в Ом, соответствующее значению АЦП } adc_temp_lookup; -// Функции инициализации и конфигурации -void init_fast_lookup_table(eAlg use_alg, eNtcTableType table_type, float custom_r1); -void set_custom_ntc_table(const ntc_table_entry* table, uint16_t size, float r1); -void select_ntc_table(eNtcTableType table_type, float custom_r1); - -// Основные функции +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); -float get_resistance_from_adc(uint16_t adc_value); -float get_resistance_fast(uint16_t adc_value); +int16_t get_temperature_log_fast(uint16_t adc_value); +int16_t get_temperature_linear_fast(uint16_t adc_value); +float get_resistance_from_adc(uint16_t adc_value); // Новая функция для получения сопротивления -// Функции для работы с текущей таблицей -eNtcTableType get_current_table_type(void); -float get_current_r1(void); #endif //MDF_ADC_TEMP_KST45_14_2_H \ No newline at end of file diff --git a/APP/main.c b/APP/main.c index 7bf8ae0..8e3f610 100644 --- a/APP/main.c +++ b/APP/main.c @@ -1,45 +1,31 @@ #include -#include -#include -#include -#include #include "ADC_Temp.h" - -// value 0..4095 - Это диапазон АЦП, Нужно проверить крайние значения и смоделировать таблицы - - extern adc_temp_lookup fast_lookup[TABLE_SIZE_LOOKUP]; int main() { + // Инициализация таблицы быстрого поиска + init_fast_lookup_table(ALG_STEINHART); - init_fast_lookup_table(ALG_STEINHART, NTC_TYPE_INCAR, 3300.0f); - -// for (int i = 0; i < TABLE_SIZE_LOOKUP; ++i) { -// printf("{%u,%d}, \n", fast_lookup[i].adc_value, fast_lookup[i].temp_c); -// } - - - - //uint16_t value = 4095 / 2; // Должно быть 25 градусов. Это середина диапазона АЦП uint16_t value = 1980; -// float T_ALG_LINEAR = get_temperature_from_adc(value, ALG_LINEAR); - //float T_ALG_STEINHART = get_temperature_from_adc(value, ALG_STEINHART); - float T_ALG_STEINHART = get_temperature_fast(value); - float T_ALG_STEINHART_1 = get_resistance_fast(value); + // Получение температуры различными методами + float T_ALG_STEINHART = get_temperature_from_adc(value, ALG_STEINHART); + int16_t T_FAST = get_temperature_log_fast(value); + float resistance = get_resistance_from_adc(value); - // float T_ALG_STEINHART_FULL = get_temperature_from_adc(value, ALG_STEINHART_FULL); -// float T_FAST = get_temperature_fast(value); -// float T_ATERLUX = calc_temperature(value); - -// printf("T_ALG_LINEAR = %f \n", T_ALG_LINEAR); - printf("T_ALG_STEINHART = %f %f\n", T_ALG_STEINHART, T_ALG_STEINHART_1); -// 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); + printf("T_ALG_STEINHART = %f °C\n", T_ALG_STEINHART); + printf("T_FAST = %.1f °C\n", T_FAST / 10.0f); + printf("Resistance = %.2f Ω\n", resistance); + // Пример доступа к таблице + printf("\nПример данных из таблицы быстрого поиска:\n"); + for(int i = 0; i < 5; i++) { + printf("ADC: %u, Temp: %.1f °C, Resistance: %.2f Ω\n", + fast_lookup[i].adc_value, + fast_lookup[i].temp_c / 10.0f, + fast_lookup[i].resistance_ohm); + } return 0; } \ No newline at end of file diff --git a/APP/src/ADC_Temp.c b/APP/src/ADC_Temp.c index c8c0f93..9e94980 100644 --- a/APP/src/ADC_Temp.c +++ b/APP/src/ADC_Temp.c @@ -1,13 +1,19 @@ // // Created by cfif on 02.12.2025. // - +#include "stdint.h" #include "ADC_Temp.h" #include -#include -// Таблица из документа KST45 (25°C = 3000Ω) -static const ntc_table_entry ntc_table_kst45[] = { +// Структура для хранения табличных данных +typedef struct { + int temp_c; // Температура (°C) + float r_nom; // Номинальное сопротивление (Ω) +} ntc_table_entry; + +/* +// Таблица из документа KST45 +static const ntc_table_entry ntc_table[] = { {-40, 100950.0f}, {-35, 72777.0f}, {-30, 53100.0f}, @@ -35,9 +41,10 @@ static const ntc_table_entry ntc_table_kst45[] = { {80, 377.4f}, {85, 321.7f} }; +*/ -// Таблица из документа Incar (25°C ≈ 2795Ω) -static const ntc_table_entry ntc_table_incar[] = { +// Таблица из документа Incar +static ntc_table_entry ntc_table[] = { {-40, 101000.0f}, {-35, 72600.0f}, {-30, 52720.0f}, @@ -45,12 +52,12 @@ static const ntc_table_entry ntc_table_incar[] = { {-20, 28620.0f}, {-15, 21390.0f}, {-10, 16120.0f}, - {-5, 12260.0f}, + {-5, 12260.0f}, {-4, 11620.0f}, {-3, 11010.0f}, {-2, 10440.0f}, {-1, 9907.0f}, - {0, 9399.0f}, + {0, 9399.0f}, {1, 8924.0f}, {2, 8473.0f}, {3, 8048.0f}, @@ -78,45 +85,38 @@ static const ntc_table_entry ntc_table_incar[] = { {85, 283.0f} }; -// Глобальные переменные для текущей конфигурации -static const ntc_table_entry* current_ntc_table = ntc_table_kst45; -static uint16_t current_table_size = sizeof(ntc_table_kst45) / sizeof(ntc_table_entry); -static float current_r1 = 3300.0f; // Сопротивление делителя по умолчанию -static eNtcTableType current_table_type = NTC_TYPE_KST45; - -// Глобальная таблица для быстрого доступа +// Предварительно вычисленная таблица для быстрого доступа adc_temp_lookup fast_lookup[TABLE_SIZE_LOOKUP]; // Функция расчёта сопротивления NTC из значения АЦП -static float calculate_resistance(uint16_t adc_value, float r1) { +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_value / (ADC_MAX - adc_value) - float R_ntc = r1 * (float) adc_value / (ADC_MAX - (float) adc_value); + float R_ntc = R1 * (float) adc_value / (ADC_MAX - (float) adc_value); return R_ntc; } // Бинарный поиск в таблице -static int find_interval_index(float resistance, const ntc_table_entry* table, uint16_t size) { +static int find_interval_index(float resistance) { int left = 0; - int right = size - 1; + int right = TABLE_SIZE - 1; // Проверка границ - if (resistance >= table[0].r_nom) return 0; - if (resistance <= table[right].r_nom) return right - 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 < size - 1 && - resistance <= table[mid].r_nom && - resistance >= table[mid + 1].r_nom) { + if (mid < TABLE_SIZE - 1 && + resistance <= ntc_table[mid].r_nom && + resistance >= ntc_table[mid + 1].r_nom) { return mid; } - if (resistance > table[mid].r_nom) { + if (resistance > ntc_table[mid].r_nom) { right = mid - 1; } else { left = mid + 1; @@ -127,12 +127,12 @@ static int find_interval_index(float resistance, const ntc_table_entry* table, u } // Интерполяция с использованием уравнения Стейнхарта-Харта между точками -static float interpolate_steinhart(float resistance, int index, const ntc_table_entry* table) { +static float interpolate_steinhart(float resistance, int index) { // Берем две соседние точки из таблицы - 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; + 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); @@ -145,11 +145,11 @@ static float interpolate_steinhart(float resistance, int index, const ntc_table_ } // Линейная интерполяция в логарифмическом масштабе -static float interpolate_log_linear(float resistance, int index, const ntc_table_entry* 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; +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); @@ -159,7 +159,7 @@ static float interpolate_log_linear(float resistance, int index, const ntc_table } // Более надежная версия с проверкой параметров -static float interpolate_steinhart_full(float resistance, int index, const ntc_table_entry* table) { +static float interpolate_steinhart_full(float resistance, int index) { // Проверка корректности входных данных if (resistance <= 0.0f) { return -273.15f; // Абсолютный ноль при некорректном сопротивлении @@ -169,158 +169,139 @@ static float interpolate_steinhart_full(float resistance, int index, const ntc_t // но основное вычисление - по уравнению с коэффициентами 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) { + // Проверка на физическую реализуемость (температура должна быть положительной в Кельвинах) + if (inv_T <= 0.0f || inv_T > 1.0f) { // 1/T не может быть <= 0 (T < 0K) или слишком большим // В случае ошибки возвращаем температуру из таблицы по индексу - return (float)table[index].temp_c; + return (float) ntc_table[index].temp_c; } double temp_K = 1.0f / inv_T; - // Дополнительная проверка диапазона - if (temp_K < 223.15f || temp_K > 423.15f) { + // Дополнительная проверка диапазона (например, для NTC обычно -50...+150°C) + if (temp_K < 223.15f || temp_K > 423.15f) { // -50°C...150°C в Кельвинах // Возвращаем значение из таблицы как запасной вариант - return (float)table[index].temp_c; + return (float) ntc_table[index].temp_c; } - return (float)(temp_K - 273.15f); + return (float) (temp_K - 273.15f); } // Основная функция для получения температуры float get_temperature_from_adc(uint16_t adc_value, eAlg use_alg) { - float resistance = calculate_resistance(adc_value, current_r1); + float resistance = calculate_resistance(adc_value); if (resistance <= 0.0f) { return -273.15f; // Ошибка } - int index = find_interval_index(resistance, current_ntc_table, current_table_size); + int index = find_interval_index(resistance); - if (index < 0 || index >= current_table_size - 1) { + if (index < 0 || index >= TABLE_SIZE - 1) { // Вне диапазона таблицы - if (resistance >= current_ntc_table[0].r_nom) return current_ntc_table[0].temp_c; - if (resistance <= current_ntc_table[current_table_size - 1].r_nom) return current_ntc_table[current_table_size - 1].temp_c; + 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, current_ntc_table); + return interpolate_steinhart(resistance, index); } else if (use_alg == ALG_STEINHART_FULL) { - return interpolate_steinhart_full(resistance, index, current_ntc_table); + return interpolate_steinhart_full(resistance, index); } else { - return interpolate_log_linear(resistance, index, current_ntc_table); + return interpolate_log_linear(resistance, index); } } -// Функция для получения сопротивления по значению АЦП +// Функция для получения сопротивления из значения АЦП float get_resistance_from_adc(uint16_t adc_value) { - return calculate_resistance(adc_value, current_r1); + return calculate_resistance(adc_value); } -// Выбор активной таблицы NTC -void select_ntc_table(eNtcTableType table_type, float custom_r1) { - switch (table_type) { - case NTC_TYPE_KST45: - current_ntc_table = ntc_table_kst45; - current_table_size = sizeof(ntc_table_kst45) / sizeof(ntc_table_entry); - current_r1 = (custom_r1 > 0) ? custom_r1 : 3300.0f; - current_table_type = NTC_TYPE_KST45; - break; - - case NTC_TYPE_INCAR: - current_ntc_table = ntc_table_incar; - current_table_size = sizeof(ntc_table_incar) / sizeof(ntc_table_entry); - current_r1 = (custom_r1 > 0) ? custom_r1 : 3300.0f; - current_table_type = NTC_TYPE_INCAR; - break; - - case NTC_TYPE_CUSTOM: - // Пользовательская таблица должна быть установлена через set_custom_ntc_table - if (current_ntc_table == NULL) { - // Если пользовательская таблица не установлена, используем KST45 - current_ntc_table = ntc_table_kst45; - current_table_size = sizeof(ntc_table_kst45) / sizeof(ntc_table_entry); - current_r1 = 3300.0f; - current_table_type = NTC_TYPE_KST45; - } - if (custom_r1 > 0) { - current_r1 = custom_r1; - } - break; - } -} - -// Установка пользовательской таблицы NTC -void set_custom_ntc_table(const ntc_table_entry* table, uint16_t size, float r1) { - if (table != NULL && size > 1) { - current_ntc_table = table; - current_table_size = size; - current_r1 = (r1 > 0) ? r1 : 3300.0f; - current_table_type = NTC_TYPE_CUSTOM; - } -} - -// Инициализация таблицы быстрого доступа -void init_fast_lookup_table(eAlg use_alg, eNtcTableType table_type, float custom_r1) { - // Выбираем таблицу NTC - select_ntc_table(table_type, custom_r1); - +void init_fast_lookup_table(eAlg use_alg) { // Создаем таблицу для быстрого преобразования АЦП->температура for (uint16_t i = 0; i < TABLE_SIZE_LOOKUP; i++) { - uint16_t adc = (uint16_t)(i * (ADC_MAX / (TABLE_SIZE_LOOKUP - 1))); + // Правильное распределение значений АЦП от 0 до ADC_MAX + uint16_t adc = (uint16_t) ((float) i * ADC_MAX / (TABLE_SIZE_LOOKUP - 1)); float temp = get_temperature_from_adc(adc, use_alg); - float resistance = calculate_resistance(adc, current_r1); + float resistance = calculate_resistance(adc); + fast_lookup[i].adc_value = adc; fast_lookup[i].temp_c = (int16_t) (temp * 10.0f); // Храним с точностью 0.1°C - fast_lookup[i].resistance = resistance; + fast_lookup[i].resistance_ohm = resistance; } } -// Быстрое получение температуры из таблицы -int16_t get_temperature_fast(uint16_t adc_value) { - // Простой поиск в таблице с линейной интерполяцией - uint16_t step = (uint16_t)(ADC_MAX / (TABLE_SIZE_LOOKUP - 1)); - if (step == 0) step = 1; +int16_t get_temperature_log_fast(uint16_t adc_value) { + // Защита от выхода за границы + if (adc_value >= fast_lookup[TABLE_SIZE_LOOKUP - 1].adc_value) { + return fast_lookup[TABLE_SIZE_LOOKUP - 1].temp_c; + } - uint16_t index = adc_value / step; - if (index >= (TABLE_SIZE_LOOKUP - 1)) return fast_lookup[TABLE_SIZE_LOOKUP - 1].temp_c; + // Бинарный поиск интервала + int left = 0; + int right = TABLE_SIZE_LOOKUP - 1; - 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; + while (left <= right) { + int mid = left + (right - left) / 2; - // Линейная интерполяция температуры - return temp1 + ((int32_t)(temp2 - temp1) * (adc_value - adc1)) / (adc2 - adc1); + if (fast_lookup[mid].adc_value <= adc_value && + (mid == TABLE_SIZE_LOOKUP - 1 || adc_value < fast_lookup[mid + 1].adc_value)) { + // Нашли интервал + if (mid == TABLE_SIZE_LOOKUP - 1) { + return fast_lookup[mid].temp_c; + } + + // Линейная интерполяция + uint16_t adc1 = fast_lookup[mid].adc_value; + uint16_t adc2 = fast_lookup[mid + 1].adc_value; + int16_t temp1 = fast_lookup[mid].temp_c; + int16_t temp2 = fast_lookup[mid + 1].temp_c; + + // Избегаем деления на ноль + if (adc2 == adc1) { + return temp1; + } + + return temp1 + ((int32_t) (temp2 - temp1) * (adc_value - adc1)) / (adc2 - adc1); + } + + if (adc_value < fast_lookup[mid].adc_value) { + right = mid - 1; + } else { + left = mid + 1; + } + } + + return fast_lookup[0].temp_c; // Если не нашли, возвращаем первое значение } -// Быстрое получение сопротивления из таблицы -float get_resistance_fast(uint16_t adc_value) { - // Простой поиск в таблице с линейной интерполяцией - uint16_t step = (uint16_t)(ADC_MAX / (TABLE_SIZE_LOOKUP - 1)); - if (step == 0) step = 1; - uint16_t index = adc_value / step; - if (index >= (TABLE_SIZE_LOOKUP - 1)) return fast_lookup[TABLE_SIZE_LOOKUP - 1].resistance; +// Альтернативная простая версия +int16_t get_temperature_linear_fast(uint16_t adc_value) { + // Находим интервал + uint16_t i = 0; + while (i < TABLE_SIZE_LOOKUP - 1 && adc_value > fast_lookup[i + 1].adc_value) { + i++; + } - uint16_t adc1 = fast_lookup[index].adc_value; - uint16_t adc2 = fast_lookup[index + 1].adc_value; - float r1 = fast_lookup[index].resistance; - float r2 = fast_lookup[index + 1].resistance; + // Линейная интерполяция + if (i >= TABLE_SIZE_LOOKUP - 1) { + return fast_lookup[TABLE_SIZE_LOOKUP - 1].temp_c; + } - // Линейная интерполяция сопротивления - return r1 + (r2 - r1) * (adc_value - adc1) / (adc2 - adc1); -} + uint16_t adc1 = fast_lookup[i].adc_value; + uint16_t adc2 = fast_lookup[i + 1].adc_value; + int16_t temp1 = fast_lookup[i].temp_c; + int16_t temp2 = fast_lookup[i + 1].temp_c; -// Получение текущего типа таблицы -eNtcTableType get_current_table_type(void) { - return current_table_type; -} + if (adc2 == adc1) { + return temp1; + } -// Получение текущего сопротивления делителя -float get_current_r1(void) { - return current_r1; + return temp1 + ((int32_t) (temp2 - temp1) * (adc_value - adc1)) / (adc2 - adc1); } \ No newline at end of file