This commit is contained in:
cfif 2026-05-13 09:53:45 +03:00
parent 2d5e7010d2
commit 927ae65602
3 changed files with 12 additions and 189 deletions

View File

@ -37,15 +37,9 @@ typedef enum {
// Алгоритмы расчёта температуры // Алгоритмы расчёта температуры
typedef enum { typedef enum {
ALG_STEINHART = 0, ALG_STEINHART = 0,
ALG_STEINHART_FULL = 1, ALG_LINEAR = 1
ALG_LINEAR = 2
} eAlg; } eAlg;
// Параметры Steinhart-Hart для термистора (общие для всех таблиц)
#define koef_A 0.001741624168166423
#define koef_B 0.00017003940268680147
#define koef_C 0.0000004890545443703666
// Размеры таблиц // Размеры таблиц
#define TABLE_SIZE_DUCT 26 #define TABLE_SIZE_DUCT 26
#define TABLE_SIZE_INCAR 38 #define TABLE_SIZE_INCAR 38
@ -63,17 +57,6 @@ typedef struct {
eNtcTable table_type; // Тип таблицы, для которой вычислены данные eNtcTable table_type; // Тип таблицы, для которой вычислены данные
} adc_temp_lookup; } adc_temp_lookup;
// Структура конфигурации для NTC
typedef struct {
eNtcTable table_type; // Тип используемой таблицы
float r1; // Сопротивление делителя напряжения (Ом)
float vcc_mv; // Напряжение питания делителя (мВ)
float vref_mv; // Опорное напряжение АЦП (мВ)
int16_t start_temp; // Начальная температура таблицы (°C)
int16_t end_temp; // Конечная температура таблицы (°C)
uint16_t table_size; // Размер таблицы
} ntc_config_t;
// Структура для хранения таблиц быстрого доступа для каждой конфигурации // Структура для хранения таблиц быстрого доступа для каждой конфигурации
typedef struct { typedef struct {
adc_temp_lookup duct[TABLE_SIZE_LOOKUP]; adc_temp_lookup duct[TABLE_SIZE_LOOKUP];
@ -98,8 +81,6 @@ void init_duct_table(float r1, eAlg use_alg);
void init_incar_table(float r1, eAlg use_alg); void init_incar_table(float r1, eAlg use_alg);
void init_ambient_table(float r1, eAlg use_alg); void init_ambient_table(float r1, eAlg use_alg);
void init_all_tables(float r1_duct, float r1_incar, float r1_ambient, eAlg use_alg); void init_all_tables(float r1_duct, float r1_incar, float r1_ambient, eAlg use_alg);
void set_active_config(eNtcTable table_type, float r1);
const ntc_config_t* get_active_config(void);
const fast_lookup_tables_t* get_fast_tables(void); const fast_lookup_tables_t* get_fast_tables(void);
// Вспомогательные функции для преобразований // Вспомогательные функции для преобразований
@ -126,10 +107,8 @@ float get_resistance_from_adc_with_table(uint16_t adc_value, eNtcTable table_typ
// Быстрые функции с использованием предварительно вычисленной таблицы // Быстрые функции с использованием предварительно вычисленной таблицы
int16_t get_temperature_log_fast(uint16_t adc_value); int16_t get_temperature_log_fast(uint16_t adc_value);
int16_t get_temperature_log_fast_for_table(uint16_t adc_value, eNtcTable table_type); int16_t get_temperature_log_fast_for_table(uint16_t adc_value, eNtcTable table_type);
int16_t get_temperature_linear_fast(uint16_t adc_value);
float get_resistance_log_fast(int16_t temperature_c10); float get_resistance_log_fast(int16_t temperature_c10);
float get_resistance_log_fast_for_table(int16_t temperature_c10, eNtcTable table_type); float get_resistance_log_fast_for_table(int16_t temperature_c10, eNtcTable table_type);
float get_resistance_fast_simple(int16_t temperature_c10);
// Новые функции для получения напряжений // Новые функции для получения напряжений
uint16_t get_voltage_from_adc(uint16_t adc_value); uint16_t get_voltage_from_adc(uint16_t adc_value);

View File

@ -209,80 +209,29 @@ void save_all_tables_to_files(void) {
save_table_to_c_file("duct_table_array.c", tables->duct, "DUCT", "duct_lookup_table"); save_table_to_c_file("duct_table_array.c", tables->duct, "DUCT", "duct_lookup_table");
save_table_to_csv("duct_table.csv", tables->duct, "DUCT"); save_table_to_csv("duct_table.csv", tables->duct, "DUCT");
save_table_to_txt("duct_table.txt", tables->duct, "DUCT"); save_table_to_txt("duct_table.txt", tables->duct, "DUCT");
// save_temperature_int_one_line("duct_table.txt", tables->duct, "DUCT"); save_temperature_int_one_line("duct_temperatures.txt", tables->duct, "DUCT");
// Сохраняем INCAR таблицу // Сохраняем INCAR таблицу
save_table_to_c_file("incar_table_array.c", tables->incar, "INCAR", "incar_lookup_table"); save_table_to_c_file("incar_table_array.c", tables->incar, "INCAR", "incar_lookup_table");
save_table_to_csv("incar_table.csv", tables->incar, "INCAR"); save_table_to_csv("incar_table.csv", tables->incar, "INCAR");
save_table_to_txt("incar_table.txt", tables->incar, "INCAR"); save_table_to_txt("incar_table.txt", tables->incar, "INCAR");
save_temperature_int_one_line("incar_temperatures.txt", tables->incar, "INCAR");
// Сохраняем AMBIENT таблицу // Сохраняем AMBIENT таблицу
save_table_to_c_file("ambient_table_array.c", tables->ambient, "AMBIENT", "ambient_lookup_table"); save_table_to_c_file("ambient_table_array.c", tables->ambient, "AMBIENT", "ambient_lookup_table");
save_table_to_csv("ambient_table.csv", tables->ambient, "AMBIENT"); save_table_to_csv("ambient_table.csv", tables->ambient, "AMBIENT");
save_table_to_txt("ambient_table.txt", tables->ambient, "AMBIENT"); save_table_to_txt("ambient_table.txt", tables->ambient, "AMBIENT");
save_temperature_int_one_line("ambient_temperatures.txt", tables->ambient, "AMBIENT");
printf("\nВсе таблицы успешно сохранены!\n"); printf("\nВсе таблицы успешно сохранены!\n");
} }
// Функция для проверки корректности таблицы
void validate_table(const adc_temp_lookup* table, const char* table_name, float r1) {
printf("\n=== Проверка таблицы: %s (R1=%.0fΩ) ===\n", table_name, r1);
int monotonic_errors = 0;
int last_temp = table[0].temp_c;
float min_resistance = 999999999.0f;
float max_resistance = 0.0f;
for (int i = 1; i < TABLE_SIZE_LOOKUP; i++) {
// Проверка монотонности температуры (должна убывать)
if (table[i].temp_c > last_temp) {
if (monotonic_errors < 5) {
printf("Ошибка монотонности: ADC=%u, Temp=%d > предыдущего %d\n",
table[i].adc_value, table[i].temp_c, last_temp);
}
monotonic_errors++;
}
last_temp = table[i].temp_c;
// Поиск min/max сопротивления
if (table[i].resistance_ohm < min_resistance && table[i].resistance_ohm > 0) {
min_resistance = table[i].resistance_ohm;
}
if (table[i].resistance_ohm > max_resistance) {
max_resistance = table[i].resistance_ohm;
}
}
if (monotonic_errors > 0) {
printf("Найдено %d ошибок монотонности\n", monotonic_errors);
} else {
printf("Монотонность: OK (температура монотонно убывает)\n");
}
printf("Диапазон сопротивлений: %.2f Ом ... %.2f Ом\n", max_resistance, min_resistance);
// Вывод первых и последних записей
printf("\nПервые 10 записей:\n");
for (int i = 0; i < 10 && i < TABLE_SIZE_LOOKUP; i++) {
printf(" ADC=%4u: Temp=%6.1f°C, R=%8.2fΩ\n",
table[i].adc_value, table[i].temp_c / 10.0f, table[i].resistance_ohm);
}
printf("\nПоследние 10 записей:\n");
for (int i = TABLE_SIZE_LOOKUP - 10; i < TABLE_SIZE_LOOKUP; i++) {
printf(" ADC=%4u: Temp=%6.1f°C, R=%8.2fΩ\n",
table[i].adc_value, table[i].temp_c / 10.0f, table[i].resistance_ohm);
}
}
int main() { int main() {
printf("=== Генерация таблиц быстрого поиска для NTC термисторов ===\n"); printf("=== Генерация таблиц быстрого поиска для NTC термисторов ===\n");
printf("Конфигурация:\n"); printf("Конфигурация:\n");
printf(" VCC делителя = %.2f V\n", VCC_DIVIDER_MV / 1000.0f); printf(" VCC делителя = %.2f V\n", VCC_DIVIDER_MV / 1000.0f);
printf(" VREF АЦП = %.2f V\n", VREF_MV / 1000.0f); printf(" VREF АЦП = %.2f V\n", VREF_MV / 1000.0f);
printf(" ADC_MAX = %.0f\n", ADC_MAX); printf(" ADC_MAX = %.0f\n", ADC_MAX);
printf(" Максимальное ADC при VCC=%.2fV: %.1f\n",
VCC_DIVIDER_MV / 1000.0f, (VCC_DIVIDER_MV * ADC_MAX) / VREF_MV);
// Инициализируем все три таблицы // Инициализируем все три таблицы
printf("\n=== Инициализация таблиц ===\n"); printf("\n=== Инициализация таблиц ===\n");
@ -293,38 +242,15 @@ int main() {
printf("Таблицы успешно инициализированы\n"); printf("Таблицы успешно инициализированы\n");
// Проверяем таблицы
const fast_lookup_tables_t* tables = get_fast_tables();
validate_table(tables->duct, "DUCT", g_fast_tables.duct_r1);
validate_table(tables->incar, "INCAR", g_fast_tables.incar_r1);
validate_table(tables->ambient, "AMBIENT", g_fast_tables.ambient_r1);
// Сохраняем таблицы в файлы // Сохраняем таблицы в файлы
save_all_tables_to_files(); save_all_tables_to_files();
// Пример использования
printf("\n=== Пример использования ===\n");
uint16_t test_adcs[] = {0, 500, 1000, 1500, 2000, 2500, 3000, 3500, 4000};
printf("ADC\tDUCT Temp\tINCAR Temp\tAMBIENT Temp\n");
printf("------------------------------------------------\n");
for (int i = 0; i < sizeof(test_adcs)/sizeof(test_adcs[0]); i++) {
uint16_t adc = test_adcs[i];
int16_t temp_duct = get_temperature_log_fast_for_table(adc, TABLE_DUCT);
int16_t temp_incar = get_temperature_log_fast_for_table(adc, TABLE_INCAR);
int16_t temp_ambient = get_temperature_log_fast_for_table(adc, TABLE_AMBIENT);
printf("%u\t%.1f°C\t\t%.1f°C\t\t%.1f°C\n",
adc, temp_duct / 10.0f, temp_incar / 10.0f, temp_ambient / 10.0f);
}
printf("\n=== Готово ===\n"); printf("\n=== Готово ===\n");
printf("Сгенерированы файлы:\n"); printf("Сгенерированы файлы:\n");
printf(" - duct_table_array.c (статический массив)\n"); printf(" - duct_table_array.c, incar_table_array.c, ambient_table_array.c (статический массив)\n");
printf(" - incar_table_array.c (статический массив)\n");
printf(" - ambient_table_array.c (статический массив)\n");
printf(" - duct_table.csv, incar_table.csv, ambient_table.csv (для Excel)\n"); printf(" - duct_table.csv, incar_table.csv, ambient_table.csv (для Excel)\n");
printf(" - duct_table.txt, incar_table.txt, ambient_table.txt (для просмотра)\n"); printf(" - duct_table.txt, incar_table.txt, ambient_table.txt (для просмотра)\n");
printf(" - duct_temperatures.txt, incar_temperatures.txt, ambient_temperatures.txt (температуры в одну строку)\n");
return 0; return 0;
} }

View File

@ -139,18 +139,6 @@ fast_lookup_tables_t g_fast_tables = {
.vref_mv = VREF_MV .vref_mv = VREF_MV
}; };
// Активная конфигурация
static ntc_config_t active_config = {
.table_type = TABLE_DUCT,
.r1 = 3300.0f,
.vcc_mv = VCC_DIVIDER_MV,
.vref_mv = VREF_MV,
.start_temp = -40,
.end_temp = 85,
.table_size = TABLE_SIZE_DUCT
};
// Вспомогательная функция для получения таблицы по типу // Вспомогательная функция для получения таблицы по типу
static const ntc_table_entry* get_table_by_type(eNtcTable table_type, uint16_t* size, int16_t* start_temp, int16_t* end_temp) { static const ntc_table_entry* get_table_by_type(eNtcTable table_type, uint16_t* size, int16_t* start_temp, int16_t* end_temp) {
const ntc_table_entry* table = NULL; const ntc_table_entry* table = NULL;
@ -268,7 +256,6 @@ static float calculate_resistance(uint16_t adc_value, float r1) {
#endif #endif
} }
// Бинарный поиск в таблице // Бинарный поиск в таблице
static int find_interval_index(float resistance, const ntc_table_entry* table, uint16_t table_size) { static int find_interval_index(float resistance, const ntc_table_entry* table, uint16_t table_size) {
int left = 0; int left = 0;
@ -330,39 +317,6 @@ static float interpolate_log_linear(float resistance, int index, const ntc_table
return t1 + (t2 - t1) * (log_r - log_r1) / (log_r2 - log_r1); return t1 + (t2 - t1) * (log_r - log_r1) / (log_r2 - log_r1);
} }
// Более надежная версия с проверкой параметров
static float interpolate_steinhart_full(float resistance, int index, const ntc_table_entry* table) {
// Проверка корректности входных данных
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) 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) table[index].temp_c;
}
return (float) (temp_K - 273.15f);
}
// Основная функция для получения температуры с указанием таблицы и R1 // Основная функция для получения температуры с указанием таблицы и R1
float get_temperature_from_adc_with_table(uint16_t adc_value, eAlg alg, eNtcTable table_type, float r1) { float get_temperature_from_adc_with_table(uint16_t adc_value, eAlg alg, eNtcTable table_type, float r1) {
float resistance = calculate_resistance(adc_value, r1); float resistance = calculate_resistance(adc_value, r1);
@ -386,16 +340,14 @@ float get_temperature_from_adc_with_table(uint16_t adc_value, eAlg alg, eNtcTabl
if (alg == ALG_STEINHART) { if (alg == ALG_STEINHART) {
return interpolate_steinhart(resistance, index, table); return interpolate_steinhart(resistance, index, table);
} else if (alg == ALG_STEINHART_FULL) {
return interpolate_steinhart_full(resistance, index, table);
} else { } else {
return interpolate_log_linear(resistance, index, table); return interpolate_log_linear(resistance, index, table);
} }
} }
// Функция для получения температуры с активной конфигурацией // Функция для получения температуры
float get_temperature_from_adc(uint16_t adc_value, eAlg alg) { float get_temperature_from_adc(uint16_t adc_value, eAlg alg) {
return get_temperature_from_adc_with_table(adc_value, alg, active_config.table_type, active_config.r1); return get_temperature_from_adc_with_table(adc_value, alg, TABLE_DUCT, 3300.0f);
} }
// Функция для получения сопротивления из значения АЦП с указанием таблицы // Функция для получения сопротивления из значения АЦП с указанием таблицы
@ -403,9 +355,9 @@ float get_resistance_from_adc_with_table(uint16_t adc_value, eNtcTable table_typ
return calculate_resistance(adc_value, r1); return calculate_resistance(adc_value, r1);
} }
// Функция для получения сопротивления с активной конфигурацией // Функция для получения сопротивления
float get_resistance_from_adc(uint16_t adc_value) { float get_resistance_from_adc(uint16_t adc_value) {
return calculate_resistance(adc_value, active_config.r1); return calculate_resistance(adc_value, 3300.0f);
} }
// Функция для получения напряжения из АЦП // Функция для получения напряжения из АЦП
@ -512,10 +464,6 @@ void init_fast_lookup_table(eNtcTable table_type, float r1, eAlg use_alg) {
max_valid_adc = temp; max_valid_adc = temp;
} }
printf("DEBUG: %s - min_temp_adc=%u (low temp), max_temp_adc=%u (high temp)\n",
table_type == TABLE_DUCT ? "DUCT" : (table_type == TABLE_INCAR ? "INCAR" : "AMBIENT"),
min_valid_adc, max_valid_adc);
// Заполняем таблицу - теперь ADC растет от 0 до MAX // Заполняем таблицу - теперь ADC растет от 0 до MAX
// При ADC=0: минимальное сопротивление, максимальная температура // При ADC=0: минимальное сопротивление, максимальная температура
// При ADC=MAX: максимальное сопротивление, минимальная температура // При ADC=MAX: максимальное сопротивление, минимальная температура
@ -575,8 +523,6 @@ void init_fast_lookup_table(eNtcTable table_type, float r1, eAlg use_alg) {
} else { } else {
if (use_alg == ALG_STEINHART) { if (use_alg == ALG_STEINHART) {
temp = interpolate_steinhart(resistance, index, table); temp = interpolate_steinhart(resistance, index, table);
} else if (use_alg == ALG_STEINHART_FULL) {
temp = interpolate_steinhart_full(resistance, index, table);
} else { } else {
temp = interpolate_log_linear(resistance, index, table); temp = interpolate_log_linear(resistance, index, table);
} }
@ -608,7 +554,6 @@ void init_fast_lookup_table(eNtcTable table_type, float r1, eAlg use_alg) {
} }
} }
// Инициализация таблицы Duct // Инициализация таблицы Duct
void init_duct_table(float r1, eAlg use_alg) { void init_duct_table(float r1, eAlg use_alg) {
init_fast_lookup_table(TABLE_DUCT, r1, use_alg); init_fast_lookup_table(TABLE_DUCT, r1, use_alg);
@ -631,23 +576,6 @@ void init_all_tables(float r1_duct, float r1_incar, float r1_ambient, eAlg use_a
init_fast_lookup_table(TABLE_AMBIENT, r1_ambient, use_alg); init_fast_lookup_table(TABLE_AMBIENT, r1_ambient, use_alg);
} }
// Установка активной конфигурации
void set_active_config(eNtcTable table_type, float r1) {
active_config.table_type = table_type;
active_config.r1 = r1;
active_config.vcc_mv = VCC_DIVIDER_MV;
active_config.vref_mv = VREF_MV;
uint16_t table_size;
get_table_by_type(table_type, &table_size, &active_config.start_temp, &active_config.end_temp);
active_config.table_size = table_size;
}
// Получение активной конфигурации
const ntc_config_t* get_active_config(void) {
return &active_config;
}
// Получение таблиц быстрого доступа // Получение таблиц быстрого доступа
const fast_lookup_tables_t* get_fast_tables(void) { const fast_lookup_tables_t* get_fast_tables(void) {
return &g_fast_tables; return &g_fast_tables;
@ -710,12 +638,7 @@ int16_t get_temperature_log_fast_for_table(uint16_t adc_value, eNtcTable table_t
// Быстрые функции с использованием активной таблицы // Быстрые функции с использованием активной таблицы
int16_t get_temperature_log_fast(uint16_t adc_value) { int16_t get_temperature_log_fast(uint16_t adc_value) {
return get_temperature_log_fast_for_table(adc_value, active_config.table_type); return get_temperature_log_fast_for_table(adc_value, TABLE_DUCT);
}
// Альтернативная простая версия
int16_t get_temperature_linear_fast(uint16_t adc_value) {
return get_temperature_log_fast_for_table(adc_value, active_config.table_type);
} }
// Функция для получения сопротивления из температуры для конкретной таблицы // Функция для получения сопротивления из температуры для конкретной таблицы
@ -775,10 +698,5 @@ float get_resistance_log_fast_for_table(int16_t temperature_c10, eNtcTable table
// Функция для получения сопротивления из температуры (обратное преобразование) // Функция для получения сопротивления из температуры (обратное преобразование)
float get_resistance_log_fast(int16_t temperature_c10) { float get_resistance_log_fast(int16_t temperature_c10) {
return get_resistance_log_fast_for_table(temperature_c10, active_config.table_type); return get_resistance_log_fast_for_table(temperature_c10, TABLE_DUCT);
}
// Упрощенная версия с линейным поиском
float get_resistance_fast_simple(int16_t temperature_c10) {
return get_resistance_log_fast_for_table(temperature_c10, active_config.table_type);
} }