精华内容
下载资源
问答
  • 如何安全关闭串口

    千次阅读 2016-07-22 15:00:36
    串口无法关闭的原因是:要关闭串口的时候,有其它线程还在读取数据或者更新界面。 关键是:在准备关闭串口的时候,看看是否在接收和处理数据,如果是就等它处理完为止;在事件处理的最前面,判断如果是准备关闭串口...

    转载自http://blog.sina.com.cn/s/blog_4c8bb86b0100e5rw.html


    串口无法关闭的原因是:要关闭串口的时候,有其它线程还在读取数据或者更新界面。
    关键是:在准备关闭串口的时候,看看是否在接收和处理数据,如果是就等它处理完为止;在事件处理的最前面,判断如果是准备关闭串口的bool类型值,就不再进入数据接收和处理。

     

    2010.03.27 更新 Help By Wyz. I appreciate it !!!

     

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.IO.Ports;
    using System.Timers;


    namespace PortTesting
    {
        /// <summary>
        /// 定义了一个委托类型
        /// </summary>
        public delegate void WhenGetNew();

        /// <summary>
        /// 串口封装类,Help By Wyz
        /// </summary>
        public class PortDataDisplay
        {
            /// <summary>
            /// 系统串口类
            /// </summary>
            public SerialPort serialPort = new SerialPort("COM1", 19200);
            /// <summary>
            /// 解析得到数据后触发事件
            /// </summary>
            public event WhenGetNew whenGetNew;
            /// <summary>
            /// 处理线程
            /// </summary>
            private SerialDataReceivedEventHandler threadCallHandler;
            /// <summary>
            /// 对外的数据类型定义
            /// </summary>
            public string dataSrc = "";
            /// <summary>
            /// 准备关闭串口=true
            /// </summary>
            private bool m_IsTryToClosePort = false;
            /// <summary>
            /// true表示正在接收数据
            /// </summary>
            private bool m_IsReceiving = false;
           

            /// <summary>
            /// 初始化
            /// </summary>
            public PortDataDisplay()
            {
            }

            /// <summary>
            /// 有参数的构造函数
            /// </summary>
            /// <param name="PortName">串口号,如"COM1"</param>
            /// <param name="BaudRate">波特率,如19200</param>
            public PortDataDisplay(string PortName, int BaudRate)
            {
                serialPort = new SerialPort(PortName, BaudRate);
            }

            /// <summary>
            /// 开始工作
            /// </summary>
            public void ConnectDeveice()
            {
                //0.注册事件
                serialPort.DataReceived -= OnSerialPortDataCome;
                serialPort.DataReceived += OnSerialPortDataCome;
                //1.再设置一下串口参数
                if (this.serialPort.IsOpen == false)
                {
                    this.serialPort.ReadBufferSize = 1000;
                    this.serialPort.ReceivedBytesThreshold = 1;//数据达到120的时候才就要触发事件,不行!!应该是数据来就触发
                    //2.打开串口开始工作
                    m_IsTryToClosePort = false;
                    this.serialPort.Open();
                }
            }

            /// <summary>
            /// 结束工作
            /// </summary>
            public void DisconnectDeveice() // 关键和核心!!!
            {
                m_IsTryToClosePort = true;
                while (m_IsReceiving)
                {
                    System.Windows.Forms.Application.DoEvents();
                }
                serialPort.Close();
            }

            /// <summary>
            /// 当通知到有数据达到120时处理(读取,与分析)
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void OnSerialPortDataCome(object sender, SerialDataReceivedEventArgs e)
            {
                if (m_IsTryToClosePort) // 关键!!!
                {
                    return;
                }

                m_IsReceiving = true; // 关键!!!

                try
                {
                    if (threadCallHandler == null)
                    {
                        threadCallHandler = new SerialDataReceivedEventHandler(OnSerialPortDataCome);
                    }

                    //read
                    dataSrc = serialPort.ReadExisting();//读出缓冲区所有数据
                    if (dataSrc != "" && this.whenGetNew != null)
                    {
                        this.whenGetNew();
                    }
                }
                finally // 放在finally里面比较好。
                {
                    m_IsReceiving = false; // 关键!!!
                }
            }
        }
    }

     

     

    使用的时候:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;

    namespace PortTesting
    {
        public partial class FrmMain : Form
        {
            /// <summary>
            /// 封装好的串口类
            /// </summary>
            private PortDataDisplay m_portDispl = new PortDataDisplay("COM1", 19200);

            public FrmMain()
            {
                InitializeComponent();
            }

            private void btnOpen_Click(object sender, EventArgs e)
            {
                if (btnOpen.Text == "打开串口")
                {
                    m_portDispl.whenGetNew -= portDispl_whenGetNew;
                    m_portDispl.whenGetNew += new WhenGetNew(portDispl_whenGetNew);
                    m_portDispl.ConnectDeveice();
                    btnOpen.Text = "关闭串口";
                }
                else if (btnOpen.Text == "关闭串口")
                {
                    m_portDispl.DisconnectDeveice();
                    btnOpen.Text = "打开串口";
                }
            }

            /// <summary>
            /// 事件
            /// </summary>
            private void portDispl_whenGetNew()
            {
                WhenGetNew ehan = delegate
                {
                    txtDisplay.AppendText(m_portDispl.dataSrc);
                };

                try
                {
                    if (InvokeRequired)
                    {
                        this.Invoke(ehan);
                    }
                }
                catch
                {
                }
            }


            private void btnClear_Click(object sender, EventArgs e)
            {
                txtDisplay.Clear();
            }
        }
    }

     


    展开全文
  • 并口的主要特点:并行接口是指数据的各位同时进行传送,其特点是传输速度快,但当传输距离较远、位数又多时,就导致通信线路复杂且成本提高。并口的分类:在IEEE1284标准中定义了多种并行接口模式,常用的有以下三种...

    并口的主要特点:

    并行接口是指数据的各位同时进行传送,其特点是传输速度快,但当传输距离较远、位数又多时,就导致通信线路复杂且成本提高。

    并口的分类:

    在IEEE1284标准中定义了多种并行接口模式,常用的有以下三种:

    SPP (StandardParallelPort) 标准并行接口

    EPP (EnhancedParallelPort) 增强并行接口

    ECP (ExtendedCapabiliTIesPort) 扩展功能并行接口

    这几种模式因硬件和编程方式的不同,传输速度可以从50KBits/秒到2MB/秒不等。一般用以从主机传输数据到打印机、绘图仪或其它数字化仪器的接口,是一种叫Centronics的36脚弹簧式接口(通常主机上是25针D型接口,打印机上是36针Centronics接口)。

    7293a540df6ffa161856e700cfeb447d.png

    四、USB接口、串口、并口有何区别

    串口叫做串行接口,现在的PC 机一般有两个串行口COM 1 和COM 2 。串行口不同于并行口之处在于它的数据和控制信息是一位接一位地传送出去的。 虽然这样速度会慢一些,但传送距离较并行口更长,因此若要进行较长距离的通信时,应使用串行口。通常 COM 1 使用的是9 针D 形连接器,也称之为RS-232接口,而COM 2 有的使用的是 老式的DB25 针连接器,也称之为RS-422接口,这种接口目前已经很少使用。

    并口又称为并行接口。目前,并行接口主要作为打印机端口,采用的是25 针D 形接头。所谓“并行”,是指8 位数据同时通过并行线进行传送,这样数据传送速度大大提高,但并行传送的线路长度受到限制,因为长度增加,干扰就会增加,数据也就容易出错。目前计算机基本上都配有并口。

    USB(Universal Serial Bus),一般称为通用串行总线,它是为了解决接口设备各自为政的连接方式与易于使用而设计。许多的计算机使用者在计算机前面使用可能没有什么问题,因为眼不见为净,最多只看到鼠标与键盘连接线。但是一旦到了计算机的背后,看到那么多种线可能就傻眼了,最少有电源线、显示讯号线、印表机连接线、调制解调器与喇叭等,除了连接时需要花点时间去辨别外,将计算机背后的这些杂七杂八的线整理起来也是相当累人,不管怎么整理,看起来还是很难看,有时就会想,若只有电源线就好了,全部改用无线方式那该有多好!当然那是未来最好的情况,目前最好的方式,便是将所有连接线加以简化,让使用者能够轻松使用。 USB最初设计的目的,只是要连接各种不同的接口设备,并且针对中、低速的接口设备,所以 USB 的传输速度并不高。USB有二种传输速率,第一种是全速模式,最高的传输率可达到12Mbps,第二种则是低速模式,传输速率有 1.5Mbps。低速模式只有少数的周边会使用,而它唯一的好处是电磁干扰(EMI) 较低。所以 USB全速的传输速度比起现有的串行埠 (标准是115200bps)可算是相当的高,不过当周边连接很多时,整体的效能可能会下降。另外,USB在使用上还有一点很方便,就是支持热插拔,若操作系统已经安装过装置的驱动程序,就可以在系统运行时,直接安装或移除,不像许多设备必须将系统电源关闭,或是操作系统重开才能使用,让使用方便性增加不少。

    另一方面就是传输方式不同,最简单的区别就是串口最宽最大,并口其次,USB接口最小,所以以前的串口并口目前只有部分老的打印机会用到,大部分都改为USB了,USB传输最快也最小最方便。

    展开全文
  • C# 串口并口 打印机控制

    千次阅读 2013-10-17 13:52:21
    //打印机端口(COM开头为串口,LPT开头为并口),看打印机是什么端口而定  public ClsPrintLPT()  {  }  private const uint GENERIC_READ = 0x80000000;  private const uint GENERIC_WRITE...
    public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();

                ///打印调用
                ClsPrintLPT printLPT = new ClsPrintLPT();
                printLPT.PrintESC(0);  //打印前退纸
                printLPT.PrintDataSet();
                printLPT.PrintESC(1);  //打印后进纸
            }
        }


        class ClsPrintLPT
        {
            private IntPtr iHandle;
            private FileStream fs;
            private StreamWriter sw;

            private string prnPort = "COM1";   //打印机端口(COM开头为串口,LPT开头为并口),看打印机是什么端口而定

            public ClsPrintLPT()
            {
            }


            private const uint GENERIC_READ = 0x80000000;
            private const uint GENERIC_WRITE = 0x40000000;
            private const int OPEN_EXISTING = 3;


            /// <summary>
            /// 打开一个vxd(设备)
            /// </summary>
            [DllImport("kernel32.dll", EntryPoint = "CreateFile", CharSet = CharSet.Auto)]
            private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, int lpSecurityAttributes,
                                                    int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile);


            /// <summary>
            /// 开始连接打印机
            /// </summary>
            private bool PrintOpen()
            {
                iHandle = CreateFile(prnPort, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);


                if (iHandle.ToInt32() == -1)
                {
                    MessageBox.Show("没有连接打印机或者打印机端口不是COM1!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return false;
                }
                else
                {
                    fs = new FileStream(iHandle, FileAccess.ReadWrite);
                    sw = new StreamWriter(fs, System.Text.Encoding.Default);   //写数据
                    return true;
                }
            }
            // <summary>
            /// 打印字符串
            /// </summary>
            /// <param name="str">要打印的字符串</param>
            private void PrintLine(string str)
            {
                sw.WriteLine(str); ;
            }


            /// <summary>
            /// 关闭打印连接
            /// </summary>
            private void PrintEnd()
            {
                sw.Close();
                fs.Close();
            }


            /// <summary>
            /// 打印票据
            /// </summary>
            /// <param name="ds">tb_Temp 全部字段数据集合</param>
            /// <returns>true:打印成功 false:打印失败</returns>
            public bool PrintDataSet(DataSet dsPrint)
            {
                try
                {
                    if (PrintOpen())
                    {
                        PrintLine(" ");
                        PrintLine("[XXXXXXXXXXXXXXXXXX超市]");
                        PrintLine("NO :      " + dsPrint.Tables[0].Rows[0][1].ToString());
                        PrintLine("XXXXXX: " + dsPrint.Tables[0].Rows[0][2].ToString());
                        PrintLine("XXXXXX: " + dsPrint.Tables[0].Rows[0][3].ToString());
                        PrintLine("XXXXXX: " + dsPrint.Tables[0].Rows[0][4].ToString());
                        PrintLine("XXXXXX: " + dsPrint.Tables[0].Rows[0][5].ToString());
                        PrintLine("操 作 员: " + dsPrint.Tables[0].Rows[0][6].ToString() + " " + dsPrint.Tables[0].Rows[0][7].ToString());
                        PrintLine("-------------------------------------------");
                    }
                    PrintEnd();


                    return true;
                }
                catch
                {
                    return false;
                }
            }


            public bool PrintDataSet()
            {
                try
                {
                    if (PrintOpen())
                    {
                        PrintLine(" ");
                        PrintLine("测试");
                        PrintLine("-------------------------------------------");
                    }
                    PrintEnd();


                    return true;
                }
                catch
                {
                    return false;
                }
            }


            /// <summary>
            /// ESC/P 指令
            /// </summary>
            /// <param name="iSelect">0:退纸命令 1:进纸命令 2:换行命令</param>
            public void PrintESC(int iSelect)
            {
                string send;


                iHandle = CreateFile(prnPort, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);


                if (iHandle.ToInt32() == -1)
                {
                    MessageBox.Show("没有连接打印机或者打印机端口不是LPT1!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                else
                {
                    fs = new FileStream(iHandle, FileAccess.ReadWrite);
                }


                byte[] buf = new byte[80];


                switch (iSelect)
                {
                    case 0:
                        send = "" + (char)(27) + (char)(64) + (char)(27) + 'j' + (char)(255);    //退纸1 255 为半张纸长
                        send = send + (char)(27) + 'j' + (char)(125);    //退纸2
                        break;
                    case 1:
                        send = "" + (char)(27) + (char)(64) + (char)(27) + 'J' + (char)(255);    //进纸
                        break;
                    case 2:
                        send = "" + (char)(27) + (char)(64) + (char)(12);   //换行
                        break;
                    default:
                        send = "" + (char)(27) + (char)(64) + (char)(12);   //换行
                        break;
                }


                for (int i = 0; i < send.Length; i++)
                {
                    buf[i] = (byte)send[i];
                }


                fs.Write(buf, 0, buf.Length);
                fs.Close();
            }

        }


    这是转别人的,原文地址:http://blog.csdn.net/pfworld/article/details/2084666


    展开全文
  • 为什么C#串口程序在关闭串口时候会死锁_老文章_赛迪网 http://www.ccidnet.com/2010/0524/2067861.shtml 【赛迪网讯】用过微软SerialPort类的人,都遇到过这个尴尬,关闭串口的时候会让软件死锁。天哪,我可...
    

    为什么C#串口程序在关闭串口时候会死锁_老文章_赛迪网 http://www.ccidnet.com/2010/0524/2067861.shtml


    【赛迪网讯】用过微软SerialPort类的人,都遇到过这个尴尬,关闭串口的时候会让软件死锁。天哪,我可不是武断,算了。不要太绝对了。99.9%的人吧,都遇到过这个问题。我想只有一半的人真的解决了。另外一半的人就睁只眼闭只眼阿弥佗佛希望不要在客户那里出现这问题了。

    view plaincopy to clipboardprint?
    void comm_DataReceived(object sender, SerialDataReceivedEventArgs e)      
    {      
        //先记录下来,避免某种原因,人为的原因,
        操作几次之间时间长,缓存不一致   
        int n = comm.BytesToRead;   
        //声明一个临时数组存储当前来的串口数据   
        byte[] buf = new byte[n];      
        //增加接收计数   
        received_count += n;    
        //读取缓冲数据     
        comm.Read(buf, 0, n);      
        //清除字符串构造器的内容   
        builder.Clear();      
        //因为要访问ui资源,所以需要使用invoke方式同步ui。      
        this.Invoke((EventHandler)(delegate{...界面更新,略}));      
    }           
    private void buttonOpenClose_Click(object sender, EventArgs e)      
    {      
        //根据当前串口对象,来判断操作      
        if (comm.IsOpen)      
        {      
            //打开时点击,则关闭串口      
            comm.Close();//这里就是可能导致软件死掉的地方   
        }      
        else     
        {...}     
    }  
    void comm_DataReceived(object sender, SerialDataReceivedEventArgs e)   
    {   
        //先记录下来,避免某种原因,人为的原因,
       操作几次之间时间长,缓存不一致
        int n = comm.BytesToRead;
        //声明一个临时数组存储当前来的串口数据
        byte[] buf = new byte[n];   
        //增加接收计数
        received_count += n; 
        //读取缓冲数据  
        comm.Read(buf, 0, n);   
        //清除字符串构造器的内容
        builder.Clear();   
        //因为要访问ui资源,所以需要使用invoke方式同步ui。   
        this.Invoke((EventHandler)(delegate{...界面更新,略}));   
    }     
    private void buttonOpenClose_Click(object sender, EventArgs e)   
    {   
        //根据当前串口对象,来判断操作   
        if (comm.IsOpen)   
        {   
            //打开时点击,则关闭串口   
            comm.Close();//这里就是可能导致软件死掉的地方
        }   
        else  
        {...}  
    }

    为什么会死锁呢,并发冲突。 我们要了解一下SerialPort的实现和串口通讯机制,在你打开串口的时候,SerialPort会创建一个监听线程ListenThread,在这个线程中,等待注册的串口中断,当收到中断后,会调用DataReceived事件。调用完成后,继续进入循环等待,直到串口被关闭退出线程。 我们的UI主线程如何做的呢,首先创建一个窗体,然后执行了Application.Run(窗体实例)。是这样把,这里的Application.Run就是创建了一个消息循环,循环的处理相关的消息。 这里我们就有了2个线程,UI主线程、串口监听线程。那么你在DataReceived处理数据的时候,就需要线程同步,避免并发冲突,什么是并发冲突?并发冲突就是2个或多个并行(至少看上去像)的线程运行的时候,多个线程共同的操作某一线程的资源,在时序上同时或没有按我们的预计顺序操作,这样就可能导致数据混乱无序或是彼此等待完成死锁软件。 而串口程序大多是后者。为什么呢,看看我们的例子中DataReceived做了什么?首先读取数据,然后就是调用this.Invoke方法更新UI了。这里Invoke的时候,监听线程将等待UI线程的标志,等到后,开始操作UI的资源,当操作完成之前,监听线程也就停在DataReceived方法的调用这里,如果这个时候。并发了关闭串口的操作会如何呢?SerialPort的Close方法,会首先尝试等待和监听线程一样的一个互斥体、临界区、或是事件(不确定.net用的哪种)。那这个同步对象什么时候释放呢?每次循环结束就释放,哦。循环为什么不结束呢?因为这一次的循环操作执行到DataReceived之后,执行了Invoke去更新界面了,那Invoke怎么又没有执行完成呢?看上去很简单的几行代码。虽然我没仔细研读过.net的Invoke原理,但我猜测是通过消息的方式来同步的,这也是为什么这么多的类,只有控件(窗体也是控件的一种,.net在概念上,颠覆了微软自己的概念,传统的win32编程,是说所有的控件都是个window,只是父窗体不同,表现形式不同,但都是基于系统消息队列的,.net出于更高的抽象,正好反过来了。呵呵)才有Invoke方法了。(委托自己的Invoke和这个不同) 我猜测控件/窗体的Invoke是SendMessage方式实现的,那么发送消息后就会等待消息循环来处理消息了。如果你直接去关闭串口了。你点击按钮本身也会被转换成消息WM_CLICK,消息循环在处理按钮的WM_CLICK时候,调用你按钮的OnClick方法,进而触发调用你的ButtonClose_Click事件,这都是同步调用的,你的主线程,处理消息的过程,停在了这个Click事件,而你的Click事件又去调用了SerialPort的Close方法,Close方法又因为和串口监听线程的同步信号量关联在一起需要等待一次的while结束,而这个while循环中调用了DataReceived方法,这个方法中调用了Invoke,也就是发送了消息到消息队列等待结果,但消息循环正在处理你的关闭按钮事件等待退出。 实在太复杂了,这个情况下,你想要真的关闭串口成功,就需要while中的DataReceived方法调用结束释放同步信号,就需要执行完Invoke,就需要执行消息循环,幸运的是,我们真的有办法执行消息循环来打破僵局。Application.DoEvents()。还好,不幸中的万幸。可是问题又来了,你能让Invoke结束,但你无法确定是否在你调用消息循环后,你的某一时刻不会再次并发,可能由于单cpu的串行操作模拟并行中,又把时间片先分给了优先级高的串口监听线程呢?是有可能的。所以,我们就需要一点方法来避免再次invoke窗体。优化后不会司机的例子如下,我们修改DataReceived方法,关闭方法,并定义2个标记Listening和Closing。

    view plaincopy to clipboardprint?
    using System;   
    using System.Collections.Generic;   
    using System.ComponentModel;   
    using System.Data;   
    using System.Drawing;   
    using System.Linq;   
    using System.Text;   
    using System.Windows.Forms;   
    using System.IO.Ports;   
    using System.Text.RegularExpressions;     
    namespace SerialportSample   
    {   
        public partial class SerialportSampleForm : Form   
        {   
            private SerialPort comm = new SerialPort();   
            private StringBuilder builder = new StringBuilder();
           //避免在事件处理方法中反复的创建,定义到外面。   
            private long received_count = 0;//接收计数   
            private long send_count = 0;//发送计数   
            private bool Listening = false;
           //是否没有执行完invoke相关操作   
            private bool Closing = false;
          //是否正在关闭串口,执行Application.DoEvents,
          并阻止再次invoke   
      
            public SerialportSampleForm()   
            {   
                InitializeComponent();   
            }     
            //窗体初始化   
            private void Form1_Load(object sender, EventArgs e)   
            {   
                //初始化下拉串口名称列表框   
                string[] ports = SerialPort.GetPortNames();   
                Array.Sort(ports);   
                comboPortName.Items.AddRange(ports);   
                comboPortName.SelectedIndex = 
                comboPortName.Items.Count > 0 ? 0 : -1;   
                comboBaudrate.SelectedIndex = 
                comboBaudrate.Items.IndexOf("9600");   
      
                //初始化SerialPort对象   
                comm.NewLine = "\r\n";   
                comm.RtsEnable = true;//根据实际情况吧。   
      
                //添加事件注册   
                comm.DataReceived += comm_DataReceived;   
            }   
      
            void comm_DataReceived
           (object sender, SerialDataReceivedEventArgs e)   
            {   
                if (Closing) return;
           //如果正在关闭,忽略操作,直接返回,
          尽快的完成串口监听线程的一次循环   
                try  
                {   
                    Listening = true;
                //设置标记,说明我已经开始处理数据,
                一会儿要使用系统UI的。   
                    int n = comm.BytesToRead;
                //先记录下来,避免某种原因,人为的原因,
                操作几次之间时间长,缓存不一致   
                    byte[] buf = new byte[n];
                 //声明一个临时数组存储当前来的串口数据   
                    received_count += n;//增加接收计数   
                    comm.Read(buf, 0, n);//读取缓冲数据   
                    builder.Clear();//清除字符串构造器的内容   
                    //因为要访问ui资源,所以需要使用invoke方式同步ui。   
                    this.Invoke((EventHandler)(delegate  
                    {   
                        //判断是否是显示为16禁止   
                        if (checkBoxHexView.Checked)   
                        {   
                            //依次的拼接出16进制字符串   
                            foreach (byte b in buf)   
                            {   
                                builder.Append(b.ToString("X2") + " ");   
                            }   
                        }   
                        else  
                        {   
                            //直接按ASCII规则转换成字符串   
                            builder.Append(Encoding.ASCII.GetString(buf));   
                        }   
                        //追加的形式添加到文本框末端,并滚动到最后。   
                        this.txGet.AppendText(builder.ToString());   
                        //修改接收计数   
                        labelGetCount.Text = "Get:" + received_count.ToString();   
                    }));   
                }   
                finally  
                {   
                    Listening = false;//我用完了,ui可以关闭串口了。   
                }   
            }   
      
            private void buttonOpenClose_Click(object sender, EventArgs e)   
            {   
                //根据当前串口对象,来判断操作   
                if (comm.IsOpen)   
                {   
                    Closing = true;   
                    while (Listening) Application.DoEvents();   
                    //打开时点击,则关闭串口   
                    comm.Close();   
                }   
                else  
                {   
                    //关闭时点击,则设置好端口,波特率后打开   
                    comm.PortName = comboPortName.Text;   
                    comm.BaudRate = int.Parse(comboBaudrate.Text);   
                    try  
                    {   
                        comm.Open();   
                    }   
                    catch(Exception ex)   
                    {   
                        //捕获到异常信息,创建一个新的comm对象,
                       之前的不能用了。   
                        comm = new SerialPort();   
                        //现实异常信息给客户。   
                        MessageBox.Show(ex.Message);   
                    }   
                }   
                //设置按钮的状态   
                buttonOpenClose.Text = comm.IsOpen ? "Close" : "Open";   
                buttonSend.Enabled = comm.IsOpen;   
            }   
      
            //动态的修改获取文本框是否支持自动换行。   
            private void checkBoxNewlineGet_CheckedChanged
            (object sender, EventArgs e)   
            {   
                txGet.WordWrap = checkBoxNewlineGet.Checked;   
            }   
      
            private void buttonSend_Click(object sender, EventArgs e)   
            {   
                //定义一个变量,记录发送了几个字节   
                int n = 0;   
                //16进制发送   
                if (checkBoxHexSend.Checked)   
                {   
                    //我们不管规则了。如果写错了一些,我们允许的,
                   只用正则得到有效的十六进制数   
                    MatchCollection mc = Regex.Matches(txSend.Text, @"(?i)[\da-f]{2}");   
                    List<byte> buf = new List<byte>();
                      //填充到这个临时列表中   
                    //依次添加到列表中   
                    foreach (Match m in mc)   
                    {   
                        buf.Add(byte.Parse(m.Value));   
                    }   
                    //转换列表为数组后发送   
                    comm.Write(buf.ToArray(), 0, buf.Count);   
                    //记录发送的字节数   
                    n = buf.Count;   
                }   
                else//ascii编码直接发送   
                {   
                    //包含换行符   
                    if (checkBoxNewlineSend.Checked)   
                    {   
                        comm.WriteLine(txSend.Text);   
                        n = txSend.Text.Length + 2;   
                    }   
                    else//不包含换行符   
                    {   
                        comm.Write(txSend.Text);   
                        n = txSend.Text.Length;   
                    }   
                }   
                send_count += n;//累加发送字节数   
                labelSendCount.Text = "Send:" + send_count.ToString();
               //更新界面   
            }   
      
            private void buttonReset_Click(object sender, EventArgs e)   
            {   
                //复位接受和发送的字节数计数器并更新界面。   
                send_count = received_count = 0;   
                labelGetCount.Text = "Get:0";   
                labelSendCount.Text = "Send:0";   
            }   
        }   
    }
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.IO.Ports;
    using System.Text.RegularExpressions;
    
    namespace SerialportSample
    {
        public partial class SerialportSampleForm : Form
        {
            private SerialPort comm = new SerialPort();
            private StringBuilder builder = new StringBuilder();
            //避免在事件处理方法中反复的创建,定义到外面。
            private long received_count = 0;
           //接收计数
            private long send_count = 0;
            //发送计数
            private bool Listening = false;
             //是否没有执行完invoke相关操作
            private bool Closing = false;
           //是否正在关闭串口,执行Application.DoEvents,
          并阻止再次invoke
    
            public SerialportSampleForm()
            {
                InitializeComponent();
            }
    
            //窗体初始化
            private void Form1_Load(object sender, EventArgs e)
            {
                //初始化下拉串口名称列表框
                string[] ports = SerialPort.GetPortNames();
                Array.Sort(ports);
                comboPortName.Items.AddRange(ports);
                comboPortName.SelectedIndex = 
                comboPortName.Items.Count > 0 ? 0 : -1;
                comboBaudrate.SelectedIndex = 
                comboBaudrate.Items.IndexOf("9600");
    
                //初始化SerialPort对象
                comm.NewLine = "\r\n";
                comm.RtsEnable = true;
               //根据实际情况吧。
    
                //添加事件注册
                comm.DataReceived += 
               comm_DataReceived;
            }
    
            void comm_DataReceived
            (object sender, SerialDataReceivedEventArgs e)
            {
                if (Closing) return;
              //如果正在关闭,忽略操作,直接返回,
               尽快的完成串口监听线程的一次循环
                try
                {
                    Listening = true;
                //设置标记,说明我已经开始处理数据,
                  一会儿要使用系统UI的。
                    int n = comm.BytesToRead;
                 //先记录下来,避免某种原因,人为的原因,
                 操作几次之间时间长,缓存不一致
                    byte[] buf = new byte[n];
                   //声明一个临时数组存储当前来的串口数据
                    received_count += n;
                    //增加接收计数
                    comm.Read(buf, 0, n);
                    //读取缓冲数据
                    builder.Clear();
                     //清除字符串构造器的内容
                    //因为要访问ui资源,所以需要使用invoke方式同步ui。
                    this.Invoke((EventHandler)(delegate
                    {
                        //判断是否是显示为16禁止
                        if (checkBoxHexView.Checked)
                        {
                            //依次的拼接出16进制字符串
                            foreach (byte b in buf)
                            {
                                builder.Append(b.ToString("X2") + " ");
                            }
                        }
                        else
                        {
                            //直接按ASCII规则转换成字符串
                            builder.Append(Encoding.ASCII.GetString(buf));
                        }
                        //追加的形式添加到文本框末端,并滚动到最后。
                        this.txGet.AppendText(builder.ToString());
                        //修改接收计数
                        labelGetCount.Text = "Get:" + received_count.ToString();
                    }));
                }
                finally
                {
                    Listening = false;//我用完了,ui可以关闭串口了。
                }
            }
    
            private void buttonOpenClose_Click(object sender, EventArgs e)
            {
                //根据当前串口对象,来判断操作
                if (comm.IsOpen)
                {
                    Closing = true;
                    while (Listening) Application.DoEvents();
                    //打开时点击,则关闭串口
                    comm.Close();
                }
                else
                {
                    //关闭时点击,则设置好端口,波特率后打开
                    comm.PortName = comboPortName.Text;
                    comm.BaudRate = int.Parse(comboBaudrate.Text);
                    try
                    {
                        comm.Open();
                    }
                    catch(Exception ex)
                    {
                        //捕获到异常信息,创建一个新的comm对象,
                      之前的不能用了。
                        comm = new SerialPort();
                        //现实异常信息给客户。
                        MessageBox.Show(ex.Message);
                    }
                }
                //设置按钮的状态
                buttonOpenClose.Text = comm.IsOpen ? "Close" : "Open";
                buttonSend.Enabled = comm.IsOpen;
            }
    
            //动态的修改获取文本框是否支持自动换行。
            private void checkBoxNewlineGet_CheckedChanged
            (object sender, EventArgs e)
            {
                txGet.WordWrap = checkBoxNewlineGet.Checked;
            }
    
            private void buttonSend_Click(object sender, EventArgs e)
            {
                //定义一个变量,记录发送了几个字节
                int n = 0;
                //16进制发送
                if (checkBoxHexSend.Checked)
                {
                    //我们不管规则了。如果写错了一些,
                   //我们允许的,只用正则得到有效的十六进制数
                    MatchCollection mc = 
                   Regex.Matches(txSend.Text, @"(?i)[\da-f]{2}");
                    List<byte> buf = new List<byte>();//填充到这个临时列表中
                    //依次添加到列表中
                    foreach (Match m in mc)
                    {
                        buf.Add(byte.Parse(m.Value));
                    }
                    //转换列表为数组后发送
                    comm.Write(buf.ToArray(), 0, buf.Count);
                    //记录发送的字节数
                    n = buf.Count;
                }
                else//ascii编码直接发送
                {
                    //包含换行符
                    if (checkBoxNewlineSend.Checked)
                    {
                        comm.WriteLine(txSend.Text);
                        n = txSend.Text.Length + 2;
                    }
                    else//不包含换行符
                    {
                        comm.Write(txSend.Text);
                        n = txSend.Text.Length;
                    }
                }
                send_count += n;
              //累加发送字节数
                labelSendCount.Text = "Send:" + send_count.ToString();
             //更新界面
            }
    
            private void buttonReset_Click(object sender, EventArgs e)
            {
                //复位接受和发送的字节数计数器并更新界面。
                send_count = received_count = 0;
                labelGetCount.Text = "Get:0";
                labelSendCount.Text = "Send:0";
            }
        }
    }

    至此,不会再出现关闭死锁问题了。 希望这篇文章能解你的燃眉之急,非常高兴能与读者分享我层遇到,大多数人都遇到的这个问题。如果说的不明白,欢迎讨论。 后续的有关通讯程序底层设计的文章会讲述一个具有丰富扩展性,但有设计简介的万能通讯库,支持网络、蓝牙、串口通讯、并口通讯。但不要指望我都实现出来了,我只是设计出这个框架。

    展开全文
  • IDE),但开始出现了一个问题:电脑原来是个串口的硬盘,又接了个并口的上去。可是在电脑上面识别不了后面的IDE接口硬盘,在bios里面识别的是一个DVD光驱,串口硬盘是接在sata1,按照一般的思路来说,并口硬盘和串口...
  • 第一篇文章我相信很多人不看都能做的出来,但是,用过微软SerialPort类的人,都遇到过这个尴尬,关闭串口的时候会让软件死锁。天哪,我可不是武断,算了。不要太绝对了。99.9%的人吧,都遇到过这个问题。我想只有...
  • 第一篇文章我相信很多人不看都能做的出来,但是,用过微软SerialPort类的人,都遇到过这个尴尬,关闭串口的时候会让软件死锁。天哪,我可不是武断,算了。不要太绝对了。99.9%的人吧,都遇到过这个问题。我想只有...
  • USB接口、串口并口有何区别

    万次阅读 2017-02-12 11:34:39
    串口叫做串行接口,现在的PC 机一般有两个串行口COM 1 和COM 2 。串行口不同于并行口之处在于它的数据和控制信息是一位接一位地传送出去的。 虽然这样速度会慢一些,但传送距离较并行口更长,因此若要进行较长距离的...
  • C# 串口操作系列(2) -- 入门篇,为什么我的串口程序在关闭串口时候会死锁 ? 标签: c#objectuibyte通讯.net 2010-05-19 08:43 55211人阅读 评论(188) 收藏 举报  分类:   通讯类库设计(4)...
  • NULL 博文链接:https://dengzhangtao.iteye.com/blog/1905143
  • C# 串口操作系列(2) -- 入门篇,为什么我的串口程序在关闭串口时候会死锁 ? 分类: 通讯类库设计2010-05-19 08:43 30544人阅读 评论(169) 收藏 举报 c#objectuibyte通讯.net  第一篇文章我相信...
  • 最近在做一个智能垃圾桶的项目,小区用的那种不是家里面的那种哈,由于需要四个垃圾桶,所以挂载多个模组就是一个比较麻烦的事情,一般STM32的芯片最多就只有八个串口,所以选择拓展串口的方式来做。 主题 使用的是...
  • 第一篇文章我相信很多人不看都能做的出来,但是,用过微软SerialPort类的人,都遇到过这个尴尬,关闭串口的时候会让软件死锁。天哪,我可不是武断,算了。不要太绝对了。99.9%的人吧,都遇到过这个问题。我想只有...
  • 第一篇文章我相信很多人不看都能做的出来,但是,用过微软SerialPort类的人,都遇到过这个尴尬,关闭串口的时候会让软件死锁。天哪,我可不是武断,算了。不要太绝对了。99.9%的人吧,都遇到过这个问题。我想只有...
  • 【赛迪网讯】用过微软SerialPort类的人,都遇到过这个尴尬,关闭串口的时候会让软件死锁。天哪,我可不是武断,算了。不要太绝对了。99.9%的人吧,都遇到过这个问题。我想只有一半的人真的解决了。另外一半的人就睁...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,905
精华内容 1,162
关键字:

关闭串口并口