274 lines
8.6 KiB
C
274 lines
8.6 KiB
C
/**
|
||
******************************************************************************
|
||
* @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));
|
||
}
|
||
}
|