问题描述
|
我正在尝试通过串行端口使用RS-485与设备通信。一切工作正常,直到我们试图增强通信以测试卡的速度限制,然后似乎出现了奇怪的问题。基本上,我们首先发送一个带有图像作为参数的命令,然后发送另一个命令以显示该图像。在执行每个命令后,卡会回答说该命令已收到。但是我们太快达到极限了,该卡应该能处理更多的事情。
因此,我想知道由于发送和接收都通过同一条导线,是否存在某种数据冲突?我应该等待接收所有数据吗?在这种情况下,SerialDataReceivedEventHandler是否太慢?我是否应该继续在单独的线程中的while true循环中读取字节,并在收到完整消息后向其他线程发出信号?
其他信息:
我们已经有一个通信协议:startdelimiter,data,
CRC16,结束符
发送2条命令是我们执行此操作的方式,无法更改。
波特率定义为115200
工程师仍在处理卡中的程序,因此问题可能也就在他身上。
英语不是我的母语,所以随时问我是否不清楚... :)
我认识到SerialPort编程不是我的强项,并且我一直在尝试寻找某种包装方法,但是我没有找到任何适合我需要的方法。如果有人向我求婚,那可能很棒,或者有人对可能出问题的想法有所了解。
无论如何,这里是一些编码:
线程发送帧:
public void SendOne()
{
timerLast = Stopwatch.GetTimestamp();
while (!Paused && conn.ClientConnState == Connexion.ConnectionState.Connected)
{
timerNow = Stopwatch.GetTimestamp();
if ((timerNow - timerLast) / (double)Stopwatch.Frequency >= 1 / (double)fps)
{
averageFPS.Add((int)((double)Stopwatch.Frequency / (timerNow - timerLast)) + 1);
if (averageFPS.Count > 10) averageFPS.RemoveAt(0);
timerLast = Stopwatch.GetTimestamp();
if (atFrame >= toSend.Count - 1)
{
atFrame = 0;
if (!isLoop)
Paused = true;
}
SendColorImage();
}
}
public void SendColorImage()
{
conn.Write(VIP16.bytesToVIP16(0x70C1,VIP16.Request.SendImage,toSend[++atFrame]));
WaitForResponse();
conn.Write(VIP16.bytesToVIP16(0x70C1,VIP16.Request.DisplayImage,VIP16.DisplayOnArg));
WaitForResponse();
}
private void WaitForResponse()
{
Thread.Sleep(25);
}
因此,WaitForResponse()至关重要,因为如果我在卡应答之前发送另一个命令,它将发疯。尽管我讨厌使用Thread.Sleep(),因为它不是很准确,而且会限制我的速度为20fps,而且如果我使用的时间小于25ms,则很可能发生崩溃的风险。因此,我打算将Thread.Sleep更改为“读取字节,直到接收到整个消息”,然后忽略DataReceivedEvent ...只是想知道我是否在这里完全偏离了轨道?
Tx很多!
更新1
首先,谢谢Brad和500-内部服务器错误!但我决定暂时使用.NET串行端口并提高Thread.Sleep的准确性(使用timebeginperiod)。我决定等待接收到完整的响应,并使用ManualResetEventSlim(为了提高速度)像这样同步线程:
public static ManualResetEventSlim _waitHandle = new ManualResetEventSlim(false);
然后我将SendColorIMage更改为:
public void SendColorImage()
{
conn.Write(VIP16.bytesToVIP16(0x70C1,VIP16.Requetes.SendImage,VIP16.Requetes.DisplayImage,VIP16.DisplayOnArg));
WaitForResponse2();
}
private void WaitForResponse()
{
Connexion._waitHandle.Wait(100);
Thread.Sleep(20);
}
private void WaitForResponse2()
{
Connexion._waitHandle.Wait(100);
//Thread.Sleep(5);
}
使用SerialDataReceivedEventHandler调用:
public void Recevoir(object sender,SerialDataReceivedEventArgs e)
{
if (!msg.IsIncomplete)
msg = new Vip16Message();
lock (locker)
{
if (sp.BytesToRead > 0)
{
byte[] byteMsg = new byte[sp.BytesToRead];
sp.Read(byteMsg,byteMsg.Length);
msg.Insert(byteMsg);
}
}
if (!msg.IsIncomplete)
{
_waitHandle.Set();
if (MessageRecu != null)
MessageRecu(msg.toByte());
}
}
因此,我发现在执行第二个命令之后,我根本不需要调用Thread.Sleep ...,在执行第一个命令之后,我需要休眠至少20ms以使卡不崩溃。所以我想这是卡需要接收/处理整个图像到其像素的时间。由于我要等到整个消息到达后才真正不会发生AND数据冲突,这意味着问题不在我的身边!是! :p
解决方法
几个指针:
发送后,您将需要等待传输缓冲区为空的事件,然后再读取响应。它是EV_TXEMPTY的非托管形式,我不记得它是如何封装在托管方面的-我们的RS485代码早于.NET comport组件。
您可以使用timeBeginPeriod(1)调用对计时器芯片进行重新编程,以在Thread.Sleep()上获得1毫秒的分辨率。
对于它的价值,我们在发送后仅短暂睡眠(1毫秒),然后进入读取循环,在该循环中,我们继续尝试从端口读取(再次,两次读取之间有1毫秒的延迟),直到收到完整的响应为止收到(或者直到超时或重试计数器用尽)。
这是timeBeginPeriod的导入声明-我不认为它可以在.NET中直接使用(还可以吗?):
[DllImport(\"winmm.dll\")]
internal static extern uint timeBeginPeriod(uint period);
我希望这有帮助。