从SerialPort对象外部看,写缓冲区的大小和是否已满似乎没有影响。使用同步写入时,write方法会阻塞直到所有数据都被发送并且缓冲区为空。使用异步写入时,数据被排队,程序继续执行。回调方法在写操作完成并且数据已经从缓冲区中移除后才会被调用。无论缓冲区中有多少数据以及是否已满,SerialPort对象的行为似乎都是相同的。当写缓冲区已满时也不会出现错误。那么,为什么要检查BytesToWrite和WriteBufferSize呢?当写缓冲区已满时,SerialPort是否有不同的行为呢?
缓冲区是一种机制,旨在允许处理缓冲区的任何人以自己的方式、自己的时间进行处理。当我发送数据时,我希望它被推送到端口的最大速率,但我不想在端口上忙等待并等待每个字节在推送下一个字节之前发送。因此,您需要一个处理缓冲区来提供给硬件,并且可以分块传递。
至于为什么要检查BytesToWrite - 您通常会有兴趣知道是否在执行下一个要做的事情之前已发送了所有数据,您可能期望在给定时间后收到响应,您可能想知道实际传输速率等等。
C# 的 SerialPort.BytesToWrite
属性对应于未托管的 Win32 COMSTAT.cbOutQue
字段,其描述如下:
这似乎表明您可以在写完成回调被调用之前使用异步写入观察写缓冲区被消耗的情况。
SerialPort.WriteBufferSize
对应于SetupComm(..., dwOutQueue)
,它被记录为设备驱动程序可以自由忽略的建议缓冲区大小:http://msdn.microsoft.com/en-us/library/aa363439(v=vs.85).aspx - Rick Sladkey我想创建一个测试实用程序,不断通过串口发送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);
}
}