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
{
    /// 
    /// 串口服务类
    /// 
    public class SimChangeService
    {

        private SerialPort serialPort;

        public string[] serialProtArray;

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

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

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

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


        /// 
        /// 构造函数
        /// 
        /// 打开端口
        /// 每包数据等待时间
        public SimChangeService(string PortName, 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();
        }

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

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

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


简单使用


/// 
/// Sim卡切换
/// 
/// 
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(waitTime);
        ShowMsg(res);
        res = await Recive(waitTime);
        //如何处理串口数据,需要根据实际逻辑。我这里是第二包收到OK就是接受成功了。
        if (res.Contains("OK"))
        {
            SimNo = index;
            ShowMsg($"Sim[{index}]卡切换成功!");
        }
        else
        {
            ShowMsg($"Sim[{index}]卡接受报文错误,应为OK!");
        }
    }
    catch (Exception ex)
    {

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

}

你可能感兴趣的:(C#,c#,开发语言)