UPP/UPP/Core/PowerMonitor/zero_cross.c
Razvalyaev 3614e8f8c3 Тестирование фильтров с шумным сигналом на АЦП
И на рабочем компе mexw64 не блокируется...
2025-11-12 16:21:18 +03:00

341 lines
11 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
******************************************************************************
* @file zero_cross.c
* @brief Модуль фиксирующий переход через ноль
******************************************************************************
* @details
******************************************************************************/
#include "zero_cross.h"
#include <string.h>
/**
* @brief Инициализация детектора нуля с индивидуальными настройками.
* @param zc Указатель на хендл детектора нуля
* @param num_channels Количество каналов для этого хендла
* @param hysteresis Гистерезис для избежания дребезга
* @param debounce_samples Количество samples для антидребезга
* @return HAL Status.
*/
HAL_StatusTypeDef ZC_Init(ZeroCross_Handle_t *zc, uint8_t num_channels,
float hysteresis, uint8_t debounce_samples)
{
if (zc == NULL || num_channels == 0 || num_channels > ZC_MAX_CHANNELS) {
return HAL_ERROR;
}
// Инициализация структуры
memset(zc, 0, sizeof(ZeroCross_Handle_t));
// Установка параметров хендла
zc->NumChannels = num_channels;
zc->Hysteresis = hysteresis;
zc->DebounceSamples = debounce_samples;
// Инициализация каналов
for (int i = 0; i < num_channels; i++) {
zc->Channel[i].CrossCount = 0;
zc->Channel[i].LastValue = 0.0f;
zc->Channel[i].CurrentValue = 0.0f;
zc->Channel[i].DebounceCounter = 0;
zc->Channel[i].StableState = 0;
zc->Channel[i].LastCrossTime = 0;
zc->Channel[i].Period = 0;
zc->Channel[i].Frequency = 0.0f;
zc->Channel[i].EdgeType = ZC_BOTH_EDGES;
}
zc->f.Initialized = 1;
zc->f.Monitoring = 1;
return HAL_OK;
}
/**
* @brief Настройка канала детектора.
* @param zc Указатель на хендл детектора нуля
* @param channel Номер канала для конфигурации
* @param edgeType Тип детектируемого перехода
* @return HAL Status.
*/
HAL_StatusTypeDef ZC_ConfigChannel(ZeroCross_Handle_t *zc, uint8_t channel,
ZC_EdgeType_t edgeType)
{
if (zc == NULL || !zc->f.Initialized) {
return HAL_ERROR;
}
if (channel >= zc->NumChannels) {
return HAL_ERROR;
}
zc->Channel[channel].EdgeType = edgeType;
// Сброс состояния канала при реконфигурации
zc->Channel[channel].LastValue = 0.0f;
zc->Channel[channel].DebounceCounter = 0;
zc->Channel[channel].StableState = 0;
zc->Channel[channel].LastCrossTime = 0;
zc->Channel[channel].Period = 0;
zc->Channel[channel].Frequency = 0.0f;
zc->Channel[channel].CrossCount = 0;
return HAL_OK;
}
/**
* @brief Основная функция обработки значения канала.
* @param zc Указатель на хендл детектора нуля
* @param channel Номер канала
* @param value Текущее значение сигнала
* @param timestamp Временная метка измерения в микросекундах
*/
void ZC_ProcessChannel(ZeroCross_Handle_t *zc, uint8_t channel, float value, uint32_t timestamp)
{
if (zc == NULL || !zc->f.Initialized || !zc->f.Monitoring) {
return;
}
if (channel >= zc->NumChannels) {
return;
}
ZC_Channel_t *zc_ch = &zc->Channel[channel];
zc_ch->CurrentValue = value;
// Фильтрация дребезга
if(zc_ch->DebounceCounter)
{
zc_ch->DebounceCounter--;
return;
}
// Детектирование rising edge (отрицательное -> положительное)
if ((zc_ch->LastValue <= -zc->Hysteresis) &&
(value >= zc->Hysteresis)) {
if (zc_ch->EdgeType == ZC_RISING_EDGE || zc_ch->EdgeType == ZC_BOTH_EDGES) {
// Переход подтвержден сразу (без дебаунса)
if (zc_ch->LastCrossTime != 0) {
// Расчет периода и частоты
zc_ch->Period = timestamp - zc_ch->LastCrossTime;
if (zc_ch->Period > 0) {
zc_ch->Frequency = 1000000.0f / zc_ch->Period;
}
}
zc_ch->LastCrossTime = timestamp;
zc_ch->CrossCount++;
zc_ch->StableState = 1;
}
}
// Детектирование falling edge (положительное -> отрицательное)
else if ((zc_ch->LastValue >= zc->Hysteresis) &&
(value <= -zc->Hysteresis)) {
if (zc_ch->EdgeType == ZC_FALLING_EDGE || zc_ch->EdgeType == ZC_BOTH_EDGES) {
// Переход подтвержден сразу (без дебаунса)
if (zc_ch->LastCrossTime != 0) {
// Расчет периода и частоты
zc_ch->Period = timestamp - zc_ch->LastCrossTime;
if (zc_ch->Period > 0) {
zc_ch->Frequency = 1000000.0f / zc_ch->Period;
}
}
zc_ch->LastCrossTime = timestamp;
zc_ch->CrossCount++;
zc_ch->StableState = 0;
}
}
// Сохраняем текущее значение для следующей итерации в случае если оно не в мертвой зоне
if((value > zc->Hysteresis) || (value < -zc->Hysteresis))
{
zc_ch->LastValue = value;
}
}
/**
* @brief Пакетная обработка всех каналов.
* @param zc Указатель на хендл детектора нуля
* @param values Массив значений сигналов
* @param timestamp Временная метка измерения
*/
void ZC_ProcessAllChannels(ZeroCross_Handle_t *zc, float values[], uint32_t timestamp)
{
if (zc == NULL || !zc->f.Initialized || !zc->f.Monitoring || values == NULL) {
return;
}
for (int ch = 0; ch < zc->NumChannels; ch++) {
ZC_ProcessChannel(zc, ch, values[ch], timestamp);
}
}
/**
* @brief Получение частоты сигнала.
* @param zc Указатель на хендл детектора нуля
* @param channel Номер канала
* @return Частота в Гц.
*/
float ZC_GetFrequency(ZeroCross_Handle_t *zc, uint8_t channel)
{
if (zc == NULL || !zc->f.Initialized || channel >= zc->NumChannels) {
return 0.0f;
}
return zc->Channel[channel].Frequency;
}
/**
* @brief Получение периода сигнала.
* @param zc Указатель на хендл детектора нуля
* @param channel Номер канала
* @return Период в тактах таймера.
*/
uint32_t ZC_GetPeriod(ZeroCross_Handle_t *zc, uint8_t channel)
{
if (zc == NULL || !zc->f.Initialized || channel >= zc->NumChannels) {
return 0;
}
return zc->Channel[channel].Period;
}
/**
* @brief Получение счетчика переходов.
* @param zc Указатель на хендл детектора нуля
* @param channel Номер канала
* @return Количество переходов.
*/
uint32_t ZC_GetCrossCount(ZeroCross_Handle_t *zc, uint8_t channel)
{
if (zc == NULL || !zc->f.Initialized || channel >= zc->NumChannels) {
return 0;
}
return zc->Channel[channel].CrossCount;
}
/**
* @brief Получение текущего состояния канала.
* @param zc Указатель на хендл детектора нуля
* @param channel Номер канала
* @return Состояние (0 - отрицательное, 1 - положительное).
*/
uint8_t ZC_GetStableState(ZeroCross_Handle_t *zc, uint8_t channel)
{
if (zc == NULL || !zc->f.Initialized || channel >= zc->NumChannels) {
return 0;
}
return zc->Channel[channel].StableState;
}
/**
* @brief Получение текущего значения канала.
* @param zc Указатель на хендл детектора нуля
* @param channel Номер канала
* @return Текущее значение сигнала.
*/
float ZC_GetCurrentValue(ZeroCross_Handle_t *zc, uint8_t channel)
{
if (zc == NULL || !zc->f.Initialized || channel >= zc->NumChannels) {
return 0.0f;
}
return zc->Channel[channel].CurrentValue;
}
/**
* @brief Включение/выключение мониторинга.
* @param zc Указатель на хендл детектора нуля
* @param enable Флаг включения (1 - вкл, 0 - выкл)
*/
void ZC_EnableMonitoring(ZeroCross_Handle_t *zc, uint8_t enable)
{
if (zc == NULL || !zc->f.Initialized) {
return;
}
zc->f.Monitoring = enable ? 1 : 0;
}
/**
* @brief Сброс статистики канала.
* @param zc Указатель на хендл детектора нуля
* @param channel Номер канала
*/
void ZC_Reset(ZeroCross_Handle_t *zc, uint8_t channel)
{
if (zc == NULL || !zc->f.Initialized) {
return;
}
if (channel < zc->NumChannels) {
zc->Channel[channel].LastValue = 0.0f;
zc->Channel[channel].CurrentValue = 0.0f;
zc->Channel[channel].DebounceCounter = 0;
zc->Channel[channel].StableState = 0;
zc->Channel[channel].LastCrossTime = 0;
zc->Channel[channel].Period = 0;
zc->Channel[channel].Frequency = 0.0f;
zc->Channel[channel].CrossCount = 0;
}
else {
// Сброс всех каналов
for (int i = 0; i < zc->NumChannels; i++) {
zc->Channel[i].LastValue = 0.0f;
zc->Channel[i].CurrentValue = 0.0f;
zc->Channel[i].DebounceCounter = 0;
zc->Channel[i].StableState = 0;
zc->Channel[i].LastCrossTime = 0;
zc->Channel[i].Period = 0;
zc->Channel[i].Frequency = 0.0f;
zc->Channel[i].CrossCount = 0;
}
}
}
/**
* @brief Установка гистерезиса для хендла.
* @param zc Указатель на хендл детектора нуля
* @param hysteresis Значение гистерезиса
*/
void ZC_SetHysteresis(ZeroCross_Handle_t *zc, float hysteresis)
{
if (zc == NULL || !zc->f.Initialized) {
return;
}
zc->Hysteresis = hysteresis;
}
/**
* @brief Установка дебаунс samples для хендла.
* @param zc Указатель на хендл детектора нуля
* @param debounce_samples Количество samples для антидребезга
*/
void ZC_SetDebounceSamples(ZeroCross_Handle_t *zc, uint8_t debounce_samples)
{
if (zc == NULL || !zc->f.Initialized) {
return;
}
zc->DebounceSamples = debounce_samples;
}
/**
* @brief Получение количества каналов хендла.
* @param zc Указатель на хендл детектора нуля
* @return Количество каналов.
*/
uint8_t ZC_GetNumChannels(ZeroCross_Handle_t *zc)
{
if (zc == NULL || !zc->f.Initialized) {
return 0;
}
return zc->NumChannels;
}