当前位置: 首页 > news >正文

基于.NET Framework 4.0的串口通信

核心功能

SendCommandAndWaitResponse 方法

  • 发送指令后持续读取串口数据
  • 两个独立的超时参数:
    1. noDataTimeoutMs - 串口完全无响应的超时时间
    2. wrongDataTimeoutMs - 有数据但不匹配期望响应的超时时间
  • 使用 out string matchedResponse 返回结果或错误信息

超时逻辑

发送指令 → 等待数据↓
没收到任何数据?↓ 是等待超过 noDataTimeoutMs? → 返回"串口无响应"错误↓ 否
收到数据但不匹配?↓ 是  距离上次收到数据超过 wrongDataTimeoutMs? → 返回"数据不匹配"错误↓ 否
继续等待...

使用示例

string result;
bool success = manager.SendCommandAndWaitResponse(command,           // 要发送的指令expectedResponses, // 期望的响应列表3000,             // 3秒无数据超时2000,             // 2秒错误数据超时out result        // 返回匹配的响应或错误信息
);if (success) {Console.WriteLine("成功: " + result);
} else {Console.WriteLine("失败: " + result);
}

代码完全符合.NET Framework 4.0,没有使用任何新语法!

using System;
using System.IO.Ports;
using System.Text;
using System.Threading;
using System.Collections.Generic;namespace SerialPortDemo
{/// <summary>/// 串口通信管理类/// </summary>public class SerialPortManager{private SerialPort serialPort;private Thread receiveThread;private bool isRunning = false;private object lockObject = new object();// 用于存储接收到的所有数据private List<string> receivedMessages = new List<string>();/// <summary>/// 构造函数/// </summary>public SerialPortManager(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits){serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);serialPort.ReadTimeout = 500;serialPort.WriteTimeout = 500;}/// <summary>/// 打开串口并启动接收线程/// </summary>public bool Open(){try{if (!serialPort.IsOpen){serialPort.Open();isRunning = true;// 启动后台接收线程receiveThread = new Thread(new ThreadStart(ReceiveDataThread));receiveThread.IsBackground = true;receiveThread.Start();return true;}return false;}catch (Exception ex){throw new Exception("打开串口失败: " + ex.Message);}}/// <summary>/// 关闭串口/// </summary>public void Close(){isRunning = false;if (receiveThread != null && receiveThread.IsAlive){receiveThread.Join(1000); // 等待线程结束}if (serialPort != null && serialPort.IsOpen){serialPort.Close();}}/// <summary>/// 后台接收数据线程/// </summary>private void ReceiveDataThread(){byte[] buffer = new byte[1024];while (isRunning){try{if (serialPort.IsOpen && serialPort.BytesToRead > 0){int bytesRead = serialPort.Read(buffer, 0, buffer.Length);if (bytesRead > 0){// 提取实际读取的字节byte[] data = new byte[bytesRead];Array.Copy(buffer, 0, data, 0, bytesRead);// 转换为16进制字符串string hexString = ByteArrayToHexString(data);lock (lockObject){receivedMessages.Add(hexString);// 打印接收到的数据(可选)Console.WriteLine("[接收] " + hexString);}}}else{Thread.Sleep(10); // 避免CPU占用过高}}catch (Exception ex){// 记录异常但不中断线程Console.WriteLine("接收数据异常: " + ex.Message);Thread.Sleep(100);}}}/// <summary>/// 将byte[]转换为16进制字符串(空格分隔)/// 例如: 01 A2 34 54 12/// </summary>public static string ByteArrayToHexString(byte[] data){if (data == null || data.Length == 0){return string.Empty;}StringBuilder sb = new StringBuilder(data.Length * 3);for (int i = 0; i < data.Length; i++){sb.Append(data[i].ToString("X2"));if (i < data.Length - 1){sb.Append(" ");}}return sb.ToString();}/// <summary>/// 将16进制字符串转换为byte[]/// 支持格式: "01A23454" 或 "01 A2 34 54"/// </summary>public static byte[] HexStringToByteArray(string hexString){if (string.IsNullOrEmpty(hexString)){return new byte[0];}// 移除空格hexString = hexString.Replace(" ", "").Replace("-", "");if (hexString.Length % 2 != 0){throw new ArgumentException("16进制字符串长度必须是偶数");}byte[] bytes = new byte[hexString.Length / 2];for (int i = 0; i < bytes.Length; i++){bytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);}return bytes;}/// <summary>/// 发送数据/// </summary>public void SendData(byte[] data){if (serialPort != null && serialPort.IsOpen){serialPort.Write(data, 0, data.Length);Console.WriteLine("[发送] " + ByteArrayToHexString(data));}else{throw new Exception("串口未打开");}}/// <summary>/// 发送16进制字符串/// </summary>public void SendHexString(string hexString){byte[] data = HexStringToByteArray(hexString);SendData(data);}/// <summary>/// 执行指令并等待指定响应/// </summary>/// <param name="command">要发送的指令(byte数组)</param>/// <param name="expectedResponses">期望的响应列表(16进制字符串)</param>/// <param name="noDataTimeoutMs">没有任何数据的超时时间(毫秒)</param>/// <param name="wrongDataTimeoutMs">有数据但不匹配的超时时间(毫秒)</param>/// <param name="matchedResponse">返回匹配的响应内容</param>/// <returns>成功返回true,超时返回false</returns>public bool SendCommandAndWaitResponse(byte[] command, string[] expectedResponses, int noDataTimeoutMs, int wrongDataTimeoutMs, out string matchedResponse){matchedResponse = string.Empty;// 清空之前的接收缓冲lock (lockObject){receivedMessages.Clear();}// 发送指令SendData(command);DateTime startTime = DateTime.Now;DateTime lastDataTime = DateTime.Now;bool hasReceivedData = false;while (true){// 检查是否有新数据List<string> currentMessages = new List<string>();lock (lockObject){currentMessages.AddRange(receivedMessages);}// 检查每条接收到的消息foreach (string receivedMsg in currentMessages){// 检查是否匹配期望的响应foreach (string expected in expectedResponses){if (IsResponseMatch(receivedMsg, expected)){matchedResponse = receivedMsg;return true; // 成功匹配}}}// 更新数据接收状态if (currentMessages.Count > 0){if (!hasReceivedData){hasReceivedData = true;lastDataTime = DateTime.Now; // 首次收到数据,记录时间}}// 检查超时条件if (!hasReceivedData){// 一直没有收到任何数据if ((DateTime.Now - startTime).TotalMilliseconds >= noDataTimeoutMs){matchedResponse = "超时错误: 串口无响应,未收到任何数据";return false;}}else{// 已经收到数据,但不是期望的响应if ((DateTime.Now - lastDataTime).TotalMilliseconds >= wrongDataTimeoutMs){// 返回所有收到的数据用于调试StringBuilder sb = new StringBuilder();sb.Append("超时错误: 收到数据但不匹配期望响应。收到的数据: ");foreach (string msg in currentMessages){sb.Append("[" + msg + "] ");}matchedResponse = sb.ToString();return false;}// 如果有新数据进来,更新lastDataTimelock (lockObject){if (receivedMessages.Count > currentMessages.Count){lastDataTime = DateTime.Now;}}}Thread.Sleep(50); // 等待一小段时间再检查}}/// <summary>/// 检查响应是否匹配(支持部分匹配)/// </summary>private bool IsResponseMatch(string received, string expected){if (string.IsNullOrEmpty(received) || string.IsNullOrEmpty(expected)){return false;}// 移除空格进行比较string receivedNoSpace = received.Replace(" ", "").ToUpper();string expectedNoSpace = expected.Replace(" ", "").ToUpper();// 使用Contains判断部分匹配return receivedNoSpace.Contains(expectedNoSpace);}/// <summary>/// 清空接收缓冲区/// </summary>public void ClearBuffer(){lock (lockObject){receivedMessages.Clear();}}/// <summary>/// 获取所有接收到的消息/// </summary>public List<string> GetReceivedMessages(){lock (lockObject){return new List<string>(receivedMessages);}}}/// <summary>/// 使用示例/// </summary>class Program{static void Main(string[] args){// 创建串口管理器SerialPortManager manager = new SerialPortManager("COM3", 9600, Parity.None, 8, StopBits.One);try{// 打开串口if (manager.Open()){Console.WriteLine("串口已打开");}// 等待串口稳定Thread.Sleep(500);// ==================== 示例1: 基本使用 ====================Console.WriteLine("\n===== 示例1: 发送指令并等待响应 =====");byte[] command1 = new byte[] { 0x01, 0xA2, 0x34, 0x54, 0x12 };string[] expectedResponses1 = new string[] { "01 A2 FF",   // 期望响应1"01 A2 00",   // 期望响应2"AA BB CC"    // 期望响应3};string matchedResponse;bool success = manager.SendCommandAndWaitResponse(command1, expectedResponses1, 3000,  // 3秒无数据超时2000,  // 2秒错误数据超时out matchedResponse);if (success){Console.WriteLine("成功! 匹配的响应: " + matchedResponse);}else{Console.WriteLine("失败! " + matchedResponse);}Thread.Sleep(1000);// ==================== 示例2: 发送16进制字符串 ====================Console.WriteLine("\n===== 示例2: 发送16进制字符串 =====");string[] expectedResponses2 = new string[] { "BB CC DD" };success = manager.SendCommandAndWaitResponse(SerialPortManager.HexStringToByteArray("AA BB CC DD"),expectedResponses2,5000,  // 5秒无数据超时3000,  // 3秒错误数据超时out matchedResponse);if (success){Console.WriteLine("成功! 匹配的响应: " + matchedResponse);}else{Console.WriteLine("失败! " + matchedResponse);}// ==================== 示例3: 测试超时情况 ====================Console.WriteLine("\n===== 示例3: 测试超时(这个会失败) =====");byte[] command3 = new byte[] { 0xFF, 0xFF };string[] expectedResponses3 = new string[] { "99 99 99" };success = manager.SendCommandAndWaitResponse(command3,expectedResponses3,2000,  // 2秒无数据超时1500,  // 1.5秒错误数据超时out matchedResponse);if (success){Console.WriteLine("成功! 匹配的响应: " + matchedResponse);}else{Console.WriteLine("失败! " + matchedResponse);}// 等待用户输入Console.WriteLine("\n按任意键退出...");Console.ReadKey();// 关闭串口manager.Close();Console.WriteLine("串口已关闭");}catch (Exception ex){Console.WriteLine("错误: " + ex.Message);}}}
}
http://www.dtcms.com/a/477664.html

