Compare commits

..

8 Commits

Author SHA1 Message Date
Razvalyaev
7286f33709 некоторый рефакторинг 2025-12-30 14:50:50 +03:00
Razvalyaev
9b4ccd63b0 добавлена литра по тиристорным регуляторам 2025-12-08 09:33:42 +03:00
Razvalyaev
67be0b2304 коэфи пида вновь приведены к диапзаону 0-6.5535, коэф Ki сделан независимым от dt 2025-12-07 18:12:58 +03:00
Razvalyaev
9234b4508b Токи были странными из-за неправильного определения перехода через ноль и преждевременного открытия тиристоров. Добавлен полосовой фильтр на напряжения, чтобы коммутации тиристоров не смещали переход через ноль
А также:
- общий rms ток считается через сумму rms фаз, а не по векторам
- в установку угла добавлен аргумент коррекции - пока только смщеение между фазными и линейными. Фильтр практически не смещает, но еще посмотрим
- коэф пид приведены к диапзону 0-1
- ПИД надо еще донастраивать. не нравится пока как он работает

И еще не были проверены тайминги. Мб это не будет успевать рассчитываться)))
2025-12-06 18:14:56 +03:00
Razvalyaev
1690cdcb93 триггер режим у второго шим таймера 2025-12-06 07:51:00 +03:00
Razvalyaev
2703f7efda Рефакторинг и фиксы
Вроде сделал управление для трехфазной сети без нулевого провода. В матлабе запускается, но токи странные и регулятор не доделан нормально
2025-12-05 18:36:38 +03:00
Razvalyaev
aa59f84fb7 рефакторинг и вроде бы понял как надо управлять импульсами
надо доделать и проверить
2025-12-04 15:01:55 +03:00
Razvalyaev
c0eea077d9 чет еще попытался но безуспешно 2025-12-02 22:16:06 +03:00
32 changed files with 1074 additions and 553 deletions

View File

