Compare commits

...

32 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
Razvalyaev
2775e0a9b6 Добавлен True RMS фильтр и оптимизации 2025-12-02 18:40:47 +03:00
Razvalyaev
3750d579fa лимит угла теперь не сатурирует, а начинается отсчет от него
плюс всякое по матлаб
2025-12-02 16:26:23 +03:00
Razvalyaev
eb6979aa27 в матлаб всё как у улитовского но не работает... почему...
плюс кучи мелочи по программе
2025-12-01 18:44:24 +03:00
Razvalyaev
7d40322f1e и еще 2025-11-30 21:22:53 +03:00
Razvalyaev
0de4aad4ef рефакторинг to_float->u2f
и всякое декоративное по модели
2025-11-30 21:22:04 +03:00
Razvalyaev
854ea6f6c2 Улушения:
- логики дма (только по полному заполнению)
- фильтров
- расчетов действующих значений
- параметров (сатурация, сделан параметр время нарастания пуска Tnt, добавлен параметр скважность пачки импульсов)
2025-11-28 18:25:05 +03:00
Razvalyaev
5624468d09 изменения под старую плату УПП 2025-11-27 12:17:58 +03:00
Razvalyaev
c26319f832 минор фиксы 2025-11-24 19:01:13 +03:00
Razvalyaev
925bfbe01e Коэфы альфа в дефолтах проставлены чтобы были независимыми от частоты дискретизации и всякое другое 2025-11-21 18:49:57 +03:00
Razvalyaev
21379c6188 фикс сброса угла при инициализайии режима работы и по мелочи 2025-11-21 16:01:43 +03:00
Razvalyaev
f3965db204 Сделано чтобы софтварный режим пачки импульсов выбирался по одному дефайну 2025-11-21 10:23:45 +03:00
Razvalyaev
b21d72b728 моделирование чет непонятное 2025-11-21 00:30:07 +03:00
Razvalyaev
d918ffb860 ШИМ и контроль угла отлажене на 417 более менее
Оптимизированы конфиг дефайны - в мкс и мс, а не тиках
2025-11-20 19:14:21 +03:00
Razvalyaev
2b22c5b0eb TIM3 и TIM8 поменяты местами
плюс по мелочи:
- полряность каналов
- симуляция тока для отладки без тока на АЦП
- регулируемый запас на максимальнйы угол
2025-11-20 10:57:47 +03:00
Razvalyaev
0e950148c5 Добавлен фильтр на частоту сети
Исправлены баги

Кое как работает:
- с периодом АЦП 30 мкс
- с периодом while 600 мкс
2025-11-19 17:06:28 +03:00
Razvalyaev
5090ddfd48 Всякие оптимизации и переделки под отладку на 417
По основному алгу
- расчет частоты сети тепер по всему периоду, а не полупериоду
- добавлены новые параметры: сдвиг синуса от нуля и сдвиг между фазами
2025-11-19 15:29:42 +03:00
Razvalyaev
b61a11b4fe Куча всего
1.В Keil добавлен таргет для тестирования прошивки на STM32F417
2. Матлаб все еще не до конца понятно как имено построить модель чтобы токи не подлетали от малейшего открытия тиристоров
2025-11-19 02:19:30 +03:00
Razvalyaev
5d8dc1183b Ничего не понятно. При регулировании и плавном уменьшении угла ток в какой-то момент резко взлетает и все уходит в колебательный процесс. 2025-11-18 23:45:03 +03:00
Razvalyaev
e1d6f1139d чебля 2025-11-18 22:30:20 +03:00
Razvalyaev
297cf9802e Начата работа над модулем с дискретными входами/выходами 2025-11-18 19:04:29 +03:00
Razvalyaev
6882d6d014 Рефакторинг и доработки 2025-11-18 18:15:37 +03:00
Razvalyaev
edac877616 Была путаница с углом альфа.
Он пидом считается наоборот. 0 -  ничего не подает - 1 полностью открываем
2025-11-18 00:49:16 +03:00
Razvalyaev
b887114510 Чет делал систему управления углом и поломалось всё... Угол неправильно рассчитывается и не в то время включается тиристор 2025-11-17 18:55:21 +03:00
Razvalyaev
e69eb0c8c9 Структуризирован PowerMonitor: сделано разделение на быстрие и медленные обработки
- Быстрые делаются в прерывании и заполняют с fast Напряжениями/Токами
- Медленные делаются в main, с каким-то периодом и работают с slow и mean Напряжениями/Токами/Температурами
2025-11-17 12:38:50 +03:00
75 changed files with 6667 additions and 1670 deletions

2
.gitignore vendored
View File

@@ -62,3 +62,5 @@ JLinkLog.txt
/MATLAB/MCU.lib
/MATLAB/MCU.mexw64.manifest
/MATLAB/upp_r2023.slx.autosave
/UPP/MDK-ARM/DebugConfig/
/UPP/MDK-ARM/Debug_F417

View File

@@ -3,6 +3,8 @@
#include "dsp/none.h"
#endif
#define _sqrtf(...) sqrtf(__VA_ARGS__)
#define __disable_irq()
#ifndef __ASM

View File

@@ -1247,7 +1247,7 @@ typedef void (*pTIM_CallbackTypeDef)(TIM_HandleTypeDef *htim); /*!< pointer to
* @arg TIM_IT_BREAK: Break interrupt
* @retval None
*/
#define __HAL_TIM_CLEAR_IT(__HANDLE__, __INTERRUPT__) ((__HANDLE__)->Instance->SR = ~(__INTERRUPT__))
#define __HAL_TIM_CLEAR_IT(__HANDLE__, __INTERRUPT__) ((__HANDLE__)->Instance->SR &= ~(__INTERRUPT__))
/**
* @brief Indicates whether or not the TIM Counter is used as downcounter.

View File

@@ -1254,7 +1254,7 @@ typedef void (*pTIM_CallbackTypeDef)(TIM_HandleTypeDef *htim); /*!< pointer to
* @arg TIM_IT_BREAK: Break interrupt
* @retval None
*/
#define __HAL_TIM_CLEAR_IT(__HANDLE__, __INTERRUPT__) ((__HANDLE__)->Instance->SR = ~(__INTERRUPT__))
#define __HAL_TIM_CLEAR_IT(__HANDLE__, __INTERRUPT__) ((__HANDLE__)->Instance->SR &= ~(__INTERRUPT__))
/**
* @brief Indicates whether or not the TIM Counter is used as downcounter.

View File

@@ -421,6 +421,7 @@ void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState Pin
{
GPIOx->BSRR = (uint32_t)GPIO_Pin << 16U;
}
__GPIO_BSRR_Sim(GPIOx);
}
/**
@@ -442,6 +443,7 @@ void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
/* Set selected pins that were at low level, and reset ones that were high */
GPIOx->BSRR = ((odr & GPIO_Pin) << GPIO_NUMBER) | (~odr & GPIO_Pin);
__GPIO_BSRR_Sim(GPIOx);
}
/**

View File

@@ -95,16 +95,76 @@ void DMA_Sim_Transfer(DMA_TypeDef* DMAx, uint32_t stream)
stream_sim->current_index++;
// Проверяем завершение передачи
// Определяем какой регистр использовать (LISR или HISR) в зависимости от потока
volatile uint32_t* lisr_reg = NULL;
volatile uint32_t* hisr_reg = NULL;
uint32_t tcif_mask = 0;
uint32_t htif_mask = 0;
// Настраиваем маски флагов в зависимости от потока
if (DMAx == DMA1) {
lisr_reg = &DMA1->LISR;
hisr_reg = &DMA1->HISR;
}
#ifdef DMA2
else if (DMAx == DMA2) {
lisr_reg = &DMA2->LISR;
hisr_reg = &DMA2->HISR;
}
#endif
// Устанавливаем маски для конкретного потока
switch (stream) {
case 0:
case 4:
tcif_mask = DMA_FLAG_TCIF0_4; htif_mask = DMA_FLAG_HTIF0_4; break;
case 1:
case 5:
tcif_mask = DMA_FLAG_TCIF1_5; htif_mask = DMA_FLAG_HTIF1_5; break;
case 2:
case 6:
tcif_mask = DMA_FLAG_TCIF2_6; htif_mask = DMA_FLAG_HTIF2_6; break;
case 3:
case 7:
tcif_mask = DMA_FLAG_TCIF3_7; htif_mask = DMA_FLAG_HTIF3_7; break;
};
// Проверяем и выставляем флаги
if (stream_sim->current_index == stream_sim->buffer_size / 2) {
// Половинное заполнение - выставляем флаг HTIF
if (stream < 4) {
*lisr_reg |= htif_mask; // Потоки 0-3 в LISR
}
else {
*hisr_reg |= htif_mask; // Потоки 4-7 в HISR
}
// Вызываем обработчик прерывания
//DMA_Call_IRQHandller(DMAx, stream); //todo
}
if (stream_sim->current_index >= stream_sim->buffer_size) {
// Полное заполнение - выставляем флаг TCIF
if (stream < 4) {
*lisr_reg |= tcif_mask; // Потоки 0-3 в LISR
}
else {
*hisr_reg |= tcif_mask; // Потоки 4-7 в HISR
}
stream_sim->transfer_complete = 1;
if (stream_sim->circular_mode) {
stream_sim->current_index = 0;
} else {
// В циклическом режиме не сбрасываем флаги - они должны быть сброшены программно
}
else {
stream_sim->transfer_enabled = 0;
stream_sim->enabled = 0;
DMA_Call_IRQHandller(DMAx, stream);
}
// Вызываем обработчик прерывания
DMA_Call_IRQHandller(DMAx, stream);
}
}

View File

@@ -16,6 +16,18 @@ void Simulate_GPIO_BSRR(void)
#ifdef GPIOD
__GPIO_BSRR_Sim(GPIOD);
#endif
#ifdef GPIOE
__GPIO_BSRR_Sim(GPIOE);
#endif
#ifdef GPIOF
__GPIO_BSRR_Sim(GPIOF);
#endif
#ifdef GPIOG
__GPIO_BSRR_Sim(GPIOG);
#endif
#ifdef GPIOH
__GPIO_BSRR_Sim(GPIOH);
#endif
}

View File

@@ -13,37 +13,50 @@ void TIM_Call_IRQHandller(TIM_TypeDef* TIMx);
/* Базовая функция для симуляции таймера: она вызывается каждый шаг симуляции */
void TIM_Simulation(TIM_TypeDef *TIMx, struct TIM_Sim *TIMS)
{
if (!(TIMx->CR1 & TIM_CR1_CEN)) // таймер не включен - возврат
{
TIMS->RepititiveCnt = 0;
return;
}
Overflow_Check(TIMx, TIMS);
// Выбор режима работы таймера
switch (TIMx->SMCR & TIM_SMCR_SMS) // TIMER MODE
{
// обычный счет
case(TIM_SLAVEMODE_DISABLE):// NORMAL MODE counting
TIMx_Count(TIMx, TIMS);
Channels_Simulation(TIMx, TIMS); // CaptureCompare and PWM channels simulation
Write_TRGO(TIMx, TIMS);
break;
switch (TIMx->SMCR & TIM_SMCR_SMS) // TIMER MODE
{
// обычный счет
case(TIM_SLAVEMODE_DISABLE):// NORMAL MODE counting
TIMx_Count(TIMx, TIMS);
Channels_Simulation(TIMx, TIMS); // CaptureCompare and PWM channels simulation
Write_TRGO(TIMx, TIMS);
break;
// включение слейв таймера по ивенту
case(TIM_SLAVEMODE_TRIGGER): // SLAVE MODE: TRIGGER MODE
Slave_Mode_Check_Source(TIMx, TIMS);
TIMx_Count(TIMx, TIMS);
Channels_Simulation(TIMx, TIMS); // CaptureCompare and PWM channels simulation
Write_TRGO(TIMx, TIMS);
break;
}
// включение слейв таймера по ивенту
case(TIM_SLAVEMODE_TRIGGER): // SLAVE MODE: TRIGGER MODE
Slave_Mode_Check_Source(TIMx, TIMS);
TIMx_Count(TIMx, TIMS);
Channels_Simulation(TIMx, TIMS); // CaptureCompare and PWM channels simulation
Write_TRGO(TIMx, TIMS);
break;
}
// EGR
TIM_EGR_Simulation(TIMx);
// Прерывание если какое-то выставлено
if (TIMx->SR & (TIM_SR_UIF | TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF))
{
TIM_Call_IRQHandller(TIMx); // call HANDLER
}
}
/* Счет таймера за один такт */
void TIMx_Count(TIM_TypeDef* TIMx, struct TIM_Sim* TIMS)
{
if ((TIMx->CR1 & TIM_CR1_DIR) && TIMx->CR1) // up COUNTER and COUNTER ENABLE
if ((TIMx->CR1 & TIM_CR1_DIR)) // up COUNTER ENABLE
TIMS->tx_cnt -= TIMS->tx_step / (TIMx->PSC + 1);
else if (((TIMx->CR1 & TIM_CR1_DIR) == 0) && TIMx->CR1) // down COUNTER and COUNTER ENABLE
else if (((TIMx->CR1 & TIM_CR1_DIR) == 0)) // down COUNTER ENABLE
TIMS->tx_cnt += TIMS->tx_step / (TIMx->PSC + 1);
TIMx->CNT = (uint32_t)TIMS->tx_cnt;
}
@@ -57,18 +70,40 @@ void Overflow_Check(TIM_TypeDef* TIMx, struct TIM_Sim* TIMS)
if ((TIMx->CR1 & TIM_CR1_ARPE) == 0) TIMS->RELOAD = TIMx->ARR; // PRELOAD disable - update ARR every itteration
if (TIMS->tx_cnt > TIMS->RELOAD || TIMS->tx_cnt < 0) // OVERFLOW
{
TIMS->RELOAD = TIMx->ARR; // RELOAD ARR
if (TIMS->tx_cnt > TIMx->ARR) // reset COUNTER
TIMS->tx_cnt -= TIMS->RELOAD + 1;
else if (TIMS->tx_cnt < 0)
TIMS->tx_cnt += TIMS->RELOAD + 1;
if (TIMS->tx_step > TIMS->RELOAD)
TIMS->tx_cnt = 0;
if (!(TIMx->CR1 & TIM_CR1_OPM)) // if no one pulse
{
TIMS->Updated = 1;
TIMS->RELOAD = TIMx->ARR; // RELOAD ARR
if (TIMx->DIER & TIM_DIER_UIE) {
TIMx->SR |= TIM_SR_UIF;
}
}
else // if one pulse mode enabled
{
if (TIMS->RepititiveCnt >= TIMx->RCR)
{
TIMS->RepititiveCnt = 0;
TIMS->Updated = 1;
TIMx->CR1 &= ~TIM_CR1_CEN;
if (TIMx->DIER & TIM_DIER_UIE) {
TIMx->SR |= TIM_SR_UIF;
}
}
else
{
TIMS->RepititiveCnt++;
}
}
if (TIMS->tx_cnt > TIMx->ARR) // reset COUNTER
TIMS->tx_cnt -= TIMS->RELOAD+1;
else if (TIMS->tx_cnt < 0)
TIMS->tx_cnt += TIMS->RELOAD+1;
if(TIMS->tx_step > TIMS->RELOAD)
TIMS->tx_cnt = 0;
TIM_Call_IRQHandller(TIMx); // call HANDLER
}
}
}
@@ -134,12 +169,10 @@ void CC_PWM_Ch1_Simulation(TIM_TypeDef *TIMx, struct TIM_Sim *TIMS)
if (((TIMS->tx_cnt - TIMS->tx_step) < TIMx->CCR1) && (TIMS->tx_cnt >= TIMx->CCR1))
{
TIMx->SR |= TIM_SR_CC1IF;
TIM_Call_IRQHandller(TIMx);
}
else if (((TIMS->tx_cnt - TIMS->tx_step) > TIMx->CCR1) && (TIMS->tx_cnt <= TIMx->CCR1))
{
TIMx->SR |= TIM_SR_CC1IF;
TIM_Call_IRQHandller(TIMx);
}
}
}
@@ -188,12 +221,10 @@ void CC_PWM_Ch2_Simulation(TIM_TypeDef *TIMx, struct TIM_Sim *TIMS)
if (((TIMS->tx_cnt - TIMS->tx_step) < TIMx->CCR2) && (TIMS->tx_cnt >= TIMx->CCR2))
{
TIMx->SR |= TIM_SR_CC2IF;
TIM_Call_IRQHandller(TIMx);
}
else if (((TIMS->tx_cnt - TIMS->tx_step) > TIMx->CCR2) && (TIMS->tx_cnt <= TIMx->CCR2))
{
TIMx->SR |= TIM_SR_CC2IF;
TIM_Call_IRQHandller(TIMx);
}
}
}
@@ -242,12 +273,10 @@ void CC_PWM_Ch3_Simulation(TIM_TypeDef *TIMx, struct TIM_Sim *TIMS)
if (((TIMS->tx_cnt - TIMS->tx_step) < TIMx->CCR3) && (TIMS->tx_cnt >= TIMx->CCR3))
{
TIMx->SR |= TIM_SR_CC3IF;
TIM_Call_IRQHandller(TIMx);
}
else if (((TIMS->tx_cnt - TIMS->tx_step) > TIMx->CCR3) && (TIMS->tx_cnt <= TIMx->CCR3))
{
TIMx->SR |= TIM_SR_CC3IF;
TIM_Call_IRQHandller(TIMx);
}
}
}
@@ -296,12 +325,10 @@ void CC_PWM_Ch4_Simulation(TIM_TypeDef *TIMx, struct TIM_Sim *TIMS)
if (((TIMS->tx_cnt - TIMS->tx_step) < TIMx->CCR4) && (TIMS->tx_cnt >= TIMx->CCR4))
{
TIMx->SR |= TIM_SR_CC4IF;
TIM_Call_IRQHandller(TIMx);
}
else if (((TIMS->tx_cnt - TIMS->tx_step) > TIMx->CCR4) && (TIMS->tx_cnt <= TIMx->CCR4))
{
TIMx->SR |= TIM_SR_CC4IF;
TIM_Call_IRQHandller(TIMx);
}
}
}
@@ -456,6 +483,42 @@ void Write_TRGO(TIM_TypeDef* TIMx, struct TIM_Sim* TIMS)
//--------------------MISC (temporary) FUNCTIONS--------------------//
void TIM_EGR_Simulation(TIM_TypeDef* TIMx, struct TIM_Sim* TIMS)
{
// Update
if (TIMx->EGR & TIM_EGR_UG) {
TIMx->EGR &= ~TIM_EGR_UG;
if (TIMx->DIER & TIM_DIER_UIE) {
TIMx->SR |= TIM_SR_UIF;
}
}
// Channels
if (TIMx->EGR & TIM_EGR_CC1G) {
TIMx->EGR &= ~TIM_EGR_CC1G;
if (TIMx->DIER & TIM_IT_CC1) {
TIMx->SR |= TIM_SR_CC1IF;
}
}
if (TIMx->EGR & TIM_EGR_CC2G) {
TIMx->EGR &= ~TIM_EGR_CC2G;
if (TIMx->DIER & TIM_IT_CC2) {
TIMx->SR |= TIM_SR_CC2IF;
}
}
if (TIMx->EGR & TIM_EGR_CC3G) {
TIMx->EGR &= ~TIM_EGR_CC3G;
if (TIMx->DIER & TIM_IT_CC3) {
TIMx->SR |= TIM_SR_CC3IF;
}
}
if (TIMx->EGR & TIM_EGR_CC4G) {
TIMx->EGR &= ~TIM_EGR_CC4G;
if (TIMx->DIER & TIM_IT_CC4) {
TIMx->SR |= TIM_SR_CC4IF;
}
}
}
/* Определение источника для запуска таймера в SLAVE MODE */
void Slave_Mode_Check_Source(TIM_TypeDef* TIMx, struct TIM_Sim* TIMS)
{
@@ -674,15 +737,15 @@ void TIM_Call_IRQHandller(TIM_TypeDef* TIMx)
TIM6_DAC_IRQHandler();
else if (TIMx == TIM7)
TIM7_IRQHandler();
else if ((TIMx == TIM8) || (TIMx == TIM13))
else if (((TIMx == TIM8) && (TIM8->SR & TIM_SR_UIF)) || (TIMx == TIM13))
TIM8_UP_TIM13_IRQHandler();
else if ((TIMx == TIM1) || (TIMx == TIM9))
else if (((TIMx == TIM1) && 0) || (TIMx == TIM9))
TIM1_BRK_TIM9_IRQHandler();
else if ((TIMx == TIM1) || (TIMx == TIM11))
else if (((TIMx == TIM1) && (TIM1->SR & (TIM_SR_CC1IF|TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF))) || (TIMx == TIM11))
TIM1_TRG_COM_TIM11_IRQHandler();
else if ((TIMx == TIM8) || (TIMx == TIM12))
else if (((TIMx == TIM8) && 0) || (TIMx == TIM12))
TIM8_BRK_TIM12_IRQHandler();
else if ((TIMx == TIM8) || (TIMx == TIM14))
else if (((TIMx == TIM8) && (TIM8->SR & (TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF | TIM_SR_CC4IF))) || (TIMx == TIM14))
TIM8_TRG_COM_TIM14_IRQHandler();
SR_PROCESS(TIMx, SR);

View File

@@ -73,6 +73,7 @@ struct TIM_Sim
double tx_cnt; // счетчик таймера
double tx_step; // шаг счета за один шаг симуляции
long long RELOAD; // буфер, если PRELOAD = 1
long long RepititiveCnt; // буфер, если PRELOAD = 1
struct Channels_Sim Channels; // структура для симуляции каналов
};
/////////////////////////////////////////////////////////////////////
@@ -108,6 +109,8 @@ void Write_TRGO(TIM_TypeDef* TIMx, struct TIM_Sim* TIMS);
//--------------------MISC (temporary) FUNCTIONS--------------------//
void TIM_Call_IRQHandller(TIM_TypeDef* TIMx);
/* Определение источника для запуска таймера в SLAVE MODE */
void Slave_Mode_Check_Source(TIM_TypeDef* TIMx, struct TIM_Sim* TIMS);
//------------------------------------------------------------------//

View File

@@ -26,6 +26,7 @@ void Simulate_Periph_Sim(void)
{
Simulate_TIMs();
Simulate_ADCs();
Simulate_GPIO_BSRR();
}
// MCU PERIPH DEINIT
@@ -216,10 +217,10 @@ void Init_TIM_SIM(void)
tim8s.Channels.OC1_PIN_SHIFT = 0;
tim8s.Channels.OC2_GPIOx = GPIOA;
tim8s.Channels.OC2_PIN_SHIFT = 0;
tim8s.Channels.OC3_GPIOx = GPIOA;
tim8s.Channels.OC3_PIN_SHIFT = 0;
tim8s.Channels.OC4_GPIOx = GPIOA;
tim8s.Channels.OC4_PIN_SHIFT = 0;
tim8s.Channels.OC3_GPIOx = GPIOC;
tim8s.Channels.OC3_PIN_SHIFT = 8;
tim8s.Channels.OC4_GPIOx = GPIOC;
tim8s.Channels.OC4_PIN_SHIFT = 9;
#endif
#ifdef USE_TIM9
memset(&tim9s, 0, sizeof(tim9s));

View File

