【C# .NET】利用串行通信接口检测输入方波的周期(频率)

【问题描述】有些控制系统中采用独立的方波或脉冲信号协调各设备的动作,这种信号称为“时统信号”,整个系统在时统信号的协调下工作。时统信号的电平幅度(峰峰值)一般在10V左右。 这时,系统开发就需要检测时统信号的周期(频率)。解决这个问题通用方法是采用计数/定时器卡对时统脉冲计数,同时计时,然后根据检测时间和检测到的计时值计算出信号的周期(频率)。如果对软件对时统信号精度要求不高,例如绝对精度为0.1毫秒(100微秒),这时可以利用串行通信端口检测时统信号。

【解决方法】串行通信接口检测时统信号有两种方法:(1)时统信号通过串行接口CTS输入,通过检测CTS引脚的状态变化得出时统信号的周期 (2)时统信号通过串行接口的RxD输入,通过检测串口的Break状态变化得出时统信号的周期。

【递进式均值计算】

利用串口检测时统信号时均使用了SerialPort.PinChanged事件。按MSDN说明,SerialPort类封装了一个内部线程,通过这个线程触发诸如DataReceivedPinChanged事件。众所周知,Windows系统线程调度是非实时性的。例如,使用Thread.Sleep(10)从线程休眠到唤醒的时间并非严格等于10毫秒,而是一个基于10毫秒的随机值。因此,SerialPort内部线程触发DataReceivedPinChanged事件时也会有随机误差。递进式均值计算就是为了消除这个误差。

/// <summary>
/// 周期均值滤波器
/// </summary>
public class CycleMeanValueFilter
{
    private double meanValue;
    private int index;


    public CycleMeanValueFilter()
    {
        Reset();
    }

    /// <summary>
    /// 均值计算的点数。默认20个点
    /// </summary>
    public int MaxCount { get; set; } = 20;


    /// <summary>
    /// 输入,递进方式求均值
    /// </summary>
    /// <param name="cycleInMicrosecond">微秒为单位的周期</param>
    public void Input(long cycleInMicrosecond)
     {
        index++;
        if (index > MaxCount)
        {
           // 保留上次均值!
           index = 2;
        }
        // 以递进方式求均值
        meanValue += (cycleInMicrosecond - meanValue) / index;
     }

    /// <summary>
    /// 获取以微秒为单位的周期均值
    /// </summary>
    public double Output { get { return meanValue; } }

    public void Reset()
    {
        meanValue = 0;
        index = 0;
    }
}

【微秒级计时器】

这个计时器用于计算时统信号的周期

using System.Diagnostics;

public class MicrosecondStopwatch
{
    static readonly double MicrosecondPerTick = 1000000D / Stopwatch.Frequency;

    private readonly Stopwatch stopwatch = new Stopwatch();

    public void Start()
    {
        stopwatch.Start();
    }

    public void Stop()
    {
        stopwatch.Stop();
    }


    public long ElapsedMicroseconds
    {
        get { return (long)(stopwatch.ElapsedTicks * MicrosecondPerTick); }
    }
}

【利用CTS检测时统信号】

using System;
using System.IO.Ports;

class Program
{
    static SerialPort Port;
    static MicrosecondStopwatch Stopwatch;
    static CycleMeanValueFilter Filter;
    static long t0 = 0;    

    static void Main(string[] args)
    {
        Filter = new CycleMeanValueFilter();
        Port = new SerialPort("COM1");
        Port.PingChanged += Port_PinChanged;
        Port.Open();
        StopWatch.Start();

        Consoel.ReadLine();

        StopWatcher.Stop();
        Port.Close();
    }

    private void Port_PinChanged(object sender, SerialPinChangedEventArgs e)
    {
        // 只检测CTS信号有效
        if (e.EventType == SerialPinChange.CtsChanged && Port.CtsHolding)
        {
            var t = Stopwatch.ElapsedMicroseconds;
            Filter.Input(t - t0);
            t0 = t;
            Console.WriteLine($"周期:{Filter.Output / 1000:0.00} 毫秒");
        }
    }
}

【利用BreakState检测时统信号】

using System;
using System.IO.Ports;

class Program
{
    static SerialPort Port;
    static MicrosecondStopwatch Stopwatch;
    static CycleMeanValueFilter Filter;
    static long t0 = 0;    

    static void Main(string[] args)
    {
        Filter = new CycleMeanValueFilter();
        Port = new SerialPort("COM1");
        Port.PingChanged += Port_PinChanged;
        Port.Open();
        StopWatch.Start();

        Consoel.ReadLine();

        StopWatcher.Stop();
        Port.Close();
    }

    private void Port_PinChanged(object sender, SerialPinChangedEventArgs e)
    {
        if (e.EventType == SerialPinChange.Break)
        {
            var t = Stopwatch.ElapsedMicroseconds;
            Filter.Input(t - t0);
            t0 = t;
            Console.WriteLine($"周期:{Filter.Output / 1000:0.00} 毫秒");
        }
    }
}

【补充电平规范】计算机串口电平规范如下:

  1. “1”:[-3V,  -15V]
  2. “0”:[+3V, +15V]

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值