在好例子网,分享、交流、成长!
您当前所在位置:首页C# 开发实例C#网络编程 → 串口通讯源码(SerialCom)

串口通讯源码(SerialCom)

C#网络编程

下载此实例
  • 开发语言:C#
  • 实例大小:5.05M
  • 下载次数:165
  • 浏览次数:1028
  • 发布时间:2019-10-09
  • 实例类别:C#网络编程
  • 发 布 人:fzzsh
  • 文件格式:.rar
  • 所需积分:5

实例介绍

【实例简介】串口通讯源码

【实例截图】

【核心代码】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
/*
 * 版本V1.01
 * 基于.net4.5
 * 学C#和WPF编的第一个软件,整个编程过程,通过百度不断学习
 * 作者是做硬件的,只为学习做简单的上位机程序,C#简单,开发效率高,所以选择C#
 * 以前没有PC端软件编程经验,所以该编程思想继承于单片机编程思想,未用到面向对象和WPF的精髓,不建议模仿,仅供参考
 * 实际上到现在我还不知道面向对象是什么意思 ̄□ ̄||
 * 欢迎反馈BUG QQ45213212 E-MAIL lincolne@126.com
 */
#define MULTITHREAD//多线程收发模式,注释本句则使用单线程模式
//相对单线收发模式,占用系统资源稍微大些,但是执行效果更好,尤其是在大数据收发时的UI反应尤其明显      
using Microsoft.Win32;
using SerialComWindow;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
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 SerialCom
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        SerialPort ComPort = new SerialPort();//声明一个串口     
        private string[] ports;//可用串口数组
        private bool recStaus = true;//接收状态字
        private bool ComPortIsOpen = false;//COM口开启状态字,在打开/关闭串口中使用,这里没有使用自带的ComPort.IsOpen,因为在串口突然丢失的时候,ComPort.IsOpen会自动false,逻辑混乱
        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隔离
        IList<customer> comList = new List<customer>();//可用串口集合
        DispatcherTimer autoSendTick = new DispatcherTimer();//定时发送
#if MULTITHREAD
        private static bool Sending = false;//正在发送数据状态字
        private static Thread _ComSend;//发送数据线程
        Queue recQueue = new Queue();//接收数据过程中,接收数据线程与数据处理线程直接传递的队列,先进先出
        private  SendSetStr SendSet = new SendSetStr();//发送数据线程传递参数的结构体
        private  struct SendSetStr//发送数据线程传递参数的结构体格式
        {
            public string SendSetData;//发送的数据
            public bool? SendSetMode;//发送模式
        }
