高精度计算交流信号周期,准确计算零点等待时间,加大历史数据权重,减轻电网波动导致的误判

This commit is contained in:
大石头 2017-03-15 11:32:23 +08:00
parent 3b351272f2
commit a659354fbf
4 changed files with 59 additions and 70 deletions

View File

@ -1,41 +1,29 @@
#include "Kernel\Sys.h"
#include "Kernel\TTime.h"
#include "Device\Port.h"
#include "ACZero.h"
static void ACZeroTask(void *param);
ACZero::ACZero()
{
Time = 0;
//AdjustTime = 2;
_taskid = 0;
Period = 10000;
Count = 0;
Last = 0;
}
ACZero::~ACZero()
{
if (Port.Opened) Close();
Sys.RemoveTask(_taskid);
}
void ACZero::Set(Pin pin)
{
if (pin == P0) return;
Port.HardEvent = true;
Port.Set(pin);
}
void ACZeroTask(void *param)
{
auto port = (ACZero*)param;
if (port)
{
debug_printf("定时检查交流电过零\r\n");
// 需要检测是否有交流电,否则关闭
port->Check();
}
Port.Press.Bind(&ACZero::OnHandler, this);
}
bool ACZero::Open()
@ -44,7 +32,9 @@ bool ACZero::Open()
debug_printf("过零检测引脚探测: ");
if (Check())
// 等一次零点
Sys.Sleep(20);
if (Count > 0)
{
debug_printf("已连接交流电!\r\n");
}
@ -55,8 +45,6 @@ bool ACZero::Open()
return false;
}
_taskid = Sys.AddTask(ACZeroTask, this, 1000, 10000, "过零检测");
return true;
}
@ -65,33 +53,49 @@ void ACZero::Close()
Port.Close();
}
bool ACZero::Check()
void ACZero::OnHandler(InputPort& port, bool down)
{
// 检测下降沿 先去掉低电平 whileio==false
int retry = 200;
while (Port == false && retry-- > 0) Sys.Delay(100);
if (retry <= 0) return false;
if (!down) return;
// 喂狗
Sys.Sleep(1);
auto now = Sys.Ms();
if (Last > 0)
{
// 两次零点
int ms = now - Last;
int us = ms * 1000;
if (Count == 0)
Period = us;
else
Period = (Period * 7 + us) / 8;
}
// 当检测到 高电平结束 就是下降沿的到来
retry = 200;
while (Port == true && retry-- > 0) Sys.Delay(100);
if (retry <= 0) return false;
Last = now;
// 计算10ms为基数的当前延迟
int ms = (int)(Sys.Ms() % 10);
// 折算为需要等待的时间
//ms = 10 - ms;
// 加上对齐纠正时间
//ms += AdjustTime;
//ms %= 10;
Count++;
}
debug_printf("ACZero::Check 交流零点延迟 %dms \r\n", ms);
// 等待下一次零点,需要考虑继电器动作延迟
bool ACZero::Wait(int usDelay) const
{
if (Count == 0 || Last == 0) return false;
// 计算加权平均数
Time = (Time + ms) / 2;
// 计算上一次零点后过去的时间
int ms = Sys.Ms() - Last;
if (ms < 0 && ms > 40) return false;
// 计算下一次零点什么时候到来
int us = Period - ms * 1000;
// 继电器动作延迟
us -= usDelay;
int d = Period;
while (us < 0) us += d;
while (us > d) us -= d;
debug_printf("ACZero::Wait 周期=%dus 等待=%dus \r\n", Period, us);
//Sys.Delay(us);
Time.Delay(us);
return true;
}

View File

@ -8,8 +8,9 @@ class ACZero
{
public:
InputPort Port; // 交流过零检测引脚
int Time; // 10ms为基数的零点延迟时间ms
//int AdjustTime; // 过零检测时间补偿。默认2ms
int Period; // 周期us
uint Count; // 累计次数
UInt64 Last; // 最后一次零点
ACZero();
~ACZero();
@ -18,10 +19,11 @@ public:
bool Open();
void Close();
bool Check();
// 等待下一次零点,需要考虑继电器动作延迟
bool Wait(int usDelay = 0) const;
private:
uint _taskid;
void OnHandler(InputPort& port, bool down);
};
#endif

View File

@ -1,8 +1,6 @@
#include "Button.h"
#include "ACZero.h"
#include "Kernel\TTime.h"
Button::Button()
{
Name = nullptr;
@ -82,31 +80,16 @@ String Button::ToString() const
void Button::SetValue(bool value)
{
int us = 0;
if (Zero)
// 过零检测
if (Zero && Zero->Count > 0)
{
// 计算10ms为基数的当前延迟
int ms = (int)(Sys.Ms() % 10);
// 而零点以10ms为基数在Zero->Time处计算需要等待的时间量
ms = Zero->Time - ms;
us = ms * 1000;
// 打开关闭的延迟时间不同,需要减去这个时间量
us -= value ? DelayOpen : DelayClose;
while (us < 0) us += 10000;
while (us > 10000) us -= 10000;
//Sys.Delay(us);
Time.Delay(us);
int us = value ? DelayOpen : DelayClose;
Zero->Wait(us);
}
Led = value;
Relay = value;
_Value = value;
if (Zero)
{
debug_printf("Button::SetValue %d 零点=%dus \r\n", value, us);
}
}

View File

@ -20,7 +20,7 @@ public:
Delegate<Button&> Press; // 按下事件
ACZero* Zero; // 交流电过零检测
const ACZero* Zero; // 交流电过零检测
ushort DelayOpen; // 延迟打开继电器us
ushort DelayClose; // 延迟关闭继电器us