USB串口读取数据

UI界面

USB串口读取数据_第1张图片

xmal  文件



    
        
            
                
                
                
            
            
                
                    
                    
                    
                    
                    
                    
                    
                
               
            
            
                

                    
                        
                        
                    
                    
                        
                            
                                
                                
                            
                            
                                
                                    
                                
                            
                            
                                
                                    
                                
                            
                        
                    
                    
                        
                            
                                
                                
                            
                            
                                
                                    
                                
                            
                            
                                
                                    
                                
                            
                        
                    
                
            
            
                
                    
                        
                            
                                
                                    
                                    
                                    
                                    
                                
                            
                        
                    
                    
                        
                            
                            
                        
                    
                    
                    
                        
                            
                            
                        
                    
                    
                
            
        
    


后台代码

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Management;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace Read串口读取速度Ports
{
    /// 
    /// MainWindow.xaml 的交互逻辑
    /// 
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        //一些变量的声明
        SerialPort ComPortL = new SerialPort();
        SerialPort ComPortR = new SerialPort();

        DispatcherTimer autoSendTickL = new DispatcherTimer();
        DispatcherTimer autoSendTickR = new DispatcherTimer();

        private bool Listening = false;//用于检测是否没有执行完invoke相关操作,仅在单线程收发使用,但是在公共代码区有相关设置,所以未用#define隔离

        private bool WaitClose = false;//invoke里判断是否正在关闭串口是否正在关闭串口,执行Application.DoEvents,并阻止再次invoke ,解决关闭串口时,程序假死,具体参见http://news.ccidnet.com/art/32859/20100524/2067861_4.html 仅在单线程收发使用,但是在公共代码区有相关设置,所以未用#define隔离
        private bool recStaus = true;//接收状态字
        Queue recQueue = new Queue();//接收数据过程中,接收数据线程与数据处理线程直接传递的队列,先进先出
        private static bool Sending = false;//正在发送数据状态字
        private static Thread _ComSend;//发送数据线程
        private SendSetStr SendSet = new SendSetStr();//发送数据线程传递参数的结构体
        private struct SendSetStr//发送数据线程传递参数的结构体格式
        {
            public string SendSetData;//发送的数据
            public bool? SendSetMode;//发送模式
        }
        public string[] port_Collect = new string[2]; //端口名字存储
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            ///串口获取方式一:
            List Portstr = new List();
            port_Collect = new string[2];
            bool kb = MulGetHardwareInfo(HardwareEnum.Win32_PnPEntity, "Name", ref Portstr);

            #region
            ///串口
            //foreach (var item in Portstr)
            //{
            //    Console.WriteLine(item);
            //}

            ///串口获取方式二
            //string[] Ports = SerialPort.GetPortNames();
            //foreach (var item in Ports)
            //{
            //    Console.WriteLine(item);
            //}
            #endregion



            if (port_Collect == null)
            {
                port_Collect = new string[2];
            }
            if (kb == true)
            {
                for (int i = 0; i < Portstr.Count; i++)
                {
                    port_Collect[i] = Portstr[i];
                }
            }
            else
            {
                if( Portstr.Count<0)
                {
                    MessageBox.Show("没找到串口,请重新检测");
                    return;
                }
            }


            if (port_Collect[0] == null || port_Collect[0].Length < 1)
            {
                MessageBox.Show("请重新连接左端口");
            }
            else
            {
                ComPortL.PortName = port_Collect[0];
                ComPortL.BaudRate = 192000;
                ComPortL.DataBits = 8;
                ComPortL.Parity = Parity.None;
                ComPortL.StopBits = StopBits.One;
                ComPortL.ReceivedBytesThreshold = 1;

                //ComPortL.DtrEnable = true;
                //ComPortL.RtsEnable = true;

                //找到之后,进行初始化
                ComPortL.ReadTimeout = 8000;
                ComPortL.WriteTimeout = 8000;
                ComPortL.ReadBufferSize = 15360;
                ComPortL.WriteBufferSize = 15360;

                ComPortL.DataReceived += new SerialDataReceivedEventHandler(ComReceiveL);//串口接受中断
                autoSendTickL.Tick += new EventHandler(autoSendL);//定时发送中断

                Thread _ComRec = new Thread(new ThreadStart(ComRec)); //查询串口接收数据线程声明
                _ComRec.Start();//启动线程

            }

          
            if (port_Collect[1] == null || port_Collect[1].Length < 1 )
            {
                MessageBox.Show("请重新连接右端口");
            }
            else
            {
                ComPortR.PortName = port_Collect[1];
                //初始化右端口
            }

        }

        public bool recModeCheck = false;
        void ComRec()//接收线程,窗口初始化中就开始启动运行
        {
            while (true)//一直查询串口接收线程中是否有新数据
            {
                if (recQueue.Count > 0)//当串口接收线程中有新的数据时候,队列中有新进的成员recQueue.Count > 0
                {
                    string recData;//接收数据转码后缓存
                    byte[] recBuffer = (byte[])recQueue.Dequeue();//出列Dequeue(全局)
                    recData = System.Text.Encoding.Default.GetString(recBuffer);//转码
                    UIAction(() =>
                    {
                        if (recModeCheck == false)//接收模式为ASCII文本模式
                        {
                            LrecTBox.Text += recData;//加显到接收区
                        }
                        else
                        {
                            StringBuilder recBuffer16 = new StringBuilder();//定义16进制接收缓存
                            for (int i = 0; i < recBuffer.Length; i++)
                            {
                                recBuffer16.AppendFormat("{0:X2}" + " ", recBuffer[i]);//X2表示十六进制格式(大写),域宽2位,不足的左边填0。
                            }
                            LrecTBox.Text += recBuffer16.ToString();//加显到接收区
                            if(LrecTBox.Text.Length>1000)
                            {
                                LrecTBox.Clear();
                                Console.WriteLine("Clear");
                            }
                        }
                        //SerialL.Text = (Convert.ToInt32(SerialL.Text) + recBuffer.Length).ToString();//接收数据字节数
                       
                        SerialL.Text = recBuffer.Length.ToString();
                       
                        LrecScrol.ScrollToBottom();//接收文本框滚动至底部
                    });
                }
                else
                {
                    Thread.Sleep(100);//如果不延时,一直查询,将占用CPU过高
                }
            }

        }


        void autoSendL(object sender, EventArgs e)//自动发送
        {
            Send();//调用发送方法
        }
        void Send()//发送数据,分为多线程方式和单线程方式
        {

            try
            {
                if (Sending == true) return;//如果当前正在发送,则取消本次发送,本句注释后,可能阻塞在ComSend的lock处
                _ComSend = new Thread(new ParameterizedThreadStart(ComSend)); //new发送线程   
                SendSet.SendSetData = @"t";//发送数据线程传递参数的结构体--发送的数据
                SendSet.SendSetMode = false;//发送数据线程传递参数的结构体--发送方式
                _ComSend.Start(SendSet);//发送线程启动
            }
            catch
            {
                return;
            }

        }
        private void ComSend(Object obj)//发送数据 独立线程方法 发送数据时UI可以响应
        {

            lock (this)//由于send()中的if (Sending == true) return,所以这里不会产生阻塞,如果没有那句,多次启动该线程,会在此处排队
            {
                Sending = true;//正在发生状态字
                byte[] sendBuffer = null;//发送数据缓冲区
                string sendData = SendSet.SendSetData;//复制发送数据,以免发送过程中数据被手动改变
                if (SendSet.SendSetMode == true)//16进制发送
                {
                    try //尝试将发送的数据转为16进制Hex
                    {
                        sendData = sendData.Replace(" ", "");//去除16进制数据中所有空格
                        sendData = sendData.Replace("\r", "");//去除16进制数据中所有换行
                        sendData = sendData.Replace("\n", "");//去除16进制数据中所有换行
                        if (sendData.Length == 1)//数据长度为1的时候,在数据前补0
                        {
                            sendData = "0" + sendData;
                        }
                        else if (sendData.Length % 2 != 0)//数据长度为奇数位时,去除最后一位数据
                        {
                            sendData = sendData.Remove(sendData.Length - 1, 1);
                        }

                        List sendData16 = new List();//将发送的数据,2个合为1个,然后放在该缓存里 如:123456→12,34,56
                        for (int i = 0; i < sendData.Length; i += 2)
                        {
                            sendData16.Add(sendData.Substring(i, 2));
                        }
                        sendBuffer = new byte[sendData16.Count];//sendBuffer的长度设置为:发送的数据2合1后的字节数
                        for (int i = 0; i < sendData16.Count; i++)
                        {
                            sendBuffer[i] = (byte)(Convert.ToInt32(sendData16[i], 16));//发送数据改为16进制
                        }
                    }
                    catch //无法转为16进制时,出现异常
                    {
                        UIAction(() =>
                        {
                            autoSendTickL.Stop();//关闭自动发送
                            MessageBox.Show("请输入正确的16进制数据");
                        });

                        Sending = false;//关闭正在发送状态
                        _ComSend.Abort();//终止本线程
                        return;//输入的16进制数据错误,无法发送,提示后返回  
                    }

                }
                else //ASCII码文本发送
                {
                    if(sendData == null)
                    {
                        return;
                    }
                    sendBuffer = System.Text.Encoding.Default.GetBytes(sendData);//转码
                    
                }
                try//尝试发送数据
                {//如果发送字节数大于1000,则每1000字节发送一次
                    int sendTimes = (sendBuffer.Length / 1000);//发送次数
                    for (int i = 0; i < sendTimes; i++)//每次发生1000Bytes
                    {
                        ComPortL.Write(sendBuffer, i * 1000, 1000);//发送sendBuffer中从第i * 1000字节开始的1000Bytes
                        UIAction(() =>//激活UI
                        {
                           // sendCount.Text = (Convert.ToInt32(sendCount.Text) + 1000).ToString();//刷新发送字节数
                        });
                    }
                    if (sendBuffer.Length % 1000 != 0)//发送字节小于1000Bytes或上面发送剩余的数据
                    {
                        ComPortL.Write(sendBuffer, sendTimes * 1000, sendBuffer.Length % 1000);
                        UIAction(() =>
                        {
                           // sendCount.Text = (Convert.ToInt32(sendCount.Text) + sendBuffer.Length % 1000).ToString();//刷新发送字节数
                        });
                    }


                }
                catch//如果无法发送,产生异常
                {
                    UIAction(() =>//激活UI
                    {
                        if (ComPortL.IsOpen == false)//如果ComPort.IsOpen == false,说明串口已丢失
                        {
                            SetComLose();//串口丢失后的设置
                        }
                        else
                        {
                            MessageBox.Show("无法发送数据,原因未知!");
                        }
                    });
                }
                //sendScrol.ScrollToBottom();//发送数据区滚动到底部
                Sending = false;//关闭正在发送状态
                _ComSend.Abort();//终止本线程
            }

        }

        //接收数据 中断只标志有数据需要读取,读取操作在中断外进行
        private void ComReceiveL(object sender, SerialDataReceivedEventArgs e)
        {
            if (WaitClose) return;//如果正在关闭串口,则直接返回
            //发送和接收均为文本时,接收中为加入判断是否为文字的算法,发送你(C4E3),接收可能识别为C4,E3,可用在这里加延时解决
            Thread.Sleep(5);
            if (recStaus)//如果已经开启接收
            {
                byte[] recBuffer;//接收缓冲区
                try
                {
                    recBuffer = new byte[ComPortL.BytesToRead];//接收数据缓存大小
                    ComPortL.Read(recBuffer, 0, recBuffer.Length);//读取数据
                    recQueue.Enqueue(recBuffer);//读取数据入列Enqueue(全局)
                }
                catch
                {
                    UIAction(() =>
                    {
                        if (ComPortL.IsOpen == false)//如果ComPort.IsOpen == false,说明串口已丢失
                        {
                            SetComLose();//串口丢失后相关设置
                        }
                        else
                        {
                            MessageBox.Show("无法接收数据,原因未知!");
                        }
                    });
                }

            }
            else//暂停接收
            {
                ComPortL.DiscardInBuffer();//清接收缓存
            }
        }
        void UIAction(Action action)//在主线程外激活线程方法
        {
            System.Threading.SynchronizationContext.SetSynchronizationContext(new System.Windows.Threading.DispatcherSynchronizationContext(App.Current.Dispatcher));
            System.Threading.SynchronizationContext.Current.Post(_ => action(), null);
        }
        private void SetComLose()//成功关闭串口或串口丢失后的设置
        {
            autoSendTickL.Stop();//串口丢失后要关闭自动发送
           //autoSendCheck.IsChecked = false;//自动发送改为未选中
            WaitClose = true;//;//激活正在关闭状态字,用于在串口接收方法的invoke里判断是否正在关闭串口
            while (Listening)//判断invoke是否结束
            {
                DispatcherHelper.DoEvents(); //循环时,仍进行等待事件中的进程,该方法为winform中的方法,WPF里面没有,这里在后面自己实现
            }
            MessageBox.Show("串口已丢失");
            WaitClose = false;//关闭正在关闭状态字,用于在串口接收方法的invoke里判断是否正在关闭串口
            
        }

        //模拟 Winfrom 中 Application.DoEvents() 详见 http://www.silverlightchina.net/html/study/WPF/2010/1216/4186.html?1292685167
        public static class DispatcherHelper
        {
            [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            public static void DoEvents()
            {
                DispatcherFrame frame = new DispatcherFrame();
                Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrames), frame);
                try { Dispatcher.PushFrame(frame); }
                catch (InvalidOperationException) { }
            }
            private static object ExitFrames(object frame)
            {
                ((DispatcherFrame)frame).Continue = false;
                return null;
            }
        }



       
        //获取串口端口号,依据Guid获取
        ///   
        /// 枚举win32 api  
        ///   
        public enum HardwareEnum
        {
            // 硬件  
            Win32_Processor, // CPU 处理器  
            Win32_PhysicalMemory, // 物理内存条  
            Win32_Keyboard, // 键盘  
            Win32_PointingDevice, // 点输入设备,包括鼠标。  
            Win32_FloppyDrive, // 软盘驱动器  
            Win32_DiskDrive, // 硬盘驱动器  
            Win32_CDROMDrive, // 光盘驱动器  
            Win32_BaseBoard, // 主板  
            Win32_BIOS, // BIOS 芯片  
            Win32_ParallelPort, // 并口  
            Win32_SerialPort, // 串口  
            Win32_SerialPortConfiguration, // 串口配置  
            Win32_SoundDevice, // 多媒体设置,一般指声卡。  
            Win32_SystemSlot, // 主板插槽 (ISA & PCI & AGP)  
            Win32_USBController, // USB 控制器  
            Win32_NetworkAdapter, // 网络适配器  
            Win32_NetworkAdapterConfiguration, // 网络适配器设置  
            Win32_Printer, // 打印机  
            Win32_PrinterConfiguration, // 打印机设置  
            Win32_PrintJob, // 打印机任务  
            Win32_TCPIPPrinterPort, // 打印机端口  
            Win32_POTSModem, // MODEM  
            Win32_POTSModemToSerialPort, // MODEM 端口  
            Win32_DesktopMonitor, // 显示器  
            Win32_DisplayConfiguration, // 显卡  
            Win32_DisplayControllerConfiguration, // 显卡设置  
            Win32_VideoController, // 显卡细节。  
            Win32_VideoSettings, // 显卡支持的显示模式。  

            // 操作系统  
            Win32_TimeZone, // 时区  
            Win32_SystemDriver, // 驱动程序  
            Win32_DiskPartition, // 磁盘分区  
            Win32_LogicalDisk, // 逻辑磁盘  
            Win32_LogicalDiskToPartition, // 逻辑磁盘所在分区及始末位置。  
            Win32_LogicalMemoryConfiguration, // 逻辑内存配置  
            Win32_PageFile, // 系统页文件信息  
            Win32_PageFileSetting, // 页文件设置  
            Win32_BootConfiguration, // 系统启动配置  
            Win32_ComputerSystem, // 计算机信息简要  
            Win32_OperatingSystem, // 操作系统信息  
            Win32_StartupCommand, // 系统自动启动程序  
            Win32_Service, // 系统安装的服务  
            Win32_Group, // 系统管理组  
            Win32_GroupUser, // 系统组帐号  
            Win32_UserAccount, // 用户帐号  
            Win32_Process, // 系统进程  
            Win32_Thread, // 系统线程  
            Win32_Share, // 共享  
            Win32_NetworkClient, // 已安装的网络客户端  
            Win32_NetworkProtocol, // 已安装的网络协议  
            Win32_PnPEntity,//all device  
        }
        ///   
        /// WMI取硬件信息  
        ///   
        ///   
        ///   
        ///   
        public static bool MulGetHardwareInfo(HardwareEnum hardType, string propKey, ref List strs)
        {

            try
            {
                using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from " + hardType))
                {
                    var hardInfos = searcher.Get();
                    foreach (var hardInfo in hardInfos)
                    {
                        //Console.WriteLine(hardInfo.Properties[propKey].Value.ToString());  将来限制的时候,在这里进行判断处理,

                        if (hardInfo.Properties[propKey].Value.ToString().ToUpper().Contains("COM") &&
                            hardInfo.Properties[propKey].Value.ToString().ToUpper().Contains("USB-SERIAL CH340"))
                        {
                            strs.Add(cutSerStr(hardInfo.Properties[propKey].Value.ToString()));
                        }

                    }
                    searcher.Dispose();
                }
                return false;
            }
            catch
            {
                return true;
            }

        }
        /// 
        /// 获取串口号
        /// 
        /// 
        public static string cutSerStr(string str)
        {

            int i = str.IndexOf("(");
            int j = str.LastIndexOf(")");
            return str.Substring(i + 1, j - i - 1);
        }


       
        //串口的开关闭合
        private void OpenPortBtn_Click(object sender, RoutedEventArgs e)
        {
            if(ComPortL.PortName.Length<0)
            {
                MessageBox.Show("端口不存在");
                return;
            }

              if(ComPortL.IsOpen == false)
              {

                  try //尝试打开串口
                  {
                      ComPortL.PortName = port_Collect[0];
                      ComPortL.BaudRate = 192000;
                      ComPortL.DataBits = 8;
                      ComPortL.Parity = Parity.None;
                      ComPortL.StopBits = StopBits.One;
                      ComPortL.ReceivedBytesThreshold = 1;

                    
                  }
                  catch
                  {
                      MessageBox.Show("无法打开串口,请检测此串口是否有效或被其他占用!");
                      return;//无法打开串口,提示后直接返回
                  }
                 

              }
              ComPortL.Open();
              WaitClose = false;//等待关闭串口状态改为false 
            
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            MessageBoxResult result = MessageBox.Show("确认是否要退出?", "退出", MessageBoxButton.YesNo);//显示确认窗口
            if (result == MessageBoxResult.No)
            {
                e.Cancel = true;//取消操作
            } 
        }

        private void Window_Closed(object sender, EventArgs e)
        {
            Application.Current.Shutdown();//先停止线程,然后终止进程.
            Environment.Exit(0);//直接终止进程.
        }

        private void ClosePortBtn_Click(object sender, RoutedEventArgs e)
        {
            ComPortL.Close();
            ComPortL.Dispose();
        }

        private void SendPortBtn_Click(object sender, RoutedEventArgs e)
        {
            //Send();
            this.Dispatcher.Invoke(new Action(delegate
            {
                LrecTBox.Clear();
            }));

            
        }

        private void check_Yes(object sender, RoutedEventArgs e)
        {
            if((bool)check.IsChecked == true)
            {
                autoSendTickL.Interval = TimeSpan.FromMilliseconds(0);//设置自动发送间隔
                autoSendTickL.Start();//开始自动发送定时器
            }
            else
            {
                autoSendTickL.Stop();
            }
        }

      

 
    }
}

以上基本包含了全部的功能,一些细节也指出了

你可能感兴趣的:(WPF,C#)