相关文章:

  • Mybatis Log Free插件使用
  • asp网站可运行jsp吗专做立体化的网站
  • angie未生成coredump文件
  • 【leetcode刷题007】leetcode116、117
  • 南昌网站推广¥做下拉去118cr如何做实验室网站
  • 从0开始搭建Vue3项目
  • 【知识】Linux将多条指令合并为一条执行
  • 将 MapReduce 程序打成 JAR 包并在 Linux 虚拟机的 Hadoop 集群上运行
  • 06_Miniconda零基础安装配置指南:打造高效的Python开发环境
  • 量子计算与AI融合:材料科学新突破!!
  • 徐州网站建设方案咨询外贸牛
  • 顶级 AI 模型横评:智能、性能与价格等方面全面对比
  • Vuex 详细用法(Vue2 和 Vue3)
  • 注册公司网站如何注册黑龙江公共资源交易网官网
  • 如何将iPhone上的HEIF图像下载到电脑
  • 欧洲宇航局使用Varjo XR头戴设备为宇航员提供虚拟现实空间站任务训练
  • iphone IOS3~IOS9游戏 旧iphone 单机游戏合集分享
  • 昂瑞微冲刺科创板:硬科技与资本市场的双向奔赴
  • 从入门到精通【Redis】Redis 典型应⽤ --- 缓存 (cache)
  • 【深入理解计算机网络10】UDP协议详解
  • 宁波网站建设模板制作什么叫优化
  • 自动裁剪图(电商使用)
  • 大模型应用开发
  • 第15题 三数之和
  • 【1015】计算并联电阻的阻值
  • 红黑树实现与原理剖析(上篇):核心规则与插入平衡逻辑
  • 【AES加密专题】8.实战-测试加密网站和代码
  • 收费的电影网站怎么做可以打开任何网站的软件
  • 设计广告网站wordpress怎么换空间
  • React 18并发模式解析:Fiber架构与性能优化技巧