@@ -6,6 +6,7 @@
#include "mcu_wrapper_conf.h"
#include "app_wrapper.h"
float dbg_err_limit = 0;
float dbg[16];
#define PIN_READ(_verbname_) (_verbname_##_GPIO_Port->ODR & (_verbname_##_Pin)) ? 1 : 0
@@ -50,7 +51,7 @@ void Write_UPP_Outputs(real_T* Buffer, int ind_port)
//pwm_wtf(upp.hpwm.AllPhases[PHASE_C_NEG].State, upp.hpwm.AllPhases[PHASE_C_POS].State, &pwm6_pin);
int err = PIN_READ(RDO1);
int work = PIN_READ(RDO2);
int ready = PIN_READ(RDO3);
int ready = upp.errors->common;
if (CEN_GPIO_Port->ODR & CEN_Pin)
{
@@ -165,6 +166,7 @@ void app_readInputs(const real_T* Buffer) {
MB_DATA.HoldRegs.pui_params.Tdelay = ReadInputArray(1, 8);
MB_DATA.HoldRegs.pui_params.Interlace = ReadInputArray(1, 9);
MB_INTERNAL.param.adc.ADC_Max[ADC_CHANNEL_UAC] = ReadInputArray(2, 0) * 10;
MB_INTERNAL.param.adc.ADC_Max[ADC_CHANNEL_UBA] = ReadInputArray(2, 0) * 10;
MB_INTERNAL.param.adc.ADC_Max[ADC_CHANNEL_IA] = ReadInputArray(2, 1) * 10;
@@ -176,7 +178,10 @@ void app_readInputs(const real_T* Buffer) {
MB_INTERNAL.param.angle.PID_Kp = ReadInputArray(2, 4) * 10000;
MB_INTERNAL.param.angle.PID_Ki = ReadInputArray(2, 5) * 10000;
MB_INTERNAL.param.angle.PID_Kd = ReadInputArray(2, 6) * 10000;
MB_INTERNAL.param.angle.PulseLengthReserve = ReadInputArray(2, 7);
MB_INTERNAL.param.angle.Angle_Max = ReadInputArray(2, 7)/180 * 65535;
MB_INTERNAL.param.angle.Angle_Min = ReadInputArray(2, 8)/180 * 65535;
MB_INTERNAL.param.pwm.PulseLength = ReadInputArray(2, 9)/180 * 65535;
}
// USER APP INPUT END

23
MATLAB/calc_pi.m Normal file
View File

@@ -0,0 +1,23 @@
clc, clear all
%% Ввод данных
Ku = 0.03; % Твой Ku
Tu = 0.0847; % Твой Tu
Ts = 0.0005; % Твой Ts
%% Расч
Kp = 0.45 * Ku;
Ti = 0.85 * Tu;
Ki_abs = Kp / Ti; % Абсолютное Ki
Ki_disc = Ki_abs * Ts; % Дискретное Ki (если программа делит на Ts)
%% Вывод
fprintf('Kp = %.3f\n', Kp);
fprintf('Ki абсолютное = %.3f\n', Ki_abs);
fprintf('Ki дискретное = %.6f (если программа делит на Ts)\n', Ki_disc);
%% Рекомендация (с запасом)
Kp_safe = Kp * 0.7;
Ki_safe = Ki_abs * 0.7;
fprintf('\nС запасом 30%%:\n');
fprintf('Kp = %.3f\n', Kp_safe);
fprintf('Ki = %.3f\n', Ki_safe);

View File

@@ -1,12 +1,14 @@
clear all
IadcMax = 50;
IadcMax = 200;%50;
VadcMax = 1216;
Ts = 5e-6;
Vnom = 400;
Inom = 15;
Inom = 30;%4.2;
Fnom = 50;
Temperature1 = 2.22; % 20 градусов
Temperature2 = 2.99; % 34 градусов
Temperature2 = 2.99; % 34 градусов

Binary file not shown.

View File

@@ -14,34 +14,60 @@
#define _UPP_CONFIG_H_
#include "stm32f4xx_hal.h"
// Проверка корректности структуры
#define assert_upp(_struct_) check_null_ptr_2(_struct_, (_struct_)->f.Initialized)
/* Дефайны для индексов */
/* Линейные напряжения */
#define U_AB 0
#define U_CA 1
#define U_BC 2
/* Токи фаз */
#define I_C 0
#define I_A 1
#define I_B 2
/* Температуры */
#define TEMP_1 0
#define TEMP_2 1
/**
* @addtogroup UPP_PARAMS_TEST Params for tests
* @ingroup UPP_CONFIG
* @brief Параметры для тестирования УПП. Отключение всякого и включение всяких специфичных режимов
* @{
*/
#define UPP_DISABLE_ERROR_BLOCK ///< Отключить блокировку УПП при ошибках
//#define UPP_SIMULATE_I ///< Симулировт токи (Iref/2) а не брать с АЦП
#define UPP_DISABLE_PROTECT_BOARDPOWER ///< Отключить проверки питания плат (+24, +5 В)
#define UPP_DISABLE_PROTECT_LOSS_PHASE ///< Отключить проверки на потерянные фазы
//#define UPP_ANGLE_COSINE ///< Расчет угла через acos, а не линейно
#define ZC_DISABLE_HYSTERESIS_DEBOUNCE ///< Отключить гиситерезис и дребезг на определении перехода через ноль
/** //UPP_PARAMS_TEST
* @}
*/
/**
* @addtogroup UPP_COMPILED_PARAMS Fixed params
* @ingroup UPP_CONFIG
* @brief Параметры устанавливаемые на этапе компиляции. Без перепрошивки их не поменять
* @{
*/
/* Периоды обновления всякого */
#define PM_FAST_PERIOD_US 25 ///< Период обновления быстрых расчетов в мкс (АЦП, пересечение нуля, ШИМ)
#define PM_SLOW_PERIOD_US 500 ///< Период обновления медленных расчетов в мкс (сглаженные и действующие значения, ПИД угла, ошибки)
#define PM_TEMP_SLOW_PERIOD_MS 1000 ///< Период обновлениия (фильтрации) датчиков температуры в мс
#define PM_F_SLOW_PERIOD_MS 40 ///< Период обновления (фильтрации) частоты в мс
#define UPP_INIT_BEFORE_READY_MS 2000 ///< Сколько сканировать сеть, перед выставлением состояния готовности
#define UPP_HALFWAVE_PERIOD 10 ///< Период полуволны. От него будет рассчитываться углы от 0 до 180 градусов
/* Частоты таймеров в МГц*/
#define PWM_TIM1_FREQ_MHZ 180 ///< Частота тактирования таймера ШИМ (1-4 каналы)
#define PWM_TIM8_FREQ_MHZ 180 ///< Частота тактирования таймера ШИМ (5-6 каналы)
#define ADC_TIM3_FREQ_MZH 90 ///< Частота тактирования таймера АЦП
#define ANGLE_TIM2_FREQ_MHZ 90 ///< Частота тактирования таймера для отсчета угла открытия тиристоров
#define US_TIM5_FREQ_MHZ 90 ///< Частота тактирования микросекундного таймера
/** //UPP_COMPILED_PARAMS
* @}
*/
/**
* @addtogroup UPP_PARAMS_DEFAULT Default params for external flash
* @ingroup UPP_CONFIG
* @brief Дефолтные параметры для внешней памяти. Они применятся по команде или по ошибке
* @details Префиксы
* @{
*/
@@ -88,7 +114,7 @@
/* Параметри мониторинга сети */
#define PM_RMS_WINDOW_PERIOD_US_DEFAULT 20000
#define PM_RMS_EXT_TAU_US_DEFAULT 0.2
#define PM_RMS_EXT_TAU_US_DEFAULT 0.02*3 // 3 периода 50 Гц
/* Параметры АЦП */
#define ADC_U_MAX_V_DEFAULT 1216.0
@@ -103,39 +129,13 @@
/* Параметры ШИМ для тиристоров */
#define PWM_THYR_FREQUENCY_HZ_DEFAULT 16000
#define PWM_THYR_DUTY_PERCENT_DEFAULT 0.5
#define PWM_THYR_PULSE_NUMBER_DEFAULT 10
#define PWM_THYR_PULSE_LENGTH_DEFAULT (60.0/180.0)
/** //UPP_PARAMS_DEFAULT
* @}
*/
/**
* @addtogroup UPP_COMPILED_PARAMS Fixed params
* @ingroup UPP_CONFIG
* @brief Параметры устанавливаемые на этапе компиляции. Без перепрошивки их не поменять
* @{
*/
/* Периоды обновления всякого */
#define PM_ADC_PERIOD_US 25 ///< Период опроса АЦП в мкс
#define PM_SLOW_PERIOD_US 500 ///< Период обновления медленных расчетов в мкс (чтобы делилось на @ref PM_ADC_PERIOD_US)
#define PM_TEMP_SLOW_PERIOD_MS 1000 ///< Период обновлениия (фильтрации) датчиков температуры в мс
#define PM_F_SLOW_PERIOD_MS 40 ///< Период обновления (фильтрации) частоты в мс
#define UPP_INIT_BEFORE_READY_MS 2000 ///< Сколько сканировать сеть, перед выставлением состояния готовности
/* Частоты таймеров в МГц*/
#define PWM_TIM1_FREQ_MHZ 180 ///< Частота тиков таймера ШИМ (1-4 каналы)
#define PWM_TIM8_FREQ_MHZ 180 ///< Частота тиков таймера ШИМ (5-6 каналы)
#define US_TIM5_FREQ_MHZ 90 ///< Частота тиков микросекундного таймера
#define ADC_TIM3_FREQ_MZH 90 ///< Частота тиков таймера АЦП
#define ANGLE_TIM2_FREQ_MHZ 90 ///< Частота тиков таймера отсчета угла открытия тиристоров
/** //UPP_COMPILED_PARAMS
* @}
*/
// ===== ОТЛАДОЧНЫЕ ШТУКИ ДЛЯ 417 ======

View File

@@ -149,14 +149,33 @@ typedef struct {
* @brief Дефайны УПП которые используютяс исключительно внутри программы
* @{
*/
/* Перерасчеты в тики */
#define PM_SLOW_PERIOD_CNT (PM_SLOW_PERIOD_US/PM_ADC_PERIOD_US) ///< Период обновления медленных расчетов тиках @ref PM_ADC_PERIOD_US
#define US_TO_SLOW_TICKS(_us_) ((_us_)/PM_SLOW_PERIOD_US)
#define MS_TO_SLOW_TICKS(_ms_) US_TO_SLOW_TICKS((_ms_)*1000)
#define PM_F_SLOW_PERIOD_CNT (MS_TO_SLOW_TICKS(PM_F_SLOW_PERIOD_MS)) ///< Период обновления частоты в тиках @ref PM_SLOW_PERIOD_CNT
// Проверка корректности структуры
#define assert_upp(_struct_) check_null_ptr_2(_struct_, (_struct_)->f.Initialized)
// Проверка корректности структуры и фазы
#define assert_upp_phase(_struct_, _phase_) (check_null_ptr_2(_struct_, (_struct_)->f.Initialized) || (_phase_ >= 3))
/* Дефайны для индексов */
/* Линейные напряжения */
#define U_AB 0
#define U_CA 1
#define U_BC 2
/* Токи фаз */
#define I_C 0
#define I_A 1
#define I_B 2
/* Температуры */
#define TEMP_1 0
#define TEMP_2 1
/* Перерасчеты в тики */
#define ANGLE_PERIOD_MS(_freq_) (((float)1/(_freq_*2))*1000) ///< Период обновления частоты в тиках @ref PM_SLOW_PERIOD_CNT
#define PM_SLOW_PERIOD_CNT (PM_SLOW_PERIOD_US/PM_FAST_PERIOD_US) ///< Период обновления медленных расчетов тиках @ref PM_FAST_PERIOD_US
#define US_TO_FAST_TICKS(_us_) ((_us_)/PM_FAST_PERIOD_US) ///< Пересчитать мкс в тики быстрых расчетов
#define MS_TO_FAST_TICKS(_ms_) US_TO_FAST_TICKS((_ms_)*1000) ///< Пересчитать мс в тики быстрых расчетов
#define US_TO_SLOW_TICKS(_us_) ((_us_)/PM_SLOW_PERIOD_US) ///< Пересчитать мкс в тики медленных расчетов
#define MS_TO_SLOW_TICKS(_ms_) US_TO_SLOW_TICKS((_ms_)*1000) ///< Пересчитать мс в тики медленных расчетов
#define PM_F_SLOW_PERIOD_CNT (MS_TO_SLOW_TICKS(PM_F_SLOW_PERIOD_MS)) ///< Период обновления частоты в тиках @ref PM_SLOW_PERIOD_CNT
#define SQRT2 1.4142135
@@ -182,8 +201,8 @@ typedef enum {
*/
typedef enum {
UPP_PHASE_A = 0,
UPP_PHASE_B = 1,
UPP_PHASE_C = 2,
UPP_PHASE_C = 1,
UPP_PHASE_B = 2,
UPP_PHASE_UNKNOWN = 3
} UPP_Phase_t;

View File

@@ -7,35 +7,49 @@
******************************************************************************/
#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++)
// FilterBandPassDerivative_Init(&adc->u_fltr[U_AB], (50.0f*PM_FAST_PERIOD_US/1000000), 0.1);
// FilterBandPassDerivative_Init(&adc->u_fltr[U_CA], (50.0f*PM_FAST_PERIOD_US/1000000), 0.1);
//
// FilterMedianInt_Init(&adc->i_fltr[I_C], 5, 2048);
// FilterMedianInt_Init(&adc->i_fltr[I_A], 5, 2048);
//
// FilterLUT_Init(&adc->temp_map[TEMP_1],
// (float *)adc_temp_quants,
// (float *)adc_temp_vals,
// numbof(adc_temp_quants), 1);
// FilterLUT_Init(&adc->temp_map[TEMP_2],
// (float *)adc_temp_quants,
// (float *)adc_temp_vals,
// numbof(adc_temp_quants), 1);
// Инициализация фильтров
for(int i = 0; i < 2; i++)
{
Filter_Init(&adc->filter[i], Filter_Initializator);
FilterBandPassDerivative_Init(&adc->u_fltr[i], (50.0f*PM_FAST_PERIOD_US/1000000), 0.1);
FilterMedianInt_Init(&adc->i_fltr[I_C], 5, 2048);
FilterLUT_Init(&adc->temp_map[i],
(float *)adc_temp_quants,
(float *)adc_temp_vals,
numbof(adc_temp_quants), 1);
}
// Запуск фильтров
for(int i = 0; i < 2; i++)
{
Filter_Start(&adc->u_fltr[i]);
Filter_Start(&adc->i_fltr[i]);
Filter_Start(&adc->temp_map[i]);
}
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);
}
__STATIC_FORCEINLINE void ADC_FilterRaw(ADC_Periodic_t *adc, int ch_start, int ch_end)
{
}
/**
* @brief Инициализация периодического АЦП.
* @param adc Указатель на кастомный хендл АЦП
@@ -110,17 +124,13 @@ HAL_StatusTypeDef ADC_Start(ADC_Periodic_t *adc, float PeriodUs)
{
return res;
}
// Запускаем АЦП который будет перекидывать данные в ADC_DMA_Buffer
res = HAL_ADC_Start_DMA(adc->hadc, (uint32_t*)adc->RawData, 6); // Затем АЦП с DMA
// Запускаем АЦП который будет перекидывать данные в DMA буфер RawData
res = HAL_ADC_Start_DMA(adc->hadc, (uint32_t*)adc->RawData, 6);
if(res != HAL_OK)
{
return res;
}
ADC_EnableAllFilters(adc);
Filter_Start(&adc->temp_map[0]);
Filter_Start(&adc->temp_map[1]);
return res;
}
/**
@@ -138,6 +148,39 @@ HAL_StatusTypeDef ADC_Stop(ADC_Periodic_t *adc)
return HAL_TIM_Base_Stop(adc->htim);
}
/**
* @brief Обновление напряжений АЦП.
* @return HAL Status.
*/
HAL_StatusTypeDef ADC_UpdateRegular(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 < 2; i++)
{
int u_ind = ADC_U_CHANNELS_START + i;
int i_ind = ADC_I_CHANNELS_START + i;
// заменяем сырые данные на отфильтрованные данные
raw[u_ind] = Filter_Process(&adc->u_fltr[i], raw[u_ind]);
raw[i_ind] = Filter_Process(&adc->i_fltr[i], raw[i_ind]);
}
// Перерасчеты Напряжений/Токов в единицы измерения
for(int i = ADC_U_CHANNELS_START; 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);
}
return HAL_OK;
}
/**
@@ -151,55 +194,12 @@ HAL_StatusTypeDef ADC_UpdateTemperatures(ADC_Periodic_t *adc)
float *data = adc->Data;
uint16_t *raw = adc->RawData;
// Фильтрация от импульсных шумов для каналов напряжения/токов
for(int i = ADC_TEMP_CHANNELS_START; i < ADC_NUMB_OF_CHANNELS; i++)
{
// заменяем сырые данные на отфильтрованные данные
raw[i] = Filter_Process(&adc->filter[i], raw[i]);
}
// Преобразования температуры по таблице
for (int i = ADC_TEMP_CHANNELS_START; i < ADC_NUMB_OF_CHANNELS; i++)
for (int i = ADC_TEMP_CHANNELS_START; i < ADC_TEMP_CHANNELS_END; i++)
{
data[i] = Filter_Process(&adc->temp_map[i-ADC_TEMP_CHANNELS_START], raw[i]);
}
return HAL_OK;
}
/**
* @brief Обработка АЦП.
* @return HAL Status.
* @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++)
{
// заменяем сырые данные на отфильтрованные данные
raw[i] = Filter_Process(&adc->filter[i], raw[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);
}
if(Filter_isDataReady(&adc->filter[0]))
adc->f.DataReady = 1;
return HAL_OK;
}

View File

@@ -18,12 +18,19 @@
#define ADC_CHANNEL_TEMP2 5
#define ADC_NUMB_OF_CHANNELS 6
#define ADC_NUMB_OF_U_CHANNELS 2
#define ADC_NUMB_OF_I_CHANNELS 2
#define ADC_NUMB_OF_T_CHANNELS 2
#define ADC_NUMB_OF_CHANNELS 6
#define ADC_NUMB_OF_U_CHANNELS 2
#define ADC_NUMB_OF_I_CHANNELS 2
#define ADC_NUMB_OF_T_CHANNELS 2
#define ADC_NUMB_OF_REGULAR_CHANNELS (ADC_NUMB_OF_U_CHANNELS+ADC_NUMB_OF_I_CHANNELS)
#define ADC_TEMP_CHANNELS_START ADC_NUMB_OF_REGULAR_CHANNELS
#define ADC_U_CHANNELS_START 0
#define ADC_U_CHANNELS_END 1
#define ADC_I_CHANNELS_START 2
#define ADC_I_CHANNELS_END 3
#define ADC_TEMP_CHANNELS_START 4
#define ADC_TEMP_CHANNELS_END 5
#define ADC_TEMPERATURES_QUANTS \
{ 2188, 2197, 2206, 2216, 2226, 2236, 2247, 2259, 2271, 2283, \
@@ -50,11 +57,7 @@
static const float adc_temp_vals[] = ADC_TEMPERATURES;
static const float adc_temp_quants[] = ADC_TEMPERATURES_QUANTS;
#define Filter_t FilterMedianInt_t
#define Filter_Init FilterMedianInt_Init
#define Filter_Initializator 5, 2048
/**
* @brief Коэфициенты канала АЦП для пересчета в единицы измерения
*/
@@ -100,9 +103,9 @@ typedef struct
uint16_t RawData[ADC_NUMB_OF_CHANNELS]; ///< Сырые значения АЦП
ADC_Coefs_t Coefs[ADC_NUMB_OF_REGULAR_CHANNELS]; ///< Коэффициенты @ref ADC_Coefs_t для регулярных каналов (не температуры)
Filter_t filter[ADC_NUMB_OF_CHANNELS]; ///< Фильтр от шумов АЦП
FilterLUT_t temp_map[2]; ///< Коррекция нелинейности датчиков температуры
FilterBandPassDerivative_t u_fltr[ADC_NUMB_OF_U_CHANNELS]; ///< Полосовой Фильтр Напряжений от шумов
FilterMedianInt_t i_fltr[ADC_NUMB_OF_I_CHANNELS]; ///< Медианный Фильтр Токов от шумов
FilterLUT_t temp_map[ADC_NUMB_OF_T_CHANNELS]; ///< Коррекция нелинейности датчиков температуры
float Data[ADC_NUMB_OF_CHANNELS]; ///< Пересчитанные значения АЦП (в Вольтах/Амперах)
@@ -128,6 +131,6 @@ HAL_StatusTypeDef ADC_Stop(ADC_Periodic_t *adc);
/* Обновление температур АЦП. */
HAL_StatusTypeDef ADC_UpdateTemperatures(ADC_Periodic_t *adc);
/* Обработка АЦП после получения данных. */
HAL_StatusTypeDef ADC_Handle(ADC_Periodic_t *adc);
HAL_StatusTypeDef ADC_UpdateRegular(ADC_Periodic_t *adc);
#endif //_ADC_TOOLS_H_

View File

@@ -28,36 +28,10 @@ HAL_StatusTypeDef PowerMonitor_Init(PowerMonitor_t *hpm)
/* Инициализация АЦП */
if(ADC_Init(&hpm->adc, &adc_tim, &hadc3) != HAL_OK)
return HAL_ERROR;
/* Инициализация каналов АЦП */
if(ADC_ConfigChannel(&hpm->adc, ADC_CHANNEL_UBA,
PARAM_INTERNAL.adc.ADC_Zero[ADC_CHANNEL_UBA],
u2f(PARAM_INTERNAL.adc.ADC_Max[ADC_CHANNEL_UBA], 10),
4095) != HAL_OK)
return HAL_ERROR;
if(ADC_ConfigChannel(&hpm->adc, ADC_CHANNEL_UAC,
PARAM_INTERNAL.adc.ADC_Zero[ADC_CHANNEL_UAC],
u2f(PARAM_INTERNAL.adc.ADC_Max[ADC_CHANNEL_UAC], 10),
4095) != HAL_OK)
return HAL_ERROR;
if(ADC_ConfigChannel(&hpm->adc, ADC_CHANNEL_IC,
PARAM_INTERNAL.adc.ADC_Zero[ADC_CHANNEL_IC],
u2f(PARAM_INTERNAL.adc.ADC_Max[ADC_CHANNEL_IC], 10),
4095) != HAL_OK)
return HAL_ERROR;
if(ADC_ConfigChannel(&hpm->adc, ADC_CHANNEL_IA,
PARAM_INTERNAL.adc.ADC_Zero[ADC_CHANNEL_IA],
u2f(PARAM_INTERNAL.adc.ADC_Max[ADC_CHANNEL_IA], 10),
4095) != HAL_OK)
return HAL_ERROR;
/* Инициализация алгоритма перехода через ноль */
if(ZC_Init(&hpm->zc, 3, u2f(PARAM_INTERNAL.zc.Hysteresis, 100), PARAM_INTERNAL.zc.DebouneCouner) != HAL_OK)
if(ZC_Init(&hpm->zc, 3, 0, 0) != HAL_OK)
return HAL_ERROR;
/* Инициализация каналов алгоритма перехода через ноль */
@@ -69,21 +43,6 @@ HAL_StatusTypeDef PowerMonitor_Init(PowerMonitor_t *hpm)
return HAL_ERROR;
/* Инициализация RMS фильтра медленного алга */
for(int i = 0; i < RMS_ALL; i++)
{
if(FilterRMS_Init(&hpm->rms[i], PARAM_INTERNAL.pm.rms_window_size))
return HAL_ERROR;
Filter_Start(&hpm->rms[i]);
}
/* Инициализация экпоненциального фильтра медленного алга */
for(int i = 0; i < RMS_EXP_ALL; i++)
{
if(FilterExp_Init(&hpm->rms_exp[i], u2f(PARAM_INTERNAL.pm.rms_exp_alpha, 65535)))
return HAL_ERROR;
Filter_Start(&hpm->rms_exp[i]);
}
/* Инициализация среднего фильтра медленного алга */
for(int i = 0; i < ADC_NUMB_OF_REGULAR_CHANNELS; i++)
{
@@ -108,6 +67,16 @@ HAL_StatusTypeDef PowerMonitor_Init(PowerMonitor_t *hpm)
Filter_Start(&hpm->avg[ADC_TEMP_CHANNELS_START+i]);
}
/* Инициализация фильтра для сглаживания синусоиды*/
for(int i = 0; i < 2; i++)
{
if(FilterBandPassDerivative_Init(&hpm->ufltr[i], (50.0f*PM_FAST_PERIOD_US/1000000), 0.1))
return HAL_ERROR;
Filter_Start(&hpm->ufltr[i]);
}
return HAL_OK;
}
@@ -116,14 +85,14 @@ HAL_StatusTypeDef PowerMonitor_Init(PowerMonitor_t *hpm)
/**
* @brief Запустить мониторинг сети.
* @param hpm Указатель на структуру мониторинга сети
* @details Запускает АЦП с периодом @ref PM_ADC_PERIOD_US
* @details Запускает АЦП с периодом @ref PM_FAST_PERIOD_US
*/
HAL_StatusTypeDef PowerMonitor_Start(PowerMonitor_t *hpm)
{
if(hpm == NULL)
return HAL_ERROR;
if(ADC_Start(&hpm->adc, PM_ADC_PERIOD_US) != HAL_OK)
if(ADC_Start(&hpm->adc, PM_FAST_PERIOD_US) != HAL_OK)
return HAL_ERROR;
return HAL_OK;
@@ -156,6 +125,8 @@ void PowerMonitor_SlowCalc(PowerMonitor_t *hpm)
/* Расчет всякого для трех фаз отдельно */
float fmean = 0; // средняя частота по трем фазам
float umean = 0; // средний напряжение по трем фазам
float imean = 0; // средний ток по трем фазам
float iphase_mean = 0; // средний ток каждой фазы
float uphase_mean = 0; // среднее напряжение каждой фазы
// Дополнительно посчитаем значения в реальных Вольтах/Амперах
@@ -164,23 +135,18 @@ void PowerMonitor_SlowCalc(PowerMonitor_t *hpm)
for(int i = 0; i < 3; i++)
{
/* Получение частоты фазы */
meas->final.F[i] = Filter_Process(&hpm->avg[AVG_FAB+i], ZC_GetFrequency(&hpm->zc, i));
meas->final.Offset[i] = ZC_GetOffset(&hpm->zc, i);
meas->final.F[i] = Filter_Process(&hpm->avg[AVG_F+i], ZC_GetFrequency(&hpm->zc, i));
// meas->final.Offset[i] = ZC_GetOffset(&hpm->zc, i);
fmean += meas->final.F[i];
/* Средниее напряжение фазы */
// uphase_mean = fabsf(meas->slow.U[i]);
// uphase_mean = Filter_Process(&hpm->rms[RMS_UAB+i], uphase_mean);
// meas->final.U[i] = uphase_mean*PI/2/SQRT2; /*PI/2 - получить амплитудное, SQRT2 - получить действующее */
uphase_mean = Filter_Process(&hpm->rms[RMS_U+i], meas->slow.U[i]);
meas->final.U[i] = Filter_Process(&hpm->rms_exp[RMS_U+i], uphase_mean);
/* Средний ток фазы */
// iphase_mean = fabsf(meas->slow.I[i]);
// iphase_mean = Filter_Process(&hpm->rms[RMS_IC+i], iphase_mean)*PI/2;
// meas->final.I[i] = iphase_mean*PI/2/SQRT2; /*PI/2 - получить амплитудное, SQRT2 - получить действующее */
iphase_mean = Filter_Process(&hpm->rms[RMS_I+i], meas->slow.I[i]);
meas->final.I[i] = Filter_Process(&hpm->rms_exp[RMS_I+i], iphase_mean);
imean += meas->final.I[i];
/* Реальные единицы измерения (Вольты/Амперы) */
meas->real.I[i] = meas->final.I[i]*i_base;
@@ -188,22 +154,24 @@ void PowerMonitor_SlowCalc(PowerMonitor_t *hpm)
}
/* Получение средней частоты по трем фазам */
meas->final.Fmean = fmean / 3;
/* Оределение сдвига фаз */
static uint32_t prev_tick_phase_a = 0;
if(prev_tick_phase_a != hpm->zc.Channel[0].PeriodStartTime)
{ // Определяем только когда начался новый период фазы A
prev_tick_phase_a = hpm->zc.Channel[0].PeriodStartTime;
meas->final.Phase[0] = 0;
meas->final.Phase[1] = ZC_GetPhaseShift(&hpm->zc, 0, 1);
meas->final.Phase[2] = ZC_GetPhaseShift(&hpm->zc, 0, 2);
}
// /* Оределение сдвига фаз */
// static uint32_t prev_tick_phase_a = 0;
// if(prev_tick_phase_a != hpm->zc.Channel[0].PeriodStartTime)
// { // Определяем только когда начался новый период фазы A
// prev_tick_phase_a = hpm->zc.Channel[0].PeriodStartTime;
// meas->final.Phase[0] = 0;
// meas->final.Phase[1] = ZC_GetPhaseShift(&hpm->zc, 0, 1);
// meas->final.Phase[2] = ZC_GetPhaseShift(&hpm->zc, 0, 2);
// }
/* Расчет амплитуд трехфазной сети */
float uamp = vector_abs_linear_calc(meas->slow.U[U_AB], meas->slow.U[U_CA])/SQRT2; /* SQRT2 - получить действующее */
float iamp = vector_abs_phase_calc(meas->slow.I[I_A], meas->slow.I[I_C])/SQRT2; /* SQRT2 - получить действующее */
meas->final.Uamp = Filter_Process(&hpm->rms_exp[RMS_EXP_U], uamp);;//Filter_Process(&hpm->rms[RMS_U], uamp);
meas->final.Iamp = Filter_Process(&hpm->rms_exp[RMS_EXP_I], iamp);;//Filter_Process(&hpm->rms[RMS_I], iamp);
// float uamp = vector_abs_linear_calc(meas->slow.U[U_AB], meas->slow.U[U_CA])/SQRT2; /* SQRT2 - получить действующее */
// float iamp = vector_abs_phase_calc(meas->slow.I[I_A], meas->slow.I[I_C]);
float uamp = umean / 3;
float iamp = imean / 3;
meas->final.Uamp = uamp;//Filter_Process(&hpm->rms_exp[RMS_EXP_U], uamp);
meas->final.Iamp = iamp;//Filter_Process(&hpm->rms_exp[RMS_EXP_I], iamp);
hpm->slow_cnt++;
@@ -219,16 +187,19 @@ void PowerMonitor_SlowCalc(PowerMonitor_t *hpm)
void PowerMonitor_FastCalc(PowerMonitor_t *hpm)
{
if(hpm == NULL)
return;
/* Считываем АЦП с пересчетами и медианой фильтрацией от выбросов */
ADC_Handle(&hpm->adc);
/* Заполняем Напряжения/Токи в о.е. */
return;
float u_base = u2f(PARAM_INTERNAL.nominal.U, 10);
float i_base = u2f(PARAM_INTERNAL.nominal.I, 10);
PowerMonitor_Measured_t *meas = &hpm->measured;
meas->fast.U[U_AB] = hpm->adc.Data[ADC_CHANNEL_UBA]/u_base;
meas->fast.U[U_CA] = hpm->adc.Data[ADC_CHANNEL_UAC]/u_base;
/* Считываем АЦП с пересчетами и медианой фильтрацией от выбросов */
ADC_UpdateRegular(&hpm->adc);
/* Заполняем Напряжения/Токи в о.е. */
float uba_fast = hpm->adc.Data[ADC_CHANNEL_UBA]/u_base;
float uac_fast = hpm->adc.Data[ADC_CHANNEL_UAC]/u_base;
meas->fast.U[U_AB] = Filter_Process(&hpm->ufltr[U_AB], uba_fast);
meas->fast.U[U_CA] = Filter_Process(&hpm->ufltr[U_CA], uac_fast);
meas->fast.U[U_BC] = U_BC_calc(meas->fast.U[U_AB], meas->fast.U[U_CA]);
meas->fast.I[I_C] = hpm->adc.Data[ADC_CHANNEL_IC]/i_base;

View File

@@ -105,6 +105,7 @@ typedef struct
PowerMonitor_Measured_t measured; ///< Измеренные/рассчитанные величины
FilterBandPassDerivative_t ufltr[2]; ///< Фильтры для сглаживаний напряжений в синусы
FilterRMS_t rms[RMS_ALL]; ///< Фильтры для расчета действующего значения Напряжения/Токов
FilterExp_t rms_exp[RMS_EXP_ALL]; ///< Фильтры для сглаживания действующего значения Напряжения/Токов +2 для результируюзих U, I
FilterAverage_t avg[AVG_ALL]; ///< Фильтры для сглаживания медленных величин АЦП

View File

@@ -103,6 +103,7 @@ void ZC_ProcessChannel(ZeroCross_Handle_t *zc, uint8_t channel, float value, uin
zc_ch->CurrentValue = value;
#ifndef ZC_DISABLE_HYSTERESIS_DEBOUNCE
// Фильтрация дребезга
if(zc_ch->DebounceCounter > 0)
{
@@ -144,6 +145,25 @@ void ZC_ProcessChannel(ZeroCross_Handle_t *zc, uint8_t channel, float value, uin
zc_detected = -1;
}
}
#else //ZC_DISABLE_HYSTERESIS_DEBOUNCE
// Детектирование rising edge (отрицательное -> положительное)
if ((zc_ch->LastValue < 0) &&
(value > 0))
{
if (zc_ch->EdgeType == ZC_RISING_EDGE || zc_ch->EdgeType == ZC_BOTH_EDGES) {
zc_detected = 1;
}
}
// Детектирование falling edge (положительное -> отрицательное)
else if ((zc_ch->LastValue > 0) &&
(value < 0))
{
if (zc_ch->EdgeType == ZC_FALLING_EDGE || zc_ch->EdgeType == ZC_BOTH_EDGES) {
zc_detected = -1;
}
}
#endif //ZC_DISABLE_HYSTERESIS_DEBOUNCE
if(zc_detected)
{
@@ -172,11 +192,15 @@ void ZC_ProcessChannel(ZeroCross_Handle_t *zc, uint8_t channel, float value, uin
zc_ch->CrossCount++;
}
#ifndef ZC_DISABLE_HYSTERESIS_DEBOUNCE
// Сохраняем текущее значение для следующей итерации в случае если оно не в мертвой зоне
if((value > zc->Config.Hysteresis) || (value < -zc->Config.Hysteresis))
{
zc_ch->LastValue = value;
}
#else //ZC_DISABLE_HYSTERESIS_DEBOUNCE
zc_ch->LastValue = value;
#endif //ZC_DISABLE_HYSTERESIS_DEBOUNCE
}
/**

View File

@@ -87,7 +87,7 @@ int main(void)
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
#ifndef MATLAB

View File

@@ -214,7 +214,7 @@ void TIM1_UP_TIM10_IRQHandler(void)
/* USER CODE END TIM1_UP_TIM10_IRQn 0 */
HAL_TIM_IRQHandler(&htim1);
/* USER CODE BEGIN TIM1_UP_TIM10_IRQn 1 */
UPP_PWM_Handle();
//UPP_PWM_Handle();
/* USER CODE END TIM1_UP_TIM10_IRQn 1 */
}
@@ -242,7 +242,7 @@ void TIM8_UP_TIM13_IRQHandler(void)
/* USER CODE END TIM8_UP_TIM13_IRQn 0 */
HAL_TIM_IRQHandler(&htim8);
/* USER CODE BEGIN TIM8_UP_TIM13_IRQn 1 */
UPP_PWM_Handle();
//UPP_PWM_Handle();
/* USER CODE END TIM8_UP_TIM13_IRQn 1 */
}
@@ -252,9 +252,6 @@ void TIM8_UP_TIM13_IRQHandler(void)
void TIM8_TRG_COM_TIM14_IRQHandler(void)
{
/* USER CODE BEGIN TIM8_TRG_COM_TIM14_IRQn 0 */
if (__HAL_TIM_GET_FLAG(&htim8, TIM_FLAG_UPDATE) != RESET) {
UPP_PWM_Handle();
}
#ifndef MATLAB // в матлабе нет htim14, т.к. это систем тики
/* USER CODE END TIM8_TRG_COM_TIM14_IRQn 0 */

View File

@@ -53,7 +53,7 @@ void MX_TIM1_Init(void)
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 65535;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 20;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
@@ -268,6 +268,7 @@ void MX_TIM8_Init(void)
/* USER CODE END TIM8_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
@@ -280,7 +281,7 @@ void MX_TIM8_Init(void)
htim8.Init.CounterMode = TIM_COUNTERMODE_UP;
htim8.Init.Period = 65535;
htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim8.Init.RepetitionCounter = 20;
htim8.Init.RepetitionCounter = 0;
htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim8) != HAL_OK)
{
@@ -299,6 +300,12 @@ void MX_TIM8_Init(void)
{
Error_Handler();
}
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;
sSlaveConfig.InputTrigger = TIM_TS_ITR0;
if (HAL_TIM_SlaveConfigSynchro(&htim8, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig) != HAL_OK)

View File

@@ -32,19 +32,11 @@ HAL_StatusTypeDef Angle_Init(Angle_Handle_t *hangle)
Angle_Reset(hangle, UPP_PHASE_B);
Angle_Reset(hangle, UPP_PHASE_C);
// Инициализация углов
float angle_max = u2f(PARAM_INTERNAL.angle.Angle_Max, 65535);
float angle_min = u2f(PARAM_INTERNAL.angle.Angle_Min, 65535);
hangle->f.Initialized = 1;
hangle->Config.PeriodLimit = 1;
// Инициализация ПИД
float kp = u2f(PARAM_INTERNAL.angle.PID_Kp, 10000);
float ki = u2f(PARAM_INTERNAL.angle.PID_Ki, 10000);
float kd = u2f(PARAM_INTERNAL.angle.PID_Kd, 10000);
float ref_alpha = PUI_Tnt_CalcAlpha(u2f(PARAM_PUI.Tnt, 1000), PM_SLOW_PERIOD_US);
return Angle_PID_Init(hangle, kp, ki, kd, ref_alpha);;
return HAL_OK;
}
@@ -78,25 +70,40 @@ HAL_StatusTypeDef Angle_PID_Init(Angle_Handle_t *hangle, float kp, float ki, flo
* @param setpoint Уставка куда регулировать
* @param measurement Измеренные регулируемые величины
*/
void Angle_PID(Angle_Handle_t *hangle, float setpoint, float measurement)
void Angle_PID(Angle_Handle_t *hangle, float setpoint, float measurement, float Correction)
{
if(assert_upp(hangle))
return;
/* Плавное нарастание уставки */
hangle->Iref = Filter_Process(&hangle->refFilter, setpoint);
hangle->Imeas = measurement;
/* Ошибка регулирования = уставка - измеренное */
float err = hangle->Iref - hangle->Imeas;
/* ПИД регулирование */
float open_control = arm_pid_f32(&hangle->pid, err); // 0 - открыть максимально поздно, 1 - открыть макситмально рано
float open_level = arm_pid_f32(&hangle->pid, err); // 0 - открыть максимально поздно, 1 - открыть макситмально рано
/* Ограничиваем диапазон */
if (open_control > 1) open_control = 1;
if(open_control < 0) open_control = 0;
if (open_level > 1)
{
open_level = 1;
}
if(open_level < 0)
{
open_level = 0;
}
/* Приводим уровень открытия к косинусу [-1:1]*/
float OpenLevelForCos = (open_level*2)-1;
float alpha_rad = acosf(OpenLevelForCos); // угол в радианах
float alpha = alpha_rad/PI*hangle->Config.AngleMax; // угол открытия тиристора в о.е. от максимально заданного
/* Выставляем заданный уровень открытия */
Angle_SetAngle(hangle, open_control);
Angle_SetAlpha(hangle, alpha, Correction);
}
/**
@@ -118,7 +125,8 @@ void Angle_PID_Reset(Angle_Handle_t *hangle)
Filter_Start(&hangle->refFilter);
Filter_Process(&hangle->refFilter, 0);
Angle_SetAngle(hangle, 0);
Angle_SetAlpha(hangle, 1, 30); // максимально закрываем
Angle_Reset(hangle, UPP_PHASE_A);
Angle_Reset(hangle, UPP_PHASE_B);
Angle_Reset(hangle, UPP_PHASE_C);
@@ -127,40 +135,28 @@ void Angle_PID_Reset(Angle_Handle_t *hangle)
/**
* @brief Выставление степени открытия тиристоров.
* @param hangle Указатель на таймер
* @param OpenLevel Насколько открыть тиристор:
- 0 - максимально закрыт,
- 1 - максимально открыт
* @param hangle Указатель на таймер
* @param Alpha Угол открытия тиристора в о.е. от 180 градусов:
- 0 - максимально закрыт,
- 1 - максимально открыт
* @param Коррекция угла в градусах
* @return HAL Status.
*/
HAL_StatusTypeDef Angle_SetAngle(Angle_Handle_t *hangle, float OpenLevel)
HAL_StatusTypeDef Angle_SetAlpha(Angle_Handle_t *hangle, float Alpha, float Correction)
{
if(assert_upp(hangle))
return HAL_ERROR;
/* Приводим уровень открытия к косинусу [-1:1]*/
#ifdef UPP_ANGLE_COSINE
float OpenLevelForCos = (OpenLevel*2)-1;
float alpha_rad = acosf(OpenLevelForCos); // угол в радианах
float alpha = alpha_rad/PI* hangle->Config.PeriodLimit; // время открытие в процентах от периода - когда открыть
#else
float alpha = (1-OpenLevel) * hangle->Config.PeriodLimit; // время открытие в процентах от периода - когда открыть
#endif
if(alpha > hangle->Config.AngleMax)
alpha = hangle->Config.AngleMax;
if(alpha < hangle->Config.AngleMin)
alpha = hangle->Config.AngleMin;
// if(alpha > hangle->Config.PeriodLimit)
// {
// alpha = hangle->Config.PeriodLimit;
// }
// float alpha_degree = alpha*180;// угол в градусах
// hangle->alpha_degree = alpha_degree;
if(Alpha > hangle->Config.AngleMax)
Alpha = hangle->Config.AngleMax;
if(Alpha < hangle->Config.AngleMin)
Alpha = hangle->Config.AngleMin;
hangle->alpha = alpha;
// сколько надо выжидать исходя из заданного угла
hangle->alpha_real = Alpha + (Correction/180.0);
hangle->alpha = Alpha;
@@ -175,26 +171,11 @@ HAL_StatusTypeDef Angle_SetAngle(Angle_Handle_t *hangle, float OpenLevel)
*/
HAL_StatusTypeDef Angle_Start(Angle_Handle_t *hangle, UPP_Phase_t Phase, float PeriodMs)
{
if(assert_upp(hangle))
if(assert_upp_phase(hangle, Phase))
return HAL_ERROR;
// Если канал дурацкий - возвращаем ошибку
if(Phase >= 3)
{
return HAL_ERROR;
}
// Дополнительно проверяем на соответствие альфа диапазону
if(hangle->alpha > hangle->Config.AngleMax)
{
hangle->alpha = hangle->Config.AngleMax;
}
if(hangle->alpha < hangle->Config.AngleMin)
{
hangle->alpha = hangle->Config.AngleMin;
}
// сколько тиков надо выждать для угла
uint32_t timer_ticks = TIM_MillisToTick(PeriodMs*hangle->alpha, ANGLE_TIM2_FREQ_MHZ);
uint32_t timer_ticks = TIM_MillisToTick(PeriodMs*hangle->alpha_real, ANGLE_TIM2_FREQ_MHZ);
// сколько тиков будет в таймере когда угол отсчитается (пойдет в CCRx регистр)
uint32_t ccr_ticks = __HAL_TIM_GET_COUNTER(hangle->htim) + timer_ticks;
@@ -259,15 +240,9 @@ HAL_StatusTypeDef Angle_Start(Angle_Handle_t *hangle, UPP_Phase_t Phase, float P
*/
HAL_StatusTypeDef Angle_Reset(Angle_Handle_t *hangle, UPP_Phase_t Phase)
{
if(assert_upp(hangle))
if(assert_upp_phase(hangle, Phase))
return HAL_ERROR;
// Если канал дурацкий - возвращаем ошибку
if(Phase >= 3)
{
return HAL_ERROR;
}
switch(Phase)
{
case UPP_PHASE_A:

View File

@@ -27,8 +27,13 @@ typedef struct
Angle_Config_t Config; ///< Конфигурации алгоритма расчета угла открытия тиристоров
float Iref; ///< текущее задание тока в о.е. [0..1]
float Imeas; ///< измеренное значение тока в о.е. [0..1]
float alpha; ///< текущий угол открытия в о.е. [0..1] (% от периода)
float Imeas; ///< измеренное значение тока в о.е. [0..1]
float alpha; ///< текущий угол открытия в о.е. [0..1] от 180 градусов
float alpha_real; /*!< @brief Фактический отсчитываемый угол открытия в о.е. [0..1] от 180 градусов
@details Этот угол отличается от @ref alpha дополнительными задержками и компенсациями:
- 30 градусов - смещение между линейными и фазными напряжение (мы смотрим линейные, а коммутируем фазные) */
arm_pid_instance_f32 pid; ///< ПИД регулятор для управления углом
FilterExp_t refFilter; ///< Фильтр для плавного нарастания регулирования
@@ -48,9 +53,9 @@ HAL_StatusTypeDef Angle_PID_Init(Angle_Handle_t *hangle, float kp, float ki, flo
// ====== УПРАВЛЕНИЕ ==========
/* Управление углом через ПИД регулятор */
void Angle_PID(Angle_Handle_t *hangle, float setpoint, float measurement);
void Angle_PID(Angle_Handle_t *hangle, float setpoint, float measurement, float Correction);
/* Выставление текущего угла открытия тиристоров. */
HAL_StatusTypeDef Angle_SetAngle(Angle_Handle_t *hangle, float Angle);
HAL_StatusTypeDef Angle_SetAlpha(Angle_Handle_t *hangle, float Angle, float Correction);
/* Установка угла открытия в таймер. */
HAL_StatusTypeDef Angle_Start(Angle_Handle_t *hangle, UPP_Phase_t Phase, float PeriodMs);

View File

@@ -59,11 +59,6 @@ HAL_StatusTypeDef PWM_Init(PWM_Handle_t *hpwm)
PWM_SetHalfWave(hpwm, UPP_PHASE_B, UPP_WAVE_UNKNOWED);
PWM_SetHalfWave(hpwm, UPP_PHASE_C, UPP_WAVE_UNKNOWED);
PWM_SetConfig(hpwm, PARAM_INTERNAL.pwm.PhaseMask,
PARAM_INTERNAL.pwm.Frequency,
u2f(PARAM_INTERNAL.pwm.Duty, 100),
PARAM_INTERNAL.pwm.PulseNumber);
HAL_TIM_PWM_Start(&hpwm1, PWM_CHANNEL_1);
HAL_TIM_PWM_Start(&hpwm1, PWM_CHANNEL_2);
HAL_TIM_PWM_Start(&hpwm1, PWM_CHANNEL_3);
@@ -72,10 +67,8 @@ HAL_StatusTypeDef PWM_Init(PWM_Handle_t *hpwm)
HAL_TIM_PWM_Start(&hpwm2, PWM_CHANNEL_6);
PWM_Stop(hpwm, 0, 1);
#ifndef PWM_HARDWARE_IMPULSES_CONTROL
__PWM_ReConfigToSoftwarePulses();
HAL_TIM_Base_Start_IT(&hpwm1);
#endif
HAL_TIM_Base_Start(&hpwm1);
return HAL_OK;
}
@@ -88,16 +81,8 @@ HAL_StatusTypeDef PWM_Init(PWM_Handle_t *hpwm)
*/
HAL_StatusTypeDef PWM_Start(PWM_Handle_t *hpwm, UPP_Phase_t Phase)
{
if(assert_upp(hpwm))
if(assert_upp_phase(hpwm, Phase))
return HAL_ERROR;
// Если уже какой-то канал запущен - не запускаем
if(hpwm->f.Running)
return HAL_BUSY;
// Если канал дурацкий - возвращаем ошибку
if(Phase >= 3)
{
return HAL_ERROR;
}
if (hpwm->Phase[Phase] == NULL || hpwm->Phase[Phase] == &hpwm->AllPhases[PHASE_UNKNOWN])
return HAL_ERROR;
@@ -115,22 +100,8 @@ HAL_StatusTypeDef PWM_Start(PWM_Handle_t *hpwm, UPP_Phase_t Phase)
// Запуск только если таймер в режиме ожидания
case PWM_THYR_TIM_WAIT:
#ifndef PWM_HARDWARE_IMPULSES_CONTROL
hpwm->Phase[Phase]->State = PWM_THYR_TIM_START;
return HAL_OK;
#else
hpwm->Phase[Phase]->State = PWM_THYR_TIM_ACTIVE;
if(hpwm->Config.PulseNumber)
hpwm->Phase[Phase]->htim->Instance->RCR = hpwm->Config.PulseNumber-1;
else
return HAL_ERROR;
__PWM_SetOutputState(hpwm->Phase[Phase], PWM_ENABLE);
hpwm->f.Running++;
return HAL_TIM_Base_Start_IT(hpwm->Phase[Phase]->htim);
#endif
default:
return HAL_ERROR;
@@ -149,7 +120,7 @@ HAL_StatusTypeDef PWM_Start(PWM_Handle_t *hpwm, UPP_Phase_t Phase)
*/
HAL_StatusTypeDef PWM_Stop(PWM_Handle_t *hpwm, UPP_Phase_t Phase, uint8_t force_stop_all)
{
if(assert_upp(hpwm))
if(assert_upp_phase(hpwm, Phase))
return HAL_ERROR;
// Если force_stop_all - сбрасываем ВСЕ КАНАЛЫ
@@ -169,29 +140,44 @@ HAL_StatusTypeDef PWM_Stop(PWM_Handle_t *hpwm, UPP_Phase_t Phase, uint8_t force_
}
// Если НЕ force_stop_all - сбрасываем ТОЛЬКО заданный канал
else
{
// Если канал дурацкий - возвращаем ошибку
if(Phase >= 3)
{
return HAL_ERROR;
}
if (hpwm->Phase[Phase] == NULL || hpwm->Phase[Phase] == &hpwm->AllPhases[PHASE_UNKNOWN])
return HAL_ERROR;
{
// Если не force_stop_all - сбрасываем только текущий канал
__PWM_SetOutputState(hpwm->Phase[Phase], PWM_DISABLE);
if(hpwm->Phase[Phase]->State != PWM_THYR_DISABLED)
{
hpwm->Phase[Phase]->State = PWM_THYR_DISABLED;
#ifdef PWM_HARDWARE_IMPULSES_CONTROL
if(hpwm->f.Running)
hpwm->f.Running--;
HAL_TIM_Base_Stop(hpwm->Phase[Phase]->htim);
#endif
}
return HAL_OK;
}
}
/**
* @brief Установка полуволны для слежения.
* @param hpwm Указатель на хендл ШИМ тиристоров
* @param Phase Для какой фазы надо установить полуволну
* @param halfwave Какую полуволну установить
* @return HAL Status.
* @details Меняет указатель канала фазы на канал соответствующей полуволны
*/
HAL_StatusTypeDef PWM_SetHalfWave(PWM_Handle_t *hpwm, UPP_Phase_t Phase, UPP_HalfWave_t halfwave)
{
if(assert_upp_phase(hpwm, Phase))
return HAL_ERROR;
// Выставляем текущий активную полуволну
switch(halfwave)
{
case UPP_WAVE_POSITIVE:
hpwm->Phase[Phase] = &hpwm->AllPhases[Phase];
hpwm->Phase[Phase]->State = PWM_THYR_TIM_WAIT;
return HAL_OK;
case UPP_WAVE_NEGATIVE:
hpwm->Phase[Phase] = &hpwm->AllPhases[Phase+3];
hpwm->Phase[Phase]->State = PWM_THYR_TIM_WAIT;
return HAL_OK;
default:
hpwm->Phase[Phase] = &hpwm->AllPhases[PHASE_UNKNOWN];
return HAL_ERROR;
}
}
/**
* @brief Установка параметров ШИМ.
@@ -199,7 +185,7 @@ HAL_StatusTypeDef PWM_Stop(PWM_Handle_t *hpwm, UPP_Phase_t Phase, uint8_t force_
* @param Frequency Частота в ГЦ
* @return HAL Status.
*/
HAL_StatusTypeDef PWM_SetConfig(PWM_Handle_t *hpwm, uint8_t PhaseMask, uint16_t Frequency, float Duty, uint8_t PulseNumber)
HAL_StatusTypeDef PWM_SetConfig(PWM_Handle_t *hpwm, uint8_t PhaseMask, uint16_t Frequency, float Duty, float PulseLength)
{
if(assert_upp(hpwm))
return HAL_ERROR;
@@ -207,10 +193,10 @@ HAL_StatusTypeDef PWM_SetConfig(PWM_Handle_t *hpwm, uint8_t PhaseMask, uint16_t
return HAL_BUSY;
// Остановка таймера
HAL_TIM_Base_Stop_IT(&hpwm1);
HAL_TIM_Base_Stop(&hpwm1);
hpwm->Config.PhaseMask.all = PhaseMask;
hpwm->Config.PulseNumber = PulseNumber;
hpwm->Config.PulseLength = PulseLength;
hpwm->Config.Frequency = Frequency;
hpwm->Config.Duty = Duty;
// Высставление периодов
@@ -233,55 +219,10 @@ HAL_StatusTypeDef PWM_SetConfig(PWM_Handle_t *hpwm, uint8_t PhaseMask, uint16_t
PWM_Stop(hpwm, 0, 1);
#ifndef PWM_HARDWARE_IMPULSES_CONTROL
return HAL_TIM_Base_Start_IT(&hpwm1);
#else
return HAL_OK;
#endif
return HAL_TIM_Base_Start(&hpwm1);
}
/**
* @brief Установка полуволны для слежения.
* @param hpwm Указатель на хендл ШИМ тиристоров
* @param Phase Для какой фазы надо установить полуволну
* @param halfwave Какую полуволну установить
* @return HAL Status.
* @details Меняет указатель канала фазы на канал соответствующей полуволны
*/
HAL_StatusTypeDef PWM_SetHalfWave(PWM_Handle_t *hpwm, UPP_Phase_t Phase, UPP_HalfWave_t halfwave)
{
if(assert_upp(hpwm))
return HAL_ERROR;
// Сбрасываем текущий канал
PWM_Stop(hpwm, Phase, 0);
// Если канал дурацкий - выставляем заглушку
if(Phase >= 3)
{
hpwm->Phase[Phase] = &hpwm->AllPhases[PHASE_UNKNOWN];
return HAL_ERROR;
}
// Выставляем канал
switch(halfwave)
{
case UPP_WAVE_POSITIVE:
hpwm->Phase[Phase] = &hpwm->AllPhases[Phase];
hpwm->Phase[Phase]->State = PWM_THYR_TIM_WAIT;
return HAL_OK;
case UPP_WAVE_NEGATIVE:
hpwm->Phase[Phase] = &hpwm->AllPhases[Phase+3];
hpwm->Phase[Phase]->State = PWM_THYR_TIM_WAIT;
return HAL_OK;
default:
hpwm->Phase[Phase] = &hpwm->AllPhases[PHASE_UNKNOWN];
return HAL_ERROR;
}
}
/**
* @brief Установка полярности шим.
* @param hpwm Указатель на хендл ШИМ тиристоров
@@ -312,7 +253,7 @@ HAL_StatusTypeDef PWM_SetPolarity(PWM_Handle_t *hpwm, int polarity)
* @return HAL Status.
* @details Автомат состояний, который определяет поведение каналов ШИМ
*/
HAL_StatusTypeDef PWM_Handle(PWM_Handle_t *hpwm)
HAL_StatusTypeDef PWM_Handle(PWM_Handle_t *hpwm, float PeriodMs)
{
if(assert_upp(hpwm))
return HAL_ERROR;
@@ -326,7 +267,6 @@ HAL_StatusTypeDef PWM_Handle(PWM_Handle_t *hpwm)
if (hPhase->htim == NULL)
continue;
#ifndef PWM_HARDWARE_IMPULSES_CONTROL
switch (hPhase->State)
{
case PWM_THYR_DISABLED: // канал отключен
@@ -339,7 +279,7 @@ HAL_StatusTypeDef PWM_Handle(PWM_Handle_t *hpwm)
case PWM_THYR_TIM_START: // начать ШИМ (пачка импульсов)
__PWM_SetOutputState(hPhase, PWM_ENABLE);
hPhase->PulseCnt = hpwm->Config.PulseNumber - 1; // 1 импульс уже прошел
hPhase->PulseCnt = MS_TO_FAST_TICKS(PeriodMs*hpwm->Config.PulseLength) - 1; // 1 импульс уже прошел
hPhase->State = PWM_THYR_TIM_ACTIVE;
hpwm->f.Running++;
break;
@@ -363,17 +303,6 @@ HAL_StatusTypeDef PWM_Handle(PWM_Handle_t *hpwm)
__PWM_SetOutputState(hPhase, PWM_DISABLE);
break;
}
#else //PWM_HARDWARE_IMPULSES_CONTROL
// после того как пачка импульсов прошла отключаем активный канал
if (hPhase->State == PWM_THYR_TIM_ACTIVE)
{
hPhase->State = PWM_THYR_TIM_WAIT;
if(hpwm->f.Running)
hpwm->f.Running--;
__PWM_SetOutputState(hPhase, PWM_DISABLE);
HAL_TIM_Base_Stop(hPhase->htim);
}
#endif
}
return HAL_OK;
}
@@ -394,37 +323,37 @@ static HAL_StatusTypeDef __PWM_SetOutputState(PWM_Channel_t *hCh, uint32_t state
if (hCh->htim == NULL)
return HAL_ERROR;
uint32_t ch_mode = state;
// Если режим уже выставлен
if(hCh->CurrentMode == state)
{
return HAL_OK;
}
hCh->CurrentMode = state;
// выставляем режим каналов
switch(hCh->ChMask)
{
case TIM_CHANNEL_1:
hCh->htim->Instance->CCMR1 &= ~TIM_OCMODE_PWM2;
hCh->htim->Instance->CCMR1 |= ch_mode;
hCh->htim->Instance->CCMR1 |= state;
break;
case TIM_CHANNEL_2:
hCh->htim->Instance->CCMR1 &= ~(TIM_OCMODE_PWM2 << 8);
hCh->htim->Instance->CCMR1 |= (ch_mode << 8);
hCh->htim->Instance->CCMR1 |= (state << 8);
break;
case TIM_CHANNEL_3:
hCh->htim->Instance->CCMR2 &= ~TIM_OCMODE_PWM2;
hCh->htim->Instance->CCMR2 |= ch_mode;
hCh->htim->Instance->CCMR2 |= state;
break;
case TIM_CHANNEL_4:
hCh->htim->Instance->CCMR2 &= ~(TIM_OCMODE_PWM2 << 8);
hCh->htim->Instance->CCMR2 |= (ch_mode << 8);
hCh->htim->Instance->CCMR2 |= (state << 8);
break;
default:
break;
}
// в последнюю очередь включаем выход. Перед этим настраиваем каналы на ШИМ
if(state == PWM_ENABLE)
{
__HAL_TIM_MOE_ENABLE(hCh->htim);
}
return HAL_OK;
}

View File

@@ -33,6 +33,8 @@
#define PHASE_UNKNOWN 6
#define PWM_PulseLengthToCnt(_length_, _period_) ((_length_)*(_period_)/
/**
* @brief Состояния канала
*/
@@ -52,6 +54,7 @@ typedef struct {
TIM_HandleTypeDef *htim; ///< указатель на соответствующий TIM (hpwm1 или hpwm2)
uint32_t PulseCnt; ///< Счетчик кол-ва импульсов. Инициализируется из структуры @ref PWM_ThyrConfig_t
uint32_t ChMask; ///< TIM_CHANNEL_x
uint32_t CurrentMode; ///< Текущий режим канала
struct {
unsigned Ready:1; ///< Флаг готовности тиристора к работе
@@ -72,7 +75,7 @@ typedef struct {
uint8_t phC:1;
};
}PhaseMask; ///< Какими каналами управлять
uint8_t PulseNumber; ///< Сколько импульсов отправить в пакете для открытия тиристоров
float PulseLength; ///< Длина импульса в о.е. от 180 градусов
uint16_t Frequency; ///< Частота импульсов
float Duty; ///< Скважность импульсов
} PWM_ThyrConfig_t;
@@ -80,9 +83,9 @@ typedef struct {
/**
* @brief Хендл управляюзщий тиристорами */
typedef struct {
PWM_ThyrConfig_t Config;
PWM_Channel_t *Phase[3]; ///< Текущие каналы для фаз
PWM_Channel_t AllPhases[7]; ///< Все каналы для фаз (+1 для деинициализированного канала)
PWM_ThyrConfig_t Config; ///< Конфигурации ШИМ
PWM_Channel_t *Phase[3]; ///< Каналы для активной в данный момент фазы
PWM_Channel_t AllPhases[7]; ///< Все каналы для фаз (+1 для деинициализированного канала)
struct {
unsigned Initialized : 1;
@@ -101,12 +104,12 @@ HAL_StatusTypeDef PWM_Start(PWM_Handle_t *hpwm, UPP_Phase_t Phase);
/* Остановить ШИМ. */
HAL_StatusTypeDef PWM_Stop(PWM_Handle_t *hpwm, UPP_Phase_t Phase, uint8_t force_stop_all);
/* Установка частоты ШИМ. */
HAL_StatusTypeDef PWM_SetConfig(PWM_Handle_t *hpwm, uint8_t PhaseMask, uint16_t Frequency, float Duty, uint8_t PulseNumber);
HAL_StatusTypeDef PWM_SetConfig(PWM_Handle_t *hpwm, uint8_t PhaseMask, uint16_t Frequency, float Duty, float PulseLength);
/* Установка полуволны для слежения. */
HAL_StatusTypeDef PWM_SetHalfWave(PWM_Handle_t *hpwm, UPP_Phase_t Phase, UPP_HalfWave_t halfwave);
/* Установка полярности шим. */
HAL_StatusTypeDef PWM_SetPolarity(PWM_Handle_t *hpwm, int polarity);
// ====== СЕРВИС ==========
/* Хендл ШИМ тиристоров. */
HAL_StatusTypeDef PWM_Handle(PWM_Handle_t *hpwm);
HAL_StatusTypeDef PWM_Handle(PWM_Handle_t *hpwm, float PeriodMs);
#endif /* _PWM_THYRISTORS_H */

View File

@@ -51,6 +51,10 @@ int UPP_App_Init(void)
return 1;
}
if(UPP_Params_Init() != HAL_OK)
{
return 1;
}
return 0;
}
@@ -90,14 +94,16 @@ int UPP_While(void)
// Медленные расчеты
PowerMonitor_SlowCalc(&upp.pm);
int razgon_done = (fabsf(upp.hangle.Iref - u2f(PARAM_PUI.Iref, 100)) < 0.1);
#ifdef UPP_SIMULATE_I // симулируем токи
upp.pm.measured.final.Iamp = upp.hangle.Iref/2;
// Защиты // При симуляции тока не включаем его проверку
PowerMonitor_Protect(&upp.pm, 0);
#else
// Защиты // Определенные защиты по току включаем только в после разгона
PowerMonitor_Protect(&upp.pm, (fabsf(upp.hangle.Iref - u2f(PARAM_PUI.Iref, 100)) < 0.1));
// При симуляции тока не включаем его проверку
razgon_done = 0;
#endif
// Защиты // Определенные защиты по току включаем только после разгона
PowerMonitor_Protect(&upp.pm, razgon_done);
// Обрабока ошибок и выставление итоговой Ошибки
UPP_Errors_Handle();
// Контроль парамеров
@@ -169,6 +175,9 @@ int UPP_While(void)
/*======= Состояние В работе =========*/
case UPP_Work:
// Разрешаем выход ШИМ
__HAL_TIM_MOE_ENABLE(&hpwm1);
__HAL_TIM_MOE_ENABLE(&hpwm2);
// Индикация
UPP_DO.Ready(DISABLE);
UPP_DO.Work(ENABLE);
@@ -176,9 +185,14 @@ int UPP_While(void)
// если пришла команда на остановку
if (!upp.call->go)
upp.workmode = UPP_Init;
// Коррекция для отсчета угла открытия
// 30 градусов - сдвиг между линейными и фазными напряжениями
// 30 градусов - фазовое смщеение эксп. фильтра АЦП для сглаживания напряжений
float Correction = 30 + 0;
// Регулирование тиристоров
Angle_PID(&upp.hangle, u2f(PARAM_PUI.Iref,100), upp.pm.measured.final.Iamp);
Angle_PID(&upp.hangle, u2f(PARAM_PUI.Iref,100), upp.pm.measured.final.Iamp, Correction);
// если слишком долгий запуск
if((local_time() - upp.StartTick) > (upp.PUI.params->Tdelay*1000))
@@ -278,27 +292,19 @@ void UPP_ADC_Handle(void)
UPP_HalfWave_t curr_halfwave = ZC_GetHalfWave(&upp.pm.zc, phase);
res = PWM_SetHalfWave(&upp.hpwm, phase, curr_halfwave);
// Начинаем отсчитывать угол
res = Angle_Start(&upp.hangle, phase, 10);
res = Angle_Start(&upp.hangle, phase, UPP_HALFWAVE_PERIOD);
if(res != HAL_OK)
__NOP();
}
}
}
// Проверяем на ошибки
// ШИМим ключи
res = PWM_Handle(&upp.hpwm, UPP_HALFWAVE_PERIOD);
upp.Timings.isr_adc_us = BenchTime_End(BT_ADC, angletim.Instance->CNT)/ANGLE_TIM2_FREQ_MHZ;
upp.pm.f.inIsr = 0;
}
/**
* @brief @ref TIM1_UP_TIM10_IRQHandler @ref
*/
void UPP_PWM_Handle(void)
{
BenchTime_Start(BT_PWM, angletim.Instance->CNT, HAL_MAX_DELAY);
res = PWM_Handle(&upp.hpwm);
upp.Timings.isr_pwm_us = BenchTime_End(BT_PWM, angletim.Instance->CNT)/ANGLE_TIM2_FREQ_MHZ;
}
/**
* @brief @ref HAL_TIM_OC_DelayElapsedCallback

View File

@@ -113,7 +113,7 @@ void UPP_Params_ControlInternal(void)
float angle_max = upp.hangle.Config.AngleMax;
float angle_min = upp.hangle.Config.AngleMin;
float angle_pid_kp = upp.hangle.pid.Kp;
float angle_pid_ki = upp.hangle.pid.Ki;
float angle_pid_ki = upp.hangle.pid.Ki/((float)PM_SLOW_PERIOD_US/1000000);
float angle_pid_kd = upp.hangle.pid.Kd;
// временная переменная для параметров каналов АЦП
float adc_channel_max[ADC_NUMB_OF_REGULAR_CHANNELS] = {0};
@@ -125,7 +125,7 @@ void UPP_Params_ControlInternal(void)
uint8_t pwm_phase_mask = upp.hpwm.Config.PhaseMask.all;
uint16_t pwm_freq = upp.hpwm.Config.Frequency;
float pwm_duty = upp.hpwm.Config.Duty;
uint8_t pwm_pulse_num = upp.hpwm.Config.PulseNumber;
float pwm_pulse_len = upp.hpwm.Config.PulseLength;
// временная переменная для параметров Мониторинга сети
uint16_t pm_rms_widnow_size = upp.pm.rms[0].window_size;
float pm_rms_exp_alpha = upp.pm.rms_exp[0].alpha;
@@ -183,7 +183,7 @@ void UPP_Params_ControlInternal(void)
zc_update = 1;
}
// Параметры ШИМ токов
// Параметры ШИМ
if(__CheckParamU8(&pwm_phase_mask, PARAM_INTERNAL.pwm.PhaseMask, 1))
{
pwm_update = 1;
@@ -196,7 +196,7 @@ void UPP_Params_ControlInternal(void)
{
pwm_update = 1;
}
if(__CheckParamU8(&pwm_pulse_num, PARAM_INTERNAL.pwm.PulseNumber, 1))
if(__CheckParamF(&pwm_pulse_len, PARAM_INTERNAL.pwm.PulseLength, 65535))
{
pwm_update = 1;
}
@@ -226,7 +226,10 @@ void UPP_Params_ControlInternal(void)
{
if(Angle_SetRange(&upp.hangle, angle_min, angle_max) == HAL_OK)
{
if(Angle_PID_Init(&upp.hangle, angle_pid_kp, angle_pid_ki, angle_pid_kd, upp.hangle.refFilter.alpha) == HAL_OK)
if(Angle_PID_Init(&upp.hangle, angle_pid_kp,
angle_pid_ki*((float)PM_SLOW_PERIOD_US/1000000),
angle_pid_kd,
upp.hangle.refFilter.alpha) == HAL_OK)
{
alpha_update = 0;
}
@@ -258,7 +261,7 @@ void UPP_Params_ControlInternal(void)
// Обновление ШИМ конфигов
if(pwm_update)
{
if(PWM_SetConfig(&upp.hpwm, pwm_phase_mask, pwm_freq, pwm_duty, pwm_pulse_num) == HAL_OK)
if(PWM_SetConfig(&upp.hpwm, pwm_phase_mask, pwm_freq, pwm_duty, pwm_pulse_len) == HAL_OK)
{
pwm_update = 0;
}
@@ -270,6 +273,89 @@ void UPP_Params_ControlInternal(void)
/**
* @brief Инициализация параметров УПП.
* @return HAL Status.
*/
HAL_StatusTypeDef UPP_Params_Init(void)
{
/*====== ИНИЦИАЛИЗАЦИЯ МОДУЛЯ angle_control ======*/
// Инициализация ПИД
if(Angle_PID_Init(&upp.hangle,
u2f(PARAM_INTERNAL.angle.PID_Kp, 10000),
u2f(PARAM_INTERNAL.angle.PID_Ki, 10000)*((float)PM_SLOW_PERIOD_US/1000000),
u2f(PARAM_INTERNAL.angle.PID_Kd, 10000),
PUI_Tnt_CalcAlpha(u2f(PARAM_PUI.Tnt, 1000), PM_SLOW_PERIOD_US)) != HAL_OK)
return HAL_ERROR;
// Инициализация углов
if(Angle_SetRange(&upp.hangle,
u2f(PARAM_INTERNAL.angle.Angle_Min, 65535),
u2f(PARAM_INTERNAL.angle.Angle_Max, 65535)) != HAL_OK)
return HAL_ERROR;
/*===== ИНИЦИАЛИЗАЦИЯ МОДУЛЯ power_monitor ======*/
/* Инициализация каналов АЦП */
if(ADC_ConfigChannel(&upp.pm.adc, ADC_CHANNEL_UBA,
PARAM_INTERNAL.adc.ADC_Zero[ADC_CHANNEL_UBA],
u2f(PARAM_INTERNAL.adc.ADC_Max[ADC_CHANNEL_UBA], 10),
4095) != HAL_OK)
return HAL_ERROR;
if(ADC_ConfigChannel(&upp.pm.adc, ADC_CHANNEL_UAC,
PARAM_INTERNAL.adc.ADC_Zero[ADC_CHANNEL_UAC],
u2f(PARAM_INTERNAL.adc.ADC_Max[ADC_CHANNEL_UAC], 10),
4095) != HAL_OK)
return HAL_ERROR;
if(ADC_ConfigChannel(&upp.pm.adc, ADC_CHANNEL_IC,
PARAM_INTERNAL.adc.ADC_Zero[ADC_CHANNEL_IC],
u2f(PARAM_INTERNAL.adc.ADC_Max[ADC_CHANNEL_IC], 10),
4095) != HAL_OK)
return HAL_ERROR;
if(ADC_ConfigChannel(&upp.pm.adc, ADC_CHANNEL_IA,
PARAM_INTERNAL.adc.ADC_Zero[ADC_CHANNEL_IA],
u2f(PARAM_INTERNAL.adc.ADC_Max[ADC_CHANNEL_IA], 10),
4095) != HAL_OK)
return HAL_ERROR;
/* Инициализация алгоритма перехода через ноль */
if(ZC_Init(&upp.pm.zc, 3, u2f(PARAM_INTERNAL.zc.Hysteresis, 100), PARAM_INTERNAL.zc.DebouneCouner) != HAL_OK)
return HAL_ERROR;
/* Инициализация RMS фильтра медленного алга */
for(int i = 0; i < RMS_ALL; i++)
{
if(FilterRMS_Init(&upp.pm.rms[i], PARAM_INTERNAL.pm.rms_window_size))
return HAL_ERROR;
Filter_Start(&upp.pm.rms[i]);
}
/* Инициализация экпоненциального фильтра медленного алга */
for(int i = 0; i < RMS_EXP_ALL; i++)
{
if(FilterExp_Init(&upp.pm.rms_exp[i], u2f(PARAM_INTERNAL.pm.rms_exp_alpha, 65535)))
return HAL_ERROR;
Filter_Start(&upp.pm.rms_exp[i]);
}
/*====== ИНИЦИАЛИЗАЦИЯ МОДУЛЯ pwm_thyristors ======*/
if(PWM_SetConfig(&upp.hpwm, PARAM_INTERNAL.pwm.PhaseMask,
PARAM_INTERNAL.pwm.Frequency,
u2f(PARAM_INTERNAL.pwm.Duty, 100),
PARAM_INTERNAL.pwm.PulseLength) != HAL_OK)
return HAL_ERROR;
return HAL_OK;
}
/**
* @brief Контроль параметров УПП на корректные значения.
* @return HAL Status.
@@ -309,7 +395,6 @@ void UPP_Params_Saturate(void)
SATURATE_U16(PARAM_INTERNAL.pwm.PhaseMask, 0, 7);
SATURATE_U16(PARAM_INTERNAL.pwm.Frequency, 1000, 40000);
SATURATE_U16(PARAM_INTERNAL.pwm.PulseNumber, 1, 50);
SATURATE_U16(PARAM_INTERNAL.zc.Hysteresis, 0, 0.1*100);
SATURATE_U16(PARAM_INTERNAL.zc.DebouneCouner, 0, 1000);
@@ -368,7 +453,7 @@ void UPP_Params_SetDefault(int pui_default, int internal_default)
PARAM_INTERNAL.pwm.PhaseMask = 7; // (все три фазы)
PARAM_INTERNAL.pwm.Frequency = PWM_THYR_FREQUENCY_HZ_DEFAULT;
PARAM_INTERNAL.pwm.Duty = PWM_THYR_DUTY_PERCENT_DEFAULT*100;
PARAM_INTERNAL.pwm.PulseNumber = PWM_THYR_PULSE_NUMBER_DEFAULT;
PARAM_INTERNAL.pwm.PulseLength = PWM_THYR_PULSE_LENGTH_DEFAULT*65535;
PARAM_INTERNAL.zc.Hysteresis = ZERO_CROSS_HYSTERESIS_PERCENT_DEFAULT*100;
PARAM_INTERNAL.zc.DebouneCouner = ZERO_CROSS_DEBOUNCE_CNT_DEFAULT;
@@ -385,13 +470,14 @@ void UPP_Params_SetDefault(int pui_default, int internal_default)
}
#define ANGLE_PERIOD_MS(_freq_) (((float)1/(_freq_*2))*1000)
// Перерасчет максимально допустимого угла
static void __AngleSetLimit(void)
{ // Сколько пачка ипульсов занимает процентов от всего периода
float pulses_percent_of_period = (((float)PARAM_INTERNAL.pwm.PulseNumber / PARAM_INTERNAL.pwm.Frequency) * 1000) / ANGLE_PERIOD_MS(upp.pm.measured.final.Fmean);
float pulses_percent_of_period = (((float)PARAM_INTERNAL.pwm.PulseLength / PARAM_INTERNAL.pwm.Frequency) * 1000) / ANGLE_PERIOD_MS(upp.pm.measured.final.Fmean);
// Вычитаем этот процент из 1 - получаем максимально безопасный угол
float angle_limit = 1 - pulses_percent_of_period;
float angle_limit = 1;
angle_limit -= pulses_percent_of_period*u2f(PARAM_INTERNAL.angle.PulseLengthReserve, 100); // добавляем запас в PulseLengthReserve процентов от пачки импульсов
Angle_SetLimit(&upp.hangle, angle_limit);
}

View File

@@ -69,7 +69,7 @@ typedef struct
uint16_t PhaseMask; ///< Битовяя маска на какие фазы подавать ШИМ: 0 бит - a, 1 бит - b, 2 бит - c
uint16_t Frequency; ///< Частота ШИМ для пачки импульсов на тиристоры [Герцы]
uint16_t Duty; ///< Скважность ШИМ для пачки импульсов на тиристоры [Проценты]
uint16_t PulseNumber; ///< Количесво импульсов в пачке [Количество]
uint16_t PulseLength; ///< Количесво импульсов в пачке [Количество]
}pwm;
/* Параметры Угла */
@@ -100,7 +100,9 @@ void UPP_Params_Control(void);
/* Контроль параметров от ПУИ. */
void UPP_Params_ControlPUI(void);
/* Контроль внутренних параметров УПП. */
void UPP_Params_ControlInternal(void);
void UPP_Params_ControlInternal(void);
/* Инициализация параметров УПП. */
HAL_StatusTypeDef UPP_Params_Init(void);
/* Контроль параметров УПП на корректные значения. */
void UPP_Params_Saturate(void);
/* Установка параметров на дефолтные значения */

View File

@@ -385,7 +385,7 @@
<Ww>
<count>11</count>
<WinNumber>1</WinNumber>
<ItemText>\\Debug_F417\../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c\uwTick</ItemText>
<ItemText>\\Debug_F417\../Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c\uwTick,0x0A</ItemText>
</Ww>
<Ww>
<count>12</count>
@@ -494,6 +494,21 @@
<WinNumber>2</WinNumber>
<ItemText>upp,0x0A</ItemText>
</Ww>
<Ww>
<count>14</count>
<WinNumber>2</WinNumber>
<ItemText>htim5,0x0A</ItemText>
</Ww>
<Ww>
<count>15</count>
<WinNumber>2</WinNumber>
<ItemText>htim3,0x0A</ItemText>
</Ww>
<Ww>
<count>16</count>
<WinNumber>2</WinNumber>
<ItemText>htim5.Instance-&gt;CNT-2605346416,0x0A</ItemText>
</Ww>
</WatchWindow2>
<Tracepoint>
<THDelay>0</THDelay>
@@ -515,7 +530,7 @@
<AscS3>0</AscS3>
<aSer3>0</aSer3>
<eProf>0</eProf>
<aLa>1</aLa>
<aLa>0</aLa>
<aPa1>0</aPa1>
<AscS4>0</AscS4>
<aSer4>0</aSer4>

View File

@@ -136,14 +136,16 @@ Mcu.Pin61=VP_TIM2_VS_no_output2
Mcu.Pin62=VP_TIM2_VS_no_output3
Mcu.Pin63=VP_TIM3_VS_ClockSourceINT
Mcu.Pin64=VP_TIM5_VS_ClockSourceINT
Mcu.Pin65=VP_TIM8_VS_ClockSourceINT
Mcu.Pin66=VP_TIM8_VS_OPM
Mcu.Pin67=VP_TIM11_VS_ClockSourceINT
Mcu.Pin68=VP_TIM12_VS_ClockSourceINT
Mcu.Pin65=VP_TIM8_VS_ControllerModeTrigger
Mcu.Pin66=VP_TIM8_VS_ClockSourceINT
Mcu.Pin67=VP_TIM8_VS_ClockSourceITR
Mcu.Pin68=VP_TIM8_VS_OPM
Mcu.Pin69=VP_TIM11_VS_ClockSourceINT
Mcu.Pin7=PC15/OSC32_OUT
Mcu.Pin70=VP_TIM12_VS_ClockSourceINT
Mcu.Pin8=PF6
Mcu.Pin9=PF7
Mcu.PinsNb=69
Mcu.PinsNb=71
Mcu.ThirdPartyNb=0
Mcu.UserConstants=angletim,htim2;mb_huart,huart3;mbdbg_htim,htim11;PWM_CHANNEL_1,TIM_CHANNEL_1;PWM_CHANNEL_2,TIM_CHANNEL_2;PWM_CHANNEL_3,TIM_CHANNEL_3;PWM_CHANNEL_4,TIM_CHANNEL_4;mem_hspi,hspi3;ANGLE_CHANNEL_2,TIM_CHANNEL_2;ANGLE_CHANNEL_3,TIM_CHANNEL_3;ANGLE_CHANNEL_1,TIM_CHANNEL_1;PWM_CHANNEL_5,TIM_CHANNEL_3;PWM_CHANNEL_6,TIM_CHANNEL_4;mb_htim,htim12;adc_tim,htim3;usTick,ustim.Instance->CNT;hpwm2,htim8;mb_dbg_huart,huart6;ustim,htim5;hpwm1,htim1
Mcu.UserName=STM32F427ZGTx
@@ -474,7 +476,7 @@ TIM1.Channel-PWM\ Generation3\ CH3=TIM_CHANNEL_3
TIM1.Channel-PWM\ Generation4\ CH4=TIM_CHANNEL_4
TIM1.IPParameters=Prescaler,Channel-PWM Generation1 CH1,Channel-PWM Generation2 CH2,Channel-PWM Generation4 CH4,Channel-PWM Generation3 CH3,TIM_MasterOutputTrigger,RepetitionCounter
TIM1.Prescaler=0
TIM1.RepetitionCounter=20
TIM1.RepetitionCounter=0
TIM1.TIM_MasterOutputTrigger=TIM_TRGO_UPDATE
TIM11.IPParameters=Prescaler
TIM11.Prescaler=180-1
@@ -498,7 +500,7 @@ TIM8.IPParameters=Prescaler,Period,TIM_MasterSlaveMode,TIM_MasterOutputTrigger,C
TIM8.OC4Preload_PWM=ENABLE
TIM8.Period=65535
TIM8.Prescaler=0
TIM8.RepetitionCounter=20
TIM8.RepetitionCounter=0
TIM8.TIM_MasterOutputTrigger=TIM_TRGO_RESET
TIM8.TIM_MasterSlaveMode=TIM_MASTERSLAVEMODE_DISABLE
USART3.IPParameters=VirtualMode
@@ -535,6 +537,10 @@ VP_TIM5_VS_ClockSourceINT.Mode=Internal
VP_TIM5_VS_ClockSourceINT.Signal=TIM5_VS_ClockSourceINT
VP_TIM8_VS_ClockSourceINT.Mode=Internal
VP_TIM8_VS_ClockSourceINT.Signal=TIM8_VS_ClockSourceINT
VP_TIM8_VS_ClockSourceITR.Mode=TriggerSource_ITR0
VP_TIM8_VS_ClockSourceITR.Signal=TIM8_VS_ClockSourceITR
VP_TIM8_VS_ControllerModeTrigger.Mode=Trigger Mode
VP_TIM8_VS_ControllerModeTrigger.Signal=TIM8_VS_ControllerModeTrigger
VP_TIM8_VS_OPM.Mode=OPM_bit
VP_TIM8_VS_OPM.Signal=TIM8_VS_OPM
board=custom

View File

@@ -0,0 +1,12 @@
open_level = 0:0.01:1; % Степень регулирования выходного напряжения (epsilon) от 0 до 1
OpenLevelForCos = (open_level.*2)-1;
alpha_rad = acos(OpenLevelForCos); % угол в радианах
alpha = alpha_rad/pi; % угол открытия тиристора в о.е. от максимально заданного
plot(alpha, open_level)
grid on;
xlabel('\alpha, о.е. (от \pi)');
ylabel('\epsilon, о.е.');
title('Регулировочная характеристика \epsilon');
legend('ε = cos(α)');

View File

@@ -2,10 +2,11 @@
clear all; close all; clc;
%% Параметры моделирования
Fs = 100000; % Частота дискретизации [Гц]
Fs = 1/25e-6; % Частота дискретизации [Гц]
T = 0.5; % Время моделирования [с]
t = 0:1/Fs:T-1/Fs; % Временной вектор
N = length(t); % Количество отсчетов
Fsrez = 50;
%% Уровни шума для разных каналов
noise_levels.voltage = 0.2; % 2% шума для напряжений
@@ -17,28 +18,28 @@ fprintf('=== АВТОМАТИЧЕСКИЙ РАСЧЕТ КОЭФФИЦИЕНТО
% 1. Полосовой фильтр 45-55 Гц для напряжений
% [b_bpf, a_bpf, coeffs_bpf] = BiquadFilterDesigner.bpf(20, 10, Fs);
[b_bpf, a_bpf, coeffs_bpf] = BiquadFilterDesigner.lpf(100, Fs);
[b_bpf, a_bpf, coeffs_bpf] = BiquadFilterDesigner.lpf(Fsrez, Fs);
fprintf('1. Полосовой фильтр 45-55 Гц:\n');
BiquadFilterDesigner.generate_c_code(coeffs_bpf, 'voltage_bpf');
% 2. ФНЧ 100 Гц для токов
[b_lpf_current, a_lpf_current, coeffs_lpf_current] = BiquadFilterDesigner.lpf(100, Fs);
[b_lpf_current, a_lpf_current, coeffs_lpf_current] = BiquadFilterDesigner.lpf(Fsrez, Fs);
fprintf('2. ФНЧ 100 Гц (токи):\n');
BiquadFilterDesigner.generate_c_code(coeffs_lpf_current, 'current_lpf');
% 3. ФНЧ 10 Гц для температур
[b_lpf_temp, a_lpf_temp, coeffs_lpf_temp] = BiquadFilterDesigner.lpf(10, Fs);
fprintf('3. ФНЧ 10 Гц (температуры):\n');
BiquadFilterDesigner.generate_c_code(coeffs_lpf_temp, 'temperature_lpf');
%
% % 3. ФНЧ 10 Гц для температур
% [b_lpf_temp, a_lpf_temp, coeffs_lpf_temp] = BiquadFilterDesigner.lpf(10, Fs);
% fprintf('3. ФНЧ 10 Гц (температуры):\n');
% BiquadFilterDesigner.generate_c_code(coeffs_lpf_temp, 'temperature_lpf');
% Вывод коэффициентов в консоль
fprintf('\n=== РАСЧЕТНЫЕ КОЭФФИЦИЕНТЫ ===\n');
fprintf('Напряжение (BPF 45-55 Гц): b = [%.6f, %.6f, %.6f], a = [1, %.6f, %.6f]\n', ...
b_bpf, a_bpf(2), a_bpf(3));
fprintf('Ток (LPF 100 Гц): b = [%.6f, %.6f, %.6f], a = [1, %.6f, %.6f]\n', ...
b_lpf_current, a_lpf_current(2), a_lpf_current(3));
fprintf('Температура (LPF 10 Гц): b = [%.6f, %.6f, %.6f], a = [1, %.6f, %.6f]\n\n', ...
b_lpf_temp, a_lpf_temp(2), a_lpf_temp(3));
% fprintf('Ток (LPF 100 Гц): b = [%.6f, %.6f, %.6f], a = [1, %.6f, %.6f]\n', ...
% b_lpf_current, a_lpf_current(2), a_lpf_current(3));
% fprintf('Температура (LPF 10 Гц): b = [%.6f, %.6f, %.6f], a = [1, %.6f, %.6f]\n\n', ...
% b_lpf_temp, a_lpf_temp(2), a_lpf_temp(3));
%% Генерация тестовых сигналов
@@ -53,25 +54,25 @@ f_current = 50;
current_clean = 1 * sin(2*pi*f_current*t); %.* ...
%(1 + 0.2 * sin(2*pi*2*t)); % амплитудная модуляция 2 Гц
% 3. Температура (медленно меняющийся сигнал)
temperature_clean = 25 + 2 * sin(2*pi*0.1*t) + ... % медленные колебания 0.1 Гц
0.5 * sin(2*pi*1*t); % быстрые колебания 1 Гц
% % 3. Температура (медленно меняющийся сигнал)
% temperature_clean = 25 + 2 * sin(2*pi*0.1*t) + ... % медленные колебания 0.1 Гц
% 0.5 * sin(2*pi*1*t); % быстрые колебания 1 Гц
%% Добавление шума
voltage_noisy = voltage_clean + noise_levels.voltage * randn(size(t));
current_noisy = current_clean + noise_levels.current * randn(size(t));
temperature_noisy = temperature_clean + noise_levels.temperature * randn(size(t));
% temperature_noisy = temperature_clean + noise_levels.temperature * randn(size(t));
%% Фильтрация сигналов
voltage_filtered = filter(b_bpf, a_bpf, voltage_noisy);
current_filtered = filter(b_lpf_current, a_lpf_current, current_noisy);
temperature_filtered = filter(b_lpf_temp, a_lpf_temp, temperature_noisy);
% temperature_filtered = filter(b_lpf_temp, a_lpf_temp, temperature_noisy);
%% НОРМАЛИЗАЦИЯ УСИЛЕНИЯ (важно для правильного SNR)
% Получаем АЧХ для нормализации
[h_bpf, f_bpf] = freqz(b_bpf, a_bpf, 1024, Fs);
[h_lpf_curr, f_lpf_curr] = freqz(b_lpf_current, a_lpf_current, 1024, Fs);
[h_lpf_temp, f_lpf_temp] = freqz(b_lpf_temp, a_lpf_temp, 1024, Fs);
% [h_lpf_temp, f_lpf_temp] = freqz(b_lpf_temp, a_lpf_temp, 1024, Fs);
% Нормализация полосового фильтра на центральной частоте
[~, idx_50hz] = min(abs(f_bpf - 50));
@@ -85,11 +86,11 @@ gain_lpf_current = sum(b_lpf_current) / (1 + sum(a_lpf_current(2:end)));
if gain_lpf_current > 0
current_filtered = current_filtered / gain_lpf_current;
end
gain_lpf_temp = sum(b_lpf_temp) / (1 + sum(a_lpf_temp(2:end)));
if gain_lpf_temp > 0
temperature_filtered = temperature_filtered / gain_lpf_temp;
end
%
% gain_lpf_temp = sum(b_lpf_temp) / (1 + sum(a_lpf_temp(2:end)));
% if gain_lpf_temp > 0
% temperature_filtered = temperature_filtered / gain_lpf_temp;
% end
%% ОКНО 1: НАПРЯЖЕНИЕ
figure('Name', 'Анализ напряжения');
@@ -213,68 +214,68 @@ text(0.1, 0.4, sprintf('Улучшение: %.1f дБ', improvement_current), 'F
text(0.1, 0.2, sprintf('Задержка 50 Гц: %.1f мс', gd_lpf_curr(idx_50hz_curr)/Fs*1000), 'FontSize', 12);
title('Статистика фильтрации тока');
axis off;
%% ОКНО 3: ТЕМПЕРАТУРА
figure('Name', 'Анализ температуры');
% Временные характеристики
subplot(2,3,1);
plot(t, temperature_noisy, 'b', 'LineWidth', 1); hold on;
plot(t, temperature_filtered, 'r', 'LineWidth', 2);
plot(t, temperature_clean, 'g--', 'LineWidth', 1);
title('Температура: временная область');
legend('С шумом', 'Фильтрованный', 'Идеальный', 'Location', 'best');
xlabel('Время [с]'); ylabel('Температура [°C]');
grid on;
% АЧХ фильтра
subplot(2,3,2);
plot(f_lpf_temp, 20*log10(abs(h_lpf_temp)), 'LineWidth', 2);
title('АЧХ: ФНЧ 10 Гц');
xlabel('Частота [Гц]'); ylabel('Усиление [дБ]');
grid on; xlim([0, 20]);
% Спектр фильтрованного сигнала
subplot(2,3,3);
[P_temp, f_temp] = pwelch(temperature_filtered, [], [], 1024, Fs);
plot(f_temp, 10*log10(P_temp), 'LineWidth', 2);
title('Спектр фильтрованной температуры');
xlabel('Частота [Гц]'); ylabel('Мощность [дБ]');
grid on; xlim([0, 10]);
% Групповая задержка
subplot(2,3,4);
[gd_lpf_temp, f_gd_temp] = grpdelay(b_lpf_temp, a_lpf_temp, 1024, Fs);
plot(f_gd_temp, gd_lpf_temp/Fs*1000, 'LineWidth', 2);
title('Групповая задержка фильтра');
xlabel('Частота [Гц]'); ylabel('Задержка [мс]');
grid on; xlim([0, 20]);
% Детальный вид (последне 0.1 секунды)
idx_end_temp = max(1, length(t) - 0.1*Fs + 1):length(t); % последние 100 мс
subplot(2,3,5);
plot(t(idx_end_temp), temperature_noisy(idx_end_temp), 'b', 'LineWidth', 2); hold on;
plot(t(idx_end_temp), temperature_filtered(idx_end_temp), 'r', 'LineWidth', 2);
plot(t(idx_end_temp), temperature_clean(idx_end_temp), 'g--', 'LineWidth', 2);
title('Температура: УВЕЛИЧЕННЫЙ ВИД (900-1000 мс)');
legend('С шумом', 'Фильтрованный', 'Идеальный', 'Location', 'best');
xlabel('Время [с]'); ylabel('Температура [°C]');
grid on;
xlim([t(idx_end_temp(1)) t(idx_end_temp(end))]);
% Статистика
subplot(2,3,6);
snr_temp_in = snr(temperature_clean, temperature_noisy - temperature_clean);
snr_temp_out = snr(temperature_clean, temperature_filtered - temperature_clean);
improvement_temp = snr_temp_out - snr_temp_in;
[~, idx_1hz] = min(abs(f_gd_temp - 1));
text(0.1, 0.8, sprintf('SNR вход: %.1f дБ', snr_temp_in), 'FontSize', 12);
text(0.1, 0.6, sprintf('SNR выход: %.1f дБ', snr_temp_out), 'FontSize', 12);
text(0.1, 0.4, sprintf('Улучшение: %.1f дБ', improvement_temp), 'FontSize', 12);
text(0.1, 0.2, sprintf('Задержка 1 Гц: %.1f мс', gd_lpf_temp(idx_1hz)/Fs*1000), 'FontSize', 12);
title('Статистика фильтрации температуры');
axis off;
%
% %% ОКНО 3: ТЕМПЕРАТУРА
% figure('Name', 'Анализ температуры');
%
% % Временные характеристики
% subplot(2,3,1);
% plot(t, temperature_noisy, 'b', 'LineWidth', 1); hold on;
% plot(t, temperature_filtered, 'r', 'LineWidth', 2);
% plot(t, temperature_clean, 'g--', 'LineWidth', 1);
% title('Температура: временная область');
% legend('С шумом', 'Фильтрованный', 'Идеальный', 'Location', 'best');
% xlabel('Время [с]'); ylabel('Температура [°C]');
% grid on;
%
% % АЧХ фильтра
% subplot(2,3,2);
% plot(f_lpf_temp, 20*log10(abs(h_lpf_temp)), 'LineWidth', 2);
% title('АЧХ: ФНЧ 10 Гц');
% xlabel('Частота [Гц]'); ylabel('Усиление [дБ]');
% grid on; xlim([0, 20]);
%
% % Спектр фильтрованного сигнала
% subplot(2,3,3);
% [P_temp, f_temp] = pwelch(temperature_filtered, [], [], 1024, Fs);
% plot(f_temp, 10*log10(P_temp), 'LineWidth', 2);
% title('Спектр фильтрованной температуры');
% xlabel('Частота [Гц]'); ylabel('Мощность [дБ]');
% grid on; xlim([0, 10]);
%
% % Групповая задержка
% subplot(2,3,4);
% [gd_lpf_temp, f_gd_temp] = grpdelay(b_lpf_temp, a_lpf_temp, 1024, Fs);
% plot(f_gd_temp, gd_lpf_temp/Fs*1000, 'LineWidth', 2);
% title('Групповая задержка фильтра');
% xlabel('Частота [Гц]'); ylabel('Задержка [мс]');
% grid on; xlim([0, 20]);
%
% % Детальный вид (последне 0.1 секунды)
% idx_end_temp = max(1, length(t) - 0.1*Fs + 1):length(t); % последние 100 мс
% subplot(2,3,5);
% plot(t(idx_end_temp), temperature_noisy(idx_end_temp), 'b', 'LineWidth', 2); hold on;
% plot(t(idx_end_temp), temperature_filtered(idx_end_temp), 'r', 'LineWidth', 2);
% plot(t(idx_end_temp), temperature_clean(idx_end_temp), 'g--', 'LineWidth', 2);
% title('Температура: УВЕЛИЧЕННЫЙ ВИД (900-1000 мс)');
% legend('С шумом', 'Фильтрованный', 'Идеальный', 'Location', 'best');
% xlabel('Время [с]'); ylabel('Температура [°C]');
% grid on;
% xlim([t(idx_end_temp(1)) t(idx_end_temp(end))]);
%
% % Статистика
% subplot(2,3,6);
% snr_temp_in = snr(temperature_clean, temperature_noisy - temperature_clean);
% snr_temp_out = snr(temperature_clean, temperature_filtered - temperature_clean);
% improvement_temp = snr_temp_out - snr_temp_in;
%
% [~, idx_1hz] = min(abs(f_gd_temp - 1));
% text(0.1, 0.8, sprintf('SNR вход: %.1f дБ', snr_temp_in), 'FontSize', 12);
% text(0.1, 0.6, sprintf('SNR выход: %.1f дБ', snr_temp_out), 'FontSize', 12);
% text(0.1, 0.4, sprintf('Улучшение: %.1f дБ', improvement_temp), 'FontSize', 12);
% text(0.1, 0.2, sprintf('Задержка 1 Гц: %.1f мс', gd_lpf_temp(idx_1hz)/Fs*1000), 'FontSize', 12);
% title('Статистика фильтрации температуры');
% axis off;
%% Вывод результатов в командное окно
fprintf('\n=== ИТОГИ ФИЛЬТРАЦИИ С АВТОРАСЧЕТОМ КОЭФФИЦИЕНТОВ ===\n\n');
@@ -288,7 +289,7 @@ fprintf(' SNR вход: %.1f дБ, выход: %.1f дБ, улучшение: %
snr_current_in, snr_current_out, improvement_current);
fprintf(' Задержка на 50 Гц: %.1f мс\n\n', gd_lpf_curr(idx_50hz_curr)/Fs*1000);
fprintf('ТЕМПЕРАТУРА (ФНЧ 10 Гц):\n');
fprintf(' SNR вход: %.1f дБ, выход: %.1f дБ, улучшение: %.1f дБ\n', ...
snr_temp_in, snr_temp_out, improvement_temp);
fprintf(' Задержка на 1 Гц: %.1f мс\n\n', gd_lpf_temp(idx_1hz)/Fs*1000);
% fprintf('ТЕМПЕРАТУРА (ФНЧ 10 Гц):\n');
% fprintf(' SNR вход: %.1f дБ, выход: %.1f дБ, улучшение: %.1f дБ\n', ...
% snr_temp_in, snr_temp_out, improvement_temp);
% fprintf(' Задержка на 1 Гц: %.1f мс\n\n', gd_lpf_temp(idx_1hz)/Fs*1000);

View File

@@ -0,0 +1,230 @@
%% ===== РАСЧЕТ КОЭФФИЦИЕНТОВ БИКВАДРАТНОГО ФИЛЬТРА 50 Гц =====
% Запуск: Ctrl+S (сохранить), потом F5 (запустить)
clear all; close all; clc;
%% 1. ПАРАМЕТРЫ (МЕНЯТЬ ЗДЕСЬ)
fs = 1/25e-6; % Частота дискретизации (Гц)
fc = 100; % Частота среза 50 Гц
Q = 0.707; % Добротность (0.707 = Баттерворт)
filter_type = 'lpf'; % 'lpf', 'hpf', 'bpf', 'notch'
test_freq = 55;
%% 2. РАСЧЕТ КОЭФФИЦИЕНТОВ
w0 = 2 * pi * fc / fs;
alpha = sin(w0) / (2 * Q);
cos_w0 = cos(w0);
switch filter_type
case 'lpf' % ФНЧ
b0 = (1 - cos_w0) / 2;
b1 = 1 - cos_w0;
b2 = b0;
a0 = 1 + alpha;
a1 = -2 * cos_w0;
a2 = 1 - alpha;
case 'hpf' % ФВЧ
b0 = (1 + cos_w0) / 2;
b1 = -(1 + cos_w0);
b2 = b0;
a0 = 1 + alpha;
a1 = -2 * cos_w0;
a2 = 1 - alpha;
case 'bpf' % Полосовой
b0 = alpha;
b1 = 0;
b2 = -alpha;
a0 = 1 + alpha;
a1 = -2 * cos_w0;
a2 = 1 - alpha;
case 'notch' % Режекторный (50 Гц)
b0 = 1;
b1 = -2 * cos_w0;
b2 = 1;
a0 = 1 + alpha;
a1 = -2 * cos_w0;
a2 = 1 - alpha;
end
% Нормализация
b = [b0, b1, b2] / a0;
a = [1, a1/a0, a2/a0];
% Проверка полюсов
poles = roots([1, a(2), a(3)]);
if any(abs(poles) >= 1)
error('Фильтр НЕУСТОЙЧИВ! Полюса: %s', mat2str(poles));
end
%% 3. ВЫВОД РЕЗУЛЬТАТОВ
fprintf('\n\n');
fprintf('ПАРАМЕТРЫ ФИЛЬТРА:\n');
fprintf('Тип: %s\n', upper(filter_type));
fprintf('Частота среза: %.1f Гц\n', fc);
fprintf('Частота дискретизации: %.0f Гц\n', fs);
fprintf('Добротность Q: %.3f\n', Q);
fprintf('\n');
fprintf('\nКОЭФФИЦИЕНТЫ ДЛЯ CMSIS-DSP:\n');
fprintf('float32_t coeffs[5] = {\n');
fprintf(' %ff, // b0\n', b(1));
fprintf(' %ff, // b1\n', b(2));
fprintf(' %ff, // b2\n', b(3));
fprintf(' %ff, // a1\n', a(2));
fprintf(' %ff // a2\n', a(3));
fprintf('};\n');
fprintf('\nФОРМАТ ДЛЯ Biquad_InitDirect:\n');
fprintf('Biquad_InitDirect(&filter, %.6ff, %.6ff, %.6ff, %.6ff, %.6ff);\n', ...
b(1), b(2), b(3), a(2), a(3));
%% 4. ПРОВЕРКА ЧАСТОТНОЙ ХАРАКТЕРИСТИКИ
[h, f] = freqz(b, a, 2048, fs);
figure();
% АЧХ
subplot(1, 2, 1);
plot(f, 20*log10(abs(h)), 'LineWidth', 2);
grid on; hold on;
xline(fc, '--r', 'Частота среза', 'LineWidth', 1.5);
xlim([0, fs/2]);
ylim([-60, 5]);
xlabel('Частота (Гц)');
ylabel('Усиление (дБ)');
title(['АЧХ: ' upper(filter_type) ' фильтр ' num2str(fc) ' Гц']);
% ФЧХ
subplot(1, 2, 2);
plot(f, angle(h)*180/pi, 'LineWidth', 2);
grid on; hold on;
xline(fc, '--r', 'Частота среза', 'LineWidth', 1.5);
xlim([0, fs/2]);
xlabel('Частота (Гц)');
ylabel('Фазовый сдвиг (градусы)');
title(['ФЧХ: ' upper(filter_type) ' фильтр ' num2str(fc) ' Гц']);
%% 4b. ГРАФИК ЗАДЕРЖКИ (40-60 Гц)
figure();
% Рассчитываем на нужном диапазоне частот
f_range = 40:0.1:60; % Частоты с шагом 0.1 Гц
h_range = freqz(b, a, f_range, fs);
% Групповая задержка (численная производная)
phase_unwrapped = unwrap(angle(h_range));
w_range = 2*pi*f_range/fs;
group_delay = -gradient(phase_unwrapped) ./ gradient(w_range);
% Фазовая задержка
phase_delay = -angle(h_range) ./ w_range;
% График 1: Групповая задержка
subplot(1, 2, 1);
plot(f_range, group_delay * 1000, 'LineWidth', 3);
grid on; hold on;
xline(test_freq, '--r', '50 Гц', 'LineWidth', 2, 'FontSize', 12);
xlim([40, 60]);
ylim([0, max(group_delay*1000)*1.1]);
xlabel('Частота (Гц)', 'FontSize', 12);
ylabel('Групповая задержка (мс)', 'FontSize', 12);
title(['Групповая задержка: ' upper(filter_type) ' фильтр'], 'FontSize', 14);
% Значение на 50 Гц
idx_50 = find(f_range >= test_freq, 1);
if ~isempty(idx_50)
delay_50hz = group_delay(idx_50) * 1000;
plot(test_freq, delay_50hz, 'ro', 'MarkerSize', 15, 'LineWidth', 3);
text(test_freq + 0.5, delay_50hz*1.05, sprintf('%.2f мс', delay_50hz), ...
'FontSize', 14, 'FontWeight', 'bold', 'BackgroundColor', 'white');
end
% График 2: Фазовая задержка
subplot(1, 2, 2);
plot(f_range, phase_delay * 1000, 'LineWidth', 3, 'Color', [0, 0.5, 0]);
grid on; hold on;
xline(test_freq, '--r', '50 Гц', 'LineWidth', 2, 'FontSize', 12);
xlim([40, 60]);
xlabel('Частота (Гц)', 'FontSize', 12);
ylabel('Фазовая задержка (мс)', 'FontSize', 12);
title(['Фазовая задержка: ' upper(filter_type) ' фильтр'], 'FontSize', 14);
% Значение на 50 Гц
if ~isempty(idx_50)
phase_delay_50hz = phase_delay(idx_50) * 1000;
plot(test_freq, phase_delay_50hz, 'ro', 'MarkerSize', 15, 'LineWidth', 3);
text(test_freq + 0.5, phase_delay_50hz*1.05, sprintf('%.2f мс', phase_delay_50hz), ...
'FontSize', 14, 'FontWeight', 'bold', 'BackgroundColor', 'white');
end
%% 4c. ГРАФИК УСИЛЕНИЯ (40-60 Гц)
figure();
% Усиление в дБ
gain_db = 20*log10(abs(h_range));
plot(f_range, gain_db, 'LineWidth', 3, 'Color', [0.8, 0, 0]);
grid on; hold on;
xline(test_freq, '--r', '50 Гц', 'LineWidth', 2, 'FontSize', 12);
xlim([40, 60]);
ylim([min(gain_db)-1, max(gain_db)+1]);
xlabel('Частота (Гц)', 'FontSize', 12);
ylabel('Усиление (дБ)', 'FontSize', 12);
title(['Усиление: ' upper(filter_type) ' фильтр (40-60 Гц)'], 'FontSize', 14);
% Значение на 50 Гц
if ~isempty(idx_50)
gain_50hz = gain_db(idx_50);
plot(test_freq, gain_50hz, 'ro', 'MarkerSize', 15, 'LineWidth', 3);
text(test_freq +0.5, gain_50hz, sprintf('%.2f дБ', gain_50hz), ...
'FontSize', 14, 'FontWeight', 'bold', 'BackgroundColor', 'white');
end
%% 5. ТЕСТОВЫЙ СИГНАЛ
t = 0:1/fs:0.2; % 200 мс
f_test = test_freq; % Тест на 50 Гц
signal = sin(2*pi*f_test*t);
% Фильтрация
filtered = filter(b, a, signal);
figure();
subplot(2, 1, 1);
plot(t, signal, 'b', 'LineWidth', 1.5);
hold on; grid on;
plot(t, filtered, 'r', 'LineWidth', 2);
xlabel('Время (с)');
ylabel('Амплитуда');
legend('Исходный', 'После фильтра', 'Location', 'best');
title(['Тест фильтра: ' num2str(f_test) ' Гц']);
% Спектр
subplot(2, 1, 2);
[P1, f1] = pwelch(signal, [], [], [], fs);
[P2, f2] = pwelch(filtered, [], [], [], fs);
plot(f1, 10*log10(P1), 'b', 'LineWidth', 1.5);
hold on; grid on;
plot(f2, 10*log10(P2), 'r', 'LineWidth', 2);
xlim([0, 200]);
xlabel('Частота (Гц)');
ylabel('Мощность (дБ)');
legend('Исходный', 'После фильтра', 'Location', 'best');
title('Спектр сигнала');
fprintf('\n\n');
fprintf('ЗНАЧЕНИЯ НА %d Гц:\n', f_test);
if ~isempty(idx_50)
fprintf('Групповая задержка: %.2f мс\n', delay_50hz);
fprintf('Фазовая задержка: %.2f мс\n', phase_delay_50hz);
fprintf('Усиление: %.2f дБ (%.3f в линейном масштабе)\n', ...
gain_50hz, abs(h_range(idx_50)));
else
fprintf('Не удалось рассчитать значения на 50 Гц\n');
end
fprintf('\n');

View File

@@ -0,0 +1,199 @@
%% МОДЕЛИРОВАНИЕ ПОЛОСОВОГО ФИЛЬТРА С ДИФФЕРЕНЦИАТОРОМ
clear all; close all; clc;
%% 1. ПАРАМЕТРЫ ФИЛЬТРА (подставь свои значения)
b0 = 0.000392540911;
b1 = 0.0;
b2 = -0.000392540911;
a1 = -1.99915338;
a2 = 0.999214947;
% Или рассчитай новые:
center_freq = 50; % Центральная частота (Гц)
sample_freq = 40000; % Частота дискретизации (Гц)
bandwidth = 5; % Ширина полосы (Гц)
% %% 2. РАСЧЕТ КОЭФФИЦИЕНТОВ (если нужно)
% if 1 % Поставь 0 если используешь готовые коэффициенты выше
% % Отношение частот
% fc_ratio = center_freq / sample_freq;
% bandwidth_ratio = bandwidth / center_freq;
%
% % Расчет коэффициентов полосового фильтра
% w0 = 2 * pi * fc_ratio;
% Q = 1 / bandwidth_ratio;
% alpha = sin(w0) / (2 * Q);
% cos_w0 = cos(w0);
%
% % Коэффициенты биквадратного полосового фильтра
% b0_bp = alpha;
% b1_bp = 0;
% b2_bp = -alpha;
% a0_bp = 1 + alpha;
% a1_bp = -2 * cos_w0;
% a2_bp = 1 - alpha;
%
% % Нормализация (a0 = 1)
% b0 = b0_bp / a0_bp;
% b1 = b1_bp / a0_bp;
% b2 = b2_bp / a0_bp;
% a1 = a1_bp / a0_bp;
% a2 = a2_bp / a0_bp;
% end
fprintf('Коэффициенты фильтра:\n');
fprintf('b0 = %.6f\n', b0);
fprintf('b1 = %.6f\n', b1);
fprintf('b2 = %.6f\n', b2);
fprintf('a1 = %.6f\n', a1);
fprintf('a2 = %.6f\n', a2);
%% 3. ПРОВЕРКА УСТОЙЧИВОСТИ
poles = roots([1, a1, a2]);
fprintf('\nПолюса фильтра:\n');
for i = 1:length(poles)
fprintf(' pole%d = %.6f %+.6fj (|pole| = %.6f)\n', ...
i, real(poles(i)), imag(poles(i)), abs(poles(i)));
end
if any(abs(poles) >= 1)
fprintf(' ВНИМАНИЕ: Фильтр НЕУСТОЙЧИВ!\n');
else
fprintf(' Фильтр устойчив\n');
end
%% 5. ЧАСТОТНАЯ ХАРАКТЕРИСТИКА ПОЛНОГО ФИЛЬТРА (с дифференциатором)
% Дифференциатор: H_diff(z) = 1 - z^-1
b_diff = [1, -1];
a_diff = 1;
% % Каскадное соединение: дифференциатор + полосовой фильтр
% b_total = conv(b_diff, b_bp); % Перемножение полиномов
% a_total = conv(a_diff, a_bp);
% полосовой фильтр
b_total = [b0, b1, b2];
a_total = [1, a1, a2];
[h_total, f_total] = freqz(b_total, a_total, 2048, sample_freq);
figure();
subplot(1,2,1);
plot(f_total, 20*log10(abs(h_total)), 'r', 'LineWidth', 2);
grid on; hold on;
xline(center_freq, '--r', sprintf('%d Гц', center_freq), 'LineWidth', 1.5);
xlim([0, sample_freq/2]);
ylim([-60, 20]);
xlabel('Частота (Гц)');
ylabel('Усиление (дБ)');
title('АЧХ полного фильтра (дифференциатор + полосовой)');
subplot(1,2,2);
plot(f_total, angle(h_total)*180/pi, 'r', 'LineWidth', 2);
grid on; hold on;
xline(center_freq, '--r', sprintf('%d Гц', center_freq), 'LineWidth', 1.5);
xlim([0, sample_freq/2]);
xlabel('Частота (Гц)');
ylabel('Фаза (градусы)');
title('ФЧХ полного фильтра (дифференциатор + полосовой)');
%% 6. МОДЕЛИРОВАНИЕ ВО ВРЕМЕННОЙ ОБЛАСТИ
% Создаем тестовый сигнал
duration = 0.2; % 200 мс
t = 0:1/sample_freq:duration;
% Сигнал 50 Гц + шум
signal_freq = 50;
signal = sin(2*pi*signal_freq*t) + 0.1*randn(size(t));
% Имитация работы твоего кода на C
% Прямая форма II с дифференциатором
x1 = 0; x2 = 0; % Состояния входа фильтра
y1 = 0; y2 = 0; % Состояния выхода фильтра
prev_input = 0; % Для дифференциатора
filtered_signal = zeros(size(signal));
for i = 1:length(signal)
% 1. Дифференциатор
diff = signal(i);
prev_input = signal(i);
% 2. Полосовой фильтр (прямая форма II)
y = b0*diff + b1*x1 + b2*x2 - a1*y1 - a2*y2;
% 3. Обновление состояний
x2 = x1;
x1 = diff;
y2 = y1;
y1 = y;
filtered_signal(i) = y;
end
% Встроенная функция MATLAB для сравнения
filtered_matlab = filter(b_total, a_total, signal);
figure();
% Сигнал и выход
subplot(2,1,1);
plot(t, signal, 'b', 'LineWidth', 1);
hold on; grid on;
plot(t, filtered_signal, 'r', 'LineWidth', 2);
plot(t, filtered_matlab, 'g--', 'LineWidth', 1);
xlabel('Время (с)');
ylabel('Амплитуда');
legend('Исходный сигнал', 'Наш фильтр (имитация C)', 'MATLAB filter()', ...
'Location', 'best');
title(sprintf('Тестовый сигнал: %d Гц + помеха 150 Гц + шум', signal_freq));
% Ошибка между нашей реализацией и MATLAB
subplot(2,1,2);
error = filtered_signal - filtered_matlab;
plot(t, error, 'k', 'LineWidth', 1);
grid on;
xlabel('Время (с)');
ylabel('Ошибка');
title('Разница между нашей реализацией и MATLAB filter()');
fprintf('\nМаксимальная ошибка: %.2e\n', max(abs(error)));
%% 7. АНАЛИЗ НА ЧАСТОТЕ 50 Гц
freq_test = 50;
w_test = 2*pi*freq_test/sample_freq;
z = exp(1i*w_test);
% Передаточная функция полного фильтра
H_z = (b0 + b1*z^-1 + b2*z^-2) / (1 + a1*z^-1 + a2*z^-2) * (1 - z^-1);
fprintf('\n\n');
fprintf('АНАЛИЗ НА %d Гц:\n', freq_test);
fprintf('Усиление: %.3f (%.2f дБ)\n', abs(H_z), 20*log10(abs(H_z)));
fprintf('Фазовый сдвиг: %.1f градусов\n', angle(H_z)*180/pi);
fprintf('Задержка: %.2f мс (фазовая)\n', -angle(H_z)*1000/(2*pi*freq_test));
fprintf('\n');
%% 8. ГРАФИК ПОЛЮСОВ И НУЛЕЙ
figure();
% Единичная окружность
theta = linspace(0, 2*pi, 100);
plot(cos(theta), sin(theta), 'k--', 'LineWidth', 1);
hold on; grid on; axis equal;
% Полюса (красные кресты)
plot(real(poles), imag(poles), 'rx', 'MarkerSize', 15, 'LineWidth', 2);
% Нули полосового фильтра
zeros_bp = roots([b0, b1, b2]);
plot(real(zeros_bp), imag(zeros_bp), 'bo', 'MarkerSize', 10, 'LineWidth', 2);
% Нули дифференциатора (z=1)
plot(1, 0, 'go', 'MarkerSize', 10, 'LineWidth', 2);
xlim([-1.2, 1.2]);
ylim([-1.2, 1.2]);
xlabel('Re(z)');
ylabel('Im(z)');
title('Диаграмма полюсов и нулей фильтра');
legend('Единичная окружность', 'Полюса', 'Нули полосового фильтра', ...
'Нуль дифференциатора (z=1)', 'Location', 'best');