#endif
         
        public MainWindow()//主窗口
        {
            InitializeComponent();//控件初始化
             
        }
 
        private void Window_Loaded(object sender, RoutedEventArgs e)//主窗口初始化
        {
            //↓↓↓↓↓↓↓↓↓可用串口下拉控件↓↓↓↓↓↓↓↓↓
            ports= SerialPort.GetPortNames();//获取可用串口
            if (ports.Length > 0)//ports.Length > 0说明有串口可用
            {
                for (int i = 0; i < ports.Length; i  )
                {
                    comList.Add(new customer() { com = ports[i] });//下拉控件里添加可用串口
                }
                AvailableComCbobox.ItemsSource = comList;//资源路劲
                AvailableComCbobox.DisplayMemberPath = "com";//显示路径
                AvailableComCbobox.SelectedValuePath = "com";//值路径
                AvailableComCbobox.SelectedValue = ports[0];//默认选第1个串口
            }
            else//未检测到串口
            {
                MessageBox.Show("无可用串口");
            }
            //↑↑↑↑↑↑↑↑↑可用串口下拉控件↑↑↑↑↑↑↑↑↑
 
            //↓↓↓↓↓↓↓↓↓波特率下拉控件↓↓↓↓↓↓↓↓↓
            IList<customer> rateList = new List<customer>();//可用波特率集合
            rateList.Add(new customer() { BaudRate = "1200" });
            rateList.Add(new customer() { BaudRate = "2400" });
            rateList.Add(new customer() { BaudRate = "4800" });
            rateList.Add(new customer() { BaudRate = "9600" });
            rateList.Add(new customer() { BaudRate = "14400" });
            rateList.Add(new customer() { BaudRate = "19200" });
            rateList.Add(new customer() { BaudRate = "28800" });
            rateList.Add(new customer() { BaudRate = "38400" });
            rateList.Add(new customer() { BaudRate = "57600" });
            rateList.Add(new customer() { BaudRate = "115200" });
            RateListCbobox.ItemsSource = rateList;
            RateListCbobox.DisplayMemberPath = "BaudRate";
            RateListCbobox.SelectedValuePath = "BaudRate";
            //↑↑↑↑↑↑↑↑↑波特率下拉控件↑↑↑↑↑↑↑↑↑
 
            //↓↓↓↓↓↓↓↓↓校验位下拉控件↓↓↓↓↓↓↓↓↓
            IList<customer> comParity = new List<customer>();//可用校验位集合
            comParity.Add(new customer() { Parity = "None", ParityValue = "0" });
            comParity.Add(new customer() { Parity = "Odd",  ParityValue = "1" });
            comParity.Add(new customer() { Parity = "Even", ParityValue = "2" });
            comParity.Add(new customer() { Parity = "Mark", ParityValue = "3" });
            comParity.Add(new customer() { Parity = "Space", ParityValue = "4" });           
            ParityComCbobox.ItemsSource = comParity;
            ParityComCbobox.DisplayMemberPath = "Parity";
            ParityComCbobox.SelectedValuePath = "ParityValue";
            //↑↑↑↑↑↑↑↑↑校验位下拉控件↑↑↑↑↑↑↑↑↑
 
            //↓↓↓↓↓↓↓↓↓数据位下拉控件↓↓↓↓↓↓↓↓↓
            IList<customer> dataBits = new List<customer>();//数据位集合
            dataBits.Add(new customer() { Dbits = "8" });
            dataBits.Add(new customer() { Dbits = "7" });
            dataBits.Add(new customer() { Dbits = "6" });
            DataBitsCbobox.ItemsSource = dataBits;
            DataBitsCbobox.SelectedValuePath = "Dbits";
            DataBitsCbobox.DisplayMemberPath = "Dbits";
            //↑↑↑↑↑↑↑↑↑数据位下拉控件↑↑↑↑↑↑↑↑↑
 
            //↓↓↓↓↓↓↓↓↓停止位下拉控件↓↓↓↓↓↓↓↓↓
            IList<customer> stopBits = new List<customer>();//停止位集合
            stopBits.Add(new customer() { Sbits = "1" });
            stopBits.Add(new customer() { Sbits = "1.5" });
            stopBits.Add(new customer() { Sbits = "2" });
            StopBitsCbobox.ItemsSource = stopBits;
            StopBitsCbobox.SelectedValuePath = "Sbits";
            StopBitsCbobox.DisplayMemberPath = "Sbits";
            //↑↑↑↑↑↑↑↑↑停止位下拉控件↑↑↑↑↑↑↑↑↑
 
            //↓↓↓↓↓↓↓↓↓默认设置↓↓↓↓↓↓↓↓↓
            RateListCbobox.SelectedValue = "9600";//波特率默认设置9600
            ParityComCbobox.SelectedValue = "0";//校验位默认设置值为0,对应NONE
            DataBitsCbobox.SelectedValue = "8";//数据位默认设置8位
            StopBitsCbobox.SelectedValue = "1";//停止位默认设置1
            ComPort.ReadTimeout = 8000;//串口读超时8秒
            ComPort.WriteTimeout = 8000;//串口写超时8秒,在1ms自动发送数据时拔掉串口,写超时5秒后,会自动停止发送,如果无超时设定,这时程序假死
            ComPort.ReadBufferSize = 1024;//数据读缓存
            ComPort.WriteBufferSize = 1024;//数据写缓存
            sendBtn.IsEnabled = false;//发送按钮初始化为不可用状态
            sendModeCheck.IsChecked = false;//发送模式默认为未选中状态
            recModeCheck.IsChecked = false;//接收模式默认为未选中状态
            //↑↑↑↑↑↑↑↑↑默认设置↑↑↑↑↑↑↑↑↑
            ComPort.DataReceived  = new SerialDataReceivedEventHandler(ComReceive);//串口接收中断
            autoSendTick.Tick  = new EventHandler(autoSend);//定时发送中断
#if MULTITHREAD
            Thread _ComRec = new Thread(new ThreadStart(ComRec)); //查询串口接收数据线程声明
            _ComRec.Start();//启动线程
#endif
 
        }
 
 
        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)//关闭窗口closing
        {
            MessageBoxResult result = MessageBox.Show("确认是否要退出?", "退出", MessageBoxButton.YesNo);//显示确认窗口
            if (result == MessageBoxResult.No)
            {
                e.Cancel = true;//取消操作
            }
        }
 
        private void Window_Closed(object sender, EventArgs e)//关闭窗口确认后closed ALT F4
        {
            Application.Current.Shutdown();//先停止线程,然后终止进程.
            Environment.Exit(0);//直接终止进程.
        }
 
        public class customer//各下拉控件访问接口
        {
 
            public string com { get; set; }//可用串口
            public string com1 { get; set; }//可用串口
            public string BaudRate { get; set; }//波特率
            public string Parity { get; set; }//校验位
            public string ParityValue { get; set; }//校验位对应值
            public string Dbits { get; set; }//数据位
            public string Sbits { get; set; }//停止位
             
             
        }
 
 
        private void Button_Open(object sender, RoutedEventArgs e)//打开/关闭串口事件
        {
            if (AvailableComCbobox.SelectedValue == null)//先判断是否有可用串口
            {
                MessageBox.Show("无可用串口,无法打开!");
                return;//没有串口,提示后直接返回
            }
            #region 打开串口
            if (ComPortIsOpen == false)//ComPortIsOpen == false当前串口为关闭状态,按钮事件为打开串口
            {
                 
                try//尝试打开串口
                {
                    ComPort.PortName = AvailableComCbobox.SelectedValue.ToString();//设置要打开的串口
                    ComPort.BaudRate = Convert.ToInt32(RateListCbobox.SelectedValue);//设置当前波特率
                    ComPort.Parity = (Parity)Convert.ToInt32(ParityComCbobox.SelectedValue);//设置当前校验位
                    ComPort.DataBits = Convert.ToInt32(DataBitsCbobox.SelectedValue);//设置当前数据位
                    ComPort.StopBits = (StopBits)Convert.ToDouble(StopBitsCbobox.SelectedValue);//设置当前停止位                   
                    ComPort.Open();//打开串口
                     
                }
                catch//如果串口被其他占用,则无法打开
                {
                    MessageBox.Show("无法打开串口,请检测此串口是否有效或被其他占用!");
                    GetPort();//刷新当前可用串口
                    return;//无法打开串口,提示后直接返回
                }
 
                //↓↓↓↓↓↓↓↓↓成功打开串口后的设置↓↓↓↓↓↓↓↓↓
                openBtn.Content = "关闭串口";//按钮显示改为“关闭按钮”
                OpenImage.Source = new BitmapImage(new Uri("image\\On.png", UriKind.Relative));//开关状态图片切换为ON
                ComPortIsOpen = true;//串口打开状态字改为true
                WaitClose = false;//等待关闭串口状态改为false               
                sendBtn.IsEnabled = true;//使能“发送数据”按钮
                defaultSet.IsEnabled = false;//打开串口后失能重置功能
                AvailableComCbobox.IsEnabled = false;//失能可用串口控件
                RateListCbobox.IsEnabled = false;//失能可用波特率控件
                ParityComCbobox.IsEnabled = false;//失能可用校验位控件
                DataBitsCbobox.IsEnabled = false;//失能可用数据位控件
                StopBitsCbobox.IsEnabled = false;//失能可用停止位控件
                //↑↑↑↑↑↑↑↑↑成功打开串口后的设置↑↑↑↑↑↑↑↑↑
 
                if (autoSendCheck.IsChecked == true)//如果打开前,自动发送控件就被选中,则打开串口后自动开始发送数据
                {
                    autoSendTick.Interval = TimeSpan.FromMilliseconds(Convert.ToInt32(Time.Text));//设置自动发送间隔
                    autoSendTick.Start();//开启自动发送
                }
 
             }
            #endregion
            #region 关闭串口
            else//ComPortIsOpen == true,当前串口为打开状态,按钮事件为关闭串口
            {               
                try//尝试关闭串口
                {
                    autoSendTick.Stop();//停止自动发送
                    autoSendCheck.IsChecked = false;//停止自动发送控件改为未选中状态
                    ComPort.DiscardOutBuffer();//清发送缓存
                    ComPort.DiscardInBuffer();//清接收缓存
                    WaitClose = true;//激活正在关闭状态字,用于在串口接收方法的invoke里判断是否正在关闭串口
                    while (Listening)//判断invoke是否结束
                    {
                        DispatcherHelper.DoEvents(); //循环时,仍进行等待事件中的进程,该方法为winform中的方法,WPF里面没有,这里在后面自己实现
                    }
                    ComPort.Close();//关闭串口
                    WaitClose = false;//关闭正在关闭状态字,用于在串口接收方法的invoke里判断是否正在关闭串口
                    SetAfterClose();//成功关闭串口或串口丢失后的设置
                }
                 
                catch//如果在未关闭串口前,串口就已丢失,这时关闭串口会出现异常
                {
                    if (ComPort.IsOpen == false)//判断当前串口状态,如果ComPort.IsOpen==false,说明串口已丢失
                    {
                        SetComLose();
                    }
                    else//未知原因,无法关闭串口
                    {
                        MessageBox.Show("无法关闭串口,原因未知!");
                        return;//无法关闭串口,提示后直接返回
                    }
                }
            }
            #endregion
 
        }
 
        private void Button_Send(object sender, RoutedEventArgs e)//发送数据按钮点击事件
        {
            Send();//调用发送方法
        }
 
        void autoSend(object sender, EventArgs e)//自动发送
        {
            Send();//调用发送方法
        }
        void Send()//发送数据,分为多线程方式和单线程方式
        {
#if MULTITHREAD
            if (Sending == true) return;//如果当前正在发送,则取消本次发送,本句注释后,可能阻塞在ComSend的lock处
            _ComSend = new Thread(new ParameterizedThreadStart(ComSend)); //new发送线程
            SendSet.SendSetData = sendTBox.Text;//发送数据线程传递参数的结构体--发送的数据
            SendSet.SendSetMode = sendModeCheck.IsChecked;//发送数据线程传递参数的结构体--发送方式
            _ComSend.Start(SendSet);//发送线程启动
#else
            ComSend();//单线程发送方法
#endif
        }