@@ -22,14 +22,16 @@ SIM__MCUHandleTypeDef hmcu; ///< Хендл для управления
*/
const int inLengths[IN_PORT_NUMB] = {
ADC_PORT_1_WIDTH,
IN_PORT_2_WIDTH
PUI_PORT_2_WIDTH,
INTERNAL_PORT_3_WIDTH
};
/**
* @brief Таблица смещений в выходном массиве IN
*/
const int inOffsets[IN_PORT_NUMB] = {
OFFSET_IN_ARRAY_1,
OFFSET_IN_ARRAY_2
OFFSET_IN_ARRAY_2,
OFFSET_IN_ARRAY_3
};
/**
@@ -37,8 +39,10 @@ const int inOffsets[IN_PORT_NUMB] = {
*/
const int outLengths[OUT_PORT_NUMB] = {
THYR_PORT_1_WIDTH,
PM_PORT_2_WIDTH,
OUT_PORT_3_WIDTH
DO_PORT_2_WIDTH,
PM_PORT_3_WIDTH,
ANGLE_PORT_4_WIDTH,
OUT_PORT_5_WIDTH
};
/**
* @brief Таблица смещений в выходном массиве OUT
@@ -46,7 +50,9 @@ const int outLengths[OUT_PORT_NUMB] = {
const int outOffsets[OUT_PORT_NUMB] = {
OFFSET_OUT_ARRAY_1,
OFFSET_OUT_ARRAY_2,
OFFSET_OUT_ARRAY_3
OFFSET_OUT_ARRAY_3,
OFFSET_OUT_ARRAY_4,
OFFSET_OUT_ARRAY_5
};
// INPUT/OUTPUTS AUTO-PARAMS END

View File

@@ -53,14 +53,17 @@
// Parameters of S_Function
// INPUT/OUTPUTS PARAMS START
#define IN_PORT_NUMB 2
#define IN_PORT_NUMB 3
#define ADC_PORT_1_WIDTH 6
#define IN_PORT_2_WIDTH 16
#define PUI_PORT_2_WIDTH 10
#define INTERNAL_PORT_3_WIDTH 16
#define OUT_PORT_NUMB 3
#define OUT_PORT_NUMB 5
#define THYR_PORT_1_WIDTH 6
#define PM_PORT_2_WIDTH 32
#define OUT_PORT_3_WIDTH 16
#define DO_PORT_2_WIDTH 3
#define PM_PORT_3_WIDTH 32
#define ANGLE_PORT_4_WIDTH 16
#define OUT_PORT_5_WIDTH 16
// INPUT/OUTPUTS PARAMS END
/** WRAPPER_CONF
@@ -92,19 +95,22 @@
// INPUT/OUTPUTS AUTO-PARAMS START
/// === Полный размер буфера ===
#define TOTAL_IN_SIZE (ADC_PORT_1_WIDTH + IN_PORT_2_WIDTH)
#define TOTAL_IN_SIZE (ADC_PORT_1_WIDTH + PUI_PORT_2_WIDTH + INTERNAL_PORT_3_WIDTH)
/// === Смещения массивов (внутри общего буфера) ===
#define OFFSET_IN_ARRAY_1 0
#define OFFSET_IN_ARRAY_2 (OFFSET_IN_ARRAY_1 + ADC_PORT_1_WIDTH)
#define OFFSET_IN_ARRAY_3 (OFFSET_IN_ARRAY_2 + PUI_PORT_2_WIDTH)
/// === Полный размер буфера ===
#define TOTAL_OUT_SIZE (THYR_PORT_1_WIDTH + PM_PORT_2_WIDTH + OUT_PORT_3_WIDTH)
#define TOTAL_OUT_SIZE (THYR_PORT_1_WIDTH + DO_PORT_2_WIDTH + PM_PORT_3_WIDTH + ANGLE_PORT_4_WIDTH + OUT_PORT_5_WIDTH)
/// === Смещения массивов (внутри общего буфера) ===
#define OFFSET_OUT_ARRAY_1 0
#define OFFSET_OUT_ARRAY_2 (OFFSET_OUT_ARRAY_1 + THYR_PORT_1_WIDTH)
#define OFFSET_OUT_ARRAY_3 (OFFSET_OUT_ARRAY_2 + PM_PORT_2_WIDTH)
#define OFFSET_OUT_ARRAY_3 (OFFSET_OUT_ARRAY_2 + DO_PORT_2_WIDTH)
#define OFFSET_OUT_ARRAY_4 (OFFSET_OUT_ARRAY_3 + PM_PORT_3_WIDTH)
#define OFFSET_OUT_ARRAY_5 (OFFSET_OUT_ARRAY_4 + ANGLE_PORT_4_WIDTH)
// INPUT/OUTPUTS AUTO-PARAMS END

View File

@@ -54,6 +54,33 @@ set code_PERIPH=.\MCU_STM32_Matlab\stm32_matlab_conf.c^
.\MCU_STM32_Matlab\Drivers\STM32_SIMULINK\stm32_matlab_gpio.c^
.\MCU_STM32_Matlab\Drivers\STM32_SIMULINK\stm32_matlab_dma.c^
.\MCU_STM32_Matlab\Drivers\STM32_SIMULINK\stm32_periph_registers.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\BasicMathFunctions\BasicMathFunctions.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\BasicMathFunctions\BasicMathFunctionsF16.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\BayesFunctions\BayesFunctions.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\BayesFunctions\BayesFunctionsF16.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\CommonTables\CommonTables.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\CommonTables\CommonTablesF16.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\ComplexMathFunctions\ComplexMathFunctions.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\ComplexMathFunctions\ComplexMathFunctionsF16.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\ControllerFunctions\ControllerFunctions.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\DistanceFunctions\DistanceFunctionsF16.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\FastMathFunctions\FastMathFunctions.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\FastMathFunctions\FastMathFunctionsF16.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\FilteringFunctions\FilteringFunctions.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\FilteringFunctions\FilteringFunctionsF16.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\InterpolationFunctions\InterpolationFunctions.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\InterpolationFunctions\InterpolationFunctionsF16.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\MatrixFunctions\MatrixFunctions.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\MatrixFunctions\MatrixFunctionsF16.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\QuaternionMathFunctions\QuaternionMathFunctions.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\StatisticsFunctions\StatisticsFunctions.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\StatisticsFunctions\StatisticsFunctionsF16.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\SupportFunctions\SupportFunctions.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\SupportFunctions\SupportFunctionsF16.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\SVMFunctions\SVMFunctions.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\SVMFunctions\SVMFunctionsF16.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\TransformFunctions\TransformFunctions.c^
.\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\TransformFunctions\TransformFunctionsF16.c^
.\MCU_STM32_Matlab\Drivers\STM32F4xx_HAL_Driver\Src\stm32f4xx_hal.c^
.\MCU_STM32_Matlab\Drivers\STM32F4xx_HAL_Driver\Src\stm32f4xx_hal_adc.c^
.\MCU_STM32_Matlab\Drivers\STM32F4xx_HAL_Driver\Src\stm32f4xx_hal_adc_ex.c^
@@ -61,6 +88,7 @@ set code_PERIPH=.\MCU_STM32_Matlab\stm32_matlab_conf.c^
.\MCU_STM32_Matlab\Drivers\STM32F4xx_HAL_Driver\Src\stm32f4xx_hal_dma.c^
.\MCU_STM32_Matlab\Drivers\STM32F4xx_HAL_Driver\Src\stm32f4xx_hal_exti.c^
.\MCU_STM32_Matlab\Drivers\STM32F4xx_HAL_Driver\Src\stm32f4xx_hal_gpio.c^
.\MCU_STM32_Matlab\Drivers\STM32F4xx_HAL_Driver\Src\stm32f4xx_hal_iwdg.c^
.\MCU_STM32_Matlab\Drivers\STM32F4xx_HAL_Driver\Src\stm32f4xx_hal_pwr.c^
.\MCU_STM32_Matlab\Drivers\STM32F4xx_HAL_Driver\Src\stm32f4xx_hal_pwr_ex.c^
.\MCU_STM32_Matlab\Drivers\STM32F4xx_HAL_Driver\Src\stm32f4xx_hal_rcc.c^
@@ -75,7 +103,24 @@ set includes_PERIPH=-I".\MCU_STM32_Matlab\."^
-I".\MCU_STM32_Matlab\Drivers\CMSIS"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\Device\STM32F4xx"^
-I".\MCU_STM32_Matlab\Drivers\STM32F4xx_HAL_Driver\Inc"^
-I".\MCU_STM32_Matlab\Drivers\STM32F4xx_HAL_Driver\Inc\Legacy"
-I".\MCU_STM32_Matlab\Drivers\STM32F4xx_HAL_Driver\Inc\Legacy"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Include"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\DSP\PrivateInclude"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\BasicMathFunctions"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\BayesFunctions"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\CommonTables"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\ComplexMathFunctions"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\ControllerFunctions"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\DistanceFunctions"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\FastMathFunctions"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\FilteringFunctions"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\InterpolationFunctions"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\MatrixFunctions"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\QuaternionMathFunctions"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\StatisticsFunctions"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\SupportFunctions"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\SVMFunctions"^
-I".\MCU_STM32_Matlab\Drivers\CMSIS\DSP\Source\TransformFunctions"
:: PERIPH BAT END
::-------------------------------------------------------------------------

View File

@@ -18,15 +18,17 @@ void app_init(void) {
// если не используется отдельный поток для main().
HAL_Init();
MX_DMA_Init();
MX_IWDG_Init();
MX_TIM1_Init();
MX_TIM2_Init();
MX_TIM3_Init();
MX_TIM8_Init();
MX_TIM5_Init();
MX_ADC3_Init();
UPP_SetDefault(1, 1);
UPP_Init();
UPP_Params_SetDefault(1, 1);
UPP_App_Init();
UPP_PreWhile();
UPP_DO.CEN(DISABLE);
// USER APP INIT END
}

View File

@@ -6,26 +6,82 @@
#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
void Write_Thyristors(real_T* Buffer, int ind_port)
void pwm_wtf(PWM_State_t state1, PWM_State_t state2, int* pwm_pin)
{
int pwm1_pin = PIN_READ(PWM1);
int pwm2_pin = PIN_READ(PWM2);
int pwm3_pin = PIN_READ(PWM3);
int pwm4_pin = PIN_READ(PWM4);
int pwm5_pin = PIN_READ(PWM5);
int pwm6_pin = PIN_READ(PWM6);
if ((*pwm_pin == 0) && (state1 == PWM_THYR_TIM_ACTIVE))
{
*pwm_pin = 1;
}
else if ((*pwm_pin == 1) && (state2 == PWM_THYR_TIM_ACTIVE))
{
*pwm_pin = 0;
}
}
int pwm1_pin = 0;
int pwm2_pin = 0;
int pwm3_pin = 0;
int pwm4_pin = 0;
int pwm5_pin = 0;
int pwm6_pin = 0;
void Write_UPP_Outputs(real_T* Buffer, int ind_port)
{
//int pwm1_pin = PIN_READ(PWM1);
//int pwm2_pin = PIN_READ(PWM2);
//int pwm3_pin = PIN_READ(PWM3);
//int pwm4_pin = PIN_READ(PWM4);
//int pwm5_pin = PIN_READ(PWM5);
//int pwm6_pin = PIN_READ(PWM6);
int pwm1_pin = (upp.hpwm.AllPhases[PHASE_A_POS].State == PWM_THYR_TIM_ACTIVE);
int pwm2_pin = (upp.hpwm.AllPhases[PHASE_A_NEG].State == PWM_THYR_TIM_ACTIVE);
int pwm3_pin = (upp.hpwm.AllPhases[PHASE_B_POS].State == PWM_THYR_TIM_ACTIVE);
int pwm4_pin = (upp.hpwm.AllPhases[PHASE_B_NEG].State == PWM_THYR_TIM_ACTIVE);
int pwm5_pin = (upp.hpwm.AllPhases[PHASE_C_POS].State == PWM_THYR_TIM_ACTIVE);
int pwm6_pin = (upp.hpwm.AllPhases[PHASE_C_NEG].State == PWM_THYR_TIM_ACTIVE);
//pwm_wtf(upp.hpwm.AllPhases[PHASE_A_POS].State, upp.hpwm.AllPhases[PHASE_A_NEG].State, &pwm1_pin);
//pwm_wtf(upp.hpwm.AllPhases[PHASE_A_NEG].State, upp.hpwm.AllPhases[PHASE_A_POS].State, &pwm2_pin);
//pwm_wtf(upp.hpwm.AllPhases[PHASE_B_POS].State, upp.hpwm.AllPhases[PHASE_B_NEG].State, &pwm3_pin);
//pwm_wtf(upp.hpwm.AllPhases[PHASE_B_NEG].State, upp.hpwm.AllPhases[PHASE_B_POS].State, &pwm4_pin);
//pwm_wtf(upp.hpwm.AllPhases[PHASE_C_POS].State, upp.hpwm.AllPhases[PHASE_C_NEG].State, &pwm5_pin);
//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 = upp.errors->common;
if (CEN_GPIO_Port->ODR & CEN_Pin)
{
WriteOutputArray(0, ind_port, 0);
WriteOutputArray(0, ind_port, 1);
WriteOutputArray(0, ind_port, 2);
WriteOutputArray(0, ind_port, 3);
WriteOutputArray(0, ind_port, 4);
WriteOutputArray(0, ind_port, 5);
WriteOutputArray(0, ind_port+1, 0);
WriteOutputArray(0, ind_port+1, 1);
WriteOutputArray(0, ind_port+1, 2);
}
else
{
WriteOutputArray(pwm1_pin, ind_port, 0);
WriteOutputArray(pwm2_pin, ind_port, 1);
WriteOutputArray(pwm3_pin, ind_port, 2);
WriteOutputArray(pwm4_pin, ind_port, 3);
WriteOutputArray(pwm5_pin, ind_port, 4);
WriteOutputArray(pwm6_pin, ind_port, 5);
WriteOutputArray(ready, ind_port+1, 0);
WriteOutputArray(work, ind_port+1, 1);
WriteOutputArray(err, ind_port+1, 2);
}
WriteOutputArray(pwm1_pin, ind_port, 0);
WriteOutputArray(pwm2_pin, ind_port, 1);
WriteOutputArray(pwm3_pin, ind_port, 2);
WriteOutputArray(pwm4_pin, ind_port, 3);
WriteOutputArray(pwm5_pin, ind_port, 4);
WriteOutputArray(pwm6_pin, ind_port, 5);
}
void Write_PowerMonitor(real_T* Buffer, int ind_port)
@@ -33,7 +89,7 @@ void Write_PowerMonitor(real_T* Buffer, int ind_port)
int nn = 0;
for (int i = 0; i < 3; i++)
{ //0-2
WriteOutputArray(upp.pm.measured.Ureal[i], ind_port, nn++);
WriteOutputArray(upp.pm.measured.fast.U[i], ind_port, nn++);
}
for (int i = 0; i < 3; i++)
{ //3-5
@@ -41,23 +97,48 @@ void Write_PowerMonitor(real_T* Buffer, int ind_port)
}
for (int i = 0; i < 3; i++)
{ //6-8
WriteOutputArray(upp.pm.measured.F[i], ind_port, nn++);
WriteOutputArray(upp.pm.measured.final.F[i], ind_port, nn++);
}
for (int i = 0; i < 3; i++)
{ //9-11
WriteOutputArray(upp.pm.measured.Ireal[i], ind_port, nn++);
WriteOutputArray(upp.pm.measured.fast.I[i], ind_port, nn++);
}
for (int i = 0; i < 2; i++)
{ //12-13
WriteOutputArray(upp.pm.measured.T[i], ind_port, nn++);
WriteOutputArray(upp.pm.measured.final.T[i], ind_port, nn++);
}
{ //14-16
WriteOutputArray(upp.pm.measured.U_mean, ind_port, nn++);
WriteOutputArray(upp.pm.measured.I_mean, ind_port, nn++);
WriteOutputArray(upp.pm.measured.F_mean, ind_port, nn++);
{ //14-19
WriteOutputArray(upp.pm.measured.final.Uamp, ind_port, nn++);
WriteOutputArray(upp.pm.measured.final.Iamp, ind_port, nn++);
WriteOutputArray(upp.pm.measured.final.I[0], ind_port, nn++);
WriteOutputArray(upp.pm.measured.final.I[1], ind_port, nn++);
WriteOutputArray(upp.pm.measured.final.I[2], ind_port, nn++);
WriteOutputArray(upp.pm.measured.final.Fmean, ind_port, nn++);
}
{ //20-21
WriteOutputArray(upp.pm.isr_cnt, ind_port, nn++);
WriteOutputArray(upp.pm.slow_cnt%PM_SLOW_PERIOD_CNT, ind_port, nn++);
}
}
void Write_AngleControl(real_T* Buffer, int ind_port)
{
int nn = 0;
WriteOutputArray(upp.hangle.Iref, ind_port, nn++);
WriteOutputArray(upp.hangle.Imeas, ind_port, nn++);
WriteOutputArray(upp.hangle.alpha, ind_port, nn++);
WriteOutputArray((long long)(upp.hangle.htim->Instance->CCR1) - upp.hangle.htim->Instance->CNT, ind_port, nn++);
WriteOutputArray((long long)(upp.hangle.htim->Instance->CCR2) - upp.hangle.htim->Instance->CNT, ind_port, nn++);
WriteOutputArray((long long)(upp.hangle.htim->Instance->CCR3) - upp.hangle.htim->Instance->CNT, ind_port, nn++);
}
/**
* @brief Функция для записи входов в приложение МК
* @param u - массив входных значений
@@ -71,8 +152,38 @@ void app_readInputs(const real_T* Buffer) {
ADC_Set_Channel_Value(ADC3, 8, ReadInputArray(0,4));
ADC_Set_Channel_Value(ADC3, 10, ReadInputArray(0,5));
alpha_dbg = ReadInputArray(1, 0);
upp.call->go = ReadInputArray(1, 1);
upp.call->go = ReadInputArray(1, 0);
if (upp.workmode != UPP_Work)
{
MB_DATA.HoldRegs.pui_params.Iref = ReadInputArray(1, 1);
MB_DATA.HoldRegs.pui_params.Tnt = ReadInputArray(1, 2);
MB_DATA.HoldRegs.pui_params.Umin = ReadInputArray(1, 3);
MB_DATA.HoldRegs.pui_params.Umax = ReadInputArray(1, 4);
MB_DATA.HoldRegs.pui_params.Imax = ReadInputArray(1, 5);
MB_DATA.HoldRegs.pui_params.Imin = ReadInputArray(1, 6);
MB_DATA.HoldRegs.pui_params.TiMax = ReadInputArray(1, 7);
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;
MB_INTERNAL.param.adc.ADC_Max[ADC_CHANNEL_IC] = ReadInputArray(2, 1) * 10;
MB_INTERNAL.param.nominal.U = ReadInputArray(2, 2) * 10;
MB_INTERNAL.param.nominal.I = ReadInputArray(2, 3) * 10;
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.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
}
@@ -83,26 +194,24 @@ void app_readInputs(const real_T* Buffer) {
*/
void app_writeOutputBuffer(real_T* Buffer) {
// USER APP OUTPUT START
Write_Thyristors(Buffer, 0);
Write_UPP_Outputs(Buffer, 0);
Write_PowerMonitor(Buffer, 1);
Write_PowerMonitor(Buffer, 2);
Write_AngleControl(Buffer, 3);
int nn = 0;
//WriteOutputArray(upp.hangle.htim->Instance->CNT, 2, nn++);
WriteOutputArray((long long)(upp.hangle.htim->Instance->CCR1) - upp.hangle.htim->Instance->CNT, 2, nn++);
WriteOutputArray((long long)(upp.hangle.htim->Instance->CCR2) - upp.hangle.htim->Instance->CNT, 2, nn++);
WriteOutputArray((long long)(upp.hangle.htim->Instance->CCR3) - upp.hangle.htim->Instance->CNT, 2, nn++);
//WriteOutputArray(dbg[0], 2, nn++);
//WriteOutputArray(dbg[1], 2, nn++);
//WriteOutputArray(dbg[2], 2, nn++);
WriteOutputArray(upp.hpwm.AllPhases[PHASE_A_POS].State, 2, nn++);
WriteOutputArray(upp.hpwm.AllPhases[PHASE_A_NEG].State, 2, nn++);
WriteOutputArray(upp.hpwm.AllPhases[PHASE_B_POS].State, 2, nn++);
WriteOutputArray(upp.hpwm.AllPhases[PHASE_B_NEG].State, 2, nn++);
WriteOutputArray(upp.hpwm.AllPhases[PHASE_C_POS].State, 2, nn++);
WriteOutputArray(upp.hpwm.AllPhases[PHASE_C_NEG].State, 2, nn++);
//WriteOutputArray(upp.hpwm.AllPhases[PHASE_A_POS].State, 2, nn++);
//WriteOutputArray(upp.hpwm.AllPhases[PHASE_A_NEG].State, 2, nn++);
//WriteOutputArray(upp.hpwm.AllPhases[PHASE_B_POS].State, 2, nn++);
//WriteOutputArray(upp.hpwm.AllPhases[PHASE_B_NEG].State, 2, nn++);
//WriteOutputArray(upp.hpwm.AllPhases[PHASE_C_POS].State, 2, nn++);
//WriteOutputArray(upp.hpwm.AllPhases[PHASE_C_NEG].State, 2, nn++);
// USER APP OUTPUT END
}

View File

