在C#中使用串口通信比较方便,.Net 提供了现成的类, SerialPort类。
本文不对原理啥的进行介绍,只介绍SerialPort类的使用。
SerialProt类内部是调用了CreateFile,WriteFile等WinAPI函数来实现串口通信。
在后期的Windows编程系列中,我会详细介绍Windows的IO

打开串口
1 SerialPort sp = new SerialPort();
2
3 public bool OpenSerialPort(string portName, string strBaudRate, string strDataBit, int stopBitIndex, int parityIndex)
4 {
5
6 if (sp.IsOpen)
7 sp.Close();
8
9 sp.PortName = portName; //串口号
10 sp.BaudRate = Convert.ToInt32(strBaudRate); //波特率
11 sp.DataBits = Convert.ToInt32(strDataBit); //数据位
12 sp.StopBits = (StopBits)stopBitIndex; //停止位
13 sp.Parity = (Parity)parityIndex; //奇偶校验位
14 sp.RtsEnable = true; //启用请求发送 (RTS) 信号
15 sp.ReadTimeout = 500; //超时时间
16
17 sp.Open(); //打开串口
18
19 }
关闭串口
1 public bool CloseSerialPort()
2 {
3 try
4 {
5 if (sp.IsOpen == true)
6 {
7 sp.Close(); //关闭串口
8 return true;
9 }
10 catch
11 {
12 return false;
13 }
14 }
发送普通字符数据
1 public bool SendNormalString(string data)
2 {
3 try
4 {
5 sp.Encoding = Encoding.ASCII;
6 sp.Write(data);
7 return true;
8 }
9 catch
10 {
11 return false;
12 }
13 }
发送十六进制数据
1 /// <summary>
2 /// 转换成ASCII字符
3 /// </summary>
4 /// <param name="str"></param>
5 /// <param name="format"></param>
6 /// <returns></returns>
7 private string GetASCIIString(string str, string format = "X2")
8 {
9 var asciiStr = "";
10
11 for (int i = 0; i < str.Length; i++)
12 {
13 var asciiCode = (int)str[i];
14 asciiStr += asciiCode.ToString(format);
15 }
16
17 return asciiStr;
18 }
1 /// <summary>
2 /// ASCII字符转换成十六进制字节数组
3 /// </summary>
4 /// <param name="str"></param>
5 /// <returns></returns>
6 private byte[] ASCIIStringToHexByteArray(string str)
7 {
8 try
9 {
10 str = str.Replace(" ", "");
11
12 byte[] buffer = new byte[str.Length / 2];
13
14 for (int i = 0; i < str.Length; i += 2)
15 {
16 buffer[i / 2] = Convert.ToByte(str.Substring(i, 2), 16);
17 }
18
19 return buffer;
20 }
21 catch
22 {
23 return new byte[] { };
24 }
25 }
发送
1 sp.Write(buffer, 0, buffer.Length);
接收串口数据
接收串口数据需要响应SerialProt类的DataReceived事件,指示已通过由 SerialPort 对象表示的端口接收了数据。然后在DataReceived的事件处理函数中,使用SerialPort.Read函数来读取接收到的数据
1 sp.DataReceived += ReceiveData;
1 private void ReceiveData(object sender, SerialDataReceivedEventArgs e)
2 {
3 //在这里处理接收到的数据
4 }
处理接收到的数据
1 /// <summary>
2 /// 接收原始数据
3 /// </summary>
4 /// <returns></returns>
5 public string GetRawReceiveData()
6 {
7 try
8 {
9 var buffer = new byte[sp.BytesToRead];
10 sp.Read(buffer, 0, sp.BytesToRead);
11 var data = "";
12 if (buffer.Length > 0)
13 data = Encoding.ASCII.GetString(buffer);
14 return data;
15 }
16 catch (Exception ex)
17 {
18 return "";
19 }
20 }
21
22 /// <summary>
23 /// 接收十六进制数据
24 /// </summary>
25 /// <returns></returns>
26 public string GetHexReceiveData()
27 {
28 try
29 {
30 var buffer = new byte[sp.BytesToRead];
31 sp.Read(buffer, 0, sp.BytesToRead);
32 var data = "";
33 if (buffer.Length > 0)
34 data = HexByteArrayToString(buffer);
35 return data;
36 }
37 catch (Exception ex)
38 {
39 return "";
40 }
41 }
42
43 public string HexByteArrayToString(byte[] data)
44 {
45 try
46 {
47 StringBuilder sb = new StringBuilder(data.Length * 3);
48 foreach (byte b in data)
49 sb.Append(Convert.ToString(b, 16).PadLeft(2, '0'));
50 return sb.ToString().ToUpper();
51 }
52 catch (Exception ex)
53 {
54 return "";
55 }
56 }
注意:一次接收到的数据可能并不能组成我们定义的通信协议。建议:使用一个变量来记录接收到的数据。当接收 到的数据能组成一个完整的通信协议时,就返回这个通信协议。并从记录的数据中移除这一段数据。