#if MULTITHREAD
        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<string> sendData16 = new List<string>();//将发送的数据,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(() =>
                        {
                            autoSendCheck.IsChecked = false;//自动发送改为未选中
                            autoSendTick.Stop();//关闭自动发送
                            MessageBox.Show("请输入正确的16进制数据");
                        });
 
                        Sending = false;//关闭正在发送状态
                        _ComSend.Abort();//终止本线程
                        return;//输入的16进制数据错误,无法发送,提示后返回 
                    }
 
                }
                else //ASCII码文本发送
                {
                    sendBuffer = System.Text.Encoding.Default.GetBytes(sendData);//转码
                }
                try//尝试发送数据
                {//如果发送字节数大于1000,则每1000字节发送一次
                        int sendTimes = (sendBuffer.Length / 1000);//发送次数
                        for (int i = 0; i < sendTimes; i  )//每次发生1000Bytes
                        {
                            ComPort.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或上面发送剩余的数据
                        {
                            ComPort.Write(sendBuffer, sendTimes * 1000, sendBuffer.Length % 1000);
                            UIAction(() =>
                            {
                                sendCount.Text = (Convert.ToInt32(sendCount.Text)   sendBuffer.Length % 1000).ToString();//刷新发送字节数
                            });
                        }
                    
 
                }
                catch//如果无法发送,产生异常
                {
                    UIAction(() =>//激活UI
                    {
                        if (ComPort.IsOpen == false)//如果ComPort.IsOpen == false,说明串口已丢失
                        {
                            SetComLose();//串口丢失后的设置
                        }
                        else
                        {
                            MessageBox.Show("无法发送数据,原因未知!");
                        }
                    });
                }
                //sendScrol.ScrollToBottom();//发送数据区滚动到底部
                Sending = false;//关闭正在发送状态
                _ComSend.Abort();//终止本线程
            }
             
        }
