341 lines
11 KiB
C
341 lines
11 KiB
C
/**
|
||
******************************************************************************
|
||
* @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;
|
||
} |