@@ -9,6 +9,13 @@
void app_step(void)
{
// USER APP STEP START
static int first_step = 0;
if(first_step == 0)
{
first_step = 1;
UPP_DO.CEN(ENABLE);
}
UPP_While();

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,8 +1,14 @@
clear all
IadcMax = 200;%50;
VadcMax = 1216;
Ts = 5e-6;
Vnom = 690;
Vnom = 400;
Inom = 30;%4.2;
Fnom = 50;
Temperature1 = 2.22; % 20 градусов
Temperature2 = 2.99; % 34 градусов

Binary file not shown.

View File

@@ -20,6 +20,7 @@
#ifndef _MODBUS_CONFIG_H_
#define _MODBUS_CONFIG_H_
#include "upp_defs.h"
#include "upp_io.h"
// Общие параметры
#define MODBUS_DEVICE_ID 1 ///< Адрес устройства в сети Modbus
@@ -40,8 +41,8 @@
// Периферия (опционально)
//#define mb_huart huart1 ///< Удобный дефайн для модбасовского uart
//#define mb_htim htim3 ///< Удобный дефайн для модбасовского таймера
//#define RS_EnableReceive() ///< Функция изменения направления передачи на ПРИЕМ для RS-485
//#define RS_EnableTransmit() ///< Функция изменения направления передачи на ПЕРЕДАЧУ для RS-485
#define RS_EnableReceive() UPP_UART1_SetDirection(GPIO_PIN_RESET) ///< Функция изменения направления передачи на ПРИЕМ для RS-485
#define RS_EnableTransmit() UPP_UART1_SetDirection(GPIO_PIN_RESET) ///< Функция изменения направления передачи на ПЕРЕДАЧУ для RS-485
// Модули modbus

View File

@@ -21,6 +21,7 @@
#include "modbus_holdregs.h"
#include "modbus_inputregs.h"
#include "modbus_devid.h"
#include "upp_main.h"
/* DEFINE DATA FOR MODBUS */
MB_DataStructureTypeDef MB_DATA = {0}; ///< Coils & Registers
@@ -66,10 +67,16 @@ MB_ExceptionTypeDef MB_Check_Address_For_Arr(uint16_t Addr, uint16_t Qnt, uint16
* @details Определение адреса начального регистра.
* @note WriteFlag пока не используется.
*/
MB_ExceptionTypeDef MB_DefineRegistersAddress(uint16_t **pRegs, uint16_t Addr, uint16_t Qnt, uint8_t RegisterType)
MB_ExceptionTypeDef MB_DefineRegistersAddress(uint16_t **pRegs, uint16_t Addr, uint16_t Qnt, uint8_t RegisterType, uint8_t WriteFlag)
{
/* В режиме работа ничего не записываем */
if(upp.workmode == UPP_Work)
{
return ET_ILLEGAL_FUNCTION;
}
/* check quantity error */
if (Qnt > DATA_SIZE)
if (Qnt > MbData_size)
{
return ET_ILLEGAL_DATA_VALUE; // return exception code
}
@@ -125,6 +132,12 @@ MB_ExceptionTypeDef MB_DefineRegistersAddress(uint16_t **pRegs, uint16_t Addr, u
*/
MB_ExceptionTypeDef MB_DefineCoilsAddress(uint16_t **pCoils, uint16_t Addr, uint16_t Qnt, uint16_t *start_shift, uint8_t WriteFlag)
{
/* В режиме работа ничего не записываем */
if(upp.workmode == UPP_Work)
{
return ET_ILLEGAL_FUNCTION;
}
/* check quantity error */
if (Qnt > 2000)
{

View File

@@ -52,7 +52,7 @@
#define _MODBUS_DATA_H_
#include "upp_defs.h"
#include "upp_control.h"
#include "upp_params.h"
#include "stdint.h"

View File

@@ -97,11 +97,14 @@
#define BENCH_TIME_ENABLE ///< Включить бенч времени
#define BENCH_TIME_MAX_CHANNELS 5 ///< Максимальное количество каналов измерения
#define BENCH_TIME_MAX_CHANNELS 6 ///< Максимальное количество каналов измерения
#define BT_ADC 0
#define BT_PWM 1
#define BT_SYSTICK 2
#define BT_SLOWCALC_PRD 0
#define BT_SLOWCALC 1
#define BT_ADC 2
#define BT_ADC_PRD 3
#define BT_PWM 4
#define BT_SYSTICK 5
/** GEN_CONFIG
* @}
*/

View File

@@ -14,50 +14,108 @@
#define _UPP_CONFIG_H_
#include "stm32f4xx_hal.h"
// Проверка корректности структуры
#define assert_upp(_struct_) check_null_ptr_2(_struct_, (_struct_)->f.Initialized)
/* Дефайны для индексов */
#define U_BA 0
#define U_AC 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 ZC_DISABLE_HYSTERESIS_DEBOUNCE ///< Отключить гиситерезис и дребезг на определении перехода через ноль
/** //UPP_PARAMS_TEST
* @}
*/
/**
* @addtogroup UPP_DEFAULT_PARAMS Default params for external flash
* @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 Дефолтные параметры для внешней памяти. Они применятся по команде или по ошибке
* @{
*/
/* Параметри мониторинга сети */
#define PM_EXP_ALPHA_COEF_DEFAULT 0.01
/* Уставки по температурам */
#define SETPOINT_TEMP_WARN 70
#define SETPOINT_TEMP_ERR 85
/* Номинальные параметры */
#define NOM_PHASE_NUMB 3
#define NOM_U_V_DEFAULT 690
#define NOM_U_V_DEFAULT 380
#define NOM_U_DEVIATION_PLUS_PERCENT_DEFAULT 6
#define NOM_U_DEVIATION_MINUS_PERCENT_DEFAULT 10
#define NOM_F_HZ_DEFAULT 50
#define NOM_F_DEVIATION_PLUS_PERCENT_DEFAULT 5
#define NOM_F_DEVIATION_MINUS_PERCENT_DEFAULT 5
#define NOM_F_DEVIATION_PLUS_PERCENT_DEFAULT 10
#define NOM_F_DEVIATION_MINUS_PERCENT_DEFAULT 10
#define NOM_I_A_DEFAULT 5
/* Параметры ПУИ */
#define PUI_Iref_PERCENT_DEFAULT 150
#define PUI_Iref_PERCENT_DEFAULT 1.5
#define PUI_Tnt_MS_DEFAULT 300
#define PUI_Umin_PERCENT_DEFAULT 80
#define PUI_Umax_PERCENT_DEFAULT 120
#define PUI_Imax_PERCENT_DEFAULT 99
#define PUI_Imin_PERCENT_DEFAULT 7
#define PUI_Tnt_CalcAlpha(...) FilterExp_CalcAlpha95(__VA_ARGS__) ///< Уровень в процентах, до куда нарастет ток за время Tnt
#define PUI_Umin_PERCENT_DEFAULT 0.8
#define PUI_Umax_PERCENT_DEFAULT 1.2
#define PUI_Imax_PERCENT_DEFAULT 0.99
#define PUI_Imin_PERCENT_DEFAULT 0.07
#define PUI_TiMax_US_DEFAULT 5000
#define PUI_Tdelay_SECONDS_DEFAULT 30
#define PUI_Interlace_EN_DEFAULT 5000
/* Время задержки перед выставлением ошибки */
#define ERRORS_DELAY_MS_UAMP_ERR 1500 // todo
#define ERRORS_DELAY_MS_F_ERR 5000
#define ERRORS_DELAY_MS_DEFAULT 0.1f
/* Параметры регулятора угла */
#define ANGLE_PULSE_LENGTH_RESERVE_PERCENT_DEFAULT 1.0
#define ANGLE_MAX_PERCENT_DEFAULT 1
#define ANGLE_MIN_PERCENT_DEFAULT 0.1
#define ANGLE_PID_KP_COEF_DEFAULT 0.0001
#define ANGLE_PID_KI_COEF_DEFAULT 0.0001
#define ANGLE_PID_KD_COEF_DEFAULT 0
/* Параметри мониторинга сети */
#define PM_RMS_WINDOW_PERIOD_US_DEFAULT 20000
#define PM_RMS_EXT_TAU_US_DEFAULT 0.02*3 // 3 периода 50 Гц
/* Параметры АЦП */
#define ADC_U_MAX_V_DEFAULT 1216.0
#define ADC_I_MAX_A_DEFAULT 53.0
@@ -66,32 +124,56 @@
/* Параметры определения перехода через ноль */
#define ZERO_CROSS_HYSTERESIS_PERCENT_DEFAULT 2.0
#define ZERO_CROSS_DEBOUNCE_10US_DEFAULT 2*100 // (2.5 * 100 = 2.5 мс)
#define ZERO_CROSS_DEBOUNCE_CNT_DEFAULT 2*100 // (2.5 * 100 = 2.5 мс)
/* Параметры ШИМ для тиристоров */
#define PWM_THYR_FREQUENCY_HZ_DEFAULT 20000
#define PWM_THYR_PULSE_NUMBER_DEFAULT 20
#define PWM_THYR_FREQUENCY_HZ_DEFAULT 16000
#define PWM_THYR_DUTY_PERCENT_DEFAULT 0.5
#define PWM_THYR_PULSE_LENGTH_DEFAULT (60.0/180.0)
/** //UPP_DEFAULT_PARAMS
/** //UPP_PARAMS_DEFAULT
* @}
*/
/**
* @addtogroup UPP_COMPILED_PARAMS Fixed params
* @ingroup UPP_CONFIG
* @brief Параметры устанавливаемые на этапе компиляции. Без перепрошивки их не поменять
* @{
*/
/* Периоды вызова всякого */
#define PM_ADC_PERIOD_US 10 ///< Период опроса АЦП в мкс
#define PM_SLOW_PERIOD_10US 50 ///< Период обновление медленных расчетов в 10мкс
#define PM_TEMP_PERIOD_10US 5000 ///< Период обновление датчиков температуры в 10мкс
/* Частоты таймеров в МГц*/
#define ADC_TIM8_FREQ_MZH 180 ///< Частота тиков таймера АЦП
#define PWM_TIM1_FREQ_MHZ 180 ///< Частота тиков таймера ШИМ (1-4 каналы)
#define PWM_TIM3_FREQ_MHZ 90 ///< Частота тиков таймера ШИМ (5-6 каналы)
#define ANGLE_TIM2_FREQ_MHZ 90 ///< Частота тиков таймера отсчета угла открытия тиристоров
// ===== ОТЛАДОЧНЫЕ ШТУКИ ДЛЯ 417 ======
#if defined(STM32F417xx)
// У старой платы УПП другие диапазоны датчиков
#undef ADC_U_MAX_V_DEFAULT
#undef ADC_I_MAX_A_DEFAULT
#define ADC_U_MAX_V_DEFAULT 707.11
#define ADC_I_MAX_A_DEFAULT 424.26
// У 417 меньше частота поэтому меняем прескалер
#undef PWM_TIM1_FREQ_MHZ
#undef PWM_TIM8_FREQ_MHZ
#undef US_TIM5_FREQ_MHZ
#undef ADC_TIM3_FREQ_MZH
#undef ANGLE_TIM2_FREQ_MHZ
#define PWM_TIM1_FREQ_MHZ 168 ///< Частота тиков таймера ШИМ (1-4 каналы)
#define PWM_TIM8_FREQ_MHZ 168 ///< Частота тиков таймера ШИМ (5-6 каналы)
#define US_TIM5_FREQ_MHZ 84 ///< Частота тиков микросекундного таймера
#define ADC_TIM3_FREQ_MZH 84 ///< Частота тиков таймера АЦП
#define ANGLE_TIM2_FREQ_MHZ 84 ///< Частота тиков таймера отсчета угла открытия тиристоров
#define HAL_PWREx_EnableOverDrive() HAL_ERROR
#endif //defined(STM32F417xx)
// ===== ОТЛАДОЧНЫЕ ШТУКИ ДЛЯ MATLAB ======
#if defined(MATLAB)
#undef UPP_INIT_BEFORE_READY_MS
#define UPP_INIT_BEFORE_READY_MS 100 ///< Сколько сканировать сеть, перед выставлением состояния готовности
#endif //defined(MATLAB)
#endif //_UPP_CONFIG_H_

View File

@@ -14,6 +14,7 @@
#define _UPP_DEFS_H_
#include "upp_config.h"
/**
* @addtogroup UPP_HIGHLEVEL Defines for high-level
* @ingroup UPP_DEFS
@@ -26,11 +27,11 @@
* @brief Список аварий УПП
*/
typedef enum {
WM_Not_Init = 0, ///< УПП не инициализирован
WM_Ready = 1, ///< УПП в готовности
WM_Running = 2, ///< УПП в работе, управляет тиристорами
WM_Done = 3, ///< УПП закончил свою работу
WM_Error = 4, ///< УПП в аварии
UPP_Init = 0, ///< УПП не инициализирован
UPP_Ready = 1, ///< УПП в готовности
UPP_Work = 2, ///< УПП в работе, управляет тиристорами
UPP_Error = 3, ///< УПП в аварии
// WM_Done = 3, ///< УПП закончил свою работу
} UPP_WorkModeType_t;
/**
@@ -59,6 +60,8 @@ typedef enum {
Err_OverVoltage = 13, ///< Напряжение сети выше допустимого (см. Umах в @ref UPP_PUI_Params_t)
Err_OverTemperature = 14, ///< Температура выше допустимой (плюс 85 °C)
Err_UnderVoltage = 15, ///< Напряжение сети ниже допустимого (см. Umin в @ref UPP_PUI_Params_t)
Err_OverFrequency = 22, ///< Частота сети выше допустимой
Err_UnderFrequency = 23, ///< Частота сети ниже допустимой
/* Ошибки по обрывам фаз */
Err_LossPhaseAll = 16, ///< Обрыв трёх фаз (см. Imin в @ref UPP_PUI_Params_t)
@@ -69,8 +72,6 @@ typedef enum {
/* Другие ошибки */
Err_LongStart = 20, ///< Затянутый пуск (ток не спадает за установленное время) (см. Tdelay в @ref UPP_PUI_Params_t)
Err_Interlace = 21, ///< Неправильный порядок чередования фаз (см. Interlace в @ref UPP_PUI_Params_t)
Err_OverFrequency = 22, ///< Частота сети выше допустимой
Err_UnderFrequency = 23, ///< Частота сети ниже допустимой
} UPP_ErrorType_t;
@@ -95,7 +96,7 @@ typedef struct {
/**
* @brief Структура параметров УПП от ПУИ
* @details Параметры по умолчанию приведены в @ref UPP_DEFAULT_PARAMS
* @details Параметры по умолчанию приведены в @ref UPP_PARAMS_DEFAULT
* @note Защиты №X приведены в @ref UPP_ErrorType_t
*/
typedef struct {
@@ -148,8 +149,44 @@ typedef struct {
* @brief Дефайны УПП которые используютяс исключительно внутри программы
* @{
*/
#define ANGLE_PERIOD_MS(_freq_) (((float)1/(_freq_*2))*1000)
// Проверка корректности структуры
#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 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
/* Дефайны для "удобного" доступа к структурам */
#define PARAM_INTERNAL MB_INTERNAL.param
#define PARAM_PUI MB_DATA.HoldRegs.pui_params
#define ERR_PUI errors.pui.err
#define ERR_PRIVATE errors.prvt.f.err
#define ERR_PRIVATE_CNT errors.prvt.cnt
/* Enum'ы УПП */
/**
* @brief Состояния полуволны
*/
@@ -164,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

@@ -31,8 +31,11 @@ extern "C" {
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* Общее по УПП */
#include "upp_defs.h"
#include "upp_io.h"
#include "upp_errors.h"
/* Общие библиотеки */
#include "mylibs_include.h"
#include "modbus.h"
/* USER CODE END Includes */
@@ -59,6 +62,7 @@ void Error_Handler(void);
/* USER CODE END EFP */
/* Private defines -----------------------------------------------------------*/
#define angletim htim2
#define mb_huart huart3
#define mbdbg_htim htim11
#define PWM_CHANNEL_1 TIM_CHANNEL_1
@@ -66,23 +70,22 @@ void Error_Handler(void);
#define PWM_CHANNEL_3 TIM_CHANNEL_3
#define PWM_CHANNEL_4 TIM_CHANNEL_4
#define mem_hspi hspi3
#define ANGLE_CHANNEL_2 TIM_CHANNEL_2
#define ANGLE_CHANNEL_3 TIM_CHANNEL_3
#define ANGLE_CHANNEL_1 TIM_CHANNEL_1
#define PWM_CHANNEL_5 TIM_CHANNEL_3
#define PWM_CHANNEL_6 TIM_CHANNEL_4
#define mb_htim htim12
#define adc_tim htim8
#define adc_tim htim3
#define usTick ustim.Instance->CNT
#define hpwm2 htim3
#define hpwm2 htim8
#define mb_dbg_huart huart6
#define ustim htim5
#define hpwm1 htim1
#define ANGLE_CHANNEL_1 TIM_CHANNEL_1
#define ANGLE_CHANNEL_2 TIM_CHANNEL_2
#define ANGLE_CHANNEL_3 TIM_CHANNEL_3
#define angletim htim2
#define UM_LED_GREEN2_Pin GPIO_PIN_2
#define UM_LED_GREEN2_GPIO_Port GPIOE
#define CEN_O_Pin GPIO_PIN_3
#define CEN_O_GPIO_Port GPIOE
#define CEN_Pin GPIO_PIN_3
#define CEN_GPIO_Port GPIOE
#define UM_ERR_5VD_Pin GPIO_PIN_4
#define UM_ERR_5VD_GPIO_Port GPIOE
#define DO2_Pin GPIO_PIN_5
@@ -172,6 +175,30 @@ void Error_Handler(void);
/* USER CODE BEGIN Private defines */
extern TIM_HandleTypeDef ustim;
void SystemClock_Config_STM32F417(void);
#if defined(STM32F417xx)
/* Ремап входного дискрета Пуск */
#undef ERR_24V_GPIO_Port
#undef ERR_24V_Pin
#undef DIN1_GPIO_Port
#undef DIN1_Pin
#define ERR_24V_Pin GPIO_PIN_15
#define ERR_24V_GPIO_Port GPIOG
#define DIN1_Pin GPIO_PIN_12
#define DIN1_GPIO_Port GPIOG
/* Ремап выходного дискрета В работе */
#undef RDO2_GPIO_Port
#undef RDO2_Pin
#undef SCIDE1_GPIO_Port
#undef SCIDE1_Pin
#define RDO2_Pin GPIO_PIN_6
#define RDO2_GPIO_Port GPIOB
#define SCIDE1_Pin GPIO_PIN_7
#define SCIDE1_GPIO_Port GPIOB
#endif
/* USER CODE END Private defines */
#ifdef __cplusplus

View File

@@ -57,6 +57,7 @@ void PendSV_Handler(void);
void SysTick_Handler(void);
void TIM1_UP_TIM10_IRQHandler(void);
void TIM2_IRQHandler(void);
void TIM8_UP_TIM13_IRQHandler(void);
void TIM8_TRG_COM_TIM14_IRQHandler(void);
void DMA2_Stream0_IRQHandler(void);
/* USER CODE BEGIN EFP */

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 Указатель на кастомный хендл АЦП
@@ -81,7 +95,6 @@ HAL_StatusTypeDef ADC_ConfigChannel(ADC_Periodic_t *adc, int ChNumb, uint16_t le
adc->Coefs[ChNumb].vMax = valueMax;
adc->Coefs[ChNumb].lZero = levelZero;
ADC_ResetStatistics(adc, ChNumb);
return HAL_OK;
}
@@ -100,24 +113,24 @@ HAL_StatusTypeDef ADC_Start(ADC_Periodic_t *adc, float PeriodUs)
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));
__HAL_TIM_SET_AUTORELOAD_FORCE(adc->htim, TIM_MicrosToTick(PeriodUs, ADC_TIM3_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
// Запускаем АЦП который будет перекидывать данные в 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;
}
/**
@@ -135,14 +148,11 @@ HAL_StatusTypeDef ADC_Stop(ADC_Periodic_t *adc)
return HAL_TIM_Base_Stop(adc->htim);
}
/**
* @brief Обработка АЦП.
* @brief Обновление напряжений АЦП.
* @return HAL Status.
* @details По факту остановка таймера, который запускает АЦП. Сам АЦП продолжает работу.
* @note Вызывается в DMA2_Stream0_IRQHandler() для обработки всего, что пришло по DMA.
*/
HAL_StatusTypeDef ADC_Handle(ADC_Periodic_t *adc)
HAL_StatusTypeDef ADC_UpdateRegular(ADC_Periodic_t *adc)
{
if(assert_upp(adc))
return HAL_ERROR;
@@ -151,93 +161,45 @@ HAL_StatusTypeDef ADC_Handle(ADC_Periodic_t *adc)
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 = 0; i < ADC_NUMB_OF_REGULAR_CHANNELS; i++)
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);
// ADC_UpdateStatistics(adc, i, ADC_LEVEL_AC);
}
// Фильтрация от шумов для всех каналов
for(int i = 0; i < ADC_NUMB_OF_CHANNELS; i++)
{
if(Filter_isEnable(&adc->filter[i]))
{
// заменяем данные на отфильтрованные данные
data[i] = Filter_Process(&adc->filter[i], data[i]);
}
}
// Преобразования температуры по таблице
for (int i = ADC_TEMP_CHANNELS_START; i < ADC_NUMB_OF_CHANNELS; i++)
{
data[i] = Filter_Process(&adc->temp_map[i-ADC_TEMP_CHANNELS_START], raw[i]);
// ADC_UpdateStatistics(adc, i, ADC_LEVEL_BASE);
}
if(Filter_isDataReady(&adc->filter[0]))
adc->f.DataReady = 1;
return HAL_OK;
}
/**
* @brief Сбор статистики.
* @brief Обновление температур АЦП.
* @return HAL Status.
*/
void ADC_UpdateStatistics(ADC_Periodic_t *adc, uint8_t channel, ADC_StatLevel_t level)
HAL_StatusTypeDef ADC_UpdateTemperatures(ADC_Periodic_t *adc)
{
if (level < ADC_LEVEL_BASE)
return;
if(assert_upp(adc))
return;
if (channel >= ADC_NUMB_OF_REGULAR_CHANNELS)
return;
return HAL_ERROR;
float *data = adc->Data;
uint16_t *raw = adc->RawData;
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)
// Преобразования температуры по таблице
for (int i = ADC_TEMP_CHANNELS_START; i < ADC_TEMP_CHANNELS_END; i++)
{
return;
}
// Накопление для Avg/RMS
stat->Sum += ABS(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;
data[i] = Filter_Process(&adc->temp_map[i-ADC_TEMP_CHANNELS_START], raw[i]);
}
return HAL_OK;
}
/**
* @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));
}
}

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, \
@@ -51,10 +58,6 @@
static const float adc_temp_vals[] = ADC_TEMPERATURES;
static const float adc_temp_quants[] = ADC_TEMPERATURES_QUANTS;
#define Filter_t FilterMedian_t
#define Filter_Init FilterMedian_Init
#define Filter_Initializator 5
/**
* @brief Коэфициенты канала АЦП для пересчета в единицы измерения
*/
@@ -100,12 +103,11 @@ 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]; ///< Пересчитанные значения АЦП (в Вольтах/Амперах)
ADC_Statistics Stat[ADC_NUMB_OF_REGULAR_CHANNELS]; ///< Статистика для регулярных каналов (не температуры)
struct
{
@@ -126,13 +128,9 @@ HAL_StatusTypeDef ADC_ConfigChannel(ADC_Periodic_t *adc, int ChNumb, uint16_t le
HAL_StatusTypeDef ADC_Start(ADC_Periodic_t *adc, float PeriodUs);
/* Остановка АЦП. */
HAL_StatusTypeDef ADC_Stop(ADC_Periodic_t *adc);
/* Обновление температур АЦП. */
HAL_StatusTypeDef ADC_UpdateTemperatures(ADC_Periodic_t *adc);
/* Обработка АЦП после получения данных. */
HAL_StatusTypeDef ADC_Handle(ADC_Periodic_t *adc);
/* Сбор статистики. */
void ADC_UpdateStatistics(ADC_Periodic_t *adc, uint8_t channel, ADC_StatLevel_t level);
/* Сброс статистики. */
void ADC_ResetStatistics(ADC_Periodic_t *adc, uint8_t channel);
HAL_StatusTypeDef ADC_UpdateRegular(ADC_Periodic_t *adc);
#endif //_ADC_TOOLS_H_

View File

@@ -0,0 +1,83 @@
/**
******************************************************************************
* @file phases_transform.c
* @brief Функции для преобразования напряжений/токов в многофазных системах
******************************************************************************
* @details
******************************************************************************/
#include "phases_transform.h"
#define SQRT3 1.7320508
/**
* @brief Рассчитать результирующий вектор трехфазной системы по фазным величинам.
* @return Длина вектора (модуль).
*/
float vector_abs_phase_calc(float phase1, float phase2)
{
/* Двухвазная система координат x-y */
float x = phase1;
float y = (-phase1 - 2*phase2)/SQRT3;
float V = 0;
arm_status res = arm_sqrt_f32(x*x + y*y, &V);
if(res == ARM_MATH_SUCCESS)
return V;
else
return 0;
}
/**
* @brief Рассчитать результирующий вектор трехфазной системы по линейным величинам.
* @return Длина вектора (модуль).
*/
float vector_abs_linear_calc(float phase1, float phase2)
{
/* Двухвазная система координат x-y */
float x = (phase1 - phase2)/SQRT3;
float y = -phase1 - phase2;
float V = 0;
arm_status res = arm_sqrt_f32(x*x + y*y, &V);
if(res == ARM_MATH_SUCCESS)
return V;
else
return 0;
}
/**
* @brief Рассчитать фазные напряжения из линейных (звезда)
* @param Ulin Линейные напряжения [Uba Uac Ubc]
* @param Uph Фазные напряжения [Ua Ub Uc]
*/
void linear_to_phase_star(float *Ulin, float *Uph)
{
if(Ulin == NULL || Uph == NULL)
return;
// Соответствие макросам: Ulin[0] = UBA, Ulin[1] = UAC, Ulin[2] = UBC
float Uba = Ulin[0];
float Uac = Ulin[1];
float Ubc = Ulin[2];
// Формулы для звезды (сумма фаз = 0)
Uph[0] = (Uba - Ubc)/3.0f; // Ua
Uph[1] = (Ubc - Uac)/3.0f; // Ub
Uph[2] = (Uac - Uba)/3.0f; // Uc
}
/**
* @brief Рассчитать фазные напряжения из линейных (треугольник)
* @param Ulin Линейные напряжения [Uba Uac Ubc]
* @param Uph Фазные напряжения [Ua Ub Uc]
*/
void linear_to_phase_delta(float *Ulin, float *Uph)
{
if(Ulin == NULL || Uph == NULL)
return;
// Соответствие макросам: Ulin[0] = UBA, Ulin[1] = UAC, Ulin[2] = UBC
// Для треугольника фазные напряжения равны линейным
Uph[0] = Ulin[0]; // Ua = Uba
Uph[1] = Ulin[2]; // Ub = Ubc
Uph[2] = Ulin[1]; // Uc = Uac
}

View File

@@ -0,0 +1,19 @@
/**
******************************************************************************
* @file phases_transform.h
* @brief Функции для преобразования напряжений/токов в многофазных системах
******************************************************************************
*****************************************************************************/
#ifndef _PHASES_TRANSFORM_H_
#define _PHASES_TRANSFORM_H_
#include "main.h"
/* Рассчитать результирующий вектор трехфазной системы по фазным величинам. */
float vector_abs_phase_calc(float phase1, float phase2);
/* Рассчитать результирующий вектор трехфазной системы по линейным величинам. */
float vector_abs_linear_calc(float phase1, float phase2);
/* Рассчитать фазные напряжения из линейных (звезда) */
void linear_to_phase_star(float *Ulin, float *UphUc);
/* Рассчитать фазные напряжения из линейных (треугольник) */
void linear_to_phase_delta(float *Ulin, float *Uph);
#endif /* _PHASES_TRANSFORM_H_ */

View File

@@ -6,10 +6,21 @@
* @details
******************************************************************************/
#include "power_monitor.h"
#include "power_protect.h"
#include "phases_transform.h"
#include "adc.h"
#include "tim.h"
#define U_BC_calc(_uab_, _uca_) (-((_uab_) + (_uca_)))
#define I_B_calc(_ia_, _ic_) (-((_ia_) + (_ic_)))
static void __SynchAvgFilters(PowerMonitor_t *hpm);
/**
* @brief Инициализация мониторинга сети.
* @param hpm Указатель на структуру мониторинга сети
* @details Инициализирует: АЦП, Алгоритм перехода через ноль, Фильтры
*/
HAL_StatusTypeDef PowerMonitor_Init(PowerMonitor_t *hpm)
{
if(hpm == NULL)
@@ -19,160 +30,200 @@ 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,
MB_INTERNAL.param.adc.ADC_Zero[ADC_CHANNEL_UBA],
MB_INTERNAL.param.adc.ADC_Max[ADC_CHANNEL_UBA],
4095) != HAL_OK)
return HAL_ERROR;
if(ADC_ConfigChannel(&hpm->adc, ADC_CHANNEL_UAC,
MB_INTERNAL.param.adc.ADC_Zero[ADC_CHANNEL_UAC],
MB_INTERNAL.param.adc.ADC_Max[ADC_CHANNEL_UAC],
4095) != HAL_OK)
return HAL_ERROR;
if(ADC_ConfigChannel(&hpm->adc, ADC_CHANNEL_IC,
MB_INTERNAL.param.adc.ADC_Zero[ADC_CHANNEL_IC],
MB_INTERNAL.param.adc.ADC_Max[ADC_CHANNEL_IC],
4095) != HAL_OK)
return HAL_ERROR;
if(ADC_ConfigChannel(&hpm->adc, ADC_CHANNEL_IA,
MB_INTERNAL.param.adc.ADC_Zero[ADC_CHANNEL_IA],
MB_INTERNAL.param.adc.ADC_Max[ADC_CHANNEL_IA],
4095) != HAL_OK)
return HAL_ERROR;
/* Инициализация алгоритма перехода через ноль */
if(ZC_Init(&hpm->zc, 3, (float)MB_INTERNAL.param.zc.Hysteresis/100, MB_INTERNAL.param.zc.DebouneCouner) != HAL_OK)
if(ZC_Init(&hpm->zc, 3, 0, 0) != HAL_OK)
return HAL_ERROR;
/* Инициализация каналов алгоритма перехода через ноль */
if(ZC_ConfigChannel(&hpm->zc, U_BA, ZC_BOTH_EDGES) != HAL_OK)
if(ZC_ConfigChannel(&hpm->zc, U_AB, ZC_BOTH_EDGES) != HAL_OK)
return HAL_ERROR;
if(ZC_ConfigChannel(&hpm->zc, U_AC, ZC_BOTH_EDGES) != HAL_OK)
if(ZC_ConfigChannel(&hpm->zc, U_CA, ZC_BOTH_EDGES) != HAL_OK)
return HAL_ERROR;
if(ZC_ConfigChannel(&hpm->zc, U_BC, ZC_BOTH_EDGES) != HAL_OK)
return HAL_ERROR;
/* Инициализация экпоненциального фильтра медленного алга */
for(int i = 0; i < 3; i++)
{
if(FilterExp_Init(&hpm->measured.exp[i], (float)MB_INTERNAL.param.pm.mean_alpha/65535))
return HAL_ERROR;
Filter_Start(&hpm->measured.exp[i]);
}
/* Инициализация среднего фильтра медленного алга */
for(int i = 0; i < ADC_NUMB_OF_REGULAR_CHANNELS; i++)
{
if(FilterAverage_Init(&hpm->measured.avg[i], PM_SLOW_PERIOD_10US, FILTER_MODE_DEFAULT))
if(FilterAverage_Init(&hpm->avg[i], PM_SLOW_PERIOD_CNT, FILTER_MODE_DEFAULT))
return HAL_ERROR;
Filter_Start(&hpm->measured.avg[i]);
Filter_Start(&hpm->avg[i]);
}
/* Инициализация среднего фильтра для температур */
for(int i = 0; i < ADC_NUMB_OF_T_CHANNELS; i++)
{
if(FilterAverage_Init(&hpm->measured.avg[ADC_TEMP_CHANNELS_START+i], PM_TEMP_PERIOD_10US, FILTER_MODE_DEFAULT))
if(FilterAverage_Init(&hpm->avg[AVG_TEMP1+i], MS_TO_SLOW_TICKS(PM_TEMP_SLOW_PERIOD_MS), FILTER_MODE_DEFAULT))
return HAL_ERROR;
Filter_Start(&hpm->measured.avg[ADC_TEMP_CHANNELS_START+i]);
Filter_Start(&hpm->avg[ADC_TEMP_CHANNELS_START+i]);
}
/* Инициализация среднего фильтра для частот */
for(int i = 0; i < 3; i++)
{
if(FilterAverage_Init(&hpm->avg[AVG_FAB+i], MS_TO_SLOW_TICKS(PM_F_SLOW_PERIOD_MS), FILTER_MODE_DEFAULT))
return HAL_ERROR;
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;
}
/**
* @brief Запустить мониторинг сети.
* @param hpm Указатель на структуру мониторинга сети
* @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;
}
void PowerMonitor_SlowHandle(PowerMonitor_t *hpm)
/**
* @brief Медленные расчеты АЦП.
* @param hpm Указатель на структуру мониторинга сети
* @details Вызывается в main после накопления @ref PM_SLOW_PERIOD_CNT выборок АЦП.
* Делаются всякие расчеты для более подробного мониторинга сети и защит.
* Сам АЦП считывается в @ref PowerMonitor_FastCalc
*/
void PowerMonitor_SlowCalc(PowerMonitor_t *hpm)
{
if(hpm == NULL)
return;
if(!hpm->f.runSlow)
return;
hpm->f.runSlow = 0;
PowerMonitor_Measured_t *meas = &hpm->measured;
meas->Uslow[U_BC] = -meas->Uslow[U_BA] - meas->Uslow[U_AC];
meas->Islow[I_B] = -meas->Islow[I_A] - meas->Islow[I_C];
/* Обработка температур */
ADC_UpdateTemperatures(&hpm->adc);
float t1 = hpm->adc.Data[ADC_CHANNEL_TEMP1];
float t2 = hpm->adc.Data[ADC_CHANNEL_TEMP2];
meas->final.T[TEMP_1] = Filter_Process(&hpm->avg[AVG_TEMP1], t1);
meas->final.T[TEMP_2] = Filter_Process(&hpm->avg[AVG_TEMP2], t2);
float umean = 0;
float imean = 0;
float fmean = 0;
/* Расчет третьей фазы */
meas->slow.U[U_BC] = U_BC_calc(meas->slow.U[U_AB], meas->slow.U[U_CA]);
meas->slow.I[I_B] = I_B_calc(meas->slow.I[I_A], meas->slow.I[I_C]);
/* Расчет всякого для трех фаз отдельно */
float fmean = 0; // средняя частота по трем фазам
float umean = 0; // средний напряжение по трем фазам
float imean = 0; // средний ток по трем фазам
float iphase_mean = 0; // средний ток каждой фазы
float uphase_mean = 0; // среднее напряжение каждой фазы
// Дополнительно посчитаем значения в реальных Вольтах/Амперах
float u_base = u2f(PARAM_INTERNAL.nominal.U, 10);
float i_base = u2f(PARAM_INTERNAL.nominal.I, 10);
for(int i = 0; i < 3; i++)
{
umean += ABS(meas->Uslow[i]);
imean += ABS(meas->Islow[i]);
fmean += ABS(meas->F[i]);
}
umean /=3;
imean /=3;
fmean /=3;
/* Получение частоты фазы */
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];
meas->U_mean = Filter_Process(&meas->exp[0], umean);
meas->I_mean = Filter_Process(&meas->exp[1], imean);
meas->F_mean = Filter_Process(&meas->exp[2], fmean);
/* Средниее напряжение фазы */
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 = 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;
meas->real.U[i] = meas->final.U[i]*u_base;
}
/* Получение средней частоты по трем фазам */
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);
// }
/* Расчет амплитуд трехфазной сети */
// 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++;
}
void PowerMonitor_Handle(PowerMonitor_t *hpm)
/**
* @brief Считывание АЦП и быстрые расчеты.
* @param hpm Указатель на структуру мониторинга сети
* @details Вызывается в прерывании АЦП по получению данных.
* Далее данные считываются и делаются базовые преобразования
* Более подробные расчеты в @ref PowerMonitor_SlowCalc
*/
void PowerMonitor_FastCalc(PowerMonitor_t *hpm)
{
if(hpm == NULL)
return;
/* Считываем АЦП */
ADC_Handle(&hpm->adc);
/* Заполняем величины */
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->Ureal[U_BA] = hpm->adc.Data[ADC_CHANNEL_UBA];
meas->Ureal[U_AC] = hpm->adc.Data[ADC_CHANNEL_UAC];
meas->Ureal[U_BC] = -meas->Ureal[U_BA] - meas->Ureal[U_AC];
meas->Ireal[I_C] = hpm->adc.Data[ADC_CHANNEL_IC];
meas->Ireal[I_A] = hpm->adc.Data[ADC_CHANNEL_IA];
meas->Ireal[I_B] = -meas->Ireal[I_A] - meas->Ireal[I_C];
meas->T[TEMP_1] = hpm->adc.Data[ADC_CHANNEL_TEMP1];
meas->T[TEMP_2] = hpm->adc.Data[ADC_CHANNEL_TEMP2];
/* Преобразуем в относительные единицы (о.е.) */
for(int i = 0; i < 3; i++)
{
meas->U[i] = 10*meas->Ureal[i]/MB_INTERNAL.param.nominal.U;
meas->I[i] = 10*meas->Ireal[i]/MB_INTERNAL.param.nominal.I;
}
/* Считываем АЦП с пересчетами и медианой фильтрацией от выбросов */
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;
meas->fast.I[I_A] = hpm->adc.Data[ADC_CHANNEL_IA]/i_base;
meas->fast.I[I_B] = I_B_calc(meas->fast.I[I_A], meas->fast.I[I_C]);
/* Ищем переход через ноль */
ZC_ProcessAllChannels(&hpm->zc, meas->U, usTick);
for(int i = 0; i < 3; i++)
{
meas->F[i] = ZC_GetFrequency(&hpm->zc, i) / 2;
}
ZC_ProcessAllChannels(&hpm->zc, meas->fast.U, usTick);
/* Вообще фильтры должны рабтоать синхронно, но на всякий синхронизация */
//__SynchAvgFilters(hpm);
/* Average для медленной фильтрации */
meas->slow.U[U_AB] = Filter_Process(&hpm->avg[AVG_UAB], meas->fast.U[U_AB]);
meas->slow.U[U_CA] = Filter_Process(&hpm->avg[AVG_UCA], meas->fast.U[U_CA]);
meas->slow.I[I_C] = Filter_Process(&hpm->avg[AVG_IC], meas->fast.I[I_C]);
meas->slow.I[I_A] = Filter_Process(&hpm->avg[AVG_IA], meas->fast.I[I_A]);
/* Накопление Average для медленной фильтрации */
meas->Uslow[U_BA] = Filter_Process(&meas->avg[0], meas->U[U_BA]);
meas->Uslow[U_AC] = Filter_Process(&meas->avg[1], meas->U[U_AC]);
meas->Islow[I_C] = Filter_Process(&meas->avg[2], meas->I[I_C]);
meas->Islow[I_A] = Filter_Process(&meas->avg[3], meas->I[I_A]);
meas->T[TEMP_1] = Filter_Process(&meas->avg[4], meas->T[TEMP_1]);
meas->T[TEMP_2] = Filter_Process(&meas->avg[5], meas->T[TEMP_2]);
/* Запускаем медленную обработку через slow_period прерываний */
// if(hpm->isr_cnt == PM_SLOW_PERIOD_10US)
// if(hpm->isr_cnt == PM_SLOW_PERIOD_CNT)
/* Запускаем медленную когда фильтры среднего зациклились */
if(Filter_isDataReady(&meas->avg[0]))
/* Берем 0 фильтр, просто так, потому что они все должны работать синхронно */
if(Filter_isDataReady(&hpm->avg[0]))
{
hpm->isr_cnt = 0;
if(!hpm->f.runSlow)
@@ -181,8 +232,8 @@ void PowerMonitor_Handle(PowerMonitor_t *hpm)
}
else // если уже запущена - ставим overrun slow calc
{
errors.prvt.f.err.slow_calc_overrun = 1;
errors.prvt.cnt.slow_calc_overrun++;
ERR_PRIVATE.overrun_slow_calc = 1;
ERR_PRIVATE_CNT.overrun_slow_calc++;
}
}
else
@@ -190,3 +241,66 @@ void PowerMonitor_Handle(PowerMonitor_t *hpm)
hpm->isr_cnt++;
}
}
/**
* @brief Проверяет защиты питания и температур.
* @param measure Указатель на структуру с измеренными значениями
* @param Running Флаг для защит по току:
* - 1 - УПП в работе, проверяем токи
* - 0 - УПП ожидает команды, не смотрим токи
* @return 1 - была обнаружена ощибка, 0 - все ок
*/
int PowerMonitor_Protect(PowerMonitor_t *hpm, uint8_t Running)
{
if(hpm == NULL)
return 1;
PowerMonitor_Measured_t *measure = &hpm->measured;
UPP_PUI_Params_t *protect = &PARAM_PUI;
UPP_ParamsNominal_t *nominal = &PARAM_INTERNAL.nominal;
/*=============== ЗАЩИТЫ ПО НАПРЯЖЕНИЮ ==================*/
hpm->f.isU = Protect_Voltages(measure, protect, nominal);
/* Если УПП в работе */
if(Running)
{
/*=============== ЗАЩИТЫ ПО ТОКУ ==================*/
hpm->f.isI = Protect_Currents(measure, protect, nominal);
}
/*=============== ЗАЩИТЫ ВСЯКИЕ ДРУГИЕ ==================*/
Protect_Misc(measure, protect, nominal);
if(errors.prvt.f.all)
return 1;
else
return 0;
}
/* Синхронизация фильтров. Но вообще не должна никогда отрабатывать */
static void __SynchAvgFilters(PowerMonitor_t *hpm)
{
uint8_t counts_equal = 1;
uint32_t first_count = hpm->avg[0].count;
for (int i = 1; i < ADC_NUMB_OF_REGULAR_CHANNELS; i++)
{
if (hpm->avg[i].count != first_count)
{
counts_equal = 0;
break;
}
}
if(!counts_equal)
{
for(int i = 0; i < ADC_NUMB_OF_REGULAR_CHANNELS; i++)
{
Filter_ReInit(&hpm->avg[i], hpm->avg[i].size, FILTER_MODE_DEFAULT);
Filter_Start(&hpm->avg[i]);
}
}
}

View File

@@ -3,6 +3,8 @@
* @file power_monitor.h
* @brief Модуль мониторящий сеть: Напряжение, Токи, Температуры
******************************************************************************
* @addtogroup POWER_MONITOR Power Monitoring
* @brief Модуль для слежения за сетью и температурами (крч все что от АЦП)
*****************************************************************************/
#ifndef _POWER_MONITOR_H_
#define _POWER_MONITOR_H_
@@ -10,66 +12,122 @@
#include "adc_tools.h"
#include "zero_cross.h"
/* Индексы RMS фильтров */
#define RMS_ALL 6
#define RMS_U 0
#define RMS_UAB 0
#define RMS_UCA 1
#define RMS_UBC 2
#define RMS_I 3
#define RMS_IC 3
#define RMS_IA 4
#define RMS_IB 5
/* Это только сглаживающий, RMS с таким индексом нет */
#define RMS_EXP_ALL 8
#define RMS_EXP_U 6
#define RMS_EXP_I 7
/* Индексы усредняющих фильтров */
#define AVG_ALL 9
#define AVG_UAB 0
#define AVG_UCA 1
#define AVG_IC 2
#define AVG_IA 3
#define AVG_TEMP1 4
#define AVG_TEMP2 5
#define AVG_F 6
#define AVG_FAB 6
#define AVG_FCA 7
#define AVG_FBC 8
/**
* @brief Флаги Мониторинга сети
*/
typedef struct
{
unsigned inIsr:1; ///< Флаг что мы в прерывании
unsigned runSlow:1; ///< Запустить медленный алгоритм в while(1)
unsigned isU:1; ///< Есть ли напряжение
unsigned isI:1; ///< Есть ли ток
unsigned protectUmax:1; ///< Отработка защиты по макс. напряжению
unsigned protectUmin:1; ///< Отработка защиты по мак с. напряжению
unsigned protectImax:1; ///< Отработка защиты по макс. току
unsigned protectImin:1; ///< Отработка защиты по мин. току
}PowerMonitor_Flags_t;
/**
* @brief Измеренные и приведенные значения с АЦП
*/
typedef struct
{
float U_mean; ///< Среднее Напряжение по трем фазам
float I_mean; ///< Средний Ток по трем фазам
float F_mean; ///< Средняя Частота по трем фазам
float Uslow[3]; ///< Напряжение от АЦП (в о.е.)
float Islow[3]; ///< Ток от АЦП (в о.е.)
/** @brief Усредненные величины (о.е.) */
struct
{
float Uamp; ///< Результирующий вектор Напряжения по трем фазам
float Iamp; ///< Результирующий вектор Тока по трем фазам
float U[3]; ///< Среднее Наряжение по трем фазам
float I[3]; ///< Средний Ток по трем фазам
float Fmean; ///< Средняя Частота по трем фазам
float F[3]; ///< Частота от Zero Cross (обновляется в main)
float Phase[3]; ///< Фазовое смещение по отношению к фазе A
float Offset[3]; ///< Смещение синуса относителньо нуля (определяется по отношению полупериодов)
float T[2]; ///< Температура (обновляется в main)
}final;
/** @brief Быстрые величины (в о.е.) - обновляются в каждом прерывании АЦП @ref PowerMonitor_FastCalc */
struct
{
float U[3]; ///< Напряжение
float I[3]; ///< Ток
}fast;
float U[3]; ///< Напряжение от АЦП (в о.е.)
float I[3]; ///< Ток от АЦП (в о.е.)
float F[3]; ///< Частота от Zero Cross
float T[2]; ///< Температура от АЦП
/** @brief Медленные величины (в о.е.) - обновляются в main в @ref PowerMonitor_SlowCalc */
struct
{
float U[3]; ///< Напряжение
float I[3]; ///< Ток
}slow;
/** @brief Реальные величины - обновляются в main в @ref PowerMonitor_SlowCalc, и содержат значения в В/А */
struct
{
float U[3]; ///< Напряжение (Действующее)
float I[3]; ///< Ток (Действующее)
}real;
float Ureal[3]; ///< Напряжение от АЦП
float Ireal[3]; ///< Ток от АЦП
FilterExp_t exp[3]; ///< Фильтры для mean
FilterAverage_t avg[6]; ///< Фильтры для avg
}PowerMonitor_Measured_t;
typedef struct
{
}PowerMonitor_Filters_t;
/**
* @brief Структура для мониторинга сети
*/
typedef struct
{
ADC_Periodic_t adc; ///< Хендл периодического АЦП
ZeroCross_Handle_t zc; ///< Хендл перехода через ноль
PowerMonitor_Filters_t fltr;
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]; ///< Фильтры для сглаживания медленных величин АЦП
PowerMonitor_Flags_t f; ///< Флаги мониторинга
uint32_t slow_period;
uint32_t isr_cnt;
uint32_t isr_cnt;
uint32_t slow_cnt;
}PowerMonitor_t;
extern PowerMonitor_t pm;
// ====== ИНИЦИАЛИЗАЦИЯ ==========
/* Инициализация мониторинга сети */
HAL_StatusTypeDef PowerMonitor_Init(PowerMonitor_t *hpm);
/* Запустить мониторинг сети */
HAL_StatusTypeDef PowerMonitor_Start(PowerMonitor_t *hpm);
void PowerMonitor_SlowHandle(PowerMonitor_t *hpm);
void PowerMonitor_Handle(PowerMonitor_t *hpm);
// ====== РАСЧЕТЫ ==========
/* Медленные расчеты АЦП */
void PowerMonitor_SlowCalc(PowerMonitor_t *hpm);
/* Считывание АЦП и быстрые расчеты */
void PowerMonitor_FastCalc(PowerMonitor_t *hpm);
/* Проверяет защиты питания и температур */
int PowerMonitor_Protect(PowerMonitor_t *hpm, uint8_t Running);
#endif /* _POWER_MONITOR_H_ */

View File

@@ -0,0 +1,203 @@
/**
******************************************************************************
* @file power_protect.c
* @brief Модуль реализующий защиты по Напряжению/Токам/Температуре
******************************************************************************
* @details
******************************************************************************/
#include "power_protect.h"
/**
* @brief Проверяет защиты по напряжению.
* @note Заполняет флаги prvt ошибок (приватные).
* Потом в @ref UPP_ErrorsHandle исходя из них заполняются ошибки для ПУИ
* @return 1 - напряжение есть, 0 - напряжения нет
*/
int Protect_Voltages(PowerMonitor_Measured_t *measure, UPP_PUI_Params_t *protect, UPP_ParamsNominal_t *nominal)
{
/* Переводим уставки ПУИ в удобный вид */
float lUmin = u2f(protect->Umin, 100)/**u2f(nominal->U, 10)*/;
float lUmax = u2f(protect->Umax, 100)/**u2f(nominal->U, 10)*/;
float lPhaseSequence = u2f(nominal->PhaseSequence, 100);
/* Общее напряжение */
if(measure->final.Uamp > lUmax)
{
ERR_PRIVATE.uamp_max = 1;
}
else if (measure->final.Uamp < lUmin)
{
ERR_PRIVATE.uamp_min = 1;
}
else
{
ERR_PRIVATE.uamp_max = 0;
ERR_PRIVATE.uamp_min = 0;
}
/* Последовательность фаз */
int realPhaseSequence = 0;
if(realPhaseSequence != lPhaseSequence)
{
ERR_PRIVATE.interlance = 1;
}
else
{
ERR_PRIVATE.interlance = 0;
}
return (ERR_PRIVATE.uamp_min == 0);
}
/**
* @brief Проверяет защиты по току.
* @note Заполняет флаги prvt ошибок (приватные).
* Потом в @ref UPP_ErrorsHandle исходя из них заполняются ошибки для ПУИ
*/
int Protect_Currents(PowerMonitor_Measured_t *measure, UPP_PUI_Params_t *protect, UPP_ParamsNominal_t *nominal)
{
/* Переводим уставки ПУИ в удобный вид */
float lIref = u2f(protect->Iref, 100)/**u2f(nominal->I, 10)*/;
float lImin = u2f(protect->Imin, 100)/**u2f(nominal->I, 10)*/;
float lImax = u2f(protect->Imax, 100) * 50 / u2f(nominal->I, 10); // Imax процентов от 50 А, в о.е. от номинального
/* Общий ток */
if(measure->final.Iamp > lImax)
{
ERR_PRIVATE.iamp_max = 1;
}
else if (measure->final.Iamp < lImin)
{
ERR_PRIVATE.iamp_min = 1;
}
else
{
ERR_PRIVATE.iamp_max = 0;
ERR_PRIVATE.iamp_min = 0;
}
/* Ток по фазам */
if(measure->final.I[I_A] > lImax)
{
ERR_PRIVATE.ia_max = 1;
}
else if (measure->final.I[I_A] < lImin)
{
ERR_PRIVATE.ia_min = 1;
}
else
{
ERR_PRIVATE.ia_max = 0;
ERR_PRIVATE.ia_min = 0;
}
if(measure->final.I[I_B] > lImax)
{
ERR_PRIVATE.ib_max = 1;
}
else if (measure->final.I[I_B] < lImin)
{
ERR_PRIVATE.ib_min = 1;
}
else
{
ERR_PRIVATE.ib_max = 0;
ERR_PRIVATE.ib_min = 0;
}
if(measure->final.I[I_C] > lImax)
{
ERR_PRIVATE.ic_max = 1;
}
else if (measure->final.I[I_C] < lImin)
{
ERR_PRIVATE.ic_min = 1;
}
else
{
ERR_PRIVATE.ic_max = 0;
ERR_PRIVATE.ic_min = 0;
}
return (ERR_PRIVATE.iamp_min == 0);
}
/**
* @brief Проверяет всякие другие защиты (частота, температура).
* @note Заполняет флаги prvt ошибок (приватные).
* Потом в @ref UPP_ErrorsHandle исходя из них заполняются ошибки для ПУИ
*/
void Protect_Misc(PowerMonitor_Measured_t *measure, UPP_PUI_Params_t *protect, UPP_ParamsNominal_t *nominal)
{
/* Переводим внутренние уставки в удобный вид */
float lFnom = u2f(PARAM_INTERNAL.nominal.F, 100);
float lFmin = lFnom - lFnom*u2f(PARAM_INTERNAL.nominal.F_deviation_minus, 10000);
float lFmax = lFnom + lFnom*u2f(PARAM_INTERNAL.nominal.F_deviation_plus, 10000);
float lTwarn = u2f(PARAM_INTERNAL.setpoints.TemperatureWarn, 100);
float lTerr = u2f(PARAM_INTERNAL.setpoints.TemperatureWarn, 100);
/*=============== ЗАЩИТЫ ПО ЧАСТОТЕ ==================*/
if(measure->final.F[U_CA] > lFmax)
{
ERR_PRIVATE.fac_max = 1;
}
else if (measure->final.F[U_CA] < lFmin)
{
ERR_PRIVATE.fac_min = 1;
}
else
{
ERR_PRIVATE.fac_max = 0;
ERR_PRIVATE.fac_min = 0;
}
if(measure->final.F[U_AB] > lFmax)
{
ERR_PRIVATE.fba_max = 1;
}
else if (measure->final.F[U_AB] < lFmin)
{
ERR_PRIVATE.fba_min = 1;
}
else
{
ERR_PRIVATE.fba_max = 0;
ERR_PRIVATE.fba_min = 0;
}
if(measure->final.F[U_BC] > lFmax)
{
ERR_PRIVATE.fbc_max = 1;
}
else if (measure->final.F[U_BC] < lFmin)
{
ERR_PRIVATE.fbc_min = 1;
}
else
{
ERR_PRIVATE.fbc_max = 0;
ERR_PRIVATE.fbc_min = 0;
}
/*=============== ЗАЩИТЫ ПО ТЕМПЕРАТУРЕ ==================*/
if(measure->final.T[TEMP_1] > lTerr)
{
ERR_PRIVATE.temp_err = 1;
}
else if (measure->final.T[TEMP_1] > lTwarn)
{
ERR_PRIVATE.temp_warn = 1;
}
else
{
ERR_PRIVATE.temp_err = 0;
ERR_PRIVATE.temp_warn = 0;
}
}

View File

@@ -0,0 +1,18 @@
/**
******************************************************************************
* @file power_monitor.h
* @brief Модуль реализующий защиты по Напряжению/Токам/Температуре
******************************************************************************
*****************************************************************************/
#ifndef _POWER_PROTECT_H_
#define _POWER_PROTECT_H_
#include "power_monitor.h"
/* Проверяет защиты по напряжению. */
int Protect_Voltages(PowerMonitor_Measured_t *measure, UPP_PUI_Params_t *protect, UPP_ParamsNominal_t *nominal);
/* Проверяет защиты по току. */
int Protect_Currents(PowerMonitor_Measured_t *measure, UPP_PUI_Params_t *protect, UPP_ParamsNominal_t *nominal);
/* Проверяет всякие другие защиты (частота, температура). */
void Protect_Misc(PowerMonitor_Measured_t *measure, UPP_PUI_Params_t *protect, UPP_ParamsNominal_t *nominal);
#endif /* _POWER_PROTECT_H_ */

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)
{
@@ -155,21 +175,32 @@ void ZC_ProcessChannel(ZeroCross_Handle_t *zc, uint8_t channel, float value, uin
uint32_t RealTimestamp = timestamp-RealTimeShift;
if (zc_ch->LastCrossTime != 0) {
// Расчет периода и частоты
zc_ch->Period = RealTimestamp - zc_ch->LastCrossTime;
if (zc_ch->Period > 0) {
zc_ch->Frequency = 1000000.0f / zc_ch->Period;
float curr_half_period = RealTimestamp - zc_ch->LastCrossTime;
if (curr_half_period > 0) {
if(zc_ch->HalfWave == UPP_WAVE_POSITIVE)
{
zc_ch->PeriodStartTime = RealTimestamp;
zc_ch->Period = zc_ch->halfPeriod + curr_half_period;
zc_ch->Frequency = 1000000.0f / zc_ch->Period;
zc_ch->FrequencyOffset = (float)zc_ch->halfPeriod / curr_half_period;
}
}
zc_ch->halfPeriod = curr_half_period;
}
zc_ch->LastCrossTime = RealTimestamp;
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
}
/**
@@ -245,6 +276,54 @@ float ZC_GetFrequency(ZeroCross_Handle_t *zc, uint8_t channel)
return zc->Channel[channel].Frequency;
}
/**
* @brief Получение смещение частот полупериода сигнала.
* @param zc Указатель на хендл детектора нуля
* @param channel Номер канала
* @return Отношение длительности первой полуволны к второй полуволне.
*/
float ZC_GetOffset(ZeroCross_Handle_t *zc, uint8_t channel)
{
if (assert_upp(zc)){
return 0.0f;
}
if (channel >= zc->Config.NumChannels) {
return 0.0f;
}
return zc->Channel[channel].FrequencyOffset;
}
/**
* @brief Получить сдвиг между двумя фазами.
* @param zc Указатель на хендл детектора нуля
* @param channel Номер канала от которого считать сдвиг
* @param channe2 Номер канала для которого расчитать сдвиг
* @return Фазовый сдвиг в процентах от периода от 1 до 2 канала
*/
float ZC_GetPhaseShift(ZeroCross_Handle_t *zc, uint8_t channel1, uint8_t channel2)
{
if (assert_upp(zc)){
return 0.0f;
}
if ((channel1 >= zc->Config.NumChannels) || (channel2 >= zc->Config.NumChannels)){
return 0.0f;
}
float delta_time = 0;
if(zc->Channel[channel1].PeriodStartTime >= zc->Channel[channel2].PeriodStartTime)
delta_time = (zc->Channel[channel1].PeriodStartTime - zc->Channel[channel2].PeriodStartTime);
else
delta_time = (zc->Channel[channel2].PeriodStartTime - zc->Channel[channel1].PeriodStartTime);
float phase_ratio = delta_time/zc->Channel[channel1].Period;
return phase_ratio;
}
/**
* @brief Включение/выключение мониторинга.
* @param zc Указатель на хендл детектора нуля

View File

@@ -94,8 +94,11 @@ typedef struct {
float CurrentValue; ///< Текущее значение
uint16_t DebounceCounter; ///< Счетчик антидребезга
uint32_t LastCrossTime; ///< Время последнего перехода
float halfPeriod; ///< Длительность полупериода (в тактах таймера)
uint32_t PeriodStartTime; ///< Время начала периода (в тактах таймера)
uint32_t Period; ///< Период сигнала (в тактах таймера)
float Frequency; ///< Частота
float FrequencyOffset; ///< Смещение частот полупериода - насколько сигнал смещен
UPP_HalfWave_t HalfWave; ///< Текущая полуволна
ZC_EdgeType_t EdgeType; ///< Тип детектируемого перехода
} ZC_Channel_t;
@@ -124,33 +127,41 @@ typedef struct {
uint32_t LastTick; ///< Послднее время вызова
} ZeroCross_Handle_t;
// ====== ИНИЦИАЛИЗАЦИЯ ==========
/* Инициализация детектора нуля с индивидуальными настройками */
HAL_StatusTypeDef ZC_Init(ZeroCross_Handle_t *zc, uint8_t num_channels,
float hysteresis, uint16_t debounce_samples);
/* Настройка канала детектора */
HAL_StatusTypeDef ZC_ConfigChannel(ZeroCross_Handle_t *zc, uint8_t channel,
ZC_EdgeType_t edgeType);
/* Обработка значения отдельного канала */
void ZC_ProcessChannel(ZeroCross_Handle_t *zc, uint8_t channel, float value,
uint32_t timestamp);
/* Пакетная обработка всех каналов */
void ZC_ProcessAllChannels(ZeroCross_Handle_t *zc, float *values,
uint32_t timestamp);
/* Полученить флаг - переход произошел. */
int ZC_isOccurred(ZeroCross_Handle_t *zc, uint8_t channel);
/* Получение частоты сигнала */
float ZC_GetFrequency(ZeroCross_Handle_t *zc, uint8_t channel);
/* Получение полуволны (после последнего zero-cross) */
UPP_HalfWave_t ZC_GetHalfWave(ZeroCross_Handle_t *zc, uint8_t channel);
// ====== УПРАВЛЕНИЕ ==========
/* Включение/выключение мониторинга */
void ZC_EnableMonitoring(ZeroCross_Handle_t *zc, uint8_t enable);
/* Сброс статистики канала */
void ZC_Reset(ZeroCross_Handle_t *zc, uint8_t channel);
// ====== РАСЧЕТЫ ==========
/* Обработка значения отдельного канала */
void ZC_ProcessChannel(ZeroCross_Handle_t *zc, uint8_t channel, float value,
uint32_t timestamp);
/* Пакетная обработка всех каналов */
void ZC_ProcessAllChannels(ZeroCross_Handle_t *zc, float *values,
uint32_t timestamp);
// ====== API ==========
/* Полученить флаг - переход произошел. */
int ZC_isOccurred(ZeroCross_Handle_t *zc, uint8_t channel);
/* Получение частоты сигнала */
float ZC_GetFrequency(ZeroCross_Handle_t *zc, uint8_t channel);
/* Получение смещение частот полупериода сигнала */
float ZC_GetOffset(ZeroCross_Handle_t *zc, uint8_t channel);
/* Получить сдвиг между двумя фазами. */
float ZC_GetPhaseShift(ZeroCross_Handle_t *zc, uint8_t channel1, uint8_t channel2);
/* Получение полуволны (после последнего zero-cross) */
UPP_HalfWave_t ZC_GetHalfWave(ZeroCross_Handle_t *zc, uint8_t channel);
#endif /* _ZERO_CROSS_H_ */
/**

View File

@@ -0,0 +1,55 @@
/**
******************************************************************************
* @file DBG_stm32f417_support.c
* @brief Модуль для запуска кода на STM32F417
******************************************************************************
* @details
******************************************************************************/
#include "main.h"
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config_STM32F417(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 168;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
}

View File

@@ -50,10 +50,10 @@ void MX_ADC3_Init(void)
hadc3.Init.ContinuousConvMode = DISABLE;
hadc3.Init.DiscontinuousConvMode = DISABLE;
hadc3.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc3.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO;
hadc3.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T3_TRGO;
hadc3.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc3.Init.NbrOfConversion = 6;
hadc3.Init.DMAContinuousRequests = DISABLE;
hadc3.Init.DMAContinuousRequests = ENABLE;
hadc3.Init.EOCSelection = ADC_EOC_SEQ_CONV;
if (HAL_ADC_Init(&hadc3) != HAL_OK)
{
@@ -162,7 +162,7 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
hdma_adc3.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc3.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_adc3.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_adc3.Init.Mode = DMA_NORMAL;
hdma_adc3.Init.Mode = DMA_CIRCULAR;
hdma_adc3.Init.Priority = DMA_PRIORITY_LOW;
hdma_adc3.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_adc3) != HAL_OK)

View File

@@ -55,20 +55,20 @@ void MX_GPIO_Init(void)
__HAL_RCC_GPIOD_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOE, UM_LED_GREEN2_Pin|CEN_O_Pin|RDO3_Pin|UM_LED_RED_Pin
HAL_GPIO_WritePin(GPIOE, UM_LED_GREEN2_Pin|CEN_Pin|RDO3_Pin|UM_LED_RED_Pin
|UM_LED_GREEN1_Pin, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(DO2_GPIO_Port, DO2_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(DO3_GPIO_Port, DO3_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(DO3_GPIO_Port, DO3_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(DO5_GPIO_Port, DO5_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, DO5_Pin|DO4_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, DO4_Pin|RDO4_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(RDO4_GPIO_Port, RDO4_Pin, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, DO1_Pin|RDO1_Pin|SCIDE2_Pin|SCIDE1_Pin
@@ -86,7 +86,7 @@ void MX_GPIO_Init(void)
/*Configure GPIO pins : PEPin PEPin PEPin PEPin
PEPin */
GPIO_InitStruct.Pin = CEN_O_Pin|DO2_Pin|RDO3_Pin|UM_LED_RED_Pin
GPIO_InitStruct.Pin = CEN_Pin|DO2_Pin|RDO3_Pin|UM_LED_RED_Pin
|UM_LED_GREEN1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
@@ -147,11 +147,17 @@ void MX_GPIO_Init(void)
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/*Configure GPIO pins : PGPin PGPin */
GPIO_InitStruct.Pin = ERR_24V_Pin|DIN1_Pin;
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = ERR_24V_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
HAL_GPIO_Init(ERR_24V_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = DIN1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(DIN1_GPIO_Port, &GPIO_InitStruct);
}

View File

@@ -22,10 +22,10 @@
#include "can.h"
#include "dma.h"
#include "iwdg.h"
#include "rtc.h"
#include "spi.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
@@ -72,7 +72,15 @@ int main(void)
{
/* USER CODE BEGIN 1 */
__HAL_FREEZE_IWDG_DBGMCU();
__HAL_FREEZE_TIM1_DBGMCU();
__HAL_FREEZE_TIM2_DBGMCU();
__HAL_FREEZE_TIM3_DBGMCU();
__HAL_FREEZE_TIM5_DBGMCU();
__HAL_FREEZE_TIM8_DBGMCU();
__HAL_FREEZE_TIM11_DBGMCU();
__HAL_FREEZE_TIM12_DBGMCU();
__HAL_FREEZE_TIM14_DBGMCU();
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
@@ -83,22 +91,25 @@ int main(void)
/* USER CODE BEGIN Init */
#ifndef MATLAB
#if defined(STM32F427xx)
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
#elif defined(STM32F417xx)
SystemClock_Config_STM32F417();
#endif //defined(STM32F417xx)
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC3_Init();
MX_USART3_UART_Init();
MX_CAN1_Init();
MX_IWDG_Init();
MX_RTC_Init();
MX_TIM1_Init();
MX_TIM3_Init();
MX_USART6_UART_Init();
@@ -109,9 +120,15 @@ int main(void)
MX_TIM5_Init();
MX_TIM2_Init();
/* USER CODE BEGIN 2 */
// меняем прескалер на частоту по дефайну
__HAL_TIM_SET_PRESCALER_FORCE(&ustim, US_TIM5_FREQ_MHZ-1);
#if defined(STM32F417xx)
// т.к. нет епромки выставляем дефолтные
UPP_Params_SetDefault(1, 1);
#endif //defined(STM32F417xx)
#else //MATLAB
#endif //MATLAB
UPP_Init();
UPP_App_Init();
/* USER CODE END 2 */
/* Infinite loop */

View File

@@ -23,8 +23,6 @@
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "upp_main.h"
#include "pwm_thyristors.h"
#include "angle_control.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
@@ -216,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 */
}
@@ -234,19 +232,33 @@ void TIM2_IRQHandler(void)
/* USER CODE END TIM2_IRQn 1 */
}
/**
* @brief This function handles TIM8 update interrupt and TIM13 global interrupt.
*/
void TIM8_UP_TIM13_IRQHandler(void)
{
/* USER CODE BEGIN TIM8_UP_TIM13_IRQn 0 */
/* USER CODE END TIM8_UP_TIM13_IRQn 0 */
HAL_TIM_IRQHandler(&htim8);
/* USER CODE BEGIN TIM8_UP_TIM13_IRQn 1 */
//UPP_PWM_Handle();
/* USER CODE END TIM8_UP_TIM13_IRQn 1 */
}
/**
* @brief This function handles TIM8 trigger and commutation interrupts and TIM14 global interrupt.
*/
void TIM8_TRG_COM_TIM14_IRQHandler(void)
{
/* USER CODE BEGIN TIM8_TRG_COM_TIM14_IRQn 0 */
#ifndef MATLAB // в матлабе нет htim14, т.к. это систем тики
/* USER CODE END TIM8_TRG_COM_TIM14_IRQn 0 */
HAL_TIM_IRQHandler(&htim8);
HAL_TIM_IRQHandler(&htim14);
/* USER CODE BEGIN TIM8_TRG_COM_TIM14_IRQn 1 */
#endif
UPP_Tick();
/* USER CODE END TIM8_TRG_COM_TIM14_IRQn 1 */
}
@@ -256,11 +268,14 @@ void TIM8_TRG_COM_TIM14_IRQHandler(void)
void DMA2_Stream0_IRQHandler(void)
{
/* USER CODE BEGIN DMA2_Stream0_IRQn 0 */
// Вторая половина буфера (Transfer Complete)
if (DMA2->LISR & DMA_LISR_TCIF0) {
UPP_ADC_Handle();
}
/* USER CODE END DMA2_Stream0_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_adc3);
/* USER CODE BEGIN DMA2_Stream0_IRQn 1 */
UPP_ADC_Handle();
/* USER CODE END DMA2_Stream0_IRQn 1 */
}

View File

@@ -188,9 +188,7 @@ void MX_TIM3_Init(void)
/* USER CODE END TIM3_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
/* USER CODE BEGIN TIM3_Init 1 */
@@ -210,42 +208,15 @@ void MX_TIM3_Init(void)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_OnePulse_Init(&htim3, TIM_OPMODE_SINGLE) != HAL_OK)
{
Error_Handler();
}
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;
sSlaveConfig.InputTrigger = TIM_TS_ITR0;
if (HAL_TIM_SlaveConfigSynchro(&htim3, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM3_Init 2 */
/* USER CODE END TIM3_Init 2 */
HAL_TIM_MspPostInit(&htim3);
}
/* TIM5 init function */
@@ -256,8 +227,8 @@ void MX_TIM5_Init(void)
/* USER CODE END TIM5_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_IC_InitTypeDef sConfigIC = {0};
/* USER CODE BEGIN TIM5_Init 1 */
@@ -268,7 +239,12 @@ void MX_TIM5_Init(void)
htim5.Init.Period = 4294967295;
htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim5.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_IC_Init(&htim5) != HAL_OK)
if (HAL_TIM_Base_Init(&htim5) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim5, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
@@ -278,14 +254,6 @@ void MX_TIM5_Init(void)
{
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim5, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM5_Init 2 */
/* USER CODE END TIM5_Init 2 */
@@ -300,7 +268,10 @@ 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};
/* USER CODE BEGIN TIM8_Init 1 */
@@ -308,7 +279,7 @@ void MX_TIM8_Init(void)
htim8.Instance = TIM8;
htim8.Init.Prescaler = 0;
htim8.Init.CounterMode = TIM_COUNTERMODE_UP;
htim8.Init.Period = 1800-1;
htim8.Init.Period = 65535;
htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim8.Init.RepetitionCounter = 0;
htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
@@ -321,15 +292,56 @@ void MX_TIM8_Init(void)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
if (HAL_TIM_PWM_Init(&htim8) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_OnePulse_Init(&htim8, TIM_OPMODE_SINGLE) != HAL_OK)
{
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)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if (HAL_TIM_PWM_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
{
Error_Handler();
}
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 0;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim8, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM8_Init 2 */
/* USER CODE END TIM8_Init 2 */
HAL_TIM_MspPostInit(&htim8);
}
/* TIM11 init function */
@@ -436,6 +448,17 @@ void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
/* USER CODE END TIM3_MspInit 1 */
}
else if(tim_baseHandle->Instance==TIM5)
{
/* USER CODE BEGIN TIM5_MspInit 0 */
/* USER CODE END TIM5_MspInit 0 */
/* TIM5 clock enable */
__HAL_RCC_TIM5_CLK_ENABLE();
/* USER CODE BEGIN TIM5_MspInit 1 */
/* USER CODE END TIM5_MspInit 1 */
}
else if(tim_baseHandle->Instance==TIM8)
{
/* USER CODE BEGIN TIM8_MspInit 0 */
@@ -445,6 +468,8 @@ void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
__HAL_RCC_TIM8_CLK_ENABLE();
/* TIM8 interrupt Init */
HAL_NVIC_SetPriority(TIM8_UP_TIM13_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM8_UP_TIM13_IRQn);
HAL_NVIC_SetPriority(TIM8_TRG_COM_TIM14_IRQn, 15, 0);
HAL_NVIC_EnableIRQ(TIM8_TRG_COM_TIM14_IRQn);
/* USER CODE BEGIN TIM8_MspInit 1 */
@@ -474,35 +499,6 @@ void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
/* USER CODE END TIM12_MspInit 1 */
}
}
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef* tim_icHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(tim_icHandle->Instance==TIM5)
{
/* USER CODE BEGIN TIM5_MspInit 0 */
/* USER CODE END TIM5_MspInit 0 */
/* TIM5 clock enable */
__HAL_RCC_TIM5_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**TIM5 GPIO Configuration
PA0/WKUP ------> TIM5_CH1
*/
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM5;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN TIM5_MspInit 1 */
/* USER CODE END TIM5_MspInit 1 */
}
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{
@@ -530,27 +526,27 @@ void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
/* USER CODE END TIM1_MspPostInit 1 */
}
else if(timHandle->Instance==TIM3)
else if(timHandle->Instance==TIM8)
{
/* USER CODE BEGIN TIM3_MspPostInit 0 */
/* USER CODE BEGIN TIM8_MspPostInit 0 */
/* USER CODE END TIM3_MspPostInit 0 */
/* USER CODE END TIM8_MspPostInit 0 */
__HAL_RCC_GPIOC_CLK_ENABLE();
/**TIM3 GPIO Configuration
PC8 ------> TIM3_CH3
PC9 ------> TIM3_CH4
/**TIM8 GPIO Configuration
PC8 ------> TIM8_CH3
PC9 ------> TIM8_CH4
*/
GPIO_InitStruct.Pin = PWM5_Pin|PWM6_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
GPIO_InitStruct.Alternate = GPIO_AF3_TIM8;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* USER CODE BEGIN TIM3_MspPostInit 1 */
/* USER CODE BEGIN TIM8_MspPostInit 1 */
/* USER CODE END TIM3_MspPostInit 1 */
/* USER CODE END TIM8_MspPostInit 1 */
}
}
@@ -597,6 +593,17 @@ void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
/* USER CODE END TIM3_MspDeInit 1 */
}
else if(tim_baseHandle->Instance==TIM5)
{
/* USER CODE BEGIN TIM5_MspDeInit 0 */
/* USER CODE END TIM5_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM5_CLK_DISABLE();
/* USER CODE BEGIN TIM5_MspDeInit 1 */
/* USER CODE END TIM5_MspDeInit 1 */
}
else if(tim_baseHandle->Instance==TIM8)
{
/* USER CODE BEGIN TIM8_MspDeInit 0 */
@@ -606,6 +613,7 @@ void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
__HAL_RCC_TIM8_CLK_DISABLE();
/* TIM8 interrupt Deinit */
HAL_NVIC_DisableIRQ(TIM8_UP_TIM13_IRQn);
HAL_NVIC_DisableIRQ(TIM8_TRG_COM_TIM14_IRQn);
/* USER CODE BEGIN TIM8_MspDeInit 1 */
@@ -635,28 +643,6 @@ void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
}
}
void HAL_TIM_IC_MspDeInit(TIM_HandleTypeDef* tim_icHandle)
{
if(tim_icHandle->Instance==TIM5)
{
/* USER CODE BEGIN TIM5_MspDeInit 0 */
/* USER CODE END TIM5_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM5_CLK_DISABLE();
/**TIM5 GPIO Configuration
PA0/WKUP ------> TIM5_CH1
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_0);
/* USER CODE BEGIN TIM5_MspDeInit 1 */
/* USER CODE END TIM5_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */

View File

@@ -22,151 +22,202 @@ HAL_StatusTypeDef Angle_Init(Angle_Handle_t *hangle)
hangle->htim = &angletim;
// Инициализация каналов
HAL_TIM_OC_Start_IT(hangle->htim, ANGLE_CHANNEL_1);
HAL_TIM_OC_Start_IT(hangle->htim, ANGLE_CHANNEL_2);
HAL_TIM_OC_Start_IT(hangle->htim, ANGLE_CHANNEL_3);
// Сброс всех каналов
Angle_Reset(hangle, UPP_PHASE_A);
Angle_Reset(hangle, UPP_PHASE_B);
Angle_Reset(hangle, UPP_PHASE_C);
hangle->f.Initialized = 1;
hangle->Config.PeriodLimit = 1;
return HAL_OK;
}
/**
* @brief Инициализация углов открытия.
* @brief Инициализация ПИД регулятора.
* @param hangle Указатель на таймер
* @param AngleLimit Лимит AngleMax, рассчитывается от параметров ШИМ
* @param AngleMin Минимально возможный угол открытия
* @param AngleMax Максимально возможный угол открытия
* @return HAL Status.
* @param kp, ki kd Коэффициенты регулятора
*/
HAL_StatusTypeDef Angle_SetRange(Angle_Handle_t *hangle, float AngleMin, float AngleMax)
HAL_StatusTypeDef Angle_PID_Init(Angle_Handle_t *hangle, float kp, float ki, float kd, float alpha)
{
if(assert_upp(hangle))
return HAL_ERROR;
if(hangle->f.Running)
return HAL_BUSY;
if(AngleMax < 0 || AngleMax > 1)
return HAL_ERROR;
if(AngleMin < 0 || AngleMin > 1)
return HAL_ERROR;
if(AngleMax > hangle->Config.AngleLimit)
AngleMax = hangle->Config.AngleLimit;
if(AngleMin > hangle->Config.AngleLimit)
AngleMin = hangle->Config.AngleLimit;
// Сам ПИД регулятор
hangle->pid.Kp = kp;
hangle->pid.Ki = ki;
hangle->pid.Kd = kd;
arm_pid_init_f32(&hangle->pid, 1);
if(AngleMin >= AngleMax)
return HAL_ERROR;
hangle->Config.AngleMax = AngleMax;
hangle->Config.AngleMin = AngleMin;
// Сглаживающий фильтр для задания ПИД регулятора
FilterExp_Init(&hangle->refFilter, alpha);
Filter_Start(&hangle->refFilter);
Filter_Process(&hangle->refFilter, 0);
return HAL_OK;
}
/**
* @brief Инициализация предельного угла открытия.
* @brief Управление углом через ПИД регулятор.
* @param hangle Указатель на таймер
* @param AngleLimit Лимит AngleMax, рассчитывается от параметров ШИМ
* @param AngleMin Минимально возможный угол открытия
* @param AngleMax Максимально возможный угол открытия
* @return HAL Status.
* @param setpoint Уставка куда регулировать
* @param measurement Измеренные регулируемые величины
*/
HAL_StatusTypeDef Angle_SetLimit(Angle_Handle_t *hangle, float AngleLimit)
void Angle_PID(Angle_Handle_t *hangle, float setpoint, float measurement, float Correction)
{
if(assert_upp(hangle))
return HAL_ERROR;
if(hangle->f.Running)
return HAL_BUSY;
return;
if(AngleLimit < 0 || AngleLimit > 1)
return HAL_ERROR;
/* Плавное нарастание уставки */
hangle->Iref = Filter_Process(&hangle->refFilter, setpoint);
hangle->Imeas = measurement;
/* Ошибка регулирования = уставка - измеренное */
float err = hangle->Iref - hangle->Imeas;
hangle->Config.AngleLimit = AngleLimit;
return HAL_OK;
}
/**
* @brief Хендл таймера для рассчета угла открытия.
* @param hangle Указатель на таймер
* @return HAL Status.
*/
UPP_Phase_t Angle_Handle(Angle_Handle_t *hangle)
{
if(assert_upp(hangle))
return UPP_PHASE_UNKNOWN;
/* ПИД регулирование */
float open_level = arm_pid_f32(&hangle->pid, err); // 0 - открыть максимально поздно, 1 - открыть макситмально рано
switch(hangle->htim->Channel)
/* Ограничиваем диапазон */
if (open_level > 1)
{
case HAL_TIM_ACTIVE_CHANNEL_1:
return UPP_PHASE_A;
break;
open_level = 1;
}
if(open_level < 0)
{
open_level = 0;
case HAL_TIM_ACTIVE_CHANNEL_2:
return UPP_PHASE_B;
break;
case HAL_TIM_ACTIVE_CHANNEL_3:
return UPP_PHASE_C;
break;
default:
return UPP_PHASE_UNKNOWN;
break;
}
/* Приводим уровень открытия к косинусу [-1:1]*/
float OpenLevelForCos = (open_level*2)-1;
return UPP_PHASE_UNKNOWN;
float alpha_rad = acosf(OpenLevelForCos); // угол в радианах
float alpha = alpha_rad/PI*hangle->Config.AngleMax; // угол открытия тиристора в о.е. от максимально заданного
/* Выставляем заданный уровень открытия */
Angle_SetAlpha(hangle, alpha, Correction);
}
/**
* @brief Установка угла открытия в таймер.
* @brief Сброс ПИД регулятора.
* @param hangle Указатель на таймер
*/
void Angle_PID_Reset(Angle_Handle_t *hangle)
{
if(assert_upp(hangle))
return;
hangle->Iref = 0;
hangle->Imeas = 0;
/* Вычисляем выход PID */
arm_pid_reset_f32(&hangle->pid);
Filter_ReInit(&hangle->refFilter, hangle->refFilter.alpha);
Filter_Start(&hangle->refFilter);
Filter_Process(&hangle->refFilter, 0);
Angle_SetAlpha(hangle, 1, 30); // максимально закрываем
Angle_Reset(hangle, UPP_PHASE_A);
Angle_Reset(hangle, UPP_PHASE_B);
Angle_Reset(hangle, UPP_PHASE_C);
}
/**
* @brief Выставление степени открытия тиристоров.
* @param hangle Указатель на таймер
* @param Alpha Угол открытия тиристора в о.е. от 180 градусов:
- 0 - максимально закрыт,
- 1 - максимально открыт
* @param Коррекция угла в градусах
* @return HAL Status.
*/
HAL_StatusTypeDef Angle_SetAlpha(Angle_Handle_t *hangle, float Alpha, float Correction)
{
if(assert_upp(hangle))
return HAL_ERROR;
if(Alpha > hangle->Config.AngleMax)
Alpha = hangle->Config.AngleMax;
if(Alpha < hangle->Config.AngleMin)
Alpha = hangle->Config.AngleMin;
// сколько надо выжидать исходя из заданного угла
hangle->alpha_real = Alpha + (Correction/180.0);
hangle->alpha = Alpha;
return HAL_OK;
}
/**
* @brief Запуск отсчета угла открытия.
* @param hangle Указатель на таймер
* @param Phase Для какой фазы надо установить угол открытия
* @return HAL Status.
*/
HAL_StatusTypeDef Angle_Start(Angle_Handle_t *hangle, UPP_Phase_t Phase, float Angle, float PeriodMs)
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(Angle > hangle->Config.AngleMax)
{
Angle = hangle->Config.AngleMax;
}
if(Angle < hangle->Config.AngleMin)
{
Angle = hangle->Config.AngleMin;
}
uint32_t timer_ticks = TIM_MillisToTick(PeriodMs*Angle, 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;
// Выставялем
switch(Phase)
{
case UPP_PHASE_A:
__HAL_TIM_CLEAR_IT(hangle->htim, TIM_IT_CC1); // очищаем флаг навсякий
__HAL_TIM_SET_COMPARE(hangle->htim, ANGLE_CHANNEL_1, ccr_ticks);
// Если слишком маленький timer_tick и счетчик уже перевалил за ccr, но не сгенерил прервыание:
if (__HAL_TIM_GET_COMPARE(hangle->htim, ANGLE_CHANNEL_1) <= __HAL_TIM_GET_COUNTER(hangle->htim))
{
// включаем прерывание вручную
HAL_TIM_GenerateEvent(hangle->htim, TIM_EVENTSOURCE_CC1);
}
__HAL_TIM_ENABLE_IT(hangle->htim, TIM_IT_CC1);
hangle->f.Running++;
break;
case UPP_PHASE_B:
__HAL_TIM_CLEAR_IT(hangle->htim, TIM_IT_CC2); // очищаем флаг навсякий
__HAL_TIM_SET_COMPARE(hangle->htim, ANGLE_CHANNEL_2, ccr_ticks);
// Если слишком маленький timer_tick и счетчик уже перевалил за ccr, но не сгенерил прервыание:
if (__HAL_TIM_GET_COMPARE(hangle->htim, ANGLE_CHANNEL_2) <= __HAL_TIM_GET_COUNTER(hangle->htim))
{
// включаем прерывание вручную
HAL_TIM_GenerateEvent(hangle->htim, TIM_EVENTSOURCE_CC2);
}
__HAL_TIM_ENABLE_IT(hangle->htim, TIM_IT_CC2);
hangle->f.Running++;
break;
case UPP_PHASE_C:
__HAL_TIM_CLEAR_IT(hangle->htim, TIM_IT_CC3); // очищаем флаг навсякий
__HAL_TIM_SET_COMPARE(hangle->htim, ANGLE_CHANNEL_3, ccr_ticks);
// Если слишком маленький timer_tick и счетчик уже перевалил за ccr, но не сгенерил прервыание:
if (__HAL_TIM_GET_COMPARE(hangle->htim, ANGLE_CHANNEL_3) <= __HAL_TIM_GET_COUNTER(hangle->htim))
{
// включаем прерывание вручную
HAL_TIM_GenerateEvent(hangle->htim, TIM_EVENTSOURCE_CC3);
}
__HAL_TIM_ENABLE_IT(hangle->htim, TIM_IT_CC3);
hangle->f.Running++;
break;
@@ -189,15 +240,9 @@ HAL_StatusTypeDef Angle_Start(Angle_Handle_t *hangle, UPP_Phase_t Phase, float A
*/
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:
@@ -224,3 +269,93 @@ HAL_StatusTypeDef Angle_Reset(Angle_Handle_t *hangle, UPP_Phase_t Phase)
}
return HAL_OK;
}
/**
* @brief Инициализация предельного угла открытия.
* @param hangle Указатель на таймер
* @param PeriodLimit Лимит AngleMax, рассчитывается от параметров ШИМ
* @param AngleMin Минимально возможный угол открытия
* @param AngleMax Максимально возможный угол открытия
* @return HAL Status.
*/
HAL_StatusTypeDef Angle_SetLimit(Angle_Handle_t *hangle, float PeriodLimit)
{
if(assert_upp(hangle))
return HAL_ERROR;
if(hangle->f.Running)
return HAL_BUSY;
if(PeriodLimit <= 0 || PeriodLimit > 1)
return HAL_ERROR;
hangle->Config.PeriodLimit = PeriodLimit;
return HAL_OK;
}
/**
* @brief Инициализация углов открытия.
* @param hangle Указатель на таймер
* @param PeriodLimit Лимит AngleMax, рассчитывается от параметров ШИМ
* @param AngleMin Минимально возможный угол открытия
* @param AngleMax Максимально возможный угол открытия
* @return HAL Status.
*/
HAL_StatusTypeDef Angle_SetRange(Angle_Handle_t *hangle, float AngleMin, float AngleMax)
{
if(assert_upp(hangle))
return HAL_ERROR;
if(hangle->f.Running)
return HAL_BUSY;
if(AngleMax < 0 || AngleMax > 1)
return HAL_ERROR;
if(AngleMin < 0 || AngleMin > 1)
return HAL_ERROR;
if(AngleMin >= AngleMax)
return HAL_ERROR;
hangle->Config.AngleMax = AngleMax;
hangle->Config.AngleMin = AngleMin;
return HAL_OK;
}
/**
* @brief Хендл таймера для рассчета угла открытия.
* @param hangle Указатель на таймер
* @return HAL Status.
* @details Просто возвращает фазу, у которой закончился отсчет угла
*/
UPP_Phase_t Angle_Handle(Angle_Handle_t *hangle)
{
if(assert_upp(hangle))
return UPP_PHASE_UNKNOWN;
switch(hangle->htim->Channel)
{
case HAL_TIM_ACTIVE_CHANNEL_1:
return UPP_PHASE_A;
break;
case HAL_TIM_ACTIVE_CHANNEL_2:
return UPP_PHASE_B;
break;
case HAL_TIM_ACTIVE_CHANNEL_3:
return UPP_PHASE_C;
break;
default:
return UPP_PHASE_UNKNOWN;
break;
}
return UPP_PHASE_UNKNOWN;
}

View File

@@ -8,35 +8,68 @@
#define _ANGLE_CONTROL_H_
#include "main.h"
/**
* @brief Конфигурация алгоритма управления углом открытия
*/
typedef struct
{
float AngleLimit; ///< Лимит AngleMax, рассчитывается от параметров ШИМ
float AngleMin; ///< Минимально возможный угол открытия
float AngleMax; ///< Максимально возможный угол открытия
float PeriodLimit; ///< Лимит периода, выше которого нельзя выставить рассчитывается от параметров ШИМ
float AngleMin; ///< Минимально возможный угол открытия
float AngleMax; ///< Максимально возможный угол открытия
}Angle_Config_t;
/**
* @brief Структура для управления углом открытия
*/
typedef struct
{
TIM_HandleTypeDef *htim;
Angle_Config_t Config;
TIM_HandleTypeDef *htim; ///< Указатель на таймер для расчета угла
Angle_Config_t Config; ///< Конфигурации алгоритма расчета угла открытия тиристоров
float Iref; ///< текущее задание тока в о.е. [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; ///< Фильтр для плавного нарастания регулирования
struct {
unsigned Initialized : 1;
unsigned Initialized : 1; ///< Структура инициализирована
unsigned Running : 3; ///< Сколько каналов запущено сейчас
} f;
} f; ///< Флаги
}Angle_Handle_t;
// ====== ИНИЦИАЛИЗАЦИЯ ==========
/* Инициализация Таймера для рассчета угла открытия. */
HAL_StatusTypeDef Angle_Init(Angle_Handle_t *hangle);
/* Инициализация углов открытия. */
HAL_StatusTypeDef Angle_SetRange(Angle_Handle_t *hangle, float AngleMin, float AngleMax);
/* Инициализация предельного угла открытия. */
HAL_StatusTypeDef Angle_SetLimit(Angle_Handle_t *hangle, float AngleLimit);
/* Инициализация ПИД регулятора. */
HAL_StatusTypeDef Angle_PID_Init(Angle_Handle_t *hangle, float kp, float ki, float kd, float alpha);
// ====== УПРАВЛЕНИЕ ==========
/* Управление углом через ПИД регулятор */
void Angle_PID(Angle_Handle_t *hangle, float setpoint, float measurement, float Correction);
/* Выставление текущего угла открытия тиристоров. */
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 Angle, float PeriodMs);
HAL_StatusTypeDef Angle_Start(Angle_Handle_t *hangle, UPP_Phase_t Phase, float PeriodMs);
// ====== СБРОС ==========
/* Сброс угла открытия у таймера. */
HAL_StatusTypeDef Angle_Reset(Angle_Handle_t *hangle, UPP_Phase_t Phase);
/* Сброс ПИД регулятора. */
void Angle_PID_Reset(Angle_Handle_t *hangle);
// ====== СЕРВИС ==========
/* Инициализация предельного угла открытия. */
HAL_StatusTypeDef Angle_SetLimit(Angle_Handle_t *hangle, float PeriodLimit);
/* Инициализация углов открытия. */
HAL_StatusTypeDef Angle_SetRange(Angle_Handle_t *hangle, float AngleMin, float AngleMax);
/* Хендл таймера для рассчета угла открытия. */
UPP_Phase_t Angle_Handle(Angle_Handle_t *hangle);
#endif /* _ANGLE_CONTROL_H_ */

View File

@@ -6,10 +6,10 @@
* @details
******************************************************************************/
#include "pwm_thyristors.h"
#include "angle_control.h"
#include "tim.h"
static HAL_StatusTypeDef __PWM_SetOutputState(PWM_Channel_t *hCh, uint32_t state);
static HAL_StatusTypeDef __PWM_ReConfigToSoftwarePulses(void);
/**
* @brief Инициализация ШИМ тиристоров.
@@ -59,16 +59,16 @@ 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, MB_INTERNAL.param.pwm.PhaseMask, MB_INTERNAL.param.pwm.Frequency, MB_INTERNAL.param.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);
HAL_TIM_PWM_Start(&hpwm1, PWM_CHANNEL_4);
HAL_TIM_PWM_Start(&hpwm2, PWM_CHANNEL_5);
HAL_TIM_PWM_Start(&hpwm2, PWM_CHANNEL_6);
HAL_TIM_Base_Start_IT(&hpwm1);
PWM_Stop(hpwm, 0, 1);
__PWM_ReConfigToSoftwarePulses();
HAL_TIM_Base_Start(&hpwm1);
return HAL_OK;
}
@@ -78,20 +78,16 @@ HAL_StatusTypeDef PWM_Init(PWM_Handle_t *hpwm)
* @param hpwm Указатель на хендл ШИМ тиристоров
* @param Phase На какой фазе надо запустить ШИМ
* @return HAL Status.
* @details Переводит автомат канала ШИМ в состояние запуска ШИМ.
*/
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(Phase >= 3)
{
return HAL_ERROR;
}
if (hpwm->Phase[Phase] == NULL || hpwm->Phase[Phase] == &hpwm->AllPhases[PHASE_UNKNOWN])
return HAL_ERROR;
if(!(hpwm->Config.PhaseMask.all&(1<<Phase)))
return HAL_ERROR;
switch(hpwm->Phase[Phase]->State)
{
@@ -110,7 +106,6 @@ HAL_StatusTypeDef PWM_Start(PWM_Handle_t *hpwm, UPP_Phase_t Phase)
default:
return HAL_ERROR;
}
return HAL_ERROR;
}
@@ -120,46 +115,145 @@ HAL_StatusTypeDef PWM_Start(PWM_Handle_t *hpwm, UPP_Phase_t Phase)
* @param Phase На какой фазе надо остановить ШИМ
* @param force_stop_all Принудительно остановить ВЕСЬ ШИМ
* @return HAL Status.
* @details Переводит автомат канала ШИМ в состояние отключенного ШИМ и
* включает канал в режим форсированного неактивного выхода.
* При передаче 1 в force_stop_all, отключаются все каналы ШИМ и выдается дискрет на запрет ШИМ
* @details Включает канал в режим форсированного неактивного выхода.
* При передаче 1 в force_stop_all, отключаются MOE (Main Output Enable) и все каналы ШИМ
*/
HAL_StatusTypeDef PWM_Stop(PWM_Handle_t *hpwm, UPP_Phase_t Phase, uint8_t force_stop_all)
{
if(assert_upp(hpwm))
return HAL_ERROR;
// Если канал дурацкий - возвращаем ошибку
if(Phase >= 3)
{
return HAL_ERROR;
}
if (hpwm->Phase[Phase] == NULL || hpwm->Phase[Phase] == &hpwm->AllPhases[PHASE_UNKNOWN])
if(assert_upp_phase(hpwm, Phase))
return HAL_ERROR;
hpwm->Phase[Phase]->State = PWM_THYR_DISABLED;
// Если не force_stop_all - сбрасываем только текущий канал
if (!force_stop_all)
{
__PWM_SetOutputState(hpwm->Phase[Phase], PWM_DISABLE);
}
// Если force_stop_all - сбрасываем ВСЕ КАНАЛЫ
else {
if(force_stop_all)
{
// в первую очередь выключаем канал, потом выставим режим каналов
__HAL_TIM_MOE_DISABLE(&hpwm1);
__HAL_TIM_MOE_DISABLE(&hpwm2);
// выставляем все каналы в FORCE MODE
for(int ch = 0; ch < 6; ch++)
{
__PWM_SetOutputState(&hpwm->AllPhases[ch], PWM_DISABLE);
}
return HAL_OK;
}
// Если НЕ force_stop_all - сбрасываем ТОЛЬКО заданный канал
else
{
// Если не force_stop_all - сбрасываем только текущий канал
__PWM_SetOutputState(hpwm->Phase[Phase], PWM_DISABLE);
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 Установка параметров ШИМ.
* @param hpwm Указатель на хендл ШИМ тиристоров
* @param Frequency Частота в ГЦ
* @return HAL Status.
*/
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;
if(hpwm->f.Running) // Если есть активные каналы - ниче не меняем
return HAL_BUSY;
// Остановка таймера
HAL_TIM_Base_Stop(&hpwm1);
hpwm->Config.PhaseMask.all = PhaseMask;
hpwm->Config.PulseLength = PulseLength;
hpwm->Config.Frequency = Frequency;
hpwm->Config.Duty = Duty;
// Высставление периодов
__HAL_TIM_SET_AUTORELOAD(&hpwm1, TIM_FreqToTick(Frequency, PWM_TIM1_FREQ_MHZ-1));
__HAL_TIM_SET_AUTORELOAD(&hpwm2, TIM_FreqToTick(Frequency, PWM_TIM8_FREQ_MHZ-1));
// Скважности
uint32_t pwm1_duty_ccr = (float)__HAL_TIM_GET_AUTORELOAD(&hpwm1)*(1-Duty);
uint32_t pwm2_duty_ccr = (float)__HAL_TIM_GET_AUTORELOAD(&hpwm2)*(1-Duty);
__HAL_TIM_SET_COMPARE(&hpwm1, PWM_CHANNEL_1, pwm1_duty_ccr);
__HAL_TIM_SET_COMPARE(&hpwm1, PWM_CHANNEL_2, pwm1_duty_ccr);
__HAL_TIM_SET_COMPARE(&hpwm1, PWM_CHANNEL_3, pwm1_duty_ccr);
__HAL_TIM_SET_COMPARE(&hpwm1, PWM_CHANNEL_4, pwm1_duty_ccr);
__HAL_TIM_SET_COMPARE(&hpwm2, PWM_CHANNEL_5, pwm2_duty_ccr);
__HAL_TIM_SET_COMPARE(&hpwm2, PWM_CHANNEL_6, pwm2_duty_ccr);
// Сброс счетчиков таймера и запуск заного
__HAL_TIM_SET_COUNTER(&hpwm1, 0);
__HAL_TIM_SET_COUNTER(&hpwm2, 0);
PWM_Stop(hpwm, 0, 1);
return HAL_TIM_Base_Start(&hpwm1);
}
/**
* @brief Установка полярности шим.
* @param hpwm Указатель на хендл ШИМ тиристоров
* @param polarity Какая полярность: состяоние бита CCxP
* @return HAL Status.
*/
HAL_StatusTypeDef PWM_SetPolarity(PWM_Handle_t *hpwm, int polarity)
{
if(assert_upp(hpwm))
return HAL_ERROR;
if(polarity)
{
hpwm1.Instance->CCER |= TIM_CCER_CC1P | TIM_CCER_CC2P | TIM_CCER_CC3P | TIM_CCER_CC4P;
hpwm2.Instance->CCER |= TIM_CCER_CC1P | TIM_CCER_CC2P | TIM_CCER_CC3P | TIM_CCER_CC4P;
}
else
{
hpwm1.Instance->CCER &= ~(TIM_CCER_CC1P | TIM_CCER_CC2P | TIM_CCER_CC3P | TIM_CCER_CC4P);
hpwm2.Instance->CCER &= ~(TIM_CCER_CC1P | TIM_CCER_CC2P | TIM_CCER_CC3P | TIM_CCER_CC4P);
}
return HAL_OK;
}
/**
* @brief Хендл ШИМ тиристоров.
* @param hpwm Указатель на хендл ШИМ тиристоров
* @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;
@@ -185,14 +279,14 @@ HAL_StatusTypeDef PWM_Handle(PWM_Handle_t *hpwm)
case PWM_THYR_TIM_START: // начать ШИМ (пачка импульсов)
__PWM_SetOutputState(hPhase, PWM_ENABLE);
hPhase->PulseCnt = hpwm->Config.PulseNumber;
hPhase->PulseCnt = MS_TO_FAST_TICKS(PeriodMs*hpwm->Config.PulseLength) - 1; // 1 импульс уже прошел
hPhase->State = PWM_THYR_TIM_ACTIVE;
hpwm->f.Running++;
break;
case PWM_THYR_TIM_ACTIVE: // управление пачкой импульсов ШИМ
hPhase->PulseCnt--;
if (hPhase->PulseCnt <= 0)
if (hPhase->PulseCnt <= 1) // если остался один импльс в след раз идем в PWM_THYR_TIM_DONE
{
hPhase->PulseCnt = 0;
hPhase->State = PWM_THYR_TIM_DONE;
@@ -213,85 +307,8 @@ HAL_StatusTypeDef PWM_Handle(PWM_Handle_t *hpwm)
return HAL_OK;
}
/**
* @brief Установка параметров ШИМ.
* @param hpwm Указатель на хендл ШИМ тиристоров
* @param Frequency Частота в ГЦ
* @return HAL Status.
*/
HAL_StatusTypeDef PWM_SetConfig(PWM_Handle_t *hpwm, uint8_t PhaseMask, uint16_t Frequency, uint8_t PulseNumber)
{
if(assert_upp(hpwm))
return HAL_ERROR;
if(hpwm->f.Running) // Если есть активные каналы - ниче не меняем
return HAL_BUSY;
// Остановка таймера
HAL_TIM_Base_Stop_IT(&hpwm1);
hpwm->Config.PhaseMask.all = PhaseMask;
hpwm->Config.PulseNumber = PulseNumber;
hpwm->Config.Frequency = Frequency;
// Высставление периодов
__HAL_TIM_SET_AUTORELOAD(&hpwm1, TIM_FreqToTick(Frequency, PWM_TIM1_FREQ_MHZ));
__HAL_TIM_SET_AUTORELOAD(&hpwm2, TIM_FreqToTick(Frequency, PWM_TIM3_FREQ_MHZ));
// Скважности 50/50
__HAL_TIM_SET_COMPARE(&hpwm1, PWM_CHANNEL_1, __HAL_TIM_GET_AUTORELOAD(&hpwm1)/2);
__HAL_TIM_SET_COMPARE(&hpwm1, PWM_CHANNEL_2, __HAL_TIM_GET_AUTORELOAD(&hpwm1)/2);
__HAL_TIM_SET_COMPARE(&hpwm1, PWM_CHANNEL_3, __HAL_TIM_GET_AUTORELOAD(&hpwm1)/2);
__HAL_TIM_SET_COMPARE(&hpwm1, PWM_CHANNEL_4, __HAL_TIM_GET_AUTORELOAD(&hpwm1)/2);
__HAL_TIM_SET_COMPARE(&hpwm2, PWM_CHANNEL_5, __HAL_TIM_GET_AUTORELOAD(&hpwm2)/2);
__HAL_TIM_SET_COMPARE(&hpwm2, PWM_CHANNEL_6, __HAL_TIM_GET_AUTORELOAD(&hpwm2)/2);
// Сброс счетчиков таймера и запуск заного
__HAL_TIM_SET_COUNTER(&hpwm1, 0);
__HAL_TIM_SET_COUNTER(&hpwm2, 0);
return HAL_TIM_Base_Start_IT(&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 Указатель на хендл ШИМ тиристоров
@@ -306,29 +323,54 @@ static HAL_StatusTypeDef __PWM_SetOutputState(PWM_Channel_t *hCh, uint32_t state
if (hCh->htim == NULL)
return HAL_ERROR;
// Если режим уже выставлен
if(hCh->CurrentMode == state)
{
return HAL_OK;
}
uint32_t ch_mode = state;
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;
}
return HAL_OK;
}
/**
* @brief Переконфигурация таймером для софтварного формирования пачки импульсов.
* @return HAL Status.
*/
static HAL_StatusTypeDef __PWM_ReConfigToSoftwarePulses(void)
{
/* One Pulse и Repetitive не используем */
hpwm1.Instance->RCR = 0;
hpwm1.Instance->CR1 &= ~TIM_CR1_OPM;
hpwm2.Instance->RCR = 0;
hpwm2.Instance->CR1 &= ~TIM_CR1_OPM;
/* Настраиваем Slave на втором таймере*/
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;
sSlaveConfig.InputTrigger = TIM_TS_ITR0;
return HAL_TIM_SlaveConfigSynchro(&htim8, &sSlaveConfig);
}

View File

@@ -9,8 +9,12 @@
#include "main.h"
/**
* @brief Вкоючить хардварный способ формирования пачки импульсов
* @note При отключении сильно возрастет нагрузка на контроллер из-за прерываний ШИМ!
* Они поак работают всегда, независимо от того есть импулсь или нет
*/
#define PWM_HARDWARE_IMPULSES_CONTROL
#define PWM_ENABLE TIM_OCMODE_PWM2
@@ -21,14 +25,16 @@
// Индексы для структур каналов @ref PWM_Handle_t
#define PHASE_A_POS 0
#define PHASE_B_POS 1
#define PHASE_C_POS 2
#define PHASE_C_POS 1
#define PHASE_B_POS 2
#define PHASE_A_NEG 3
#define PHASE_B_NEG 4
#define PHASE_C_NEG 5
#define PHASE_C_NEG 4
#define PHASE_B_NEG 5
#define PHASE_UNKNOWN 6
#define PWM_PulseLengthToCnt(_length_, _period_) ((_length_)*(_period_)/
/**
* @brief Состояния канала
*/
@@ -48,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; ///< Флаг готовности тиристора к работе
@@ -68,16 +75,17 @@ typedef struct {
uint8_t phC:1;
};
}PhaseMask; ///< Какими каналами управлять
uint8_t PulseNumber; ///< Сколько импульсов отправить в пакете для открытия тиристоров
float PulseLength; ///< Длина импульса в о.е. от 180 градусов
uint16_t Frequency; ///< Частота импульсов
float Duty; ///< Скважность импульсов
} PWM_ThyrConfig_t;
/**
* @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;
@@ -85,18 +93,23 @@ typedef struct {
} f;
} PWM_Handle_t;
/* ---- API ---- */
// ====== ИНИЦИАЛИЗАЦИЯ ==========
/* Инициализация ШИМ тиристоров. */
HAL_StatusTypeDef PWM_Init(PWM_Handle_t *hpwm);
// ====== УПРАВЛЕНИЕ ==========
/* Запуск ШИМ. */
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, 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

@@ -1,289 +0,0 @@
/**
******************************************************************************
* @file upp_control.c
* @brief Модуль определябщий поведение УПП
******************************************************************************
* @details
******************************************************************************/
#include "upp_main.h" // всё остальное по работе с УПП
static int __CheckSimpleParamF(float *paramDist, uint16_t paramSrc, float Coef);
static int __CheckSimpleParamU32(uint32_t *paramDist, uint16_t paramSrc, float Coef);
static int __CheckSimpleParamU16(uint16_t *paramDist, uint16_t paramSrc);
static int __CheckSimpleParamU8(uint8_t *paramDist, uint16_t paramSrc, float Coef);
static void __AngleSetLimit(void);
/**
* @brief Контроль внутренних параметров УПП.
* @return HAL Status.
*/
void UPP_Control_InternalParams(void)
{
if(upp.call->go) // при запущеном УПП ничего не меняем
return;
// флаги обновились ли конфиги
static int adc_channel_update[ADC_NUMB_OF_REGULAR_CHANNELS] = {0};
static int zc_update = 0;
static int pwm_update = 0;
// временная переменная для параметров каналов АЦП
float adc_channel_max[ADC_NUMB_OF_REGULAR_CHANNELS] = {0};
uint16_t adc_channel_zero[ADC_NUMB_OF_REGULAR_CHANNELS] = {0};
// временная переменная для параметров перехода через ноль
float zc_hysteresis = upp.pm.zc.Config.Hysteresis;
uint16_t zc_debounce = upp.pm.zc.Config.DebounceSamples;
// временная переменная для параметров ШИМ
uint8_t pwm_phase_mask = upp.hpwm.Config.PhaseMask.all;
uint16_t pwm_freq = upp.hpwm.Config.Frequency;
uint8_t pwm_pulse_num = upp.hpwm.Config.PulseNumber;
// временная переменная для параметров Мониторинга сети
float pm_alpha = upp.pm.measured.exp[0].alpha;
for(int i = 0; i < ADC_NUMB_OF_REGULAR_CHANNELS; i++)
{
adc_channel_max[i] = upp.pm.adc.Coefs[i].vMax;
adc_channel_zero[i] = upp.pm.adc.Coefs[i].lZero;
// Максимальное измеряемое напряжение
if(__CheckSimpleParamF(&adc_channel_max[i], MB_INTERNAL.param.adc.ADC_Max[i], 10))
{
adc_channel_update[i] = 1;
}
// Значение АЦП при нулевом входе
if(__CheckSimpleParamU16(&adc_channel_zero[i], MB_INTERNAL.param.adc.ADC_Zero[i]))
{
adc_channel_update[i] = 1;
}
}
// Параметры алгоритма перехода через ноль
if(__CheckSimpleParamF(&zc_hysteresis, MB_INTERNAL.param.zc.Hysteresis, 10000))
{
zc_update = 1;
}
if(__CheckSimpleParamU16(&zc_debounce, MB_INTERNAL.param.zc.DebouneCouner))
{
zc_update = 1;
}
// Параметры ШИМ токов
if(__CheckSimpleParamU8(&pwm_phase_mask, MB_INTERNAL.param.pwm.PhaseMask, 1))
{
pwm_update = 1;
}
if(__CheckSimpleParamU16(&pwm_freq, MB_INTERNAL.param.pwm.Frequency))
{
pwm_update = 1;
}
if(__CheckSimpleParamU8(&pwm_pulse_num, MB_INTERNAL.param.pwm.PulseNumber, 1))
{
pwm_update = 1;
}
// Параметры мониторинга
if(__CheckSimpleParamF(&pm_alpha, MB_INTERNAL.param.pm.mean_alpha, 65535))
{
for(int i = 0; i < 3; i++)
{
Filter_ReInit(&upp.pm.measured.exp[i], pm_alpha);
}
}
// Обновление АЦП конфигов
for(int i = 0; i < ADC_NUMB_OF_REGULAR_CHANNELS; i++)
{
if(adc_channel_update[i])
{
if(ADC_ConfigChannel(&upp.pm.adc, i, adc_channel_zero[i], adc_channel_max[i], 4095) == HAL_OK)
adc_channel_update[i] = 0;
else
errors.prvt.cnt.adc_reinit_err++;
}
}
// Обновление Zero-Cross конфигов
if(zc_update)
{
if(ZC_Init(&upp.pm.zc, upp.pm.zc.Config.NumChannels, zc_hysteresis, zc_debounce) == HAL_OK)
zc_update = 0;
else
errors.prvt.cnt.zc_reinit_err++;
}
// Обновление ШИМ конфигов
if(pwm_update)
{
if(PWM_SetConfig(&upp.hpwm, pwm_phase_mask, pwm_freq, pwm_pulse_num) == HAL_OK)
{
pwm_update = 0;
__AngleSetLimit();
}
else
errors.prvt.cnt.pwm_reinit_err++;
}
if (upp.hangle.Config.AngleLimit == 0)
{
__AngleSetLimit();
}
}
/**
* @brief Установка параметров на дефолтные значения @ref UPP_DEFAULT_PARAMS.
* @param pui_default Сбросить параметры ПУИ
* @param internal_default Сбросить внутренние параметры
* @return HAL Status.
*/
void UPP_SetDefault(int pui_default, int internal_default)
{
if(pui_default)
{
MB_DATA.HoldRegs.pui_params.Iref = PUI_Iref_PERCENT_DEFAULT;
MB_DATA.HoldRegs.pui_params.Tnt = PUI_Tnt_MS_DEFAULT;
MB_DATA.HoldRegs.pui_params.Umin = PUI_Umin_PERCENT_DEFAULT;
MB_DATA.HoldRegs.pui_params.Umax = PUI_Umax_PERCENT_DEFAULT;
MB_DATA.HoldRegs.pui_params.Imax = PUI_Imax_PERCENT_DEFAULT;
MB_DATA.HoldRegs.pui_params.Imin = PUI_Imin_PERCENT_DEFAULT;
MB_DATA.HoldRegs.pui_params.TiMax = PUI_TiMax_US_DEFAULT;
MB_DATA.HoldRegs.pui_params.Tdelay = PUI_Tdelay_SECONDS_DEFAULT;
MB_DATA.HoldRegs.pui_params.Interlace = PUI_Interlace_EN_DEFAULT;
}
if(internal_default)
{
MB_INTERNAL.param.nominal.PhaseNumber = NOM_PHASE_NUMB;
MB_INTERNAL.param.nominal.U = NOM_U_V_DEFAULT*10;
MB_INTERNAL.param.nominal.U_deviation_plus = NOM_U_DEVIATION_PLUS_PERCENT_DEFAULT*100;
MB_INTERNAL.param.nominal.U_deviation_minus = NOM_U_DEVIATION_MINUS_PERCENT_DEFAULT*100;
MB_INTERNAL.param.nominal.F = NOM_F_HZ_DEFAULT*100;
MB_INTERNAL.param.nominal.F_deviation_plus = NOM_F_DEVIATION_PLUS_PERCENT_DEFAULT*100;
MB_INTERNAL.param.nominal.F_deviation_minus = NOM_F_DEVIATION_MINUS_PERCENT_DEFAULT*100;
MB_INTERNAL.param.nominal.I = NOM_I_A_DEFAULT*10;
MB_INTERNAL.param.pm.mean_alpha = PM_EXP_ALPHA_COEF_DEFAULT*65535;
MB_INTERNAL.param.adc.ADC_Max[ADC_CHANNEL_UBA] = ADC_U_MAX_V_DEFAULT*10;
MB_INTERNAL.param.adc.ADC_Max[ADC_CHANNEL_UAC] = ADC_U_MAX_V_DEFAULT*10;
MB_INTERNAL.param.adc.ADC_Max[ADC_CHANNEL_IC] = ADC_I_MAX_A_DEFAULT*10;
MB_INTERNAL.param.adc.ADC_Max[ADC_CHANNEL_IA] = ADC_I_MAX_A_DEFAULT*10;
MB_INTERNAL.param.adc.ADC_Zero[ADC_CHANNEL_UBA] = ADC_U_ZERO_DEFAULT;
MB_INTERNAL.param.adc.ADC_Zero[ADC_CHANNEL_UAC] = ADC_U_ZERO_DEFAULT;
MB_INTERNAL.param.adc.ADC_Zero[ADC_CHANNEL_IC] = ADC_I_ZERO_DEFAULT;
MB_INTERNAL.param.adc.ADC_Zero[ADC_CHANNEL_IA] = ADC_I_ZERO_DEFAULT;
MB_INTERNAL.param.pwm.PhaseMask = 0x7; // (все три фазы)
MB_INTERNAL.param.pwm.Frequency = (float)PWM_THYR_FREQUENCY_HZ_DEFAULT;
MB_INTERNAL.param.pwm.PulseNumber = PWM_THYR_PULSE_NUMBER_DEFAULT;
MB_INTERNAL.param.zc.Hysteresis = ZERO_CROSS_HYSTERESIS_PERCENT_DEFAULT*100;
MB_INTERNAL.param.zc.DebouneCouner = ZERO_CROSS_DEBOUNCE_10US_DEFAULT;
//__AngleSetLimit();
}
}
static void __AngleSetLimit(void)
{
// Перерасчет максимально допустимого угла
float pulses_percent_of_period = ((MB_INTERNAL.param.pwm.PulseNumber / MB_INTERNAL.param.pwm.Frequency) * 1000) / ANGLE_PERIOD_MS(NOM_F_HZ_DEFAULT);
float angle_limit = 1 - pulses_percent_of_period;
Angle_SetLimit(&upp.hangle, angle_limit);
}
/**
* @brief Сверить и обновить float параметр из uint16_t.
* @param paramDist Указатель на float параметр
* @param paramSrc Значение для сравнения с float параметром
* @param Coef Коэффициент для приведения float к uint16_t: uint16_t = float*coef, float = uint16_t/coef
* @return 0 - параметры совпадают, 1 - параметр был обновлен на paramSrc.
*/
static int __CheckSimpleParamF(float *paramDist, uint16_t paramSrc, float Coef)
{
if(paramDist == NULL)
return 0;
uint16_t expected_mb_param = *paramDist*Coef;
if(expected_mb_param != paramSrc)
{
*paramDist = (float)paramSrc/Coef;
return 1;
}
else
{
return 0;
}
}
/**
* @brief Сверить и обновить uint32_t параметр из uint16_t.
* @param paramDist Указатель на uint32_t параметр
* @param paramSrc Значение для сравнения с uint32_t параметром
* @param Coef Коэффициент для приведения uint32_t к uint16_t: uint16_t = uint32_t*coef, uint32_t = uint16_t/coef
* @return 0 - параметры совпадают, 1 - параметр был обновлен на paramSrc.
*/
static int __CheckSimpleParamU32(uint32_t *paramDist, uint16_t paramSrc, float Coef)
{
if(paramDist == NULL)
return 0;
uint16_t expected_mb_param = *paramDist*Coef;
if(expected_mb_param != paramSrc)
{
*paramDist = (uint32_t)paramSrc/Coef;
return 1;
}
else
{
return 0;
}
}
/**
* @brief Сверить и обновить uint16_t параметр из uint16_t.
* @param paramDist Указатель на uint16_t параметр
* @param paramSrc Значение для сравнения с uint16_t параметром
* @return 0 - параметры совпадают, 1 - параметр был обновлен на paramSrc.
*/
static int __CheckSimpleParamU16(uint16_t *paramDist, uint16_t paramSrc)
{
if(paramDist == NULL)
return 0;
uint16_t expected_mb_param = *paramDist;
if(expected_mb_param != paramSrc)
{
*paramDist = (uint16_t)paramSrc;
return 1;
}
else
{
return 0;
}
}
/**
* @brief Сверить и обновить uint8_t параметр из uint16_t.
* @param paramDist Указатель на uint8_t параметр
* @param paramSrc Значение для сравнения с uint32_t параметром
* @param Coef Коэффициент для приведения uint32_t к uint16_t: uint16_t = uint8_t*coef, uint8_t = uint16_t/coef
* @return 0 - параметры совпадают, 1 - параметр был обновлен на paramSrc.
*/
static int __CheckSimpleParamU8(uint8_t *paramDist, uint16_t paramSrc, float Coef)
{
if(paramDist == NULL)
return 0;
uint16_t expected_mb_param = *paramDist*Coef;
if(expected_mb_param != paramSrc)
{
*paramDist = (uint8_t)paramSrc/Coef;
return 1;
}
else
{
return 0;
}
}

View File

@@ -1,85 +0,0 @@
/**
******************************************************************************
* @file upp_control.h
* @brief Модуль определябщий поведение УПП
******************************************************************************
* @details
******************************************************************************/
#ifndef _UPP_CONTROL_H
#define _UPP_CONTROL_H
#include "upp_defs.h"
typedef struct
{
unsigned set_default_pui:1; ///< Выставить настройки ПУИ в дефолтные
unsigned set_default_internal:1;///< Выставить внутренние настройки в дефолтные
unsigned go:1; ///< Запустить УПП
unsigned stop:1; ///< Выставить внутренние настройки в дефолтные
unsigned reserved:11;
unsigned reset_mcu:1;
}UPP_FuncCalls_t;
typedef struct
{
struct
{
uint16_t mean_alpha; ///< Коэф альфа x10000 для эксп. фильтра Umean, Imean, Fmean
}pm;
struct
{
uint16_t PhaseNumber; ///< Количество
uint16_t U; ///< В x 10
uint16_t U_deviation_plus; ///< Проценты x 100
uint16_t U_deviation_minus; ///< Проценты x 100
uint16_t F; ///< Гц x 100
uint16_t F_deviation_plus; ///< Проценты x 100
uint16_t F_deviation_minus; ///< Проценты x 100
uint16_t I; ///< Амперы x 10
}nominal;
/* Параметры АЦП */
struct
{
uint16_t ADC_Max[4]; ///< В x 10
uint16_t ADC_Zero[4]; ///< Кванты АЦП
}adc;
/* Параметры ШИМ */
struct
{
uint16_t PhaseMask; ///< Битовяя маска на какие фазы подавать ШИМ: 0 бит - a, 1 бит - b, 2 бит - c
uint16_t Frequency; ///< Частота ШИМ для пачки импульсов на тиристоры
uint16_t PulseNumber; ///< Количесво импульсов в пачке
}pwm;
/* Параметры Угла */
struct
{
uint16_t Hysteresis; ///< Гистерезис для определения перехода через ноль
uint16_t DebouneCouner; ///< Защита от дребезга: через сколько тактов снова начать фиксировать переход через ноль
}zc;
/* Параметры Угла */
struct
{
uint16_t Angle_Max; ///< Максимальный угол открытия тиристора
uint16_t Angle_Min; ///< Минимальный угол открытия тиристора
}angle;
}UPP_PrvtParams_t;
/* Контроль внутренних параметров УПП. */
void UPP_Control_InternalParams(void);
/* Установка параметров на дефолтные значения */
void UPP_SetDefault(int pui_default, int internal_default);
#endif //_UPP_CONTROL_H

View File

@@ -1,13 +1,199 @@
/**
******************************************************************************
* @file upp_errors.c
* @brief Ошибки УПП и их обработка
* @brief Формирование ошибок в ПУИ
******************************************************************************
* @details
Общая логика:
В программе выставляются всякие внутренние флаги ошибок: ERR_PRIVATE
В этом модуле смотрятся какие флаги выставились и переносят эти флаги
в структуру ошибок ПУИ ERR_PUI.
Исключение: Программные ошибки и ошибки питания плат,
они пишутся напрямую в ERR_PUI.
Также реализована защита от дребезга и в целом задержка на выставление ошибок.
******************************************************************************/
#include "upp_main.h" // УПП
#include "upp_errors.h" // всё остальное по работе с УПП
UPP_Errors_t errors;
static UPP_ErrorType_t UPP_SelectCommonError(void);
__STATIC_FORCEINLINE int setError(int condition, int flag, int *timer, int delay);
void UPP_Errors_Program(void);
void UPP_Errors_Power(void);
void UPP_Errors_Ranges(void);
void UPP_Errors_LossPhase(void);
void UPP_Errors_Other(void);
void UPP_Errors_Handle(void)
{
/*====== Программные ошибки ======*/
UPP_Errors_Program();
/*====== Ошибки питания плат ======*/
#ifndef UPP_DISABLE_PROTECT_BOARDPOWER
UPP_Errors_Power();
#endif
/*====== Ошибки выхода за допустимые пределы ======*/
UPP_Errors_Ranges();
/*====== Потери фазы ======*/
#ifndef UPP_DISABLE_PROTECT_LOSS_PHASE
UPP_Errors_LossPhase();
#endif
/*====== Остальные ======*/
UPP_Errors_Other();
errors.common = UPP_SelectCommonError();
}
void UPP_Errors_Program(void)
{
}
void UPP_Errors_Power(void)
{
static int error_latch_ticks = 0;
int error_latch_timeout = 5000;
/* Считывание неисправностей источников питания */
int err_24Vdio = GPIO_Read_Switch(&UPP_DIN.err_24Vdio);
int err_24V = GPIO_Read_Switch(&UPP_DIN.err_24V);
int err_5Vd = GPIO_Read_Switch(&UPP_DIN.err_5Vd);
int err_5Vsi = GPIO_Read_Switch(&UPP_DIN.err_5Vsi);
int err_Va = GPIO_Read_Switch(&UPP_DIN.err_Va);
ERR_PUI.Power_DIO_24V = err_24Vdio;
ERR_PUI.Power_24V = err_24V;
ERR_PUI.Power_Digit_5V = err_5Vd;
ERR_PUI.Power_SCI_5V = err_5Vsi;
ERR_PUI.Power_Analog_5V = err_Va;
}
void UPP_Errors_Ranges(void)
{
/* Преобразуем уставки в нормальные тики */
float ticksTiMax = u2f(PARAM_PUI.TiMax, 1)/PM_SLOW_PERIOD_US;
/* Счетчики для отсчитывания задержки выставления ошибки */
static int IMaxCnt = 0;
static int UMaxCnt = 0;
static int UMinCnt = 0;
static int FMaxCnt = 0;
static int FMinCnt = 0;
static int TMaxCnt = 0;
/* Напряжения */
ERR_PUI.OverVoltage = setError(ERR_PRIVATE.uamp_max,
ERR_PUI.OverVoltage,
&UMaxCnt,
MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_UAMP_ERR));
ERR_PUI.UnderVoltage = setError(ERR_PRIVATE.uamp_min,
ERR_PUI.UnderVoltage,
&UMinCnt,
MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_UAMP_ERR));
/* Токи */
int i_max = ( ERR_PRIVATE.iamp_max ||
ERR_PRIVATE.ia_max ||
ERR_PRIVATE.ib_max ||
ERR_PRIVATE.ic_max);
ERR_PUI.OverCurrent = setError(i_max,
ERR_PUI.OverCurrent,
&IMaxCnt,
ticksTiMax);
/* Частота */
int f_max = ( ERR_PRIVATE.fac_max ||
ERR_PRIVATE.fba_max ||
ERR_PRIVATE.fbc_max);
int f_min = ( ERR_PRIVATE.fac_min ||
ERR_PRIVATE.fba_min ||
ERR_PRIVATE.fbc_min);
ERR_PUI.OverFrequency = setError(f_max,
ERR_PUI.OverFrequency,
&FMaxCnt,
MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_F_ERR));
ERR_PUI.UnderFrequency = setError( f_min,
ERR_PUI.UnderFrequency,
&FMinCnt,
MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_F_ERR));
/* Температуры */
ERR_PUI.OverTemperature = setError(ERR_PRIVATE.temp_err,
ERR_PUI.OverTemperature,
&TMaxCnt,
MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_DEFAULT));
}
void UPP_Errors_LossPhase(void)
{
/* Счетчики для отсчитывания задержки выставления ошибки */
static int LossPhaseAllCnt = 0;
static int LossPhaseACnt = 0;
static int LossPhaseBCnt = 0;
static int LossPhaseCCnt = 0;
int loss_phases_all = ( ERR_PRIVATE.ia_min &&
ERR_PRIVATE.ib_min &&
ERR_PRIVATE.ic_min );
ERR_PUI.LossPhaseAll = setError( loss_phases_all,
ERR_PUI.LossPhaseAll,
&LossPhaseAllCnt,
MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_DEFAULT));
/* Если хотя бы одна фаза есть проверяем фазы отдельно */
if(!ERR_PUI.LossPhaseAll)
{
ERR_PUI.LossPhaseA = setError( ERR_PRIVATE.ia_min,
ERR_PUI.LossPhaseA,
&LossPhaseACnt,
MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_DEFAULT));
ERR_PUI.LossPhaseB = setError( ERR_PRIVATE.ib_min,
ERR_PUI.LossPhaseB,
&LossPhaseBCnt,
MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_DEFAULT));
ERR_PUI.LossPhaseC = setError( ERR_PRIVATE.ic_min,
ERR_PUI.LossPhaseC,
&LossPhaseCCnt,
MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_DEFAULT));
}
/* Если всех фаз нет, то отдельные не смотрим */
else
{
ERR_PUI.LossPhaseA = 0;
ERR_PUI.LossPhaseB = 0;
ERR_PUI.LossPhaseC = 0;
}
}
void UPP_Errors_Other(void)
{
static int InterlaceCnt = 0;
if(ERR_PRIVATE.longstart)
ERR_PUI.LongStart = 1;
else
ERR_PUI.LongStart = 0;
ERR_PUI.Interlace = setError(ERR_PRIVATE.interlance,
ERR_PUI.Interlace,
&InterlaceCnt,
MS_TO_SLOW_TICKS(ERRORS_DELAY_MS_DEFAULT));
//Interlance
}
static UPP_ErrorType_t UPP_SelectCommonError(void)
{
// Пока нет ошибки
@@ -38,7 +224,20 @@ static UPP_ErrorType_t UPP_SelectCommonError(void)
return best;
}
void UPP_ErrorsHandle(void)
__STATIC_FORCEINLINE int setError(int condition, int flag, int *timer, int delay)
{
errors.common = UPP_SelectCommonError();
if (condition) {
if (*timer < delay)
(*timer)++;
else
flag = 1;
} else {
if (*timer > 0)
(*timer)--;
else
flag = 0;
}
return flag;
}

View File

@@ -64,36 +64,37 @@ typedef struct
struct
{
/* Програмные ошибки */
unsigned Internal_1:1; ///< Внутренняя неисправность УПП 1
unsigned Internal_2:1; ///< Внутренняя неисправность УПП 2
unsigned Internal_3:1; ///< Внутренняя неисправность УПП 3
unsigned Internal_4:1; ///< Внутренняя неисправность УПП 4
unsigned Internal_5:1; ///< Внутренняя неисправность УПП 5
unsigned Internal_6:1; ///< Внутренняя неисправность УПП 6
unsigned Internal_1:1; ///< Ошибка 1: Внутренняя неисправность УПП 1
unsigned Internal_2:1; ///< Ошибка 2: Внутренняя неисправность УПП 2
unsigned Internal_3:1; ///< Ошибка 3: Внутренняя неисправность УПП 3
unsigned Internal_4:1; ///< Ошибка 4: Внутренняя неисправность УПП 4
unsigned Internal_5:1; ///< Ошибка 5: Внутренняя неисправность УПП 5
unsigned Internal_6:1; ///< Ошибка 6: Внутренняя неисправность УПП 6
/* Ошибки по питанию */
unsigned Power_Digit_5V:1; ///< Неисправность цифрового источника питания (5 В)
unsigned Power_24V:1; ///< Неисправность источника питания 24 В
unsigned Power_Analog_5V:1; ///< Неисправность аналогового источника питания микроконтроллера (± 5 В)
unsigned Power_SCI_5V:1; ///< Неисправность источника питания последовательных интерфейсов микроконтроллера (5 В)
unsigned Power_DIO_24V:1; ///< Неисправность источника питания дискретных входов/выходов (24 В)
unsigned Power_Digit_5V:1; ///< Ошибка 7: Неисправность цифрового источника питания (5 В)
unsigned Power_24V:1; ///< Ошибка 8: Неисправность источника питания 24 В
unsigned Power_Analog_5V:1; ///< Ошибка 9: Неисправность аналогового источника питания микроконтроллера (± 5 В)
unsigned Power_SCI_5V:1; ///< Ошибка 10: Неисправность источника питания последовательных интерфейсов микроконтроллера (5 В)
unsigned Power_DIO_24V:1; ///< Ошибка 11: Неисправность источника питания дискретных входов/выходов (24 В)
/* Ошибки по допустимым пределам Наряжений/Токов/Температуры */
unsigned OverCurrent:1; ///< Ток выше допустимого (см. Imax и TiMax в @ref UPP_PUI_Params_t)
unsigned OverVoltage:1; ///< Напряжение сети выше допустимого (см. Umах в @ref UPP_PUI_Params_t)
unsigned OverTemperature:1; ///< Температура выше допустимой (плюс 85 °C)
unsigned UnderVoltage:1; ///< Напряжение сети ниже допустимого (см. Umin в @ref UPP_PUI_Params_t)
unsigned OverCurrent:1; ///< Ошибка 12: Ток выше допустимого (см. Imax и TiMax в @ref UPP_PUI_Params_t)
unsigned OverVoltage:1; ///< Ошибка 13: Напряжение сети выше допустимого (см. Umах в @ref UPP_PUI_Params_t)
unsigned OverTemperature:1; ///< Ошибка 14: Температура выше допустимой (плюс 85 °C)
unsigned UnderVoltage:1; ///< Ошибка 15: Напряжение сети ниже допустимого (см. Umin в @ref UPP_PUI_Params_t)
/* Ошибки по обрывам фаз */
unsigned LossPhaseA:1; ///< Обрыв фазы A (см. Imin в @ref UPP_PUI_Params_t)
unsigned LossPhaseB:1; ///< Обрыв фазы B (см. Imin в @ref UPP_PUI_Params_t)
unsigned LossPhaseC:1; ///< Обрыв фазы C (см. Imin в @ref UPP_PUI_Params_t)
unsigned LossPhaseAll:1; ///< Ошибка 16: Обрыв трёх фаз (см. Imin в @ref UPP_PUI_Params_t)
unsigned LossPhaseA:1; ///< Ошибка 17: Обрыв фазы A (см. Imin в @ref UPP_PUI_Params_t)
unsigned LossPhaseB:1; ///< Ошибка 18: Обрыв фазы B (см. Imin в @ref UPP_PUI_Params_t)
unsigned LossPhaseC:1; ///< Ошибка 19: Обрыв фазы C (см. Imin в @ref UPP_PUI_Params_t)
/* Другие ошибки */
unsigned LongStart:1; ///< Затянутый пуск (ток не спадает за установленное время) (см. Tdelay в @ref UPP_PUI_Params_t)
unsigned Interlace:1; ///< Неправильный порядок чередования фаз (см. Interlace в @ref UPP_PUI_Params_t)
unsigned OverFrequency:1; ///< Частота сети выше допустимой
unsigned UnderFrequency:1; ///< Частота сети ниже допустимой
unsigned LongStart:1; ///< Ошибка 20: Затянутый пуск (ток не спадает за установленное время) (см. Tdelay в @ref UPP_PUI_Params_t)
unsigned Interlace:1; ///< Ошибка 21: Неправильный порядок чередования фаз (см. Interlace в @ref UPP_PUI_Params_t)
unsigned OverFrequency:1; ///< Ошибка 22: Частота сети выше допустимой
unsigned UnderFrequency:1; ///< Ошибка 23: Частота сети ниже допустимой
}err;
}pui;
@@ -104,22 +105,58 @@ typedef struct
uint64_t all;
struct
{
unsigned slow_calc_overrun:1;
unsigned uamp_max:1;
unsigned uamp_min:1;
unsigned iamp_max:1;
unsigned iamp_min:1;
unsigned ic_max:1;
unsigned ic_min:1;
unsigned ib_max:1;
unsigned ib_min:1;
unsigned ia_max:1;
unsigned ia_min:1;
unsigned uba_max:1;
unsigned uba_min:1;
unsigned uac_max:1;
unsigned uac_min:1;
unsigned ubc_max:1;
unsigned ubc_min:1;
unsigned fba_max:1;
unsigned fba_min:1;
unsigned fac_max:1;
unsigned fac_min:1;
unsigned fbc_max:1;
unsigned fbc_min:1;
unsigned temp_warn:1;
unsigned temp_err:1;
unsigned longstart:1;
unsigned interlance:3;
unsigned overrun_slow_calc:1;
unsigned overrun_fast_calc:1;
}err;
}f;
struct
{
uint16_t angle_reinit_err;
uint16_t adc_reinit_err;
uint16_t zc_reinit_err;
uint16_t pwm_reinit_err;
uint16_t slow_calc_overrun;
uint16_t overrun_slow_calc;
uint16_t overrun_fast_calc;
}cnt;
}prvt; ///< Приватные ошибки, не идущие напрямую в ПУИ
}UPP_Errors_t;
extern UPP_Errors_t errors;
void UPP_ErrorsHandle(void);
void UPP_Errors_Handle(void);
#endif //_UPP_ERRORS_H

143
UPP/Core/UPP/upp_io.c Normal file
View File

@@ -0,0 +1,143 @@
/**
******************************************************************************
* @file upp_io.c
* @brief Входы/выходы УПП
******************************************************************************
* @details
******************************************************************************/
#include "upp_io.h"
#include "main.h"
UPP_LEDs_t UPP_LEDS;
UPP_DiscreteInputs_t UPP_DIN;
UPP_DiscreteOutputs_t UPP_DO;
static void UPP_CEN_Write(int state);
static void UPP_RDO1_Write(int state);
static void UPP_RDO2_Write(int state);
static void UPP_RDO3_Write(int state);
static void UPP_RDO4_Write(int state);
static void UPP_DO1_Write(int state);
static void UPP_DO2_Write(int state);
static void UPP_DO3_Write(int state);
static void UPP_DO4_Write(int state);
static void UPP_DO5_Write(int state);
/**
* @brief Инициализация дискретных входов/выходов УПП
*/
void UPP_IO_Init(void)
{
/* Дискретные выходы */
UPP_DO.CEN = &UPP_CEN_Write;
UPP_DO.Ready = &UPP_RDO3_Write;
UPP_DO.Work = &UPP_RDO2_Write;
UPP_DO.Error = &UPP_RDO1_Write;
UPP_DO.RDO4 = &UPP_RDO4_Write;
UPP_DO.DO1 = &UPP_DO1_Write;
UPP_DO.DO2 = &UPP_DO2_Write;
UPP_DO.DO3 = &UPP_DO3_Write;
UPP_DO.DO4 = &UPP_DO4_Write;
UPP_DO.DO5 = &UPP_DO5_Write;
/* Дискретные входы */
GPIO_Switch_Init(&UPP_DIN.Pusk, DIN1_GPIO_Port, DIN1_Pin, 0);
UPP_DIN.Pusk.Sw_FilterDelay = 100;
GPIO_Switch_Init(&UPP_DIN.MestDist, DIN2_GPIO_Port, DIN2_Pin, 0);
UPP_DIN.MestDist.Sw_FilterDelay = 100;
GPIO_Switch_Init(&UPP_DIN.DIN3, DIN3_GPIO_Port, DIN3_Pin, 0);
GPIO_Switch_Init(&UPP_DIN.err_24Vdio, ERR_24VDIO_GPIO_Port, ERR_24VDIO_Pin, 1);
GPIO_Switch_Init(&UPP_DIN.err_24V, ERR_24V_GPIO_Port, ERR_24V_Pin, 1);
GPIO_Switch_Init(&UPP_DIN.err_5Vsi, ERR_5VSI_GPIO_Port, ERR_5VSI_Pin, 0);
/* Дискретные входы платы УМ */
GPIO_Switch_Init(&UPP_DIN.err_5Vd, UM_ERR_5VD_GPIO_Port, UM_ERR_5VD_Pin, 0);
GPIO_Switch_Init(&UPP_DIN.err_Va, UM_ERR_VA_GPIO_Port, UM_ERR_VA_Pin, 1);
/* Светодиоды платы УМ */
GPIO_LED_Init(&UPP_LEDS.green1, UM_LED_GREEN1_GPIO_Port, UM_LED_GREEN1_Pin, 0);
GPIO_LED_Init(&UPP_LEDS.green2, UM_LED_GREEN2_GPIO_Port, UM_LED_GREEN2_Pin, 0);
GPIO_LED_Init(&UPP_LEDS.red, UM_LED_RED_GPIO_Port, UM_LED_RED_Pin, 0);
/* Очищаем выходы */
UPP_DO.CEN(DISABLE);
UPP_DO.Error(DISABLE);
UPP_DO.Work(DISABLE);
UPP_DO.Ready(DISABLE);
UPP_DO.RDO4(DISABLE);
UPP_DO.DO1(DISABLE);
UPP_DO.DO2(DISABLE);
UPP_DO.DO3(DISABLE);
UPP_DO.DO4(DISABLE);
UPP_DO.DO5(DISABLE);
GPIO_LED_Off(&UPP_LEDS.green1);
GPIO_LED_Off(&UPP_LEDS.green2);
GPIO_LED_Off(&UPP_LEDS.red);
}
/**
* @brief Выставить направление UART1 (STM USART2)
*/
void UPP_UART1_SetDirection(int state)
{
HAL_GPIO_WritePin(SCIDE1_GPIO_Port, SCIDE1_Pin, state);
}
/**
* @brief Выставить направление UART2 (STM USART5)
*/
void UPP_UART2_SetDirection(int state)
{
HAL_GPIO_WritePin(SCIDE2_GPIO_Port, SCIDE2_Pin, state);
}
static void UPP_CEN_Write(int state)
{
#if !defined(STM32F417xx)
HAL_GPIO_WritePin(CEN_GPIO_Port, CEN_Pin, !state);
#else
HAL_GPIO_WritePin(CEN_GPIO_Port, CEN_Pin, state);
#endif
}
static void UPP_RDO1_Write(int state)
{
HAL_GPIO_WritePin(RDO1_GPIO_Port, RDO1_Pin, state);
}
static void UPP_RDO2_Write(int state)
{
HAL_GPIO_WritePin(RDO2_GPIO_Port, RDO2_Pin, state);
}
static void UPP_RDO3_Write(int state)
{
HAL_GPIO_WritePin(RDO3_GPIO_Port, RDO3_Pin, state);
}
static void UPP_RDO4_Write(int state)
{
HAL_GPIO_WritePin(RDO4_GPIO_Port, RDO4_Pin, state);
}
static void UPP_DO1_Write(int state)
{
HAL_GPIO_WritePin(DO1_GPIO_Port, DO1_Pin, state);
}
static void UPP_DO2_Write(int state)
{
HAL_GPIO_WritePin(DO2_GPIO_Port, DO2_Pin, state);
}
static void UPP_DO3_Write(int state)
{
HAL_GPIO_WritePin(DO3_GPIO_Port, DO3_Pin, state);
}
static void UPP_DO4_Write(int state)
{
HAL_GPIO_WritePin(DO4_GPIO_Port, DO4_Pin, state);
}
static void UPP_DO5_Write(int state)
{
HAL_GPIO_WritePin(DO5_GPIO_Port, DO5_Pin, state);
}

66
UPP/Core/UPP/upp_io.h Normal file
View File

@@ -0,0 +1,66 @@
/**
******************************************************************************
* @file upp_io.h
* @brief Входы/выходы УПП
******************************************************************************
* @details
******************************************************************************/
#ifndef _UPP_IO_H
#define _UPP_IO_H
#include "mylibs_include.h"
#define DIN_Pusk_GPIO_Port DIN1_GPIO_Port
#define DIN_Pusk_Pin DIN1_Pin
#define DIN_MestDist_GPIO_Port DIN2_GPIO_Port
#define DIN_MestDist_Pin DIN2_Pin
typedef struct
{
/* Отладочные светодиоды */
GPIO_LEDTypeDef green1;
GPIO_LEDTypeDef green2;
GPIO_LEDTypeDef red;
}UPP_LEDs_t;
extern UPP_LEDs_t UPP_LEDS;
typedef struct
{
GPIO_SwitchTypeDef Pusk; ///< Команда «ПУСК»
GPIO_SwitchTypeDef MestDist; ///< Мест/дист управление
GPIO_SwitchTypeDef DIN3; ///< Резерв
GPIO_SwitchTypeDef err_24Vdio;///< Сигнал ошибки источника питания цифровых входов/выходов
GPIO_SwitchTypeDef err_24V; ///< Сигнал ошибки основного источника питания 24В
GPIO_SwitchTypeDef err_5Vsi; ///< Сигнал ошибки источника питания цифровых интерфейсов
GPIO_SwitchTypeDef err_5Vd; ///< Вход сигнала неисправности цифрового источника питания 5В(+5Vd) микроконтроллера и микросхем ввода-вывода (5 В).
GPIO_SwitchTypeDef err_Va; ///< Вход обобщенного сигнала неисправности аналоговых источников питания +3,3В(+3Va), 5В(+5Vа), +15В(+15Va).
}UPP_DiscreteInputs_t;
extern UPP_DiscreteInputs_t UPP_DIN;
typedef struct
{
void (*CEN)(int state);
void (*Error)(int state);
void (*Work)(int state);
void (*Ready)(int state);
void (*RDO4)(int state);
void (*DO1)(int state);
void (*DO2)(int state);
void (*DO3)(int state);
void (*DO4)(int state);
void (*DO5)(int state);
}UPP_DiscreteOutputs_t;
extern UPP_DiscreteOutputs_t UPP_DO;
/* Инициализация дискретных входов/выходов УПП */
void UPP_IO_Init(void);
/* Выставить направление UART1 (STM USART2) */
void UPP_UART1_SetDirection(int state);
/* Выставить направление UART2 (STM USART5) */
void UPP_UART2_SetDirection(int state);
#endif //_UPP_IO_H

View File

@@ -1,36 +1,60 @@
/**
******************************************************************************
* @file upp_main.c
* @brief Инициализация и самые базовые вещи по работе УПП
* @brief Главный файл по работе УПП
******************************************************************************
* @details
Работа УПП состоит из нескольких модулей:
- @ref POWER_MONITOR - Считывание и фильтрация данных от АЦП и выставление защит по этим данным
- @ref PWM_THYRISTORS - Формирование пачки импульсов
- @ref ANGLE_CONTROL - Формирование и регулирование угла открытия тиристора
******************************************************************************/
#include "upp_main.h" // всё остальное по работе с УПП
#include "tim.h"
#include "iwdg.h"
UPP_t upp;
float alpha_dbg = 0.5;
// ОСНОВНОЙ ЦИКЛ main.c
HAL_StatusTypeDef res; // сюда сохраняется результат от выполения всяких функций
int dbg_polarity = 1;
/**
* @brief Инициализация УПП.
* @return 0 - если ОК, >1 если ошибка.
*/
int UPP_Init(void)
int UPP_App_Init(void)
{
upp.workmode = UPP_Init;
/* Очищаем входы */
UPP_IO_Init();
BenchTime_Init();
// Подключение указателей
upp.errors = &errors;
upp.PUI.params = &MB_DATA.HoldRegs.pui_params;
upp.PUI.params = &PARAM_PUI;
upp.PUI.values = &MB_DATA.InRegs.pui;
upp.call = &MB_INTERNAL.FuncCalls;
HAL_TIM_Base_Start(&ustim);
PowerMonitor_Init(&upp.pm);
PWM_Init(&upp.hpwm);
Angle_Init(&upp.hangle);
if(HAL_TIM_Base_Start(&ustim) != HAL_OK)
{
return 1;
}
if(PowerMonitor_Init(&upp.pm) != HAL_OK)
{
return 1;
}
if(PWM_Init(&upp.hpwm) != HAL_OK)
{
return 1;
}
if(Angle_Init(&upp.hangle) != HAL_OK)
{
return 1;
}
upp.workmode = WM_Ready;
if(UPP_Params_Init() != HAL_OK)
{
return 1;
}
return 0;
}
@@ -40,9 +64,16 @@ int UPP_Init(void)
*/
int UPP_PreWhile(void)
{
UPP_Control_InternalParams();
Angle_SetRange(&upp.hangle, 0.0, 0.8);
PowerMonitor_Start(&upp.pm);
UPP_Params_Control();
if(PowerMonitor_Start(&upp.pm) != HAL_OK)
return 1;
#ifdef MATLAB
dbg_polarity = 0;
#endif
UPP_DO.CEN(ENABLE);
return 0;
}
@@ -52,74 +83,157 @@ int UPP_PreWhile(void)
*/
int UPP_While(void)
{
// если ошибка вызываем СТОП
if(errors.pui.all)
PWM_SetPolarity(&upp.hpwm, dbg_polarity);
int retval = 0;
if(upp.pm.f.runSlow)
{
upp.call->stop = 1;
}
// иначе снимаем СТОП
static uint32_t slow_cnt = 0;
upp.Timings.slow_calc_prd_us = BenchTime_Period(BT_SLOWCALC_PRD, angletim.Instance->CNT, HAL_MAX_DELAY)/ANGLE_TIM2_FREQ_MHZ;
BenchTime_Start(BT_SLOWCALC, angletim.Instance->CNT, HAL_MAX_DELAY);
res = HAL_IWDG_Refresh(&hiwdg); // если не вызываются медленные расчеты - что-то не то сбрасываемся по watchdog
// Медленные расчеты
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;
// При симуляции тока не включаем его проверку
razgon_done = 0;
#endif
// Защиты // Определенные защиты по току включаем только после разгона
PowerMonitor_Protect(&upp.pm, razgon_done);
// Обрабока ошибок и выставление итоговой Ошибки
UPP_Errors_Handle();
// Контроль парамеров
UPP_Params_Control();
#ifndef UPP_DISABLE_ERROR_BLOCK
// если ошибка вызываем СТОП
upp.call->stop = (errors.common != Err_None);
#endif
// Сброс на дефолтные по запросу
if(upp.call->set_default_pui || upp.call->set_default_internal)
{
UPP_Params_SetDefault(upp.call->set_default_pui, upp.call->set_default_internal);
upp.call->set_default_pui = 0;
upp.call->set_default_internal = 0;
}
// Если СТОП - переходим в ошибку
if (upp.call->stop)
upp.workmode = UPP_Error;
// Автомат состояний УПП
switch(upp.workmode)
{
/*======= Состояние Инициализация =========*/
case UPP_Init:
res = PWM_Stop(&upp.hpwm, 0, 1); // Останавливаем ВЕСЬ ШИМ
// Индикация
UPP_DO.Ready(DISABLE);
UPP_DO.Work(DISABLE);
UPP_DO.Error(DISABLE);
if(slow_cnt == 0)
{ // начало инициализации - сбрасываем флаги
memset(&ERR_PRIVATE, 0, sizeof(ERR_PRIVATE));
memset(&ERR_PUI, 0, sizeof(ERR_PUI));
}
if(slow_cnt > MS_TO_SLOW_TICKS(UPP_INIT_BEFORE_READY_MS))
{ // конец инициализации - сбрасываем чтобы потом еще раз инициализироватся
slow_cnt = 0;
upp.workmode = UPP_Ready;
}
else
{ // инициализация в процессе
slow_cnt++;
}
break;
/*======= Состояние Готовность =========*/
case UPP_Ready:
res = PWM_Stop(&upp.hpwm, 0, 1); // Останавливаем ВЕСЬ ШИМ
// Индикация
UPP_DO.Ready(ENABLE);
UPP_DO.Work(DISABLE);
UPP_DO.Error(DISABLE);
// если пришла команда на запуск
if (upp.call->go)
{
upp.workmode = UPP_Work;
Angle_PID_Reset(&upp.hangle);
upp.StartTick = local_time();
}
break;
/*======= Состояние В работе =========*/
case UPP_Work:
// Разрешаем выход ШИМ
__HAL_TIM_MOE_ENABLE(&hpwm1);
__HAL_TIM_MOE_ENABLE(&hpwm2);
// Индикация
UPP_DO.Ready(DISABLE);
UPP_DO.Work(ENABLE);
UPP_DO.Error(DISABLE);
// если пришла команда на остановку
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, Correction);
// если слишком долгий запуск
if((local_time() - upp.StartTick) > (upp.PUI.params->Tdelay*1000))
{
ERR_PRIVATE.longstart = 1;
}
break;
/*======= Состояние Работа завершена =========*/
// case WM_Done:
// // Индикация
// UPP_DO.Ready(DISABLE);
// UPP_DO.Work(DISABLE);
// UPP_DO.Error(DISABLE);
// PWM_Stop(&upp.hpwm, 0, 1); // Останавливаем ВЕСЬ ШИМ
// break;
/*======= Состояние Ошибка/Неизвестно =========*/
case UPP_Error:
default:
PWM_Stop(&upp.hpwm, 0, 1); // Останавливаем ВЕСЬ ШИМ
// Индикация
UPP_DO.Ready(DISABLE);
UPP_DO.Work(DISABLE);
UPP_DO.Error(ENABLE);
// Находимся до тех пор пока ошибки не будет устранена
if(errors.common == Err_None)
upp.workmode = UPP_Init;
retval = 1;
break;
}
upp.pm.f.runSlow = 0;
upp.Timings.slow_calc_us = BenchTime_End(BT_SLOWCALC, angletim.Instance->CNT)/ANGLE_TIM2_FREQ_MHZ;
}//if(upp.pm.f.runSlow)
else
{
upp.call->stop = 0;
}
// Сброс на дефолтные по запросу
if(upp.call->set_default_pui)
{
UPP_SetDefault(1, 0);
}
if(upp.call->set_default_internal)
{
UPP_SetDefault(0, 1);
}
PowerMonitor_SlowHandle(&upp.pm);
// Если СТОП - переходим в ошибку
if (upp.call->stop)
upp.workmode = WM_Error;
// Автомат состояний УПП
switch(upp.workmode)
{
case WM_Ready:
// если пришла команда на запуск
if (upp.call->go)
{
upp.workmode = WM_Running;
upp.StartTick = local_time();
}
break;
case WM_Running:
// если пришла команда на остановку
if (!upp.call->go)
upp.workmode = WM_Ready;
// если слишком долгий запуск
if((local_time() - upp.StartTick) > (upp.PUI.params->Tdelay*1000))
{
errors.pui.err.LongStart = 1;
}
break;
case WM_Error:
if(errors.common == Err_None)
upp.workmode = WM_Ready;
PWM_Stop(&upp.hpwm, 0, 1); // Останавливаем ВЕСЬ ШИМ
break;
case WM_Done:
PWM_Stop(&upp.hpwm, 0, 1); // Останавливаем ВЕСЬ ШИМ
break;
default:
break;
}
return 0;
return retval;
}
@@ -129,21 +243,42 @@ int UPP_While(void)
*/
void UPP_Tick(void)
{
if(upp.workmode == WM_Not_Init)
// Начинаем все проверять только после того как уйдем из режима инициализации
if(upp.workmode == UPP_Init)
return;
UPP_ErrorsHandle();
UPP_Control_InternalParams();
if(GPIO_Read_Switch(&UPP_DIN.Pusk))
{
upp.call->go = 1;
}
else
{
upp.call->go = 0;
}
}
// ПРЕРЫВАНИЯ stm32f4xx_it.c
//================ ПРЕРЫВАНИЯ stm32f4xx_it.c ===================
/**
* @brief @ref DMA2_Stream0_IRQHandler
*/
void UPP_ADC_Handle(void)
{
upp.Timings.isr_adc_prd_us = BenchTime_Period(BT_ADC_PRD, angletim.Instance->CNT, HAL_MAX_DELAY)/ANGLE_TIM2_FREQ_MHZ;
BenchTime_Start(BT_ADC, angletim.Instance->CNT, HAL_MAX_DELAY);
PowerMonitor_Handle(&upp.pm);
if(upp.pm.f.inIsr)
{
ERR_PRIVATE.overrun_fast_calc = 1;
ERR_PRIVATE_CNT.overrun_fast_calc++;
return;
}
upp.pm.f.inIsr = 1;
PowerMonitor_FastCalc(&upp.pm);
for(int phase = 0; phase < 3; phase++)
{
@@ -151,58 +286,72 @@ void UPP_ADC_Handle(void)
if(ZC_isOccurred(&upp.pm.zc, phase))
{
// Если УПП в работе
if(upp.workmode == WM_Running)
if(upp.workmode == UPP_Work)
{
// Меняем полуволну тиристора
UPP_HalfWave_t curr_halfwave = ZC_GetHalfWave(&upp.pm.zc, phase);
PWM_SetHalfWave(&upp.hpwm, phase, curr_halfwave);
res = PWM_SetHalfWave(&upp.hpwm, phase, curr_halfwave);
// Начинаем отсчитывать угол
Angle_Start(&upp.hangle, phase, alpha_dbg, 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 = BenchTime_End(BT_ADC, angletim.Instance->CNT);
}
void UPP_PWM_Handle(void)
{
BenchTime_Start(BT_PWM, angletim.Instance->CNT, HAL_MAX_DELAY);
PWM_Handle(&upp.hpwm);
upp.Timings.isr_pwm = BenchTime_End(BT_PWM, angletim.Instance->CNT);
upp.Timings.isr_adc_us = BenchTime_End(BT_ADC, angletim.Instance->CNT)/ANGLE_TIM2_FREQ_MHZ;
upp.pm.f.inIsr = 0;
}
/**
* @brief @ref HAL_TIM_OC_DelayElapsedCallback
*/
void UPP_Angle_Handle(void)
{
UPP_Phase_t phase = Angle_Handle(&upp.hangle);
Angle_Reset(&upp.hangle, phase);
res = Angle_Reset(&upp.hangle, phase);
// Если УПП в работе
if(upp.workmode == WM_Running)
if(upp.workmode == UPP_Work)
{
switch(phase)
{
case UPP_PHASE_A:
PWM_Start(&upp.hpwm, UPP_PHASE_A);
res = PWM_Start(&upp.hpwm, UPP_PHASE_A);
break;
case UPP_PHASE_B:
PWM_Start(&upp.hpwm, UPP_PHASE_B);
res = PWM_Start(&upp.hpwm, UPP_PHASE_B);
break;
case UPP_PHASE_C:
PWM_Start(&upp.hpwm, UPP_PHASE_C);
res = PWM_Start(&upp.hpwm, UPP_PHASE_C);
break;
default:
break;
}
if(res != HAL_OK)
__NOP();
}
}
/**
* @brief Callback по совпадению CCRx c CNT
*/
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef* htim)
{
if (htim == upp.hangle.htim)
{
BenchTime_Start(BT_SYSTICK, angletim.Instance->CNT, HAL_MAX_DELAY);
UPP_Angle_Handle();
upp.Timings.isr_systick = BenchTime_End(BT_SYSTICK, angletim.Instance->CNT);
}
}
/**
* @brief @ref HAL_TIM_PeriodElapsedCallback
*/
void HAL_IncTick(void)
{
BenchTime_Start(BT_SYSTICK, angletim.Instance->CNT, HAL_MAX_DELAY);
uwTick += uwTickFreq;
UPP_Tick();
upp.Timings.isr_systick_us = BenchTime_End(BT_SYSTICK, angletim.Instance->CNT)/ANGLE_TIM2_FREQ_MHZ;
}

View File

@@ -1,7 +1,7 @@
/**
******************************************************************************
* @file upp_main.h
* @brief Определения структур данных Modbus устройства
* @brief Определения структур для работы УПП
******************************************************************************
* @details
******************************************************************************/
@@ -15,9 +15,7 @@
#include "angle_control.h" // Управление углом открытия
#include "upp_status.h" // статус упп
#include "upp_control.h" // управление упп
extern float alpha_dbg;
#include "upp_params.h" // управление упп
typedef struct
@@ -41,16 +39,19 @@ typedef struct
struct
{
uint32_t isr_adc;
uint32_t isr_pwm;
uint32_t isr_systick;
uint32_t slow_calc_prd_us;
uint32_t slow_calc_us;
uint32_t isr_adc_prd_us;
uint32_t isr_adc_us;
uint32_t isr_pwm_us;
uint32_t isr_systick_us;
}Timings;
}UPP_t;
extern UPP_t upp;
/* Инициализация УПП */
int UPP_Init(void);
int UPP_App_Init(void);
/* Инициализация основного цикла УПП. */
int UPP_PreWhile(void);
/* Основной цикл УПП. */

578
UPP/Core/UPP/upp_params.c Normal file
View File

@@ -0,0 +1,578 @@
/**
******************************************************************************
* @file upp_params.c
* @brief Модуль проверяющий параметры УПП
******************************************************************************
* @details
* ИНСТРУКЦИЯ ПО ДОБАВЛЕНИЮ НОВЫХ ПАРАМЕТРОВ:
*
* 1. Добавить новый параметр в соответствующую структуру в файле параметров:
* - PARAM_PUI для параметров от ПУИ (пульт управления и индикации)
* - PARAM_INTERNAL для внутренних параметров УПП
*
* 2. В функции UPP_Params_ControlInternal() или UPP_Params_ControlPUI():
* a. Объявить временную переменную для хранения текущего значения
* b. Проверить изменение параметра (можно с помощью __CheckParamX() функции)
* c. Обновить параметр в соответствующем модуле новым параметром если он изменился
*
* 3. В функции UPP_Params_SetDefault() добавить установку значения по умолчанию
*
* 4. При необходимости добавить сатурацию параметра в UPP_Params_Saturate()
*
* Пример добавления простого параметра:
*
* // В UPP_Params_ControlInternal():
* float new_param = module.param;
* if(__CheckParamF(&new_param, PARAM_INTERNAL.new_param, 1000))
* {
* module_update = 1;
* }
*
* // В блоке обновления модуля:
* if(module_update)
* {
* if(Module_SetParam(&module, new_param) == HAL_OK)
* module_update = 0;
* }
*
* // В UPP_Params_SetDefault():
* PARAM_INTERNAL.new_param = NEW_PARAM_DEFAULT * 1000;
*
******************************************************************************
*/
#include "upp_main.h" // всё остальное по работе с УПП
#define SATURATE_U16(value, min, max) \
value = ((value) < (min) ? (min) : ((value) > (max) ? (max) : (value)))
static int __CheckParamF(float *paramDist, uint16_t paramSrc, float Coef);
static int __CheckParamU32(uint32_t *paramDist, uint16_t paramSrc, float Coef);
static int __CheckParamU16(uint16_t *paramDist, uint16_t paramSrc);
static int __CheckParamU8(uint8_t *paramDist, uint16_t paramSrc, float Coef);
static void __AngleSetLimit(void);
/**
* @brief Контроль параметров УПП.
* @return HAL Status.
*/
void UPP_Params_Control(void)
{
/* Проверяем параметры на корректный диапазон */
UPP_Params_Saturate();
/* Чекаем изменились ли параметры от ПУИ */
UPP_Params_ControlPUI();
/* Чекаем изменились ли внутренние параметры */
UPP_Params_ControlInternal();
}
/**
* @brief Контроль параметров от ПУИ.
* @return HAL Status.
*/
void UPP_Params_ControlPUI(void)
{
if(upp.call->go) // при запущеном УПП ничего не меняем
return;
/* Tnt - Уставка на скорость нарастания пускового тока */
float angle_ref_alphaPUI = PUI_Tnt_CalcAlpha(u2f(PARAM_PUI.Tnt, 1000), PM_SLOW_PERIOD_US);
float angle_ref_alpha = upp.hangle.refFilter.alpha;
if(angle_ref_alpha != angle_ref_alphaPUI)
{
angle_ref_alpha = angle_ref_alphaPUI;
if(Angle_PID_Init(&upp.hangle,
upp.hangle.pid.Kp,
upp.hangle.pid.Ki,
upp.hangle.pid.Kd,
angle_ref_alpha) != HAL_OK)
ERR_PRIVATE_CNT.angle_reinit_err++;
}
}
/**
* @brief Контроль внутренних параметров УПП.
* @return HAL Status.
*/
void UPP_Params_ControlInternal(void)
{
if(upp.call->go) // при запущеном УПП ничего не меняем
return;
// флаги обновились ли конфиги
static int alpha_update = 0;
static int adc_channel_update[ADC_NUMB_OF_REGULAR_CHANNELS] = {0};
static int zc_update = 0;
static int pwm_update = 0;
// временная переменная для параметров Мониторинга сети
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)PM_SLOW_PERIOD_US/1000000);
float angle_pid_kd = upp.hangle.pid.Kd;
// временная переменная для параметров каналов АЦП
float adc_channel_max[ADC_NUMB_OF_REGULAR_CHANNELS] = {0};
uint16_t adc_channel_zero[ADC_NUMB_OF_REGULAR_CHANNELS] = {0};
// временная переменная для параметров перехода через ноль
float zc_hysteresis = upp.pm.zc.Config.Hysteresis;
uint16_t zc_debounce = upp.pm.zc.Config.DebounceSamples;
// временная переменная для параметров ШИМ
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;
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;
// Параметры регулятора Угла открытия
if(__CheckParamF(&angle_max, PARAM_INTERNAL.angle.Angle_Max, 65535))
{
alpha_update = 1;
}
if(__CheckParamF(&angle_min, PARAM_INTERNAL.angle.Angle_Min, 65535))
{
alpha_update = 1;
}
if(__CheckParamF(&angle_pid_kp, PARAM_INTERNAL.angle.PID_Kp, 10000))
{
alpha_update = 1;
}
if(__CheckParamF(&angle_pid_ki, PARAM_INTERNAL.angle.PID_Ki, 10000))
{
alpha_update = 1;
}
if(__CheckParamF(&angle_pid_kd, PARAM_INTERNAL.angle.PID_Kd, 10000))
{
alpha_update = 1;
}
// Параметры АЦП
for(int i = 0; i < ADC_NUMB_OF_REGULAR_CHANNELS; i++)
{
adc_channel_max[i] = upp.pm.adc.Coefs[i].vMax;
adc_channel_zero[i] = upp.pm.adc.Coefs[i].lZero;
// Максимальное измеряемое напряжение
if(__CheckParamF(&adc_channel_max[i], PARAM_INTERNAL.adc.ADC_Max[i], 10))
{
adc_channel_update[i] = 1;
}
// Значение АЦП при нулевом входе
if(__CheckParamU16(&adc_channel_zero[i], PARAM_INTERNAL.adc.ADC_Zero[i]))
{
adc_channel_update[i] = 1;
}
}
// Параметры алгоритма перехода через ноль
if(__CheckParamF(&zc_hysteresis, PARAM_INTERNAL.zc.Hysteresis, 10000))
{
zc_update = 1;
}
if(__CheckParamU16(&zc_debounce, PARAM_INTERNAL.zc.DebouneCouner))
{
zc_update = 1;
}
// Параметры ШИМ
if(__CheckParamU8(&pwm_phase_mask, PARAM_INTERNAL.pwm.PhaseMask, 1))
{
pwm_update = 1;
}
if(__CheckParamU16(&pwm_freq, PARAM_INTERNAL.pwm.Frequency))
{
pwm_update = 1;
}
if(__CheckParamF(&pwm_duty, PARAM_INTERNAL.pwm.Duty, 100))
{
pwm_update = 1;
}
if(__CheckParamF(&pwm_pulse_len, PARAM_INTERNAL.pwm.PulseLength, 65535))
{
pwm_update = 1;
}
// Параметры мониторинга
if(__CheckParamU16(&pm_rms_widnow_size, PARAM_INTERNAL.pm.rms_window_size))
{
for(int i = 0; i < RMS_ALL; i++)
{
Filter_ReInit(&upp.pm.rms[i], pm_rms_widnow_size);
Filter_Start(&upp.pm.rms[i]);
}
}
if(__CheckParamF(&pm_rms_exp_alpha, PARAM_INTERNAL.pm.rms_exp_alpha, 65535))
{
for(int i = 0; i < RMS_EXP_ALL; i++)
{
Filter_ReInit(&upp.pm.rms_exp[i], pm_rms_exp_alpha);
Filter_Start(&upp.pm.rms_exp[i]);
}
}
// Обновление регулятора угла открытия
__AngleSetLimit();
if(alpha_update)
{
if(Angle_SetRange(&upp.hangle, angle_min, angle_max) == 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;
}
else
ERR_PRIVATE_CNT.angle_reinit_err++;
}
else
ERR_PRIVATE_CNT.angle_reinit_err++;
}
// Обновление АЦП конфигов
for(int i = 0; i < ADC_NUMB_OF_REGULAR_CHANNELS; i++)
{
if(adc_channel_update[i])
{
if(ADC_ConfigChannel(&upp.pm.adc, i, adc_channel_zero[i], adc_channel_max[i], 4095) == HAL_OK)
adc_channel_update[i] = 0;
else
ERR_PRIVATE_CNT.adc_reinit_err++;
}
}
// Обновление Zero-Cross конфигов
if(zc_update)
{
if(ZC_Init(&upp.pm.zc, upp.pm.zc.Config.NumChannels, zc_hysteresis, zc_debounce) == HAL_OK)
zc_update = 0;
else
ERR_PRIVATE_CNT.zc_reinit_err++;
}
// Обновление ШИМ конфигов
if(pwm_update)
{
if(PWM_SetConfig(&upp.hpwm, pwm_phase_mask, pwm_freq, pwm_duty, pwm_pulse_len) == HAL_OK)
{
pwm_update = 0;
}
else
ERR_PRIVATE_CNT.pwm_reinit_err++;
}
}
/**
* @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.
*/
void UPP_Params_Saturate(void)
{
SATURATE_U16(PARAM_PUI.Iref, 100, 500);
// SATURATE_U16(PARAM_PUI.Tnt, 50, 5000);
SATURATE_U16(PARAM_PUI.Umin, 5, 99);
SATURATE_U16(PARAM_PUI.Umax, 100, 120);
SATURATE_U16(PARAM_PUI.Imax, 5, 99);
SATURATE_U16(PARAM_PUI.Imin, 0, 40);
SATURATE_U16(PARAM_PUI.TiMax, 500, 10000);
SATURATE_U16(PARAM_PUI.Tdelay, 5, 60);
SATURATE_U16(PARAM_PUI.Interlace, 0, 1);
SATURATE_U16(PARAM_INTERNAL.setpoints.TemperatureWarn, 0, 90);
SATURATE_U16(PARAM_INTERNAL.setpoints.TemperatureErr, 0, 90);
SATURATE_U16(PARAM_INTERNAL.nominal.PhaseNumber, 0, 3);
SATURATE_U16(PARAM_INTERNAL.nominal.U, 0, ADC_U_MAX_V_DEFAULT*10);
SATURATE_U16(PARAM_INTERNAL.nominal.U_deviation_plus, 0, 100*100);
SATURATE_U16(PARAM_INTERNAL.nominal.U_deviation_minus, 0, 100*100);
SATURATE_U16(PARAM_INTERNAL.nominal.F, 40*100, 60*100);
SATURATE_U16(PARAM_INTERNAL.nominal.F_deviation_plus, 0, 100*100);
SATURATE_U16(PARAM_INTERNAL.nominal.F_deviation_minus, 0, 100*100);
SATURATE_U16(PARAM_INTERNAL.nominal.I, 0, ADC_I_MAX_A_DEFAULT*10);
SATURATE_U16(PARAM_INTERNAL.adc.ADC_Max[ADC_CHANNEL_UBA], 0, 5000*10);
SATURATE_U16(PARAM_INTERNAL.adc.ADC_Max[ADC_CHANNEL_UAC], 0, 5000*10);
SATURATE_U16(PARAM_INTERNAL.adc.ADC_Max[ADC_CHANNEL_IC], 0, 1000*10);
SATURATE_U16(PARAM_INTERNAL.adc.ADC_Max[ADC_CHANNEL_IA], 0, 1000*10);
SATURATE_U16(PARAM_INTERNAL.adc.ADC_Zero[ADC_CHANNEL_UBA], 1848, 2248);
SATURATE_U16(PARAM_INTERNAL.adc.ADC_Zero[ADC_CHANNEL_UAC], 1848, 2248);
SATURATE_U16(PARAM_INTERNAL.adc.ADC_Zero[ADC_CHANNEL_IC], 1848, 2248);
SATURATE_U16(PARAM_INTERNAL.adc.ADC_Zero[ADC_CHANNEL_IA], 1848, 2248);
SATURATE_U16(PARAM_INTERNAL.pwm.PhaseMask, 0, 7);
SATURATE_U16(PARAM_INTERNAL.pwm.Frequency, 1000, 40000);
SATURATE_U16(PARAM_INTERNAL.zc.Hysteresis, 0, 0.1*100);
SATURATE_U16(PARAM_INTERNAL.zc.DebouneCouner, 0, 1000);
SATURATE_U16(PARAM_INTERNAL.angle.PulseLengthReserve, 50, 1000);
}
/**
* @brief Установка параметров на дефолтные значения @ref UPP_PARAMS_DEFAULT.
* @param pui_default Сбросить параметры ПУИ
* @param internal_default Сбросить внутренние параметры
* @return HAL Status.
*/
void UPP_Params_SetDefault(int pui_default, int internal_default)
{
if(pui_default)
{
PARAM_PUI.Iref = PUI_Iref_PERCENT_DEFAULT*100;
PARAM_PUI.Tnt = PUI_Tnt_MS_DEFAULT;
PARAM_PUI.Umin = PUI_Umin_PERCENT_DEFAULT*100;
PARAM_PUI.Umax = PUI_Umax_PERCENT_DEFAULT*100;
PARAM_PUI.Imax = PUI_Imax_PERCENT_DEFAULT*100;
PARAM_PUI.Imin = PUI_Imin_PERCENT_DEFAULT*100;
PARAM_PUI.TiMax = PUI_TiMax_US_DEFAULT;
PARAM_PUI.Tdelay = PUI_Tdelay_SECONDS_DEFAULT;
PARAM_PUI.Interlace = PUI_Interlace_EN_DEFAULT;
}
if(internal_default)
{
PARAM_INTERNAL.setpoints.TemperatureWarn = SETPOINT_TEMP_WARN*10;
PARAM_INTERNAL.setpoints.TemperatureErr = SETPOINT_TEMP_ERR*10;
PARAM_INTERNAL.nominal.PhaseNumber = NOM_PHASE_NUMB;
PARAM_INTERNAL.nominal.U = NOM_U_V_DEFAULT*10;
PARAM_INTERNAL.nominal.U_deviation_plus = NOM_U_DEVIATION_PLUS_PERCENT_DEFAULT*100;
PARAM_INTERNAL.nominal.U_deviation_minus = NOM_U_DEVIATION_MINUS_PERCENT_DEFAULT*100;
PARAM_INTERNAL.nominal.F = NOM_F_HZ_DEFAULT*100;
PARAM_INTERNAL.nominal.F_deviation_plus = NOM_F_DEVIATION_PLUS_PERCENT_DEFAULT*100;
PARAM_INTERNAL.nominal.F_deviation_minus = NOM_F_DEVIATION_MINUS_PERCENT_DEFAULT*100;
PARAM_INTERNAL.nominal.I = NOM_I_A_DEFAULT*10;
PARAM_INTERNAL.pm.rms_window_size = PM_RMS_WINDOW_PERIOD_US_DEFAULT/PM_SLOW_PERIOD_US;
PARAM_INTERNAL.pm.rms_exp_alpha = FilterExp_CalcAlpha98(PM_RMS_EXT_TAU_US_DEFAULT, PM_SLOW_PERIOD_US)*65535;
PARAM_INTERNAL.adc.ADC_Max[ADC_CHANNEL_UBA] = ADC_U_MAX_V_DEFAULT*10;
PARAM_INTERNAL.adc.ADC_Max[ADC_CHANNEL_UAC] = ADC_U_MAX_V_DEFAULT*10;
PARAM_INTERNAL.adc.ADC_Max[ADC_CHANNEL_IC] = ADC_I_MAX_A_DEFAULT*10;
PARAM_INTERNAL.adc.ADC_Max[ADC_CHANNEL_IA] = ADC_I_MAX_A_DEFAULT*10;
PARAM_INTERNAL.adc.ADC_Zero[ADC_CHANNEL_UBA] = ADC_U_ZERO_DEFAULT;
PARAM_INTERNAL.adc.ADC_Zero[ADC_CHANNEL_UAC] = ADC_U_ZERO_DEFAULT;
PARAM_INTERNAL.adc.ADC_Zero[ADC_CHANNEL_IC] = ADC_I_ZERO_DEFAULT;
PARAM_INTERNAL.adc.ADC_Zero[ADC_CHANNEL_IA] = ADC_I_ZERO_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.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;
PARAM_INTERNAL.angle.PID_Kp = ANGLE_PID_KP_COEF_DEFAULT*10000;
PARAM_INTERNAL.angle.PID_Ki = ANGLE_PID_KI_COEF_DEFAULT*10000;
PARAM_INTERNAL.angle.PID_Kd = ANGLE_PID_KD_COEF_DEFAULT*10000;
PARAM_INTERNAL.angle.Angle_Max = ANGLE_MAX_PERCENT_DEFAULT*65535;
PARAM_INTERNAL.angle.Angle_Min = ANGLE_MIN_PERCENT_DEFAULT*65535;
PARAM_INTERNAL.angle.PulseLengthReserve = ANGLE_PULSE_LENGTH_RESERVE_PERCENT_DEFAULT*100;
//__AngleSetLimit();
}
}
#define ANGLE_PERIOD_MS(_freq_) (((float)1/(_freq_*2))*1000)
// Перерасчет максимально допустимого угла
static void __AngleSetLimit(void)
{ // Сколько пачка ипульсов занимает процентов от всего периода
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;
angle_limit -= pulses_percent_of_period*u2f(PARAM_INTERNAL.angle.PulseLengthReserve, 100); // добавляем запас в PulseLengthReserve процентов от пачки импульсов
Angle_SetLimit(&upp.hangle, angle_limit);
}
/**
* @brief Сверить и обновить float параметр из uint16_t.
* @param paramDist Указатель на float параметр
* @param paramSrc Значение для сравнения с float параметром
* @param Coef Коэффициент для приведения float к uint16_t: uint16_t = float*coef, float = uint16_t/coef
* @return 0 - параметры совпадают, 1 - параметр был обновлен на paramSrc.
*/
static int __CheckParamF(float *paramDist, uint16_t paramSrc, float Coef)
{
if(paramDist == NULL)
return 0;
uint16_t expected_mb_param = *paramDist*Coef;
if(expected_mb_param != paramSrc)
{
*paramDist = (float)paramSrc/Coef;
return 1;
}
else
{
return 0;
}
}
/**
* @brief Сверить и обновить uint32_t параметр из uint16_t.
* @param paramDist Указатель на uint32_t параметр
* @param paramSrc Значение для сравнения с uint32_t параметром
* @param Coef Коэффициент для приведения uint32_t к uint16_t: uint16_t = uint32_t*coef, uint32_t = uint16_t/coef
* @return 0 - параметры совпадают, 1 - параметр был обновлен на paramSrc.
*/
static int __CheckParamU32(uint32_t *paramDist, uint16_t paramSrc, float Coef)
{
if(paramDist == NULL)
return 0;
uint16_t expected_mb_param = *paramDist*Coef;
if(expected_mb_param != paramSrc)
{
*paramDist = (uint32_t)paramSrc/Coef;
return 1;
}
else
{
return 0;
}
}
/**
* @brief Сверить и обновить uint16_t параметр из uint16_t.
* @param paramDist Указатель на uint16_t параметр
* @param paramSrc Значение для сравнения с uint16_t параметром
* @return 0 - параметры совпадают, 1 - параметр был обновлен на paramSrc.
*/
static int __CheckParamU16(uint16_t *paramDist, uint16_t paramSrc)
{
if(paramDist == NULL)
return 0;
uint16_t expected_mb_param = *paramDist;
if(expected_mb_param != paramSrc)
{
*paramDist = (uint16_t)paramSrc;
return 1;
}
else
{
return 0;
}
}
/**
* @brief Сверить и обновить uint8_t параметр из uint16_t.
* @param paramDist Указатель на uint8_t параметр
* @param paramSrc Значение для сравнения с uint32_t параметром
* @param Coef Коэффициент для приведения uint32_t к uint16_t: uint16_t = uint8_t*coef, uint8_t = uint16_t/coef
* @return 0 - параметры совпадают, 1 - параметр был обновлен на paramSrc.
*/
static int __CheckParamU8(uint8_t *paramDist, uint16_t paramSrc, float Coef)
{
if(paramDist == NULL)
return 0;
uint16_t expected_mb_param = *paramDist*Coef;
if(expected_mb_param != paramSrc)
{
*paramDist = (uint8_t)paramSrc/Coef;
return 1;
}
else
{
return 0;
}
}

111
UPP/Core/UPP/upp_params.h Normal file
View File

@@ -0,0 +1,111 @@
/**
******************************************************************************
* @file upp_params.h
* @brief Модуль определябщий поведение УПП
******************************************************************************
* @details
******************************************************************************/
#ifndef _UPP_PARAMS_H
#define _UPP_PARAMS_H
#include "upp_defs.h"
#define u2f(_u16_, _coef_) ((float)_u16_/_coef_)
typedef struct
{
unsigned set_default_pui:1; ///< Выставить настройки ПУИ в дефолтные
unsigned set_default_internal:1;///< Выставить внутренние настройки в дефолтные
unsigned go:1; ///< Запустить УПП
unsigned stop:1; ///< Остановка УПП (авария)
unsigned reserved:11;
unsigned reset_mcu:1;
}UPP_FuncCalls_t;
typedef struct
{
uint16_t PhaseSequence; ///< Последовательность фаз todo
uint16_t PhaseNumber; ///< [Количество]
uint16_t U; ///< [В x 10]
uint16_t U_deviation_plus; ///< [Проценты x 100]
uint16_t U_deviation_minus; ///< [Проценты x 100]
uint16_t F; ///< [Гц x 100]
uint16_t F_deviation_plus; ///< [Проценты x 100]
uint16_t F_deviation_minus; ///< [Проценты x 100]
uint16_t I; ///< [Амперы x 10]
}UPP_ParamsNominal_t;
typedef struct
{
uint16_t TemperatureWarn; ///< Предупредительный порог температуры [Градусы x 100]
uint16_t TemperatureErr; ///< Аварийный порог температуры [Градусы x 100]
}UPP_ParamsSetpoints_t;
typedef struct
{
struct
{
uint16_t rms_window_size; ///< Размер окна для RMS
uint16_t rms_exp_alpha; ///< Постоянная времени для сглаживания RMS
}pm;
UPP_ParamsNominal_t nominal;
UPP_ParamsSetpoints_t setpoints;
/* Параметры АЦП */
struct
{
uint16_t ADC_Max[4]; ///< Величина в единицах измерения при АЦП = 4095 [В или А x 10]
uint16_t ADC_Zero[4]; ///< Кванты АЦП когда на входе ноль
}adc;
/* Параметры ШИМ */
struct
{
uint16_t PhaseMask; ///< Битовяя маска на какие фазы подавать ШИМ: 0 бит - a, 1 бит - b, 2 бит - c
uint16_t Frequency; ///< Частота ШИМ для пачки импульсов на тиристоры [Герцы]
uint16_t Duty; ///< Скважность ШИМ для пачки импульсов на тиристоры [Проценты]
uint16_t PulseLength; ///< Количесво импульсов в пачке [Количество]
}pwm;
/* Параметры Угла */
struct
{
uint16_t Hysteresis; ///< Гистерезис для определения перехода через ноль [Проценты x 100]
uint16_t DebouneCouner; ///< Защита от дребезга: через сколько тактов снова начать фиксировать переход через ноль [Количество]
}zc;
/* Параметры Угла */
struct
{
uint16_t PulseLengthReserve;/*!< @brief Сколько запаса закладывать на длительность пачки импульсов [Проценты] @ref __AngleSetLimit
@details Пример: 100% - будет запас в одну пачку импульсов */
uint16_t Angle_Max; ///< Максимальный угол открытия тиристора [0..1 x 65535]
uint16_t Angle_Min; ///< Минимальный угол открытия тиристора [0..1 x 65535]
uint16_t PID_Kp; ///< Пропорциональный коэфициент ПИ регулятора угла [x 10000]
uint16_t PID_Ki; ///< Интегральный коэфициент ПИ регулятора угла [x 10000]
uint16_t PID_Kd; ///< Интегральный коэфициент ПИ регулятора угла [x 10000]
}angle;
}UPP_PrvtParams_t;
/* Контроль параметров УПП. */
void UPP_Params_Control(void);
/* Контроль параметров от ПУИ. */
void UPP_Params_ControlPUI(void);
/* Контроль внутренних параметров УПП. */
void UPP_Params_ControlInternal(void);
/* Инициализация параметров УПП. */
HAL_StatusTypeDef UPP_Params_Init(void);
/* Контроль параметров УПП на корректные значения. */
void UPP_Params_Saturate(void);
/* Установка параметров на дефолтные значения */
void UPP_Params_SetDefault(int pui_default, int internal_default);
#endif //_UPP_PARAMS_H

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<component_viewer schemaVersion="0.1" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:noNamespaceSchemaLocation="Component_Viewer.xsd">
<component name="EventRecorderStub" version="1.0.0"/> <!--name and version of the component-->
<events>
</events>
</component_viewer>

View File