#else
        private void ComSend()//发送数据 普通方法,发送数据过程中UI会失去响应
        {
 
            byte[] sendBuffer = null;//发送数据缓冲区
            string sendData = sendTBox.Text;//复制发送数据,以免发送过程中数据被手动改变
            if (sendModeCheck.IsChecked == 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<string> sendData16 = new List<string>();//将发送的数据,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进制时,出现异常
                {
                    autoSendCheck.IsChecked = false;//自动发送改为未选中
                    autoSendTick.Stop();//关闭自动发送
                    MessageBox.Show("请输入正确的16进制数据");
                    return;//输入的16进制数据错误,无法发送,提示后返回
                }
 
            }
            else //ASCII码文本发送
            {
                sendBuffer = System.Text.Encoding.Default.GetBytes(sendData);//转码
            }
 
            try//尝试发送数据
            {//如果发送字节数大于1000,则每1000字节发送一次
                int sendTimes = (sendBuffer.Length / 1000);//发送次数
                for (int i = 0; i < sendTimes; i  )//每次发生1000Bytes
                {
                    ComPort.Write(sendBuffer, i*1000, 1000);//发送sendBuffer中从第i * 1000字节开始的1000Bytes
                    sendCount.Text = (Convert.ToInt32(sendCount.Text)   1000).ToString();//刷新发送字节数
                }
                if (sendBuffer.Length % 1000 != 0)
                {
                    ComPort.Write(sendBuffer, sendTimes * 1000, sendBuffer.Length % 1000);//发送字节小于1000Bytes或上面发送剩余的数据
                    sendCount.Text = (Convert.ToInt32(sendCount.Text)   sendBuffer.Length % 1000).ToString();//刷新发送字节数
                }
            }
            catch//如果无法发送,产生异常
            {
                if (ComPort.IsOpen == false)//如果ComPort.IsOpen == false,说明串口已丢失
                {
                    SetComLose();//串口丢失后相关设置
                }
                else
                {
                    MessageBox.Show("无法发送数据,原因未知!");
                }
            }
            //sendScrol.ScrollToBottom();//发送数据区滚动到底部
 
        }
