C# 串口通讯异步封装

前言

最近在写C# 串口通讯,顺便总结一下。C# 串口通讯已经被微软封装好了,可以直接使用。

相关资料

C#中SerialPort 的使用

C# Task任务详解

设计思路

因为串口通讯的延迟性,我们希望将其封装成一个Task 线程。通过异步来控制收发。其实就两个方法。发送和接收。

发送比较简单,因为发送是不需要等待延迟的。
接收是异步,需要异步等待,等收到数据了才能接收数据。

代码封装

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace HY_SokectClient.SocketManager
{
    /// <summary>
    /// 串口服务类
    /// </summary>
    public class SimChangeService
    {

        private SerialPort serialPort;

        public string[] serialProtArray;

        private string name = "Sim卡切换设备:";

        /// <summary>
        /// 接受的数据
        /// </summary>
        private string receiveMsg = "";

        /// <summary>
        /// 最大超时时间
        /// </summary>
        private int waitTime = 10 * 1000;

        /// <summary>
        /// 线程阻塞
        /// </summary>
        private ManualResetEvent manualResetEvent = new ManualResetEvent(true);
		// 消息打印的委托,可以是控制台,也可以是Winfrom/WPF窗口
        private Action<string> ShowMsg;


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="PortName">打开端口</param>
        /// <param name="waitTime">每包数据等待时间</param>
        public SimChangeService(int waitTime,Action<string> action)
        {
            ShowMsg = action;
            this.waitTime = waitTime;
            //这个参数根据实际的情况设置
            serialPort = new SerialPort()
            {
                PortName = "COM7",
                BaudRate = 9600,
                Parity = Parity.None,
                DataBits = 8,
                StopBits = StopBits.Two,
            };
            //获取当前机器所有串口
            serialProtArray = SerialPort.GetPortNames();
            //打开串口
            serialPort.Open();
            //读取最大超时时间
            serialPort.ReadTimeout = waitTime;
            //收到数据的回调
            serialPort.DataReceived += SerialPort_DataReceived;
            ShowMsg("找到本机已有的串口");
            ShowMsg(JsonConvert.SerializeObject(serialProtArray));
        }

		//专门用于线程暂停的函数,用于阻塞读取线程
        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            //ShowMsg("取消线程暂停");
            manualResetEvent.Set();
        }

        /// <summary>
        /// 接受数据
        /// </summary>
        /// <returns></returns>
        public async Task<string> Recive()
        {
            var isOutTime = false;
            //这个是超时计数,查看接收数据时间是否超时
            //开启两个任务,一个是休眠时间,一个是线程阻塞
            await Task.WhenAny(Task.Run(async () =>
            {
                await Task.Delay(waitTime);
                isOutTime = true;
            }), Task.Run(() =>
            {
                manualResetEvent.WaitOne();
            }));

            if (isOutTime)
            {
                throw new Exception("已超时");
            }
			//ReadExisting函数会清空暂存区所有数据,如果你的数据是多次拼接,需要自己主动拼接。默认是Ascll的字符串数据。可以自己去更改
            var res = serialPort.ReadExisting();
			//每次读完数据,就阻塞自己。只能由接收数据事件放开阻塞
            manualResetEvent.Reset();
            ShowMsg("返回命令:" + res);
            return res;

        }
		//发送函数
        public void Send(string msg)
        {
            ShowMsg($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}。发送命令:" + msg);
            //ShowMsg("线程阻塞");
            //主动阻塞读取线程,只能由接收数据事件放开阻塞
            manualResetEvent.Reset();
            //串口发送数据
            serialPort.Write(msg);
        }
    }
}


简单使用


/// <summary>
/// Sim卡切换
/// </summary>
/// <param name="index"></param>
public async Task ChangeSimNo(int index)
{
    int row = (index / 12);
    string col = Convert.ToInt32(index % 12).ToString("X1");
    var msg = $"AT+S{row}{col}";
    SimNo = -1;
    Send(msg);
    try
    {
        var res = await Recive();
        ShowMsg(res);
        res = await Recive();
        //如何处理串口数据,需要根据实际逻辑。我这里是第二包收到OK就是接受成功了。
        if (res.Contains("OK"))
        {
            SimNo = index;
            ShowMsg($"Sim[{index}]卡切换成功!");
        }
        else
        {
            ShowMsg($"Sim[{index}]卡接受报文错误,应为OK!");
        }
    }
    catch (Exception ex)
    {

        throw new Exception("等待OK超时");
    }

}
  • 8
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是C#串口通讯断线重连的示例代码: ```csharp using System.IO.Ports; using System.Threading; public class SerialPortManager { private SerialPort serialPort; private Thread reconnectThread; private bool isReconnecting = false; public SerialPortManager(string portName, int baudRate) { serialPort = new SerialPort(portName, baudRate); serialPort.DataReceived += SerialPort_DataReceived; serialPort.ErrorReceived += SerialPort_ErrorReceived; serialPort.Open(); } private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { // 处理接收到的数据 } private void SerialPort_ErrorReceived(object sender, SerialErrorReceivedEventArgs e) { if (e.EventType == SerialError.RXOver || e.EventType == SerialError.Overrun || e.EventType == SerialError.Frame || e.EventType == SerialError.RXParity) { // 发生错误,断开连接并尝试重新连接 Reconnect(); } } private void Reconnect() { if (isReconnecting) { return; } isReconnecting = true; // 关闭串口 serialPort.Close(); // 开启重连线程 reconnectThread = new Thread(() => { while (true) { try { // 尝试重新连接 serialPort.Open(); // 重新连接成功,退出重连线程 isReconnecting = false; break; } catch { // 重新连接失败,等待一段时间后继续尝试 Thread.Sleep(1000); } } }); reconnectThread.Start(); } } ``` 该示例代码中,SerialPortManager类封装串口通讯的相关操作,包括初始化串口、处理接收到的数据、处理错误事件、断线重连等。当发生错误事件时,会调用Reconnect方法进行断线重连,该方法会关闭串口并开启一个新的线程进行重连操作,直到重连成功为止。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值