C#教程
主页 > 软件编程 > C#教程 >

c#模拟串口通信SerialPort的实现

2022-05-11 | 秩名 | 点击:

一、前导知识

串行口是计算机的标准接口,现在的PC机(个人电脑)一般至少有两个串行口COM1和COM2。串行口应用广泛,在数据通信、计算机网络以及分布式工业控制系统中,经常采用串行通信来交换数据和信息

电气标准及协议来分包括RS-232-C、RS-422、RS485、USB(Universal Serial Bus)等

实现串口通信的必要设置

串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。

对于两个进行通行的端口,这些参数必须匹配:

波特率

这是一个衡量通信速度的参数。它表示**每秒钟传送的bit的个数**。例如300波特表示每秒钟发送300个bit,波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB设备的通信

数据位

这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、7和8位,如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。

停止位

用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢

奇偶校验位

 在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位位1,这样就有3个逻辑高位。高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步

二、实验

我们将通过模拟串口通信,在pc机上进行两个串口(COM1、COM2)的交互

需要用到的软件:

Launch Virtual Serial Port Driver Pro:虚拟串口。使用它来模拟两个串口的连接

绘制窗口

 代码实现

1.使用SerialPort控制串口

1

private SerialPort sp1 = new SerialPort();

2.打开串口

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

private void button2_Click(object sender, EventArgs e)

{

    if (!sp1.IsOpen)

    {

        try

        {

            //串口号

            sp1.PortName = "COM1";

            //波特率

            sp1.BaudRate = 115200;

            //数据位

            sp1.DataBits = 8;

            //停止位

            sp1.StopBits = StopBits.One;

            //奇偶校验位

            sp1.Parity = Parity.Even;

            //DataReceived事件发送前,内部缓冲区里的字符数

            sp1.ReceivedBytesThreshold = 1;

            sp1.RtsEnable = true; sp1.DtrEnable = true; sp1.ReadTimeout = 3000;

           // Control.CheckForIllegalCrossThreadCalls = false;

            //表示将处理 System.IO.Ports.SerialPort 对象的数据接收事件的方法。

            sp1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(sp1_DataReceived_1);

            //打开串口

            sp1.Open();

            MessageBox.Show("COM1打开成功!");

        }

        catch (Exception ex)

        {

            MessageBox.Show("COM1打开失败!");

        }

    }

    else

    {

        MessageBox.Show("COM1打开成功!");

    }

}

3.关闭串口

1

2

3

4

5

6

7

8

private void button3_Click(object sender, EventArgs e)

{

    if (sp1.IsOpen)

    {

        sp1.Close();

        MessageBox.Show("COM1关闭成功!");

    }

}

串口2的打开和关闭同理串口1实现

4.发送

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

private void button1_Click(object sender, EventArgs e)

{

    if (sp1.IsOpen)

    {

        if (!string.IsNullOrEmpty(this.textBox1.Text))

        {

            sp1.WriteLine(this.textBox1.Text+"\r\n");

 

        }

        else

        {

            MessageBox.Show("发送数据为空");

        }

    }

    else

    {

        MessageBox.Show("COM1未打开!");

    }

 

 

}

5.接收

1

2

3

4

5

6

7

8

9

10

11

12

13

14

StringBuilder builder1 = new StringBuilder();

//在接收到了ReceivedBytesThreshold设置的字符个数或接收到了文件结束字符并将其放入了输入缓冲区时被触发

public void sp1_DataReceived_1(object sender, SerialDataReceivedEventArgs e)

{

    Console.WriteLine("接收中...");

    int n = sp1.BytesToRead;      //先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致

    byte[] buf = new byte[n];   //声明一个临时数组存储当前来的串口数据

    sp1.Read(buf, 0, n);      //读取缓冲数据

    builder1.Remove(0, builder1.Length); //清除字符串构造器的内容

    builder1.Append(Encoding.ASCII.GetString(buf));

    string comdata = builder1.ToString();

    Console.WriteLine("data: + " + comdata);

    this.Invoke(settextevent,comdata);

}

这里仅仅实现了一般的接收方式,并不严谨和健壮

测试

使用软件模拟串口连接

 打开两个程序

 在一程序中打开串口1,在二程序中打开串口2,发送消息

在一程序中输入字符"hello,HanHanCheng!",发现在二程序中接收到,同样,在二程序中输入,在一中也能收到

 三、总结

1.由于是异步线程接收,在接收中需要使用委托来跨线程调用组件

1

2

3

4

5

6

7

8

public delegate void settext(string text);

public event settext settextevent;

public void set(string text)

{

    this.textBox2.Text = text;

}

//再注册

settextevent += set;

