串口知识回顾:
1、常见的串口
(1)RS-232接口:通常适合于数据传输频率在0~20000bps范围的通信。在PC机上通常都是COM串口。
不足:抗干扰能力差,通信距离短,速率低。
(2)RS-422/485接口:传输速度和通信距离变得非常远(1200m),并且在一条平衡总线上连接最多10个接收器。
2、串行通信的基本参数
(1)波特率:是指串行通信中每1秒所传送的数据位数,单位是bps。也就是传输速度。
比如发送一个字符需要8位,如果波特率是4800bps传输,则每秒可以传输600个字符。
(2)起始位:起始位表示字符传输的开始。在通信线上,没有数据传输时处于逻辑“1”状态。当发送一个字符数据时,首先发出一个“0”信号,这个逻辑低电平就是起始位。接收设备检测到低电平后,就准备开始接收。
(3)数据位:当接收设备收到起始位后,紧挨着的就是数据位,数据位的个数可以是5、6、7、8位。在字符串数据传送的过程中,数据位从最低有效位开始传输。
(4)奇偶校验位:数据位发送完以后,就可以发送奇偶校验位,用于有限差错的检查。通信双方需要约定一致的奇偶校验方式。
(5)停止位:在奇偶校验位或者数据位(无奇偶校验位时)之后是停止位,它可以是1位,1.5位或者2位,停止位是数据结束的标志。
3、异步传输中的一帧数据组成
【发送端】->停止位(1-2)->奇偶校验位(1位)->数据位(7-8个)->起始位(0)【接收端】
串口助手类的设计
准备工作:准备一个进制转换类助手,能够完成十进制、16进制、汉字、英文、byte[]之间的转换。
新建一个HelpClass文件夹,并新建一个进制转换类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
namespace SerialPortHelperDemo
{
/// <summary>
/// 16进制使用的隔离符枚举
/// </summary>
public enum Enum16Hex
{
None,//无
Blank,//空格
OX, //OX
Ox //Ox
}
/// <summary>
///计算进制类助手
/// </summary>
public class AlgorithmHelper
{
#region十进制转十六进制
public string From10To16(int d)
{
string hex = "";
if (d < 16)
{
hex = BeginChange(d);
}
else
{
int c;
int s = 0;
int n = d;
int temp = d;
while (n >= 16)
{
s++;
n = n / 16;
}
string[] m = new string[s];
int i = 0;
do
{
c = d / 16;
m[i++] = BeginChange(d % 16);//判断是否大于10,如果大于10,则转换为A~F的格式
d = c;
} while (c >= 16);
hex = BeginChange(d);
for (int j = m.Length - 1; j >= 0; j--)
{
hex += m[j];
}
}
return hex;
}
//判断是否为10~15之间的数,如果是则进行转换
public string BeginChange(int d)
{
string hex = "";
switch (d)
{
case 10:
hex = "A";
break;
case 11:
hex = "B";
break;
case 12:
hex = "C";
break;
case 13:
hex = "D";
break;
case 14:
hex = "E";
break;
case 15:
hex = "F";
break;
default:
hex = d.ToString();
break;
}
return hex;
}
#endregion
#region其他函数【16进制中的隔离符处理】
/// <summary>
///把16进制隔离符转换成实际的字符串
/// </summary>
/// <param name="enum16">16进制隔离符枚举</param>
/// <returns></returns>
private string AddSplitString(Enum16Hex enum16)
{
switch (enum16)
{
case Enum16Hex.None:
return "";
case Enum16Hex.Ox:
return "0x";
case Enum16Hex.OX:
return "0X";
case Enum16Hex.Blank:
return " ";
default:
return "";
}
}
/// <summary>
///去掉16进制字符串中的隔离符【 如:" ", "0x", "0X"】
/// </summary>
/// <param name="inString">需要转换的字符串数据</param>
/// <returns></returns>
private string DeleteSplitString(string inString)
{
string outString = string.Empty;
string[] delArray = { " ", "0x", "0X" };
if (inString.Contains(" ") || inString.Contains("0x") || inString.Contains("0X"))//存在隔离符
{
string[] str = inString.Split(delArray,
System.StringSplitOptions.RemoveEmptyEntries);//以隔离符进行转换数组,去掉隔离符,去掉空格。
for (int i = 0; i < str.Length; i++)
{
outString += str[i].ToString();
}
return outString;
}
else//不存在隔离符就直接返回
{
return inString;
}
}
#endregion
#region汉字、英文、纯16进制数、byte[]之间的各种转换方法
/// <summary>
///字符串转换成16进制
/// </summary>
/// <param name="inSting"></param>
/// <param name="enum16"></param>
/// <returns></returns>
public string StringTo16(string inSting, Enum16Hex enum16)
{
string outString = "";
byte[] bytes = Encoding.Default.GetBytes(inSting);
for (int i = 0; i < bytes.Length; i++)
{
int strInt = Convert.ToInt16(bytes[i] - '\0');
string s = strInt.ToString("X");
if (s.Length == 1)
{
s = "0" + s;
}
s = s + AddSplitString(enum16);
outString += s;
}
return outString;
}
/// <summary>
///字符串转换成byte[]
/// </summary>
/// <param name="inSting"></param>
/// <returns></returns>
public byte[] StringToBtyes(string inSting)
{
inSting = StringTo16(inSting, Enum16Hex.None);//把字符串转换成16进制数
return From16ToBtyes(inSting);//把16进制数转换成byte[]
}
/// <summary>
///把16进制字符串转换成byte[]
/// </summary>
/// <param name="inSting"></param>
/// <returns></returns>
public byte[] From16ToBtyes(string inSting)
{
inSting = DeleteSplitString(inSting);//去掉16进制中的隔离符
byte[] stringByte = new byte[inSting.Length / 2];
for (int a = 0, b = 0; a < inSting.Length; a = a + 2, b++)
{
try
{
string str = inSting.Substring(a, 2);
stringByte[b] = (byte)Convert.ToInt16(str, 16);
}
catch (Exception ex)
{
throw new Exception("输入的数据不是纯16进制数! 参考错误信息:" + ex.Message);
}
}
return stringByte;
}
/// <summary>
///把16进制字符串转换成英文数字和汉字混合格式
/// </summary>
/// <param name="inSting">需要转换的16进制字符串</param>
/// <returns></returns>
public string From16ToString(string inSting)
{
inSting = DeleteSplitString(inSting);
return Encoding.Default.GetString(From16ToBtyes(inSting));
}
/// <summary>
///把byte[]转换成String
/// </summary>
/// <param name="bytes">需要转换的byte[]</param>
/// <param name="enum16">隔离符</param>
/// <returns></returns>
public string BytesToString(byte[] bytes, Enum16Hex enum16)
{
return From16ToString(BytesTo16(bytes, enum16));
}
/// <summary>
/// byte[]转换成16进制字符串
/// </summary>
/// <param name="bytes">需要转换的byte[]</param>
/// <param name="enum16"></param>
/// <returns></returns>
public string BytesTo16(byte[] bytes, Enum16Hex enum16)
{
string outString = "";
for (int i = 0; i < bytes.Length; i++)
{
if (bytes[i].ToString("X").Length < 2)//16进制数为一位时前面填充0
{
outString += "0" + bytes[i].ToString("X") + AddSplitString(enum16);//转成16进制数据
}
else
{
outString += bytes[i].ToString("X") + AddSplitString(enum16);//转成16进制数据
}
}
return outString;
}
/// <summary>
///把byte[]直接转换成字符串,直接以2进制形式显示出来。
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public string BytesTo2String(byte[] bytes, Enum16Hex enum16)
{
string outString = "";
for (int i = 0; i < bytes.Length; i++)
{
string tempString = Convert.ToString(bytes[i], 2);
if (tempString.Length != 8)
{
string add0 = "";
for (int j = 0; j < 8 - tempString.Length; j++)
{
add0 += "0";
}
outString += add0 + tempString + AddSplitString(enum16);
}
else
{
outString += tempString + AddSplitString(enum16);
}
}
return outString;
}
/// <summary>
///把字符串传进来,输出一个byte数组【可以把此byte数组直接发送到串口中】
/// </summary>
/// <param name="inString">要转换的字符串</param>
/// <param name="is16">是否已经是16进制数据,true时已经是(已经转换好的数据),false时不是(需要内部转换)</param>
/// <returns>输出一个byte数组</returns>
public byte[] StringToBytes(string inString, bool is16)
{
if (is16)
{
return From16ToBtyes(inString);
}
else
{
return StringToBtyes(inString);
}
}
#endregion
}
}
1、新建一个串口类SerialPortHelper.cs
namespace SerialPortHelper
{
/// <summary>
///串口助手通用类
/// </summary>
public class SerialPortHelper
{
}
}
2、引入串口所使用的命名空间
using System.IO.Ports;
3.设置相关的属性
(1)属性:串口操作对象。
/// <summary>
///创建一个串口操作对象
/// </summary>
private SerialPort serialPort = null;
public SerialPort SerialPortObject
{
get { return serialPort; }//只读
}
(2)属性:获取计算机串口列表。
/// <summary>
///获取当前计算机上可用的端口列表【只读】
/// </summary>
public string[] PortNames
{
get { return System.IO.Ports.SerialPort.GetPortNames(); }
}
(3)属性:进制转换对象。
/// <summary>
///进制转换对象属性
/// </summary>
private AlgorithmHelper algorithmHelper = null;
public AlgorithmHelper AlgorithmHelperObject
{
get { return algorithmHelper; }
}
(4)构造方法:完成属性初始化和串口参数默认值。
/// <summary>
///构造方法中初始化相关数据
/// </summary>
public SerialPortHelper()
{
this.serialPort = new SerialPort();//创建串口实例
this.algorithmHelper = new AlgorithmHelper();//创建进制转换对象
//串口基本参数初始化
this.serialPort.BaudRate = 9600;
this.serialPort.Parity = System.IO.Ports.Parity.None;//校验位默认NONE
this.serialPort.DataBits = 8;//数据位默认8位
this.serialPort.StopBits = System.IO.Ports.StopBits.One;//停止位默认1位
}
4.设置相关的方法
(1)打开或关闭串口(端口)。
#region打开或关闭串口
/// <summary>
///根据端口名称打开端口或关闭端口
/// </summary>
/// <param name="portName">端口名称</param>
/// <param name="status">操作状态:1表示打开,0表示关闭</param>
/// <returns>返回当前端口的打开状态true或false</returns>
public bool OpenSerialPort(string portName, int status)
{
if (status == 1)
{
this.serialPort.PortName = portName;//只有在端口没有打开的时候,才能设置名称。
this.serialPort.Open();
}
else
{
this.serialPort.Close();
}
return this.serialPort.IsOpen;//返回串口打开的状态
}
#endregion
(2)枚举限制格式
#region发送格式选择枚举
/// <summary>
///发送格式选择
/// </summary>
public enum SendFormat
{
Hex,//十六进制
String
}
#endregion
(3)编写发送数据的方法。
#region发送数据
/// <summary>
///判断十六进制字符串hex是否正确
/// </summary>
/// <param name="hex">十六进制字符串</param>
/// <returns>true:正确,false:不正确<returns>
private bool IsIIlegalHex(string hex)
{
return System.Text.RegularExpressions.Regex.IsMatch(hex, @"([^A-Fa-f0-9]|\s+?)+");
}
/// <summary>
///发送数据(可以是16进制,也可以是字符串)
/// </summary>
/// <param name="data">要发送的数据</param>
/// <param name="format">发送的数据格式</param>
public void SendData(string data, SendFormat format)
{
if (!this.serialPort.IsOpen)
{
throw new Exception("端口未打开!请打开相关端口!");//让调用者处理
}
else
{
byte[] byteData;
if (format == SendFormat.Hex)//如果是16进制
{
if (this.IsIIlegalHex(data))
{
byteData = algorithmHelper.From16ToBtyes(data);//将16进制转换成byte[]数组
}
else
{
throw new Exception("数据不是16进制格式!");
}
}
else //发送字符串
{
byteData = algorithmHelper.StringToBytes(data, false);
}
this.serialPort.Write(byteData, 0, byteData.Length);//发送数据(数据,从0开始,结束位置)
}
}
#endregion
(4)编写接收数据的方法。
#region接收数据
/// <summary>
///串口接收数据
/// </summary>
/// <returns></returns>
public byte[] ReceiveData()
{
//定义一个接收数组,获取接收缓冲区数据的字节数
byte[] byteData = new byte[this.serialPort.BytesToRead];
//读取数据(存储区,起始位置,终点位置)
this.serialPort.Read(byteData, 0, serialPort.BytesToRead);
return byteData;
}
#endregion
5.串口助手UI设计和任务编写