UPP/UPP/Core/PowerMonitor/adc_tools.c
Razvalyaev 2cdcebeffa Куча всего.
Добавлена интерполяция по таблице датчиков
Структурирован проект в матлаб
2025-11-14 18:03:44 +03:00

274 lines
8.6 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.

/**
******************************************************************************
* @file adc_tools.c
* @brief Модуль считывающий данные с АЦП
******************************************************************************
* @details
******************************************************************************/
#include "adc_tools.h"
//Полосовой фильтр 45-55 Гц
static float coefs_biquad_U[5] = {
0.000010f, // b0
0.000020f, // b1
0.000010f, // b2
-1.900000f, // a1
0.950000f // a2
};
// ФНЧ 100 Гц
static float coefs_biquad_I[5] = {
0.000010f, // b0
0.000020f, // b1
0.000010f, // b2
-1.900000f, // a1
0.950000f // a2
};
// ФНЧ 10 Гц
static float coefs_biquad_T[5] = {
0.0002f, // b0
0.0004f, // b1
0.0002f, // b2
-1.98f, // a1
0.980f // a2
};
// Проверка корректности структуры АЦП
#define assert_adc(_adc_) check_null_ptr_2(_adc_, (_adc_)->f.Initialized)
static void ADC_EnableAllFilters(ADC_Periodic_t *adc)
{
for(int i = 0; i < ADC_NUMB_OF_CHANNELS; i++)
{
Filter_Start(&adc->filter[i]);
}
}
static void ADC_InitAllFilters(ADC_Periodic_t *adc)
{
// Filter_Init(&adc->filter[ADC_CHANNEL_UBA], coefs_biquad_U);
// Filter_Init(&adc->filter[ADC_CHANNEL_UAC], coefs_biquad_U);
// Filter_Init(&adc->filter[ADC_CHANNEL_IC], coefs_biquad_I);
// Filter_Init(&adc->filter[ADC_CHANNEL_IA], coefs_biquad_I);
// Filter_Init(&adc->filter[ADC_CHANNEL_TEMP1], coefs_biquad_T);
// Filter_Init(&adc->filter[ADC_CHANNEL_TEMP2], coefs_biquad_T);
for(int i = 0; i < ADC_NUMB_OF_CHANNELS; i++)
{
Filter_Init(&adc->filter[i], Filter_Initializator);
}
FilterLUT_Init(&adc->temp_map[0],
(float *)adc_temp_quants,
(float *)adc_temp_vals,
numbof(adc_temp_quants), 1);
FilterLUT_Init(&adc->temp_map[1],
(float *)adc_temp_quants,
(float *)adc_temp_vals,
numbof(adc_temp_quants), 1);
}
/**
* @brief Инициализация периодического АЦП.
* @param adc Указатель на кастомный хендл АЦП
* @param htim Указатель на HAL хендл таймера
* @param hadc Указатель на HAL хендл АЦП
* @return HAL Status.
*/
HAL_StatusTypeDef ADC_Init(ADC_Periodic_t *adc, TIM_HandleTypeDef *htim, ADC_HandleTypeDef *hadc)
{
HAL_StatusTypeDef res;
if(check_null_ptr_2(htim, hadc))
return HAL_ERROR;
adc->htim = htim;
adc->hadc = hadc;
ADC_InitAllFilters(adc);
adc->f.AdcRunning = 0;
adc->f.DataReady = 0;
adc->f.Initialized = 1;
return HAL_OK;
}
/**
* @brief Конфигуарция канала АЦП.
* @param adc Указатель на кастомный хендл АЦП
* @param ChNumb Номер канала для конфигурации
* @param levelZero Нулевой уровень (в квантах АЦП)
* @param valueMax Максимальный уровень Единиц Измерения (в Вольтах/Амперах/Градусах)
* @param levelMax Максимальный уровень АЦП (в квантах АЦП)
* @return HAL Status.
*/
HAL_StatusTypeDef ADC_ConfigChannel(ADC_Periodic_t *adc, int ChNumb, uint16_t levelZero, float valueMax, uint16_t levelMax)
{
HAL_StatusTypeDef res;
if(assert_adc(adc))
return HAL_ERROR;
if((valueMax == 0) || (levelMax == 0))
return HAL_ERROR;
adc->Coefs[ChNumb].lMax = levelMax;
adc->Coefs[ChNumb].vMax = valueMax;
adc->Coefs[ChNumb].lZero = levelZero;
return HAL_OK;
}
/**
* @brief Запуск АЦП.
* @param adc Указатель на кастомный хендл АЦП
* @param Period Период таймера с какой частотой будет работать АЦП
* @return HAL Status.
* @details Запускает АЦП с частотой дискретизации на которую настроен таймер adc_tim.
*/
HAL_StatusTypeDef ADC_Start(ADC_Periodic_t *adc, uint16_t Period)
{
HAL_StatusTypeDef res;
if(assert_adc(adc))
return HAL_ERROR;
if(Period == 0)
return HAL_ERROR;
// Запускаем таймер который будет запускать опрос АЦП с заданным периодом
__HAL_TIM_SET_AUTORELOAD(adc->htim, Period);
res = HAL_TIM_Base_Start(adc->htim);
if(res != HAL_OK)
{
return res;
}
// Запускаем АЦП который будет перекидывать данные в ADC_DMA_Buffer
res = HAL_ADC_Start_DMA(adc->hadc, (uint32_t*)adc->RawData, 6); // Затем АЦП с DMA
if(res != HAL_OK)
{
return res;
}
ADC_EnableAllFilters(adc);
Filter_Start(&adc->temp_map[0]);
Filter_Start(&adc->temp_map[1]);
return res;
}
/**
* @brief Остановка АЦП .
* @param adc Указатель на кастомный хендл АЦП
* @return HAL Status.
* @details По факту остановка таймера, который запускает АЦП. Сам АЦП продолжает работу.
*/
HAL_StatusTypeDef ADC_Stop(ADC_Periodic_t *adc)
{
if(assert_adc(adc))
return HAL_ERROR;
// Запускаем таймер который будет запускать опрос АЦП
return HAL_TIM_Base_Stop(adc->htim);
}
/**
* @brief Обработка АЦП.
* @return HAL Status.
* @details По факту остановка таймера, который запускает АЦП. Сам АЦП продолжает работу.
* @note Вызывается в DMA2_Stream0_IRQHandler() для обработки всего, что пришло по DMA.
*/
HAL_StatusTypeDef ADC_Handle(ADC_Periodic_t *adc)
{
if(assert_adc(adc))
return HAL_ERROR;
ADC_Coefs_t *coefs = adc->Coefs;
uint16_t *raw = adc->RawData;
float *data = adc->Data;
// Перерасчеты Напряжений/Токов в единицы измерения
for(int i = 0; i < ADC_NUMB_OF_REGULAR_CHANNELS; i++)
{
ADC_Coefs_t *coefs = &adc->Coefs[i];
data[i] = ((float)(raw[i])-coefs->lZero) * coefs->vMax / (coefs->lMax-coefs->lZero);
ADC_UpdateStatistics(adc, i, ADC_LEVEL_AC);
}
// Фильтрация от шумов для всех каналов
for(int i = 0; i < ADC_NUMB_OF_CHANNELS; i++)
{
if(Filter_isEnable(&adc->filter[i]))
{
// заменяем данные на отфильтрованные данные
data[i] = Filter_Process(&adc->filter[i], data[i]);
}
}
// Преобразования температуры по таблице
for (int i = ADC_TEMP_CHANNELS_START; i < ADC_NUMB_OF_CHANNELS; i++)
{
data[i] = Filter_Process(&adc->temp_map[i-ADC_TEMP_CHANNELS_START], raw[i]);
ADC_UpdateStatistics(adc, i, ADC_LEVEL_BASE);
}
adc->f.DataReady = 1;
return HAL_OK;
}
/**
* @brief Сбор статистики.
*/
void ADC_UpdateStatistics(ADC_Periodic_t *adc, uint8_t channel, ADC_StatLevel_t level)
{
if (level < ADC_LEVEL_BASE)
return;
if(assert_adc(adc))
return;
if (channel >= ADC_NUMB_OF_REGULAR_CHANNELS)
return;
ADC_Statistics *stat = &adc->Stat[channel];
float value = adc->Data[channel];
// Первая инициализация
if (stat->SampleCount == 0) {
stat->Max = value;
stat->Min = value;
stat->Sum = 0;
stat->SumSquares = 0;
}
// Обновление min/max
if (value > stat->Max) stat->Max = value;
if (value < stat->Min) stat->Min = value;
// если не выбраны характеристики переменного сигнала - уходим
if(level < ADC_LEVEL_AC)
{
return;
}
// Накопление для Avg/RMS
stat->Sum += value;
stat->SumSquares += value * value;
stat->SampleCount++;
// Расчет Avg/RMS (периодически или по запросу)
if (stat->SampleCount >= 4000) { // Пример: пересчет каждые 1000 samples
stat->Avg = stat->Sum / stat->SampleCount;
stat->RMS = sqrtf(stat->SumSquares / stat->SampleCount);
// Сброс накопителей
stat->Sum = 0;
stat->SumSquares = 0;
stat->SampleCount = 0;
}
}
/**
* @brief Сброс статистики.
*/
void ADC_ResetStatistics(ADC_Periodic_t *adc, uint8_t channel)
{
if (channel < ADC_NUMB_OF_REGULAR_CHANNELS) {
memset(&adc->Stat[channel], 0, sizeof(ADC_Statistics));
}
}