【问题标题】:Serial Port Trigger DataReceived when certain amounts of bytes received当接收到一定数量的字节时,串行端口触发 DataReceived
【发布时间】:2013-06-24 23:29:13
【问题描述】:

我正在尝试编写一个程序,每次新数据进入串行端口时都会更新 Windows 窗体,但我在努力理解串行端口的工作原理以及如何以我想要的方式使用它。

我有一个外部设备以 1Hz 的频率向我的串行端口发送 8 个字节,并希望使用来自 SerialPort 类的 DataReceived 事件。当我调试我的代码时,事件或多或少会根据程序在特定时间执行的操作随机触发。代码如下:

    private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        //byte[] rxbyte = new byte[1];
        byte[] rxbyte = new byte[8];
        byte currentbyte;
        port.Read(rxbyte, 0, port.BytesToRead);

        currentbyte = rxbyte[0];

        int channel = (currentbyte >> 6) & 3; //3 = binary 11, ANDS the last 2 bits
        int msb_2bit = (currentbyte >> 0) & 255; //AND compare all bits in a byte
        currentbyte = rxbyte[1];
        int val = ((msb_2bit << 8) | (currentbyte << 0));

        //Extra stuff

        SetText_tmp1(val.ToString());
    }

在调用 Read 函数之前,我希望接收缓冲区中正好有 8 个字节,但我不确定如何执行此操作(之前从未使用过 SerialPort 类),并且只想对数据进行所有操作当我有整个 8 个字节时。仅当缓冲区中有一定数量的字节时,是否有内置方法来切换事件?或者有没有其他方法只获取 8 个字节,而不是更多,并将剩余的字节留给下一个实例?

【问题讨论】:

  • 显然你有一个 XY 问题。你得到了问题 Y 的答案,但 IMO 你仍然必须解决 X。如果这只是家庭作业,那么可能不需要强大的解决方案(但应该值得 A 或额外的学分)。如果这是一个真正的产品,那么你还没有完成。

标签: c# serial-port


【解决方案1】:

是的,您的编码不正确。您无法预测您将收到多少字节。所以在你得到所有字节之前不要处理接收到的字节。像这样:

private byte[] rxbyte = new byte[8];
private int rxcount = 0;

private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    rxcount += port.Read(rxbyte, rxcount, 8 - rxcount);
    if (rxcount < 8) return;
    rxcount = 0;
    // Process rxbyte content
    //...
}

【讨论】:

  • 我怀疑这段代码大部分时间都可以“工作”,但不够健壮,无法一直工作。消息同步没有逻辑。
  • 您希望我如何从问题中的信息中添加它?他在寻求一种他永远不会让它工作的方法,至少他有机会使用这段代码。
  • 例程在静默期间而不是在消息期间开始读取的概率与波特率和字符间距成正比(在现代系统上通常或接近于零)。随着波特率越来越快,消息同步正确的概率增加。 “你希望我怎么样……” -- 我没有写你必须提供完整的解决方案,但你根本没有提到消息帧同步,这是一个隐含的要求的正确解决方案。
  • 就我的目的而言,它只是让串行通信以我想要的方式工作,以便我可以测试代码的其他部分。我知道最好的方法是在带有标头的数据包中包含数据包结构信息,并在标头说您完成之前接收,但只需要一种快速的方法来测试其他内容。另一种方法是对 8 个字节的 1hz 传输设置超时,因为我知道数据是以脉冲形式背靠背发送的,但我认为找出计时器比使用快速肮脏的方法来准确获取更困难8 个字节。
【解决方案2】:

将 ReceivedBytesThreshold 属性设置为 8。如port.ReceivedBytesThreshold = 8;

【讨论】:

  • 这太简单了,无法正确实现消息帧的同步。您需要保证从消息的第一个字节开始计数。
  • 如果您按照 Hans 的回答,则不需要。
  • @sawdust - 是的,根据提供的其余代码,它是简单的并且可以说是错误的。但是,该帖子仅声明“在调用 Read 函数之前,我希望能够在接收缓冲区中恰好有 8 个字节”。没有任何关于同步的内容,尽管几乎可以肯定是必要的。
  • @dbasnett - Hans 的回答完全相同,只是手动操作。
【解决方案3】:

处理这个问题的一种有效方法是在类中添加一个计时器,该计时器每秒可能会计时 9 次。彻底消除串口事件处理程序。

在每个计时器滴答声中,让代码检查串行端口是否有从串行端口接收到的字节。如果那里有一些,则将它们从串行端口中取出并将它们附加到作为数据成员在类中维护的缓冲区的末尾。

当缓冲区中有八个或更多字符时,计时器计时逻辑将从缓冲区中取出前 8 个字节并使用它们来更新用户界面窗口。缓冲区中的任何剩余字节都可以向上移动到缓冲区的头部。

定时器滴答例程还可以维护一个计数器值,每次滴答进入并且在此滴答时串行端口没有准备好数据时递增。当此计数器达到 3 或 4 的值时,代码会将数据缓冲区重置为空并将计数器重置为零。当实际从串行端口看到数据时,此计数器将重置为零。这种计数器机制的目的是使数据接收缓冲区与传入的 1Hz 数据流同步,这样接收过程就不会与代表 8 字节消息开始的数据不同步。

请注意,此方法优于串行端口接收数据事件,因为它允许您的程序保持对事物的控制。我已经描述了与数据流突发同步的能力——尝试将串行端口接收数据阈值设置为 8 这样的计数是不可能的。另一个优点是计时器滴答代码可以包含额外的处理功能例如,如果在 2 或 3 秒内没有数据从串行端口到达,则发出超时信号。

【讨论】:

  • @sawdust - 计时器本身是事件驱动的。作为重复事件的结果,超时被测量。我可以说已经构建了许多使用串行端口的应用程序,这个方案运行得很好。也许引用的评论对每个人来说都太强烈了,但它对我在数十种工业生产环境中使用的应用程序来说效果很好。
  • “请注意,这种方法更优越......” -- 并非如此,您是在建议轮询比事件驱动“更好”。如果延迟是可以容忍的,那么这是一个可能的解决方案。我根本不懂 C#,但在 C 中,我认为使用适当的 VMIN 和 VTIME 设置的非规范读取将是最简单的解决方案。
  • @MichaelKaras - 为什么要为以 1 字节/秒到达的数据每秒触发 9 次计时器?我喜欢汉斯的回答。避免处理程序的问题是计时器必须执行读取并处理适用于低比特率的数据,但对于更高的速率则不是很好。
  • @dbasnett - 我在 OP 给出的参数范围内工作。他说他的 8 字节数据包每秒到达一次。由于他的数据内容显然没有与帧信息绑定,因此必须使用时序来判断数据边界。这可以通过每秒 4-5 个滴答声来完成,但很久以前我注意到,在每秒 9 个或更大的滴答声时,用户通常不会注意到用户界面更新的延迟变化。他们会以较慢的速度。至于数据在哪里被读取和处理——如果这是一个(续)
  • (续上)计算机所需的任务。事件处理程序不是中断服务程序。如果必须收集和显示数据,谁会关心它在处理系统中所有事件的可用带宽中分布在哪里。当然,无论在哪里完成,您都需要使工作简短而有趣,这样鼠标和触摸等其他事情就不会受到影响。如果数据处理确实变得复杂并且需要很长时间,则可以轻松地将其拆分并跨多个计时器滴答进行处理。我一直在我的生产应用程序中做这种事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多