1.В Keil добавлен таргет для тестирования прошивки на STM32F417 2. Матлаб все еще не до конца понятно как имено построить модель чтобы токи не подлетали от малейшего открытия тиристоров
247 lines
8.1 KiB
C
247 lines
8.1 KiB
C
/**
|
||
******************************************************************************
|
||
* @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));
|
||
}
|
||
}
|