#endif
 
 
#if MULTITHREAD
        private void ComReceive(object sender, SerialDataReceivedEventArgs e)//接收数据 中断只标志有数据需要读取,读取操作在中断外进行
        {
            if (WaitClose) return;//如果正在关闭串口,则直接返回
            Thread.Sleep(10);//发送和接收均为文本时,接收中为加入判断是否为文字的算法,发送你(C4E3),接收可能识别为C4,E3,可用在这里加延时解决
            if (recStaus)//如果已经开启接收
            {
                byte[] recBuffer;//接收缓冲区
                try
                {
                    recBuffer = new byte[ComPort.BytesToRead];//接收数据缓存大小
                    ComPort.Read(recBuffer, 0, recBuffer.Length);//读取数据
                    recQueue.Enqueue(recBuffer);//读取数据入列Enqueue(全局)
                }
                catch
                {
                    UIAction(() =>
                    {
                        if (ComPort.IsOpen == false)//如果ComPort.IsOpen == false,说明串口已丢失
                        {
                            SetComLose();//串口丢失后相关设置
                        }
                        else
                        {
                            MessageBox.Show("无法接收数据,原因未知!");
                        }
                    });
                 }
                  
            }
            else//暂停接收
            {
                ComPort.DiscardInBuffer();//清接收缓存
            }
        }
        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.IsChecked == false)//接收模式为ASCII文本模式
                        {
                            recTBox.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。
                            }
                            recTBox.Text  = recBuffer16.ToString();//加显到接收区
                        }
                        recCount.Text = (Convert.ToInt32(recCount.Text)   recBuffer.Length).ToString();//接收数据字节数
                        recScrol.ScrollToBottom();//接收文本框滚动至底部
                    });
                }
                else
                  Thread.Sleep(100);//如果不延时,一直查询,将占用CPU过高
            }
             
        }
#else
        private void ComReceive(object sender, SerialDataReceivedEventArgs e)//接收数据 数据在接收中断里面处理
        {
            if (WaitClose) return;//如果正在关闭串口,则直接返回
            if (recStaus)//如果已经开启接收
            {
                try
                {
                    Listening = true;////设置标记,说明我已经开始处理数据,一会儿要使用系统UI的。
                    Thread.Sleep(10);//发送和接收均为文本时,接收中为加入判断是否为文字的算法,发送你(C4E3),接收可能识别为C4,E3,可用在这里加延时解决
                    string recData;//接收数据转码后缓存
                    byte[] recBuffer = new byte[ComPort.BytesToRead];//接收数据缓存
                    ComPort.Read(recBuffer, 0, recBuffer.Length);//读取数据
                    recData = System.Text.Encoding.Default.GetString(recBuffer);//转码
                    this.recTBox.Dispatcher.Invoke(//WPF为单线程,此接收中断线程不能对WPF进行操作,用如下方法才可操作
                    new Action(
                         delegate
                         {
                             recCount.Text = (Convert.ToInt32(recCount.Text)   recBuffer.Length).ToString();//接收数据字节数
                             if (recModeCheck.IsChecked == false)//接收模式为ASCII文本模式
                             {
                                 recTBox.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。
                                 }
                                 recTBox.Text  = recBuffer16.ToString();//加显到接收区
                             }
                             recScrol.ScrollToBottom();//接收文本框滚动至底部
                         }
                    )
                    );
 
                }
                finally
                {
                    Listening = false;//UI使用结束,用于关闭串口时判断,避免自动发送时拔掉串口,陷入死循环
                }
 
            }
            else//暂停接收
            {
                ComPort.DiscardInBuffer();//清接收缓存
            }
 
 
        }
#endif
        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 SetAfterClose()//成功关闭串口或串口丢失后的设置
        {
 
            openBtn.Content = "打开串口";//按钮显示为“打开串口”
            OpenImage.Source = new BitmapImage(new Uri("image\\Off.png", UriKind.Relative));
            ComPortIsOpen = false;//串口状态设置为关闭状态
            sendBtn.IsEnabled = false;//失能发送数据按钮
            defaultSet.IsEnabled = true;//打开串口后使能重置功能
            AvailableComCbobox.IsEnabled = true;//使能可用串口控件
            RateListCbobox.IsEnabled = true;//使能可用波特率下拉控件
            ParityComCbobox.IsEnabled = true;//使能可用校验位下拉控件
            DataBitsCbobox.IsEnabled = true;//使能数据位下拉控件
            StopBitsCbobox.IsEnabled = true;//使能停止位下拉控件
        }
        private void SetComLose()//成功关闭串口或串口丢失后的设置
        {
            autoSendTick.Stop();//串口丢失后要关闭自动发送
            autoSendCheck.IsChecked = false;//自动发送改为未选中
            WaitClose = true;//;//激活正在关闭状态字,用于在串口接收方法的invoke里判断是否正在关闭串口
            while (Listening)//判断invoke是否结束
            {
                DispatcherHelper.DoEvents(); //循环时,仍进行等待事件中的进程,该方法为winform中的方法,WPF里面没有,这里在后面自己实现
            }
            MessageBox.Show("串口已丢失");
            WaitClose = false;//关闭正在关闭状态字,用于在串口接收方法的invoke里判断是否正在关闭串口
            GetPort();//刷新可用串口
            SetAfterClose();//成功关闭串口或串口丢失后的设置
        }
 
       
        private void AvailableComCbobox_PreviewMouseDown(object sender, MouseButtonEventArgs e)//刷新可用串口
        {
            GetPort();//刷新可用串口
        }
 
 
        private void GetPort()//刷新可用串口
        {
 
            comList.Clear();//情况控件链接资源
            AvailableComCbobox.DisplayMemberPath = "com1";
            AvailableComCbobox.SelectedValuePath = null;//路径都指为空,清空下拉控件显示,下面重新添加
 
            ports = new string[SerialPort.GetPortNames().Length];//重新定义可用串口数组长度
            ports = SerialPort.GetPortNames();//获取可用串口
            if (ports.Length > 0)//有可用串口
            {
                for (int i = 0; i < ports.Length; i  )
                {
                    comList.Add(new customer() { com = ports[i] });//下拉控件里添加可用串口
                }
                AvailableComCbobox.ItemsSource = comList;//可用串口下拉控件资源路径
                AvailableComCbobox.DisplayMemberPath = "com";//可用串口下拉控件显示路径
                AvailableComCbobox.SelectedValuePath = "com";//可用串口下拉控件值路径
            }
 
 
        }
 
       
        private void sendClearBtn_Click(object sender, RoutedEventArgs e)//清空发送区
        {
            sendTBox.Text = "";
        }
 
        private void recClearBtn_Click(object sender, RoutedEventArgs e)//清空接收区
        {
            recTBox.Text = "";
        }
 
 
        private void countClearBtn_Click(object sender, RoutedEventArgs e)//计数清零
        {
            sendCount.Text = "0";
            recCount.Text = "0";
        }
 
        private void stopRecBtn_Click(object sender, RoutedEventArgs e)//暂停/开启接收按钮事件
        {
            if (recStaus == true)//当前为开启接收状态
            {
                recStaus = false;//暂停接收
                stopRecBtn.Content = "开启接收";//按钮显示为开启接收
                recPrompt.Visibility = Visibility.Visible;//显示已暂停接收提示
                recBorder.Opacity = 0;//接收区透明度改为0
            }
            else//当前状态为关闭接收状态
            {
                recStaus = true;//开启接收
                stopRecBtn.Content = "暂停接收";//按钮显示状态改为暂停接收
                recPrompt.Visibility = Visibility.Hidden;//隐藏已暂停接收提示
                recBorder.Opacity = 0.4;////接收区透明度改为0.4
            }  
        }
 
        
         
 
        private void autoSendCheck_Click(object sender, RoutedEventArgs e)//自动发送控件点击事件
        {
 
            if (autoSendCheck.IsChecked == true && ComPort.IsOpen == true)//如果当前状态为开启自动发送且串口已打开,则开始自动发送
            {
                autoSendTick.Interval = TimeSpan.FromMilliseconds(Convert.ToInt32(Time.Text));//设置自动发送间隔
                autoSendTick.Start();//开始自动发送定时器
            }
            else//点击之前为开启自动发送状态,点击后关闭自动发送
            {
                autoSendTick.Stop();//关闭自动发送定时器
            }
        }
 
        private void Time_KeyDown(object sender, KeyEventArgs e)//发送周期文本控件-键盘按键事件
        {
            if (e.Key >= Key.D0 && e.Key <= Key.D9 || e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9)//只能输入数字
            {
                e.Handled = false;
            }
            else e.Handled = true;
            if (e.Key == Key.Enter)//输入回车
            {
                if (Time.Text.Length == 0 || Convert.ToInt32(Time.Text) == 0)//时间为空或时间等于0,设置为1000
                {
                    Time.Text = "1000";
                }
                autoSendTick.Interval = TimeSpan.FromMilliseconds(Convert.ToInt32(Time.Text));//设置自动发送周期
            }
        }
 
        private void Time_TextChanged(object sender, TextChangedEventArgs e)//发送周期文本控件-文本内容改变事件,与上面Time_KeyDown事件相比,可以防止粘贴进来非数字数据
        {
            //只允许输入数字
            TextBox textBox = sender as TextBox;
            TextChange[] change = new TextChange[e.Changes.Count];
            byte[] checkText = new byte[textBox.Text.Length];
            bool result = true;
            e.Changes.CopyTo(change, 0);
            int offset = change[0].Offset;
            checkText = System.Text.Encoding.Default.GetBytes(textBox.Text);
            for (int i = 0; i < textBox.Text.Length; i  )
            {
                result &= 0x2F < (Convert.ToInt32(checkText[i])) & (Convert.ToInt32(checkText[i])) < 0x3A;//0x2f-0x3a之间是数字0-10的ASCII码
            }
            if (change[0].AddedLength > 0)
            {
                 
                if (!result || Convert.ToInt32(textBox.Text) > 100000)//不是数字或数据大于100000,取消本次change
                {
                    textBox.Text = textBox.Text.Remove(offset, change[0].AddedLength);
                    textBox.Select(offset, 0);
                }
            }
             
        }
 
        private void Time_LostFocus(object sender, RoutedEventArgs e)//发送周期文本控件-失去事件
        {
            if (Time.Text.Length == 0 || Convert.ToInt32(Time.Text) == 0)//时间为空或时间等于0,设置为1000
            {
                Time.Text = "1000";
            }
            autoSendTick.Interval = TimeSpan.FromMilliseconds(Convert.ToInt32(Time.Text));//设置自动发送周期
        }
 
        //模拟 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;
            }
        }
 
        private void defaultSet_Click(object sender, RoutedEventArgs e)//重置按钮click事件
        {
            RateListCbobox.SelectedValue = "9600";//波特率默认设置9600
            ParityComCbobox.SelectedValue = "0";//校验位默认设置值为0,对应NONE
            DataBitsCbobox.SelectedValue = "8";//数据位默认设置8位
            StopBitsCbobox.SelectedValue = "1";//停止位默认设置1
        }
        private void FileOpen(object sender, ExecutedRoutedEventArgs e)//打开文件快捷键事件crtl O
        {
            OpenFileDialog open_fd = new OpenFileDialog();//调用系统打开文件窗口
            open_fd.Filter = "TXT文本|*.txt";//文件过滤器
            if (open_fd.ShowDialog() == true)//选择了文件
            {
                sendTBox.Text = File.ReadAllText(open_fd.FileName);//读TXT方法1 简单,快捷,为StreamReader的封装
                //StreamReader sr = new StreamReader(open_fd.FileName);//读TXT方法2 复杂,功能强大
                //sendTBox.Text = sr.ReadToEnd();//调用ReadToEnd方法读取选中文件的全部内容
                //sr.Close();//关闭当前文件读取流
            }
        }
        private void FileSave(object sender, RoutedEventArgs e)//保存数据按钮crtl S
        {
            SaveModWindow SaveMod = new SaveModWindow();//new保存数据方式窗口
            SaveMod.Owner = this;//赋予主窗口,子窗口打开后,再次点击主窗口,子窗口闪烁
            SaveMod.ShowDialog();//ShowDialog方式打开保存数据方式窗口
            if (SaveMod.mode == "new")//保存为新文件
            {
                SaveNew();//保存为新文件
            }
            else if (SaveMod.mode == "old")//保存到已有文件
            {
                SaveOld();//保存到已有文件
            }
            else//取消
            {
                return;
            }
 
        }
        private void SaveNew_Click(object sender, RoutedEventArgs e)//文件-保存-保存为新文件click事件
        {
            SaveNew();//保存为新文件
        }
        private void SaveOld_Click(object sender, RoutedEventArgs e)//文件-保存-保存到已有文件click事件
        {
            SaveOld();//保存到已有文件
        }
        private void SaveNew()//保存为新文件
        {
            if (recTBox.Text == string.Empty)//接收区数据为空
            {
                MessageBox.Show("接收区为空,无法保存!");
            }
            else
            {
                SaveFileDialog Save_fd = new SaveFileDialog();//调用系统保存文件窗口
                Save_fd.Filter = "TXT文本|*.txt";//文件过滤器
                if (Save_fd.ShowDialog() == true)//选择了文件
                {
                    File.WriteAllText(Save_fd.FileName, recTBox.Text);//写入新的数据
                    File.AppendAllText(Save_fd.FileName, "\r\n------"   DateTime.Now.ToString()   "\r\n");//数据后面写入时间戳
                    MessageBox.Show("保存成功!");
                }
                 
            }
        }
        private void SaveOld()//保存到已有文件
        {
            if (recTBox.Text == string.Empty)//接收区数据为空
            {
                MessageBox.Show("接收区为空,无法保存!");
            }
            else
            {
                OpenFileDialog Open_fd = new OpenFileDialog();//调用系统保存文件窗口
                Open_fd.Filter = "TXT文本|*.txt";//文件过滤器
                if (Open_fd.ShowDialog() == true)//选择了文件
                {
                    File.AppendAllText(Open_fd.FileName, recTBox.Text);//在打开文件末尾写入数据
                    File.AppendAllText(Open_fd.FileName, "\r\n------"   DateTime.Now.ToString()   "\r\n");//数据后面写入时间戳
                    MessageBox.Show("添加成功!");
                }
            }
        }
 
        private void info_click(object sender, RoutedEventArgs e)//帮助-关于click事件
        {
             InfoWindow info = new InfoWindow();//new关于窗口
             info.Owner = this;//赋予主窗口,子窗口打开后,再次点击主窗口,子窗口闪烁
             info.Show();//ShowDialog方式打开关于窗口
        }
 
        private void feedBack_Click(object sender, RoutedEventArgs e)//帮助-反馈click事件
        {
            FeedBackWindow feedBack = new FeedBackWindow();//new反馈窗口
            feedBack.Owner = this;//赋予主窗口,子窗口打开后,再次点击主窗口,子窗口闪烁
            feedBack.ShowDialog();//ShowDialog方式打开反馈窗口
        }
 
 
    }
}

实例下载地址

串口通讯源码(SerialCom)

不能下载?内容有错? 点击这里报错 + 投诉 + 提问

好例子网口号:伸出你的我的手 — 分享

网友评论

发表评论

(您的评论需要经过审核才能显示)

查看所有0条评论>>

小贴士

感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。

  • 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
  • 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
  • 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
  • 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。

关于好例子网

本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明

;
报警