MODEL_ADC_EX/APP/main.c

330 lines
16 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <stdio.h>
#include "ADC_Temp.h"
// Упрощенная структура для хранения записи таблицы
typedef struct {
uint16_t adc_value; // Значение АЦП
int16_t temp_c; // Температура в °C * 10
float resistance_ohm; // Сопротивление в Ом
} adc_temp_lookup_entry_t;
// Функция для сохранения температур в одну строку через пробел (все значения)
void save_temperature_int_one_line(const char* filename, const adc_temp_lookup* table, const char* table_name) {
FILE* file = fopen(filename, "w");
if (file == NULL) {
printf("Ошибка: не удалось создать файл %s\n", filename);
return;
}
fprintf(file, "# Температуры для таблицы %s (одна строка, через пробел)\n", table_name);
fprintf(file, "# VCC=%.2fV, VREF=%.2fV\n", VCC_DIVIDER_MV / 1000.0f, VREF_MV / 1000.0f);
fprintf(file, "# Всего значений: %d\n", TABLE_SIZE_LOOKUP);
fprintf(file, "# Температура в °C * 10 (целые числа)\n\n");
for (int i = 0; i < TABLE_SIZE_LOOKUP; i++) {
if (i > 0) fprintf(file, " ");
fprintf(file, "%d", table[i].temp_c);
}
fprintf(file, "\n");
fclose(file);
printf("Температуры (int one-line) для '%s' сохранены в файл: %s\n", table_name, filename);
}
// Функция для сохранения таблицы в C файл в виде статического массива
void save_table_to_c_file(const char* filename, const adc_temp_lookup* table,
const char* table_name, const char* array_name) {
FILE* file = fopen(filename, "w");
if (file == NULL) {
printf("Ошибка: не удалось создать файл %s\n", filename);
return;
}
// Заголовок файла
fprintf(file, "//\n");
fprintf(file, "// Автоматически сгенерированный файл таблицы быстрого поиска: %s\n", table_name);
fprintf(file, "// VCC делителя = %.2f V\n", VCC_DIVIDER_MV / 1000.0f);
fprintf(file, "// VREF АЦП = %.2f V\n", VREF_MV / 1000.0f);
fprintf(file, "// Сопротивление R1 = %.0f Ом\n",
(table_name[0] == 'D' && table_name[1] == 'U') ? g_fast_tables.duct_r1 :
(table_name[0] == 'I') ? g_fast_tables.incar_r1 : g_fast_tables.ambient_r1);
fprintf(file, "// Всего записей: %d\n", TABLE_SIZE_LOOKUP);
fprintf(file, "//\n\n");
fprintf(file, "#include <stdint.h>\n\n");
// Структура для элемента таблицы
fprintf(file, "// Структура для хранения записи таблицы\n");
fprintf(file, "typedef struct {\n");
fprintf(file, " uint16_t adc_value; // Значение АЦП\n");
fprintf(file, " int16_t temp_c; // Температура в °C * 10\n");
fprintf(file, " float resistance_ohm; // Сопротивление в Ом\n");
fprintf(file, "} adc_temp_lookup_entry_t;\n\n");
// Объявление массива
fprintf(file, "static const adc_temp_lookup_entry_t %s[%d] = {\n",
array_name, TABLE_SIZE_LOOKUP);
// Вывод данных - по 4 элемента в строке для компактности
for (int i = 0; i < TABLE_SIZE_LOOKUP; i++) {
if (i % 4 == 0) {
fprintf(file, " ");
}
// Округляем сопротивление до 2 знаков после запятой
fprintf(file, "{%u, %d, %.2ff}",
table[i].adc_value,
table[i].temp_c,
table[i].resistance_ohm);
if (i < TABLE_SIZE_LOOKUP - 1) {
fprintf(file, ", ");
}
if ((i + 1) % 4 == 0) {
fprintf(file, "\n");
}
}
// Завершаем массив
if (TABLE_SIZE_LOOKUP % 4 != 0) {
fprintf(file, "\n");
}
fprintf(file, "};\n\n");
// Добавляем функцию для быстрого доступа по ADC
fprintf(file, "// Быстрый поиск температуры по ADC (прямая индексация)\n");
fprintf(file, "static inline int16_t %s_get_temp(uint16_t adc_value) {\n", array_name);
fprintf(file, " if (adc_value >= %d) return %s[%d].temp_c;\n",
TABLE_SIZE_LOOKUP, array_name, TABLE_SIZE_LOOKUP - 1);
fprintf(file, " return %s[adc_value].temp_c;\n", array_name);
fprintf(file, "}\n\n");
fprintf(file, "// Быстрый поиск сопротивления по ADC (прямая индексация)\n");
fprintf(file, "static inline float %s_get_resistance(uint16_t adc_value) {\n", array_name);
fprintf(file, " if (adc_value >= %d) return %s[%d].resistance_ohm;\n",
TABLE_SIZE_LOOKUP, array_name, TABLE_SIZE_LOOKUP - 1);
fprintf(file, " return %s[adc_value].resistance_ohm;\n", array_name);
fprintf(file, "}\n\n");
// Добавляем функцию для обратного поиска (температура -> сопротивление)
fprintf(file, "// Поиск сопротивления по температуре (бинарный поиск)\n");
fprintf(file, "static inline float %s_get_resistance_by_temp(int16_t temp_c10) {\n", array_name);
fprintf(file, " if (temp_c10 >= %s[0].temp_c) return %s[0].resistance_ohm;\n", array_name, array_name);
fprintf(file, " if (temp_c10 <= %s[%d].temp_c) return %s[%d].resistance_ohm;\n",
array_name, TABLE_SIZE_LOOKUP - 1, array_name, TABLE_SIZE_LOOKUP - 1);
fprintf(file, " \n");
fprintf(file, " // Бинарный поиск\n");
fprintf(file, " int left = 0, right = %d;\n", TABLE_SIZE_LOOKUP - 1);
fprintf(file, " while (left <= right) {\n");
fprintf(file, " int mid = left + (right - left) / 2;\n");
fprintf(file, " if (temp_c10 >= %s[mid].temp_c && temp_c10 <= %s[mid + 1].temp_c) {\n", array_name, array_name);
fprintf(file, " // Нашли интервал, интерполируем в логарифмическом масштабе\n");
fprintf(file, " int16_t t_high = %s[mid].temp_c;\n", array_name);
fprintf(file, " int16_t t_low = %s[mid + 1].temp_c;\n", array_name);
fprintf(file, " float r_high = %s[mid].resistance_ohm;\n", array_name);
fprintf(file, " float r_low = %s[mid + 1].resistance_ohm;\n", array_name);
fprintf(file, " \n");
fprintf(file, " if (t_high == t_low) return r_high;\n");
fprintf(file, " \n");
fprintf(file, " float log_r_high = logf(r_high);\n");
fprintf(file, " float log_r_low = logf(r_low);\n");
fprintf(file, " float log_r = log_r_high + (log_r_low - log_r_high) * \n");
fprintf(file, " (float)(t_high - temp_c10) / (float)(t_high - t_low);\n");
fprintf(file, " return expf(log_r);\n");
fprintf(file, " }\n");
fprintf(file, " \n");
fprintf(file, " if (temp_c10 > %s[mid].temp_c) {\n", array_name);
fprintf(file, " right = mid - 1;\n");
fprintf(file, " } else {\n");
fprintf(file, " left = mid + 1;\n");
fprintf(file, " }\n");
fprintf(file, " }\n");
fprintf(file, " return %s[%d].resistance_ohm;\n", array_name, TABLE_SIZE_LOOKUP - 1);
fprintf(file, "}\n");
fclose(file);
printf("Таблица '%s' сохранена в файл: %s\n", table_name, filename);
}
// Функция для сохранения таблицы в CSV формате (для Excel)
void save_table_to_csv(const char* filename, const adc_temp_lookup* table, const char* table_name) {
FILE* file = fopen(filename, "w");
if (file == NULL) {
printf("Ошибка: не удалось создать файл %s\n", filename);
return;
}
fprintf(file, "# Таблица быстрого поиска: %s\n", table_name);
fprintf(file, "# VCC_DIVIDER = %.2f V, VREF = %.2f V\n",
VCC_DIVIDER_MV / 1000.0f, VREF_MV / 1000.0f);
fprintf(file, "ADC,Temperature_C,Temperature_x10,Resistance_Ohm\n");
for (int i = 0; i < TABLE_SIZE_LOOKUP; i++) {
fprintf(file, "%u,%.1f,%d,%.2f\n",
table[i].adc_value,
table[i].temp_c / 10.0f,
table[i].temp_c,
table[i].resistance_ohm);
}
fclose(file);
printf("Таблица '%s' сохранена в CSV файл: %s\n", table_name, filename);
}
// Функция для сохранения таблицы в TXT формате (для просмотра)
void save_table_to_txt(const char* filename, const adc_temp_lookup* table, const char* table_name) {
FILE* file = fopen(filename, "w");
if (file == NULL) {
printf("Ошибка: не удалось создать файл %s\n", filename);
return;
}
fprintf(file, "=== Таблица быстрого поиска: %s ===\n", table_name);
fprintf(file, "VCC делителя = %.2f V\n", VCC_DIVIDER_MV / 1000.0f);
fprintf(file, "VREF АЦП = %.2f V\n", VREF_MV / 1000.0f);
fprintf(file, "Всего записей: %d\n", TABLE_SIZE_LOOKUP);
fprintf(file, "%-8s %-12s %-15s\n", "ADC", "Temp (°C)", "Resistance (Ω)");
fprintf(file, "---------------------------------------------\n");
// Выводим каждые 100 записей для компактности
for (int i = 0; i < TABLE_SIZE_LOOKUP; i++) {
fprintf(file, "%-8u %-12.1f %-15.2f\n",
table[i].adc_value,
table[i].temp_c / 10.0f,
table[i].resistance_ohm);
}
fclose(file);
printf("Таблица '%s' сохранена в TXT файл: %s\n", table_name, filename);
}
// Функция для сохранения всех таблиц во всех форматах
void save_all_tables_to_files(void) {
const fast_lookup_tables_t* tables = get_fast_tables();
printf("\n=== Сохранение таблиц в файлы ===\n");
// Сохраняем DUCT таблицу
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_txt("duct_table.txt", tables->duct, "DUCT");
// save_temperature_int_one_line("duct_table.txt", tables->duct, "DUCT");
// Сохраняем INCAR таблицу
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_txt("incar_table.txt", tables->incar, "INCAR");
// Сохраняем AMBIENT таблицу
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_txt("ambient_table.txt", tables->ambient, "AMBIENT");
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() {
printf("=== Генерация таблиц быстрого поиска для NTC термисторов ===\n");
printf("Конфигурация:\n");
printf(" VCC делителя = %.2f V\n", VCC_DIVIDER_MV / 1000.0f);
printf(" VREF АЦП = %.2f V\n", VREF_MV / 1000.0f);
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");
init_all_tables(3000.0f, // R1 для DUCT (KST45) - 3kΩ
20000.0f, // R1 для INCAR - 20kΩ
20000.0f, // R1 для AMBIENT (NTC 10k) - 20kΩ
ALG_STEINHART);
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();
// Пример использования
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");
printf(" - duct_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.txt, incar_table.txt, ambient_table.txt (для просмотра)\n");
return 0;
}