Init
This commit is contained in:
parent
cbd0e62ca2
commit
1075ecf4ff
|
|
@ -8,6 +8,25 @@
|
|||
#include <stdint.h>
|
||||
#include "stdbool.h"
|
||||
|
||||
// ==================== НАСТРАИВАЕМЫЕ ПАРАМЕТРЫ ====================
|
||||
#define ADC_MAX 4095.0f // 12-битный АЦП (0-4095)
|
||||
|
||||
// Используем целые числа для препроцессора (значения в милливольтах)
|
||||
#define VREF_MV_INT 5000 // Опорное напряжение АЦП в мВ (5.0V)
|
||||
#define VCC_DIVIDER_MV_INT 4960 // Напряжение питания делителя в мВ (4.96V)
|
||||
|
||||
// Для использования в коде - преобразуем в float
|
||||
#define VREF_MV ((float)VREF_MV_INT)
|
||||
#define VCC_DIVIDER_MV ((float)VCC_DIVIDER_MV_INT)
|
||||
|
||||
// Статическая проверка во время компиляции (работает с целыми числами)
|
||||
#if VCC_DIVIDER_MV_INT == VREF_MV_INT
|
||||
#define VCC_EQUALS_VREF 1
|
||||
#else
|
||||
#define VCC_EQUALS_VREF 0
|
||||
#endif
|
||||
// =================================================================
|
||||
|
||||
// Типы таблиц NTC
|
||||
typedef enum {
|
||||
TABLE_DUCT = 0, // Таблица из документа KST45 (Duct)
|
||||
|
|
@ -22,10 +41,6 @@ typedef enum {
|
|||
ALG_LINEAR = 2
|
||||
} eAlg;
|
||||
|
||||
// Константы
|
||||
#define ADC_MAX 4095.0f // 12-битный АЦП
|
||||
#define VREF_MV 5000.0f // Опорное напряжение в милливольтах (5.0V)
|
||||
|
||||
// Параметры Steinhart-Hart для термистора (общие для всех таблиц)
|
||||
#define koef_A 0.001741624168166423
|
||||
#define koef_B 0.00017003940268680147
|
||||
|
|
@ -42,7 +57,9 @@ typedef struct {
|
|||
uint16_t adc_value; // Значение АЦП
|
||||
int16_t temp_c; // Температура в °C * 10 (для фиксированной точки)
|
||||
float resistance_ohm; // Сопротивление в Ом, соответствующее значению АЦП
|
||||
uint16_t voltage_mv; // Напряжение в милливольтах (ADC * VREF / ADC_MAX)
|
||||
uint16_t voltage_mv; // Напряжение на NTC в милливольтах
|
||||
float v_ntc_mv; // Точное напряжение на NTC
|
||||
float v_r1_mv; // Напряжение на балластном резисторе
|
||||
eNtcTable table_type; // Тип таблицы, для которой вычислены данные
|
||||
} adc_temp_lookup;
|
||||
|
||||
|
|
@ -50,6 +67,8 @@ typedef struct {
|
|||
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; // Размер таблицы
|
||||
|
|
@ -66,6 +85,8 @@ typedef struct {
|
|||
float duct_r1;
|
||||
float incar_r1;
|
||||
float ambient_r1;
|
||||
float vcc_mv; // Общее Vcc для всех таблиц
|
||||
float vref_mv; // Общее Vref для всех таблиц
|
||||
} fast_lookup_tables_t;
|
||||
|
||||
// Глобальная структура для доступа к таблицам
|
||||
|
|
@ -81,9 +102,19 @@ 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);
|
||||
|
||||
// Вспомогательная функция для преобразования ADC в напряжение
|
||||
// Вспомогательные функции для преобразований
|
||||
static inline uint16_t adc_to_voltage_mv(uint16_t adc_value) {
|
||||
return (uint16_t)(((uint32_t)adc_value * VREF_MV) / (uint32_t)ADC_MAX);
|
||||
return (uint16_t)(((uint32_t)adc_value * (uint32_t)VREF_MV_INT) / (uint32_t)ADC_MAX);
|
||||
}
|
||||
|
||||
// Функция для получения напряжения на NTC с учетом VCC и VREF
|
||||
static inline float get_ntc_voltage_mv(uint16_t adc_value) {
|
||||
return (adc_value * VREF_MV) / ADC_MAX;
|
||||
}
|
||||
|
||||
// Функция для получения напряжения на балластном резисторе
|
||||
static inline float get_r1_voltage_mv(uint16_t adc_value) {
|
||||
return VCC_DIVIDER_MV - get_ntc_voltage_mv(adc_value);
|
||||
}
|
||||
|
||||
// Основные функции получения температуры
|
||||
|
|
@ -100,8 +131,16 @@ 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_fast_simple(int16_t temperature_c10);
|
||||
|
||||
// Новые функции для получения напряжения
|
||||
// Новые функции для получения напряжений
|
||||
uint16_t get_voltage_from_adc(uint16_t adc_value);
|
||||
uint16_t get_voltage_fast_for_table(uint16_t adc_value, eNtcTable table_type);
|
||||
float get_ntc_voltage_fast(uint16_t adc_value);
|
||||
float get_r1_voltage_fast(uint16_t adc_value);
|
||||
|
||||
// Функция для получения информации о конфигурации VCC/VREF
|
||||
void get_vcc_vref_info(float* vcc_mv, float* vref_mv);
|
||||
float get_vcc_divider_voltage(void);
|
||||
float get_vref_voltage(void);
|
||||
bool is_vcc_equal_to_vref(void);
|
||||
|
||||
#endif //MDF_ADC_TEMP_KST45_14_2_H
|
||||
315
APP/main.c
315
APP/main.c
|
|
@ -1,32 +1,130 @@
|
|||
#include <stdio.h>
|
||||
#include "ADC_Temp.h"
|
||||
|
||||
// Функция для сохранения таблицы в файл
|
||||
void save_table_to_file(const char* filename, const adc_temp_lookup* table, const char* table_name) {
|
||||
// Упрощенная структура для хранения записи таблицы
|
||||
typedef struct {
|
||||
uint16_t adc_value; // Значение АЦП
|
||||
int16_t temp_c; // Температура в °C * 10
|
||||
float resistance_ohm; // Сопротивление в Ом
|
||||
} adc_temp_lookup_entry_t;
|
||||
|
||||
// Функция для сохранения таблицы в 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, "=== Таблица быстрого поиска: %s ===\n", table_name);
|
||||
fprintf(file, "Всего записей: %d\n", TABLE_SIZE_LOOKUP);
|
||||
fprintf(file, "%-8s %-12s %-15s %-10s\n", "ADC", "Temp (°C)", "Resistance (Ω)", "Voltage (mV)");
|
||||
fprintf(file, "--------------------------------------------------------\n");
|
||||
// Заголовок файла
|
||||
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++) {
|
||||
fprintf(file, "%-8u %-12.1f %-15.2f %-10u\n",
|
||||
table[i].adc_value,
|
||||
table[i].temp_c / 10.0f,
|
||||
table[i].resistance_ohm,
|
||||
table[i].voltage_mv);
|
||||
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 формате
|
||||
// Функция для сохранения таблицы в 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) {
|
||||
|
|
@ -35,83 +133,174 @@ void save_table_to_csv(const char* filename, const adc_temp_lookup* table, const
|
|||
}
|
||||
|
||||
fprintf(file, "# Таблица быстрого поиска: %s\n", table_name);
|
||||
fprintf(file, "ADC,Temperature_C,Resistance_Ohm,Voltage_mV\n");
|
||||
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,%.2f,%u\n",
|
||||
fprintf(file, "%u,%.1f,%d,%.2f\n",
|
||||
table[i].adc_value,
|
||||
table[i].temp_c / 10.0f,
|
||||
table[i].resistance_ohm,
|
||||
table[i].voltage_mv);
|
||||
table[i].temp_c,
|
||||
table[i].resistance_ohm);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
printf("Таблица '%s' сохранена в CSV файл: %s\n", table_name, filename);
|
||||
}
|
||||
|
||||
// Функция для сохранения всех таблиц
|
||||
void save_all_tables(void) {
|
||||
// Функция для сохранения таблицы в 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();
|
||||
|
||||
// Сохраняем в текстовом формате
|
||||
save_table_to_file("duct_table.txt", tables->duct, "DUCT");
|
||||
save_table_to_file("incar_table.txt", tables->incar, "INCAR");
|
||||
save_table_to_file("ambient_table.txt", tables->ambient, "AMBIENT");
|
||||
printf("\n=== Сохранение таблиц в файлы ===\n");
|
||||
|
||||
// Сохраняем в CSV формате (удобно для Excel)
|
||||
// Сохраняем 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");
|
||||
|
||||
// Сохраняем 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() {
|
||||
// Инициализируем все три таблицы с разными значениями R1
|
||||
init_all_tables(3000.0f, // R1 для DUCT (KST45)
|
||||
20000.0f, // R1 для INCAR
|
||||
20000.0f, // R1 для AMBIENT (NTC 10k)
|
||||
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);
|
||||
|
||||
uint16_t adc_value = 2000;
|
||||
printf("Таблицы успешно инициализированы\n");
|
||||
|
||||
printf("\n=== Работа с тремя датчиками одновременно ===\n");
|
||||
|
||||
// Получаем температуру и напряжение для DUCT (KST45)
|
||||
int16_t temp_duct = get_temperature_log_fast_for_table(adc_value, TABLE_DUCT);
|
||||
uint16_t volt_duct = get_voltage_fast_for_table(adc_value, TABLE_DUCT);
|
||||
printf("DUCT (KST45): ADC=%u, Temp=%.2f °C, Voltage=%u mV\n",
|
||||
adc_value, temp_duct / 10.0f, volt_duct);
|
||||
|
||||
// Получаем температуру и напряжение для INCAR
|
||||
int16_t temp_incar = get_temperature_log_fast_for_table(adc_value, TABLE_INCAR);
|
||||
uint16_t volt_incar = get_voltage_fast_for_table(adc_value, TABLE_INCAR);
|
||||
printf("INCAR: ADC=%u, Temp=%.2f °C, Voltage=%u mV\n",
|
||||
adc_value, temp_incar / 10.0f, volt_incar);
|
||||
|
||||
// Получаем температуру и напряжение для AMBIENT (NTC 10k)
|
||||
int16_t temp_ambient = get_temperature_log_fast_for_table(adc_value, TABLE_AMBIENT);
|
||||
uint16_t volt_ambient = get_voltage_fast_for_table(adc_value, TABLE_AMBIENT);
|
||||
printf("AMBIENT (NTC 10k): ADC=%u, Temp=%.2f °C, Voltage=%u mV\n",
|
||||
adc_value, temp_ambient / 10.0f, volt_ambient);
|
||||
|
||||
// Пример прямого вычисления напряжения
|
||||
uint16_t direct_voltage = get_voltage_from_adc(adc_value);
|
||||
printf("\nПрямое вычисление напряжения: ADC=%u -> %u mV\n", adc_value, direct_voltage);
|
||||
// Проверяем таблицы
|
||||
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);
|
||||
|
||||
// Сохраняем таблицы в файлы
|
||||
printf("\n=== Сохранение таблиц в файлы ===\n");
|
||||
save_all_tables();
|
||||
save_all_tables_to_files();
|
||||
|
||||
// Пример обратного преобразования (температура -> сопротивление)
|
||||
printf("\n=== Обратное преобразование ===\n");
|
||||
int16_t temp_test = 250; // 25.0 °C
|
||||
float resistance_duct = get_resistance_log_fast_for_table(temp_test, TABLE_DUCT);
|
||||
float resistance_incar = get_resistance_log_fast_for_table(temp_test, TABLE_INCAR);
|
||||
float resistance_ambient = get_resistance_log_fast_for_table(temp_test, TABLE_AMBIENT);
|
||||
// Пример использования
|
||||
printf("\n=== Пример использования ===\n");
|
||||
uint16_t test_adcs[] = {0, 500, 1000, 1500, 2000, 2500, 3000, 3500, 4000};
|
||||
|
||||
printf("При %.1f °C:\n", temp_test / 10.0f);
|
||||
printf(" DUCT сопротивление: %.2f Ω\n", resistance_duct);
|
||||
printf(" INCAR сопротивление: %.2f Ω\n", resistance_incar);
|
||||
printf(" AMBIENT сопротивление: %.2f Ω\n", resistance_ambient);
|
||||
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;
|
||||
}
|
||||
|
|
@ -134,18 +134,23 @@ fast_lookup_tables_t g_fast_tables = {
|
|||
.ambient_initialized = false,
|
||||
.duct_r1 = 3300.0f,
|
||||
.incar_r1 = 3300.0f,
|
||||
.ambient_r1 = 3300.0f
|
||||
.ambient_r1 = 3300.0f,
|
||||
.vcc_mv = VCC_DIVIDER_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) {
|
||||
const ntc_table_entry* table = NULL;
|
||||
|
|
@ -208,15 +213,62 @@ static bool is_table_initialized(eNtcTable table_type) {
|
|||
}
|
||||
}
|
||||
|
||||
// Функция расчёта сопротивления NTC из значения АЦП
|
||||
static float calculate_resistance(uint16_t adc_value, float r1) {
|
||||
// Расширенная функция расчёта сопротивления NTC с учетом VCC и VREF
|
||||
static float calculate_resistance_full(uint16_t adc_value, float r1, float vcc_mv, float vref_mv) {
|
||||
if (adc_value == 0) {
|
||||
return INFINITY; // Бесконечное сопротивление при 0 ADC (обрыв)
|
||||
}
|
||||
|
||||
// Защита от переполнения
|
||||
if (adc_value >= (uint16_t) (ADC_MAX - 1)) {
|
||||
return 0.0f; // Нулевое сопротивление при максимальном ADC (короткое замыкание)
|
||||
}
|
||||
|
||||
// Напряжение на NTC (измеренное АЦП)
|
||||
float v_ntc = (adc_value * vref_mv) / ADC_MAX;
|
||||
|
||||
// Напряжение на балластном резисторе
|
||||
float v_r1 = vcc_mv - v_ntc;
|
||||
|
||||
// Проверка на корректность
|
||||
if (v_r1 <= 0.0f) {
|
||||
return 0.0f; // Короткое замыкание или ошибка
|
||||
}
|
||||
|
||||
if (v_ntc <= 0.0f) {
|
||||
return INFINITY; // Обрыв
|
||||
}
|
||||
|
||||
// Сопротивление NTC (закон Ома)
|
||||
float r_ntc = r1 * (v_ntc / v_r1);
|
||||
|
||||
// Ограничиваем разумными пределами (1 Ом - 10 МОм)
|
||||
if (r_ntc < 1.0f) r_ntc = 1.0f;
|
||||
if (r_ntc > 10000000.0f) r_ntc = 10000000.0f;
|
||||
|
||||
return r_ntc;
|
||||
}
|
||||
|
||||
// Оптимизированная функция для случая VCC == VREF
|
||||
static float calculate_resistance_optimized(uint16_t adc_value, float r1) {
|
||||
if (adc_value == 0 || adc_value >= (uint16_t) ADC_MAX) {
|
||||
return 0.0f;
|
||||
}
|
||||
float R_ntc = r1 * (float) adc_value / (ADC_MAX - (float) adc_value);
|
||||
return R_ntc;
|
||||
return r1 * (float) adc_value / (ADC_MAX - (float) adc_value);
|
||||
}
|
||||
|
||||
// Главная функция расчета сопротивления (использует константу времени компиляции)
|
||||
static float calculate_resistance(uint16_t adc_value, float r1) {
|
||||
#if VCC_EQUALS_VREF == 1
|
||||
// Оптимизированный путь, когда напряжения равны
|
||||
return calculate_resistance_optimized(adc_value, r1);
|
||||
#else
|
||||
// Точный путь с учетом разных напряжений
|
||||
return calculate_resistance_full(adc_value, r1, VCC_DIVIDER_MV, VREF_MV);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Бинарный поиск в таблице
|
||||
static int find_interval_index(float resistance, const ntc_table_entry* table, uint16_t table_size) {
|
||||
int left = 0;
|
||||
|
|
@ -315,7 +367,7 @@ static float interpolate_steinhart_full(float resistance, int index, const ntc_t
|
|||
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);
|
||||
|
||||
if (resistance <= 0.0f) {
|
||||
if (resistance <= 0.0f || isinf(resistance)) {
|
||||
return -273.15f; // Ошибка
|
||||
}
|
||||
|
||||
|
|
@ -361,6 +413,16 @@ uint16_t get_voltage_from_adc(uint16_t adc_value) {
|
|||
return adc_to_voltage_mv(adc_value);
|
||||
}
|
||||
|
||||
// Функция для получения точного напряжения на NTC
|
||||
float get_ntc_voltage_fast(uint16_t adc_value) {
|
||||
return (adc_value * VREF_MV) / ADC_MAX;
|
||||
}
|
||||
|
||||
// Функция для получения напряжения на балластном резисторе
|
||||
float get_r1_voltage_fast(uint16_t adc_value) {
|
||||
return VCC_DIVIDER_MV - ((adc_value * VREF_MV) / ADC_MAX);
|
||||
}
|
||||
|
||||
// Функция для получения напряжения из таблицы быстрого доступа
|
||||
uint16_t get_voltage_fast_for_table(uint16_t adc_value, eNtcTable table_type) {
|
||||
if (!is_table_initialized(table_type)) {
|
||||
|
|
@ -399,7 +461,6 @@ uint16_t get_voltage_fast_for_table(uint16_t adc_value, eNtcTable table_type) {
|
|||
}
|
||||
}
|
||||
|
||||
// Инициализация таблицы быстрого поиска для конкретной таблицы
|
||||
void init_fast_lookup_table(eNtcTable table_type, float r1, eAlg use_alg) {
|
||||
uint16_t table_size;
|
||||
int16_t start_temp, end_temp;
|
||||
|
|
@ -415,93 +476,103 @@ void init_fast_lookup_table(eNtcTable table_type, float r1, eAlg use_alg) {
|
|||
g_fast_tables.ambient_r1 = r1;
|
||||
}
|
||||
|
||||
// Находим рабочий диапазон АЦП
|
||||
// Находим рабочий диапазон ADC
|
||||
uint16_t min_valid_adc = 0;
|
||||
uint16_t max_valid_adc = 0;
|
||||
uint16_t max_valid_adc = (uint16_t)(ADC_MAX - 1);
|
||||
float max_resistance = table[0].r_nom; // Максимальное сопротивление (мин. температура)
|
||||
float min_resistance = table[table_size - 1].r_nom; // Минимальное сопротивление (макс. температура)
|
||||
|
||||
// Значения для граничных точек
|
||||
float temp_at_min_adc = 0.0f;
|
||||
float temp_at_max_adc = 0.0f;
|
||||
float res_at_min_adc = 0.0f;
|
||||
float res_at_max_adc = 0.0f;
|
||||
|
||||
// Ищем минимальное АЦП, при котором температура >= start_temp (максимальная температура)
|
||||
// Ищем минимальное ADC, при котором сопротивление <= max_resistance
|
||||
for (uint16_t adc = 1; adc < ADC_MAX; adc++) {
|
||||
float resistance = calculate_resistance(adc, r1);
|
||||
float temp = get_temperature_from_adc_with_table(adc, use_alg, table_type, r1);
|
||||
|
||||
if (temp >= start_temp && temp <= end_temp) {
|
||||
if (resistance <= max_resistance && !isinf(resistance)) {
|
||||
min_valid_adc = adc;
|
||||
temp_at_min_adc = temp;
|
||||
res_at_min_adc = resistance;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Ищем максимальное АЦП, при котором температура <= end_temp (минимальная температура)
|
||||
// Ищем максимальное ADC, при котором сопротивление >= min_resistance
|
||||
for (uint16_t adc = (uint16_t)(ADC_MAX - 1); adc > 0; adc--) {
|
||||
float resistance = calculate_resistance(adc, r1);
|
||||
float temp = get_temperature_from_adc_with_table(adc, use_alg, table_type, r1);
|
||||
|
||||
if (temp >= start_temp && temp <= end_temp) {
|
||||
if (resistance >= min_resistance && resistance < INFINITY) {
|
||||
max_valid_adc = adc;
|
||||
temp_at_max_adc = temp;
|
||||
res_at_max_adc = resistance;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Заполняем таблицу для всего диапазона ADC от 0 до 4095
|
||||
printf("DEBUG: %s - min_valid_adc=%u, max_valid_adc=%u\n",
|
||||
table_type == TABLE_DUCT ? "DUCT" : (table_type == TABLE_INCAR ? "INCAR" : "AMBIENT"),
|
||||
min_valid_adc, max_valid_adc);
|
||||
|
||||
// Заполняем таблицу
|
||||
uint32_t adc_step = ((uint32_t)(ADC_MAX - 1) * 1000) / (TABLE_SIZE_LOOKUP - 1);
|
||||
uint32_t current_adc = 0;
|
||||
uint16_t prev_adc = 0;
|
||||
|
||||
for (uint16_t i = 0; i < TABLE_SIZE_LOOKUP; i++) {
|
||||
uint16_t adc;
|
||||
float resistance;
|
||||
float temp;
|
||||
|
||||
// Определяем ADC для текущей записи
|
||||
if (i == 0) {
|
||||
adc = 0;
|
||||
current_adc = 0;
|
||||
prev_adc = 0;
|
||||
} else if (i == TABLE_SIZE_LOOKUP - 1) {
|
||||
adc = (uint16_t)(ADC_MAX - 1);
|
||||
} else {
|
||||
current_adc += adc_step;
|
||||
adc = (uint16_t)(current_adc / 1000);
|
||||
|
||||
// Убеждаемся, что значения монотонно возрастают и нет дубликатов
|
||||
if (adc <= prev_adc) {
|
||||
adc = prev_adc + 1;
|
||||
if (adc <= prev_adc) adc = prev_adc + 1;
|
||||
if (adc >= (uint16_t)(ADC_MAX - 1)) adc = (uint16_t)(ADC_MAX - 2);
|
||||
}
|
||||
|
||||
// Убеждаемся, что не выходим за пределы
|
||||
if (adc >= (uint16_t)(ADC_MAX - 1)) {
|
||||
adc = (uint16_t)(ADC_MAX - 2);
|
||||
}
|
||||
}
|
||||
|
||||
float resistance;
|
||||
float temp;
|
||||
|
||||
// Определяем, находится ли ADC в рабочем диапазоне
|
||||
// Расчет сопротивления и температуры в зависимости от зоны
|
||||
if (adc <= min_valid_adc) {
|
||||
// Зона низких ADC (высокая температура) - используем граничное значение
|
||||
resistance = res_at_min_adc;
|
||||
temp = temp_at_min_adc;
|
||||
} else if (adc >= max_valid_adc) {
|
||||
// Зона высоких ADC (низкая температура) - используем граничное значение
|
||||
resistance = res_at_max_adc;
|
||||
temp = temp_at_max_adc;
|
||||
} else {
|
||||
// В рабочем диапазоне - вычисляем нормально
|
||||
// Зона низких ADC (высокая температура) - насыщение
|
||||
resistance = min_resistance;
|
||||
temp = (float)end_temp; // Максимальная температура (+85°C)
|
||||
}
|
||||
else if (adc >= max_valid_adc) {
|
||||
// Зона высоких ADC (низкая температура) - насыщение
|
||||
resistance = max_resistance;
|
||||
temp = (float)start_temp; // Минимальная температура (-40°C)
|
||||
}
|
||||
else {
|
||||
// Рабочая зона - нормальный расчет
|
||||
resistance = calculate_resistance(adc, r1);
|
||||
temp = get_temperature_from_adc_with_table(adc, use_alg, table_type, r1);
|
||||
|
||||
if (isinf(resistance) || resistance <= 0.0f) {
|
||||
temp = (float)start_temp - 10.0f;
|
||||
} else {
|
||||
int index = find_interval_index(resistance, table, table_size);
|
||||
|
||||
if (index < 0 || index >= table_size - 1) {
|
||||
if (resistance >= table[0].r_nom) {
|
||||
temp = (float)start_temp;
|
||||
} else if (resistance <= table[table_size - 1].r_nom) {
|
||||
temp = (float)end_temp;
|
||||
} else {
|
||||
temp = 25.0f;
|
||||
}
|
||||
} else {
|
||||
if (use_alg == ALG_STEINHART) {
|
||||
temp = interpolate_steinhart(resistance, index, table);
|
||||
} else if (use_alg == ALG_STEINHART_FULL) {
|
||||
temp = interpolate_steinhart_full(resistance, index, table);
|
||||
} else {
|
||||
temp = interpolate_log_linear(resistance, index, table);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Заполняем запись
|
||||
fast_table[i].adc_value = adc;
|
||||
fast_table[i].temp_c = (int16_t)(temp * 10.0f);
|
||||
fast_table[i].resistance_ohm = resistance;
|
||||
fast_table[i].voltage_mv = adc_to_voltage_mv(adc); // Добавляем напряжение
|
||||
fast_table[i].voltage_mv = adc_to_voltage_mv(adc);
|
||||
fast_table[i].v_ntc_mv = (adc * VREF_MV) / ADC_MAX;
|
||||
fast_table[i].v_r1_mv = VCC_DIVIDER_MV - fast_table[i].v_ntc_mv;
|
||||
fast_table[i].table_type = table_type;
|
||||
|
||||
prev_adc = adc;
|
||||
|
|
@ -543,6 +614,8 @@ void init_all_tables(float r1_duct, float r1_incar, float r1_ambient, eAlg use_a
|
|||
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);
|
||||
|
|
@ -559,6 +632,24 @@ const fast_lookup_tables_t* get_fast_tables(void) {
|
|||
return &g_fast_tables;
|
||||
}
|
||||
|
||||
// Функция для получения информации о конфигурации VCC/VREF
|
||||
void get_vcc_vref_info(float* vcc_mv, float* vref_mv) {
|
||||
if (vcc_mv) *vcc_mv = VCC_DIVIDER_MV;
|
||||
if (vref_mv) *vref_mv = VREF_MV;
|
||||
}
|
||||
|
||||
float get_vcc_divider_voltage(void) {
|
||||
return VCC_DIVIDER_MV;
|
||||
}
|
||||
|
||||
float get_vref_voltage(void) {
|
||||
return VREF_MV;
|
||||
}
|
||||
|
||||
bool is_vcc_equal_to_vref(void) {
|
||||
return (VCC_DIVIDER_MV_INT == VREF_MV_INT);
|
||||
}
|
||||
|
||||
// Быстрая функция для конкретной таблицы
|
||||
int16_t get_temperature_log_fast_for_table(uint16_t adc_value, eNtcTable table_type) {
|
||||
if (!is_table_initialized(table_type)) {
|
||||
|
|
@ -629,7 +720,8 @@ float get_resistance_log_fast_for_table(int16_t temperature_c10, eNtcTable table
|
|||
while (left <= right) {
|
||||
int mid = left + (right - left) / 2;
|
||||
|
||||
if (temperature_c10 <= fast_table[mid].temp_c &&
|
||||
if (mid < TABLE_SIZE_LOOKUP - 1 &&
|
||||
temperature_c10 <= fast_table[mid].temp_c &&
|
||||
temperature_c10 >= fast_table[mid + 1].temp_c) {
|
||||
// Нашли интервал
|
||||
int16_t temp_high = fast_table[mid].temp_c;
|
||||
|
|
|
|||
Loading…
Reference in New Issue