串行端口RS-485和通信限制
本文关键字:通信 RS-485 串行端口 | 更新日期: 2023-09-27 17:57:51
我正试图通过串行端口使用RS-485与设备通信。一切都很好,直到我们试图增强通信以测试卡的速度限制,然后似乎出现了奇怪的问题。我们基本上是发送第一个以图像为参数的命令,然后发送另一个显示该图像的命令。每接到一个命令后,卡片都会回答,说这个命令很受欢迎。但我们很快就达到了极限,这张卡应该能处理更多的问题。
所以我想知道,由于传输和接收是通过同一条线路进行的,是否存在某种数据冲突?我应该等待接收所有数据吗?SerialDataReceivedEventHandler在这种情况下是否太慢了?我是否应该在单独的线程中继续读取while true循环中的字节,并在收到完整消息后向其他线程发出信号?
其他信息:
- 我们已经有了一个通信协议:startdelimiter、data、,CRC16,结束分隔符
- 发送两个命令是我们的操作方式,不能更改
- 波特率定义为115200
- 工程师仍在处理卡片中的程序,所以问题可能也在他这边
- 英语不是我的第一语言,所以如果我不清楚,请随时询问…:)
我知道串行端口编程不是我的强项,我一直在努力寻找某种包装器,但我没有找到任何适合我需要的。如果有人向我求婚,那就太好了,或者有人知道可能出了什么问题。无论如何,这里有一点编码:
线程发送帧:
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的准确性(随着时间的推移)。我决定等待收到完整的响应,并使用ManualResetEventSlim同步我的线程(为了速度):
public static ManualResetEventSlim _waitHandle = new ManualResetEventSlim(false);
然后我将SendColorIMage更改为:
public void SendColorImage()
{
conn.Write(VIP16.bytesToVIP16(0x70C1, VIP16.Requetes.SendImage, toSend[++atFrame]));
WaitForResponse();
conn.Write(VIP16.bytesToVIP16(0x70C1, 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, 0, byteMsg.Length);
msg.Insert(byteMsg);
}
}
if (!msg.IsIncomplete)
{
_waitHandle.Set();
if (MessageRecu != null)
MessageRecu(msg.toByte());
}
}
所以我发现,在第二个命令之后,我不需要调用线程。睡眠。。。在第一次之后,我需要至少睡20秒才能让卡不崩溃。所以我想这是卡需要接收/处理整个图像到像素的时间。数据的AND冲突不应该真的发生,因为我一直等到整个消息到达,这意味着问题不在我这边!是的!:p
几个指针:
-
发送后,您需要等待传输缓冲区为空事件,然后再读取响应。它是非托管的EV_TXEMPTY,我不记得它是如何在托管端封装的——我们的RS485代码早于.NET组件。
-
您可以使用timeBeginPeriod(1)调用重新编程定时器芯片,以在Thread.Sleep().上获得1毫秒的分辨率
-
值得一提的是,我们在发送后只短暂睡眠(1毫秒),然后进入读取循环,在那里我们继续尝试从端口读取(再次,读取尝试之间有1毫秒的延迟),直到收到完整响应(或者直到超时或重试计数器用完)。
以下是timeBeginPeriod的导入声明-我不认为它在.NET中直接可用(还没有?):
[DllImport("winmm.dll")]
internal static extern uint timeBeginPeriod(uint period);
我希望这能有所帮助。