C# WPF开发中串口接收数据比较判断失效如何解决
问题根因
你这段代码if判断不触发是三个必然的逻辑问题,和串口返回正确与否没关系:
ReadExisting()方法读取的是调用瞬间串口接收缓冲区里所有现存的内容,你连续发了M400、M114两条指令,设备返回的内容是分段的:先回M400的响应,再回M114对应的坐标数据,最后才回ok,最终缓冲区里存的是类似X:12.34 Y:56.78 Z:0.00 E:0.00\r\nok\r\n的拼接字符串,不可能刚好等于纯字符串"ok"。- 绝大多数串口设备(尤其是你发M代码对应的3D打印机固件)返回响应时,ok后面都会自动带上
\r\n回车换行符,哪怕缓冲区里只收到ok,实际字符串是"ok\r\n",直接用==做全等判断自然不成立。 - 你发完指令立刻调用ReadExisting,串口数据传输存在物理延迟,调用读方法的时候数据还没传到PC的串口缓冲区,第一次读到的是空值或者半截数据,自然匹配失败;再加上你把do-while循环写在UI线程,循环跑起来的时候WPF UI会被完全阻塞,文本框没法刷新显示收到的数据,甚至会导致串口接收事件无法正常触发,进入死循环。
修复方法
- 不要用同步死循环的方式读串口,改用
SerialPort自带的DataReceived事件做异步接收,单独维护一个全局字符串缓冲区累计所有收到的内容,不要每次读完就丢弃。 - 匹配响应内容时不要用全等判断,先对收到的内容做预处理,判断累计的内容中是否包含"ok"即可,注意兼容大小写、前后附带的换行符和其他响应内容。
- 所有UI更新操作必须通过WPF的
Dispatcher跨线程调用,避免跨线程操作控件报错。 - 等待响应时加超时机制,不要无限循环阻塞线程。
参考实现代码
首先在窗口类中定义全局接收缓冲和锁对象(串口DataReceived事件是在独立线程触发的,必须加锁避免多线程冲突):
private readonly StringBuilder _serialReceiveBuffer = new StringBuilder(); private readonly object _bufferLock = new object();
串口初始化时绑定DataReceived事件:
// 串口初始化其他参数(波特率、校验位等)按你设备实际情况配置 sp.DataReceived += Sp_DataReceived;
实现接收事件逻辑:
private void Sp_DataReceived(object sender, SerialDataReceivedEventArgs e) { SerialPort port = (SerialPort)sender; string currentRead = port.ReadExisting(); // 把新收到的内容追加到全局缓冲区 lock (_bufferLock) { _serialReceiveBuffer.Append(currentRead); } // 跨线程更新文本框显示 Dispatcher.Invoke(() => { ReceiveTextBox.AppendText(currentRead); ReceiveTextBox.ScrollToEnd(); }); // 检查是否收到ok响应 lock (_bufferLock) { string totalReceived = _serialReceiveBuffer.ToString(); // 不区分大小写匹配ok,兼容设备返回OK、ok带换行等情况 if (totalReceived.Contains("ok", StringComparison.OrdinalIgnoreCase)) { f2 = true; xml_flag = true; // 匹配成功后清空缓冲区,避免后续响应干扰下次判断 _serialReceiveBuffer.Clear(); } } }
原来的发送指令逻辑改成异步等待,不要阻塞UI线程:
// 发送前先重置标志位、清空缓冲区 xml_flag = false; f2 = false; lock (_bufferLock) { _serialReceiveBuffer.Clear(); } // 发送指令 sp.WriteLine("M400"); sp.WriteLine("M114"); // 等待响应,加5秒超时避免无限等待 CancellationTokenSource timeoutCts = new CancellationTokenSource(5000); while (!xml_flag && !timeoutCts.IsCancellationRequested) { // 短暂等待让出CPU,避免占满资源 await Task.Delay(10); } if (!xml_flag) { MessageBox.Show("设备响应超时"); // 超时后的异常处理逻辑 }
补充说明:如果对接的是Marlin等3D打印机固件,M114指令会先返回坐标信息再返回ok,必须累计接收所有返回内容再做匹配,单次读取单条响应的写法完全不适用这类设备的交互逻辑。
内容的提问来源于stack exchange,提问作者Benoy Sharry
相关产品推荐
相关产品推荐

