Init
This commit is contained in:
commit
10ee522b9b
|
|
@ -0,0 +1,20 @@
|
||||||
|
//
|
||||||
|
// Created by cfif on 04.12.2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef MDF_ADC_TEMP_KST45_14_2_H
|
||||||
|
#define MDF_ADC_TEMP_KST45_14_2_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ALG_STEINHART = 0,
|
||||||
|
ALG_STEINHART_FULL = 1,
|
||||||
|
ALG_LINEAR = 2
|
||||||
|
} eAlg;
|
||||||
|
|
||||||
|
void init_fast_lookup_table(eAlg use_alg);
|
||||||
|
float get_temperature_from_adc_KST45(uint16_t adc_value, eAlg alg);
|
||||||
|
int16_t get_temperature_fast_KST45(uint16_t adc_value);
|
||||||
|
|
||||||
|
#endif //MDF_ADC_TEMP_KST45_14_2_H
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
//
|
||||||
|
// Created by cfif on 04.12.2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef MDF_ATERLUX_H
|
||||||
|
#define MDF_ATERLUX_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
int16_t calc_temperature(uint16_t adcsum);
|
||||||
|
|
||||||
|
#endif //MDF_ATERLUX_H
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "ADC_Temp_KST45-14-2.h"
|
||||||
|
#include "aterlux.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
init_fast_lookup_table(ALG_STEINHART);
|
||||||
|
uint16_t value = 4095 / 2; // Должно быть 25 градусов. Это середина диапазона АЦП
|
||||||
|
|
||||||
|
float T_ALG_LINEAR = get_temperature_from_adc_KST45(value, ALG_LINEAR);
|
||||||
|
float T_ALG_STEINHART = get_temperature_from_adc_KST45(value, ALG_STEINHART);
|
||||||
|
float T_ALG_STEINHART_FULL = get_temperature_from_adc_KST45(value, ALG_STEINHART_FULL);
|
||||||
|
float T_FAST = get_temperature_fast_KST45(value);
|
||||||
|
float T_ATERLUX = calc_temperature(value);
|
||||||
|
|
||||||
|
printf("T_ALG_LINEAR = %f \n", T_ALG_LINEAR);
|
||||||
|
printf("T_ALG_STEINHART = %f \n", T_ALG_STEINHART);
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
|
||||||
|
"cmake": {
|
||||||
|
"inc_dirs": [
|
||||||
|
"./inc/"
|
||||||
|
],
|
||||||
|
"srcs": [
|
||||||
|
"./main.c",
|
||||||
|
"./src/*.c",
|
||||||
|
"./src/*.c"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,226 @@
|
||||||
|
//
|
||||||
|
// Created by cfif on 02.12.2025.
|
||||||
|
//
|
||||||
|
#include "stdint.h"
|
||||||
|
#include "ADC_Temp_KST45-14-2.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
// Константы
|
||||||
|
#define ADC_MAX 4095.0f // 12-битный АЦП
|
||||||
|
#define R1 3000.0f // Сопротивление делителя напряжения
|
||||||
|
#define TABLE_START_TEMP (-40)
|
||||||
|
#define TABLE_END_TEMP 85
|
||||||
|
#define TABLE_SIZE 26
|
||||||
|
|
||||||
|
// Параметры Steinhart-Hart для термистора
|
||||||
|
#define koef_A 0.001741624168166423
|
||||||
|
#define koef_B 0.00017003940268680147
|
||||||
|
#define koef_C 0.0000004890545443703666
|
||||||
|
|
||||||
|
#define TABLE_SIZE_LOOKUP 32
|
||||||
|
|
||||||
|
// Структура для хранения табличных данных
|
||||||
|
typedef struct {
|
||||||
|
int temp_c; // Температура (°C)
|
||||||
|
float r_nom; // Номинальное сопротивление (Ω)
|
||||||
|
} ntc_table_entry;
|
||||||
|
|
||||||
|
// Таблица из документа
|
||||||
|
static const ntc_table_entry ntc_table[] = {
|
||||||
|
{-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}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Функция расчёта сопротивления NTC из значения АЦП
|
||||||
|
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_MAX / adc_value - 1)
|
||||||
|
// float R_ntc = R1 * (ADC_MAX / (float)adc_value - 1.0f);
|
||||||
|
|
||||||
|
float R_ntc = R1 * (float) adc_value / (ADC_MAX - (float) adc_value);
|
||||||
|
|
||||||
|
return R_ntc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Бинарный поиск в таблице
|
||||||
|
static int find_interval_index(float resistance) {
|
||||||
|
int left = 0;
|
||||||
|
int right = TABLE_SIZE - 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 < TABLE_SIZE - 1 &&
|
||||||
|
resistance <= ntc_table[mid].r_nom &&
|
||||||
|
resistance >= ntc_table[mid + 1].r_nom) {
|
||||||
|
return mid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resistance > ntc_table[mid].r_nom) {
|
||||||
|
right = mid - 1;
|
||||||
|
} else {
|
||||||
|
left = mid + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1; // Не найден
|
||||||
|
}
|
||||||
|
|
||||||
|
// Интерполяция с использованием уравнения Стейнхарта-Харта между точками
|
||||||
|
static float interpolate_steinhart(float resistance, int index) {
|
||||||
|
// Берем две соседние точки из таблицы
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Используем уравнение Стейнхарта-Харта для вычисления температуры
|
||||||
|
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) {
|
||||||
|
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);
|
||||||
|
float log_r = logf(resistance);
|
||||||
|
|
||||||
|
return t1 + (t2 - t1) * (log_r - log_r1) / (log_r2 - log_r1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Более надежная версия с проверкой параметров
|
||||||
|
static float interpolate_steinhart_full(float resistance, int index) {
|
||||||
|
// Проверка корректности входных данных
|
||||||
|
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)ntc_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)ntc_table[index].temp_c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (float)(temp_K - 273.15f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Основная функция для получения температуры
|
||||||
|
float get_temperature_from_adc_KST45(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 >= TABLE_SIZE - 1) {
|
||||||
|
// Вне диапазона таблицы
|
||||||
|
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);
|
||||||
|
} else if (use_alg == ALG_STEINHART_FULL) {
|
||||||
|
return interpolate_steinhart_full(resistance, index);
|
||||||
|
} else {
|
||||||
|
return interpolate_log_linear(resistance, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Предварительно вычисленная таблица для быстрого доступа
|
||||||
|
typedef struct {
|
||||||
|
uint16_t adc_value; // Значение АЦП
|
||||||
|
int16_t temp_c; // Температура в °C * 10 (для фиксированной точки)
|
||||||
|
} adc_temp_lookup;
|
||||||
|
|
||||||
|
static adc_temp_lookup fast_lookup[TABLE_SIZE_LOOKUP]; // Таблица на TABLE_SIZE_LOOKUP значений
|
||||||
|
|
||||||
|
void init_fast_lookup_table(eAlg use_alg) {
|
||||||
|
// Создаем таблицу для быстрого преобразования АЦП->температура
|
||||||
|
for (uint16_t i = 0; i < TABLE_SIZE_LOOKUP; i++) {
|
||||||
|
uint16_t adc = i * (uint8_t)roundf(4095.0f / (TABLE_SIZE_LOOKUP - 1)); // Для 12-битного АЦП (0-4095)
|
||||||
|
float temp = get_temperature_from_adc_KST45(adc, use_alg);
|
||||||
|
fast_lookup[i].adc_value = adc;
|
||||||
|
fast_lookup[i].temp_c = (int16_t) (temp * 10.0f); // Храним с точностью 0.1°C
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t get_temperature_fast_KST45(uint16_t adc_value) {
|
||||||
|
// Простой поиск в таблице с линейной интерполяцией
|
||||||
|
uint16_t index = adc_value / (uint8_t)roundf(4095.0f / (TABLE_SIZE_LOOKUP - 1)); // Делим на 16 для TABLE_SIZE_LOOKUP = 256
|
||||||
|
if (index >= (TABLE_SIZE_LOOKUP - 1)) return fast_lookup[TABLE_SIZE_LOOKUP - 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;
|
||||||
|
|
||||||
|
// Линейная интерполяция
|
||||||
|
return temp1 + ((temp2 - temp1) * (adc_value - adc1)) / (adc2 - adc1);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
//
|
||||||
|
// Created by cfif on 03.12.2025.
|
||||||
|
//
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Значение температуры, возвращаемое если сумма результатов АЦП больше первого значения таблицы
|
||||||
|
#define TEMPERATURE_UNDER (-400)
|
||||||
|
// Значение температуры, возвращаемое если сумма результатов АЦП меньше последнего значения таблицы
|
||||||
|
#define TEMPERATURE_OVER 800
|
||||||
|
// Значение температуры соответствующее первому значению таблицы
|
||||||
|
#define TEMPERATURE_TABLE_START (-400)
|
||||||
|
// Шаг таблицы
|
||||||
|
#define TEMPERATURE_TABLE_STEP 50
|
||||||
|
|
||||||
|
/* Таблица суммарного значения АЦП в зависимости от температуры. От большего значения к меньшему
|
||||||
|
Для построения таблицы использованы следующие параметры:
|
||||||
|
R1(T1): 3кОм(25°С)
|
||||||
|
Схема включения: A
|
||||||
|
Ra: 3кОм
|
||||||
|
Напряжения U0/Uref: 5В/5В
|
||||||
|
*/
|
||||||
|
const uint16_t termo_table[] = {
|
||||||
|
3978, 3934, 3877, 3804, 3713, 3602, 3469, 3313,
|
||||||
|
3136, 2939, 2726, 2503, 2275, 2048, 1828, 1618,
|
||||||
|
1424, 1246, 1085, 942, 816, 706, 611, 528,
|
||||||
|
458, 397
|
||||||
|
};
|
||||||
|
|
||||||
|
// Функция вычисляет значение температуры в десятых долях градусов Цельсия
|
||||||
|
// в зависимости от суммарного значения АЦП.
|
||||||
|
int16_t calc_temperature(uint16_t adcsum) {
|
||||||
|
uint8_t l = 0;
|
||||||
|
uint8_t r = (sizeof(termo_table) / sizeof(termo_table[0])) - 1;
|
||||||
|
uint16_t thigh = termo_table[r];
|
||||||
|
|
||||||
|
// Проверка выхода за пределы и граничных значений
|
||||||
|
if (adcsum <= thigh) {
|
||||||
|
#ifdef TEMPERATURE_UNDER
|
||||||
|
if (adcsum < thigh)
|
||||||
|
return TEMPERATURE_UNDER;
|
||||||
|
#endif
|
||||||
|
return TEMPERATURE_TABLE_STEP * r + TEMPERATURE_TABLE_START;
|
||||||
|
}
|
||||||
|
uint16_t tlow = termo_table[0];
|
||||||
|
if (adcsum >= tlow) {
|
||||||
|
#ifdef TEMPERATURE_OVER
|
||||||
|
if (adcsum > tlow)
|
||||||
|
return TEMPERATURE_OVER;
|
||||||
|
#endif
|
||||||
|
return TEMPERATURE_TABLE_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Двоичный поиск по таблице
|
||||||
|
while ((r - l) > 1) {
|
||||||
|
uint8_t m = (l + r) >> 1;
|
||||||
|
uint16_t mid = termo_table[m];
|
||||||
|
if (adcsum > mid) {
|
||||||
|
r = m;
|
||||||
|
} else {
|
||||||
|
l = m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint16_t vl = termo_table[l];
|
||||||
|
if (adcsum >= vl) {
|
||||||
|
return l * TEMPERATURE_TABLE_STEP + TEMPERATURE_TABLE_START;
|
||||||
|
}
|
||||||
|
uint16_t vr = termo_table[r];
|
||||||
|
uint16_t vd = vl - vr;
|
||||||
|
int16_t res = TEMPERATURE_TABLE_START + r * TEMPERATURE_TABLE_STEP;
|
||||||
|
if (vd) {
|
||||||
|
// Линейная интерполяция
|
||||||
|
res -= ((TEMPERATURE_TABLE_STEP * (int32_t)(adcsum - vr) + (vd >> 1)) / vd);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
@ -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(mdf ${SOURCES})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
function(add_sources FILE_LIST FILES_PATH)
|
||||||
|
file(GLOB_RECURSE ADD_FILES_LIST ${FILES_PATH})
|
||||||
|
list(APPEND ${FILE_LIST} ${ADD_FILES_LIST})
|
||||||
|
set(${FILE_LIST} ${${FILE_LIST}} PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
include_directories("APP/inc/")
|
||||||
|
|
||||||
|
add_sources(SOURCES "APP/main.c")
|
||||||
|
add_sources(SOURCES "APP/src/*.c")
|
||||||
|
add_sources(SOURCES "APP/src/*.c")
|
||||||
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"dep": [
|
||||||
|
{
|
||||||
|
"type": "local",
|
||||||
|
"dir": "APP"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cmake": {
|
||||||
|
"inc_dirs": [
|
||||||
|
],
|
||||||
|
"srcs": [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue