Добавлены комменты

This commit is contained in:
Razvalyaev 2025-05-17 14:28:50 +03:00
parent 416260c9e1
commit 95a022d6c1
10 changed files with 664 additions and 318 deletions

View File

@ -1,35 +1,63 @@
#include "adc_filter.h"
/**
* @brief Привязка структуры фильтра к конкретному АЦП
* @param hfilter Указатель на структуру ADCFilter_t
* @param hadc Указатель на структуру HAL АЦП
*/
void adc_Attach(ADCFilter_t *hfilter, ADC_HandleTypeDef *hadc)
{
// Связываем фильтр с конкретным аппаратным АЦП для чтения данных
hfilter->hadc = hadc;
}
// фильтрация
/**
* @brief Обновление и фильтрация новых данных АЦП методом усреднения
* @param hfilter Указатель на структуру ADCFilter_t
* @return int Возвращает 1, если произведено обновление отфильтрованных данных, иначе 0
*/
int adc_filterring(ADCFilter_t *hfilter)
{
// Добавляем текущее значение из регистра данных АЦП в сумму
hfilter->sum += hfilter->hadc->Instance->DR;
// Увеличиваем счётчик накопленных выборок
hfilter->count++;
// Если набрано достаточное количество выборок для усреднения
if (hfilter->count >= hfilter->bufferSize)
{
// Вычисляем среднее значение и сохраняем как отфильтрованный результат
hfilter->AdcResult = hfilter->sum / hfilter->bufferSize;
// Сбрасываем сумму и счётчик для следующего цикла
hfilter->sum = 0;
hfilter->count = 0;
hfilter->f.DataUpdated = 1;
// Отмечаем, что новые данные отфильтрованы и готовы к чтению
hfilter->f.DataUpdated = 1;
return 1;
}
// Если ещё недостаточно данных, возвращаем 0 — фильтрация не завершена
return 0;
}
/**
* @brief Чтение последнего отфильтрованного значения АЦП
* @param hfilter Указатель на структуру ADCFilter_t
* @return uint16_t Последнее отфильтрованное значение
*/
uint16_t adc_read_data(ADCFilter_t *hfilter)
{
hfilter->f.DataUpdated = 0;
return hfilter->AdcResult;
// После чтения сбрасываем флаг обновления данных
hfilter->f.DataUpdated = 0;
return hfilter->AdcResult;
}
/**
* @brief Проверка, обновились ли данные АЦП после фильтрации
* @param hfilter Указатель на структуру ADCFilter_t
* @return int Возвращает 1, если данные были обновлены, иначе 0
*/
int adc_is_data_updated(ADCFilter_t *hfilter)
{
return hfilter->f.DataUpdated;
}
return hfilter->f.DataUpdated;
}

View File

@ -3,25 +3,37 @@
#include "main.h"
/**
* @brief Флаги состояния фильтра АЦП
*/
typedef struct
{
unsigned DataUpdated:1;
}AdcFilterFlags;
unsigned DataUpdated : 1; /**< Флаг обновления данных */
} AdcFilterFlags;
// структура для ацп
/**
* @brief Структура фильтрации данных АЦП
*/
typedef struct
{
AdcFilterFlags f;
uint16_t AdcResult;
ADC_HandleTypeDef *hadc;
uint32_t sum;
uint16_t count;
uint16_t bufferSize;
AdcFilterFlags f; /**< Флаги состояния */
uint16_t AdcResult; /**< Отфильтрованное значение АЦП */
ADC_HandleTypeDef *hadc; /**< Указатель на структуру HAL АЦП */
uint32_t sum; /**< Сумма накопленных значений для усреднения */
uint16_t count; /**< Текущий счётчик накопленных значений */
uint16_t bufferSize; /**< Размер буфера для усреднения (число выборок) */
} ADCFilter_t;
/** Привязка структуры фильтра к конкретному АЦП */
void adc_Attach(ADCFilter_t *hfilter, ADC_HandleTypeDef *hadc);
/** Обновление и фильтрация нового значения АЦП */
int adc_filterring(ADCFilter_t *hfilter);
/** Чтение последнего отфильтрованного значения АЦП */
uint16_t adc_read_data(ADCFilter_t *hfilter);
/** Проверка, обновились ли данные АЦП после фильтрации */
int adc_is_data_updated(ADCFilter_t *hfilter);
#endif //__ADC_FILTER_H
#endif //__ADC_FILTER_H

View File

@ -1,104 +1,142 @@
#include "tiristor.h"
// управление тиристором
/**
* @brief Управление состоянием тиристора (включение/выключение) по флагам и времени открытия
* @param ctrl Указатель на структуру управления тиристором
*/
void tiristor_control(TiristorControl_t *ctrl)
{
if(ctrl->f.EnableTiristor)
{
if(ctrl->f.TiristorIsEnable == 0)
{
tiristor_enable(ctrl);
ctrl->enable_start_tick = HAL_GetTick();
}
else
{
if(HAL_GetTick() - ctrl->enable_start_tick > ctrl->open_time)
{
tiristor_disable(ctrl);
ctrl->f.EnableTiristor = 0;
}
}
}
else
{
if(ctrl->f.TiristorIsEnable)
tiristor_disable(ctrl);
}
if(ctrl->f.EnableTiristor) // Если разрешено включить тиристор
{
if(ctrl->f.TiristorIsEnable == 0) // Если тиристор еще выключен
{
tiristor_enable(ctrl); // Включить тиристор
ctrl->enable_start_tick = HAL_GetTick(); // Запомнить время включения для отсчёта длительности открытия
}
else
{
// Если время с момента включения превысило заданное время открытия
if(HAL_GetTick() - ctrl->enable_start_tick > ctrl->open_time)
{
tiristor_disable(ctrl); // Выключить тиристор
ctrl->f.EnableTiristor = 0; // Снять разрешение на включение, чтобы не включался снова без команды
}
}
}
else // Если тиристор не должен быть включен
{
if(ctrl->f.TiristorIsEnable) // Если тиристор включен
tiristor_disable(ctrl); // Выключить тиристор
}
}
/**
* @brief Обновление значения задержки угла открытия тиристора в соответствии с направлением и шагом
* @param angle Указатель на структуру управления углом тиристора
*/
void tiristor_angle_update(TiristorAngleControl_t *angle)
{
uint32_t current_time_ms = HAL_GetTick();
if ((current_time_ms - angle->last_update_ms) >= angle->Init->sample_time_ms)
{
angle->last_update_ms = current_time_ms;
{
uint32_t current_time_ms = HAL_GetTick(); // Текущее время в миллисекундах
if(angle->Init->direction)
angle->delay_us += angle->Init->delay_step_us;
else
angle->delay_us -= angle->Init->delay_step_us;
if (angle->delay_us < angle->Init->delay_min_us)
{
angle->delay_us = angle->Init->delay_min_us;
}
else if (angle->delay_us > angle->Init->delay_max_us)
{
angle->delay_us = angle->Init->delay_max_us;
}
}
// Проверяем, прошло ли нужное время с последнего обновления
if ((current_time_ms - angle->last_update_ms) >= angle->Init->sample_time_ms)
{
angle->last_update_ms = current_time_ms; // Обновляем время последнего изменения задержки
// Изменяем задержку в зависимости от направления (разгон или торможение)
if(angle->Init->direction)
angle->delay_us += angle->Init->delay_step_us; // Увеличиваем задержку (увеличиваем угол)
else
angle->delay_us -= angle->Init->delay_step_us; // Уменьшаем задержку (уменьшаем угол)
// Ограничиваем задержку в пределах минимального и максимального значения
if (angle->delay_us < angle->Init->delay_min_us)
{
angle->delay_us = angle->Init->delay_min_us;
}
else if (angle->delay_us > angle->Init->delay_max_us)
{
angle->delay_us = angle->Init->delay_max_us;
}
}
}
/**
* @brief Контроль угла открытия тиристора с проверкой таймера и флага готовности
* @param ctrl Указатель на структуру управления тиристором
*/
void tiristor_angle_control(TiristorControl_t *ctrl)
{
tiristor_angle_update(&ctrl->angle);
if(ctrl->angle.delay_us != 0)
{
if ((uint16_t)((uint16_t)TIMER->CNT - ctrl->angle.start_delay_tick) > ctrl->angle.delay_us)
{
if(ctrl->f.TiristorReady)
{
ctrl->f.EnableTiristor = 1;
ctrl->f.TiristorReady = 0;
}
}
}
tiristor_angle_update(&ctrl->angle); // Обновляем задержку угла открытия
if(ctrl->angle.delay_us != 0) // Если задержка не нулевая
{
// Проверяем, прошла ли задержка с момента старта отсчёта таймера
if ((uint16_t)((uint16_t)TIMER->CNT - ctrl->angle.start_delay_tick) > ctrl->angle.delay_us)
{
if(ctrl->f.TiristorReady) // Если тиристор готов к включению
{
ctrl->f.EnableTiristor = 1; // Разрешаем включение тиристора
ctrl->f.TiristorReady = 0; // Снимаем флаг готовности, чтобы не включать повторно сразу
}
}
}
}
/**
* @brief Запуск отсчёта задержки для открытия тиристора
* @param ctrl Указатель на структуру управления тиристором
*/
void tiristor_start_angle_delay(TiristorControl_t *ctrl)
{
ctrl->f.TiristorReady = 1;
ctrl->angle.start_delay_tick = TIMER->CNT;
ctrl->f.TiristorReady = 1; // Устанавливаем флаг готовности тиристора к включению
ctrl->angle.start_delay_tick = TIMER->CNT; // Запоминаем текущее значение счётчика таймера
}
/**
* @brief Включение тиристора путём установки GPIO в состояние открытия
* @param ctrl Указатель на структуру управления тиристором
*/
void tiristor_enable(TiristorControl_t *ctrl)
{
//HAL_GPIO_WritePin(ctrl->gpiox, ctrl->gpio_pin, GPIO_TIRISTOR_OPEN);
ctrl->gpiox->ODR |= ctrl->gpio_pin;
ctrl->f.TiristorIsEnable = 1;
// Открываем тиристор, установив соответствующий пин в высокое состояние
ctrl->gpiox->ODR |= ctrl->gpio_pin;
ctrl->f.TiristorIsEnable = 1; // Устанавливаем флаг, что тиристор включен
}
/**
* @brief Выключение тиристора путём установки GPIO в состояние закрытия
* @param ctrl Указатель на структуру управления тиристором
*/
void tiristor_disable(TiristorControl_t *ctrl)
{
ctrl->gpiox->ODR &= ~ctrl->gpio_pin;
//HAL_GPIO_WritePin(ctrl->gpiox, ctrl->gpio_pin, GPIO_TIRISTOR_CLOSE);
ctrl->f.TiristorIsEnable = 0;
// Закрываем тиристор, сбросив соответствующий пин в низкое состояние
ctrl->gpiox->ODR &= ~ctrl->gpio_pin;
ctrl->f.TiristorIsEnable = 0; // Снимаем флаг включения тиристора
}
/**
* @brief Сброс значения задержки угла открытия тиристора к начальному в зависимости от направления
* @param ctrl Указатель на структуру управления тиристором
*/
void tiristor_angle_reset(TiristorControl_t *ctrl)
{
if(ctrl->angle.Init->direction)
ctrl->angle.delay_us = ctrl->angle.Init->delay_min_us;
else
ctrl->angle.delay_us = ctrl->angle.Init->delay_max_us;
// В зависимости от направления устанавливаем задержку на минимальное или максимальное значение
if(ctrl->angle.Init->direction)
ctrl->angle.delay_us = ctrl->angle.Init->delay_min_us;
else
ctrl->angle.delay_us = ctrl->angle.Init->delay_max_us;
}
/**
* @brief Инициализация структуры управления тиристором, установка GPIO и сброс угла открытия
* @param ctrl Указатель на структуру управления тиристором
* @param gpiox Указатель на порт GPIO
* @param gpio_pin Номер пина GPIO
*/
void tiristor_init(TiristorControl_t *ctrl, GPIO_TypeDef *gpiox, uint32_t gpio_pin)
{
ctrl->gpiox = gpiox;
ctrl->gpio_pin = gpio_pin;
tiristor_angle_reset(ctrl);
}
ctrl->gpiox = gpiox; // Сохраняем порт GPIO
ctrl->gpio_pin = gpio_pin; // Сохраняем номер пина GPIO
tiristor_angle_reset(ctrl); // Сбрасываем угол открытия тиристора на начальное значение
}

View File

@ -5,55 +5,108 @@
#include "tim.h"
#define GPIO_TIRISTOR_OPEN GPIO_PIN_SET
#define GPIO_TIRISTOR_CLOSE GPIO_PIN_RESET
#define GPIO_TIRISTOR_OPEN GPIO_PIN_SET /**< Состояние GPIO для открытия тиристора */
#define GPIO_TIRISTOR_CLOSE GPIO_PIN_RESET /**< Состояние GPIO для закрытия тиристора */
#define TIMER TIM2 /**< Таймер, используемый для управления тиристором */
#define TIMER TIM2
/**
* @brief Флаги состояния управления тиристором
*/
typedef struct
{
unsigned EnableTiristor:1;
unsigned TiristorIsEnable:1;
unsigned TiristorReady:1;
}TiristorControlFlags;
typedef struct
{
uint32_t delay_min_us; // Минимальная задержка (максимальное открытие тиристора)
uint32_t delay_max_us; // Начальная задержка (практически закрыт)
uint32_t delay_step_us; // Шаг уменьшения задержки
uint32_t sample_time_ms; // Интервал между шагами (например, 200 мс)
unsigned direction; // Направление разгон/торможение
}AngleInit_t;
unsigned EnableTiristor:1; /**< Флаг разрешения управления тиристором */
unsigned TiristorIsEnable:1; /**< Флаг, указывающий, что тиристор включен */
unsigned TiristorReady:1; /**< Флаг готовности тиристора к работе */
} TiristorControlFlags;
/**
* @brief Параметры инициализации угла открытия тиристора
*/
typedef struct
{
AngleInit_t *Init;
uint32_t last_update_ms; // Время последнего обновления
uint32_t delay_us; // Текущая задержка (в микросекундах)
uint16_t start_delay_tick;
}TiristorAngleControl_t;
uint32_t delay_min_us; /**< Минимальная задержка (микросекунды), соответствует максимальному открытию тиристора */
uint32_t delay_max_us; /**< Начальная задержка (микросекунды), соответствует практически закрытому тиристору */
uint32_t delay_step_us; /**< Шаг уменьшения задержки (микросекунды) */
uint32_t sample_time_ms; /**< Интервал времени между шагами регулировки (миллисекунды) */
unsigned direction; /**< Направление регулировки: разгон (увеличение открытого угла) или торможение */
} AngleInit_t;
/**
* @brief Структура управления углом открытия тиристора
*/
typedef struct
{
AngleInit_t *Init; /**< Указатель на структуру параметров инициализации угла */
uint32_t last_update_ms; /**< Время последнего обновления (миллисекунды) */
uint32_t delay_us; /**< Текущая задержка (микросекунды) */
uint16_t start_delay_tick; /**< Значение таймера при старте задержки */
} TiristorAngleControl_t;
typedef struct TiristorControl_t TiristorControl_t;
/**
* @brief Основная структура управления тиристором
*/
struct TiristorControl_t
{
TiristorControlFlags f;
TiristorAngleControl_t angle;
GPIO_TypeDef *gpiox;
uint32_t gpio_pin;
uint32_t open_time;
uint32_t enable_start_tick;
void (*start_delay)(TiristorControl_t *ctrl); // Указатель на функцию запуска задержки включения
TiristorControlFlags f; /**< Флаги состояния тиристора */
TiristorAngleControl_t angle; /**< Управление углом открытия */
GPIO_TypeDef *gpiox; /**< Порт GPIO для управления тиристором */
uint32_t gpio_pin; /**< Номер пина GPIO */
uint32_t open_time; /**< Время открытия тиристора */
uint32_t enable_start_tick; /**< Время включения тиристора по таймеру */
};
/**
* @brief Управление состоянием тиристора (включение/выключение)
* @param ctrl Указатель на структуру управления тиристором
*/
void tiristor_control(TiristorControl_t *ctrl);
/**
* @brief Обновление угла открытия тиристора согласно параметрам
* @param angle Указатель на структуру управления углом тиристора
*/
void tiristor_angle_update(TiristorAngleControl_t *angle);
/**
* @brief Контроль угла открытия тиристора, включая обновление состояния
* @param ctrl Указатель на структуру управления тиристором
*/
void tiristor_angle_control(TiristorControl_t *ctrl);
/**
* @brief Запуск задержки открытия тиристора
* @param ctrl Указатель на структуру управления тиристором
*/
void tiristor_start_angle_delay(TiristorControl_t* ctrl);
/**
* @brief Сброс угла открытия тиристора к начальному состоянию
* @param ctrl Указатель на структуру управления тиристором
*/
void tiristor_angle_reset(TiristorControl_t *ctrl);
/**
* @brief Включение тиристора
* @param ctrl Указатель на структуру управления тиристором
*/
void tiristor_enable(TiristorControl_t *ctrl);
/**
* @brief Выключение тиристора
* @param ctrl Указатель на структуру управления тиристором
*/
void tiristor_disable(TiristorControl_t *ctrl);
/**
* @brief Инициализация структуры управления тиристором
* @param ctrl Указатель на структуру управления тиристором
* @param gpiox Указатель на GPIO порт
* @param gpio_pin Номер GPIO пина
*/
void tiristor_init(TiristorControl_t *ctrl, GPIO_TypeDef *gpiox, uint32_t gpio_pin);
#endif //__TIRISTORS_H
#endif //__TIRISTORS_H

View File

@ -1,207 +1,299 @@
#include "upp.h"
Phase_t phase_A;
Phase_t phase_B;
Phase_t phase_C;
UPP_Control_t Upp;
// главная функция
void upp_main(void)
Phase_t phase_A; /*< Фаза управления тиристорами A */
Phase_t phase_B; /*< Фаза управления тиристорами B */
Phase_t phase_C; /*< Фаза управления тиристорами C */
UPP_Control_t Upp; /*< Структура управления УПП */
/**
* @brief Главная функция управления УПП
*
* @details Выполняет основную логику управления пускателем:
* инициализация углов, безопасный запуск,
* проверка флагов остановки/отключения,
* управление фазами и тиристорами.
*/void upp_main(void)
{
// Проверяем необходимость обновления параметров угла управления тиристорами
if(GetAngleInit(&Upp.angleInit))
{
// Если параметры изменились, сбрасываем углы для всех фаз
tiristor_angle_reset(&phase_A.ctrl);
tiristor_angle_reset(&phase_B.ctrl);
tiristor_angle_reset(&phase_C.ctrl);
}
// безопасный запуск
// Выполняем безопасный запуск (обработка изменения направления и стартового состояния)
upp_safe_go();
// останавливаем УПП (убираем питание с выхода упп) если выставлен флаг
// Если установлен флаг принудительной остановки, выключаем питание УПП и подключаем выход
if(Upp.ForceStop)
{
Upp.Go = 0;
connect_upp();
return;
Upp.Go = 0; // Останавливаем работу
connect_upp(); // Подключаем УПП (прямое питание)
return; // Выход из функции, дальнейшая логика не выполняется
}
// Если установлен флаг принудительного отключения, выставляем готовность тиристоров и отключаем УПП
if(Upp.ForceDisconnect)
{
phase_A.ctrl.f.TiristorReady = 1;
phase_B.ctrl.f.TiristorReady = 1;
phase_C.ctrl.f.TiristorReady = 1;
Upp.Go = 0;
disconnect_upp();
Upp.Go = 0; // Останавливаем работу
disconnect_upp(); // Отключаем УПП (снимаем питание с выхода)
return;
}
// отключаем упп если выставлен флаг
// Если установлен флаг плавного отключения УПП, готовим тиристоры и отключаем УПП
if(Upp.GoDisconnect)
{
phase_A.ctrl.f.TiristorReady = 1;
phase_B.ctrl.f.TiristorReady = 1;
phase_C.ctrl.f.TiristorReady = 1;
Upp.Go = 0;
disconnect_upp();
}
// останавливаем упп если выставлен флаг
// Если установлен флаг остановки, останавливаем работу и подключаем УПП (прямое питание)
if(Upp.GoStop)
{
Upp.Go = 0;
connect_upp();
}
// Если в режиме подготовки (запуска)
if(Upp.Prepare)
{
// Если УПП в состоянии отключения, подключаем его (готовим к работе)
if(Upp.Disconnected)
{
connect_upp();
}
// Обрабатываем каждую фазу (детектирование нуля, управление углом тиристора)
upp_phase_routine(&phase_A);
upp_phase_routine(&phase_B);
upp_phase_routine(&phase_C);
}
// Если работа разрешена (флаг Go)
if(Upp.Go)
{
// Если всё ещё в подготовке, проверяем готовность тиристоров
if(Upp.Prepare)
{
// Если все тиристоры готовы — снимаем флаг подготовки и продолжаем работу
if(phase_A.ctrl.f.TiristorReady && phase_B.ctrl.f.TiristorReady && phase_C.ctrl.f.TiristorReady)
{
Upp.Prepare = 0;
}
else
{
// Если хоть один тиристор не готов — выходим, не продолжая управление
return;
}
}
// Если во время работы произошло отключение УПП — ставим флаг принудительной остановки
if(Upp.Disconnected)
{
Upp.ForceStop = 1;
return;
}
// если все фазы дошли до минимума в режиме разгона, то выставляем флаг на отключение упп (прямая подача питания на двигатель)
// Проверяем условие достижения минимального угла (минимальная задержка) во время запуска (direction == 0)
// Это значит, что тиристоры открыты максимально рано — можно перейти на прямое питание двигателя
if( (phase_A.ctrl.angle.delay_us == phase_A.ctrl.angle.Init->delay_min_us) &&
(phase_B.ctrl.angle.delay_us == phase_B.ctrl.angle.Init->delay_min_us) &&
(phase_C.ctrl.angle.delay_us == phase_C.ctrl.angle.Init->delay_min_us) && (Upp.angleInit.direction == 0))
{
Upp.GoDisconnect = 1;
Upp.GoDisconnect = 1; // Флаг для отключения УПП и подачи питания напрямую
}
else
{
Upp.GoDisconnect = 0;
}
// если все фазы дошли до максимума в режиме торможения, то выставляем флаг на остановку упп (выключения питания на двигателе)
// Проверяем условие достижения максимального угла (максимальная задержка) во время торможения (direction == 1)
// Это значит, что тиристоры максимально закрыты — нужно остановить питание двигателя
if( (phase_A.ctrl.angle.delay_us == phase_A.ctrl.angle.Init->delay_max_us) &&
(phase_B.ctrl.angle.delay_us == phase_B.ctrl.angle.Init->delay_max_us) &&
(phase_C.ctrl.angle.delay_us == phase_C.ctrl.angle.Init->delay_max_us) && (Upp.angleInit.direction == 1))
{
Upp.GoStop = 1;
Upp.GoStop = 1; // Флаг для остановки УПП и отключения питания
}
else
{
Upp.GoStop = 0;
}
// Продолжаем обработку фаз — обновляем состояние и проверяем условия управления тиристорами
upp_phase_routine(&phase_A);
upp_phase_routine(&phase_B);
upp_phase_routine(&phase_C);
// Управляем тиристорами каждой фазы с помощью функций контроля угла и самого тиристора
upp_phase_control(&phase_A);
upp_phase_control(&phase_B);
upp_phase_control(&phase_C);
}
else
{
// Если флаг Go не установлен, сбрасываем углы управления тиристорами для всех фаз
tiristor_angle_reset(&phase_A.ctrl);
tiristor_angle_reset(&phase_B.ctrl);
tiristor_angle_reset(&phase_C.ctrl);
}
}
/**
* @brief Функция безопасного запуска УПП
*
* @details Следит за изменениями флага GoSafe и запускает или останавливает пускатель,
* сбрасывая угол задержки тиристоров в зависимости от направления.
*/
void upp_safe_go(void)
{
static int prev_gosafe;
static int prev_gosafe; // Статическая переменная для хранения предыдущего значения флага GoSafe
// Если текущее значение GoSafe больше предыдущего — это сигнал о старте в режиме запуска (направление 0)
if(Upp.GoSafe > prev_gosafe)
{
Upp.angleInit.direction = 0;
Upp.Prepare = 1;
Upp.Go = 1;
Upp.angleInit.direction = 0; // Устанавливаем направление пуска (разгон)
Upp.Prepare = 1; // Включаем режим подготовки
Upp.Go = 1; // Включаем основной флаг запуска работы УПП
// Сбрасываем углы управления тиристорами для всех фаз — начинаем с начального состояния
tiristor_angle_reset(&phase_A.ctrl);
tiristor_angle_reset(&phase_B.ctrl);
tiristor_angle_reset(&phase_C.ctrl);
}
// Если текущее значение GoSafe меньше предыдущего — это сигнал о старте в режиме торможения (направление 1)
else if (Upp.GoSafe < prev_gosafe)
{
Upp.angleInit.direction = 1;
Upp.Prepare = 1;
Upp.Go = 1;
Upp.angleInit.direction = 1; // Устанавливаем направление торможения
Upp.Prepare = 1; // Включаем режим подготовки
Upp.Go = 1; // Включаем основной флаг запуска работы УПП
// Сбрасываем углы управления тиристорами для всех фаз — начинаем с начального состояния
tiristor_angle_reset(&phase_A.ctrl);
tiristor_angle_reset(&phase_B.ctrl);
tiristor_angle_reset(&phase_C.ctrl);
}
// Обновляем сохранённое предыдущее значение GoSafe для отслеживания изменений в следующем вызове
prev_gosafe = Upp.GoSafe;
}
/**
* @brief Отключение питания УПП (разрыв всех фаз)
*
* @details Если тиристор готов, вызывает макросы отключения фаз,
* после чего выставляет соответствующие флаги состояния.
*/
void disconnect_upp(void)
{
if(phase_A.ctrl.f.TiristorReady)
{
disconnect_phase(&phase_A);
}
if(phase_B.ctrl.f.TiristorReady)
{
disconnect_phase(&phase_B);
}
if(phase_C.ctrl.f.TiristorReady)
{
disconnect_phase(&phase_C);
}
if(phase_A.disconnect.Disconnected && phase_B.disconnect.Disconnected && phase_C.disconnect.Disconnected)
{
Upp.Disconnected = 1;
Upp.GoDisconnect = 0;
Upp.Go = 0;
}
// Если тиристоры фазы A открыты, подключаем фазу напрямую
if(phase_A.ctrl.f.TiristorReady)
{
disconnect_phase(&phase_A);
}
// Аналогично для фазы B
if(phase_B.ctrl.f.TiristorReady)
{
disconnect_phase(&phase_B);
}
// Аналогично для фазы C
if(phase_C.ctrl.f.TiristorReady)
{
disconnect_phase(&phase_C);
}
// Если УПП на всех трех фазах отключены
if(phase_A.disconnect.Disconnected && phase_B.disconnect.Disconnected && phase_C.disconnect.Disconnected)
{
Upp.Disconnected = 1; // Устанавливаем флаг, что УПП полностью отключена
Upp.GoDisconnect = 0; // Сбрасываем флаг запроса на отключение
Upp.Go = 0; // Прекращаем работу УПП
}
}
/**
* @brief Подключение питания УПП (соединение всех фаз)
*
* @details Вызывает отключение тиристоров и макросы подключения фаз,
* сбрасывает флаг отключения.
*/
void connect_upp(void)
{
tiristor_disable(&phase_A.ctrl);
tiristor_disable(&phase_B.ctrl);
tiristor_disable(&phase_C.ctrl);
// Отключаем управление тиристорами для всех фаз
tiristor_disable(&phase_A.ctrl);
tiristor_disable(&phase_B.ctrl);
tiristor_disable(&phase_C.ctrl);
connect_phase(&phase_A);
connect_phase(&phase_B);
connect_phase(&phase_C);
Upp.Disconnected = 0;
// Подключаем УПП к каждой фазе)
connect_phase(&phase_A);
connect_phase(&phase_B);
connect_phase(&phase_C);
// Сбрасываем флаг, указывающий на то, что УПП было отключено
Upp.Disconnected = 0;
}
/**
* @brief Управление одной фазой УПП
* @param phase Указатель на структуру фазы Phase_t
*
* @details Контролирует угол и включает/отключает тиристор для данной фазы.
*/
void upp_phase_control(Phase_t *phase)
{
tiristor_angle_control(&phase->ctrl);
tiristor_control(&phase->ctrl);
}
/**
* @brief Обработка фазы при каждом нулевом переходе синусоиды
* @param phase Указатель на структуру фазы Phase_t
*
* @details Обновляет состояние детектора нулевого перехода,
* запускает задержку угла тиристора,
* отключает тиристор, если он был включен.
*/
void upp_phase_routine(Phase_t *phase)
{
// Обновляем детектор нулевого перехода по текущему состоянию входного сигнала
zero_cross_update(&phase->zc_detector);
// Если обнаружен нулевой переход (синусоида пересекла 0)
if(is_zero_cross(&phase->zc_detector))
{
// Запускаем отсчёт задержки до открытия тиристора (по углу)
tiristor_start_angle_delay(&phase->ctrl);
// Если тиристор был включён в предыдущем полупериоде — отключаем его
if (phase->ctrl.f.TiristorIsEnable)
tiristor_disable(&phase->ctrl);
}
}
/**
* @brief Расчёт параметров угла запуска тиристора
* @param angle Указатель на структуру AngleInit_t для записи параметров
* @return int 1, если произошли изменения параметров, иначе 0
*
* @details Проверяет изменения в параметрах управления и при необходимости
* пересчитывает максимальные и минимальные задержки, шаг изменения угла,
* а также изменяет прескалер таймера.
*/
int GetAngleInit(AngleInit_t *angle)
{
static float sine_freq_old = 0;
@ -209,7 +301,7 @@ int GetAngleInit(AngleInit_t *angle)
static float max_duty_old = 0, min_duty_old = 0; // Задаются в процентах
int update = 0;
// Проверка, изменились ли параметры: частота, скважности
if( (Upp.sine_freq != sine_freq_old) &&
(Upp.max_duty != max_duty_old) &&
(Upp.min_duty != min_duty_old) )
@ -218,64 +310,73 @@ int GetAngleInit(AngleInit_t *angle)
min_duty_old = Upp.min_duty;
max_duty_old = Upp.max_duty;
sine_freq_old = Upp.sine_freq;
}
// Проверка, изменились ли длительность
if(Upp.Duration != Duration_old)
{
{
update = 1;
Duration_old = Upp.Duration;
}
if(update)
{
// max/min duty
uint32_t half_period_us = (500000.0f / Upp.sine_freq) - 1000; // полупериод в мкс - время открытия тиристоры
// Расчёт длительности полупериода в микросекундах (с учётом вычета резерва на открытие тиристора)
uint32_t half_period_us = (500000.0f / Upp.sine_freq) - 1000;
// Расчёт максимальной и минимальной задержки (в мкс) по процентам скважности
angle->delay_max_us = (uint32_t)(Upp.max_duty * half_period_us);
angle->delay_min_us = (uint32_t)(Upp.min_duty * half_period_us);
// Проверка, помещаются ли значения задержек в 16-битный таймер
if((angle->delay_max_us > 0xFFFF) || (angle->delay_min_us > 0xFFFF))
{
// увеличение прескалера в 10 раз (с 1мкс до 10мкс, с 7Гц до 0.7Гц)
// Если нет — увеличиваем прескалер в 10 раз (точность 10 мкс)
angle->delay_max_us /= 10;
angle->delay_min_us /= 10;
TIMER->PSC = 719;
if((angle->delay_max_us > 0xFFFF) || (angle->delay_min_us > 0xFFFF))
{
// увеличение прескалера в 10 раз (с 10мкс до 0,1мс, с 0.7Гц до 0.07 Гц)
// Если всё ещё не помещается — ещё в 10 раз (точность 0.1 мс)
angle->delay_max_us /= 10;
angle->delay_min_us /= 10;
TIMER->PSC = 7299;
if ((angle->delay_max_us > 0xFFFF) || (angle->delay_min_us > 0xFFFF))
{
// если все еще переполнение то выключаем всё
// Если даже при этом переполнение — аварийная остановка
Upp.ForceStop = 1;
}
}
}
else
{
// Задержки помещаются — устанавливаем стандартный прескалер (1 мкс)
TIMER->PSC = 71;
}
// duration
// Перевод длительности разгона/торможения из секунд в миллисекунды
float duration_ms = Duration_old * 1000.0f;
uint32_t steps = duration_ms / angle->sample_time_ms;
if (steps == 0) steps = 1;
// Вычисление шага изменения задержки на каждом шаге
if (angle->delay_max_us > angle->delay_min_us)
angle->delay_step_us = (angle->delay_max_us - angle->delay_min_us) / steps;
else
angle->delay_step_us = 0;
}
return update;
}
/**
* @brief Инициализация УПП и связанных структур
*
* @details Настраивает параметры управления, GPIO для фаз,
* инициализирует тиристоры, запускает таймер и настраивает детектор нулевого перехода.
*/
void upp_init(void)
{
Upp.max_duty = 0.9;

View File

@ -9,46 +9,69 @@
#include "zero_cross.h"
#include "tiristor.h"
#define hadc hadc1
#define ADC_INITIAL_ZERO_LEVEL 2048
/**
* @brief Определение используемого ADC
*/
#define hadc hadc1
#define disconnect_phase(_ph_) { (_ph_)->disconnect.gpiox->ODR |= (_ph_)->disconnect.gpio_pin; (_ph_)->disconnect.Disconnected = 1;}
#define connect_phase(_ph_) { (_ph_)->disconnect.gpiox->ODR &= ~(_ph_)->disconnect.gpio_pin; (_ph_)->disconnect.Disconnected = 0;}
/**
* @brief Начальный уровень отсчёта для определения нуля в ADC (обычно середина диапазона)
*/
#define ADC_INITIAL_ZERO_LEVEL 2048
/**
* @brief Макрос для разрыва (отключения) фазы устанавливает GPIO в высокий уровень и флаг Disconnected
* @param _ph_ Указатель на структуру фазы Phase_t
*/
#define disconnect_phase(_ph_) { (_ph_)->disconnect.gpiox->ODR |= (_ph_)->disconnect.gpio_pin; (_ph_)->disconnect.Disconnected = 1;}
/**
* @brief Макрос для подключения фазы сбрасывает GPIO в низкий уровень и флаг Disconnected
* @param _ph_ Указатель на структуру фазы Phase_t
*/
#define connect_phase(_ph_) { (_ph_)->disconnect.gpiox->ODR &= ~(_ph_)->disconnect.gpio_pin; (_ph_)->disconnect.Disconnected = 0;}
/**
* @struct Phase_t
* @brief Структура, описывающая одну фазу с состоянием тиристора и детектом нуля
*/
typedef struct
{
struct
{
unsigned Disconnected:1;
GPIO_TypeDef *gpiox;
uint32_t gpio_pin;
}disconnect;
ZeroCrossDetector_t zc_detector;
TiristorControl_t ctrl;
} Phase_t; // структура для фазы
struct
{
unsigned Disconnected:1; /**< Флаг разрыва фазы */
GPIO_TypeDef *gpiox; /**< Порт GPIO для разрыва */
uint32_t gpio_pin; /**< Пин GPIO для разрыва */
}disconnect;
ZeroCrossDetector_t zc_detector; /**< Детектор пересечения нуля */
TiristorControl_t ctrl; /**< Управление тиристором */
} Phase_t;
/**
* @struct UPP_Control_t
* @brief Основная структура управления устройством плавного пуска
*/
typedef struct
{
unsigned GoSafe:1;
unsigned Go:1;
unsigned GoStop:1;
unsigned Prepare:1;
unsigned Disconnected:1;
unsigned GoDisconnect:1;
unsigned ForceStop:1;
unsigned ForceDisconnect:1;
unsigned PreGoDone:1;
float Duration;
float sine_freq;
float max_duty;
float min_duty;
AngleInit_t angleInit;
}UPP_Control_t;
unsigned GoSafe:1; /**< Флаг безопасного запуска */
unsigned Go:1; /**< Флаг запуска */
unsigned GoStop:1; /**< Флаг остановки */
unsigned Prepare:1; /**< Флаг подготовки */
unsigned Disconnected:1; /**< Флаг разрыва */
unsigned GoDisconnect:1; /**< Флаг отключения */
unsigned ForceStop:1; /**< Флаг форсированной остановки */
unsigned ForceDisconnect:1; /**< Флаг форсированного отключения */
unsigned PreGoDone:1; /**< Флаг завершения подготовки */
float Duration; /**< Время нарастания и спада напряжение через УПП */
float sine_freq; /**< Частота сети */
float max_duty; /**< Максимальная скважность угла открытия */
float min_duty; /**< Минимальная скважность угла открытия */
AngleInit_t angleInit; /**< Настройки угла открытия тиристора */
} UPP_Control_t;
extern Phase_t phase_A;
@ -56,14 +79,28 @@ extern Phase_t phase_B;
extern Phase_t phase_C;
extern UPP_Control_t Upp;
/** Основной цикл работы устройства плавного пуска */
void upp_main(void);
/** Выполнение безопасного запуска устройства */
void upp_safe_go(void);
/** Отключение устройства плавного пуска (разрыв фаз) */
void disconnect_upp(void);
/** Подключение устройства плавного пуска (восстановление фаз) */
void connect_upp(void);
/** Выполнение обработки одной фазы в цикле */
void upp_phase_routine(Phase_t *phase);
/** Управление фазой с контролем тиристора и нуля */
void upp_phase_control(Phase_t *phase);
/** Получение настроек угла открытия тиристора */
int GetAngleInit(AngleInit_t *angle);
/** Инициализация устройства плавного пуска */
void upp_init(void);
#endif //__UPP_H
#endif //__UPP_H

View File

@ -2,55 +2,92 @@
ADCFilter_t AdcFilter;
/**
* @brief Инициализация структуры детектора перехода через ноль
* @param zc Указатель на структуру ZeroCrossDetector_t
* @param zeroLevel Значение уровня АЦП, соответствующее нулю (mid-scale)
*/
void zero_cross_Init(ZeroCrossDetector_t *zc, uint16_t zeroLevel)
{
zc->lastSample = 0;
zc->f.ZeroCrossDetected = 0;
zc->zeroLevel = zeroLevel;
// Обнуляем последнее измеренное значение сдвига относительно нуля
zc->lastSample = 0;
// Сбрасываем флаг обнаружения перехода через ноль
zc->f.ZeroCrossDetected = 0;
// Запоминаем уровень, соответствующий нулю (обычно mid-scale АЦП)
zc->zeroLevel = zeroLevel;
}
// апдейт флага зерокросс детектед
/**
* @brief Обновление флага перехода через ноль
* @param zc Указатель на структуру ZeroCrossDetector_t
* @details Если аппаратный детектор включен, просто переносим флаг EXTI.
* Иначе анализируем разницу между текущим и предыдущим значением АЦП,
* чтобы определить, произошёл ли переход через ноль.
*/
void zero_cross_update(ZeroCrossDetector_t *zc)
{
#ifdef HARDWARE_ZERO_CROSS_DETECT
zc->f.ZeroCrossDetected = zc->f.EXTIZeroCrossDetected;
// Используем флаг аппаратного прерывания EXTI для установки флага перехода через ноль
zc->f.ZeroCrossDetected = zc->f.EXTIZeroCrossDetected;
#else
uint16_t adcValue;
if(adc_is_data_updated(&zc->AdcFilter))
{
adcValue = adc_read_data(&zc->AdcFilter);
}
else
{
return;
}
zc->currSample = (int16_t)adcValue - (int16_t)zc->zeroLevel;
uint16_t adcValue;
if ((zc->lastSample < 0 && zc->currSample >= 0) ||
(zc->lastSample > 0 && zc->currSample <= 0))
{
zc->f.ZeroCrossDetected = 1;
}
// Проверяем, обновились ли данные АЦП (фильтр)
if(adc_is_data_updated(&zc->AdcFilter))
{
adcValue = adc_read_data(&zc->AdcFilter);
}
else
{
// Нет новых данных — выход из функции
return;
}
zc->lastSample = zc->currSample;
// Вычисляем смещение текущей выборки относительно нуля
zc->currSample = (int16_t)adcValue - (int16_t)zc->zeroLevel;
// Проверяем, произошёл ли переход через ноль между предыдущей и текущей выборками
if ((zc->lastSample < 0 && zc->currSample >= 0) ||
(zc->lastSample > 0 && zc->currSample <= 0))
{
// Устанавливаем флаг обнаружения перехода через ноль
zc->f.ZeroCrossDetected = 1;
}
// Сохраняем текущее значение для следующего сравнения
zc->lastSample = zc->currSample;
#endif
}
/**
* @brief Проверка наличия перехода через ноль и сброс флагов
* @param zc Указатель на структуру ZeroCrossDetector_t
* @return int 1 переход через ноль обнаружен, 0 нет
* @details Если переход обнаружен, сбрасываем флаги и возвращаем 1,
* иначе возвращаем 0.
*/
int is_zero_cross(ZeroCrossDetector_t *zc)
{
if(zc->f.ZeroCrossDetected)
{
zc->f.ZeroCrossDetected = 0;
zc->f.EXTIZeroCrossDetected = 0;
return 1;
}
return 0;
if(zc->f.ZeroCrossDetected)
{
// Сброс флагов после обнаружения
zc->f.ZeroCrossDetected = 0;
zc->f.EXTIZeroCrossDetected = 0;
return 1;
}
return 0;
}
#ifdef HARDWARE_ZERO_CROSS_DETECT
/**
* @brief Обработчик прерывания EXTI для аппаратного детектора перехода через ноль
* @param zc Указатель на структуру ZeroCrossDetector_t
* @details Устанавливает флаг аппаратного перехода через ноль.
*/
void zero_cross_update_EXTI(ZeroCrossDetector_t *zc)
{
zc->f.EXTIZeroCrossDetected = 1;
zc->f.EXTIZeroCrossDetected = 1;
}
#endif
#endif

View File

@ -4,38 +4,43 @@
#include "main.h"
#include "adc_filter.h"
#define hadc hadc1
#define hadc hadc1
#define HARDWARE_ZERO_CROSS_DETECT // аппаратный детект zero cross (альтернатива — считывание через АЦП)
#define HARDWARE_ZERO_CROSS_DETECT // аппаратный детект зеро кросс (альтернатива - считывая через ацп)
/**
* @brief Флаги состояния детектора нуля
*/
typedef struct
{
unsigned WaitForZeroCrossDetected : 1;
unsigned WaitForZeroCrossDetected : 1; /**< Ожидание обнаружения перехода через ноль */
#ifdef HARDWARE_ZERO_CROSS_DETECT
unsigned EXTIZeroCrossDetected:1;
unsigned EXTIZeroCrossDetected : 1; /**< Флаг обнаружения нуля аппаратным прерыванием EXTI */
#endif
unsigned ZeroCrossDetected:1;
}ZeroCrossFlags;
unsigned ZeroCrossDetected : 1; /**< Флаг обнаружения перехода через ноль */
} ZeroCrossFlags;
/**
* @brief Структура детектора перехода через ноль
*/
typedef struct
{
ZeroCrossFlags f;
int currSample;
int16_t lastSample; // предыдущее значение (относительно нуля)
uint16_t zeroLevel; // уровень, соответствующий "нулю", обычно mid-scale
ADCFilter_t AdcFilter;
ZeroCrossFlags f; /**< Флаги состояния детектора */
int currSample; /**< Текущее значение выборки */
int16_t lastSample; /**< Предыдущее значение выборки (относительно нуля) */
uint16_t zeroLevel; /**< Уровень, соответствующий "нулю", обычно mid-scale АЦП */
ADCFilter_t AdcFilter; /**< Фильтр АЦП для сглаживания входных данных */
} ZeroCrossDetector_t;
/** Инициализация структуры детектора перехода через ноль */
void zero_cross_Init(ZeroCrossDetector_t *zc, uint16_t zeroLevel);
/** Обновление состояния детектора перехода через ноль (через АЦП или аппаратный метод) */
void zero_cross_update(ZeroCrossDetector_t *zc);
/** Проверка, был ли обнаружен переход через ноль */
int is_zero_cross(ZeroCrossDetector_t *zc);
#ifdef HARDWARE_ZERO_CROSS_DETECT
/** Обновление состояния детектора перехода через ноль при аппаратном прерывании EXTI*/
void zero_cross_update_EXTI(ZeroCrossDetector_t *zc);
#endif
#endif //__ZERO_CROSS_H
#endif //__ZERO_CROSS_H

File diff suppressed because one or more lines are too long

View File

@ -128,7 +128,24 @@
<Name>-U-O142 -O2254 -S0 -C0 -N00("ARM CoreSight SW-DP") -D00(2BA01477) -L00(0) -TO18 -TC10000000 -TP21 -TDS8007 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -FO7 -FD20000000 -FC800 -FN1 -FF0STM32F10x_128 -FS08000000 -FL08000 -FP0($$Device:STM32F103C6$Flash\STM32F10x_128.FLM)</Name>
</SetRegEntry>
</TargetDriverDllRegistry>
<Breakpoint/>
<Breakpoint>
<Bp>
<Number>0</Number>
<Type>0</Type>
<LineNumber>62</LineNumber>
<EnabledFlag>1</EnabledFlag>
<Address>0</Address>
<ByteObject>0</ByteObject>
<HtxType>0</HtxType>
<ManyObjects>0</ManyObjects>
<SizeOfObject>0</SizeOfObject>
<BreakByAccess>0</BreakByAccess>
<BreakIfRCount>0</BreakIfRCount>
<Filename>..\Core\upp\tiristor.c</Filename>
<ExecCommand></ExecCommand>
<Expression></Expression>
</Bp>
</Breakpoint>
<Tracepoint>
<THDelay>0</THDelay>
</Tracepoint>