串口写缓冲区的目的是什么?

3
从SerialPort对象外部看,写缓冲区的大小和是否已满似乎没有影响。使用同步写入时,write方法会阻塞直到所有数据都被发送并且缓冲区为空。使用异步写入时,数据被排队,程序继续执行。回调方法在写操作完成并且数据已经从缓冲区中移除后才会被调用。无论缓冲区中有多少数据以及是否已满,SerialPort对象的行为似乎都是相同的。当写缓冲区已满时也不会出现错误。那么,为什么要检查BytesToWrite和WriteBufferSize呢?当写缓冲区已满时,SerialPort是否有不同的行为呢?
3个回答

1

缓冲区是一种机制,旨在允许处理缓冲区的任何人以自己的方式、自己的时间进行处理。当我发送数据时,我希望它被推送到端口的最大速率,但我不想在端口上忙等待并等待每个字节在推送下一个字节之前发送。因此,您需要一个处理缓冲区来提供给硬件,并且可以分块传递。

至于为什么要检查BytesToWrite - 您通常会有兴趣知道是否在执行下一个要做的事情之前已发送了所有数据,您可能期望在给定时间后收到响应,您可能想知道实际传输速率等等。


那么,BytesToWrite属性与缓冲区大小无关? BytesToWrite可能是缓冲区大小的一半或两倍,并不意味着缓冲区是满的还是未满的? - HypnoToad
nop,BytesToWrite是尚未写入串口的字节数。根据文档,它表示发送缓冲区中的字节数,因此它可以是一半,但永远不会是缓冲区大小的两倍。写缓冲区与您在调用Write方法时传递的数据不同。缓冲区由底层的win32实现用于与实际端口通信。抽象(C#中的Write方法)为您处理更长(或更短)的数据的分段和传输。 - NightDweller
好的,这就是我感到困惑的地方——写缓冲区是2k,但我可以异步写入100k,并且BytesToWrite会告诉我有100000字节,即使WriteBufferSize只有2048。我可以看着它随着字节的发送而倒计时到0,但程序的行为不受缓冲区填充程度的影响。 - HypnoToad

1

C# 的 SerialPort.BytesToWrite 属性对应于未托管的 Win32 COMSTAT.cbOutQue 字段,其描述如下:

  • 所有写操作中剩余待传输的用户数据字节数。对于非重叠写操作,此值将为零。

这似乎表明您可以在写完成回调被调用之前使用异步写入观察写缓冲区被消耗的情况。


这正是我所想的,除了当缓冲区已满时,我可以继续写入数据,而且没有任何数据丢失,也不会抛出错误。 - HypnoToad
我忘记回答你问题的其余部分了。SerialPort.WriteBufferSize对应于SetupComm(..., dwOutQueue),它被记录为设备驱动程序可以自由忽略的建议缓冲区大小:http://msdn.microsoft.com/en-us/library/aa363439(v=vs.85).aspx - Rick Sladkey
我明白了。文档中说可以自由使用任何I/O缓冲“方案”,这意味着它可能具有某种自适应缓冲或其他功能。我要看看在它崩溃之前可以写入多少字节到“队列”中 :) - HypnoToad
这意味着明智的做法是不要超出自己的缓冲区大小,但在实践中,如果您使用的是股票硬件和微软驱动程序,则不会有害。 - Rick Sladkey
明白了——如果 BytesToWrite >= WriteBufferSize,我想我可以假设缓冲区已满。谢谢! - HypnoToad

0

我想创建一个测试实用程序,不断通过串口发送0xAA,没有间隙,永远不停止。我不关心RX。

我使用了一个计时器来保持缓冲区满,并监视BytesToWrite,等待在低于阈值之前,再将更多数据写入缓冲区。

我本来可以在AsyncCallback中刷新串口而不使用计时器,但我想以这种方式做仅仅是为了好玩。你可以查看label11来查看缓冲区填充和清空。

请注意,您可以在短时间内使用BeginWrite而没有EndWrite,但最终资源将耗尽。我基本上只是插入了一个虚拟的EndWrite。

    private void checkBox2_CheckedChanged(object sender, EventArgs e)
    {
        timerFill.Enabled = checkBox2.Checked;
    }

    private void timerFill_Tick(object sender, EventArgs e)
    {
        GenerateSquareWave();
    }

    int const bufferSize = 256;

    void GenerateSquareWave()
    {
        int k = serialPort1.BytesToWrite;
        label11.Text = k.ToString();
        if (k < bufferSize)
        {
            byte[] data = new byte[bufferSize];
            for (int i = 0; i < bufferSize; i++)
            {
                data[i] = 0xAA;
            }
            serialPort1.BaseStream.BeginWrite(data, 0, data.Length, new AsyncCallback((IAsyncResult ar) => serialPort1.BaseStream.EndWrite(ar)), null);
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接