2.DataReceived事件触发条件需要注意,可能在实现时,无法触发导致无法接收。

触发条件是:在接收到了ReceivedBytesThreshold设置的字符个数或接收到了文件结束字符并将其放入了输入缓冲区时被触发

四、附件完整代码

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

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;

  

namespace Training_USBCOM

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

            settextevent += set;

        }

  

        private SerialPort sp1 = new SerialPort();

        StringBuilder builder = new StringBuilder();

        private void button1_Click(object sender, EventArgs e)

        {

            if (sp1.IsOpen)

            {

                if (!string.IsNullOrEmpty(this.textBox1.Text))

                {

                    sp1.WriteLine(this.textBox1.Text+"\r\n");

  

                }

                else

                {

                    MessageBox.Show("发送数据为空");

                }

            }

            else

            {

                MessageBox.Show("COM1未打开!");

            }

  

  

        }

  

        public delegate void settext(string text);

        public event settext settextevent;

        public void set(string text)

        {

            this.textBox2.Text = text;

        }

         

  

        StringBuilder builder1 = new StringBuilder();

        //在接收到了ReceivedBytesThreshold设置的字符个数或接收到了文件结束字符并将其放入了输入缓冲区时被触发

        public void sp1_DataReceived_1(object sender, SerialDataReceivedEventArgs e)

        {

            Console.WriteLine("接收中...");

            int n = sp1.BytesToRead;      //先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致

            byte[] buf = new byte[n];   //声明一个临时数组存储当前来的串口数据

            sp1.Read(buf, 0, n);      //读取缓冲数据

            builder1.Remove(0, builder1.Length); //清除字符串构造器的内容

            builder1.Append(Encoding.ASCII.GetString(buf));

            string comdata = builder1.ToString();

            Console.WriteLine("data: + " + comdata);

            this.Invoke(settextevent,comdata);

        }

  

        private void button2_Click(object sender, EventArgs e)

        {

            if (!sp1.IsOpen)

            {

                try

                {

                    //串口号

                    sp1.PortName = "COM1";

                    //波特率

                    sp1.BaudRate = 115200;

                    //数据位

                    sp1.DataBits = 8;

                    //停止位

                    sp1.StopBits = StopBits.One;

                    //奇偶校验位

                    sp1.Parity = Parity.Even;

                    //DataReceived事件发送前,内部缓冲区里的字符数

                    sp1.ReceivedBytesThreshold = 1;

                    sp1.RtsEnable = true; sp1.DtrEnable = true; sp1.ReadTimeout = 3000;

                   // Control.CheckForIllegalCrossThreadCalls = false;

                    //表示将处理 System.IO.Ports.SerialPort 对象的数据接收事件的方法。

                    sp1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(sp1_DataReceived_1);

                    //打开串口

                    sp1.Open();

                    MessageBox.Show("COM1打开成功!");

                }

                catch (Exception ex)

                {

                    MessageBox.Show("COM1打开失败!");

                }

            }

            else

            {

                MessageBox.Show("COM1打开成功!");

            }

        }

  

        private void button3_Click(object sender, EventArgs e)

        {

            if (sp1.IsOpen)

            {

                sp1.Close();

                MessageBox.Show("COM1关闭成功!");

            }

        }

  

        private void button5_Click(object sender, EventArgs e)

        {

            if (!sp1.IsOpen)

            {

                try

                {

                    //串口号

                    sp1.PortName = "COM2";

                    //波特率

                    sp1.BaudRate = 115200;

                    //数据位

                    sp1.DataBits = 8;

                    //停止位

                    sp1.StopBits = StopBits.One;

                    //奇偶校验位

                    sp1.Parity = Parity.Even;

                    sp1.ReceivedBytesThreshold = 1;

                    sp1.RtsEnable = true; sp1.DtrEnable = true; sp1.ReadTimeout = 3000;

                    Control.CheckForIllegalCrossThreadCalls = false;

                    //表示将处理 System.IO.Ports.SerialPort 对象的数据接收事件的方法。

                    sp1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(sp1_DataReceived_1);

                    //打开串口

                    sp1.Open();

                    MessageBox.Show("COM2打开成功!");

                }

                catch (Exception ex)

                {

                    MessageBox.Show("COM2打开失败!");

                }

            }

            else

            {

                MessageBox.Show("COM2打开成功!");

            }

        }

  

        private void button4_Click(object sender, EventArgs e)

        {

            if (sp1.IsOpen)

            {

                sp1.Close();

                MessageBox.Show("COM2关闭成功!");

            }

        }

    }

}

原文链接:https://blog.csdn.net/hey_lie/article/details/120194909
相关文章
最新更新