C#_serialPort_串口的应用
前言:一个上位机与下位机通讯最常用的就是串口了,是上位机与外界数据交互的通道。
1、获取串口号部分
2、串口必要参数设置
3、串口数据的接收
4、串口数据的发送
获取串口号部分
说明:一般串口号只能获取是COM3这样的,并不能真实的获取像设备管理器里面这样的串口名字,所以我们要引入一个能够获取电脑硬件信息的类。在项目名上右键添加类,命名为SystemHardware.cs,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace JD_MINI_TOOL
{
class SystemHardware
{
//获取CPU序列号
public static string GetCPUSerialNumber()
{
try
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select * From Win32_Processor");
string sCPUSerialNumber = "";
foreach (ManagementObject mo in searcher.Get())
{
sCPUSerialNumber = mo["ProcessorId"].ToString().Trim();
break;
}
return sCPUSerialNumber;
}
catch
{
return "";
}
}
//获取主板序列号
public static string GetBIOSSerialNumber()
{
try
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select * From Win32_BIOS");
string sBIOSSerialNumber = "";
foreach (ManagementObject mo in searcher.Get())
{
sBIOSSerialNumber = mo.GetPropertyValue("SerialNumber").ToString().Trim();
break;
}
return sBIOSSerialNumber;
}
catch
{
return "";
}
}
//获取硬盘序列号
public static string GetHardDiskSerialNumber()
{
try
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PhysicalMedia");
string sHardDiskSerialNumber = "";
foreach (ManagementObject mo in searcher.Get())
{
sHardDiskSerialNumber = mo["SerialNumber"].ToString().Trim();
break;
}
return sHardDiskSerialNumber;
}
catch
{
return "";
}
}
/// <summary>
/// Get system serail port
/// </summary>
/// <returns></returns>
public static string[] GetSerialPort()
{
List<string> spList = new List<string>();
Regex reg = new Regex(@"COM\d+");
string[] strArr = GetHarewareInfo(HardwareEnum.Win32_PnPEntity, "Name");
foreach (string s in strArr)
{
if (reg.IsMatch(s))
{
spList.Add(s);
}
}
return spList.ToArray();
//List<string> strs = new List<string>();
//try
//{
// using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from " + HardwareEnum.Win32_PnPEntity))
// {
// var hardInfos = searcher.Get();
// foreach (var hardInfo in hardInfos)
// {
// string str = hardInfo.Properties["Name"].Value.ToString();
// //Regex reg = new Regex(@"COM\d+");
// //if (reg.IsMatch(str))
// //{
// strs.Add(str);
// //}
// }
// searcher.Dispose();
// }
//}
//catch (Exception ex)
//{
// Console.WriteLine(ex.ToString());
//}
//return strs.ToArray();
}
/// <summary>
/// Get the system devices information with windows api.
/// </summary>
/// <param name="hardType">Device type.</param>
/// <param name="propKey">the property of the device.</param>
/// <returns></returns>
public static string[] GetHarewareInfo(HardwareEnum hardType, string propKey)
{
List<string> strs = new List<string>();
try
{
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from " + hardType))
{
var hardInfos = searcher.Get();
foreach (var hardInfo in hardInfos)
{
if (hardInfo.Properties[propKey].Value != null)
{
String str = hardInfo.Properties[propKey].Value.ToString();
strs.Add(str);
}
}
}
return strs.ToArray();
}
catch
{
return null;
}
finally
{
strs = null;
}
}//end of func GetHarewareInfo().
/// <summary>
/// 枚举win32 api
/// </summary>
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
}
}
}
然后就可以获取串口号了
public static bool com_update_arrange_flog = false; //用来标志该电脑是否存在COM1
private void Form1_Load(object sender, EventArgs e) //主窗口打开后
{
serialPort.ReadTimeout = -1; //设置超时读取时间
serialPort.RtsEnable = true; //定义DataReceived事件,当串口收到数据后触发事件
serialPort.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
string[] ArryPort = SystemHardware.GetSerialPort();
if (ArryPort.Length > 0) cbComName.Text = ArryPort[0]; //如果读到串口默认填入一个串口号
foreach (string arryport in ArryPort)
{
if (string.Equals(arryport, "通信端口 (COM1)"))
{
com_update_arrange_flog = true;
break;
}
}
}
然后就是动态更新串口了
public void timer2_Tick(object sender, EventArgs e) //刷新串口
{
if (com_update_arrange_flog == true)
{
update_com_1();
}
else
{
update_com();
}
}
void update_com_1() //识别到有串口COM1
{
string[] ArryPort = SystemHardware.GetSerialPort();
if (com_num != ArryPort.Length - 1)
{
cbComName.Items.Clear();
create_com_list.Clear();
foreach (string arryport in ArryPort)
{
if (string.Equals(arryport, "通信端口 (COM1)") == false)
{
Console.WriteLine(arryport);
create_com_list.Add(arryport);
cbComName.Items.Add(arryport);
}
}
com_num = create_com_list.Count();
}
}
void update_com() //没有识别到有串口COM1
{
string[] ArryPort = SystemHardware.GetSerialPort();
if (com_num != ArryPort.Length)
{
cbComName.Items.Clear();
create_com_list.Clear();
foreach (string arryport in ArryPort)
{
if (string.Equals(arryport, "通信端口 (COM1)") == false)
{
Console.WriteLine(arryport);
create_com_list.Add(arryport);
cbComName.Items.Add(arryport);
}
}
com_num = create_com_list.Count();
}
}
串口必要参数设置
//打开和关闭串口的按钮
private void btnPortOpt_Click(object sender, EventArgs e) //打开串口
{
if (cbComName.Text == "")
{
MessageBox.Show("串口选择不能为空", "提示");
return;
}
try
{
if (btnPortOpt.Text == "打开串口")
{
serialPort.PortName = sp_com_name(cbComName.Text);
serialPort.BaudRate = Convert.ToInt32(cbxBaudRate.Text.Trim());
serialPort.Parity = System.IO.Ports.Parity.None; //奇偶校验
serialPort.DataBits = 8; //数据位
serialPort.StopBits = System.IO.Ports.StopBits.One; //停止位
serialPort.ReadBufferSize = 2 * 1024 * 1024; //缓冲区 2M
serialPort.Open();
}
if (btnPortOpt.Text == "关闭串口")
{
serialPort.Close();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
if (serialPort.IsOpen)
{
btnPortOpt.Text = "关闭串口";
UpdatalabText();//信息提示框
}
else
{
btnPortOpt.Text = "打开串口";
UpdatalabText();
}
}
}
//输入串口名字字符串将串口号转变为串口可以识别的COM
public string sp_com_name(string str)
{
string sp_com_name;
if (str != null)
{
string S1 = str.ToString().ToLower();
if (S1.Contains("com"))
{
int i = S1.IndexOf("(");
int j = S1.IndexOf(")");
if ((j - i) == 5)
{
sp_com_name = "COM" + S1[i + 4];//设置串口号
return sp_com_name;
}
if ((j - i) == 6)
{
sp_com_name = "COM" + S1[i + 4] + S1[i + 5];//设置串口号
return sp_com_name;
}
if ((j - i) == 7)
{
sp_com_name = "COM" + S1[i + 4] + S1[i + 5] + S1[i + 6];//设置串口号
return sp_com_name;
}
}
}
return "";
}
//重新设定串口波特率后要及时更新
private void cbxBaudRate_TextChanged(object sender, EventArgs e)
{
int baudrate = 9600; //默认9600
if (int.TryParse(cbxBaudRate.Text, out baudrate))
{
serialPort.BaudRate = Convert.ToInt32(cbxBaudRate.Text.Trim());
}
else MessageBox.Show("波特率异常!", "错误提示");
}
串口数据的接收
下面这个函数就是项目中实际用到的,接收16进制数据后进行处理的过程,解析数据帧
private void sp_DataReceived(object sender, SerialDataReceivedEventArgs e) //CRCPing-X:宽度16 多项式1021 初始值FFFF 结果异或值0000
{
bool isHex = true;
if (rdio_show_hax.Checked)
{
isHex = true;
}
else
{
isHex = false;
}
System.Threading.Thread.Sleep(100); //延时100ms等待接收完数据
//this.Invoke就是跨线程访问ui的方法,也是文本的范例
this.Invoke((EventHandler)delegate
{
serport_richTextBox.Text += "收--《《";
if (isHex == false)
{
System.Text.UTF8Encoding utf8 = new System.Text.UTF8Encoding();
Byte[] readBytes = new Byte[serialPort.BytesToRead];
serialPort.Read(readBytes, 0, readBytes.Length);
string decodString = utf8.GetString(readBytes);
serport_richTextBox.Text += decodString;// sp.ReadLine();
}
else
{
Byte[] ReceivedData = new Byte[serialPort.BytesToRead];//创建接收字节数组
serialPort.Read(ReceivedData, 0, ReceivedData.Length);//读取所接收到的数据
String RecvDataText = null;
for (int i = 0; i < ReceivedData.Length; i++)
{
RecvDataText = (ReceivedData[i].ToString("X2") + " ");
serport_richTextBox.Text += RecvDataText;
}
serport_richTextBox.Text += "\r\n";
ushort data_length = (ushort)(ReceivedData[5] + (ReceivedData[6] * 256)); //获取Payload的数据长度
byte[] data1 = new byte[data_length+5]; //根据上述获取的长度动态创建适合长度的数组,不然0x00这个的数据会影响crc校验值
byte[] data2 = new byte[2];
for (int j = 0; j < ReceivedData.Length - 4; j++)
{
data1[j] = ReceivedData[j+2];
}
data2[0] = ReceivedData[ReceivedData.Length - 1];
data2[1] = ReceivedData[ReceivedData.Length - 2];
if (CRC16(data1,ref data2)) //crc校验
{
if (ReceivedData[0] == Sync_h) //检验头部数据
{
if (ReceivedData[1] == Sync_l) //检验头部数据
{
if (ReceivedData[2] == Version) //校验协议版本
{
byte Seq = ReceivedData[4]; //mcu->wifi上报数据
switch (ReceivedData[3])
{
case Query_mcu: //数据类型一
Console.WriteLine("查询设备状态");
break;
case Control_mcu: //数据类型二
Console.WriteLine("控制设备");
break;
case Upload_wifi: //数据类型三
Console.WriteLine("123");
break;
}
}
}
}
}
}
serialPort.DiscardInBuffer();
});
}
串口数据的发送
byte[] send_data = new byte[10];
send_data[0] = 0x5a;
send_data[1] = 0x5a;
send_data[2] = data[0];
send_data[3] = data[1];
send_data[4] = data[2];
send_data[5] = data[3];
send_data[6] = data[4];
send_data[7] = data[5];
send_data[8] = crc_data[1];
send_data[9] = crc_data[0];
serialPort.Write(send_data, 0, send_data.Length);
//下面是显示在日志框中
String SendDataText = null;
serport_richTextBox.Text += "发--》》";
for (int i = 0; i < send_data.Length; i++)
{
SendDataText = (send_data[i].ToString("X2") + " ");
serport_richTextBox.Text += SendDataText;
}
serport_richTextBox.Text += "\r\n";