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(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();
        }

        /// <summary>
        /// 接受数据
        /// </summary>
        /// <param name="timeout">超时时间</param>
        /// <returns></returns>
        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);
        }
    }
}


简单使用


/// <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(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超时");
    }

}

相关推荐

  1. C# 串口通讯异步封装

    2023-12-07 11:30:03       71 阅读
  2. 串口通讯串行接口通讯

    2023-12-07 11:30:03       51 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2023-12-07 11:30:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2023-12-07 11:30:03       101 阅读
  3. 在Django里面运行非项目文件

    2023-12-07 11:30:03       82 阅读
  4. Python语言-面向对象

    2023-12-07 11:30:03       91 阅读

热门阅读

  1. redis实现排行榜功能

    2023-12-07 11:30:03       66 阅读
  2. docker安装sybase数据库

    2023-12-07 11:30:03       54 阅读
  3. Mysql大数据量删除

    2023-12-07 11:30:03       52 阅读
  4. 【前端】windows下nvm安装node、npm的一些坑

    2023-12-07 11:30:03       69 阅读
  5. Ubuntu18.04的VSCode输入不了中文

    2023-12-07 11:30:03       67 阅读
  6. Spring MVC详解

    2023-12-07 11:30:03       39 阅读
  7. MFC中updatedata(False)和Updatedata(TRUE)区别

    2023-12-07 11:30:03       48 阅读
  8. Redis小知识

    2023-12-07 11:30:03       59 阅读
  9. 【力扣100】 3.最长连续序列

    2023-12-07 11:30:03       69 阅读
  10. leetcode每日一题4

    2023-12-07 11:30:03       58 阅读
  11. 向量数据库,展望AGI时代

    2023-12-07 11:30:03       53 阅读