@@ -0,0 +1,24 @@
/*
* Auto generated Run-Time-Environment Configuration File
* *** Do not modify ! ***
*
* Project: 'UPP'
* Target: 'Debug_F417'
*/
#ifndef RTE_COMPONENTS_H
#define RTE_COMPONENTS_H
/*
* Define the Device Header File:
*/
#define CMSIS_device_header "stm32f4xx.h"
/* Keil.ARM Compiler::Compiler:I/O:STDOUT:ITM:1.2.0 */
#define RTE_Compiler_IO_STDOUT /* Compiler I/O: STDOUT */
#define RTE_Compiler_IO_STDOUT_ITM /* Compiler I/O: STDOUT ITM */
#endif /* RTE_COMPONENTS_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,424 @@
;*******************************************************************************
;* File Name : startup_stm32f417xx.s
;* Author : MCD Application Team
;* Description : STM32F417xx devices vector table for MDK-ARM toolchain.
;* This module performs:
;* - Set the initial SP
;* - Set the initial PC == Reset_Handler
;* - Set the vector table entries with the exceptions ISR address
;* - Branches to __main in the C library (which eventually
;* calls main()).
;* After Reset the CortexM4 processor is in Thread mode,
;* priority is Privileged, and the Stack is set to Main.
;*******************************************************************************
;* @attention
;*
;* Copyright (c) 2017 STMicroelectronics.
;* All rights reserved.
;*
;* This software is licensed under terms that can be found in the LICENSE file
;* in the root directory of this software component.
;* If no LICENSE file comes with this software, it is provided AS-IS.
;*
;*******************************************************************************
;* <<< Use Configuration Wizard in Context Menu >>>
;
; Amount of memory (in bytes) allocated for Stack
; Tailor this value to your application needs
; <h> Stack Configuration
; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Stack_Size EQU 0x400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Heap_Size EQU 0x400
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
PRESERVE8
THUMB
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; External Interrupts
DCD WWDG_IRQHandler ; Window WatchDog
DCD PVD_IRQHandler ; PVD through EXTI Line detection
DCD TAMP_STAMP_IRQHandler ; Tamper and TimeStamps through the EXTI line
DCD RTC_WKUP_IRQHandler ; RTC Wakeup through the EXTI line
DCD FLASH_IRQHandler ; FLASH
DCD RCC_IRQHandler ; RCC
DCD EXTI0_IRQHandler ; EXTI Line0
DCD EXTI1_IRQHandler ; EXTI Line1
DCD EXTI2_IRQHandler ; EXTI Line2
DCD EXTI3_IRQHandler ; EXTI Line3
DCD EXTI4_IRQHandler ; EXTI Line4
DCD DMA1_Stream0_IRQHandler ; DMA1 Stream 0
DCD DMA1_Stream1_IRQHandler ; DMA1 Stream 1
DCD DMA1_Stream2_IRQHandler ; DMA1 Stream 2
DCD DMA1_Stream3_IRQHandler ; DMA1 Stream 3
DCD DMA1_Stream4_IRQHandler ; DMA1 Stream 4
DCD DMA1_Stream5_IRQHandler ; DMA1 Stream 5
DCD DMA1_Stream6_IRQHandler ; DMA1 Stream 6
DCD ADC_IRQHandler ; ADC1, ADC2 and ADC3s
DCD CAN1_TX_IRQHandler ; CAN1 TX
DCD CAN1_RX0_IRQHandler ; CAN1 RX0
DCD CAN1_RX1_IRQHandler ; CAN1 RX1
DCD CAN1_SCE_IRQHandler ; CAN1 SCE
DCD EXTI9_5_IRQHandler ; External Line[9:5]s
DCD TIM1_BRK_TIM9_IRQHandler ; TIM1 Break and TIM9
DCD TIM1_UP_TIM10_IRQHandler ; TIM1 Update and TIM10
DCD TIM1_TRG_COM_TIM11_IRQHandler ; TIM1 Trigger and Commutation and TIM11
DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare
DCD TIM2_IRQHandler ; TIM2
DCD TIM3_IRQHandler ; TIM3
DCD TIM4_IRQHandler ; TIM4
DCD I2C1_EV_IRQHandler ; I2C1 Event
DCD I2C1_ER_IRQHandler ; I2C1 Error
DCD I2C2_EV_IRQHandler ; I2C2 Event
DCD I2C2_ER_IRQHandler ; I2C2 Error
DCD SPI1_IRQHandler ; SPI1
DCD SPI2_IRQHandler ; SPI2
DCD USART1_IRQHandler ; USART1
DCD USART2_IRQHandler ; USART2
DCD USART3_IRQHandler ; USART3
DCD EXTI15_10_IRQHandler ; External Line[15:10]s
DCD RTC_Alarm_IRQHandler ; RTC Alarm (A and B) through EXTI Line
DCD OTG_FS_WKUP_IRQHandler ; USB OTG FS Wakeup through EXTI line
DCD TIM8_BRK_TIM12_IRQHandler ; TIM8 Break and TIM12
DCD TIM8_UP_TIM13_IRQHandler ; TIM8 Update and TIM13
DCD TIM8_TRG_COM_TIM14_IRQHandler ; TIM8 Trigger and Commutation and TIM14
DCD TIM8_CC_IRQHandler ; TIM8 Capture Compare
DCD DMA1_Stream7_IRQHandler ; DMA1 Stream7
DCD FMC_IRQHandler ; FMC
DCD SDIO_IRQHandler ; SDIO
DCD TIM5_IRQHandler ; TIM5
DCD SPI3_IRQHandler ; SPI3
DCD UART4_IRQHandler ; UART4
DCD UART5_IRQHandler ; UART5
DCD TIM6_DAC_IRQHandler ; TIM6 and DAC1&2 underrun errors
DCD TIM7_IRQHandler ; TIM7
DCD DMA2_Stream0_IRQHandler ; DMA2 Stream 0
DCD DMA2_Stream1_IRQHandler ; DMA2 Stream 1
DCD DMA2_Stream2_IRQHandler ; DMA2 Stream 2
DCD DMA2_Stream3_IRQHandler ; DMA2 Stream 3
DCD DMA2_Stream4_IRQHandler ; DMA2 Stream 4
DCD ETH_IRQHandler ; Ethernet
DCD ETH_WKUP_IRQHandler ; Ethernet Wakeup through EXTI line
DCD CAN2_TX_IRQHandler ; CAN2 TX
DCD CAN2_RX0_IRQHandler ; CAN2 RX0
DCD CAN2_RX1_IRQHandler ; CAN2 RX1
DCD CAN2_SCE_IRQHandler ; CAN2 SCE
DCD OTG_FS_IRQHandler ; USB OTG FS
DCD DMA2_Stream5_IRQHandler ; DMA2 Stream 5
DCD DMA2_Stream6_IRQHandler ; DMA2 Stream 6
DCD DMA2_Stream7_IRQHandler ; DMA2 Stream 7
DCD USART6_IRQHandler ; USART6
DCD I2C3_EV_IRQHandler ; I2C3 event
DCD I2C3_ER_IRQHandler ; I2C3 error
DCD OTG_HS_EP1_OUT_IRQHandler ; USB OTG HS End Point 1 Out
DCD OTG_HS_EP1_IN_IRQHandler ; USB OTG HS End Point 1 In
DCD OTG_HS_WKUP_IRQHandler ; USB OTG HS Wakeup through EXTI
DCD OTG_HS_IRQHandler ; USB OTG HS
DCD DCMI_IRQHandler ; DCMI
DCD CRYP_IRQHandler ; CRYPTO
DCD HASH_RNG_IRQHandler ; Hash and Rng
DCD FPU_IRQHandler ; FPU
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
AREA |.text|, CODE, READONLY
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
; Dummy Exception Handlers (infinite loops which can be modified)
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
B .
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
MemManage_Handler\
PROC
EXPORT MemManage_Handler [WEAK]
B .
ENDP
BusFault_Handler\
PROC
EXPORT BusFault_Handler [WEAK]
B .
ENDP
UsageFault_Handler\
PROC
EXPORT UsageFault_Handler [WEAK]
B .
ENDP
SVC_Handler PROC
EXPORT SVC_Handler [WEAK]
B .
ENDP
DebugMon_Handler\
PROC
EXPORT DebugMon_Handler [WEAK]
B .
ENDP
PendSV_Handler PROC
EXPORT PendSV_Handler [WEAK]
B .
ENDP
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
Default_Handler PROC
EXPORT WWDG_IRQHandler [WEAK]
EXPORT PVD_IRQHandler [WEAK]
EXPORT TAMP_STAMP_IRQHandler [WEAK]
EXPORT RTC_WKUP_IRQHandler [WEAK]
EXPORT FLASH_IRQHandler [WEAK]
EXPORT RCC_IRQHandler [WEAK]
EXPORT EXTI0_IRQHandler [WEAK]
EXPORT EXTI1_IRQHandler [WEAK]
EXPORT EXTI2_IRQHandler [WEAK]
EXPORT EXTI3_IRQHandler [WEAK]
EXPORT EXTI4_IRQHandler [WEAK]
EXPORT DMA1_Stream0_IRQHandler [WEAK]
EXPORT DMA1_Stream1_IRQHandler [WEAK]
EXPORT DMA1_Stream2_IRQHandler [WEAK]
EXPORT DMA1_Stream3_IRQHandler [WEAK]
EXPORT DMA1_Stream4_IRQHandler [WEAK]
EXPORT DMA1_Stream5_IRQHandler [WEAK]
EXPORT DMA1_Stream6_IRQHandler [WEAK]
EXPORT ADC_IRQHandler [WEAK]
EXPORT CAN1_TX_IRQHandler [WEAK]
EXPORT CAN1_RX0_IRQHandler [WEAK]
EXPORT CAN1_RX1_IRQHandler [WEAK]
EXPORT CAN1_SCE_IRQHandler [WEAK]
EXPORT EXTI9_5_IRQHandler [WEAK]
EXPORT TIM1_BRK_TIM9_IRQHandler [WEAK]
EXPORT TIM1_UP_TIM10_IRQHandler [WEAK]
EXPORT TIM1_TRG_COM_TIM11_IRQHandler [WEAK]
EXPORT TIM1_CC_IRQHandler [WEAK]
EXPORT TIM2_IRQHandler [WEAK]
EXPORT TIM3_IRQHandler [WEAK]
EXPORT TIM4_IRQHandler [WEAK]
EXPORT I2C1_EV_IRQHandler [WEAK]
EXPORT I2C1_ER_IRQHandler [WEAK]
EXPORT I2C2_EV_IRQHandler [WEAK]
EXPORT I2C2_ER_IRQHandler [WEAK]
EXPORT SPI1_IRQHandler [WEAK]
EXPORT SPI2_IRQHandler [WEAK]
EXPORT USART1_IRQHandler [WEAK]
EXPORT USART2_IRQHandler [WEAK]
EXPORT USART3_IRQHandler [WEAK]
EXPORT EXTI15_10_IRQHandler [WEAK]
EXPORT RTC_Alarm_IRQHandler [WEAK]
EXPORT OTG_FS_WKUP_IRQHandler [WEAK]
EXPORT TIM8_BRK_TIM12_IRQHandler [WEAK]
EXPORT TIM8_UP_TIM13_IRQHandler [WEAK]
EXPORT TIM8_TRG_COM_TIM14_IRQHandler [WEAK]
EXPORT TIM8_CC_IRQHandler [WEAK]
EXPORT DMA1_Stream7_IRQHandler [WEAK]
EXPORT FMC_IRQHandler [WEAK]
EXPORT SDIO_IRQHandler [WEAK]
EXPORT TIM5_IRQHandler [WEAK]
EXPORT SPI3_IRQHandler [WEAK]
EXPORT UART4_IRQHandler [WEAK]
EXPORT UART5_IRQHandler [WEAK]
EXPORT TIM6_DAC_IRQHandler [WEAK]
EXPORT TIM7_IRQHandler [WEAK]
EXPORT DMA2_Stream0_IRQHandler [WEAK]
EXPORT DMA2_Stream1_IRQHandler [WEAK]
EXPORT DMA2_Stream2_IRQHandler [WEAK]
EXPORT DMA2_Stream3_IRQHandler [WEAK]
EXPORT DMA2_Stream4_IRQHandler [WEAK]
EXPORT ETH_IRQHandler [WEAK]
EXPORT ETH_WKUP_IRQHandler [WEAK]
EXPORT CAN2_TX_IRQHandler [WEAK]
EXPORT CAN2_RX0_IRQHandler [WEAK]
EXPORT CAN2_RX1_IRQHandler [WEAK]
EXPORT CAN2_SCE_IRQHandler [WEAK]
EXPORT OTG_FS_IRQHandler [WEAK]
EXPORT DMA2_Stream5_IRQHandler [WEAK]
EXPORT DMA2_Stream6_IRQHandler [WEAK]
EXPORT DMA2_Stream7_IRQHandler [WEAK]
EXPORT USART6_IRQHandler [WEAK]
EXPORT I2C3_EV_IRQHandler [WEAK]
EXPORT I2C3_ER_IRQHandler [WEAK]
EXPORT OTG_HS_EP1_OUT_IRQHandler [WEAK]
EXPORT OTG_HS_EP1_IN_IRQHandler [WEAK]
EXPORT OTG_HS_WKUP_IRQHandler [WEAK]
EXPORT OTG_HS_IRQHandler [WEAK]
EXPORT DCMI_IRQHandler [WEAK]
EXPORT CRYP_IRQHandler [WEAK]
EXPORT HASH_RNG_IRQHandler [WEAK]
EXPORT FPU_IRQHandler [WEAK]
WWDG_IRQHandler
PVD_IRQHandler
TAMP_STAMP_IRQHandler
RTC_WKUP_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMA1_Stream0_IRQHandler
DMA1_Stream1_IRQHandler
DMA1_Stream2_IRQHandler
DMA1_Stream3_IRQHandler
DMA1_Stream4_IRQHandler
DMA1_Stream5_IRQHandler
DMA1_Stream6_IRQHandler
ADC_IRQHandler
CAN1_TX_IRQHandler
CAN1_RX0_IRQHandler
CAN1_RX1_IRQHandler
CAN1_SCE_IRQHandler
EXTI9_5_IRQHandler
TIM1_BRK_TIM9_IRQHandler
TIM1_UP_TIM10_IRQHandler
TIM1_TRG_COM_TIM11_IRQHandler
TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM4_IRQHandler
I2C1_EV_IRQHandler
I2C1_ER_IRQHandler
I2C2_EV_IRQHandler
I2C2_ER_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
USART3_IRQHandler
EXTI15_10_IRQHandler
RTC_Alarm_IRQHandler
OTG_FS_WKUP_IRQHandler
TIM8_BRK_TIM12_IRQHandler
TIM8_UP_TIM13_IRQHandler
TIM8_TRG_COM_TIM14_IRQHandler
TIM8_CC_IRQHandler
DMA1_Stream7_IRQHandler
FMC_IRQHandler
SDIO_IRQHandler
TIM5_IRQHandler
SPI3_IRQHandler
UART4_IRQHandler
UART5_IRQHandler
TIM6_DAC_IRQHandler
TIM7_IRQHandler
DMA2_Stream0_IRQHandler
DMA2_Stream1_IRQHandler
DMA2_Stream2_IRQHandler
DMA2_Stream3_IRQHandler
DMA2_Stream4_IRQHandler
ETH_IRQHandler
ETH_WKUP_IRQHandler
CAN2_TX_IRQHandler
CAN2_RX0_IRQHandler
CAN2_RX1_IRQHandler
CAN2_SCE_IRQHandler
OTG_FS_IRQHandler
DMA2_Stream5_IRQHandler
DMA2_Stream6_IRQHandler
DMA2_Stream7_IRQHandler
USART6_IRQHandler
I2C3_EV_IRQHandler
I2C3_ER_IRQHandler
OTG_HS_EP1_OUT_IRQHandler
OTG_HS_EP1_IN_IRQHandler
OTG_HS_WKUP_IRQHandler
OTG_HS_IRQHandler
DCMI_IRQHandler
CRYP_IRQHandler
HASH_RNG_IRQHandler
FPU_IRQHandler
B .
ENDP
ALIGN
;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
IF :DEF:__MICROLIB
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE
IMPORT __use_two_region_memory
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDR R0, = Heap_Mem
LDR R1, =(Stack_Mem + Stack_Size)
LDR R2, = (Heap_Mem + Heap_Size)
LDR R3, = Stack_Mem
BX LR
ALIGN
ENDIF
END

View File

@@ -5,9 +5,10 @@ ADC3.Channel-17\#ChannelRegularConversion=ADC_CHANNEL_6
ADC3.Channel-18\#ChannelRegularConversion=ADC_CHANNEL_7
ADC3.Channel-19\#ChannelRegularConversion=ADC_CHANNEL_8
ADC3.Channel-20\#ChannelRegularConversion=ADC_CHANNEL_10
ADC3.DMAContinuousRequests=ENABLE
ADC3.EOCSelection=ADC_EOC_SEQ_CONV
ADC3.ExternalTrigConv=ADC_EXTERNALTRIGCONV_T8_TRGO
ADC3.IPParameters=Rank-15\#ChannelRegularConversion,Channel-15\#ChannelRegularConversion,SamplingTime-15\#ChannelRegularConversion,NbrOfConversionFlag,NbrOfConversion,ScanConvMode,Rank-16\#ChannelRegularConversion,Channel-16\#ChannelRegularConversion,SamplingTime-16\#ChannelRegularConversion,Rank-17\#ChannelRegularConversion,Channel-17\#ChannelRegularConversion,SamplingTime-17\#ChannelRegularConversion,Rank-18\#ChannelRegularConversion,Channel-18\#ChannelRegularConversion,SamplingTime-18\#ChannelRegularConversion,Rank-19\#ChannelRegularConversion,Channel-19\#ChannelRegularConversion,SamplingTime-19\#ChannelRegularConversion,Rank-20\#ChannelRegularConversion,Channel-20\#ChannelRegularConversion,SamplingTime-20\#ChannelRegularConversion,ExternalTrigConv,EOCSelection
ADC3.ExternalTrigConv=ADC_EXTERNALTRIGCONV_T3_TRGO
ADC3.IPParameters=Rank-15\#ChannelRegularConversion,Channel-15\#ChannelRegularConversion,SamplingTime-15\#ChannelRegularConversion,NbrOfConversionFlag,NbrOfConversion,ScanConvMode,Rank-16\#ChannelRegularConversion,Channel-16\#ChannelRegularConversion,SamplingTime-16\#ChannelRegularConversion,Rank-17\#ChannelRegularConversion,Channel-17\#ChannelRegularConversion,SamplingTime-17\#ChannelRegularConversion,Rank-18\#ChannelRegularConversion,Channel-18\#ChannelRegularConversion,SamplingTime-18\#ChannelRegularConversion,Rank-19\#ChannelRegularConversion,Channel-19\#ChannelRegularConversion,SamplingTime-19\#ChannelRegularConversion,Rank-20\#ChannelRegularConversion,Channel-20\#ChannelRegularConversion,SamplingTime-20\#ChannelRegularConversion,ExternalTrigConv,EOCSelection,DMAContinuousRequests
ADC3.NbrOfConversion=6
ADC3.NbrOfConversionFlag=1
ADC3.Rank-15\#ChannelRegularConversion=1
@@ -38,7 +39,7 @@ Dma.ADC3.0.FIFOMode=DMA_FIFOMODE_DISABLE
Dma.ADC3.0.Instance=DMA2_Stream0
Dma.ADC3.0.MemDataAlignment=DMA_MDATAALIGN_HALFWORD
Dma.ADC3.0.MemInc=DMA_MINC_ENABLE
Dma.ADC3.0.Mode=DMA_NORMAL
Dma.ADC3.0.Mode=DMA_CIRCULAR
Dma.ADC3.0.PeriphDataAlignment=DMA_PDATAALIGN_HALFWORD
Dma.ADC3.0.PeriphInc=DMA_PINC_DISABLE
Dma.ADC3.0.Priority=DMA_PRIORITY_LOW
@@ -81,64 +82,64 @@ Mcu.Pin12=PF10
Mcu.Pin13=PH0/OSC_IN
Mcu.Pin14=PH1/OSC_OUT
Mcu.Pin15=PC0
Mcu.Pin16=PA0/WKUP
Mcu.Pin17=PA4
Mcu.Pin18=PA5
Mcu.Pin19=PA6
Mcu.Pin16=PA4
Mcu.Pin17=PA5
Mcu.Pin18=PA6
Mcu.Pin19=PB0
Mcu.Pin2=PE4
Mcu.Pin20=PB0
Mcu.Pin21=PB1
Mcu.Pin22=PF11
Mcu.Pin23=PB10
Mcu.Pin24=PB11
Mcu.Pin25=PB13
Mcu.Pin26=PG6
Mcu.Pin27=PC6
Mcu.Pin28=PC7
Mcu.Pin29=PC8
Mcu.Pin20=PB1
Mcu.Pin21=PF11
Mcu.Pin22=PB10
Mcu.Pin23=PB11
Mcu.Pin24=PB13
Mcu.Pin25=PG6
Mcu.Pin26=PC6
Mcu.Pin27=PC7
Mcu.Pin28=PC8
Mcu.Pin29=PC9
Mcu.Pin3=PE5
Mcu.Pin30=PC9
Mcu.Pin31=PA8
Mcu.Pin32=PA9
Mcu.Pin33=PA10
Mcu.Pin34=PA11
Mcu.Pin35=PA12
Mcu.Pin36=PA13
Mcu.Pin37=PA14
Mcu.Pin38=PA15
Mcu.Pin39=PC10
Mcu.Pin30=PA8
Mcu.Pin31=PA9
Mcu.Pin32=PA10
Mcu.Pin33=PA11
Mcu.Pin34=PA12
Mcu.Pin35=PA13
Mcu.Pin36=PA14
Mcu.Pin37=PA15
Mcu.Pin38=PC10
Mcu.Pin39=PC11
Mcu.Pin4=PE6
Mcu.Pin40=PC11
Mcu.Pin41=PC12
Mcu.Pin42=PD2
Mcu.Pin43=PD3
Mcu.Pin44=PD6
Mcu.Pin45=PG12
Mcu.Pin46=PG15
Mcu.Pin47=PB3
Mcu.Pin48=PB6
Mcu.Pin49=PB7
Mcu.Pin40=PC12
Mcu.Pin41=PD2
Mcu.Pin42=PD3
Mcu.Pin43=PD6
Mcu.Pin44=PG12
Mcu.Pin45=PG15
Mcu.Pin46=PB3
Mcu.Pin47=PB6
Mcu.Pin48=PB7
Mcu.Pin49=PB8
Mcu.Pin5=PC13
Mcu.Pin50=PB8
Mcu.Pin51=PB9
Mcu.Pin52=PE0
Mcu.Pin53=PE1
Mcu.Pin54=VP_IWDG_VS_IWDG
Mcu.Pin55=VP_RTC_VS_RTC_Activate
Mcu.Pin56=VP_RTC_VS_RTC_Calendar
Mcu.Pin57=VP_SYS_VS_tim14
Mcu.Pin58=VP_TIM1_VS_ClockSourceINT
Mcu.Pin59=VP_TIM1_VS_OPM
Mcu.Pin50=PB9
Mcu.Pin51=PE0
Mcu.Pin52=PE1
Mcu.Pin53=VP_IWDG_VS_IWDG
Mcu.Pin54=VP_RTC_VS_RTC_Activate
Mcu.Pin55=VP_RTC_VS_RTC_Calendar
Mcu.Pin56=VP_SYS_VS_tim14
Mcu.Pin57=VP_TIM1_VS_ClockSourceINT
Mcu.Pin58=VP_TIM1_VS_OPM
Mcu.Pin59=VP_TIM2_VS_ClockSourceINT
Mcu.Pin6=PC14/OSC32_IN
Mcu.Pin60=VP_TIM2_VS_ClockSourceINT
Mcu.Pin61=VP_TIM2_VS_no_output1
Mcu.Pin62=VP_TIM2_VS_no_output2
Mcu.Pin63=VP_TIM2_VS_no_output3
Mcu.Pin64=VP_TIM3_VS_ControllerModeTrigger
Mcu.Pin65=VP_TIM3_VS_ClockSourceINT
Mcu.Pin66=VP_TIM3_VS_ClockSourceITR
Mcu.Pin67=VP_TIM3_VS_OPM
Mcu.Pin68=VP_TIM8_VS_ClockSourceINT
Mcu.Pin60=VP_TIM2_VS_no_output1
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_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
@@ -146,7 +147,7 @@ Mcu.Pin8=PF6
Mcu.Pin9=PF7
Mcu.PinsNb=71
Mcu.ThirdPartyNb=0
Mcu.UserConstants=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;PWM_CHANNEL_5,TIM_CHANNEL_3;PWM_CHANNEL_6,TIM_CHANNEL_4;mb_htim,htim12;adc_tim,htim8;usTick,ustim.Instance->CNT;hpwm2,htim3;mb_dbg_huart,huart6;ustim,htim5;hpwm1,htim1;ANGLE_CHANNEL_1,TIM_CHANNEL_1;ANGLE_CHANNEL_2,TIM_CHANNEL_2;ANGLE_CHANNEL_3,TIM_CHANNEL_3;angletim,htim2
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
MxCube.Version=6.12.1
MxDb.Version=DB.6.0.121
@@ -164,10 +165,10 @@ NVIC.SysTick_IRQn=true\:15\:0\:false\:false\:true\:false\:true\:false
NVIC.TIM1_UP_TIM10_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true
NVIC.TIM2_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true
NVIC.TIM8_TRG_COM_TIM14_IRQn=true\:15\:0\:false\:false\:true\:false\:true\:true
NVIC.TIM8_UP_TIM13_IRQn=true\:0\:0\:false\:false\:true\:true\:true\:true
NVIC.TimeBase=TIM8_TRG_COM_TIM14_IRQn
NVIC.TimeBaseIP=TIM14
NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false
PA0/WKUP.Signal=S_TIM5_CH1
PA10.GPIOParameters=GPIO_Label
PA10.GPIO_Label=PWM3
PA10.Locked=true
@@ -193,7 +194,7 @@ PA4.Signal=GPIO_Output
PA5.GPIOParameters=PinState,GPIO_Label
PA5.GPIO_Label=DO4
PA5.Locked=true
PA5.PinState=GPIO_PIN_SET
PA5.PinState=GPIO_PIN_RESET
PA5.Signal=GPIO_Output
PA6.GPIOParameters=PinState,GPIO_Label
PA6.GPIO_Label=RDO4
@@ -263,10 +264,9 @@ PC12.GPIOParameters=GPIO_Label
PC12.GPIO_Label=UM_SPI_MOSI
PC12.Mode=Full_Duplex_Master
PC12.Signal=SPI3_MOSI
PC13.GPIOParameters=PinState,GPIO_Label
PC13.GPIOParameters=GPIO_Label
PC13.GPIO_Label=DO3
PC13.Locked=true
PC13.PinState=GPIO_PIN_SET
PC13.Signal=GPIO_Output
PC14/OSC32_IN.Mode=LSE-External-Oscillator
PC14/OSC32_IN.Signal=RCC_OSC32_IN
@@ -278,16 +278,15 @@ PC6.Mode=Asynchronous
PC6.Signal=USART6_TX
PC7.GPIOParameters=GPIO_Label
PC7.GPIO_Label=SCIR2
PC7.Locked=true
PC7.Mode=Asynchronous
PC7.Signal=USART6_RX
PC8.GPIOParameters=GPIO_Label
PC8.GPIO_Label=PWM5
PC8.Locked=true
PC8.Signal=S_TIM3_CH3
PC8.Signal=S_TIM8_CH3
PC9.GPIOParameters=GPIO_Label
PC9.GPIO_Label=PWM6
PC9.Locked=true
PC9.Signal=S_TIM3_CH4
PC9.Signal=S_TIM8_CH4
PD2.GPIOParameters=GPIO_Label
PD2.GPIO_Label=DIN3
PD2.Locked=true
@@ -317,7 +316,7 @@ PE2.Locked=true
PE2.PinState=GPIO_PIN_SET
PE2.Signal=GPIO_Output
PE3.GPIOParameters=PinState,GPIO_Label
PE3.GPIO_Label=CEN_O
PE3.GPIO_Label=CEN
PE3.Locked=true
PE3.PinState=GPIO_PIN_SET
PE3.Signal=GPIO_Output
@@ -362,8 +361,9 @@ PG12.GPIOParameters=GPIO_Label
PG12.GPIO_Label=ERR_24V
PG12.Locked=true
PG12.Signal=GPIO_Input
PG15.GPIOParameters=GPIO_Label
PG15.GPIOParameters=GPIO_PuPd,GPIO_Label
PG15.GPIO_Label=DIN1
PG15.GPIO_PuPd=GPIO_PULLUP
PG15.Locked=true
PG15.Signal=GPIO_Input
PG6.GPIOParameters=GPIO_PuPd,GPIO_Label
@@ -406,7 +406,7 @@ ProjectManager.ToolChainLocation=
ProjectManager.UAScriptAfterPath=
ProjectManager.UAScriptBeforePath=
ProjectManager.UnderRoot=false
ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-true-HAL-true,3-MX_DMA_Init-DMA-false-HAL-true,4-MX_ADC3_Init-ADC3-false-HAL-true,5-MX_USART3_UART_Init-USART3-false-HAL-true,6-MX_CAN1_Init-CAN1-false-HAL-true,7-MX_IWDG_Init-IWDG-false-HAL-true,8-MX_RTC_Init-RTC-false-HAL-true,9-MX_TIM1_Init-TIM1-false-HAL-true,10-MX_TIM3_Init-TIM3-false-HAL-true,11-MX_USART6_UART_Init-USART6-false-HAL-true,12-MX_SPI3_Init-SPI3-false-HAL-true,13-MX_TIM11_Init-TIM11-false-HAL-true,14-MX_TIM12_Init-TIM12-false-HAL-true,15-MX_TIM8_Init-TIM8-false-HAL-true,16-MX_TIM5_Init-TIM5-false-HAL-true,17-MX_TIM2_Init-TIM2-false-HAL-true
ProjectManager.functionlistsort=1-SystemClock_Config-RCC-false-HAL-false,2-MX_GPIO_Init-GPIO-false-HAL-true,3-MX_DMA_Init-DMA-false-HAL-true,4-MX_ADC3_Init-ADC3-false-HAL-true,5-MX_USART3_UART_Init-USART3-false-HAL-true,6-MX_CAN1_Init-CAN1-false-HAL-true,7-MX_IWDG_Init-IWDG-false-HAL-true,8-MX_RTC_Init-RTC-true-HAL-true,9-MX_TIM1_Init-TIM1-false-HAL-true,10-MX_TIM3_Init-TIM3-false-HAL-true,11-MX_USART6_UART_Init-USART6-false-HAL-true,12-MX_SPI3_Init-SPI3-false-HAL-true,13-MX_TIM11_Init-TIM11-false-HAL-true,14-MX_TIM12_Init-TIM12-false-HAL-true,15-MX_TIM8_Init-TIM8-false-HAL-true,16-MX_TIM5_Init-TIM5-false-HAL-true,17-MX_TIM2_Init-TIM2-false-HAL-true
RCC.48MHZClocksFreq_Value=90000000
RCC.AHBFreq_Value=180000000
RCC.APB1CLKDivider=RCC_HCLK_DIV4
@@ -461,24 +461,22 @@ SH.S_TIM1_CH3.0=TIM1_CH3,PWM Generation3 CH3
SH.S_TIM1_CH3.ConfNb=1
SH.S_TIM1_CH4.0=TIM1_CH4,PWM Generation4 CH4
SH.S_TIM1_CH4.ConfNb=1
SH.S_TIM3_CH3.0=TIM3_CH3,PWM Generation3 CH3
SH.S_TIM3_CH3.ConfNb=1
SH.S_TIM3_CH4.0=TIM3_CH4,PWM Generation4 CH4
SH.S_TIM3_CH4.ConfNb=1
SH.S_TIM5_CH1.0=TIM5_CH1,Input_Capture1_from_TI1
SH.S_TIM5_CH1.ConfNb=1
SH.S_TIM8_CH3.0=TIM8_CH3,PWM Generation3 CH3
SH.S_TIM8_CH3.ConfNb=1
SH.S_TIM8_CH4.0=TIM8_CH4,PWM Generation4 CH4
SH.S_TIM8_CH4.ConfNb=1
SPI3.CalculateBaudRate=22.5 MBits/s
SPI3.Direction=SPI_DIRECTION_2LINES
SPI3.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate
SPI3.Mode=SPI_MODE_MASTER
SPI3.VirtualType=VM_MASTER
TIM1.Channel-Output\ Compare2\ CH2=TIM_CHANNEL_2
TIM1.Channel-PWM\ Generation1\ CH1=TIM_CHANNEL_1
TIM1.Channel-PWM\ Generation2\ CH2=TIM_CHANNEL_2
TIM1.Channel-PWM\ Generation3\ CH3=TIM_CHANNEL_3
TIM1.Channel-PWM\ Generation4\ CH4=TIM_CHANNEL_4
TIM1.IPParameters=Prescaler,Channel-Output Compare2 CH2,Channel-PWM Generation1 CH1,Channel-PWM Generation2 CH2,Channel-PWM Generation4 CH4,Channel-PWM Generation3 CH3,TIM_MasterOutputTrigger
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=0
TIM1.TIM_MasterOutputTrigger=TIM_TRGO_UPDATE
TIM11.IPParameters=Prescaler
TIM11.Prescaler=180-1
@@ -491,18 +489,19 @@ TIM2.IPParameters=Channel-Output Compare2 No Output,Channel-Output Compare1 No O
TIM2.OCMode_1=TIM_OCMODE_TIMING
TIM2.OCMode_2=TIM_OCMODE_TIMING
TIM2.OCMode_3=TIM_OCMODE_TIMING
TIM3.Channel-PWM\ Generation3\ CH3=TIM_CHANNEL_3
TIM3.Channel-PWM\ Generation4\ CH4=TIM_CHANNEL_4
TIM3.IPParameters=Channel-PWM Generation3 CH3,Channel-PWM Generation4 CH4,TIM_MasterSlaveMode,TIM_MasterOutputTrigger
TIM3.TIM_MasterOutputTrigger=TIM_TRGO_RESET
TIM3.TIM_MasterSlaveMode=TIM_MASTERSLAVEMODE_ENABLE
TIM5.Channel-Input_Capture1_from_TI1=TIM_CHANNEL_1
TIM5.IPParameters=Channel-Input_Capture1_from_TI1,Prescaler
TIM3.IPParameters=TIM_MasterSlaveMode,TIM_MasterOutputTrigger
TIM3.TIM_MasterOutputTrigger=TIM_TRGO_UPDATE
TIM3.TIM_MasterSlaveMode=TIM_MASTERSLAVEMODE_DISABLE
TIM5.IPParameters=Prescaler
TIM5.Prescaler=90-1
TIM8.IPParameters=Prescaler,Period,TIM_MasterSlaveMode,TIM_MasterOutputTrigger
TIM8.Period=1800-1
TIM8.Channel-PWM\ Generation3\ CH3=TIM_CHANNEL_3
TIM8.Channel-PWM\ Generation4\ CH4=TIM_CHANNEL_4
TIM8.IPParameters=Prescaler,Period,TIM_MasterSlaveMode,TIM_MasterOutputTrigger,Channel-PWM Generation3 CH3,Channel-PWM Generation4 CH4,OC4Preload_PWM,RepetitionCounter
TIM8.OC4Preload_PWM=ENABLE
TIM8.Period=65535
TIM8.Prescaler=0
TIM8.TIM_MasterOutputTrigger=TIM_TRGO_UPDATE
TIM8.RepetitionCounter=0
TIM8.TIM_MasterOutputTrigger=TIM_TRGO_RESET
TIM8.TIM_MasterSlaveMode=TIM_MASTERSLAVEMODE_DISABLE
USART3.IPParameters=VirtualMode
USART3.VirtualMode=VM_ASYNC
@@ -534,12 +533,14 @@ VP_TIM2_VS_no_output3.Mode=Output Compare3 No Output
VP_TIM2_VS_no_output3.Signal=TIM2_VS_no_output3
VP_TIM3_VS_ClockSourceINT.Mode=Internal
VP_TIM3_VS_ClockSourceINT.Signal=TIM3_VS_ClockSourceINT
VP_TIM3_VS_ClockSourceITR.Mode=TriggerSource_ITR0
VP_TIM3_VS_ClockSourceITR.Signal=TIM3_VS_ClockSourceITR
VP_TIM3_VS_ControllerModeTrigger.Mode=Trigger Mode
VP_TIM3_VS_ControllerModeTrigger.Signal=TIM3_VS_ControllerModeTrigger
VP_TIM3_VS_OPM.Mode=OPM_bit
VP_TIM3_VS_OPM.Signal=TIM3_VS_OPM
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');

View File

@@ -0,0 +1,11 @@
clc
clear all
alpha = 0.01
ts = 500;
tau = (ts/1000000) * (1-alpha)/alpha
alpha2 = ts/1000000 / (ts/1000000 + tau )