我正在使用C#中的SerialPort类编写串行通信程序,以便与通过RS232电缆连接的剥离机进行交互。当我将命令发送到机器时,它会根据命令以一些字节响应。就像我发送“\ D”命令一样,我希望将180字节的机器程序数据下载为连续字符串。根据机器的手册,它建议最好的做法是发送一个未识别的字符,如逗号(,)字符,以确保在循环中发送第一个命令之前初始化机器。我的串口通讯代码如下:
public class SerialHelper
{
SerialPort commPort = null;
string currentReceived = string.Empty;
string receivedStr = string.Empty;
private bool CommInitialized()
{
try
{
commPort = new SerialPort();
commPort.PortName = "COM1";
if (!commPort.IsOpen)
commPort.Open();
commPort.BaudRate = 9600;
commPort.Parity = System.IO.Ports.Parity.None;
commPort.StopBits = StopBits.One;
commPort.DataBits = 8;
commPort.RtsEnable = true;
commPort.DtrEnable = true;
commPort.DataReceived += new SerialDataReceivedEventHandler(commPort_DataReceived);
return true;
}
catch (Exception ex)
{
return false;
}
}
void commPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort currentPort = (SerialPort)sender;
currentReceived = currentPort.ReadExisting();
receivedStr += currentReceived;
}
internal int CommIO(string outString, int outLen, ref string inBuffer, int inLen)
{
receivedStr = string.Empty;
inBuffer = string.Empty;
if (CommInitialized())
{
commPort.Write(outString);
}
System.Threading.Thread.Sleep(1500);
int i = 0;
while ((receivedStr.Length < inLen) && i < 10)
{
System.Threading.Thread.Sleep(500);
i += 1;
}
if (!string.IsNullOrEmpty(receivedStr))
{
inBuffer = receivedStr;
}
commPort.Close();
return inBuffer.Length;
}
}
我从Windows窗体中调用此代码,如下所示:
len = SerialHelperObj.CommIO(",",1,ref inBuffer, 4)
len = SerialHelperObj.CommIO(",",1,ref inBuffer, 4)
If(inBuffer == "!?*O")
{
len = SerialHelperObj.CommIO("\D",2,ref inBuffer, 180)
}
串口的有效返回值如下所示: \ D00000010000000000010 550 3250 0000256000等......
我得到这样的事情: \ D00000010D ,, 000 550 D ,,依旧......
当我发送命令时,我觉得我的通话调用被干扰了。但我试图确保逗号命令的结果然后启动实际命令。但是接收到的线程正在插入前一个通信周期中的字节。
任何人都可以对此有所了解......? 我试图让这项工作失去了一些头发。我不确定我在哪里做错了
答案 0 :(得分:2)
串行端口不是线程安全的资源,也不是使用它们的包装器,也不是用于保存结果的字符串。发生的事情是DataReceived事件处理程序多次触发,并且您最终会同时执行多个处理程序。尝试向事件处理程序添加锁定块:
...
Object lockingObj = new Object();
...
void commPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
lock(lockingObj)
{
SerialPort currentPort = (SerialPort)sender;
currentReceived = currentPort.ReadExisting();
receivedStr += currentReceived;
}
}
lock语句阻止多个线程一次执行块中的代码,因此receiveStr将获得一个ReadExisting的全部内容,然后再写入另一个。
请注意,这并不保证执行顺序;先进的胜利。所以,假设线程1首先到达此处,然后线程2在250ms后到达,而线程3在此之后250ms到达。线程1可能首先进入,执行Read需要一段时间,然后退出。在那段时间,线程2和3进入函数并在lock语句中被阻塞,等待线程1释放其锁定。一旦发生这种情况,由内核首先调度哪个线程,这可能是线程3,具体取决于许多操作系统和硬件因素。
答案 1 :(得分:0)
为什么每次都要重新初始化端口?我认为一个就足够了。
答案 2 :(得分:0)
我发现您的代码存在许多问题。
false
忽略了异常。当你这样做时,你不知道发生了什么异常。commPort
对象可能永远不会被实例化。它可以处于任何状态,但是您忽略了异常并继续访问可能无效的对象。DataReceived
事件可能随时发生。它可能会也可能不会在同一个线程上引发。您的代码可能正在从事件处理程序下关闭该端口,这会抛出您的代码未捕获的异常。receivedStr += currentReceived
不会附加到receivedStr
- 它会创建一个新的字符串对象来保存这两个部分。