/** ****************************************************************************** * @file adc_tools.c * @brief Модуль считывающий данные с АЦП ****************************************************************************** * @details ******************************************************************************/ #include "adc_tools.h" 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_upp(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; ADC_ResetStatistics(adc, ChNumb); return HAL_OK; } /** * @brief Запуск АЦП. * @param adc Указатель на кастомный хендл АЦП * @param Period Период таймера с какой частотой будет работать АЦП * @return HAL Status. * @details Запускает АЦП с частотой дискретизации на которую настроен таймер adc_tim. */ HAL_StatusTypeDef ADC_Start(ADC_Periodic_t *adc, float PeriodUs) { HAL_StatusTypeDef res; if(assert_upp(adc)) return HAL_ERROR; if(PeriodUs == 0) return HAL_ERROR; // Остановить перед перенастройкой HAL_TIM_Base_Stop(adc->htim); // Запускаем таймер который будет запускать опрос АЦП с заданным периодом __HAL_TIM_SET_AUTORELOAD(adc->htim, TIM_MicrosToTick(PeriodUs, ADC_TIM8_FREQ_MZH)); 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_upp(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_upp(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_CHANNELS; i++) // { // if(Filter_isEnable(&adc->filter[i])) // { // // заменяем данные на отфильтрованные данные // data[i] = Filter_Process(&adc->filter[i], data[i]); // } // } // Перерасчеты Напряжений/Токов в единицы измерения 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); } // Преобразования температуры по таблице 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]); } if(Filter_isDataReady(&adc->filter[0])) 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_upp(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 += fabsf(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)); } }